Skip to content

Phase 4: wire encryption migration journal into production (disable/rotate flow, secondary-adapter startup, write-admission race, verification-deletion race) #338

Description

@qnbs

Summary

The encryption migration journal system (services/storage/encryptionMigrationJournal.ts, protectedStoreMigration.ts, secondaryProtectedStoreAdapters.ts) currently has zero production callersbeginEncryptionMigration, runProtectedStoreMigration, and getRegisteredSecondaryProtectedStoreAdapters are only invoked from tests/unit/storage/*.test.ts. The live "Encrypt project data at rest" toggle (components/settings/PrivacySection.tsx) only supports enable (setupIdbEncryption) and unlock (verifyAndInitIdbEncryption) — PassphraseModalMode is type-restricted to 'set' | 'unlock' only, and the toggle is explicitly disabled={encEnabled} once on ("Locked is still encrypted; showing it as off invited an unsafe disable path.").

This matches documented tech debt in CLAUDE.md § Known Technical Debt, B-1: "Actual IDB read/write integration for stores is Phase 4 (service-layer only currently)."

Because there is no production trigger, three CodeRabbit findings from PR #337's review are real bugs in the migration engine's own logic, but not currently reachable by any user action. They were deliberately left unresolved (not fixed, not falsely closed) on that PR pending this dedicated Phase-4 design work. This issue is the tracking follow-up CodeRabbit requested (PR #337 thread acks: "A follow-up issue should track the Phase-4 production migration flow, including UI entry points, journal startup, adapter checkpoint creation, recovery UX, and verification.").

Required Phase-4 scope

  1. Disable + passphrase-rotation product flows. Extend PassphraseModalMode beyond 'set' | 'unlock', build the UI entry points, and wire a real production caller for beginEncryptionMigration/runProtectedStoreMigration.
  2. Wire secondary adapters into production migration startup (originally flagged at services/storage/secondaryProtectedStoreAdapters.ts#L136-185). No production code currently calls getRegisteredSecondaryProtectedStoreAdapters; migration checkpoints must be created with IDs matching each adapter's databaseName/storeName format (sceneRevisionAdapterSpec, inferenceCacheAdapterSpec), otherwise those stores are silently never migrated.
  3. Atomic migration admission vs. protected writes (originally flagged at services/storage/idbAssetStore.ts#L32-34 + 6 sibling sites). assertNoActiveEncryptionMigration() is only a preflight check — journal ownership is stored separately from the affected data stores, so a writer can pass the check, migration can claim ownership and commit, and the writer can then persist plaintext/ciphertext under the old key. Needs shared cross-tab write admission: migration ownership must block new writers and wait for admitted writers; writers must acquire admission before key resolution and hold it through the write transaction. Applies to saveImage(), saveBinderAsset(), deleteAllBinderAssetsForProject(), saveStoryCodex(), saveRagVectors(), saveSlice(), createSnapshot().
  4. Handle records deleted during verification (originally flagged at services/storage/protectedStoreMigration.ts#L280). verify() counts current records, but checkpoint.processed counts records seen during migration. Cache eviction (services/ai/aiInferenceCacheService.ts) and scene-revision retention (services/sceneRevisionService.ts) can delete records independently of migration state and cause a false ProtectedStoreVerificationShortfallError/recovery-required. Either block those two mutation paths during migration, or verify against the set of records that still exist.

Acceptance criteria

  • A real production flow (disable and/or passphrase-rotation) can trigger beginEncryptionMigration.
  • Secondary adapters (sceneRevisionAdapterSpec, inferenceCacheAdapterSpec) are included in that production migration run, with checkpoint IDs matching adapter databaseName/storeName.
  • Shared cross-tab write admission is implemented and tested: a writer cannot commit a protected write after migration has claimed ownership; migration waits for admitted writers to drain.
  • Migration verification tolerates legitimate concurrent deletion from cache eviction / scene-revision retention (regression test included, per CodeRabbit's requested "Add a regression test for deletion during verification").
  • CLAUDE.md § Known Technical Debt B-1 updated once closed.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions