Skip to content

Preserve encrypted receive history when cache writes fail - #226

Merged
itz4blitz merged 8 commits into
mainfrom
diagnose/sealed-cache-write-order
Nov 8, 2026
Merged

itz4blitz merged 8 commits into
mainfrom
diagnose/sealed-cache-write-order

Conversation

@itz4blitz

@itz4blitz itz4blitz commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

What changed and why

A received Tiny message could advance its persisted MLS ratchet before its opened-history cache append failed. It stayed readable in RAM, then became undecryptable after restart. Save authenticated opened history durably before accepting that receive; a failed cache commit leaves its key available for retry after storage repair.

Type

  • feature
  • fix
  • perf
  • refactor
  • chore
  • docs

Platforms

  • windows
  • macos
  • linux
  • android
  • ios

Proof

Scenario: e2e/scenarios/opened-history-cache-commit

Published Linux matched recordings and manifest, exact source 27cd07263a49176fcbca55f1538541ef8d26547c against official released base 7ffa840a9beb7a7eb8017c679bc4ea1d82710876. Both use normal release-profile apps, invented people, fresh independent databases, and persistent wrapping keys established before first launch.

  • Linux base recording: a cache-only write fault permits plaintext to appear, but the same server-assigned message becomes undecryptable after repair and cold restart. Only the history-survival assertion fails; setup succeeds.
  • Linux branch recording: a failed cache commit refuses the read. After repair and cold restart, the same message opens, persists, and appears in the conversation pane. All 15 steps pass.

Recorders started before driver steps. Both videos validate and were visually reviewed. Exact scoped message IDs, durable-cache presence and captured conversation pixels agree. The branch was built directly from a pinned source archive with normal release features and explicit embedded commit; all 1,535 archived source files were verified unchanged after build. The copied bundle hashes were checked before launch. All nine published asset sizes and SHA256 digests were verified. e2e-lab/linux is successful on this exact head; no aggregate status was posted.

Both cases remove only the empty cache-path blocker and the scoped SQLite message index between cold launches. Conversation, keys and MLS state remain intact, without group resets or ratchet rewinds. The fixture server binary's source tree matches current server main; its manifest distinguishes build commit from source-tree equivalence.

Earlier Windows matched recordings remain pinned to prior head b7f02bda8869ee9c0c706ec416fb98475d51b583; they are not relabeled as proof of the current head. The production Rust code is unchanged since that head; the later delta integrates current main and adds Linux test support. The Linux adapter uses owned executable/PID/foreground guards, native Jump input, conversation-pane image OCR and diagnostic Unix framing; Welcome screens and sidebar previews cannot satisfy the body assertion.

The MLS/FileStore/Tiny component regression fails on the base. Additional regressions cover accepted-receive state-save failure, mismatched row epoch acceptance and attachment-registration ordering. At the production-code head: focused Tiny E2EE 95 passed / five existing ignored; cargo test -p e2ee passed; full cargo test -p chatd passed with 499 library tests, 11 existing ignored and all integration suites green. Windows and Linux scenario suites each passed 19 tests with one platform-specific skip; all 108 lab tests passed. Independent Linux driver review found no blocking defect. Current-head release build and hosted CI pass.

Draft / MERGE HOLD: the full five-platform plan is incomplete. Current-head Windows, macOS, Android and iOS native runs remain outstanding. No aggregate e2e-proof, release certification, power-loss guarantee, or affected-install recovery claim is made. Scenario remains release: false.

Compatibility

Existing decrypt API keeps its lenient state-save semantics. The new receive-specific commit callback runs after authentication while its checkpoint is reversible. No protocol, key, snapshot/cache format or account-scope change. Older servers remain compatible. Soft outgoing/import/sibling persistence retains existing behavior; the separate long-history cutoff fix is outside this change.

Risk and rollback

Strict receives synchronize cache data before persisting consumed state, adding I/O latency. A cache failure refuses that read and logs storage failure; repairing storage permits retry. State-save failures retain consumed state in memory and remain visible on flush. A durable cached row survives an older state snapshot, but this is not an atomic two-file transaction: after restarting from lagging state, a genuine ciphertext under another server row ID may reopen, as with the existing lenient failure contract. No arbitrary post-crash replay, partial-write/sync-error injection, or power-loss guarantee is claimed. This prevents a reproduced future loss path; it does not reconstruct already lost plaintext or establish any affected user's exact cause. Reverting restores the unsafe cache-after-ratchet order. No client release or server deployment.

Release note

Keep received encrypted messages recoverable when saving their local history cache fails.

@itz4blitz
itz4blitz marked this pull request as ready for review November 8, 2026 19:59
@itz4blitz
itz4blitz force-pushed the diagnose/sealed-cache-write-order branch from 27cd072 to 3ecc067 Compare November 8, 2026 20:00
@itz4blitz
itz4blitz merged commit 804d7fd into main Nov 8, 2026
5 checks passed
@itz4blitz
itz4blitz deleted the diagnose/sealed-cache-write-order branch November 8, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant