From 1c9b47e35c992077bb614976e4458f134804588a Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sun, 1 Nov 2026 18:33:58 +0100 Subject: [PATCH] docs: plan crdt_sync E2E encryption Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WzxdWXTUM9dpnCS2wtFkx4 --- TODO-crdt-e2e-encryption.md | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 TODO-crdt-e2e-encryption.md diff --git a/TODO-crdt-e2e-encryption.md b/TODO-crdt-e2e-encryption.md new file mode 100644 index 0000000..253629a --- /dev/null +++ b/TODO-crdt-e2e-encryption.md @@ -0,0 +1,38 @@ +# Encrypt crdt_sync payloads end to end + +## what +Firebase RTDB stores every crdt_sync record in plaintext, so Google can read +all app data. Encrypt record payloads on the device before push and decrypt +after pull, so RTDB stays a blind relay (the "ownership" meaning of +local-first: hologram.page/blog/local-first-means-three-different-things). + +## open decisions (ask kuhy before coding) +- Key source: derived from the shared crdt-sync password (Argon2id) or a + random key stored in the OS keystore and copied to new devices. +- Migration: re-encrypt existing logs in place, or new prefix + one-time copy. +- Scope: payload only (keep HLC/ids clear for merge) or whole record. +- tutor uses its own RTDB client (`~/src/tutor/app/lib/sync/rtdb_client.dart`): + in or out of scope. + +## steps +1. Add an encrypt/decrypt seam in `crdt_sync_dart/lib/src/remote_store.dart` + (push/pull), AEAD (XChaCha20-Poly1305), version byte per record. +2. Property test: decrypt(encrypt(r)) == r; merge result identical with and + without encryption (see property-and-differential-tests skill). +3. Read path accepts plaintext and v1 records during migration. +4. Roll out app by app (todo first), bump pins, verify on the phone. + +## done +`dart test` in `crdt_sync_dart` passes, and +`firebase database:get / | grep -c '"title"'` returns 0 for every +migrated app. + +## verify +Pixel 6a + desktop todo: edit on one, see it on the other; raw RTDB JSON shows +only ciphertext. + +## read first +- `crdt_sync_dart/lib/src/sync.dart`, `log.dart`, `remote_store.dart` +- `crdt_sync_flutter/lib/src/bootstrap.dart` + +REMOVE ME AFTER FINISH