Fix date inconsistency in TECHNICAL_ARCHITECTURE.md#7
Merged
Conversation
Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Update details for IPFS/IPNS relay endpoint feedback
Fix date inconsistency in TECHNICAL_ARCHITECTURE.md
Jan 18, 2026
FSM1
approved these changes
Jan 18, 2026
FSM1
added a commit
that referenced
this pull request
Jan 18, 2026
* bump version to 1.8.1 and update documentation for IPFS/IPNS relay integration * Fix date inconsistency in TECHNICAL_ARCHITECTURE.md (#7) * Initial plan * Fix date inconsistency in TECHNICAL_ARCHITECTURE.md Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix date inconsistency in DATA_FLOWS.md frontmatter (#8) * Initial plan * Fix date inconsistency in DATA_FLOWS.md frontmatter Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add missing CID return arrows in IPFS/IPNS sequence diagrams (#6) * Initial plan * Add missing return arrows in IPFS/IPNS sequence diagrams Added `B->>C: Return CID` after all `B->>IPFS: Add metadata, return CID` operations to show that the Backend returns the CID to the Client before the Client signs the IPNS record. The Client needs this CID to create the IPNS record that points to it. Updated 8 sequence diagrams: - File Upload Flow (IPNS publish) - Create Folder (empty folder + update parent) - Rename File/Folder - Move File/Folder (destination + source) - Delete File - Update File Content Bumped version to 1.8.2 Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add missing Backend→Client CID return flows in IPFS/IPNS sequence diagrams (#5) * Initial plan * Add return arrows from Backend to Client in IPFS/IPNS sequence diagrams Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add missing Backend→Client CID return arrows in IPFS sequence diagrams (#10) * Initial plan * Add missing Backend->Client CID return arrows in sequence diagrams Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add missing CID return step in IPNS publish sequence diagrams (#16) * Initial plan * Add missing return CID step in IPNS publish sequences Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix date inconsistency in API_SPECIFICATION.md (#14) * Initial plan * Fix date inconsistency in API_SPECIFICATION.md Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix missing YAML frontmatter delimiter in API_SPECIFICATION.md (#15) * Initial plan * Fix YAML frontmatter: add missing opening delimiter to API_SPECIFICATION.md Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix missing Backend→Client CID return in IPFS/IPNS sequence diagrams (#11) * Initial plan * Fix sequence diagrams: add missing Backend->Client CID return arrows Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix missing return arrow in IPNS publishing sequence diagram (#13) * Initial plan * Add missing return arrow in IPNS publishing sequence diagram Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix date inconsistency in CLIENT_SPECIFICATION.md (#9) * Initial plan * Fix date inconsistency in CLIENT_SPECIFICATION.md Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add missing Backend→Client CID return flows in IPFS metadata upload sequences (#12) * Initial plan * Add missing return arrows showing CID returned from backend to client after IPFS metadata upload Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix documentation inconsistencies and clarify IPFS/IPNS relay architecture (#17) * Initial plan * Fix all PR review feedback items Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Improve documentation clarity per code review feedback Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Add BASE64 encoding steps in IPNS publish sequences and bump to v1.9.0 (#18) * Initial plan * Add BASE64 encoding steps in IPNS sequences and bump version to 1.9.0 Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> * Fix inconsistent indentation in mermaid sequence diagrams (#19) * Initial plan * Fix mermaid diagram indentation to use consistent 4-space formatting Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: FSM1 <12774278+FSM1@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
FSM1
added a commit
that referenced
this pull request
Jun 15, 2026
* docs: add phases 46 (desktop FUSE) and 47 (SDK), close mock-ipns todo Group the outstanding pending todos into two planned phases: - Phase 46 — Desktop FUSE data-loss bugs + replay hardening: the #7/#8/#17 data-loss bugs Phase 45 deferred, the two PR #491 replay follow-ups (empty file_meta_ipns_name parking, strict IPNS resolve), and the deferred read_ops/write_ops + journal_helpers test coverage (6 desktop todos). - Phase 47 — SDK folder-state and publish-path consolidation: unify folderTree/Zustand ownership, one publishWithCas CAS-retry, encapsulate baseChildren bookkeeping, fix updateSharedFile prunedCids pin leak (4 SDK todos). Also close the mock-ipns-routing-layer investigation todo (won't do — moved pending -> done with a closed_reason). Update ROADMAP + STATE (phase counts 30 -> 32, status, current position, pending count 18). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 86c4d8e330df * docs: correct Phase 47 folder-state requirement to single-source-of-truth Requirement (1) described the reconcileFolderState band-aid ("reconcile folderTree before sdk-core publishes"). Correct it to the todo's actual cure: route the two leaking web file hooks through new SDK client methods, make useFolderStore a projection fed only by folder:updated events, and delete reconcileFolderState. Clarify scope is folder-state-mutating paths only — non-mutating sdk-core usage in the web app stays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 099be3f795be --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
FSM1
added a commit
that referenced
this pull request
Jun 18, 2026
…solidation (#509) * docs: add phase 49 — shared-folder intra-share move + useFolderNavigation consolidation Entire-Checkpoint: e58bf755e671 * docs: add phase 49 pre-planning context (cross-layer discovery) Entire-Checkpoint: 08cdb22c0127 * docs(49): add research + validation strategy Entire-Checkpoint: cc679fefc1f7 * docs(49): add pattern map Entire-Checkpoint: 244b430c8211 * docs(49): expand scope to include shared batch + drag move (REQ-6, private-vault parity) Entire-Checkpoint: e7041909f23d * docs(49): create phase plan for shared-folder move + useFolderNavigation consolidation 5 plans across 3 waves: SDK enumerate+move op (TDD), useFolderNavigation unwrap consolidation, web move UX, batch+drag parity, and the two-account decrypt-survival e2e. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 5aa79041aceb * docs(49): revise plans — add moveItemHandler unit test (REQ-3), mark research resolved Entire-Checkpoint: aa1b86a5cc69 * test(49-01): add failing tests for moveInSharedFolder op + client method and enumerateSharedSubtree Entire-Checkpoint: 3022e6584373 * feat(49-01): implement moveInSharedFolder op and client method, enumerateSharedSubtree - Add moveInSharedFolder stateless op to shared-write.ts (dual src/dest context, publish DEST first, re-key FileMetadata, publish SOURCE, no key zeroing in op) - Export moveInSharedFolder from share/index.ts - Add CipherBoxClient.moveInSharedFolder with write-cap guard (T-49-01), fresh dest loadFolderMetadata (A1), adoptSharedFolderResult for SOURCE only, finally-zeroing destFolderKey/destIpnsPrivateKey/fileIpnsPrivateKey (T-49-04) - Add CipherBoxClient.enumerateSharedSubtree with stack-based DFS, visited Set guard (cycle prevention), writable flag from folder-ipns key presence Entire-Checkpoint: b1ede215cbe2 * docs(49-01): complete moveInSharedFolder + enumerateSharedSubtree plan Entire-Checkpoint: 4ba821aa32f3 * refactor(49-02): replace hand-rolled ECIES unwrap in useFolderNavigation with ensureFolderLoaded - Remove duplicate web-side unwrapKey + resolveIpnsRecord + fetchAndDecryptMetadata block (lines 241-302) - Delegate unwrap/resolve/decrypt to client.ensureFolderLoaded (SDK single source of truth) - Preserve 3x/2s IPNS-propagation retry wrapper with latestNavTarget guard on each iteration - Clone SDK-owned key buffers into FolderNode (new Uint8Array) to prevent use-after-zero on client.destroy() - Remove now-dead imports: unwrapKey, hexToBytes, fetchAndDecryptMetadata, resolveIpnsRecord, useAuthStore - Add FolderState type import from @cipherbox/sdk for mapping Entire-Checkpoint: 923d8deca666 * docs(49-02): complete useFolderNavigation unwrap consolidation plan Entire-Checkpoint: fc527fd40cf7 * feat(49-03): add moveInSharedFolder+enumerateSharedSubtree to Pick allowlist + moveItemHandler - Add 'moveInSharedFolder' and 'enumerateSharedSubtree' to SharedFolderClient Pick union - Add moveItemHandler to useSharedWriteOps routing through runWrite -> client.moveInSharedFolder - Expose moveItem in useSharedNavigation return type Entire-Checkpoint: 2dffc00ec239 * test(49-03): add moveItemHandler unit case to useSharedWriteOps test - Add vi.mock stubs for react/useCallback, auth store, sdk-provider, share.service - Add 3 moveItemHandler cases: routes runWrite->client.moveInSharedFolder, surfaces errors, guards absent keypair - Tests run in node env (no React render harness) matching existing file style Entire-Checkpoint: a54d3d8ef26e * feat(49-03): build SharedMoveDialog shared subtree picker + fix test type - New SharedMoveDialog loading picker nodes via enumerateSharedSubtree - Props: open/onClose/onConfirm(destFolderId,destIpnsName)/item/currentFolderId/shareId/isLoading - Disables read-only and current-folder rows; role=button + onKeyDown a11y - :focus-visible style added to dialogs.css with modern rgb() notation - SharedMoveDialog readonly badge [RO] for non-writable folders - Fix vi.fn type argument in test for newer vitest compat Entire-Checkpoint: 37b6e4209fa6 * feat(49-03): wire onMove into SharedFileBrowser folder-view ContextMenu - Import SharedMoveDialog and destructure moveItem from useSharedNavigation - Add moveDialogItem state + handleMoveClick callback - Wire onMove on folder-view ContextMenu for write-permission recipients (files only) - List-view (synthetic top-level shares) ContextMenu has no onMove (T-49-09) - Mount SharedMoveDialog with onConfirm calling moveItem; refresh via sharedFolder:updated projection Entire-Checkpoint: a468fcb78a68 * docs(49-03): complete shared-folder move web UI plan - 49-03-SUMMARY.md: SharedMoveDialog + moveItemHandler + onMove wiring - STATE.md: advance to plan 4 of 5, add decisions - ROADMAP.md: mark 49-03 complete Entire-Checkpoint: dccd7953e33e * feat(49-04): multi-select state + SelectionActionBar + batchMoveItemsHandler - Add batchMoveItemsHandler to useSharedWriteOps: loops client.moveInSharedFolder per item inside runWrite, stops on first failure, clearSelection on success - Add batchMoveItems to UseSharedNavigationReturn type - Add selectedIds Set, selectedItems useMemo, multiSelectActive, clearSelection, handleSelect, and handleBatchMoveClick to SharedFileBrowser - Wire SelectionActionBar into folder view (write shares only, multiSelectActive) - Mount batch-mode SharedMoveDialog opened from SelectionActionBar - Pass isSelected/onSelect/onMoveItemTo/selectedItems to SharedFolderRow Entire-Checkpoint: 4ea2f59e45f2 * feat(49-04): SharedMoveDialog accepts items prop + batch confirm routing - Add optional items?: FolderChild[] prop to SharedMoveDialog (mirrors private MoveDialog item|items shape from MoveDialog.tsx:20-21) - isBatchMode flag: items.length > 1 auto-adapts title (Move N items) and label - SharedFileBrowser opens dialog in batch mode from handleBatchMoveClick, passing items={batchMoveItems_} and routing onConfirm to batchMoveItems - Single-item path (item prop) unchanged Entire-Checkpoint: a88d61c493fc * feat(49-04): add drag-and-drop move onto SharedFolderRow - handleDragStart: multi-select-aware payload (application/json {items,parentId}) includes all selectedItems when dragged item is part of a selection (> 1), else single item — mirrors FileListItem :160-177 - handleDrop: folder rows only; parses application/json defensively (T-49-13); routes single item -> moveItemHandler, multi -> batchMoveItemsHandler via onMoveItemTo callback in SharedFileBrowser; ignores payload parentId (drop target row id/ipnsName is the authoritative dest, T-49-12) - handleDragOver/handleDragLeave: visual affordance (isDragOver -> css class file-list-item--drag-over); distinguishes internal moves from external drops via dataTransfer.types.includes('application/json') - onSelect click handler for Ctrl/Cmd+click multi-select selection - External file upload drop (container level) preserved; SharedFolderRow does not handle external file drops (falls through to parent handler) - a11y: draggable + existing role=row + onKeyDown Enter/Space for navigation Entire-Checkpoint: b1e16fc43eff * docs(49-04): complete batch + drag move plan summary and state update Entire-Checkpoint: 4ee9d4a04375 * test(49-05): add SharedMoveDialogPage page object - Mirrors MoveDialogPage shape for the shared-folder picker dialog - dialog() scoped via .move-dialog-folder-list filter (avoids collision with private MoveDialog) - getFolderItem/folderItems target .shared-move-dialog-folder-item rows (role=button) - waitForTreeLoaded polls loading indicator hidden + listbox visible - move() helper: waitForTreeLoaded -> selectFolder -> clickMove -> waitForClose Entire-Checkpoint: 245398a29315 * test(49-05): shared-folder intra-share move + decrypt-survival e2e - Two-account Alice/Bob setup mirroring writable-shares.spec.ts (createWalletTestAccount, closeWalletTestAccounts, navigateToShared) - Alice creates parent folder with a subfolder and a text file; shares read-write with Bob - Bob navigates via SharedFileBrowserPage, right-clicks file -> Move to... -> selects subfolder in SharedMoveDialogPage -> confirm - Asserts file disappears from source view, appears in subfolder in Bob view - readContentViaEditor (right-click -> Edit -> waitForContentLoaded -> getContent) asserts content decrypts for Bob (REQ-5 T-49-14) - alice.page.reload({waitUntil:'networkidle'}) -> owner navigates to subfolder -> readContentViaEditor asserts same decrypted content for Alice - Decrypt-on-read assertion (TextEditorDialogPage.getContent) not mere list visibility Entire-Checkpoint: 6ff2b365878a * docs(49-05): complete intra-share move e2e plan summary and state update - 49-05-SUMMARY.md: SharedMoveDialogPage + shared-folder-move.spec.ts decrypt-survival e2e - STATE.md: completed_plans 150->151 (100%), added P05 metrics row, updated last session - ROADMAP.md: mark 49-04 and 49-05 complete, mark todos #7 and #8 as closed Entire-Checkpoint: 5dea4ff22a11 * docs(49): add phase verification report (6/6 must-haves verified; live e2e deferred to main-push) Entire-Checkpoint: 9821869af98d * refactor(49): simplify shared move row + dialog (reuse DragItem/isExternalFileDrag, collapse reset effects) Entire-Checkpoint: acb4a538dbbd * fix(49): guard shared folder-move cycles + clear batch selection only on success - moveInSharedFolder throws when a folder is moved into itself - enumerateSharedSubtree returns parentId so the picker can disable the moved folder's own subtree (move-into-descendant would cycle the tree) - SharedMoveDialog disables moved folder(s) + descendants; SharedFolderRow drag drop skips dropping a folder onto itself - runWrite returns success; batchMoveItems clears selection only on success Entire-Checkpoint: d63fc00d5327 * fix(49): zero per-iteration folderKey in enumerateSharedSubtree (security review) Each DFS step unwrapped a subfolder AES folderKey but dropped it without zeroing, leaving plaintext folder keys live in the heap across the walk. Wrap each iteration in try/finally with folderKey.fill(0). Entire-Checkpoint: e696538da67f * docs(49): add phase 49 crypto security review report (risk: LOW) Entire-Checkpoint: b300fa0a45ce * fix(49): hide unwired Delete/Download in shared selection bar (appsec review) The shared multi-select action bar stubbed onDownload/onDelete to no-ops, so a Delete button rendered that silently did nothing — misleading users about deletion of shared data. Make those handlers optional in SelectionActionBar and omit them in the shared browser so only the wired Move action renders. Entire-Checkpoint: 4ca756afaef6 * fix(49): address CodeRabbit review findings - drag move routes by the actually-dragged items (forwarded through onMoveItemTo) instead of re-deriving from the current selection / drop target (critical) - useFolderNavigation: treat null after retries as a load failure (redirect to parent) instead of rendering an empty keyless folder - SharedMoveDialog: role=option + aria-selected for the listbox picker (a11y) - SharedFolderRow: Ctrl/Cmd+Enter/Space keyboard selection parity with click - test: vi.spyOn+mockRestore for guaranteed auth-mock cleanup (kept the not-called-with-String assertion — runWrite legitimately calls setError(null)) Entire-Checkpoint: e58f28b54fbc * test(49): address CodeRabbit nitpicks — exact e2e selector, fail-fast load, batch coverage - shared-move-dialog page object: exact folder-name match (no docs/docs-old collision) + waitForTreeLoaded fails fast with a clear message on load error - add batchMoveItems tests: empty early-return, clearSelection only on full success, stop-on-first-failure (locks in the on-success-only selection clear) - skipped the 'item.type !== folder' narrowing nitpick: redundant (isFolder already guards) and tsc -b passes strict without it Entire-Checkpoint: ca5f6fcd5105 * fix(web): shared multi-select bar must not break folder navigation The shared-view batch action bar was gated on `selectedIds.size > 0`, so a plain single click — which is also the first click of a double-click — selected one item and rendered the SelectionActionBar above the file list. That layout shift moved the rows down mid-double-click, so the second click missed the row and the `dblclick` never fired: folder navigation silently failed (e2e writable-shares 8.2 + shared-folder-move 3.x). Gate the bar on `size > 1`, matching the private vault (FileBrowser :205). A single click still highlights the row but no longer pops the bar. Also strip the decorative trailing "/" from folder names in the shared file-browser page object's getFolderItemNames so exact `toContain(name)` assertions work (other specs already used slash-tolerant `.includes`). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 6a5174789ead * feat(share): distribute IPNS write keys to write-share recipients A read-write folder share now grants the recipient the IPNS signing keys (folder-ipns / file-ipns) for owner-created descendant subfolders and files, not just the read keys. Without them a recipient could only write to items they created themselves, so moving a file into an owner-created subfolder was impossible. - collectChildKeys re-wraps each descendant's ipnsPrivateKey for the recipient when permission is write (file-ipns guarded on the optional field; folders always carry it). Invite links stay read-only. - CHILD_KEY_TYPES gains folder-ipns so the create-share endpoint accepts it; regenerated the API client. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 2a7e3a5b1b04 * feat(ipns): decentralized signature-gated IPNS publishing Key the folder_ipns cache by ipnsName alone (one canonical record per name) and authorize publishes by verifying the record's Ed25519 SignatureV2 (key possession) instead of per-user ownership or a write-share lookup. Any holder of a name's key may update it -- the model IPNS itself uses -- so a write-share recipient can publish owner-created descendant records (enabling intra-share moves) with no server-side share check. - verifyIpnsRecordSignature / parseIpnsRecord added to @cipherbox/crypto via the ipns package, replacing the API hand-rolled protobuf parser. - publishRecord verifies the signature before create OR update. - Anti-rollback: reject a record whose embedded (signature-covered) sequence regresses below the stored record's, blocking replay of an old still-valid record to roll a folder back to a stale CID. - Migration dedupes to the highest-sequence row, then swaps the unique constraint user_id+ipns_name to ipns_name. userId retained as a denormalized creator marker for listing / TEE enrollment / cleanup. Tradeoff: write access is now revocable only by IPNS key rotation, not a server-side share revoke (the decentralization goal). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 355a7f89d28a * chore(release): set release targets for PR #509 * test(desktop-e2e): bump IPNS sequence with a real signed record The conflict-detection helper faked a competing-device publish by POSTing a dummy unsigned record to /ipns/publish to advance the server sequence. Under the new signature-gated IPNS model the server rejects unsigned records (400), so the bump failed -- surfacing only on Windows, whose .ps1 throws on a failed bump (the .sh swallowed it as a warning and passed falsely). Replace the dummy bump with bump-ipns-sequence.mjs, which derives the deterministic vault IPNS keypair and republishes the current root metadata UNCHANGED at sequence+1 via the SDK CAS path -- a real, validly-signed, non-destructive bump, exactly what a legitimate second device does. Restores genuine conflict exercise on every platform. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: c4a5195bab1e * docs: todo to migrate untyped .mjs E2E helpers to TypeScript The .mjs E2E helper scripts import dist bundles and are skipped by tsc/eslint/ vitest/jest, so they drift from SDK contract changes and only break deep in slow single-OS E2E runs (e.g. the conflict-detection bump just broke on Windows only). Capture a pending todo to type them so drift fails fast at CI build time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: f930d519570b * fix(share): guard folder.ipnsPrivateKeyEncrypted before re-wrapping The write-share branch re-wrapped folder.ipnsPrivateKeyEncrypted unconditionally. The field is typed required on FolderEntry (so no crash for type-conforming data), but the file branch already guards its optional equivalent — mirror that here so malformed/legacy folder metadata decrypted from IPFS degrades to read-only instead of throwing. Addresses CodeRabbit review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 3c5bc9c42ea9 --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
FSM1
added a commit
that referenced
this pull request
Jun 19, 2026
* docs: reopen v1.1 milestone with hardening block (phases 50-55) Reopen Milestone 3 (v1.1) rather than open a v1.2, since Milestone 4 (v2.0) is already defined, and append a six-phase hardening block absorbing the tracked tech-debt/security todos surfaced during v1.1 verification and audits. Phases: - 50 IPFS/IPNS data-integrity fixes (unpin-integrity findings, unenroll subtrees) - 51 crypto-signature & secret-leak hardening (IPNS signedRecord, web logger redaction) - 52 desktop FUSE durability & at-rest safety (journal growth, replay timeout, plaintext scrub) - 53 release & supply-chain engineering (pin actions to SHAs, Cargo.lock, release-please pins) - 54 E2E test-infra typing (migrate .mjs helpers to TypeScript) - 55 large source-file refactor (split/dedup oversized files) Todos #7 (useFolderNavigation consolidation) and #8 (shared-move re-encrypt) were verified already-resolved by Phase 49 in live code and moved to completed/, so they are not phases. The GSD-tooling STATE regression (#10) is excluded as an upstream chore. New requirement IDs HARD-01..06 map to phases 50-55. Also backfill the ROADMAP progress table for phases 40-55 and correct the stale 38/39 rows (were "Planned", actually 4/4 complete). STATE reopened to In progress (40 phases, 34 complete, 85%). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: b8539c7b282d * docs: address greptile review on v1.1 reopen - ROADMAP execution-order line: extend to phases 50-55 and drop the dangling trailing arrow that ended at "49 -> " (prettier had auto-extended it to 49 without the new hardening phases) - ROADMAP footer: add a 2026-06-19 "Last updated" entry for the reopen and note the 6-phase hardening block in the totals line - REQUIREMENTS footer: reorder the two 2026-06-19 entries so the reopen note follows the PERF verification note (was reverse-insertion order) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 05036e2382fd --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
FSM1
added a commit
that referenced
this pull request
Jun 30, 2026
#6 fail closed when unsealed node has no recoverable write body, preventing a read-only node from being promoted to write-capable without key proof. #2+#7 zero each child newWriteKey immediately after sealChildWriteKey (inline in the map callback) and add a post-Promise.all defensive sweep for any unclaimed child results — D-09 terminal-owner rule. #8 check createAndPublishIpnsRecord return value success flag before firing tombstones or grant mutations so a non-throwing rejection cannot leave the write plane inconsistent. #9 assert rootResult.nodeId matches caller-supplied rootNodeId before grant mutations to prevent rewrapping grants for a different node. #10 wrap tombstone and grant callbacks in try/finally so the root write key is zeroed even when a callback throws — D-09 terminal-owner rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FSM1
added a commit
that referenced
this pull request
Jun 30, 2026
#583) * docs(65): capture phase context Entire-Checkpoint: aed8bf6fa830 * docs(state): record phase 65 context session Entire-Checkpoint: f49b3b2fe198 * docs(phase-65): research write-chain, bin re-link, and invite claim Entire-Checkpoint: ef5c6ef6e756 * docs(65): add validation strategy Entire-Checkpoint: fe7e071404d5 * docs(65): map phase patterns Entire-Checkpoint: 3c9673822b65 * docs(65): create phase 65 write-chain plan (7 plans, 4 waves) WRITE-01 write-body Ed25519 under writeKey (role 0x04), WRITE-02 full Ed25519 write-revocation (child-first cascade), WRITE-03 co-writer re-wrap + offline error, WRITE-04 tombstone-intent; bin re-link + invite re-wrap (Success Criterion 4). Mock-seam discipline (D-02); sdk-e2e round-trip gate (D-04). OQ-1 (BinEntry.nodeReadKey) and OQ-2 (child-first cascade) resolved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUSYqMyCLbNGDLcn1cp1eh Entire-Checkpoint: 9638c8677236 * docs(65): address plan-checker warnings — surface e2e typecheck and mark research OQs resolved Entire-Checkpoint: e025a94f56f3 * docs(65): record planning complete and annotate roadmap waves Entire-Checkpoint: 7a6909081de9 * docs(phase-65): begin phase execution tracking Entire-Checkpoint: 2a373269bfea * test(65-01): add failing role-0x04 write-chain seal KAT and round-trip - round-trip: sealChildWriteKey then unsealChildWriteKey recovers 32-byte key - role isolation: role-0x02 blob rejected by role-0x04 unseal and vice versa - AAD binding: childId / childKind / childGeneration / wrong key all throw - terminal-owner: input buffers unchanged after both calls (D-09 / T-65-04) * feat(65-01): implement sealChildWriteKey and unsealChildWriteKey with role 0x04 - verbatim copy of sealChildReadKey / unsealChildReadKey with role byte 0x02 → 0x04 - AAD: buildNodeAad(childId, kindByte, childGeneration, 0x04) — child-writekey role frozen - no new imports: reuses sealAesGcmAad / unsealAesGcmAad / buildNodeAad from @cipherbox/crypto - D-09 terminal-owner: neither function zeros caller-supplied buffers - exported from packages/core/src/node/index.ts and packages/core/src/index.ts - all 9 role-0x04 tests green (round-trip, cross-role rejection, AAD-mismatch, terminal-owner) * test(65-03): add failing claimInvite service-flow test - RED: claimInvite import fails — function does not exist yet - Asserts getInviteDataFn called once per claim - Asserts insertShareFn called once with no encryptedChildKeys - Asserts two independent claims of same invite yield two separate grants * docs(65-01): complete plan summary for role-0x04 write-chain seal primitives * feat(65-03): implement claimInvite service flow and barrel export - Add claimInvite to packages/sdk-core/src/share/grant.ts composing claimInviteReadKey with injected getInviteDataFn and insertShareFn callbacks - Re-export claimInvite from packages/sdk-core/src/share/index.ts - One re-wrapped root readKey per claimer, no per-child key fan-out - Zero sdk-layer consumption of the fan-out path confirmed by grep gate * docs(65-03): complete invite-claim service flow plan summary * test(65-02): add failing bin re-link spec for addToBin and restoreFromBin - add nodeReadKey and nodeIpnsName fields to BinEntry in @cipherbox/core - un-skip addToBin and restoreFromBin describe blocks - remove legacy originalFolderKeyEncrypted and FolderChild references - add 4 addToBin tests covering happy-path, revoke ordering, abort-on-revoke-fail, and not-loaded guard - add 5 restoreFromBin tests including AEAD asymmetry proof via real sealChildReadKey - switch vi.mock to importOriginal spread keeping real AES-GCM primitives for asymmetry test * feat(65-02): implement addToBin and restoreFromBin as pure key re-link addToBin: - resolve child IPNS record to extract plaintext PublishedNode id and kind - unseal nodeReadKey from source parent folderKey via unsealChildReadKey - revoke shares before destructive folder mutation (fail-closed ordering) - capture nodeReadKey and nodeIpnsName on BinEntry for restore restoreFromBin: - re-seal entry.nodeReadKey under destination folderKey via sealChildReadKey (role 0x02) - build SealedChildRef with re-linked readKeySealed (no content re-encryption) - add restored ref to target folder and publish; remove entry from bin also fix test fixture: use valid UUID for nodeRef.id to satisfy uuidToBytes validator; replace vi.restoreAllMocks with targeted sealSpy.mockRestore to prevent resetting module-level vi.fn mocks (deriveBinIpnsKeypair) in subsequent test cases * docs(65-02): complete bin re-link plan * docs(phase-65): update tracking after wave 1 Entire-Checkpoint: 02004cb54e8b * test(65-05): add failing write-body-reseal tests for rotateOne write-plane preservation - Tests 1-4: assert unsealNode called with nodeWriteKey, sealNode receives real writeKey not all-zeros placeholder, writeBody.ipnsPrivateKey preserved post-rotation - Tests 5-6: fail-closed guard -- writeSealed present without valid writeKey throws - Test 7: nodeKeySource.writeKey threads through BFS to sealNode - All 6 write-plane assertions fail RED against placeholder engine * feat(65-05): thread real writeKey through rotateOne, remove PLACEHOLDER_WRITE_KEY - Add nodeWriteKey to RotateOneParams and nodeKeySource return type - unsealNode now called with nodeWriteKey to recover write-body on rotation - Fail-closed guard: throws when published.writeSealed present but nodeWriteKey is absent or all-zeros, mirroring the IPNS-key guard pattern - writeKeyForReseal = node.writeBody ? nodeWriteKey : empty; used at all three sealNode sites: main reseal and both CAS-409 merge paths - PLACEHOLDER_WRITE_KEY deleted from all three sites - rotateReadFromNode BFS threads nodeKeySource.writeKey at root, child, grandchild and dirty-resume enqueue sites - Existing IPNS fail-closed guard retained unchanged - Folds FLAG-63-U1 / rotateone-placeholder-writekey-phase65 * docs(65-05): complete write-body-reseal plan summary * feat(65-04): implement shared-write on write-body model with WRITE-01 security and WRITE-03 offline error - Reshape SharedWriteContext: writeKey + readKey + publishedNode replaces raw folderKey and ipnsPrivateKey fields - Add publishNodeFn and addToIpfsFn transport seams for mock-testable operations - Add buildChildWriteLink helper sealing child writeKey under parent writeKey via role 0x04 - Add walkChildWriteKey helper walking the write chain via unsealChildWriteKey - Prove WRITE-01 security: unsealNode without writeKey returns no writeBody; read-only holder cannot reach ipnsPrivateKey - Prove NODE-03: SealedChildRef carries no write field; write link lives only in parent writeBody.writeChildren - Implement createSharedSubfolder: mint child keypair, build write-body, seal, publish, add SealedChildRef + WriteChildRef to parent - Implement uploadToSharedFolder: same pattern for file node with AES-256-GCM content encryption via addToIpfsFn - Implement renameInSharedFolder: mutate read-body child name, preserve writeChildren, re-publish parent - Implement deleteFromSharedFolder: remove from children and writeChildren by IPNS name, re-publish parent - Implement updateSharedFile: caller-supplied fileReadKey + fileWriteKey + fileIpnsPrivateKey, re-seal + re-publish - Implement moveInSharedFolder: re-seal child readKey under dest readKey via sealChildReadKey, walk write chain for writeKey re-link - Add CannotWriteUntilRefetchError with stable code CANNOT_WRITE_UNTIL_REFETCH for tombstoned publish targets - addShareKeysFn never invoked across all six operations - All 29 shared-write tests pass including WRITE-01 security, write-link round-trip, and WRITE-03 tombstone tests * docs(65-04): fix stale Phase-65 convention comments in shared-write WriteChildRef.childId is a UUID from crypto.randomUUID matching Node.id, not the IPNS name. Clarify deleteFromSharedFolder itemId semantics and remove incorrect module-header annotation. * docs(65-04): complete plan 04 shared-write write-body model * fix(65-04): reconcile client SharedWriteContext consumers with write-body model * docs(phase-65): update tracking after wave 2 Entire-Checkpoint: 0c43e1d2e603 * test(65-06): add failing write-revocation driver contract test - Specifies rotateWriteFromNode behavior: per-node new Ed25519 keypair + k51 name + writeKey - Asserts child-first cascade, first-publish at 1n, tombstone-intent teeUnenrollFn calls - Asserts co-writer re-wrap via wrapKey and revoked grant drop via deleteWriteGrantFn - Asserts read-plane invariance: no generation bump, no new readKey minted - Asserts parent SealedChildRef.ipnsName re-pointed to new child name - Tests RED because rotateWriteFromNode is not yet exported from rotation engine * feat(65-06): implement rotateWriteFromNode with child-first write-revocation cascade - Exports WriteRevocationCallbacks type and rotateWriteFromNode function from rotation engine - Child-first bottom-up traversal: leaves get new Ed25519 keypair + k51 name + writeKey first - First-publishes each new k51 name at sequenceNumber 1n via createAndPublishIpnsRecord - Fires teeUnenrollFn for each old name after the new name is published - Re-seals write-body with new ipnsPrivateKey and re-pointed writeChildren under new parent writeKey - Updates parent SealedChildRef.ipnsName to new child name while leaving readKeySealed unchanged - Handles co-writer grants: wrapKey for survivors, deleteWriteGrantFn for revoked recipients - Read plane invariant: no readKey minted, no generation bump on any node - Zeros minted writeKey and Ed25519 seeds on failure paths only (D-09 / Pitfall 4) - Adds createAndPublishIpnsRecord and sealChildWriteKey/unsealChildWriteKey to engine imports - All 70 rotation suite tests pass including 8 new write-revocation tests * docs(65-06): complete write-revocation plan summary * docs(phase-65): update tracking after wave 3 Entire-Checkpoint: 1669fd50ff58 * feat(65-07): export rotateWriteFromNode and WriteRevocationCallbacks from sdk-core - Adds rotateWriteFromNode to the sdk-core top-level barrel so the sdk-e2e suite can import it via the package name rather than an internal subpath (Rule 3 fix: missing export blocked the D-04 gate test) - Adds WriteRevocationCallbacks type export alongside the function * test(65-07): add D-04 write-chain rotation E2E suite - Creates tests/sdk-e2e/src/suites/write-chain-rotation.test.ts as the D-04 phase gate for WRITE-02/03/04 against the live docker API stack - publishWriteCapableNode helper seals folder nodes with real write-bodies and first-publishes each new k51 name at sequenceNumber 1n - Test 1: builds a 2-level subtree (root + child), asserts pre-rotation baseline: write-body unseals under writeKey; parent write link unseals to child writeKey; generation 0 for both nodes - Test 2: drives rotateWriteFromNode with vi.fn() callbacks, asserts WRITE-02 (new k51 names + parent re-point cascade), WRITE-04 (teeUnenrollFn per old name), WRITE-03 (survivor re-wrap unwraps with bob private key; revoked grant dropped), and read-plane invariance (generation unchanged) - Derives new IPNS names from captured getRandomValues spy in child-first order (child-ed25519, child-writeKey, root-ed25519, root-writeKey) * docs(65-07): complete write-chain rotation plan summary * docs(phase-65): update tracking after wave 4 Entire-Checkpoint: c40a8c198c21 * docs(phase-65): complete phase execution Entire-Checkpoint: 77baeecc7244 * docs(phase-65): close rotateone-placeholder-writekey todo resolved by plan 65-05 Entire-Checkpoint: f69e0ef17961 * docs(phase-66): re-tag bin-pin-leak todo to phase 66 where apps/api/shares is in scope Entire-Checkpoint: f7bf47fb6251 * docs(65): security audit — all 29 threats closed Static analysis of Plans 01-07 threat models: 29 mitigations verified in implementation (seal.ts role-0x04, engine.ts writeKey threading + PLACEHOLDER removal, shared-write.ts write-body model, bin/index.ts pure re-link, grant.ts claimInvite). Two accepted risks (T-65-07 Q3 sub-share, T-65-09 invite link) documented. threats_open=0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(65): nyquist validation audit — fill coverage map, mark compliant Retroactive audit of phase 65 validation gaps. Maps all 15 tasks across 7 plans to their concrete test files and commands. Marks nyquist_compliant: true; records the one explicitly-deferred item (WRITE-04 live publish-gate) with Phase 66 evidence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Entire-Checkpoint: 4f25369a581a * docs(phase-68): defer shared-write context publishedNode wiring from phase-65 coderabbit review Entire-Checkpoint: 9406964c08bd * fix(65): resolve CR cluster-B findings in shared-write - F1 zeroization: fill(0) minted key buffers before null on success paths in createSharedSubfolder, uploadToSharedFolder, and updateSharedFile - F2 childId alignment: add fileNodeId param to updateSharedFile so Node.id stays aligned with WriteChildRef.childId across content updates - F3 publish order: publish dest before src in moveInSharedFolder so a partial failure leaves a duplicate rather than an orphaned item - F4 sequence number: pass swCtx.sequenceNumber + 1n in resealAndPublishParent so parent updates use the correct next sequence - F5 fail-closed: throw when neither childWriteKey nor a walkable src write link is available; zero derived key in finally (D-09) Tests: update callers for new fileNodeId param; add fail-closed test for moveInSharedFolder; 36 tests pass (was 35) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(65): fail-closed restoreFromBin and reorder addToBin for atomicity - restoreFromBin: throw instead of defaulting nodeIpnsName/nodeRef to empty values — prevents sealChildReadKey binding to wrong AAD - addToBin: persist bin entry before the destructive folder publish so a failed save cannot orphan the item with no restore key - grant.ts claimInvite: reject empty/whitespace inviteToken, rootNodeId, rootIpnsName before any I/O or crypto work Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(65): make revocation-failure test self-contained Add setupAddToBinMocks() to stub resolveIpnsRecord and fetchFromIpfs so the test reaches the revocation step independently of prior test cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(65): defer tombstones post-subtree, tighten key lifecycle, fix childKey zeroing Findings addressed: F5 CRITICAL, F6 major, F7 major, F4 major. F5: move teeUnenrollFn calls from inside rotateWriteSubtree to after the full subtree rotation in rotateWriteFromNode. Adds pendingTombstones[] collection so a failed ancestor publish cannot leave the TEE with a unenrolled child name the parent still references. F6: remove unused newIpnsPrivateKey from WriteRotationResult; zero newKeypair.privateKey immediately after createAndPublishIpnsRecord; zero rootResult.newWriteKey at end of rotateWriteFromNode after grants loop. F7: wrap both unsealChildReadKey and unsealChildWriteKey in a single try/finally so childReadKey is zeroed even when unsealChildWriteKey throws before childReadKey enters the inner try scope. F4: change rotateWriteFromNode return type from Promise<void> to Promise<{ newRootIpnsName: string }> so callers can persist the new root IPNS pointer alongside the grant descriptor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(65): add missing core mocks, strengthen write-key assertions in rotation unit tests Findings addressed: F3 major, F1 minor, F8 minor. F3: add sealChildWriteKey and unsealChildWriteKey to the @cipherbox/core mock in write-body-reseal.test.ts so the module does not silently export undefined for these symbols if the write-rotation code path is hit. F1: assert sealChildWriteKey call arguments in write-revocation Test 8 -- child write key identity, parent write key shape, and AAD binding args. F8: strengthen write-body-reseal Test 7 to assert both NODE_WRITE_KEY and CHILD_WRITE_KEY appear per-node in the BFS; remove the .catch that was masking rotation errors in GREEN. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(65): zero captured Ed25519 seeds early and verify survivor write key opens write body Findings addressed: F10 minor, F11 major. F10: call clearCapturedKeys immediately after deriving new IPNS names so Ed25519 seed material does not linger in the spy array until afterAll. F11: after unwrapping the survivor write-descriptor with bob private key, call unsealNode on the new root with the unwrapped key and assert writeBody is present; proves the persisted descriptor is the actual new root write key and not merely some 32-byte value. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(65): defer write-chain e2e seed index-stability hardening from coderabbit review Entire-Checkpoint: 0a6ec17e80ef * fix(65): deleteFromSharedFolder write-body filter uses UUID not ipnsName WriteChildRef.childId is a UUID minted at creation time; filtering by params.itemId (an IPNS name) never matched, leaving a stale WriteChildRef that later crashed rotateWriteFromNode. Add childNodeId param to deleteFromSharedFolder (mirroring moveInSharedFolder) so read-body is filtered by ipnsName and write-body by UUID. Also extend updateSharedFile with optional originalCreatedAt / originalVersions so callers can preserve immutable metadata; Phase-68 will supply prior values. Update client.deleteFromSharedFolder signature to thread childNodeId through. Remove the misleading "childId === ipnsName" module-level comment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(65): cover write-body WriteChildRef removal on delete Add a test that seeds the parent write-body with a WriteChildRef, calls deleteFromSharedFolder with both itemId and childNodeId, then unseals the republished parent envelope and asserts writeBody.writeChildren is empty. Extend buildSealedParent to accept optional writeChildren so tests can seed real write-body state. Update existing delete tests to supply the new required childNodeId param. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(65): harden write-rotation engine against security edge cases #6 fail closed when unsealed node has no recoverable write body, preventing a read-only node from being promoted to write-capable without key proof. #2+#7 zero each child newWriteKey immediately after sealChildWriteKey (inline in the map callback) and add a post-Promise.all defensive sweep for any unclaimed child results — D-09 terminal-owner rule. #8 check createAndPublishIpnsRecord return value success flag before firing tombstones or grant mutations so a non-throwing rejection cannot leave the write plane inconsistent. #9 assert rootResult.nodeId matches caller-supplied rootNodeId before grant mutations to prevent rewrapping grants for a different node. #10 wrap tombstone and grant callbacks in try/finally so the root write key is zeroed even when a callback throws — D-09 terminal-owner rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(65): harden rotation test hygiene for zeroed buffer handling #4 remove swallowed .catch handlers in write-body-reseal green-path tests — RED-phase workaround is no longer needed; let failures surface. #5 return fresh Uint8Array copies from crypto mocks in write-revocation tests so engine fill(0) zeroisation does not mutate shared fixture constants and pollute subsequent tests. #5 same treatment for generateRandomBytes write key mocks. Test 8 in write-revocation now captures fresh copies of sealChildWriteKey arguments via a custom mock implementation, since the engine zeroes the child write key after sealing. #20 zero the minted IPNS private key in publishWriteCapableNode e2e helper after publish in a finally block, narrowing the return type to only ipnsName. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(65): address coderabbit round-2 findings for bin and grant - FIX #16 restoreFromBin: check target folder for existing child by ipnsName before appending restoredItem so a retry after saveBinMetadata failure cannot duplicate the restored child ref. - FIX #11 claimInvite: snapshot ephemeralPrivateKey and claimerPublicKey buffers before the first await so a caller zeroing the inputs mid-await cannot corrupt the subsequent re-wrap; zero the owned copy in finally. - FIX #12 claimInvite: trim rootNodeId/rootIpnsName before using and persisting them so whitespace-padded inputs are not stored as-is. - test(65): add idempotency test for restoreFromBin, trim-input test and snapshot-before-await test for claimInvite; update toBe → toStrictEqual for recipientPublicKey (now a snapshotted copy). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(phase-68): add SDK envelope-return enablers from coderabbit PR review Entire-Checkpoint: 4710f9a40648 * fix(65): hex-encode bin nodeReadKey on the wire so restoreFromBin survives JSON round-trip Entire-Checkpoint: 19c3208fe45e * fix(65): fail closed on non-32-byte bin nodeReadKey and missing deleteFromSharedFolder childNodeId Entire-Checkpoint: c7f2d3a74cdf * fix(65): route shared-write content uploads through pinWithMode to honor BYO pinning Entire-Checkpoint: e99a4ebfa963 * fix(65): fail closed on non-throwing IPNS publish rejection in publishNodeFn The shared-write publishNodeFn closure ignored pubResult.success, so a 2xx relay response carrying success:false would proceed as if the node were committed — leaving the parent's SealedChildRef pointing at an IPNS name that was never published. Guard on success and throw, mirroring rotateWriteSubtree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 9e04bdf55b05 * fix(65): use childRef.generation for bin AAD, not stale envelope generation addToBin reconstructed the child-readkey AAD from publishedNode.generation, sourced from an independent IPNS resolve of the child. A stale-CID serve makes that diverge from childRef.generation (the parent mirror the readKeySealed blob was sealed under), so unsealChildReadKey fails GCM auth closed even when the parent folder state is current. Pass childRef.generation per the §2.6 generation-source rule, matching moveItem and navigate.ts. Also capture nodeRef.generation from childRef so restoreFromBin re-seals under the same generation as the unsealed key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 1af3242d5c34 --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The YAML frontmatter and document body showed conflicting dates (2026-01-18 vs January 17, 2026).
Changes
This ensures consistency across the documentation version metadata per the project's version management rules.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.