# Cards — Projekt-Status & Onboarding **Letztes Update:** 2026-05-08 **Wenn du gerade neu bist (Mensch oder KI):** dieses Dokument soll dir in 5 Minuten den vollen Kontext geben. Lies es vor allem anderen. --- ## TL;DR - **Cards** ist die föderierte Spaced-Repetition-App des Vereins **mana e.V.** Strategie-B-Greenfield (beschlossen 2026-05-08): kein Code-Übernahme aus dem alten `mana-monorepo`, sauber neu gebaut. - **6 saubere Commits** auf `main`. Type-check 4/4 grün, **70 Tests grün**, lokaler E2E-Smoke-Test (Postgres → API → Frontend → Föderations- Endpunkte) durch. - **Phasen 0, 1, 3, 4, 5 sind durch** und verifiziert. Phase 2 (Auth- Föderation) ist auf user-side Pre-Flight blockiert. - Cards läuft lokal, ist im Browser benutzbar, hat alle Föderations- Endpoints aus dem `app-manifest.json` implementiert. ``` ┌─────────────────────────────────┐ │ cards/ (this repo) │ │ │ │ apps/web/ SvelteKit + Svelte 5 ← cardecky.mana.how │ apps/api/ Hono + Bun + Drizzle ← Postgres `cards` │ packages/cards-domain/ Pure-TS ← FSRS, Schemas, Protocol-Mirror │ app-manifest.json Föderations-Vertrag └─────────────────────────────────┘ ▲ │ HTTP, JWT, Manifest ▼ ┌─────────────────────────────────┐ │ mana/ Plattform │ │ mana-auth, mana-share, │ │ mana-links, mana-mcp, │ │ mana-search, mana-credits, … │ └─────────────────────────────────┘ ``` --- ## Architektur-Entscheidungen (festgenagelt) Diese stehen — nicht ohne explizite Diskussion antasten: 1. **Strategie B (Greenfield).** Kein Code aus mana-monorepo. 2. **Server-authoritative MVP.** Keine Dexie, keine eigene Sync-Engine. Local-First später via mana-sync-Federation, nicht durch eigenen Stack. 3. **Eigene Postgres-DB `cards`** mit Schema-Isolation `pgSchema('cards')`. 4. **Föderations-Endpoints als Pflicht** — alle aus `app-manifest.json` implementiert (siehe Phase 5 unten). 5. **Encryption initial AUS.** Nachrüstbar via mana-auth-MK. 6. **MVP-Card-Types nur `basic` + `basic-reverse`.** Schema vorbereitet auf full-set (cloze, type-in, image-occlusion, audio, multiple-choice). Vollständiger Plan: [`mana/docs/playbooks/CARDS_GREENFIELD.md`](../mana/docs/playbooks/CARDS_GREENFIELD.md) (im Plattform-Repo, weil er Verein-übergreifend gilt). --- ## Phasen-Status | # | Phase | Status | Verifikation | |---|---|---|---| | 0 | Read-Day mana-monorepo-Cards-Code lesen | ✅ | `docs/LESSONS_FROM_MANA_MONOREPO.md` | | 1 | Repo-Skelett (Turbo, pnpm, Bun, Docker, CI) | ✅ | `pnpm install` durch, 136 packages | | 2 | Auth-Föderation (mana-auth Registrierung, JWT-Verify) | 🚧 blockiert | siehe „Pre-Flight" unten | | 3 | Domain-Modell + Drizzle + CRUD-API | ✅ | 8 Tabellen, FSRS via ts-fsrs, 46 Tests grün, E2E-Smoke durch | | 4 | Frontend-Core (SvelteKit, Tailwind 4, Markdown-Editor, Study-View) | ✅ | type-check + build grün, manuell testbar im Browser | | 5 | Föderations-Endpunkte (share, tools, search, dsgvo) | ✅ | 70 Tests grün, E2E-Smoke (Quote→Inbox→Search→DSGVO-Roundtrip) | | 6 | Subscriptions/Credits via mana-credits | ⏸ offen | autonom möglich | | 7 | AI/MCP-Integration | ⏸ offen | braucht laufende mana-mcp | | 8 | Anki-Import (.apkg-Parser + mana-media-Upload) | ⏸ offen | autonom möglich, ~5–7 Tage | | 9 | Polish (DSGVO-UI, Settings, Account, Statistik, i18n, A11y) | ⏸ offen | breite Polish-Phase | | 10 | Production-Deploy (Mac Mini, Cloudflare-Tunnel) | ⏸ offen | braucht DNS + Tunnel-Config | | 11 | Decommission Cards-Modul aus mana-monorepo | ⏸ offen | erst nach Phase 10 | Legende: ✅ erledigt + verifiziert · 🚧 blockiert · ⏸ noch nicht begonnen --- ## Was läuft ### Lokal voll einsatzbereit ```bash cd /Users/till/Documents/Code/cards # 1. Dependencies (idempotent) pnpm install # 2. Postgres-Container (auf :5435, kollidiert nicht mit Mana-Plattform-:5432) pnpm docker:up # 3. Drizzle-Schema pushen cd apps/api DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' \ pnpm exec drizzle-kit push --force # 4. API starten (auf :3081) DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' \ CARDS_API_PORT=3081 \ CARDS_DSGVO_SERVICE_KEY='msk_test_dsgvo_42' \ bun run src/index.ts & # 5. Web starten (auf :3082) cd ../web && pnpm dev & # 6. Browser öffnen open http://localhost:3082 ``` Login mit beliebigem User-ID-String (Dev-Stub speichert via `sessionStorage`). Für Föderations-Endpunkte (share/receive) muss die User-ID UUID-formatiert sein, z.B. `00000000-0000-0000-0000-00000000aaaa`. Aufräumen: `kill %1 %2 && pnpm docker:down` (Daten in `infrastructure/.volumes/cards-postgres`). Vollständiger Smoke-Test-Runbook: [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md). ### Verifizierte Endpoints ``` GET /healthz → {"status":"ok"} GET /version → {"app":"cards","version":"…","build":"…"} GET /.well-known/mana-app.json → Manifest POST /api/v1/decks User-JWT CRUD GET /api/v1/decks User-JWT GET /api/v1/decks/:id User-JWT PATCH/DELETE /api/v1/decks/:id User-JWT POST /api/v1/cards User-JWT Create + Auto-Reviews GET /api/v1/cards?deck_id=… User-JWT GET/PATCH/DELETE /api/v1/cards/:id User-JWT GET /api/v1/reviews/due User-JWT Hot-Path POST /api/v1/reviews/:cardId/:subIndex/grade User-JWT FSRS-Transition POST /api/v1/share/receive User-JWT Föderations-Inbox POST /api/v1/tools/:name User-JWT cards.create | cards.search GET /api/v1/search?q=… User-JWT SearchResultEnvelope GET /api/v1/dsgvo/export?user_id=… Service-Key POST /api/v1/dsgvo/delete Service-Key ``` --- ## Pre-Flight für Phase 2 + Live-Föderation Diese Items sind **nicht autonom machbar** — du oder ein Mensch musst sie freischalten: | Item | Wer | Status | |---|---|---| | DNS für `cardecky.mana.how` reservieren (Cloudflare) | Mensch | offen | | GitHub-Repo `mana-ev/cards` anlegen + Remote pushen | Mensch | offen | | Cards in `mana-auth.apps` registrieren (Service-Key + Public-Key) | Mensch oder KI gegen laufende mana-auth | offen | | `NPM_AUTH_TOKEN` für Verdaccio in `~/.npmrc` setzen | Mensch | offen | | Cards-Manifest bei mana-share registrieren | KI gegen laufende mana-share | offen | Sobald `NPM_AUTH_TOKEN` da ist, kann der lokale Protocol-Mirror in `packages/cards-domain/src/protocol/` durch Re-Exports aus `@mana/shared-share-protocol` ersetzt werden — das ist eine 1-Liner- Änderung in `cards-domain/src/index.ts` plus Imports. --- ## Wichtige Pointer ### Konventionen + Stack - pnpm 9.15.x, Node 20+, Bun für apps/api - Tabs-Indent, single-quotes, 100-col Prettier (`.prettierrc.json`) - SvelteKit 2 + Svelte 5 (**runes-only** — kein legacy `let count = 0`) - Hono + Bun + Drizzle für API - Drizzle 0.38 / drizzle-kit 0.30 / zod 3 (gleicher Stand wie Mana-Plattform) - Tailwind 4 via `@tailwindcss/vite` (oklch-Theme + Dark-Mode-Auto) - Tests: Vitest + Hono `app.request()`, später Playwright für e2e Volle Konventionen: [`CLAUDE.md`](CLAUDE.md) ### Wichtige Dateien | Pfad | Zweck | |---|---| | [`STATUS.md`](STATUS.md) | dieses Dokument — Single Source of Truth für Status | | [`CLAUDE.md`](CLAUDE.md) | Konventionen + Architektur-Invarianten + Stack-Decisions | | [`README.md`](README.md) | Kurz-Anleitung, ein paar Befehle | | [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md) | Reproduzierbarer E2E-Lauf (curl-Sequenz) | | [`docs/LESSONS_FROM_MANA_MONOREPO.md`](docs/LESSONS_FROM_MANA_MONOREPO.md) | 15 Architektur-Lessons aus dem Read-Day, 5 Kern-Entscheidungen | | [`app-manifest.json`](app-manifest.json) | Source of Truth für Föderations-Vertrag (v1.0.0, beta-tier) | | [`packages/cards-domain/src/`](packages/cards-domain/src/) | zod-Schemas SSOT + FSRS-Adapter + Protocol-Mirror | | [`apps/api/src/`](apps/api/src/) | Hono-Routen, Drizzle-Schemas, Share-Handlers | | [`apps/web/src/`](apps/web/src/) | SvelteKit-Routes, $lib/api, $lib/auth-Stub | | [`infrastructure/docker-compose.yml`](infrastructure/docker-compose.yml) | Postgres-Container für lokal-dev | ### Cross-Repo-Dokumente (im Plattform-Repo `mana/`) | Pfad | Zweck | |---|---| | `mana/docs/playbooks/CARDS_GREENFIELD.md` | Master-Playbook (alle Phasen, Pre-Flight, Decommission) | | `mana/docs/FEDERATION.md` | Föderations-Architektur-Grundlagen | | `mana/docs/SHARE_PROTOCOL.md` | Manifest- + Envelope-Schema-Spezifikation | | `mana/docs/MANA_AUTH_FEDERATION.md` | App-Identitäts-Modell (Service-Keys, JWKS) | | `mana/docs/SHARED_PACKAGES.md` | Versions-Disziplin Klasse A/B/C | | `mana/docs/PORTS.md` | Port-Allokation (cards-api: **3081**, cards-web: **3082**) | | `mana/docs/PLAN.md` | Übergreifende mana-e.V.-Roadmap inkl. Phase 6 (Cards-Greenfield) | --- ## Git-Historie ``` 0328caa Phase 5: Föderations-Endpunkte — Cards ist föderierter Peer 89a7a92 Phase 4: Frontend-Core MVP — Decks, Cards, Study mit FSRS-Loop e3b3a2b docs: SMOKE_TEST.md — verifizierter E2E-Lauf gegen lokale Postgres 5f67bd9 Phase 3 follow-up: type-check + tests grün, ts-fsrs v5 API 45a47e0 Phase 3: Domain-Modell + Decks/Cards/Reviews-CRUD 8605b1b Phase 0+1: Repo-Skelett für Cards-Greenfield ``` `git remote -v` ist leer — Repo lebt lokal, GitHub-Remote folgt mit Pre-Flight (`mana-ev/cards`). --- ## Architektur-Subtilitäten, die nicht offensichtlich sind ### 1. Reviews bleiben PLAINTEXT Der FSRS-Scheduler quert täglich `due <= now`. Wenn die Reviews verschlüsselt wären, müsste man jeden Tag N Reviews entschlüsseln nur um zu wissen welche fällig sind. Geht nicht. → Wenn Encryption nachgerüstet wird: nur `cards.fields` (front/back) und `decks.{name,description}` werden encrypted, Reviews bleiben plaintext. Pattern aus mana-monorepo bestätigt (`crypto/registry.ts` hat `cardReviews` plaintext-allowlisted). ### 2. SubIndex-Granularität pro Card-Type Eine `basic-reverse`-Karte hat **2** Reviews (sub_index 0 = front→back, sub_index 1 = back→front). Cloze hat 1 Review pro Cluster-Index. Beim Card-Insert werden alle initialen Reviews in **einer Transaktion** mit angelegt — siehe `apps/api/src/routes/cards.ts` POST-Handler. `subIndexCount(type)` in `@cards/domain` ist die Single Source of Truth. ### 3. Lokales Protocol-Mirror `packages/cards-domain/src/protocol/` enthält eine **TEMPORARY**- Kopie der Schemas aus `@mana/shared-share-protocol`. Solange Verdaccio nicht offen ist (kein `NPM_AUTH_TOKEN`), halten wir sie hier lokal. → Drift-Risiko: bei jedem Update der mana-Spec MUSS diese Datei nachgezogen werden, bis der Swap erfolgt. Marker-Kommentar oben in jeder Mirror-Datei. ### 4. Inbox-Deck wird auto-erstellt Eingehende Shares (über `/share/receive`) landen alle in einem auto-erstellten "Inbox"-Deck pro User. `ensureInboxDeck(db, userId)` prüft auf Existenz oder legt es neu an. User kann Karten später in echte Decks umsortieren. → Naming-Hinweis: Wenn ein User schon ein Deck namens "Inbox" hat, greift unser `ensureInboxDeck` darauf zu. Das ist gewollt (idempotent). ### 5. Dev-Auth via X-User-Id ist EXPLICIT temporär `apps/api/src/middleware/auth.ts` und `apps/web/src/lib/auth/dev-stub.svelte.ts` sind beide klar als „Phase 2 ersetzt durch echtes JWT" markiert. Beim Swap auf mana-auth: - API: `@mana/shared-hono` `authMiddleware()` mit JWKS-Cache - Web: `@mana/shared-auth`-Login-Flow gegen `auth.mana.how` - Beide aus Verdaccio (= NPM_AUTH_TOKEN-blockiert) ### 6. Cards-Modul in mana-monorepo wird nach Live-Gang **gelöscht** Strategie-B-Konsequenz: nach `cardecky.mana.how` live + 2 Wochen Test folgt ein Decommission-PR in mana-monorepo, der `apps/mana/.../modules/cards/`, `packages/cards-core/`, `services/cards-server/` (Marketplace-Backend) und alle DB-Schemas in `mana_platform.cards.*` entfernt. Keine zwei Cards-Welten parallel. → Diese Entscheidung ist im Greenfield-Playbook festgehalten und im älteren `CARDS_CUTOVER.md` (jetzt überholt) als Strategie-A-Variante diskutiert worden. --- ## Wenn du gerade neu bist — Onboarding-Sequenz 1. **Lies dieses Dokument zu Ende** (5 Min). 2. **Lies `CLAUDE.md`** (Konventionen + Stack-Decisions, 3 Min). 3. **Lies `mana/docs/playbooks/CARDS_GREENFIELD.md`** (Master-Plan, 10 Min). 4. **Optional:** `docs/LESSONS_FROM_MANA_MONOREPO.md` für Domain-Verständnis. 5. **Verifiziere lokal:** `docs/SMOKE_TEST.md` durchspielen — wenn alle 7 Schritte grün, ist die Umgebung in Ordnung. 6. **Memory-Check (KI):** falls du auto-memory hast, prüfe `memory/project_phasenstand.md` für eventuell neueren Stand. --- ## Was als Nächstes ansteht (Vorschläge) In Reihenfolge meiner Empfehlung: 1. **Phase 8 (Anki-Import)** — autonom, klar abgegrenzte Funktionalität, ~5–7 Tage. Brauchbar als Migrations-Hilfe für künftige User. 2. **Card-Edit-Page + Inbox-Banner im Frontend** — 1–2 Tage Polish. 3. **Pre-Flight aktiv abräumen** — Mana-Plattform-Stack + mana-auth live, dann Cards als App registrieren (Phase 2). 4. **Phase 6 (Subscriptions)** — braucht laufende mana-credits. 5. **Phase 9 (Polish)** — Settings, Account, DSGVO-UI, Statistik, i18n. Was nicht autonom geht: Phase 2 (Auth-Föderation), Phase 7 (mana-mcp- Live), Phase 10 (Mac-Mini-Deploy) — alle hängen an Pre-Flight-Items. --- **Wenn du dieses Dokument liest und etwas hier nicht stimmt, ist das Dokument schuld, nicht der Code. Update es.**