Commit graph

3925 commits

Author SHA1 Message Date
Till JS
0aec1d43c0 fix(docs): astro starlight repo URL → Memo-2023/managarten
Vorbestehender Bug: zeigte auf nicht-existente Org `mana/`. Korrigiert
auf den tatsächlichen GitHub-Pfad. Edit-Link auf docs-Seite + GitHub-
Icon im Footer funktionieren jetzt.
2026-05-09 01:28:50 +02:00
Till JS
b1b9bbc269 chore: rename repo mana-monorepo → managarten
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
Phase-3-Rename des ehemaligen Multi-App-Monorepos zum eigenständigen
Produkt-Repo. Verein heißt mana e.V., Plattform-Domain bleibt mana.how,
apps/mana/ bleibt unverändert — nur der Repo-Container kriegt den
neuen Namen "managarten" (Garten der mana-Apps).

Geändert:
- package.json#name + #description
- README.md (Titel + erster Absatz)
- TROUBLESHOOTING.md
- alle Mac-Mini-Skripte (Pfade ~/projects/mana-monorepo → ~/projects/managarten)
- COMPOSE_PROJECT_NAME-default in scripts/mac-mini/status.sh
- .github/workflows/cd-macmini.yml + mirror-to-forgejo.yml
- apps/docs (astro.config.mjs + content)
- .claude/settings.local.json (Bash-Permission-Pfade)
- alle docs/*.md Pfad-Referenzen
- launchd plists, .env.macmini.example, infrastructure/

Forgejo-Repo + GitHub-Repo bereits via API umbenannt. Lokales
Verzeichnis-Rename + Mac-Mini-Cutover folgen separat.
2026-05-09 01:16:02 +02:00
Till JS
ac15de280b chore(decommission): remove cards module from mana web app
Some checks are pending
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Cards-Modul war im unified mana-Frontend tief verzahnt. Cardecky
ist seit 2026-05-08 standalone auf cardecky.mana.how — Dual-Stack
ist nicht das Ziel. Entfernt:

  - apps/mana/apps/web/src/lib/modules/cards/ (UI + stores + queries
    + collections + module.config + tools + cloze + fsrs + render)
  - apps/mana/apps/web/src/routes/(app)/cards/ (alle Routes)
  - apps/mana/apps/web/src/lib/i18n/locales/cards/ (5 Locales)
  - apps/mana/apps/web/src/lib/search/providers/cards.ts
  - apps/mana/apps/web/src/lib/components/dashboard/widgets/
    CardsProgressWidget.svelte + 'cards-progress' WidgetType-Eintrag

Cross-Refs aufgeräumt:
  - app-registry/apps.ts: Cards-Icon-Import + registerApp-Block raus
  - shared-branding/mana-apps.ts: 'cards'-App-Eintrag raus
  - data/cross-app-queries.ts: useCardsProgress + Cards-Queries-Block
    raus (Konsument war nur das gelöschte Dashboard-Widget)
  - data/seed-registry.ts: CARDS_GUEST_SEED-Import + register-Aufruf
  - data/module-registry.ts: cardsModuleConfig-Import + Eintrag
  - data/privacy/exposed-records.ts: Cards-Block (cardDecks visibility)
  - data/tools/init.ts: cardsTools-Import + registerTools
  - modules/website/embeds.ts: 'cards.decks'-Source + resolveCardDecks
  - apps/mana/apps/web/package.json: @mana/cards-core dependency
  - pnpm-lock.yaml regeneriert
  - dashboard.test.ts: cards-progress-Assertion

Dexie-Tabellen `cardDecks`/`cardReviews`/`cards` (lokal pro User-IndexedDB)
und ggf. mana_platform.cards.* in der prod-DB werden NICHT in diesem
Commit gedroppt — bleibt offen als separater Migrations-Schritt, sobald
sicher ist dass kein anderer Pfad mehr darauf zugreift.

Type-check (svelte-check) 7669 files 0 errors.

Rollback: git checkout cards-decommission-base -- apps/mana/apps/web

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:36:33 +02:00
Till JS
dd1bab09d5 chore(decommission): remove packages/cards-core/
@mana/cards-core wurde nur von apps/cards + services/cards-server +
apps/mana/.../modules/cards genutzt — die ersten zwei sind weg, das
mana-Modul kommt im nächsten Commit.

Rollback: git checkout cards-decommission-base -- packages/cards-core/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:27:33 +02:00
Till JS
bc158cb0bc chore(decommission): remove services/cards-server/
Marketplace-Backend war an apps/cards gekoppelt — beides ist
jetzt im standalone-Repo git.mana.how/till/cards. Live auf
cardecky-api.mana.how (apps/api in cards-Repo).

Rollback: git checkout cards-decommission-base -- services/cards-server/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:27:33 +02:00
Till JS
9cd8717494 chore(decommission): remove apps/cards/
Cards-App ist seit 2026-05-08 ein eigenständiges Repo
git.mana.how/till/cards (Strategie-B-Greenfield, beschlossen
2026-05-08), live auf cardecky.mana.how + cardecky-api.mana.how.
Alte cards-web-Container wurden gestoppt + entfernt.

Rollback: git checkout cards-decommission-base -- apps/cards/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:27:24 +02:00
Till JS
ba254f5854 infra: commit git.mana.how cloudflared route + ignore secrets/.bak
Two long-uncommitted Mac Mini drifts cleaned up:

1. cloudflared-config.yml — git.mana.how → :3030 (Forgejo). The
   route has been live for weeks (HTTP 200), just never committed.
2. .gitignore — exclude secrets/ (private keys: mana-ai mission-grant
   RSA keypair lives there; must NEVER be committed) and *.bak-*
   files (operator backup workflow on the Mac Mini).

services/mana-auth/drizzle/ on the Mac Mini was Mac-Mini-side
generated state for the (now deleted) mana-monorepo mana-auth
service; cleanup fell out with the Phase 7 deletion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:03:52 +02:00
Till JS
f65330399f fix(stalwart): disable healthcheck — distroless image has no wget
Stalwart's official Docker image is distroless and has no wget, curl,
nc, ss, or netstat. The compose healthcheck (CMD wget ...) was failing
with "executable file not found" since 2026-05-05; container shows
status=unhealthy 24/7 even though Stalwart itself runs fine on
:25 / :587 / :465 / :993 / :8080.

Disable. Crash detection comes from docker's restart=always plus
mana-monitoring's external SMTP probe (blackbox-exporter), not from
inside the container.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 20:00:27 +02:00
Till JS
27798f009f chore(cutover): regenerate pnpm-lock after removing 8 platform services
-1023 / +113 lines: lockfile shrinks dramatically as the workspace
loses 8 service packages plus their transitive devDeps. @mana/media-client
now resolved from Verdaccio (^0.1.0) instead of as a workspace member.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:54:57 +02:00
Till JS
0a30b91200 chore(cutover): remove services/mana-auth/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-auth/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was 560K in this repo, gone now. Active code lives in
`Code/mana/services/mana-auth/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:56 +02:00
Till JS
af3f21a179 chore(cutover): remove services/mana-credits/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-credits/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was 156K in this repo, gone now. Active code lives in
`Code/mana/services/mana-credits/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:56 +02:00
Till JS
fcc36eadcb chore(cutover): remove services/mana-media/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-media/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was  17M in this repo, gone now. Active code lives in
`Code/mana/services/mana-media/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:55 +02:00
Till JS
af8ef60fe4 chore(cutover): remove services/mana-notify/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-notify/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was  43M in this repo, gone now. Active code lives in
`Code/mana/services/mana-notify/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:55 +02:00
Till JS
2b07f6ef89 chore(cutover): remove services/mana-llm/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-llm/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was  90M in this repo, gone now. Active code lives in
`Code/mana/services/mana-llm/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:54 +02:00
Till JS
6103d4d2d9 chore(cutover): remove services/mana-tts/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-tts/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was 148K in this repo, gone now. Active code lives in
`Code/mana/services/mana-tts/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:53 +02:00
Till JS
3c4a6d4f69 chore(cutover): remove services/mana-stt/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-stt/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was 132K in this repo, gone now. Active code lives in
`Code/mana/services/mana-stt/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:53 +02:00
Till JS
879975b665 chore(cutover): remove services/mana-mail/ — moved to mana-platform
Live containers on the Mac Mini build out of `../mana/services/mana-mail/`
since the 8-Doppel-Cutover commit (774852ba2). Smoke test green
2026-05-08 — health endpoints, JWKS, login flow, Stripe-webhook all
reachable from the new build path. Removing the now-stale duplicate.

Was 188K in this repo, gone now. Active code lives in
`Code/mana/services/mana-mail/` (siehe ../mana/CLAUDE.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:53:52 +02:00
Till JS
467d8339cc fix(apps/api): COPY packages/eslint-config in Dockerfile
Root package.json devDeps reference @mana/eslint-config (workspace:*).
Even with `--filter @mana/api...`, pnpm resolves the root workspace
manifest and chokes on the missing package. Copy eslint-config into the
builder stage so the root resolves cleanly. Same pattern as the platform
mana-auth Dockerfile already uses.

Discovered while running the 8-Doppel-Cutover smoke test on Mac Mini:
mana-api build failed with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:44:57 +02:00
Till JS
774852ba2d feat(cutover): platform services build from ../mana, not from this repo
Part of the 8-Doppel-Cutover (2026-05-08, plan
~/.claude/plans/floating-swinging-flurry.md):

- docker-compose.{macmini,dev,test}.yml: build context for
  mana-{auth,credits,media,llm,notify} switched to ../mana/services/...
  so the Mac Mini stack pulls platform services from the platform repo
  (sibling clone), not from services/ in this monorepo.
- .npmrc + apps/api/{Dockerfile,package.json}: @mana/media-client now
  resolved from Verdaccio (npm.mana.how, ^0.1.0) instead of as a
  workspace COPY from services/mana-media/packages/client. Build-arg
  NPM_TOKEN flows through .npmrc for pnpm install auth. Required
  before services/mana-media/ can be deleted.
- .github/workflows/{ci,cd-macmini,daily-tests}.yml: removed the
  detect-/build-/test-jobs that targeted services/mana-{auth,credits,
  notify,media}/. Those services build out of the platform repo now —
  CI for them belongs in mana/-repo (open). cd-macmini's
  workflow_dispatch can still rebuild any of them on demand;
  auto-detect on path-change is gone for these five.
- scripts/{mac-mini/push-schemas.sh,run-integration-tests.sh}:
  rewritten to look in ../mana/ for the platform services.
- package.json dev:{auth,credits,notify,media}: paths point at
  ../mana/services/... so local dev still works post-cutover.

What this commit does NOT do: delete services/mana-{auth,credits,...}
from this repo. That waits for Phase 7 once the Mac Mini stack has
booted cleanly from the new build paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:40:08 +02:00
Till JS
7b362066bb feat(auth): SSO + CORS origins for zitare.mana.how/zitare-api.mana.how
Adds the two zitare hostnames to PRODUCTION_TRUSTED_ORIGINS in
sso-origins.ts and to the mana-auth CORS_ORIGINS in
docker-compose.macmini.yml. Pre-condition for the first Zitare
live-cut on the Mac Mini — the running mana-auth container must
be rebuilt for the new TRUSTED_ORIGINS list to take effect (see
zitare/DEPLOY.md Schritt 3).

sso-config.spec.ts asserts symmetry between sso-origins.ts and
the CORS_ORIGINS env in compose. Test runs 8/8 green after this
change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:07:39 +02:00
Till JS
364f3c2284 infra(tunnel): add zitare.com / zitare.mana.how / zitare-api.mana.how
Three new ingress rules for the Zitare repo (Code/zitare/, separate
repo, deployed under ~/projects/zitare-deploy/ on the Mac Mini).
Ports follow mana/docs/PORTS.md: 3083 api / 3084 app / 3085 com.

zitare.com is a separate Cloudflare zone — the tunnel route for
that hostname needs a one-time `cloudflared tunnel route dns
1435166a-0e3f-4222-8de6-744f32cea5c9 zitare.com` to point the CNAME
at this tunnel. Same for the two .mana.how subdomains, which sit on
the existing mana.how zone.

Code-only: no Mac Mini deploy in this commit. The actual reload
needs ./scripts/mac-mini/sync-tunnel-config.sh after the matching
mana-auth/CORS_ORIGINS + sso-origins changes are committed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:02:05 +02:00
Till JS
8acf35eecf chore(dev): finish --watch → --hot sweep across remaining Bun services
Some checks failed
CD Mac Mini / Detect Changes (push) Failing after 11s
CI / Detect Changes (push) Successful in 7s
CI / Validate (push) Has been skipped
CI / Build mana-auth (push) Waiting to run
CI / Build mana-search (push) Waiting to run
CI / Build mana-sync (push) Waiting to run
CI / Build mana-notify (push) Waiting to run
CI / Build mana-api-gateway (push) Waiting to run
CI / Build mana-crawler (push) Waiting to run
CI / Build mana-media (push) Waiting to run
CI / Build mana-credits (push) Waiting to run
CI / Auth flow integration test (push) Has been skipped
Docker Validate / Validate Dockerfiles (push) Failing after 1m35s
Docker Validate / Build calendar-web (push) Has been skipped
Docker Validate / Build quotes-web (push) Has been skipped
Docker Validate / Build todo-backend (push) Has been skipped
Docker Validate / Build todo-web (push) Has been skipped
Docker Validate / Build mana-auth (push) Has been skipped
Docker Validate / Build mana-sync (push) Has been skipped
Docker Validate / Build mana-media (push) Has been skipped
Mirror to Forgejo / Push to Forgejo (push) Failing after 1s
CD Mac Mini / Deploy (push) Has been cancelled
Catches the service-level package.json files that the previous
sweep (4cca25ed0) missed — they don't appear in any dev:*:full
orchestrator but get invoked when someone runs `pnpm --filter
@mana/<service> dev` directly.

Touched: mana-geocoding, mana-mail, mana-subscriptions, mana-mcp,
news-ingester, mana-persona-runner, mana-research, mana-user,
plus apps/memoro (server + audio-server).

mana-ai stays on --watch on purpose: its entry uses an explicit
`Bun.serve({...})` call instead of `export default { port,
fetch }`, plus a SIGTERM/SIGINT handler that calls
`server.stop()`. --hot would replace the module without releasing
the old server reference and produce exactly the EADDRINUSE we're
trying to avoid. If mana-ai gets refactored to the standard
default-export shape, flip its dev script too.
2026-05-08 14:33:27 +02:00
Till JS
15e2abd7f9 fix(env): mana-events default port 3065 → 3115
Completes the migration documented in docs/PORT_SCHEMA.md:29-30
("moved from 3065 on 2026-05-06 because the platform mana-media
reserves 3065"). services/mana-events/src/config.ts already
defaults to 3115 — generate-env.mjs was the last file still
emitting the old value, so anyone running pnpm setup:env would
get a .env that pinned the service back to 3065 and collided
with mana-media on the platform.
2026-05-08 14:31:03 +02:00
Till JS
4cca25ed03 chore(dev): switch all Bun services from --watch to --hot
Fans out the cards-server fix from 08f422340 to every other Bun
service in the monorepo. --hot keeps the port bound across HMR
reloads via globalThis[hmrSymbol]; --watch restarts the process
and races old/new Bun.serve for the port.

Touched: dev:auth, dev:credits, dev:events, dev:analytics,
dev:memoro:server, dev:memoro:audio-server, dev:uload:server in
the root package.json plus the matching `dev` script in each
service's own package.json. All six services already export the
`{ port, fetch }` default that Bun's --hot expects.

Smoke-tested: pnpm dev:cardecky:full boots clean, then touching
auth/credits/cards-server entry files all hot-reload without
dropping their port.

(memoro/apps/audio-server doesn't have a `dev: bun --watch ...`
script in its own package.json, so only the root entry got the
swap there.)
2026-05-08 14:24:24 +02:00
Till JS
08f4223404 fix(dev): cards-server uses --hot + setup-databases creates mana_notify/credits
cards-server: switch from `bun run --watch` to `bun run --hot`.
--watch restarts the whole process on file change, racing the
old + new Bun.serve calls for the port (the EADDRINUSE you see
right after `listening on :3072`). --hot does in-process HMR via
the globalThis[hmrSymbol] pattern; the port stays bound across
reloads. apps/api already uses --hot for the same reason.

scripts/setup-databases.sh:
- create_db_if_not_exists "mana_notify" + "mana_credits" so a
  fresh-machine `pnpm setup:db` no longer leaves these two DBs
  off (mana-notify was crashing on boot with SASL fallback,
  mana-credits was less obvious because its drizzle config
  defaults to mana_platform — but the runtime config can point
  at mana_credits, so safer to have the DB exist).
- Fix the cards branch: was pointing at the non-existent
  @mana/cards-database package; now points at @mana/cards-server
  where the actual schema lives.

Verified: drop+re-create flow + cardecky:full boot + touch-trigger
hot-reload all clean.
2026-05-08 14:10:55 +02:00
Till JS
61f2772789 chore(brand): rename Cards → Cardecky (display, infra, license-IDs)
- App display name → Cardecky in mana-apps.ts, MODULE_REGISTRY, alle Docs
- Domains: cardecky.mana.how (App), cardecky-api.mana.how (Marketplace
  API), cardecky.com (Marketing-Landing — cloudflared-route + nginx-Block
  vorbereitet, DNS muss noch gesetzt werden)
- 301-Redirect cards.mana.how → cardecky.mana.how (nginx + cloudflared)
  für alte Bookmarks; kann nach 6–12 Monaten wieder raus
- SPDX license IDs Cards-Personal-Use/Pro-Only-1.0 → Cardecky-* via
  Drizzle 0001-Migration (DROP CHECK → UPDATE rows → SET DEFAULT → ADD
  CHECK), inkl. _journal- und 0001_snapshot-Update
- In-mana cards-Modul: dezenter Banner zur Standalone-App (GUIDELINES
  §12), einmal schließbar via localStorage
- Docker-CORS-Listen, sso-origins.ts, Prometheus-Target aktualisiert

Technische IDs bleiben bewusst: appId 'cards', schema
mana_platform.cards.*, Verzeichnis apps/cards/, Package @cards/web,
services/cards-server, Env-Vars CARDS_*, UMAMI_WEBSITE_ID_CARDS*, Class
CardsEvents — Mana-Konvention (Brand ≠ technischer Identifier).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 13:49:47 +02:00
Till JS
a6a003fa5e fix(dev): mana-notify dev script ships explicit DATABASE_URL
The Go service defaults to postgresql://mana:mana@... but the
local docker-compose postgres uses devpassword (matches every
other service + .env.development). Without the override the
notify worker died on boot with "failed SASL auth".

Inline the URL on the script so dev:cardecky:full + dev:notify
both work out of the box. Long-term the right home for this is
an .env file the Go service reads, but that's a separate
refactor across all Go services in the monorepo.
2026-05-08 13:47:44 +02:00
Till JS
7a96a9a3fa chore: add dev:cardecky:full + dev:cards-server scripts
dev:cardecky:full spins up the six processes Cardecky needs end-
to-end so a fresh `pnpm dev:cardecky:full` boots the full stack
in one terminal: mana-auth (3001) + mana-sync (3050) + mana-
credits (3061) + mana-notify (3040) + cards-server (3072) +
cards-web (5180).

dev:cards-server is the standalone shortcut for just the Hono
backend — useful when iterating on the API alone.

One-time prereqs (not in the script): `pnpm docker:up`,
`pnpm setup:env`, `pnpm setup:db`, plus
`cd services/cards-server && bun run db:push` (the legacy
setup-databases.sh cards branch points at a non-existent
@mana/cards-database package).
2026-05-08 13:19:36 +02:00
Till JS
39e508075a feat(cards): CardFace v2 — 3D-Flip + tap-anywhere reveal
- CardFace now renders one card surface that flips on Y-axis when
  the user taps it. Both faces share a CSS-grid cell so the parent
  height is the max of front/back, no jumpy reflow on flip.
- Tap-anywhere on the surface reveals (only while showBack is
  false). The /learn page keeps the keyboard space/enter shortcut
  via the existing handler; the standalone "Aufdecken" button is
  now type-in-only (where the input field on the front breaks the
  flip-mental-model).
- prefers-reduced-motion: reduce collapses the rotateY into an
  instant cross-fade — same affordance, no vestibular trigger.
- Added a subtle "Tippe auf die Karte oder drücke Leertaste" hint
  on the front face so the new affordance is discoverable.
- Fixed the last Phase-A leftover: focus:border-indigo-400 in the
  type-in input → focus:border-app-accent.
2026-05-08 02:48:40 +02:00
Till JS
ad3b99fe6d refactor(cards): Phase A + C — adopt @mana/shared-theme + per-app accent
Phase A — Cards joins the unified theme system:
- Drop placeholder --color-cards-* palette; app.css imports
  @mana/shared-tailwind/themes.css + sources.css.
- Remove hardcoded class="dark" from app.html; body uses
  bg-background text-foreground.
- New $lib/stores/theme.ts: createThemeStore({ appId: 'cards' }).
  ThemeToggle from @mana/shared-theme-ui in the header next to
  the streak chip.
- Sweep all neutral / red / emerald / amber / indigo utilities in
  apps/cards/apps/web/src to semantic tokens (560 substitutions
  across 19 files): bg-neutral-900 → bg-card, text-neutral-400 →
  text-muted-foreground, bg-red-500 → bg-error, etc. Domain
  literals kept (FSRS grade colors red/orange/green/blue, GitHub-
  violet PR-merged badge, marketplace-amber Buy button, admin-
  inbox category palette).
- Cards added to validate-theme-utilities scope so future drift
  fails CI.

Phase C — per-app accent token:
- New --color-app-accent in shared-tailwind/themes.css. Theme-
  agnostic (registered in validate-theme-parity's THEME_AGNOSTIC
  regex), so it stays the same across light/dark/lume/etc. Defaults
  to Mana indigo at :root.
- Cards layout writes 258 90% 66% (= #8b5cf6 violet, from
  MANA_APPS.cards.color) onto documentElement at boot via
  applyCardsAccent(). All Cards CTAs (Lernen, Abonnieren, Senden,
  links inside cloze cards) flow through bg-app-accent /
  text-app-accent now.

Net effect: Cards gets light/dark + 4 palette variants + a11y
toggles for free, and any future app can drop in by setting its
own --color-app-accent without touching shared-tailwind.
2026-05-08 01:54:16 +02:00
Till JS
863311eefa docs(cards): Phasen-Statusupdate nach η.1 — Stand 2026-05-07
Marketplace-Plan auf den realen Code-Stand gebracht:

- §11 Phasen-Liste: pro Phase  shipped / 🟡 partial /  pending
  Marker mit Kurzbegründung. β/γ/δ/ε sind , ζ und η sind 🟡 mit
  ζ.2 / η.2 als nächste Sub-Phasen.
- §13a „Bekannte Limitierungen": neue Einträge für ζ (Refunds,
  Reconciler, CSV) und η (Community-Verify-Cron, Mod-Permissions,
  Public-Changelog, Rate-Limit). PR-Merge-Stale-Blindness und
  Card-Preview-Heuristik bleiben dokumentiert.
- §15 ersetzt: Status-Tabelle pro Phase + Empfehlung für die
  nächsten 4 Schritte (ζ.2 Reconciler → η.2 Verify-Cron →
  Update-Mail δ.4 → Phase θ Auto-Tags/Summary).
2026-05-07 23:38:42 +02:00
Till JS
b185ee2473 doc(mac-mini): aktualisiere Container-Bilanz nach Phase 2c-2g
- Stand-Zeile auf 2026-05-07 inkl. aller Migrations-Phasen 2c+2d+2e+2f+2g
  + Verdaccio-Rollback erklärt
- Mem-Budget-Tabelle: news-ingester aus "Andere" raus (auf GPU-Box seit
  Phase 2f-2), Standalone-Compose-Zeile für verdaccio dazu, Total
  ~45 → ~42 Container
- GPU-Hostname-Liste vervollständigt: status, mana-ai, research, photon

Pre-existing Drift; nicht durch Phase 2g/Rollback verursacht, aber bei
der Audit aufgefallen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 23:30:06 +02:00
Till JS
c05022611e feat(cards): Phase η.1 — Reports + admin moderation actions
Server (cards-server):
- ModerationService: createReport, listOpen, resolveReport,
  takedownDeck, banAuthor, setVerifiedMana, restoreDeck. Takedown
  also auto-closes any sibling open reports against the deck and
  closes any open PRs (so contributors see clean state). Ban
  cascades to all of the author's decks.
- routes/moderation.ts: POST /v1/reports (any authed user),
  GET /v1/admin/reports (admin only), POST /v1/admin/reports/:id/
  resolve, POST /v1/admin/decks/:slug/{takedown,restore}, POST
  /v1/admin/authors/:slug/verify. Admin gate is `role === 'admin'`
  for now — verified-mana-only mods land later.
- Notify hooks: takedown emails the deck owner, mana-verify status
  change emails the author.

Frontend (cards-web):
- <ReportButton> (icon or inline variant) with category picker
  + optional explanation. On /d/<slug> as a discreet 🚩 next to
  the published-date stamp; in <CardDiscussions> per non-own
  comment.
- /d/<slug> shows a red "wurde von der Moderation entfernt" banner
  when isTakedown is true.
- /admin/reports inbox: lists open reports with category badges,
  Abweisen / Deck entfernen / Author bannen actions. Renders a
  forbidden state if the current user isn't admin.
2026-05-07 23:24:23 +02:00
Till JS
aeaefaf675 infra(phase 2f-1 rollback): verdaccio bleibt auf Mac Mini
Phase 2f-1 hatte verdaccio von der Mini auf die GPU-Box verlegt — das
Storage-Volume kam dort aber nie an. Der GPU-Container war leer (keine
htpasswd, keine @mana/*-Pakete), externe `npm install @mana/foo` lief
auf 404. Rollback statt Storage-Migration nachzuholen, weil:

- Mini's Standalone-Verdaccio (~/projects/verdaccio/) hat alle Daten
  inklusive claudebot-Service-Account und 9 published Pakete
- npm-Reads sind ohnehin niedrig (CI-builds), Mini-Disk hat Platz
- Vereinfacht den User-/Token-Pflad-Lebenszyklus (eine Quelle, keine
  Sync-Choreografie)

Cleanup:
- DNS npm.mana.how zurück auf Mini-Tunnel via Cloudflare-API
- Mini cloudflared-config.yml: npm.mana.how-Ingress wieder eingetragen
- GPU-Box: verdaccio-Container + 3 Volumes entfernt (mana_verdaccio-storage,
  mana_verdaccio-plugins, verdaccio-storage)
- infrastructure/docker-compose.gpu-box.yml: verdaccio-Service-Block raus
- infrastructure/verdaccio/config.yaml: gelöscht (war GPU-spezifischer
  Bundle, der Code/mana hat die kanonische Kopie für Mini)
- docs/PLAN_OPTION_C.md: Phase 2f markiert als ⚠️ teilweise zurückgerollt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 23:12:11 +02:00
Till JS
5dbc9ace2d feat(cards): Phase ζ.1 — Paid decks via mana-credits
Server (cards-server):
- lib/credits.ts: thin internal-API client for mana-credits
  (reserve / commit / refund-reservation / grant). Service-to-
  service via X-Service-Key. Throws InsufficientCreditsError
  separately so the buy flow can branch on UX.
- services/purchases.ts: 4-step purchase pipeline: reserve →
  insert deck_purchases row → commit reservation → grant
  author share + insert author_payouts. Idempotent on
  (buyer, deck) so a refresh-spam-click can't double-charge.
  Verified-mana authors get the 90/10 split, others 80/20
  (already in config). Refunds intentionally out of scope —
  see MARKETPLACE_PLAN §13a.
- routes/purchases.ts: POST /v1/decks/:slug/purchase,
  GET /v1/me/purchases, GET /v1/authors/me/payouts.
- decks.bySlug now returns hasPurchased (null when anonymous,
  bool when authed) so the deck-detail page can pick the right
  CTA.
- subscriptions.subscribe now blocks paid decks unless the
  caller has a non-refunded purchase row (owner exempt for
  testing).
- Notify: author gets a "Verkauf"-Email at grant time, with a
  deterministic externalId for dedup.

Frontend (cards-web):
- /d/<slug> shows "Kaufen für N 💎" instead of "Abonnieren"
  when paid + not yet bought; flips to subscribe path once
  purchased.
- /me/purchases page listing buyer history + (when present)
  author-payout history. Linked from the top nav.
2026-05-07 23:10:18 +02:00
Till JS
4fcc15737f feat(auth): add memoro-app.mana.how to SSO trusted origins
Memoro's SvelteKit SPA at memoro-app.mana.how is a separate deploy
under mana e.V. that needs to use the central mana-auth (login,
session, JWT). Without this entry Better-Auth rejects its preflight
silently (no Access-Control-Allow-Origin header) and the SPA can't
even reach POST /api/v1/auth/login.

Updates both SSOTs per the rule in CLAUDE.md / mana-auth/CLAUDE.md:
  1. PRODUCTION_TRUSTED_ORIGINS in services/mana-auth/src/auth/sso-origins.ts
  2. CORS_ORIGINS for mana-auth in docker-compose.macmini.yml

sso-config.spec.ts will pick up the consistency between the two.
2026-05-07 23:07:22 +02:00
Till JS
46fefd5cc4 feat(cards): Phase ε.4 — Card list + discussions on /d/<slug>
- DiscussionService.countsForDeck: bulk count (visible) comments per
  card-content-hash for one deck. Mounted at GET
  /v1/decks/:slug/discussion-counts so the public deck page can
  render comment badges without N+1 fetches.
- <DeckCardList> on /d/<slug>: lists the latest version's cards,
  renders a one-line preview + "💬 N" badge, and expands the
  inline <CardDiscussions> on click. Anonymous visitors see counts;
  posting requires auth (CardDiscussions already gates that).
2026-05-07 22:46:47 +02:00
Till JS
a8ddb6dea4 feat(cards): Phase ε.3 — PR notifications + Card-Discussions UI
- mana-notify integration in cards-server: PR-create notifies the
  deck owner, merge/reject notifies the PR author. Fire-and-forget
  via lib/notify.ts so a notify-service outage never rolls back a
  domain action. ExternalIDs are deterministic (cards.pr.{event}.{id})
  so retries dedupe.
- <CardDiscussions> on the learn page: collapsed by default, opens
  via "💬 Diskussion" alongside the "✏️ Verbessern" trigger. Resets
  whenever the current card changes so the panel doesn't bleed
  between flashcards.
- MARKETPLACE_PLAN.md §13a — known limitations: PR-merge is
  stale-blind (no rebase yet), diff-preview flat, threading 1-level.
2026-05-07 22:24:45 +02:00
Till JS
61fc16e8e9 feat(cards): Phase ε — Pull-Requests + Card-Discussions
Server (cards-server):
- PullRequestService: create / list / get / merge / close / reject.
  Merge applies the PR's {add, modify, remove} diff to the latest
  version's cards in a single transaction, writes a new
  deck_version + deck_cards, bumps latest_version_id, and stamps
  the PR with mergedIntoVersionId.
- DiscussionService: post / listForCard / hide. Threads are keyed
  by card_content_hash so they survive version bumps.
- Routes mounted under /v1: POST/GET /decks/:slug/pull-requests,
  GET /pull-requests/:id, POST /pull-requests/:id/{merge,close,reject},
  GET/POST /cards/:contentHash/discussions, POST /discussions/:id/hide.

Frontend (cards-web):
- cardsApi.pullRequests + cardsApi.discussions client surface.
- <PullRequestsSection> on /d/:slug — lists PRs with diff preview;
  owner sees Merge/Reject/Close buttons.
- <SuggestEditModal> + "✏️ Verbessern" button on /learn/:deckId for
  cards from a subscribed deck — submits a one-card modify (or
  remove) PR using the card's serverContentHash as the previous
  hash.
- Deck/Card DTOs gain subscribedFromSlug + serverContentHash so the
  learn page can decide whether to show the suggest-edit affordance.
2026-05-07 21:56:20 +02:00
Till JS
c84742005b infra(phase 2g): mana-research → GPU-Box
Web-Research-Orchestrator (16+ search-/LLM-providers) auf die GPU-Box
verlagert. Cross-LAN für mana-auth/mana-credits/mana-llm/mana-search/
postgres/redis (192.168.178.131). research.mana.how routet jetzt zum
mana-gpu-server-Tunnel (CF config v29). Mini-Container-Count 42 → 41.

PUBLIC_MANA_RESEARCH_URL in mana-app-web auf https-URL umgestellt —
Mini-Container können 192.168.178.11 nicht direkt erreichen (Colima-NAT),
daher Cross-LAN-Bridge via Cloudflare-Tunnel wie bei mana-ai.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:26:10 +02:00
Till JS
521ae52a62 feat(cards-web): Phase δ.3 — Smart-merge updates + read-only subscribed decks
Closes the marketplace subscribe loop. When the author publishes a
new version of a subscribed deck, the user sees a compact banner
with a per-card breakdown and one-click "Update anwenden" — FSRS
learning state survives across the merge.

  - lib/services/subscribe.ts:
    - previewUpdate(slug): server diff /v1/decks/:slug/diff?from=…
      with a small UpdatePreview shape ({added, changed, removed,
      unchanged} as counts).
    - applyUpdate(slug):
      - removed → soft-delete by [deckId+serverContentHash], cascade
        reviewStore.softDeleteForCard.
      - changed → update local card in place; ensureReviewsForCard
        re-runs to pick up cloze-cluster changes. FSRS reviews stay
        attached because the card-id never changes.
      - added → fresh local card + fresh FSRS reviews.
      - unchanged → only re-stamp ord if the diff moved it.
      Bumps subscribedAtVersion locally + re-stamps server-side.
  - lib/data/database.ts: Dexie v3 adds compound index
    [deckId+serverContentHash] for the smart-merge lookup.
  - routes/decks/[id]/+page.svelte:
    - subscribed decks get a green "📥 Abonniert"-banner with the
      version stamp + a deep-link to the public marketplace page.
    - if previewUpdate returns a non-null diff, the banner inlines
      the counts and an "Update anwenden" button.
    - "Veröffentlichen", "Neue Karte", "Aus Text generieren" and
      the per-card delete buttons are hidden when the deck is
      subscribed — read-only mirror of the author's content.

Validated: svelte-check 0/0, vite build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:21:32 +02:00
Till JS
58c057f6c5 feat(cards-web): Phase δ.2 — Subscribe + initial pull
End-to-end subscribe flow on cards.mana.how. From a public deck page
the user can now pull the deck into their own Cards instance with
one click; subscribed decks live alongside own decks but carry a
`subscribedFromSlug` marker so the editor knows to hide mutate
controls (UI gating in δ.3).

  - cards-core types: LocalDeck gains subscribedFromSlug +
    subscribedAtVersion. LocalCard gains serverContentHash. Both
    optional — own decks/cards are unaffected.
  - data/database.ts: Dexie v2 adds index on cardDecks.subscribedFromSlug
    so the lookup-by-slug path is O(1).
  - lib/api/cards-api.ts: subscriptions.{list,subscribe,unsubscribe,
    version,diff} + the SubscriptionInfo / ServerCard / DeckVersionPayload
    / DiffPayload types.
  - lib/services/subscribe.ts: subscribeAndPull() sequences server
    POST /subscribe → GET /decks/:slug → GET /versions/:semver →
    create LocalDeck + LocalCards + ensure FSRS reviews. Re-pull
    refreshes in place (Phase δ.3 will swap to real diff-apply that
    keeps FSRS state). unsubscribe() soft-deletes the local mirror.
    isSubscribedLocally() backs the deck-page state check.
  - routes/d/[slug]/+page.svelte: full subscribe UI — Abonnieren →
    Abonniert + Lernen-Button (deep-links into the existing learn
    session route).

Validated: svelte-check 0/0, vite build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:56:48 +02:00
Till JS
86a01426e8 feat(cards-server): Phase δ.1 — subscriptions + version reads + smart-merge diff
Server-side plumbing for Phase δ. Frontend hookup follows in δ.2.

  - services/subscriptions.ts: subscribe/unsubscribe (idempotent
    upsert on (user, deck), stamps the latest_version_id at
    subscribe-time so the client knows what it pulled). listForUser
    returns each sub with `updateAvailable: currentVersion !== latest`
    so the client can render an update indicator without a second
    round-trip. Refuses paid decks with 403 — that path comes back
    in Phase ζ once the credits Marketplace lands.
  - versionWithCards: deterministic ord-ordered card payload for a
    specific version. Read-public so anonymous browsers can preview
    a deck's content.
  - diffSince: smart-merge payload between any two versions. Splits
    the latest cards into added/changed/unchanged + lists removed
    by content_hash. The 'changed' bucket is heuristic (ord-position
    pair where one was removed and one was added) — solid enough
    until Phase ε's pull-request pipeline gives us real card
    lineage.
  - routes/subscriptions.ts mounts: GET /v1/me/subscriptions,
    POST/DELETE /v1/decks/:slug/subscribe (auth required),
    GET /v1/decks/:slug/versions/:semver (public),
    GET /v1/decks/:slug/diff?from=<semver> (public).

cards-web layout fix:
  - Marketplace surface (/explore, /u/, /d/) was previously gated
    behind the AuthGate — anonymous browsers got pushed to /login
    via client-side navigate. PUBLIC_PATHS extended so those routes
    SSR + render unauthed.

Validated: tsc clean on cards-server, svelte-check 0/0 on cards-web.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:33:58 +02:00
Till JS
e77134bd8b docs(infra): Phase 2f added to PLAN_OPTION_C + hostname table updated to v28
- PLAN_OPTION_C.md: new row covers verdaccio + news-ingester + mana-ai
  with the cross-arch + workspace-deps gotchas
- infrastructure/README.md: hostname table catches up to npm.mana.how
  (Phase 2f-1) and mana-ai.mana.how (Phase 2f-3); config v26 → v28
- infrastructure/.env.gpu-box.example: MANA_SERVICE_KEY +
  MANA_AI_PRIVATE_KEY_PEM block added with note that the values mirror
  Mini's .env.macmini (the latter's matching public-half stays on
  mana-auth, that's what makes Mission-Grant decryption work)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:09:28 +02:00
Till JS
a8cce79e4c fix(monitoring): comment-out mana-ai metrics scrape after Phase 2f-3 move
mana-ai's /metrics endpoint is no longer exposed on Mini's
192.168.178.131:3067 (service moved to GPU-Box, no public /metrics
tunnel since the endpoint is internal). The blackbox-api job
already probes mana-ai.mana.how/health for liveness, which gives
us up/down without needing the metrics scrape.

Status-page is now 58/58 UP after VM rolled past the stale 3067
samples.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:06:04 +02:00
Till JS
dcd16067b5 feat(cards-server): Phase γ — public reads + browse + search + engagement
Marketplace discovery surface lights up. Anonymous browsers can
explore + search; signed-in users get the same surface plus star/
follow mutations.

  - middleware/optional-auth.ts: opportunistic JWT — sets c.get('user')
    if a token validates, otherwise leaves it undefined. Read paths
    use this; mutating routes call requireUser() inline.
  - services/explore.ts: browse() with q (ilike on title/description),
    tag, language, author-slug, sort (recent/popular/trending), pagination.
    explore() composes featured + trending for the landing.
    tagTree()/curatedTagsOnly() round it out. Subqueries for star/
    subscriber counts avoid N+1.
  - services/engagement.ts: star/unstar deck, follow/unfollow author.
    Idempotent via ON CONFLICT DO NOTHING. Self-follow rejected.
  - routes/explore.ts mounts /v1/explore, /v1/decks (browse list),
    /v1/tags. routes/engagement.ts mounts /v1/decks/:slug/star
    (POST/DELETE) + /v1/authors/:slug/follow (POST/DELETE).
  - index.ts replaces the previous strict-jwt-on-everything middleware
    with optionalAuth on all of /v1, then individual routers gate
    their write paths via local requireUser(). Hono context type
    relaxes from `user: AuthUser` to `user?: AuthUser` accordingly.

Validated: tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:01:32 +02:00
Till JS
4c044e849d fix(monitoring): mana-ai probe now uses public mana-ai.mana.how/health
After Phase 2f-3 mana-ai lives on the GPU-Box, so the
blackbox-internal docker-DNS probe (http://mana-ai:3066/health) is
gone — that target sits in a Docker network the blackbox-exporter
can't reach across LAN. Move the probe into blackbox-api against
the public hostname; gives the same up/down signal plus exercises
the Cloudflare-tunnel hop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:55:39 +02:00
Till JS
ec8abfe6b8 feat(cards-web): Phase β.2 — author onboarding + publish flow
End-to-end "publish my local deck to the marketplace" surface in
the Cards standalone app. Hooks into cards-server (Phase β) so a
user can take a deck they've been editing locally and put it under
cards.mana.how/d/<slug> with one modal.

Pipeline:
  • lib/api/cards-api.ts — typed fetch wrapper around the cards-server
    /v1 surface. Reads the JWT from authStore, never from storage
    directly. CardsApiError carries `{status, message, details}`
    so UI can branch on 401/409/etc.
  • lib/stores/author.svelte.ts — lazy-loaded author state. Caches
    `cardsApi.authors.me()` on first access; resets cleanly on logout.
  • lib/util/slug.ts — best-effort slugify mirror of the server-side
    validator (server still has final say).
  • lib/components/PublishDeckModal.svelte — three-stage flow:
    become-author (slug + displayName + pseudonym), deck-meta (title,
    description, language, license picker, semver, changelog), then
    publishing → done with moderation-flag surface if AI mod returned
    'flag'. Keys off authorStore.isAuthor to skip stage 1 for
    returning authors.
  • routes/decks/[id]/+page.svelte gets a "🌍 Veröffentlichen" button
    next to "Lernen". Disabled until the deck has cards.

Wiring:
  • hooks.server.ts injects __PUBLIC_CARDS_API_URL__ on every SSR'd
    page so the client knows where cards-server lives.
  • compose adds PUBLIC_CARDS_API_URL_CLIENT=https://cards-api.mana.how
    to the cards-web container.

Validated: svelte-check 0/0, vite build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:53:17 +02:00
Till JS
f47edc14af feat(gpu-box): mana-ai (AI Mission Runner) migrated, mana-ai.mana.how → GPU tunnel
Phase 2f-3 (final of the 2f-trio). The background tick-loop runner is
the most coupled of the three: it queries mana-api, mana-llm, and
mana-research, and writes through to the mana_sync DB. Wired up via
cross-LAN host-IPs to those Mini-side services + the existing RSA
key-pair for Mission-Grant decryption (MANA_AI_PRIVATE_KEY_PEM moved
into /srv/mana/.env on the GPU-Box; the matching MANA_AI_PUBLIC_KEY_PEM
stays on mana-auth's env-set as before).

Bonus rationale: AI Mission Runner now sits in the same compose
network as the GPU-Box's gpu-llm/gpu-ollama tasks, so future
"agent talks to local LLM" paths skip the Cloudflare round-trip.

Tunnel: mana-ai.mana.how repointed at the mana-gpu-server tunnel
(config v28). The Mini-side ingress was removed in the same step.
OTEL_EXPORTER_OTLP_ENDPOINT cleared since Tempo was retired in 2c.

Mini-side: container stopped + removed from docker-compose.macmini.yml.
Running count went from 39 → 42 because of unrelated services that
re-appeared on the latest CD pull (cards-server, memoro-web), but the
actual mana-ai service is gone — net move accomplished.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:40:57 +02:00
Till JS
be155ca737 fix(cards-server): error classes extend Hono HTTPException
Shared-hono's serviceErrorHandler only translates HTTPException
instances; anything else degrades to 500. Our custom Error subclasses
were silently bypassing the translation layer, so a missing JWT came
back as `500 Internal server error` instead of the expected `401
Unauthorized`. Confirmed in prod logs after the Phase-β deploy.

Switching the error hierarchy to extend HTTPException directly. The
JSON body now carries the right status code + the existing `cause`
object surfaces our `code` discriminator + zod-style `details` for
BadRequest. No call-site changes needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:40:20 +02:00