docs(mana/web): roundup data layer audit through encryption phase 6

Updates DATA_LAYER_AUDIT.md to reflect everything that landed since
the last refresh (which stopped at Sprint 4). The doc is now the
authoritative status surface for both audit-sprint and encryption-
sprint progress.

What's new in the doc:

  Status table (Section 0)
    Adds the missing post-Sprint 4 work and the full encryption phase
    table:
      - Sprint 4+ Listeners (575c5c36f)
      - Test-Fix sprint (ae648650e)
      - Backlog 1/2/3 — Indexed queries V9, SSE pipeline, Activity log
      - Encryption phases 1-6 with commits
    The "tests passing" line bumps to 262/262 across 20 files.

  Architecture diagram (Section 1)
    Shows how a write now flows through encryptRecord BEFORE the
    Dexie hook, and how reads route through decryptRecords on the
    way out of liveQuery. Adds a second diagram for the Encryption
    Pipeline (login → vault unlock → MemoryKeyProvider → wrap/
    unwrap → IndexedDB) that wasn't documented anywhere before.

  File map (Section 1)
    Splits into "Datenschicht" and "Encryption" sub-tables. The
    encryption table lists all 17 new files across crypto/, mana-auth
    services, the settings page and the onboarding banner with a
    one-line purpose for each.

  Eckdaten
    Schema versions 1-10 (was 1-7), and the new "At-Rest-Encryption"
    bullet noting 22+ tables.

  Critical fixes table (Section 2 🔴)
    #4 "Keine Verschlüsselung im Browser" flips from "noch offen" to
    "Encryption Phase 1-6 " with the one-line summary.

  🟢 backlog status table
    #13 SSE buffer flips to  via Backlog 2.
    #14 Tombstone cleanup loop flips to  via Sprint 4+.
    #18 Activity log flips to  via Backlog 3.

  New Section 5 — Encryption Pipeline
    Documents the trust model end-to-end:
      - Where each piece lives (mana-auth env KEK, wrapped MK in
        encryption_vaults, browser sessionStorage, IndexedDB blobs)
      - The complete table-by-table list of WHAT is encrypted and
        WHAT stays plaintext, with the per-table reasoning for the
        plaintext exceptions (dreamSymbols.name for indexed lookup,
        cycleDayLogs.symptoms for Set-diff, inventar.invItems.name
        for index, etc.)
      - "Was Mana technisch (nicht) sehen kann" — three-level honest
        disclosure: never / theoretically / structurally

  Section 6 — Backlog
    Reorders by remaining encryption work first:
      1. Phase 7 cross-module title coverage (timeBlocks coupling)
      2. Phase 7 server-pushed records (picture/storage/music)
      3. Phase 7 storeless modules (nutriphi/uload/context/questions)
      4. Phase 8 recovery code opt-in for true zero-knowledge
      5. Conflict viz UI
      6. Composite indexes for multi-account
      7. V3 migration tests

  Stärken (Section 7)
    Adds the encryption-specific properties: dedicated crypto/ sub-
    module entkoppelt vom sync layer, vault-singleton via
    vault-instance.ts, dimension "Vertraulichkeit" added to the
    final tagline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-07 20:03:43 +02:00
parent 28395b313d
commit 4bdf4238ce

View file

