From 766ad2ea8f5d88a755c857868c535dbd4446de9d Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 22 Apr 2026 17:07:05 +0200 Subject: [PATCH] =?UTF-8?q?feat(crypto):=20Phase=202a=20=E2=80=94=20declar?= =?UTF-8?q?e=20encryption=20intent=20for=20tags/scenes/missions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparation step for the space-scoped data model migration (Phase 2b). Moves globalTags, tagGroups, workbenchScenes, and aiMissions from the plaintext allowlist into the encryption registry with enabled:false — so the audit script documents which fields WILL be encrypted without changing any runtime behaviour. Fields chosen per design-doc: - globalTags.name — personal categorization (Therapie, Finanzen-privat) - tagGroups.name — same - workbenchScenes.name + description — scene labels often encode Space-specific context (Q2-Launch, Urlaub 2026) - aiMissions.title + conceptMarkdown + objective — all user-typed mission config; state/cadence/inputs stay plaintext for the Runner Deliberately kept plaintext (against my initial suggestion): - aiAgents.name — registry comment explains: name is the Actor displayName cache key for historic attribution. Encrypting would show "🤖 [encrypted]" on every past task the agent ever touched. - globalTags.icon / tagGroups.icon / color — not personal content; icon is a visual cue, color is theme metadata The 2c migration (Dexie v35, flip enabled:true) runs after 2b lands the schema changes so existing rows get encrypted in one controlled pass instead of mixing schema + encryption in the same upgrade. Crypto audit: 195 Dexie tables classified (94 encrypted, 101 plaintext-allowlisted). Type-check clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../lib/data/crypto/plaintext-allowlist.ts | 4 --- .../apps/web/src/lib/data/crypto/registry.ts | 36 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts b/apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts index 2bca7ae44..4ec55c8da 100644 --- a/apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts +++ b/apps/mana/apps/web/src/lib/data/crypto/plaintext-allowlist.ts @@ -19,7 +19,6 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [ 'achievements', // TODO: audit 'activities', // TODO: audit - 'aiMissions', // TODO: audit 'albumItems', // TODO: audit 'albums', // TODO: audit 'articleTags', // FK-only junction into globalTags (articleId, tagId). Tag names live in globalTags. @@ -50,7 +49,6 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [ 'fileTags', // TODO: audit 'financeCategories', // TODO: audit 'foodFavorites', // TODO: audit - 'globalTags', // TODO: audit 'goals', // TODO: audit 'guideCollections', // TODO: audit 'guideTags', // TODO: audit @@ -99,7 +97,6 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [ 'songTags', // TODO: audit 'spaceMembers', // TODO: audit 'storageFolders', // TODO: audit - 'tagGroups', // TODO: audit 'taskLabels', // TODO: audit 'timeAlarms', // TODO: audit 'timeBlockTags', // TODO: audit @@ -121,5 +118,4 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [ 'wishesItems', // TODO: audit 'wishesLists', // TODO: audit 'wishesPriceChecks', // TODO: audit - 'workbenchScenes', // TODO: audit ]; diff --git a/apps/mana/apps/web/src/lib/data/crypto/registry.ts b/apps/mana/apps/web/src/lib/data/crypto/registry.ts index 36e34ad03..c9e42afd3 100644 --- a/apps/mana/apps/web/src/lib/data/crypto/registry.ts +++ b/apps/mana/apps/web/src/lib/data/crypto/registry.ts @@ -573,6 +573,42 @@ export const ENCRYPTION_REGISTRY: Record = { // structural fields. agents: { enabled: true, fields: ['systemPrompt', 'memory'] }, + // ─── AI Missions ───────────────────────────────────────── + // docs/plans/space-scoped-data-model.md §2a — declared with + // enabled:false during prep so the audit script is happy; flips to + // true in 2c alongside the Dexie v35 encryption migration. + // + // User-typed content on missions: `title` (display label the user + // types at create time), `conceptMarkdown` (free-form context the + // planner reads), `objective` (the actionable goal string). State, + // cadence, inputs (FK-only), nextRunAt, iterations, agentId all + // stay plaintext — needed for the Runner's "due now" index walk + // and mission-detail filters. + aiMissions: { enabled: false, fields: ['title', 'conceptMarkdown', 'objective'] }, + + // ─── Tags (shared-stores) ──────────────────────────────── + // docs/plans/space-scoped-data-model.md §2a — declared with + // enabled:false during prep; flips to true in 2c. Tag names like + // "Therapie" or "Finanzen-privat" can leak personal categorization, + // so they belong under encryption. `color` + `icon` + `groupId` + + // `sortOrder` stay plaintext: they're visual metadata + the group + // FK, none of which leak sensitive taxonomy. `name` is NOT indexed + // for .where() lookups today, so encrypting it is safe — dedupe- + // within-space lookups go through the new [spaceId+name] index after + // Phase 2b and run over already-decrypted rows in the scoped store. + globalTags: { enabled: false, fields: ['name'] }, + tagGroups: { enabled: false, fields: ['name'] }, + + // ─── Workbench Scenes ──────────────────────────────────── + // docs/plans/space-scoped-data-model.md §2a — declared with + // enabled:false during prep; flips to true in 2c. `name` is the + // user-visible scene label ("Heute", "Q2-Launch") and `description` + // is the short subtitle — both are user-typed free text that can + // leak Space-specific context. openApps / order / wallpaper / + // viewingAsAgentId / scopeTagIds stay plaintext (structural / + // indexed / foreign-key data). + workbenchScenes: { enabled: false, fields: ['name', 'description'] }, + // ─── Articles (Pocket-style read-it-later) ────────────── // Reading-behaviour data — same sensitivity class as newsArticles. // Encrypted: