feat(schema): encrypted addon-config collection (issue #20) #2
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/addon-config-schema"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Server half of myanime-app#20 — the
addon_configcollection that holds a user's addon configuration as a client-side-encrypted blob, so a fresh device sign-in restores the addons without the server ever storing usable credentials.What's here
pb_migrations/1785200000_addon_config.js— one row per profile (unique index),OWNSaccess rules like the other per-profile collections. Storesblob(AES-GCM ciphertext, opaque),salt(per-account KDF salt — not secret; lets a second device derive the same key from the password),kdf(derivation descriptor), plus the same two-clock model as #11 (updatedAtclient clock for LWW,updatedautodate as the pull cursor).README.md— data model row + a section on why this one blob is encrypted while the rest stays server-readable (the no-E2E stance is unchanged).scripts/verify.py— schema assertions (fields, unique index, per-owner rules), a ciphertext round-trip that checks the clientupdatedAtisn't restamped and a duplicate-per-profile is rejected, and cross-user isolation checks foraddon_config.Notes
feature/sync-fields, so until that merges the diff here also shows its commit; it collapses to just theaddon_configchange once PR #1 lands. Please merge after PR #1.addon_configmigration itself is independent of the sync-fields work — it only needs theprofilescollection from the init schema (already onmain).🤖 Generated with Claude Code
Review: approve ✅ — deploy before merging client #29
Clean, and it matches the client exactly.
addon_configcollection stores ciphertext only —blob+ the non-secretsalt/kdf+ the two-clockupdatedAt(client) /updated(autodate cursor), one row per profile (unique index), owned viaprofile.user = @request.auth.idon all 5 verbs. That's precisely the shape client PR #29 writes ({profile, blob, salt, kdf, updatedAt}) and the same LWW/cursor model as the #11 collections.cascadeDeleteoff the profile) with a clean down; timestamp1785200000sorts aftersync_fields. Safe to apply to the live backend.verify.pycoverage is exactly right: existence + per-owner rules on all 5 verbs, field types, the unique-profile index, a client-clock round-trip, duplicate-per-profile rejected (400), and cross-user isolation (bob can't list or view alice's blob).Field sizes are fine (
blobmax 100000 ≫ the ~1KB base64 of a small AES-GCM blob;salt/kdfcomfortably bounded).Deploy order: merge + redeploy this, then merge client #29 — not the other way round, or #29's writes 404 silently (the reconcile no-ops without surfacing an error).