@ -1,7 +1,7 @@
# Mana Web App Data Layer Audit
> **Initial Audit:** 2026-04-07
> **Last Update:** 2026-04-07 (Sprint 4 abgeschlossen)
> **Last Update:** 2026-04-07 (Encryption Phase 16 abgeschlossen)
> **Scope:** `apps/mana/apps/web/src/lib/data/*` und `src/lib/modules/*` (Local-First Layer der Unified Mana Web App)
> **Ziel:** Funktionsweise dokumentieren, Schwachstellen aufdecken, priorisierte Refactor-Roadmap.
@ -9,17 +9,36 @@
## 0. Status-Übersicht
| Sprint | Thema | Status | Commit |
| ------- | ------------------------------------------------------------- | ------ | ----------- |
| 0 | Audit-Bericht | ✅ | `b900df5ee` |
| 1 | Datenintegrität — Field-LWW, Retry, Atomare Cascades | ✅ | `090953882` |
| 2.1+2.3 | Auth-aware Data Layer + Guest→User Migration | ✅ | `28942abed` |
| 2.2 | PostgreSQL Row-Level-Security auf `sync_changes` | ✅ | `a9529bcf1` |
| 3 | Type-Safe Sync Protocol + Runtime Validation + 20 Unit Tests | ✅ | `9e0ade4c0` |
| 3+ | Vitest-Toolchain Aufräum (5 Versionen → 1) | ✅ | `e974761e8` |
| 4 | Per-Table Lock + Quota Handling + Telemetry + Indexed Queries | ✅ | `733dca45f` |
### Audit-Sprints (Datenschicht-Härtung)
**Test-Status:** 20/20 sync.test.ts grün, vitest@4.1.3 workspace-weit unifiziert.
| Sprint | Thema | Status | Commit |
| ------------ | ------------------------------------------------------------- | ------ | ----------- |
| 0 | Audit-Bericht | ✅ | `b900df5ee` |
| 1 | Datenintegrität — Field-LWW, Retry, Atomare Cascades | ✅ | `090953882` |
| 2.1+2.3 | Auth-aware Data Layer + Guest→User Migration | ✅ | `28942abed` |
| 2.2 | PostgreSQL Row-Level-Security auf `sync_changes` | ✅ | `a9529bcf1` |
| 3 | Type-Safe Sync Protocol + Runtime Validation + 20 Unit Tests | ✅ | `9e0ade4c0` |
| 3+ | Vitest-Toolchain Aufräum (5 Versionen → 1) | ✅ | `e974761e8` |
| 4 | Per-Table Lock + Quota Handling + Telemetry + Indexed Queries | ✅ | `733dca45f` |
| 4+ Listeners | Toast + Sentry + Tombstone-Scheduler | ✅ | `575c5c36f` |
| Test-Fix | base-client + dashboard + content/help unbreaked | ✅ | `ae648650e` |
| Backlog 1 | Indexed Recent-Queries V9 (4 Dashboard-Widgets) | ✅ | `42c9eb1e1` |
| Backlog 2 | SSE Streaming Pipeline | ✅ | `ad0215863` |
| Backlog 3 | Activity Log + Cleanup-Scheduler | ✅ | `82559f684` |
### Encryption-Sprints (Phase 16)
| Phase | Thema | Status | Commit |
| --------- | ---------------------------------------------------------------- | ------ | ----------- |
| 1 | Foundation: AES-GCM-256, KeyProvider, Registry, 31 Unit-Tests | ✅ | `1ba5948ce` |
| 2 | mana-auth Server Vault: encryption_vaults + RLS + KEK + 11 Tests | ✅ | `e9915428c` |
| 3 | Client Wire-up: vault-client, record-helpers, layout integration | ✅ | `354cbcb17` |
| 4 | Pilot: notes table mit 8 End-to-End Tests | ✅ | `bed08a1aa` |
| 5 | Rollout: chat, dreams, memoro, contacts, cycles, finance | ✅ | `af92720a6` |
| 6.1 | Rollout: cards, presi, inventar, planta | ✅ | `73f294b29` |
| 6.2 + 6.3 | Settings UI (`/settings/security`) + Encryption Intro Banner | ✅ | `6b8e2c717` |
**Test-Status:** 20 test files, 262/262 tests passing. Vitest@4.1.3 workspace-weit unifiziert. **22+ Tabellen mit At-Rest-Encryption live**, deckt **>85% der user-getippten Bytes** ab.
---
@ -31,14 +50,20 @@ Die Mana Web App nutzt eine **Local-First-Architektur** mit einer einzigen Dexie
User-Aktion (z.B. Task anlegen)
Module-Store schreibt direkt in Dexie-Tabelle
(z.B. taskTable.add({...}))
Module-Store schreibt einen Plain-Record (z.B. notesStore.createNote)
─ snapshot via toX() für die optimistische UI-Antwort
─ encryptRecord(tableName, record) wrapt die konfigurierten
Felder mit AES-GCM-256 (Encryption Phase 46)
table.add(encryptedRecord) → Dexie
Dexie Hook (database.ts)
─ stempelt userId aus current-user.ts (Sprint 2.1)
─ stempelt __fieldTimestamps für jedes Feld (Sprint 1)
─ schreibt _pendingChanges via trackPendingChange (Sprint 4.2)
─ schreibt _activity via trackActivity (Backlog 3)
─ feuert Trigger / Automation-Suggestions
@ -48,10 +73,11 @@ Sync Engine (sync.ts) debounced 1 s
POST /sync/{appId} → mana-sync (Go) → PostgreSQL (sync_changes mit RLS, Sprint 2.2)
(Server sieht ciphertext-Blobs für encrypted Felder, kein Plaintext)
Andere Clients holen Changes via:
─ SSE-Stream /sync/{appId}/stream
─ SSE-Stream /sync/{appId}/stream (pipelined parser, Backlog 2)
─ Polling Pull /sync/{appId}/pull (alle 30 s)
@ -63,31 +89,94 @@ applyServerChanges() (sync.ts)
─ emitSyncTelemetry(...) (Sprint 4.3)
liveQuery (Dexie) → Svelte 5 reaktiv → UI
liveQuery (Dexie) → decryptRecords(tableName, results) → Svelte 5 → UI
```
### Encryption Pipeline (Phase 16)
```
Login (mana-auth) → JWT
vault-client.unlock() GET /api/v1/me/encryption-vault/key
mana-auth EncryptionVaultService
─ Liest auth.encryption_vaults mit RLS-Scope (set_config app.current_user_id)
─ Unwrap wrapped_mk + wrap_iv mit dem KEK (env MANA_AUTH_KEK)
─ Audit-Eintrag in encryption_vault_audit
Raw 32-byte master key → HTTPS → Browser
importMasterKey() → CryptoKey (non-extractable) → MemoryKeyProvider.setKey()
sessionStorage flag 'mana-vault-unlocked' = 1
Erste Schreib-/Lese-Operation: encryptRecord/decryptRecord lesen
getActiveKey() aus dem Provider und wrappen/unwrappen die Felder
gemäß ENCRYPTION_REGISTRY.
Logout / Tab-Close → MemoryKeyProvider.setKey(null) → Cyphertext bleibt
auf der Platte, ist ohne erneuten Login nicht lesbar.
```
### Schlüsseldateien
| Datei | Zweck |
| ----------------------------------- | -------------------------------------------------------------------------- |
| `src/lib/data/database.ts` | Dexie-Schema, Hooks, SYNC_APP_MAP, beginApplyingTables, trackPendingChange |
| `src/lib/data/sync.ts` | Sync Engine, applyServerChanges, fetchWithRetry, push/pull/SSE |
| `src/lib/data/current-user.ts` | Single source of truth für aktive userId (Sprint 2.1) |
| `src/lib/data/guest-migration.ts` | Guest → User Datenmigration beim ersten Login (Sprint 2.3) |
| `src/lib/data/quota-detect.ts` | Quota-Detection ohne Dexie-Cycle (Sprint 4.2) |
| `src/lib/data/quota.ts` | cleanupTombstones, withQuotaRecovery (Sprint 4.2) |
| `src/lib/data/sync-telemetry.ts` | CustomEvent-Bus für Push/Pull Lifecycle (Sprint 4.3) |
| `src/lib/data/sync.test.ts` | 20 Unit Tests gegen fake-indexeddb (Sprint 3.3) |
| `src/lib/data/cross-app-queries.ts` | Indexed Range Queries für Dashboard-Widgets (Sprint 4.4) |
#### Datenschicht
| Datei | Zweck |
| -------------------------------------- | ----------------------------------------------------------------------------- |
| `src/lib/data/database.ts` | Dexie-Schema, Hooks, beginApplyingTables, trackPendingChange, trackActivity |
| `src/lib/data/module-registry.ts` | SYNC_APP_MAP / TABLE_TO_SYNC_NAME aggregiert aus per-Modul `module.config.ts` |
| `src/lib/data/sync.ts` | Sync Engine, applyServerChanges, fetchWithRetry, push/pull/SSE Pipeline |
| `src/lib/data/current-user.ts` | Single source of truth für aktive userId (Sprint 2.1) |
| `src/lib/data/guest-migration.ts` | Guest → User Datenmigration beim ersten Login (Sprint 2.3) |
| `src/lib/data/quota-detect.ts` | Quota-Detection ohne Dexie-Cycle (Sprint 4.2) |
| `src/lib/data/quota.ts` | cleanupTombstones, withQuotaRecovery (Sprint 4.2) |
| `src/lib/data/sync-telemetry.ts` | CustomEvent-Bus für Push/Pull Lifecycle (Sprint 4.3) |
| `src/lib/data/data-layer-listeners.ts` | Quota-Toast + Sentry-Bridge + Tombstone/Activity Cleanup-Loop (Sprint 4+) |
| `src/lib/data/activity.ts` | Read-API + prune für `_activity` (Backlog 3) |
| `src/lib/data/cross-app-queries.ts` | Indexed Range Queries für Dashboard-Widgets (Sprint 4.4) |
| `src/lib/data/sync.test.ts` | 20 Unit Tests gegen fake-indexeddb (Sprint 3.3) |
| `src/lib/data/activity.test.ts` | 6 Tests für Activity Log (Backlog 3) |
#### Encryption (Phase 16)
| Datei | Zweck |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `src/lib/data/crypto/aes.ts` | Web Crypto AES-GCM-256 wrap/unwrap, format-versionierte Envelope `enc:1:<iv>.<ct>` |
| `src/lib/data/crypto/key-provider.ts` | KeyProvider Interface, NullKeyProvider (default), MemoryKeyProvider |
| `src/lib/data/crypto/registry.ts` | Strict allowlist `ENCRYPTION_REGISTRY` aller Tabellen × verschlüsselten Felder |
| `src/lib/data/crypto/record-helpers.ts` | encryptRecord / decryptRecord / decryptRecords (registry-driven) |
| `src/lib/data/crypto/vault-client.ts` | Browser-HTTP-Client für /me/encryption-vault/{init,key,rotate} |
| `src/lib/data/crypto/vault-instance.ts` | Lazy-Singleton, von Layout + Settings-Page gemeinsam genutzt |
| `src/lib/data/crypto/aes.test.ts` | 31 Unit-Tests für Crypto-Primitives |
| `src/lib/data/crypto/record-helpers.test.ts` | 12 Tests für encrypt/decrypt-Roundtrip + locked-Vault-Verhalten |
| `src/lib/data/crypto/vault-client.test.ts` | 12 Tests gegen `globalThis.fetch`-Mock |
| `src/lib/modules/notes/notes-encryption.test.ts` | 8 End-to-End Tests des Pilots gegen fake-indexeddb |
| `services/mana-auth/src/db/schema/encryption-vaults.ts` | Drizzle-Schema für `auth.encryption_vaults` + audit |
| `services/mana-auth/sql/002_encryption_vaults.sql` | Migration mit `ENABLE + FORCE ROW LEVEL SECURITY` |
| `services/mana-auth/src/services/encryption-vault/kek.ts` | KEK-Loader, AES-GCM wrap/unwrap des Master Keys |
| `services/mana-auth/src/services/encryption-vault/index.ts` | EncryptionVaultService mit init/getMasterKey/rotate |
| `services/mana-auth/src/routes/encryption-vault.ts` | Hono-Routen `POST /init`, `GET /key`, `POST /rotate` |
| `services/mana-auth/src/services/encryption-vault/kek.test.ts` | 11 Bun-Tests für KEK-Crypto |
| `apps/mana/apps/web/src/routes/(app)/settings/security/+page.svelte` | Settings UI: Vault-Status, Encrypted-Tables-Liste, Rotate-Button |
| `apps/mana/apps/web/src/lib/components/EncryptionIntroBanner.svelte` | Einmaliges Onboarding-Banner beim ersten Vault-Unlock |
### Eckdaten
- **120+ Collections** in einer einzigen IndexedDB
- **Schema-Versionen** 17 (mit `cycles` und `events` aus parallelen Modul-Scaffolds)
- **Schema-Versionen** 110 (V9 fügte updatedAt-Indexes hinzu, V10 die `_activity` Tabelle)
- **Eager Apps**: mana, todo, calendar, contacts, tags, links — syncen beim Start
- **Lazy Apps**: starten Sync erst beim ersten Modul-Besuch via `ensureAppSynced()`
- **Conflict Resolution**: ✅ echter Field-Level LWW via `__fieldTimestamps`
- **Soft Delete** ist Standard via `deletedAt`
- **At-Rest-Encryption**: AES-GCM-256, server-wrapped Master Key, 22+ Tabellen aktiv
---
@ -95,13 +184,13 @@ liveQuery (Dexie) → Svelte 5 reaktiv → UI
### 🔴 Kritisch — alle erledigt
| # | Problem | Lösung | Sprint |
| --- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ---------- |
| 1 | Field-Level LWW falsch implementiert | `__fieldTimestamps` Hidden Field via Dexie hooks, per-Feld Vergleich in `applyServerChanges` | 1 ✅ |
| 2 | Keine Client-Side User Isolation | `current-user.ts` als Single Source, Dexie creating-hook auto-stamped, updating-hook strippt userId, backend RLS | 2.1+2.2 ✅ |
| 3 | Schema Migrationen ohne Tests | 20 Unit-Tests gegen fake-indexeddb sichern den kritischen apply-Pfad ab | 3.3 ✅ |
| 4 | Keine Verschlüsselung im Browser | **(noch offen — UX-Entscheidung)** | — |
| 5 | Keine Guest → User Migration | `guest-migration.ts` walkt sync-Tabellen, re-inserted unter neuer userId, $effect im Layout | 2.3 ✅ |
| # | Problem | Lösung | Sprint |
| --- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ----------------------- |
| 1 | Field-Level LWW falsch implementiert | `__fieldTimestamps` Hidden Field via Dexie hooks, per-Feld Vergleich in `applyServerChanges` | 1 ✅ |
| 2 | Keine Client-Side User Isolation | `current-user.ts` als Single Source, Dexie creating-hook auto-stamped, updating-hook strippt userId, backend RLS | 2.1+2.2 ✅ |
| 3 | Schema Migrationen ohne Tests | 20 Unit-Tests gegen fake-indexeddb sichern den kritischen apply-Pfad ab | 3.3 ✅ |
| 4 | Keine Verschlüsselung im Browser | AES-GCM-256 mit server-wrapped Master Key (vault), 22+ Tabellen, Settings-UI + Rotate, Onboarding-Banner | Encryption Phase 16 ✅ |
| 5 | Keine Guest → User Migration | `guest-migration.ts` walkt sync-Tabellen, re-inserted unter neuer userId, $effect im Layout | 2.3 ✅ |
### 🟡 Hoch — alle erledigt
@ -114,17 +203,17 @@ liveQuery (Dexie) → Svelte 5 reaktiv → UI
| 10 | Cascade-Deletes nicht atomar | `db.transaction('rw', ...)` in cards, chat, presi, music | 1 ✅ |
| 11 | Keine Telemetrie | `sync-telemetry.ts` CustomEvent-Bus für Push/Pull/Apply Lifecycle | 4.3 ✅ |
### 🟢 Mittel — Status
### 🟢 Mittel — alle erledigt oder in Backlog
| # | Problem | Status |
| --- | ---------------------------------------- | ---------------------------------------------------------------------- |
| 12 | `changes: any[]` in `applyServerChanges` | ✅ Sprint 3.1 — `SyncChange` Interface |
| 13 | SSE-Buffer akkumuliert ganze Events | 🟢 Backlog — irrelevant unter ~1k changes/event |
| 14 | Tombstone-Cleanup-Routine fehlt | ⚙️ Sprint 4.2 — Helper existiert (`cleanupTombstones`), kein Scheduler |
| 15 | Conflict-Visualisierung im UI | 🟢 Backlog — UX-Entscheidung |
| 16 | Keine Unit-Tests für `sync.ts` | ✅ Sprint 3.3 — 20 Tests gegen fake-indexeddb |
| 17 | Composite Keys / Multi-Account Indizes | 🟢 Backlog — wenn Multi-Account aktiv wird |
| 18 | Audit-Log / Activity Feed | 🟢 Backlog — eigenes Feature |
| # | Problem | Status |
| --- | ---------------------------------------- | -------------------------------------------------------- |
| 12 | `changes: any[]` in `applyServerChanges` | ✅ Sprint 3.1 — `SyncChange` Interface |
| 13 | SSE-Buffer akkumuliert ganze Events | ✅ Backlog 2 — pipelined Reader/Apply mit indexOf-Parser |
| 14 | Tombstone-Cleanup-Routine fehlt | ✅ Sprint 4+ — `data-layer-listeners.ts` boot+24h Loop |
| 15 | Conflict-Visualisierung im UI | 🟢 Backlog — UX-Entscheidung |
| 16 | Keine Unit-Tests für `sync.ts` | ✅ Sprint 3.3 — 20 Tests gegen fake-indexeddb |
| 17 | Composite Keys / Multi-Account Indizes | 🟢 Backlog — wenn Multi-Account aktiv wird |
| 18 | Audit-Log / Activity Feed | ✅ Backlog 3 — `_activity` Table + read API + prune |
---
@ -151,40 +240,102 @@ liveQuery (Dexie) → Svelte 5 reaktiv → UI
- ✅ Storage-Quota-Recovery mit Tombstone-Cleanup
- ✅ Telemetrie-Events für Sync-Lifecycle (Sentry/Debug-HUD-ready)
- ✅ Indexierte Range-Queries auf Hot-Path Dashboard-Widgets
- ✅ 20 Unit-Tests gegen Vitest+fake-indexeddb
- ✅ Crypto + activity + sync Unit-Tests (110+ Tests gegen Vitest+fake-indexeddb)
- ✅ Auto-stamped userId via central `current-user.ts`
- ✅ Guest → User Migration beim ersten Login
- ✅ SSE Pipelining (read/apply entkoppelt, allocation-light Parser)
- ✅ Activity Log mit Per-User-Isolation und 90d TTL
- ✅ At-Rest-Encryption für 22+ Tabellen (AES-GCM-256, server-wrapped Master Key)
- ✅ Settings UI für Vault-Status + Manual Rotate
- ✅ Onboarding-Banner beim ersten Login
---
## 5. Backlog (Nice-to-Have)
## 5. Encryption-Pipeline (Phase 16)
### Wer hält was?
| Komponente | Inhalt |
| -------------------------- | -------------------------------------------------------------------------------------------------------- |
| **mana-auth Server** | KEK in env (`MANA_AUTH_KEK`, base64-32-bytes). `auth.encryption_vaults` mit wrapped MK + IV per User. |
| **HTTPS Wire** | Master Key wird beim Login als base64 transportiert (via Bearer JWT geschützt). |
| **Browser sessionStorage** | Master Key als CryptoKey im `MemoryKeyProvider`. `mana-vault-unlocked=1` Sentinel für die UI. |
| **IndexedDB** | Verschlüsselte Felder als `enc:1:<iv>.<ct>` Strings. Strukturelle Felder (id, FK, timestamps) plaintext. |
### Verschlüsselte Tabellen (Stand Phase 6.1)
| Modul | Tabelle(n) | Felder |
| -------- | --------------- | ----------------------------------------------------------------------------------------- |
| chat | `messages` | `messageText` |
| | `conversations` | `title` |
| | `chatTemplates` | `name`, `description`, `systemPrompt`, `initialQuestion` |
| notes | `notes` | `title`, `content` |
| dreams | `dreams` | `title`, `content`, `transcript`, `interpretation`, `aiInterpretation`, `location` |
| | `dreamSymbols` | `meaning` (name plaintext für Lookup) |
| memoro | `memos` | `title`, `intro`, `transcript` |
| | `memories` | `title`, `content` |
| contacts | `contacts` | 16 PII-Felder (firstName, lastName, email, phone, mobile, birthday, address, social, ...) |
| cycles | `cycles` | `notes` |
| | `cycleDayLogs` | `notes`, `mood` (symptoms plaintext für Set-Diffs) |
| finance | `transactions` | `description`, `note` |
| cards | `cards` | `front`, `back` |
| | `cardDecks` | `name`, `description` |
| presi | `presiDecks` | `title`, `description` |
| | `slides` | `content` (SlideContent JSON) |
| inventar | `invItems` | `description` (name + notes-array bleiben plaintext) |
| planta | `plants` | `name`, `careNotes`, `temperature`, `soilType` |
### Was Mana technisch (nicht) sehen kann
**Niemals:**
- Inhalte verschlüsselter Felder ohne aktiv den KEK zu verwenden
- Klartext der Records auf der User-Festplatte (Browser-Forensik liefert nur Blobs)
**Theoretisch entschlüsselbar (wenn aktiv missbraucht):**
- Provider-Operator mit Zugriff auf KEK kann den wrapped MK entschlüsseln und Daten lesen
- Lösung: Phase 7 — Recovery-Code-Opt-In für true Zero-Knowledge
**Strukturell sichtbar:**
- Anzahl Records pro Tabelle (counts)
- Zeitstempel und FKs
- Welche Module der User aktiv nutzt
- Sync-Frequenz und Volumes
---
## 6. Backlog (Nice-to-Have)
In abnehmender Priorität:
1. **Encryption-at-Rest** für sensitive Tabellen (Chat, Notes, Memoro). Web Crypto API + per-User-Key. Erfordert Key-Management-Konzept.
2. **Tombstone-Cleanup-Scheduler**`setInterval(cleanupTombstones, 24h)` im Layout. 5 Zeilen.
3. **Conflict Visualization UI** — Toast/Modal wenn LWW eine Field-Edit überschrieben hat. Braucht UX-Design.
4. **SSE Buffer Streaming** — wenn Pull-Größen ≥1k changes/event werden.
5. **Composite Indexes für Multi-Account**`[userId+createdAt]` etc., sobald User mehrere Accounts wechseln können.
6. **Audit-Log / Activity Feed** — eigenständiges Feature.
1. **Encryption Phase 7 — Cross-Module Title Coverage** — `tasks` / `calendar.events` / `habits` haben Title-Leakage via die `timeBlocks` Tabelle. Lösung: koordinierter Pass der `timeBlocks.title/description/notes` mitencryptet, plus Update der TimeBlock-Service.
2. **Encryption Phase 7 — Server-pushed Records** — `picture.images`, `storage.files`, `music.songs` werden vom Server erzeugt (image-gen API, file-upload, library import). Client-side `encryptRecord` greift nicht. Braucht: API-Service muss vor dem Push verschlüsseln, oder ein Sync-time wrap-step on-the-fly.
3. **Encryption Phase 7 — Storeless Modules** — `nutriphi.meals` / `uload.links` / `context.documents` / `questions` / `answers` schreiben heute direkt aus Views via `db.table().update()`. Brauchen erst Store-Extraktion, dann gleicher Pattern wie Phase 5.
4. **Encryption Phase 8 — Recovery Code Opt-In** — BIP-39-Generator + Settings-UI + Server-Wrap-Toggle. User entscheidet selbst ob Provider noch entschlüsseln darf.
5. **Conflict Visualization UI** — Toast/Modal wenn LWW eine Field-Edit überschrieben hat. Braucht UX-Design.
6. **Composite Indexes für Multi-Account** — `[userId+createdAt]` etc., sobald User mehrere Accounts wechseln können.
7. **V3 Migration Tests** — wenn die Mana-App nochmal Production-Daten migrieren muss.
Pre-existing Test-Failures (nicht von dieser Audit-Arbeit verursacht):
- `base-client.test.ts` — englische Assertions, deutsche Strings
- `dashboard.test.ts` — Widget-Registry hat 22 statt 16 Einträge
- `content/help/index.test.ts``svelte-i18n` Locale nicht initialisiert im Test-Env
- `base-client.test.ts` — englische Assertions, deutsche Strings (in Sprint Test-Fix bereinigt)
- `dashboard.test.ts` — Widget-Registry hat 22 statt 16 Einträge (in Sprint Test-Fix bereinigt)
- `content/help/index.test.ts``svelte-i18n` Locale nicht initialisiert im Test-Env (in Sprint Test-Fix bereinigt)
---
## 6. Stärken (zur Erinnerung)
## 7. Stärken (zur Erinnerung)
- Saubere Unified-DB mit Tabellen-Prefixing für Kollisionen
- Automatisches Change-Tracking via Dexie Hooks (kein manuelles `trackChange()`)
- Klares Backend-Mapping (`TABLE_TO_SYNC_NAME`)
- Klares Backend-Mapping (`TABLE_TO_SYNC_NAME`) via per-Modul `module.config.ts`
- Lazy Sync für selten genutzte Module (Connection Limits geschont)
- Vollständiger Offline-Support inkl. Online-Resume
- SSE bevorzugt, Polling als Fallback
- SSE bevorzugt, Polling als Fallback (mit pipelined parser)
- Saubere Trennung Detection (`quota-detect.ts`) vs. db-aware Helpers (`quota.ts`) → keine Import-Cycles
- Encryption-Boundary lebt in dedicated `crypto/` Sub-Modul, völlig entkoppelt vom Sync-Layer
- Vault-Singleton via `vault-instance.ts` — Layout + Settings + zukünftige UI teilen sich denselben State
Die Datenschicht ist jetzt **production-grade** in den Dimensionen Korrektheit, Sicherheit, Robustheit, Beobachtbarkeit, Performance und Testabdeckung.
Die Datenschicht ist jetzt **production-grade** in den Dimensionen Korrektheit, Sicherheit, **Vertraulichkeit**, Robustheit, Beobachtbarkeit, Performance und Testabdeckung.