Skip to content

fix: harden rotation read-plane durability and deep crash-resume soundness#598

Merged
FSM1 merged 109 commits into
mainfrom
feat/rotation-read-plane-durability-and-deep-crash-resume-soundne
Jul 9, 2026
Merged

fix: harden rotation read-plane durability and deep crash-resume soundness#598
FSM1 merged 109 commits into
mainfrom
feat/rotation-read-plane-durability-and-deep-crash-resume-soundne

Conversation

@FSM1

@FSM1 FSM1 commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Phase 70.1 — Rotation read-plane durability & deep crash-resume soundness

Hardens the read-key rotation engines (TS sdk-core + Rust sdk twin) and the FUSE/WinFsp shared-scope-exit rotation live-wiring so that read-plane rotation is durable across crashes and sound under deep (depth ≥ 3) fan-out, with an anti-rollback floor plane that fails closed.

13 plans across 7 waves. Full per-plan detail in the phase SUMMARY.md files.

What changed

  • Depth-aware dirty-frontier consumption (both engines). The BFS resume walk checks edge dirtiness before attempting any decrypt, so a depth-3 deep dirty node can no longer be dequeued ahead of its parent's tracking entry. DirtyNodeUnrecoverableError / RotationError::DirtyNodeUnrecoverable surface a clear, actionable error instead of an opaque AEAD failure.
  • Anti-rollback floor plane. Single atomic combined per-node floor record; fails closed on a corrupt or unreadable sidecar (returns a max floor that forces the high-water regression check to reject); per-path shared lock fixes a concurrent-rename lost-update race.
  • Client reconcile. reconcileFolderSequence gates on the freshly-resolved generation (real unsealNodeenforceResolved), not the cached nodeGeneration.
  • Rust engine twin parity. Recursive verify_subtree_clean, missing-root-as-dirty, depth-3 crash-resume via FakeDeps.
  • FUSE/WinFsp shared-scope-exit rotation. Production RotationDeps adapter; D-15a/b/c/d fail-closed gates; the scope-exit read-key revocation-bypass fix (refresh_grant_root_read_key copies the rotated key into the in-memory grant-root inode so the old key is dead); coalesced single publish; WinFsp delete-path parity.

