Bis heute schrieb `emit()` in apps/api/src/lib/sync2-pageta.ts Klartext:
`clientsFor(jwt)` baute den Vault-Crypto-Provider und `const { http } = ...`
warf ihn weg. Der Web-Client verschluesselt seit `applySyncConsent` — also
lag dasselbe Event-Type in mana_sync_v2.pageta.events in beiden Formen,
329 Zeilen davon im Klartext, darunter `content` und `userNote` ganzer
Artikel. Betroffene Aufrufer: MCP-Tool save_article, imports-worker,
routes/articles.ts.
Warum verschluesseln hier richtig und nicht bloss moeglich ist: Client und
API benutzen denselben `createMasterKeyProviderFromVault`. Der Lesepfad
(pullAll -> decryptPayload) holt den Schluessel ohnehin. Verschluesseln
beim Schreiben oeffnet also KEINE neue Vertrauensgrenze, es stellt die
fehlende Symmetrie her. Die verworfenen Alternativen: die Felder fuer
nicht-vertraulich erklaeren (Lesehistorie und Notizen sind das Gegenteil)
oder Key-Escrow (gibt Zero-Knowledge auf).
Fail-closed: steht der Vault im zero-knowledge-Modus, wirft
createMasterKeyProviderFromVault und es wird NICHTS geschrieben. Ein
Server, der nicht verschluesseln kann, darf nicht ersatzweise Klartext
ablegen.
Der Altbestand bleibt lesbar — decryptPayload reicht Klartext durch.
CLAUDE.md Invariante 4 sagte noch "Encryption initial AUS ... heute
plaintext". Sie war da schon falsch und hat den Fehler drei Monate am
Leben gehalten; jetzt beschreibt sie den Ist-Zustand samt Fail-closed-
Regel und traegt den Vorfall als Warnung.
Vier Regressionstests, gegengeprueft: ohne den Fix fallen drei davon, und
der Fehlertext zeigt woertlich den Artikeltext im Envelope.
6.6 KiB
CLAUDE.md — pageta repo
Guidance für Claude Code in diesem Repository.
Wenn du gerade neu bist: lies zuerst
STATUS.md— dort steht der aktuelle Phasen-Stand und was offen ist. Dieses CLAUDE.md ist nur die Konventions- und Architektur-Referenz.
Was dieses Repo ist
Pageta — eigenständige Reader-Webapp des Vereins mana Aktiver Kern-Use-Case:
- Lese-Liste — Pocket-Style Save-it-later mit Highlights, Tags,
Bulk-Import bis 200 URLs, einmaliger Blog/Feed-Archiv-Import
(RSS-Paging + optional WP-Sitemap, bis 1000 Artikel) unter
/lese-liste/settings.
Feed (kuratierte News aus dem Plattform-Pool) und Recherche
(RSS-Feed-Discovery) sind seit 2026-06-01 dormant — aus der Nav
genommen, Code bleibt im Repo. Feed wegen Werte-Spannung zum
Engagement-Feed-Verbot; Recherche, weil angepinnte Feeds keinen Lese-Ort
hatten (der Feed fetchte customFeeds nie). Begründung +
Revival-Bedingung (mana-discover-Andockung): docs/FEED.md.
Pageta ist eine Peer-App im mana-Ökosystem: redet HTTP/JWT mit
mana-auth, mana-news-pool, mana-research, mana-share,
mana-credits. Eigene DB mana_pageta, eigenes Frontend, eigene
Deploy-Pipeline.
HTTP/JWT ┌──────────────┐
mana ◄─────────────── │ pageta/ │ Postgres `mana_pageta`
Plattform │ (this repo) │ pageta.mana.how
(auth/credits/share/etc.) └──────────────┘
Status
LIVE 🚀 + voll-featured (λ-0..λ-6 ✅). App unter app.pageta.com,
API unter pageta-api.mana.how, Marketing-Landing auf pageta.com.
Aktueller Detail-Stand + offene Punkte: STATUS.md.
Konventionen
- pnpm 9.15.x, Node 20+, Turborepo 2.x
- Tabs für Indent, single quotes, 100-col Prettier
(
.prettierrc.jsonist die Source of Truth) - Stack: SvelteKit 2 + Svelte 5 (runes-only) für Web,
Hono + Bun + Drizzle für API, Postgres mit
pgSchema('reader') - Drizzle 0.38 / drizzle-kit 0.30 / zod 3 — Parität zur managarten- und mana-Plattform-Toolchain
- JWT-Validation lokal via JWKS-Cache (5 min), niemals Live-Call zu mana-auth pro Request
- Service-to-Service-Calls via
X-Service-Key - Tests: Vitest für Unit/Integration
- Formatierung:
pnpm run formatvor jedem Commit
Architektonische Invarianten
- Event-sourced, local-first. User-owned-Daten (Lese-Liste,
Highlights, Tags, Warnungen) laufen seit der G-3-Migration
(2026-05-19) über
sync2.mana.howmit lokalem EventLog/Outbox via@mana/event-sync— nicht mehr server-authoritative. HTTP-only gegen die pageta-API bleiben nur stateless-Compute (Feed,/articles/extract/preview,/summarize,/research). Die retired user-CRUD-Routen antworten 410. - Eigene Postgres-DB
mana_pagetamit Schema-Isolation viapgSchema('reader'). - Föderation über
@mana/shared-app-tpl. Pflicht-Endpoints (/.well-known/mana-app.json,/healthz,/api/v1/share/receive,/api/v1/tools/:name,/api/v1/search,/api/v1/dsgvo/...) kommen aus dem geteilten Boilerplate. - Encryption ist AN — auf JEDEM Schreibpfad. Payloads der
user-owned-Events werden mit dem Vault-Master-Key des Users
verschlüsselt (
enc:1:…). Das gilt nicht nur für den Web-Client (applySyncConsentinapps/web/src/lib/sync.svelte.ts), sondern genauso für den serverseitigen Pfad inapps/api/src/lib/sync2-pageta.ts(MCP-Tools,imports-worker,routes/articles.ts). Beide benutzen denselbencreateMasterKeyProviderFromVault— ein Server-Schreiber öffnet also keine zusätzliche Vertrauensgrenze, aber er muss verschlüsseln. Fail-closed: kann der Vault den Schlüssel nicht liefern (Zero-Knowledge-Modus), schlägt der Schreibvorgang fehl — niemals ersatzweise Klartext ablegen.Diese Invariante hiess bis 2026-08-02 „Encryption initial AUS … heute plaintext". Sie war da schon falsch: der Client verschlüsselte längst, nur der API-Pfad nicht. Ergebnis waren 329 Klartext-Events (mit
contentunduserNote) inmana_sync_v2.pageta.events. Wer die Verschlüsselung anfasst, aktualisiert diesen Absatz mit. - Keine externe SaaS, wo vermeidbar. Erlaubte Ausnahmen kommen über mana-Plattform.
- Aggregator-Policy gilt für kuratierten Feed-Pool und
RSS-Recherche — siehe
mana/docs/AGGREGATOR_POLICY.md. User-AgentManaPageta/0.1 (+https://pageta.mana.how), robots.txt-Respekt.
Repo-Struktur
pageta/
├── apps/
│ ├── api/ Hono+Bun-Backend (Port 3099)
│ └── web/ SvelteKit-Frontend (Port 3100)
├── packages/
│ └── pageta-domain/ Pure-TS: Feed-Engine-Scoring, Types,
│ Source-Meta-Liste
├── infrastructure/ docker-compose, tunnel-config
├── docs/ DOMAIN.md, etc.
├── app-manifest.json Source of Truth für Föderation
└── .github/workflows/ ci.yml, deploy.yml
@mana/* Konsumation
Pageta zieht alle Shared-Pakete aus Verdaccio (pkg.mana.how):
@mana/shared-share-protocol— Manifest, Envelope-Schemas@mana/shared-app-tpl— Pflicht-Endpoint-Boilerplate@mana/shared-hono— Auth-Middleware, Health-Routes, Error-Handler@mana/shared-auth— Client-SDK für Login-Flow@mana/shared-rss— RSS-Parsing + Readability (nach Lift-A; bis dahin vendored inapps/api/src/lib/shared-rss/)@mana/shared-types,@mana/shared-icons,@mana/shared-i18n,@mana/shared-theme,@mana/shared-tailwind@mana/themes— Theme-Variant (TBD)@mana/shared-ui-2— Greenfield-Komponenten
Wichtige Cross-Repo-Doks
mana/docs/FEDERATION.md— Architektur-Grundlagenmana/docs/SHARE_PROTOCOL.md— Manifest- und Envelope-Schemasmana/docs/MANA_AUTH_FEDERATION.md— App-Identitäts-Modellmana/docs/SHARED_PACKAGES.md— Versionierungs-Disziplinmana/docs/PORTS.md— Port-Allokation (pageta-api: 3099, pageta-web: 3100)mana/docs/AGGREGATOR_POLICY.md— Crawl- + Source-Policy
Lokal entwickeln
pnpm install
pnpm docker:up # Lokales Postgres :5443
pnpm --filter @pageta/api db:push
pnpm dev # api + web parallel
Voraussetzungen:
- mana-Plattform-Stack lokal laufend (mana-auth zumindest für Login- Test), oder Auth via JWT-Stub in DEV.
- Verdaccio-Token in
~/.npmrc(npm login --registry=https://pkg.mana.how)