Problem — three Phase 2 header decisions that freeze permanently
chief-performance-officer, measured. All three are operation-header or content-format decisions, so they cannot be added later.
1. Sync is permanently O(operations) without a per-device sequence number
With 1M operations, exchanging operation ID sets is ~32 MB per sync. Bloom filters at 1% FPR are ~1.2 MB for 1M items — better, still O(operations), and probabilistic on a medical-records store.
The fix makes reconciliation O(device count): a per-device monotonic sequence number, so two devices exchange {device_id: highest_seq} — a few hundred bytes — then stream the ranges each is missing.
Design §3.1's header carries versionVector but no deviceSeq. Add deviceSeq: u64.
Without it, sync is permanently O(operations) and Bloom filters become a mandatory workaround for a problem a u64 prevents.
Note: version vector scaling is a non-issue and should not absorb design effort — the trust boundary is one user's device mesh, realistically ≤10 devices, so VV size is O(devices).
2. Content must be chunked, or transfer is not resumable
ContentEnvelope wraps one key for one blob with no chunking concept. A single-AEAD-blob content object cannot be decrypted until fully received, and cannot be resumed. For media that is disqualifying.
Chunk at 1–4 MiB, each independently sealed with nonce_i = KDF(content_key, chunk_index). Gives streaming decryption, resumable transfer, parallel download.
This is the same framing fix as #1305 — do them together, once.
3. Reserve a signed-checkpoint slot before the header freezes
The design forbids compaction. It does not forbid a signed checkpoint operation that supersedes a prefix — compaction expressed as an operation, so it stays signed, stays the sync unit, and leaves the invariant intact.
Storage is currently O(all edits ever), not O(current state), and the text primitive's default merge is revisions — the worst case. A note edited 500 times costs 500 signed operations forever, with no product-level policy bounding it.
Reserve the format slot now. The header is unretrofittable; a reserved slot costs nothing and its absence is permanent.
Also required: group commit
One fsync per operation on Android means a 4 KB page write minimum for a ~200-byte header, plus the filesystem journal — an estimated 10–50× write amplification, sustained, on the user's flash.
Group-commit with a bounded latency window (start at 20 ms), one fsync per batch; single-op fsync reserved for explicit user-visible durability points.
The window cannot be estimated. Real-device fsync ranges 0.5 ms to 50 ms — it needs measurement on a Pixel 9 and a mid-range device.
Record explicitly: content deduplication is ruled out
Impossible by construction, and correctly so — content is sealed under a random per-object key, so identical plaintext yields different ciphertext. Convergent encryption would enable dedup and leak plaintext equality across the whole store.
Write the decision into the design so a future performance pass cannot "discover" it as free savings. The envelope already handles the case that matters: one object in many contexts, stored once.
Reviewers
Edge Architect + Chief Cloud Officer + chief-performance-officer jointly, per the Decision Matrix — this is a sync/storage engine change.
Blocked by
#1194 (Operation Log).
Problem — three Phase 2 header decisions that freeze permanently
chief-performance-officer, measured. All three are operation-header or content-format decisions, so they cannot be added later.
1. Sync is permanently O(operations) without a per-device sequence number
With 1M operations, exchanging operation ID sets is ~32 MB per sync. Bloom filters at 1% FPR are ~1.2 MB for 1M items — better, still O(operations), and probabilistic on a medical-records store.
The fix makes reconciliation O(device count): a per-device monotonic sequence number, so two devices exchange
{device_id: highest_seq}— a few hundred bytes — then stream the ranges each is missing.Design §3.1's header carries
versionVectorbut nodeviceSeq. AdddeviceSeq: u64.Note: version vector scaling is a non-issue and should not absorb design effort — the trust boundary is one user's device mesh, realistically ≤10 devices, so VV size is O(devices).
2. Content must be chunked, or transfer is not resumable
ContentEnvelopewraps one key for one blob with no chunking concept. A single-AEAD-blob content object cannot be decrypted until fully received, and cannot be resumed. For media that is disqualifying.Chunk at 1–4 MiB, each independently sealed with
nonce_i = KDF(content_key, chunk_index). Gives streaming decryption, resumable transfer, parallel download.This is the same framing fix as #1305 — do them together, once.
3. Reserve a signed-checkpoint slot before the header freezes
The design forbids compaction. It does not forbid a signed checkpoint operation that supersedes a prefix — compaction expressed as an operation, so it stays signed, stays the sync unit, and leaves the invariant intact.
Storage is currently O(all edits ever), not O(current state), and the
textprimitive's default merge isrevisions— the worst case. A note edited 500 times costs 500 signed operations forever, with no product-level policy bounding it.Reserve the format slot now. The header is unretrofittable; a reserved slot costs nothing and its absence is permanent.
Also required: group commit
One
fsyncper operation on Android means a 4 KB page write minimum for a ~200-byte header, plus the filesystem journal — an estimated 10–50× write amplification, sustained, on the user's flash.Group-commit with a bounded latency window (start at 20 ms), one fsync per batch; single-op fsync reserved for explicit user-visible durability points.
The window cannot be estimated. Real-device fsync ranges 0.5 ms to 50 ms — it needs measurement on a Pixel 9 and a mid-range device.
Record explicitly: content deduplication is ruled out
Impossible by construction, and correctly so — content is sealed under a random per-object key, so identical plaintext yields different ciphertext. Convergent encryption would enable dedup and leak plaintext equality across the whole store.
Write the decision into the design so a future performance pass cannot "discover" it as free savings. The envelope already handles the case that matters: one object in many contexts, stored once.
Reviewers
Edge Architect + Chief Cloud Officer + chief-performance-officer jointly, per the Decision Matrix — this is a sync/storage engine change.
Blocked by
#1194 (Operation Log).