Cross-App-Hook nach Pageta-Pattern. context.app='wordeck' im Payload.
Bewusst NICHT im dev-stub-Branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Behebt live verifiziertes Auth-Bypass auf cardecky-api.mana.how
(X-User-Id → founder-Tier) und zieht im selben Patch das fehlende
Operations-/Compliance-Fundament nach.
* Auth-Middleware fail-secure: opt-in via CARDS_AUTH_DEV_STUB="true"
(war opt-out, Default true). Compose-Default flipped auf "false",
NODE_ENV="production" für cards-api ergänzt, env-Template
dokumentiert. vitest.config.ts + tests/setup.ts aktivieren den
Stub gezielt für Test-Suiten.
* Security-Headers: Hono secureHeaders() in apps/api,
SvelteKit hooks.server.ts mit X-Frame/X-Content-Type/Referrer/
HSTS in apps/web. CSP bewusst ausgelassen — eigener Sprint.
* CORS-localhost-Whitelist nur außerhalb Prod.
* Rate-Limiting (in-memory sliding window, dependency-frei) auf
share.receive 60/min/IP, media.upload 30/min/user,
decks.generate + decks.from-image 10/min/user, dsgvo.* 10/min/IP.
* Health-Endpoint mit echter DB- und MinIO-Probe; /healthz bleibt
Liveness, /healthz/details ist Readiness mit 503 bei Failure.
* DSGVO-Honesty: storage_ok + storage_error im Response (statt
schluckend console.warn), Account-UI zeigt Fehler-Toast.
* Audit-Log: strukturierte JSON-Zeile (kind: "audit") auf stdout
für /dsgvo/export, /dsgvo/delete, /me/export, /me/delete.
* Bug-Fix: duplizierte case "multiple-choice"-Clause in fsrs.ts.
Verifiziert: apps/api 17 Files / 104 Tests grün, apps/web check
0 errors. Deploy auf Mac Mini steht noch aus.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routes (additiv unter /api/v1/marketplace/*):
- POST/GET /authors/me — eigenes Author-Profil anlegen/updaten/lesen
- GET /authors/:slug — public Profile-Lookup (banned-reason gestrippt)
- POST /decks — Deck-Init (Slug-Validation + Pflicht-Author-Profil +
CHECK auf paid + Pro-License)
- POST /decks/:slug/publish — Versions-Snapshot mit per-Karte
cardContentHash aus @cards/domain, per-Version-Hash, AI-Mod-Stub-Log,
atomarer latest_version_id-Bump in Drizzle-Transaction
- PATCH /decks/:slug — Metadaten-Update (Owner-Only)
- GET /decks/:slug — Public-Detail mit optional-auth-Middleware
Geport aus cards-decommission-base:services/cards-server/, mit
Greenfield-Anpassungen:
- Hashing über @cards/domain.cardContentHash (gemeinsame SoT
zwischen privatem cards.cards und marketplace.deck_cards), per-
Version-Hash als SHA-256 über sortierte Karten-Hashes mit Ord-Prefix
- AI-Moderation als R2-Stub (pass+rationale+model='stub'),
echte mana-llm-Anbindung in späterer Welle
- Auth-Middleware-Shape an Greenfield (userId/tier/authMode in
c.get(...) statt user-Object), optional-auth als Schwester für
anonymen Public-Read
- Hono-typing: outer Marketplace-Decks-Router ist Partial<AuthVars>
weil Public-GET kein JWT braucht; Auth-Subroute ist strict
Lese-Referenz:
- 3331 LOC altes cards-server-Code (routes, services, middleware,
lib) unter docs/marketplace/archive/code/ archiviert. Read-only,
nicht im Build-Path.
Verifikation:
- 16 neue Vitest-Tests (Slug + Version-Hash), 72 gesamt grün
- type-check 0 errors
- E2E-Smoke gegen lokale cards-api: Cardecky-Author + Deck
r2-stoische-ethik mit 3 Karten v1.0.0 (basic + basic + cloze),
per-Karten-Hashes geschrieben, ai_moderation_log-Row da, semver-409
+ paid-422-Errors verifiziert. Smoke-Daten danach aufgeräumt.
Verbleibend für R3+: Discovery (explore + search), Engagement (stars/
subscribe/fork), Smart-Merge mit FSRS-State-Erhalt; danach R4 PRs +
Card-Discussions, R5 Frontend-Routes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
auth-middleware setzt jetzt zusätzlich `tier`-Claim aus dem JWT
(public/beta/alpha/founder, default public bei unbekannt) und
`authMode` (jwt|dev-stub). Dev-Stub bekommt founder-Tier — Tests
und der Anki-Importer (X-User-Id) bleiben unbeeinträchtigt.
`requireTier(min)`-Middleware-Helper für künftige Premium-Pfade,
liefert 403 mit `{need, have}` bei Tier-Unterlauf. Aktuell nicht
auf Cards-Endpoints angewendet — der MVP ist tier-frei für jeden
authentifizierten User. Plumbing ist da, sobald Premium-Features
auftauchen (AI-Bulk-Convert, Image-Generation, …).
services/credits-client.ts: schmaler Service-to-Service-Wrapper um
mana-credits (https://credits.mana.how/api/v1/internal/*) für
balance / reserve / commit / refund-reservation. Auth via
X-Service-Key (Cards-App-Key aus mana-auth, env
CARDS_MANA_SERVICE_KEY). Cards-MVP nutzt das nicht produktiv —
ist der Pre-Wire-Layer für später.
56 API-Tests grün, type-check sauber.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cards-api akzeptiert jetzt zwei Auth-Quellen:
1. Authorization: Bearer <jwt> — JWT aus mana-auth, gegen
/api/auth/jwks gecached, sub→userId
2. X-User-Id: <uuid> — Dev-Stub, bleibt aktiv
Schalter: CARDS_AUTH_DEV_STUB=false deaktiviert den Dev-Stub
hart (für die spätere Phase-10b-Cutover-Stufe). Default behält
beide Pfade — Tests + Anki-Importer + Sprint-9-User-Smokes
laufen unverändert weiter.
Cards-App ist heute in mana-auth (auth.mana.how) registriert
(app_id='cards', ownership_kind='verein', status='active'),
Service-Key generiert + per direktem DB-Insert in
auth.app_service_keys gelegt (Migrations-Tabellen waren in
prod-DB noch nicht angewendet, jetzt nachgezogen).
56 API-Tests grün, jose als neue Dependency.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>