Acceptance — D-16 (SC#8)

Real-mount shared-scope-exit-rotation.mts acceptance leg is GREEN on macOS + Linux + Windows (CI run 28984795137): coalesced single rotation publish, pre-rotation key dead, recipient (Bob) cut off, private-delete = zero rotation.

Ship-gate results

  • Verify: goal achieved, 7/8 SCs verified in code; SC#6 closed by the live gate below.
  • Validate (Nyquist): COMPLIANT, 8/8 SCs with wired non-vacuous coverage.
  • Secure: SECURED — 4/4 core invariants (revocation cut, terminal-owner-only zeroization, no key logged/persisted-plain/sent-to-server, floor plane fail-closed), 33/33 threats resolved, 0 open. Root SECURITY.md unchanged.
  • SDK E2E (SC#6 live gate): rotation-crash-safety 6/6 green — including the depth-3 fan-out≥2 mid-walk-crash anti-vacuous scenario and the multi-dirty-edge single-batched-repair scenario. Full suite 102/104; the 2 failures are an unrelated local tee-republish precondition (tee_key_state not seeded — the TEE worker init recipe, not run here; this phase touches zero TEE code).

CodeRabbit disposition (7 findings)

Every finding was verified against the actual code before acting.

  • Fixed — engine soundness (was flagged critical): the Rust resume/Skipped branch dropped the is_dirty signal from verify_subtree_clean, so a missing/unresolvable root (empty frontier with is_dirty) could be mis-marked converged. Now consults is_dirty and falls through to the fail-closed resolve. Verified as real-but-dormant (unreachable today: FUSE persist_job is a no-op so no resume repopulates completed_node_ids), fixed anyway since crash-resume soundness is this phase's point. Applied the same parity fix to the TS reference engine (identical frontier.length re-derivation).
  • Fixed — read-plane data loss (major): build_scope_exit_child_override failed open per-child (dropped a transiently-missing sibling or a build_child_refs error), and the override replaces the grant-root's published child list — silently orphaning the dropped child with no EIO. Now returns Result, propagates those two cases as errors, and the caller maps to fail-closed EIO — mirroring the fail-closed sibling build_folder_metadata exactly (still skips the same legitimately-unlinkable children it skips).
  • Fixed — zeroization hygiene (minor): persist_wrapped_key now holds decoded plaintext key bytes in Zeroizing, matching its siblings.
  • Fixed — test robustness (minor): the shared-scope-exit cleanup revoke is wrapped so a failed revoke can't abort Part B.
  • Fixed — doc accuracy (2 minor): VALIDATION SC#6 row reflects the completed green live run; PATTERNS intro no longer over-claims "all self-analogous".
  • Refuted (major): the client rotation seam passes borrowed key buffers, but the engine's zeroization is terminal-owner-only and never zeroes those borrowed ipns/owner/write buffers — not a defect (latent hardening only). No change; documented here.

Deferrals (tracked as todos, not phase-blocking)

  • ROT-04 desktop query_grants_rooted_at re-mint no-op — 2026-07-08-desktop-query-grants-rooted-at-remint-noop.md (sanctioned in plan 70.1-09).
  • WinFsp handle_rename ungated-dest D-15d parity — 2026-07-08-winfsp-d15d-gate-ordering-parity.md.
  • macOS FUSE-T cross-client cache invalidation (Tests 5 & 7 soft-skip) — 2026-07-09-macos-fuse-t-cross-client-sync-invalidation.md.

Note

The phase 70.1-09 accept rationale said "zero new dependencies"; the diff adds two test-only devDependencies (@noble/secp256k1 promoted, fake-indexeddb new). Zero new runtime/production deps and zero new Rust crates.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added safer shared-scope exit rotation on desktop, including real mount end-to-end coverage.
    • Implemented durable rotation state persistence with crash-safe recovery and checkpointed key material preservation.
  • Bug Fixes

    • Fixed desktop shared delete/move rotation behavior to ensure correct revocation and publish counts, including fail-closed gate correctness.
    • Improved rotation state fail-closed handling for invalid, incomplete, or poisoned rotation data.
    • Resolved macOS reliability issues by ensuring pending publish work drains during idle mounts.

FSM1 and others added 30 commits July 8, 2026 15:39
Entire-Checkpoint: 570d1b905004
Entire-Checkpoint: 020b4d928299
Grounds the locked D-01..D-10 approach against live code: confirms
CONTEXT.md's line citations, and surfaces two undisclosed landmines —
the desktop FUSE/WinFsp Rust rotation engine shares the same depth-1
consumption bug and is further behind (non-recursive verify_subtree_clean,
missing-root-treated-as-clean) but is outside CONTEXT's stated file scope,
and no durable-store seam is currently wired into either rotation engine
at all, so the new ECIES key-checkpoint requires inventing an end-to-end
callback seam from scratch.

Entire-Checkpoint: 29d80117a191
Entire-Checkpoint: df4c5b7e6b17
Entire-Checkpoint: 7817addd10e0
Entire-Checkpoint: 73a507dab3be
…nning complete

Entire-Checkpoint: 447d12ea33f6
- Add depth-3 recursive dirty-edge test expecting parent_ipns_name on the
  frontier entry (fails to compile against the current naive
  DirtyFrontierEntry / Vec-returning verify_subtree_clean)
- Add missing-root-is-dirty test expecting a VerifySubtreeOutcome shape
  instead of an empty converged Vec
…tests

- Add RED tests for D-08 fail-closed put propagation surfaced through
  enforce_resolved, D-06 single combined-record atomicity, Pitfall-4
  old-two-file fold-forward migration, and D-07 wrapped-key checkpoint
  round-trip on the combined record
- All new cases reference production items that do not exist yet
  (RotationError::PersistFailed, CombinedFloorRecord,
  persist_wrapped_key/get_wrapped_key/delete_wrapped_key) and fail to
  compile -- confirmed RED
- Widen DirtyFrontierEntry to carry parent_ipns_name, node_read_key,
  child_pub_kind, and enqueued_generation (Rust twin of TS
  DirtyFrontierItem) -- D-12 prerequisite for a depth-aware consumption
  fix
- Make verify_subtree_clean recurse the full subtree via a new
  VerifySubtreeOutcome { is_dirty, frontier } return shape and an
  explicit work-stack walk (collect_dirty_frontier): descend below
  clean folder edges only, stop below a dirty edge
- Surface a missing root as is_dirty: true with an empty frontier,
  distinct from a fully converged (also-empty) frontier -- the
  resume-skip call site in rotate_read_from_node now branches on
  is_dirty instead of frontier.is_empty()
- Extract derive_child_read_key, a shared read-only child-key
  derivation helper, out of enqueue_child's unseal_child_read_key
  logic and reuse it from collect_dirty_frontier
… plan

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- rotation-high-water.test.ts: collapse the fake HighWaterStore fixture
  from two independent generation/seq Maps to ONE combined per-nodeId
  record store, matching the SC#4/D-06 combined-record target
- add a new describe block asserting enforceResolved writes generation
  AND seq into a single combined record via exactly one store.put call,
  and that a rejected regression issues zero store.put calls
- existing scalar-floor/regression-gate cases updated to the new
  single-store constructor signature -- currently RED against the
  production code (still two-store), pending Task 2/3
Adds two RED unit tests for the SC#1/SC#2 rotation engine bugs: a
depth-2 dirty-resume Bug A case (intermediate parent decrement
mis-attributed to root, item dropped) and a depth-3 Bug B ordering
case (a deep dirty child dequeued before its real parent's
parentTracking entry exists, dropping the decrement on both the
missing-guard and skipped-idempotency paths). Both assert concrete
per-parent publishWithCas call counts, not subjective pass/fail.
- Add CombinedFloorRecord { generation, seq, wrapped_key_checkpoint } and
  collapse the two independent generation/seq sidecar files into ONE
  combined JSON sidecar (rotation-high-water.json); for_generation/for_seq
  now both point at that same file and differ only in which field they
  read/write (D-06)
- Fold the OLD two-file shape forward on first read when the combined
  sidecar is absent, so an upgrading device never resets either floor
  (Pitfall 4)
- Add RotationError::PersistFailed and change HighWaterStore::put to
  return Result<(), RotationError>; a write failure (including refusing to
  write over a corrupt sidecar) now fails closed instead of being
  log-and-swallowed (D-08)
- Propagate the new fallible put through bump_floor/bump_generation/
  bump_seq/seed_from_grant/enforce_resolved
- Add JsonSidecarFloorStore::persist_wrapped_key/get_wrapped_key/
  delete_wrapped_key on the same combined record, fail-closed on persist
  (D-07 Rust half) -- the concrete target the FUSE RotationDeps adapter
  (Plan 09) will delegate to
- All Task-1 RED tests now pass GREEN; existing floor_store/high_water
  suites updated for the new Result-returning put signature
…ature

- Update the three in-memory MemStore test fixtures (listing.rs, emit.rs,
  crates/fuse/src/fs.rs) to satisfy the new
  HighWaterStore::put -> Result<(), RotationError> signature
- Fix 3 direct bump_generation call sites in listing.rs test fixtures that
  now return an unused Result
- Re-export CombinedFloorRecord from crates/sdk/src/lib.rs alongside
  JsonSidecarFloorStore
- Update adapter.rs doc comments to describe the combined single-sidecar
  file (rotation-high-water.json) instead of the retired two-file shape
- content_ops.rs/inode.rs/events.rs/replay.rs needed no changes -- they
  only reference RotationHighWater<JsonSidecarFloorStore> as a type,
  never call .put()/bump_floor() directly, confirmed via clean
  `cargo check -p cipherbox-fuse --features fuse --tests`
- No swallowed puts introduced (grep for `let _ = .*\.put(` is empty);
  cargo test -p cipherbox-sdk (139) and cipherbox-fuse --features fuse
  (96 + 1) all green
…DB store

- rotation-state.service.ts: bump DB_VERSION 1 to 2, replace the two
  generation-high-water/seq-high-water object stores with a single
  rotation-floor store keyed by nodeId holding
  { generation?, seq?, wrappedKeyCheckpoint? } (SC#4/D-06)
- onupgradeneeded folds any existing legacy two-store data into the
  combined store via cursor before retiring the old stores -- floors
  are never silently dropped (Pitfall 4)
- idbPutFloors/idbPutWrappedKey each read-back inside ONE readwrite
  transaction and write independently max-preserved generation/seq
  fields while merging/overwriting the wrappedKeyCheckpoint ciphertext
  blob (never max'd)
- add persistWrappedKey/getWrappedKey/deleteWrappedKey accessors on
  the same combined store for the SC#3 ECIES wrapped-key checkpoint
  (D-07) -- client-side IndexedDB only, no API/network call
- add fake-indexeddb as a devDependency and a new colocated
  rotation-state.service.test.ts proving the wrapped-key round trip
  and old-schema migration-preservation against a real (faked)
  IndexedDB -- kept out of packages/sdk's test file since this
  browser-only IndexedDB adapter must not be exercised from a
  published, browser-agnostic SDK package's own test suite
…rdering fix

Fixes the two depth-1-only CONSUMPTION bugs in the rotation engine
(dirty-frontier COLLECTION was already depth-aware from Phase 70):

- Bug A (SC#1): the dirty-resume branch used a hard-coded
  rootNode.children.find lookup and attributed every decrement to
  root, mis-attributing depth>=2 items and dropping them. Replaced
  with a group-by-parentIpnsName pass that seeds a
  ParentTrackingState for every DISTINCT parent (root or otherwise)
  via a new resolveParentTrackingState helper, then enqueues every
  frontier item through the existing enqueueDirtyFrontierItem
  helper (which already threads item.parentIpnsName correctly).

- Bug B (SC#2): the normal branch enqueues root's children first
  then appends all preRotationDirtyFrontier items regardless of
  depth, so a deep dirty node could dequeue before its own parent's
  parentTracking entry exists. Added a bounded requeue guard (defer
  an item whose parent isn't tracked yet but is still pending in the
  queue), a lazy resolveParentTrackingState fallback at the re-seal
  gate, and a decrement on the skipped-idempotency path so a
  parent's batched republish is never silently dropped.

New shared helpers: findParentNodeByIpnsName (module-level tree walk
reusing the existing resolveChildKeyAndEnvelope key-chain primitive)
and resolveParentTrackingState (resolve-or-seed an arbitrary parent's
ParentTrackingState), both following the file's terminal-owner
zeroization discipline.
- rotation-high-water.ts: createRotationHighWater now takes a SINGLE
  combined HighWaterStore seam instead of separate generationStore/
  seqStore -- CombinedFloorRecord holds
  { generation?, seq?, wrappedKeyCheckpoint? } for a nodeId
- enforceResolved reads the combined record once and, on a
  non-regressing resolve, writes generation AND seq back via ONE
  store.put call -- closes the cross-store sequencing window the
  module doc previously flagged as a DOC-ONLY residual (SC#4/D-06)
- bumpGeneration/bumpSeq/seedFromGrant reuse the same single-record
  read-modify-write helper, always preserving the other field and the
  wrappedKeyCheckpoint untouched
- public RotationHighWater interface (getGenerationFloor/bumpGeneration/
  seedFromGrant/getSeqFloor/bumpSeq/enforceResolved) is unchanged, so
  client.ts and the existing client-rotation/folder-listing-gate/
  folder-reresolve test mocks keep compiling against the same shape
- export CombinedFloorRecord from the package barrel for apps/web's
  concrete IndexedDB adapter
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FSM1 and others added 2 commits July 9, 2026 03:45
axiosInstance.delete rejects on non-2xx before the status check runs, so a failed cleanup revoke aborted Part B; wrapped in try/catch to keep the intended non-fatal behavior. CodeRabbit finding 4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: b50674a909a2
SC#6 row reflects the completed green live-stack run; PATTERNS intro no longer over-claims all files self-analogous. CodeRabbit findings 1 and 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 09d1efafa6ed
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@FSM1, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22363db7-3a2b-4276-8cee-e8e39e7e8a62

📥 Commits

Reviewing files that changed from the base of the PR and between 6afbf2d and 620202d.

📒 Files selected for processing (3)
  • .planning/todos/completed/2026-07-07-fuse-shared-scope-exit-rotation-live-wiring.md
  • .planning/todos/completed/2026-07-08-rotation-crash-resume-depth2-soundness-gap.md
  • .planning/todos/pending/2026-07-09-deep-scope-exit-rotation-refreshes-only-grant-root-inode-key.md

Walkthrough

This PR advances Phase 70.1 by updating planning/state docs, implementing checkpoint-backed rotation durability and fail-closed gate handling across TS/Rust/FUSE, and adding combined-floor storage plus fresh-generation reconcile gating. It also adds desktop/e2e coverage for shared-scope-exit rotation and crash-resume scenarios.

Changes

Phase 70.1 planning and verification

Layer / File(s) Summary
Roadmap, state, and phase context
.planning/ROADMAP.md, .planning/STATE.md, .planning/debug/*.md, .planning/todos/pending/*
Phase 70.1 status, progress, context, and investigation notes were updated, including new planning todos and debug writeups.
Phase plans, summaries, and validation
.planning/phases/70.1-.../*.md
Phase 70.1 plans, summaries, security, validation, verification, patterns, discussion, and learnings documents were added or expanded for the rotation durability work.
Debug reports and learnings
.planning/debug/*.md, .planning/phases/70.1-.../70.1-LEARNINGS.md
Investigation notes document the timeout, crash-safety, and scope-exit failures and the resulting resolution paths.

Rotation implementation

Layer / File(s) Summary
TypeScript rotation engine and tests
packages/sdk-core/src/rotation/engine.ts, packages/sdk-core/src/rotation/index.ts, packages/sdk-core/src/index.ts, packages/sdk-core/package.json, packages/sdk-core/src/__tests__/rotation/engine.test.ts
The TS rotation engine now distinguishes dirty edges before decryption, persists ECIES key checkpoints, exposes checkpoint-related exports, and adds test coverage for repair and fail-closed behavior.
Rust rotation engine and floor API changes
crates/sdk/src/rotation/mod.rs, crates/sdk/src/adapter.rs, crates/sdk/src/emit.rs, crates/sdk/src/rotation/high_water.rs
Rust rotation APIs now propagate durable write failures, and the rotation module re-exports updated helpers.
Combined floor storage and IndexedDB adapter
crates/sdk/src/floor_store.rs, crates/sdk/src/lib.rs, crates/sdk/src/listing.rs, crates/sdk/src/queue.rs, packages/sdk/src/state/rotation-high-water.ts, packages/sdk/src/state/rotation-idb-store.ts, packages/sdk/package.json, packages/sdk/tsup.config.ts, packages/sdk/src/index.ts, packages/sdk/src/types.ts, apps/web/src/services/rotation-state.service.ts, packages/sdk/src/__tests__/*.ts
Rust JSON sidecar storage and TS IndexedDB storage now use combined per-node records with wrapped-key fields, migration, and fail-closed behavior.
Client reconcile and rotation wiring
packages/sdk/src/client.ts, packages/sdk/src/types.ts, packages/sdk/src/__tests__/client-rotation.test.ts, packages/sdk/src/__tests__/reconcile-generation-gate.test.ts
Reconcile now gates on freshly-resolved generation, and the scope-exit rotation path threads checkpoint callbacks and key sources through to the engine.
FUSE grant-scope correctness and production adapter
crates/fuse/src/write_ops/grant_scope.rs, crates/fuse/src/write_ops/rotation_deps.rs, crates/fuse/src/write_ops/mod.rs, crates/fuse/src/fs.rs, crates/fuse/src/test_support.rs, crates/fuse/src/write_ops/implementation/delete.rs, crates/fuse/src/write_ops/implementation/rename.rs, crates/fuse/src/platform/windows/write_ops.rs
Grant-scope detection, coalesced delete/rename rotation, live read-key refresh, and production rotation adapter wiring were updated for fail-closed behavior.
Desktop mount wiring
apps/desktop/src-tauri/src/fuse/mod.rs, apps/desktop/src-tauri/src/fuse/windows/mod.rs
Desktop mounts now seed and refresh sent-shares and hold a rotation checkpoint store.
E2E and desktop-e2e coverage
tests/sdk-e2e/src/suites/rotation-crash-safety.test.ts, tests/desktop-e2e/scripts/shared-scope-exit-rotation.mts, tests/desktop-e2e/scripts/run-all.sh, tests/desktop-e2e/scripts/run-all.ps1
New crash-resume and real-mount shared-scope-exit acceptance tests were added and wired into orchestration.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • FSM1/cipher-box#579: Shares the same packages/sdk-core/src/rotation/engine.ts and scope/rotation control flow.
  • FSM1/cipher-box#587: Extends the same reconcile gating and scope-exit rotation wiring in packages/sdk/src/client.ts.
  • FSM1/cipher-box#594: Continues the same FUSE grant-root scope-exit rotation path and live wiring.

Suggested labels: release:api:fix, release:web:fix, release:desktop:fix, release:sdk-core:fix, release:cipherbox-fuse:fix

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main theme of the changes: hardening rotation durability and crash-resume behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rotation-read-plane-durability-and-deep-crash-resume-soundne

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added release:cipherbox-sdk:feat Minor version bump (new feature) for cipherbox-sdk release:cipherbox-fuse:feat Minor version bump (new feature) for cipherbox-fuse release:web:feat Minor version bump (new feature) for web release:sdk-core:feat Minor version bump (new feature) for sdk-core release:sdk:feat Minor version bump (new feature) for sdk release:desktop:feat Minor version bump (new feature) for desktop release:tee-worker:fix Patch version bump (bug fix) for tee-worker labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Release Preview

Package Bump Label Source
cipherbox-fuse minor release:cipherbox-fuse:feat Direct (feat commit)
cipherbox-sdk minor release:cipherbox-sdk:feat Direct (feat commit)
desktop minor release:desktop:feat Direct (feat commit)
sdk minor release:sdk:feat Direct (feat commit)
sdk-core minor release:sdk-core:feat Direct (feat commit)
tee-worker patch release:tee-worker:fix Cascade (sdk-core minor)
web minor release:web:feat Direct (feat commit)

Cascade Details

  • sdk-core minor -> tee-worker patch (direct dependency)

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.29208% with 405 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.53%. Comparing base (faa781e) to head (620202d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/sdk-core/src/rotation/engine.ts 66.54% 93 Missing and 2 partials ⚠️
crates/fuse/src/write_ops/rotation_deps.rs 85.27% 77 Missing ⚠️
crates/fuse/src/write_ops/grant_scope.rs 82.37% 58 Missing ⚠️
apps/desktop/src-tauri/src/fuse/mod.rs 0.00% 41 Missing ⚠️
crates/fuse/src/fs.rs 58.06% 39 Missing ⚠️
crates/fuse/src/write_ops/implementation/delete.rs 84.29% 30 Missing ⚠️
crates/fuse/src/write_ops/implementation/rename.rs 92.38% 15 Missing ⚠️
crates/sdk/src/floor_store.rs 94.91% 15 Missing ⚠️
packages/sdk/src/client.ts 67.44% 14 Missing ⚠️
packages/sdk/src/state/rotation-idb-store.ts 94.75% 11 Missing and 1 partial ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #598      +/-   ##
==========================================
+ Coverage   65.99%   75.53%   +9.54%     
==========================================
  Files         154      195      +41     
  Lines       14748    31803   +17055     
  Branches     1731     1839     +108     
==========================================
+ Hits         9733    24023   +14290     
- Misses       4770     7530    +2760     
- Partials      245      250       +5     
Flag Coverage Δ
api 80.06% <78.77%> (+0.22%) ⬆️
api-client 80.06% <78.77%> (+0.22%) ⬆️
core 80.06% <78.77%> (+0.22%) ⬆️
crypto 80.06% <78.77%> (+0.22%) ⬆️
desktop 21.41% <0.00%> (-10.03%) ⬇️
rust 78.55% <86.67%> (?)
sdk 80.06% <78.77%> (+0.22%) ⬆️
sdk-core 80.06% <78.77%> (+0.22%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
crates/fuse/src/test_support.rs 100.00% <100.00%> (ø)
crates/sdk/src/adapter.rs 84.61% <ø> (ø)
crates/sdk/src/emit.rs 76.53% <100.00%> (ø)
crates/sdk/src/listing.rs 96.58% <100.00%> (ø)
crates/sdk/src/rotation/engine.rs 87.75% <ø> (ø)
crates/sdk/src/rotation/high_water.rs 99.66% <97.87%> (ø)
crates/sdk/src/queue.rs 95.76% <95.91%> (ø)
packages/sdk/src/state/rotation-high-water.ts 95.60% <87.87%> (-4.40%) ⬇️
packages/sdk/src/state/rotation-idb-store.ts 94.75% <94.75%> (ø)
packages/sdk/src/client.ts 70.04% <67.44%> (-0.09%) ⬇️
... and 8 more

... and 83 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This phase hardens the read-key rotation engines (TypeScript sdk-core + Rust sdk) and the FUSE/WinFsp shared-scope-exit rotation live-wiring with depth-aware crash-resume, a combined atomic anti-rollback floor record, and D-15a/b fail-closed gates.

  • Depth-aware dirty-frontier BFS (both engines): collectDirtyFrontier/collect_dirty_frontier now performs a plaintext generation comparison before any unseal attempt, fixing the T1 AEAD-crash regression. DirtyFrontierItem.nodeReadKey is always undefined; the repair path recovers the key exclusively via the ECIES checkpoint plane.
  • Combined anti-rollback floor record (SC#4/D-06): Both TS and Rust collapse the previous two-store/two-sidecar shapes into a single CombinedFloorRecord, eliminating the cross-store sequencing window. A DB_VERSION-2 migration fold handles existing deployments.
  • FUSE production RotationDeps adapter (Plan 70.1-09): FuseRotationDeps wires resolve/fetch/publish to ApiClientTransport, ECIES-wraps the D-01 checkpoint under the owner's own keypair, and correctly materialises a remote node on 409 conflict (Pitfall 7). D-15a/b gates fail closed on a non-authoritative shares cache or incomplete ancestor walk.

Confidence Score: 5/5

Safe to merge. The correctness fixes are well-scoped and covered by the new CI suite. No data-loss or key-material exposure issues were found in the changed paths.

The ECIES checkpoint seam and combined floor store are backed by new integration tests using fake-indexeddb and real sealNode/unsealNode calls. The Rust changes mirror TS semantics closely and are validated by the acceptance suite. The two flagged items are non-blocking: the reconcileFolderSequence AEAD failure is intentional fail-closed behavior missing error-message polish, and the migration || edge case is theoretically unreachable with the old store schema.

packages/sdk/src/client.ts for the new AEAD failure mode in reconcileFolderSequence; packages/sdk/src/state/rotation-idb-store.ts for the migration early-exit condition.

Important Files Changed

Filename Overview
crates/sdk/src/rotation/engine.rs Major expansion: depth-aware BFS with collect_dirty_frontier, ECIES checkpoint seam, VerifySubtreeOutcome.is_dirty signal fixing missing-root false-convergence, and coalesced scope-exit child-override.
packages/sdk-core/src/rotation/engine.ts TS twin gains parity with Rust: depth-aware collectDirtyFrontier, findParentNodeByIpnsName, DirtyFrontierItem.nodeReadKey always undefined, isDirty signal for missing-root.
packages/sdk/src/state/rotation-idb-store.ts New CI-covered IndexedDB adapter with combined record schema, DB_VERSION-2 migration fold, D-08 degradation, and max-preserving writes. Migration early-exit uses
packages/sdk/src/state/rotation-high-water.ts Collapses dual-store seam into a single CombinedFloorRecord store (SC#4/D-06). bumpField correctly max-preserves. API breaking change correctly updated in all consumers.
packages/sdk/src/client.ts reconcileFolderSequence now performs IPFS fetch+unseal for real generation (SC#5/D-09). New AEAD failure path when folderKey is stale lacks a user-actionable error message.
crates/fuse/src/write_ops/rotation_deps.rs New production FuseRotationDeps adapter: generic over RotationTransport; ECIES wrap/unwrap for D-01/D-05; Pitfall 7 (409 conflict materialisation) handled correctly.
crates/fuse/src/write_ops/grant_scope.rs D-15a/b fail-closed gates added; detect_scope_exit split enables borrow-checker-safe threading; install_sent_shares_cache correctly recovers poisoned RwLock.
crates/sdk/src/floor_store.rs Combined CombinedFloorRecord sidecar replaces two-file shape. Legacy fold-forward is safe. is_reserved_floor_sidecar prevents noisy parse warnings.
packages/sdk/src/tests/rotation-idb-store.test.ts New CI-covered test suite using fake-indexeddb: legacy migration fold, max-preserving writes, wrapped-key round trip, D-08 degradation.
packages/sdk/src/tests/reconcile-generation-gate.test.ts New test validates reconcileFolderSequence uses the unsealed real generation with genuine AAD-bound envelopes.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant Engine as RotateReadFromNode
    participant Store as ECIES Checkpoint Store
    participant IPNS

    Note over Engine: Pre-rotation verify_subtree_clean
    Engine->>IPNS: resolve(root_ipns)
    IPNS-->>Engine: cid + seq
    Engine->>Engine: collectDirtyFrontier (generation compare BEFORE unseal)
    Engine->>IPNS: rotate_one root mint readKeyPrime
    Engine->>Store: persist_wrapped_key nodeId ECIES readKeyPrime
    Store-->>Engine: OK D-03 checkpoint before publish
    Engine->>IPNS: publish root new_read_key
    loop BFS children
        Engine->>Store: persist_wrapped_key childId
        Engine->>IPNS: publish child
        Engine->>Store: delete_wrapped_key childId after parent mirror commits D-04
    end
    alt Crash-resume dirty node
        Engine->>Store: get_wrapped_key nodeId
        Store-->>Engine: ECIES ciphertext
        Engine->>Engine: unwrap repairDirtyNode
    end
    Note over Client: reconcileFolderSequence SC5
    Client->>IPNS: resolveIpnsRecord folderIpns
    Client->>IPNS: fetchFromIpfs cid
    Client->>Client: unsealNode pub folderKey real generation
    Client->>Store: enforceResolved seq generation versionFloor
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant Engine as RotateReadFromNode
    participant Store as ECIES Checkpoint Store
    participant IPNS

    Note over Engine: Pre-rotation verify_subtree_clean
    Engine->>IPNS: resolve(root_ipns)
    IPNS-->>Engine: cid + seq
    Engine->>Engine: collectDirtyFrontier (generation compare BEFORE unseal)
    Engine->>IPNS: rotate_one root mint readKeyPrime
    Engine->>Store: persist_wrapped_key nodeId ECIES readKeyPrime
    Store-->>Engine: OK D-03 checkpoint before publish
    Engine->>IPNS: publish root new_read_key
    loop BFS children
        Engine->>Store: persist_wrapped_key childId
        Engine->>IPNS: publish child
        Engine->>Store: delete_wrapped_key childId after parent mirror commits D-04
    end
    alt Crash-resume dirty node
        Engine->>Store: get_wrapped_key nodeId
        Store-->>Engine: ECIES ciphertext
        Engine->>Engine: unwrap repairDirtyNode
    end
    Note over Client: reconcileFolderSequence SC5
    Client->>IPNS: resolveIpnsRecord folderIpns
    Client->>IPNS: fetchFromIpfs cid
    Client->>Client: unsealNode pub folderKey real generation
    Client->>Store: enforceResolved seq generation versionFloor
Loading

Reviews (4): Last reviewed commit: "docs: capture todo - deep scope-exit rot..." | Re-trigger Greptile

Comment thread crates/fuse/src/write_ops/rotation_deps.rs
Comment thread crates/fuse/src/write_ops/rotation_deps.rs Outdated
Comment thread crates/sdk/src/floor_store.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.planning/debug/scope-exit-part-a-fail.md:
- Around line 460-504: The current “fixed” description overstates the scope
because the deep scope-exit path still leaves intermediate parents resealing
with stale in-memory keys. Update the status in this note to clearly mark the
change as shallow D-16/grant-root-only, or extend the fix in the scope-exit flow
so the deep path also refreshes all rotated parents; reference the
`rotate_read_on_scope_exit`, `refresh_grant_root_read_key`, and
`rotate_read_from_node_with_root_children` paths when adjusting the wording or
implementation.

In
@.planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-11-SUMMARY.md:
- Around line 76-81: The summary overstates test closure by implying the full
crate suite is green, while the deferred-failures section still notes
private-delete tests fail outside the scoped grant_scope run. Update the wording
in the summary block and the related deferred-failures text to clearly limit the
claim to the scoped verification, using the existing grant_scope.rs /
Grant-Scope Gate Fail-Closed Correctness Summary language so readers do not
infer full-crate success.

In `@crates/sdk/src/floor_store.rs`:
- Around line 697-708: The permission-failure test in the floor store logic can
still succeed under root, so the `expect_err` assertion may panic in root-based
CI. Update the test around `store.put` to either skip the chmod-based failure
path when running as root or use an alternative write-failure setup that
reliably fails regardless of `euid`; keep the change localized to the
`#[cfg(unix)]` permission block and the subsequent failure assertion in
`floor_store.rs`.

In `@packages/sdk-core/src/rotation/engine.ts`:
- Around line 1789-1796: Checkpoint GC is happening too early in the repair
flow, before the batched parent republish has fully committed. Update the
`decrementPendingAndMaybeRepublish` / `updateFolderMetadataAndPublish` path so
`deleteWrappedKey` is deferred until the final republish completes, or store
pending child IDs on `ParentTrackingState` and flush them there. Keep the
`deleteWrappedKey(item.childPubId)` cleanup out of the immediate repair branch
so the checkpoint survives until the parent mirror update is safely committed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58a22cc8-8452-4cf3-924c-f7201fc1bb04

📥 Commits

Reviewing files that changed from the base of the PR and between 7356707 and c91d5c3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
📒 Files selected for processing (82)
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/debug/macos-first-publish-timeout.md
  • .planning/debug/rotation-crash-safety-depth3.md
  • .planning/debug/scope-exit-part-a-fail.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-01-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-01-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-02-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-02-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-03-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-03-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-04-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-04-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-05-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-05-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-06-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-06-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-07-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-07-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-08-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-08-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-09-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-09-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-10-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-10-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-11-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-11-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-12-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-12-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-13-PLAN.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-13-SUMMARY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-CONTEXT.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-DISCUSSION-LOG.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-PATTERNS.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-RESEARCH.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-SECURITY.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-VALIDATION.md
  • .planning/phases/70.1-rotation-read-plane-durability-and-deep-crash-resume-soundne/70.1-VERIFICATION.md
  • .planning/todos/pending/2026-07-07-fuse-shared-scope-exit-rotation-live-wiring.md
  • .planning/todos/pending/2026-07-08-desktop-query-grants-rooted-at-remint-noop.md
  • .planning/todos/pending/2026-07-08-winfsp-d15d-gate-ordering-parity.md
  • .planning/todos/pending/2026-07-09-macos-fuse-t-cross-client-sync-invalidation.md
  • apps/desktop/src-tauri/src/fuse/mod.rs
  • apps/desktop/src-tauri/src/fuse/windows/mod.rs
  • apps/web/src/services/rotation-state.service.ts
  • crates/fuse/src/fs.rs
  • crates/fuse/src/platform/windows/write_ops.rs
  • crates/fuse/src/test_support.rs
  • crates/fuse/src/write_ops/grant_scope.rs
  • crates/fuse/src/write_ops/implementation/delete.rs
  • crates/fuse/src/write_ops/implementation/rename.rs
  • crates/fuse/src/write_ops/mod.rs
  • crates/fuse/src/write_ops/rotation_deps.rs
  • crates/sdk/src/adapter.rs
  • crates/sdk/src/emit.rs
  • crates/sdk/src/floor_store.rs
  • crates/sdk/src/lib.rs
  • crates/sdk/src/listing.rs
  • crates/sdk/src/queue.rs
  • crates/sdk/src/rotation/engine.rs
  • crates/sdk/src/rotation/high_water.rs
  • crates/sdk/src/rotation/mod.rs
  • packages/sdk-core/package.json
  • packages/sdk-core/src/__tests__/rotation/engine.test.ts
  • packages/sdk-core/src/index.ts
  • packages/sdk-core/src/rotation/engine.ts
  • packages/sdk-core/src/rotation/index.ts
  • packages/sdk/package.json
  • packages/sdk/src/__tests__/client-rotation.test.ts
  • packages/sdk/src/__tests__/reconcile-generation-gate.test.ts
  • packages/sdk/src/__tests__/rotation-high-water.test.ts
  • packages/sdk/src/__tests__/rotation-idb-store.test.ts
  • packages/sdk/src/client.ts
  • packages/sdk/src/index.ts
  • packages/sdk/src/state/rotation-high-water.ts
  • packages/sdk/src/state/rotation-idb-store.ts
  • packages/sdk/src/types.ts
  • packages/sdk/tsup.config.ts
  • tests/desktop-e2e/scripts/run-all.ps1
  • tests/desktop-e2e/scripts/run-all.sh
  • tests/desktop-e2e/scripts/shared-scope-exit-rotation.mts
  • tests/sdk-e2e/src/suites/rotation-crash-safety.test.ts

Comment thread .planning/debug/scope-exit-part-a-fail.md
Comment thread crates/sdk/src/floor_store.rs Outdated
Comment thread packages/sdk-core/src/rotation/engine.ts Outdated
FSM1 and others added 8 commits July 9, 2026 04:31
…ommits

The TS engine GC'd a repaired dirty child's ECIES key-checkpoint (deleteWrappedKey) immediately, even when its parent had pendingChildCount>1 and had NOT yet republished. A crash in that window left the child's edge dirty on resume with no checkpoint to recover from -> DirtyNodeUnrecoverableError (permanent loss of read access to the subtree). Now queues repaired child ids on ParentTrackingState.pendingCheckpointNodeIds and flushes deleteWrappedKey only in decrementPendingAndMaybeRepublish's count===0 branch, AFTER updateFolderMetadataAndPublish durably commits — porting the Rust twin's already-correct pending_checkpoint_node_ids / complete_pending_child ordering (restores D-01/D-04 parity). CodeRabbit PR review (Major).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: d2524b6f16e5
The grant_scope test module used crate::test_support (fuse-only harness) but was gated on #[cfg(test)] alone, so the Windows 'cargo test --features winfsp' build compiled it and failed with E0433 (test_support configured out). Gated on #[cfg(all(test, feature = "fuse"))] like every sibling module. Surfaced by the first full PR CI run on the branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 1d805ec219c5
…er key

(1) Add the // sc6-allow marker on ApiClientTransport::resolve's resolve_ipns_verified call — the rotation transport's OWN verified fail-closed chokepoint (D-08), not a read-plane bypass, satisfying the SC#6 single-gated-read CI grep gate. (2) Hold FuseRotationDeps.owner_private_key in Zeroizing so the ECIES private key is wiped on drop (Greptile PR review).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 5bc796044848
Add a scope note under the Resolution headline marking status: fixed as shallow D-16 / grant-root-only; deep-path intermediate-node key refresh stays a documented out-of-scope follow-up. CodeRabbit PR review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 629901f6d06f
put_fails_closed_on_write_failure_and_enforce_resolved_surfaces_it induces the write failure by chmod-ing the dir read-only (0o500), which Windows' permission model does not honor — the create still succeeds, so the unconditional expect_err panicked on the Windows 'cargo test --features winfsp' job. Gated the whole test on #[cfg(unix)] (matching the other permission tests); the fail-closed D-08 behavior is platform-agnostic and stays covered on Linux/macOS. Surfaced by the first full PR CI run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: f3fece7e4fc5
Ship-cycle learnings: TS/Rust twin-parity is not automatic (CodeRabbit caught the checkpoint-GC divergence); open the PR earlier so branch-wide CI gates run sooner; verify whether CI fmt-checks before running cargo fmt; adversarial-reviewer fix-or-refute pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 2902e4f9064f
Move the two resolves_phase: 70.1 todos to completed/: the depth>=2 crash-resume soundness gap (delivered by SC#1/2/3/6/7, depth-3 crash-safety e2e green) and the FUSE shared-scope-exit rotation live-wiring (delivered by SC#8, D-16 acceptance green on all 3 platforms, incl. the D-15a/b/c/d gate-correctness findings). The ROT-04 remint no-op, WinFsp D-15d rename parity, and macOS FUSE-T Test 5/7 invalidation remain pending as sanctioned deferrals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: da8cc1282572
…efresh

Deep scope-exit rotation refreshes only the grant-root inode's in-memory read key; intermediate parent inodes keep stale pre-rotation keys (latent deep-path revocation-bypass). Documented in the Phase 70.1 D-16 debug note but was untracked as a forward item.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: e7c0a0d3701c
@FSM1 FSM1 enabled auto-merge (squash) July 9, 2026 10:17
@FSM1 FSM1 merged commit d5486e5 into main Jul 9, 2026
30 checks passed
@FSM1 FSM1 deleted the feat/rotation-read-plane-durability-and-deep-crash-resume-soundne branch July 9, 2026 10:19
FSM1 added a commit that referenced this pull request Jul 10, 2026
…store

Phase 70.1 (D-06, #598) collapsed the generation-high-water and
seq-high-water IndexedDB stores into a single rotation-floor store keyed
by nodeId holding a { generation?, seq?, wrappedKeyCheckpoint? } record.
The rotation-durability e2e helpers still probed the retired store names,
so readDurableFloors short-circuited and returned generation: undefined,
failing the SC#4 setup assertion at line 216 on every main run since #598.

Point readDurableFloors and writeDurableSeqFloor at the rotation-floor
store; the write does a read-modify-write so it preserves the node's
existing generation/wrappedKeyCheckpoint floors. Test-only -- the product
write path (rotation-idb-store.ts) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 545aa91edc15
FSM1 added a commit that referenced this pull request Jul 10, 2026
…store (#600)

* test(web-e2e): align rotation-durability floor helpers with combined store

Phase 70.1 (D-06, #598) collapsed the generation-high-water and
seq-high-water IndexedDB stores into a single rotation-floor store keyed
by nodeId holding a { generation?, seq?, wrappedKeyCheckpoint? } record.
The rotation-durability e2e helpers still probed the retired store names,
so readDurableFloors short-circuited and returned generation: undefined,
failing the SC#4 setup assertion at line 216 on every main run since #598.

Point readDurableFloors and writeDurableSeqFloor at the rotation-floor
store; the write does a read-modify-write so it preserves the node's
existing generation/wrappedKeyCheckpoint floors. Test-only -- the product
write path (rotation-idb-store.ts) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 545aa91edc15

* docs(web-e2e): clarify writeDurableSeqFloor seq is a direct overwrite

The doc comment claimed the read-modify-write "matches production's
max-preserving write", which is misleading: production idbPutFloors
max-preserves seq, whereas this staging helper intentionally overwrites seq
so a test can pin an exact sequence floor. Only the preservation of the
other fields (generation / wrappedKeyCheckpoint) matches production.
Comment-only; no behavior change. Addresses CodeRabbit review on #600.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: f73432632360

* test(sdk-e2e): accept idempotent two-200 outcome in D-06 concurrent publish

Test 21 (D-06) fires two concurrent first-publishes of the same brand-new
ipnsName with the SAME CID at sequence=1, and asserted exactly one 200 plus
one 409. But same-CID / same-sequence is idempotent by design (#599): when
the loser serializes after the winner commits, it re-reads the identical CID
and returns an idempotent 200 -- so both calls can legitimately succeed,
flaking the strict one-409 assertion (observed red on #600's SDK E2E leg).

Accept both correct shapes: at least one winner (never zero) and any loser is
a clean 409 (never a 5xx). The follow-up resolve still asserts exactly one row
at sequence=1. Test-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: d752b84767ae

* test: stabilize web-e2e batch-op and shared-mkdir timeouts

Batch delete/move (4.10/4.11), version restore poll (6.6.2), the serial
root-file delete chain (8.2), and recipient-side shared mkdirs each drive
IPNS publish round-trips whose serial-calibrated waits were too tight,
flaking locally and under parallel-worker CI load. Raise the explicit
per-call timeouts (CI already floors waiters to 60s via ciFloor) and make
6.6.2 poll the editor for the restored content instead of reading once.

Verified: full-workflow.spec.ts 53/53 green locally under the CI 60s-floor
timeout regime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: 1fe70eb98011

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:cipherbox-fuse:feat Minor version bump (new feature) for cipherbox-fuse release:cipherbox-sdk:feat Minor version bump (new feature) for cipherbox-sdk release:desktop:feat Minor version bump (new feature) for desktop release:sdk:feat Minor version bump (new feature) for sdk release:sdk-core:feat Minor version bump (new feature) for sdk-core release:tee-worker:fix Patch version bump (bug fix) for tee-worker release:web:feat Minor version bump (new feature) for web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant