feat: integrate web client with node/v3 read and write runtime#588
feat: integrate web client with node/v3 read and write runtime#588FSM1 wants to merge 200 commits into
Conversation
Entire-Checkpoint: 983f6e8889d6
Entire-Checkpoint: 47c7d1c18846
Entire-Checkpoint: b8f805f2a240
…lan 14) Checker BLOCKER: resolveKinds (kind cache, plan 04) was never called on any folder-load/navigation path, leaving the cache empty so every child defaulted to folder and files were never recognized. Add plan 68.1-14 (wave 4, depends_on 01/04/05) invoking resolveKinds on owned (useFolderNavigation + folder.store) and shared (useSharedNavigationActions + useSharedNavigation) render paths. Plan 13 depends_on gains 68.1-14; noted per-wave smoke pass as future work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 8533336e3176
Entire-Checkpoint: 1ddbd8a1d4f2
…ptyRootNode - updateFolderMetadataAndPublish must seal a real write-body (ipnsPrivateKey + writeChildren) when writeKey is supplied, RED against current stub behavior - publishEmptyRootNode helper does not exist yet in sdk-core vault module Entire-Checkpoint: cbc0276c9637
- updateFolderMetadataAndPublish gains writeKey/writeChildren params; seals a real write-body (ipnsPrivateKey + writeChildren) when writeKey is supplied, preserving the zero-writeKey legacy path when it is omitted (D-03) - Add publishEmptyRootNode to sdk-core vault module: publishes a new user's empty kind:root Node at IPNS sequence 1n with a write-body carrying the root ipnsPrivateKey, templated on createSubfolder's TEE-enrollment guards - Export publishEmptyRootNode from the sdk-core barrel Entire-Checkpoint: 29de6a54b46e
- FolderState gains writeKey; CipherBoxClientConfig gains optional rootWriteKey (self-bootstrap requires both rootIpnsKeypair and rootWriteKey; matches the existing rootIpnsKeypair optionality since host wiring lands in Phase 68.1-03) - ensureFolderLoaded seeds the root FolderState from config and DFS-walks the write chain (SealedChildRef.readKeySealed + WriteChildRef.writeKeySealed) to recover each subfolder's readKey/writeKey/ipnsPrivateKey, registering every visited folder into folderTree (early exit on match); returns null on any structurally-unresolvable hop, matching requireFolder's existing fallback - Crypto/AEAD failures (T-68.1-01-02/03) propagate as throws rather than being swallowed, mirroring navigateReadChain's fail-closed contract - FolderTree now defensively copies + zeroes writeKey alongside folderKey - registerFolder/loadFolder accept an optional writeKey (zero-fallback legacy path); fixed test fixtures (helpers.ts, bin.test.ts) broken by the new required FolderState.writeKey field Entire-Checkpoint: 7b6f74a043fb
…d publishes - Add CipherBoxClient.getWriteBodyParams: sources the folder's current writeChildren from in-memory metadata.writeBody when present, else unseals the on-wire node once per operation; legacy zero-fallback writeKey yields a write-body-less publish identical to pre-D-03 behavior - Thread writeKey/writeChildren at every owned updateFolderMetadataAndPublish call site: renameItem, moveItem source+dest, deleteItem, uploadFile, uploadFiles batch - Mirror the helper in bin/index.ts for addToBin and restoreFromBin publishes since the actual publish calls live there, not in client.ts - This plan only PRESERVES existing writeChildren verbatim; WriteChildRef insertion/removal is owned by 68.1-02 and 68.1-07/09 - helpers.ts setupFolder fixture uses the zero writeKey legacy path so existing unit tests keep their no-network publish behavior Entire-Checkpoint: f3d8b5c3bbf3
Entire-Checkpoint: 589cb0270537
Mints a subfolder Node with its own write-body (ipnsPrivateKey + empty writeChildren), publishes at seq 1n, then inserts a SealedChildRef into the parent read-body and a WriteChildRef into the parent write-body and republishes the parent. Adapts the createSharedSubfolder build-path to the owned (non-share) write chain and registers the new child in folderTree so it is immediately usable without a reload. Entire-Checkpoint: d97ed8ef3542
collectRemovedItemIpnsNames and collectBinEntryIpnsNames now walk a removed item's subtree via the read-chain (resolveIpnsRecord + fetchFromIpfs + unsealChildReadKey/unsealNode), sharing a bounded DFS helper (collectDescendantIpnsNames, capped at UNENROLL_COLLECT_CONCURRENCY) that returns every descendant IPNS name for unenroll/unpin. An unreadable descendant is logged and skipped (best-effort) rather than thrown, so empty-bin unpin never wedges. collectRemovedItemIpnsNames now takes the parent readKey to recover the removed item's own readKey from its SealedChildRef. Entire-Checkpoint: 6f1e48046410
Content self-seal (phase 65) removes re-encrypt-on-move: a file node's readKey is sealed under the parent's read-chain, so moving a file across folders is a pure child-ref re-seal under the dest parent's write-body, not a separate per-IPNS re-encrypt step. No production callers import reencryptFileMetadataForFolderChange; the only references are vi.mock() factories in three already-quarantined legacy test files (pre-node/v3 FolderEntry/FilePointer types), unaffected at runtime since vi.mock virtualizes the module path. Entire-Checkpoint: 1efa6e5fd8a6
Entire-Checkpoint: c6fb6eb49aab
- New-user login branch publishes an empty kind:root Node via sdk-core publishEmptyRootNode at sequenceNumber 1n, registers the vault with the zero-crypto v2 API schema (ownerPublicKey + rootIpnsName only), and sets vault keys -- replacing the phase-63 throwing stub - Both new-user and existing-user branches now feed rootWriteKey through to CipherBoxClientConfig so ensureFolderLoaded can recover the full write chain from root Entire-Checkpoint: 4fa07782c100
Entire-Checkpoint: fcf6e9e410c3
- navigateToShare/navigateToSubfolder walk the Node v3 read-chain: ECIES unwrap readDescriptorRef for the share root, unsealChildReadKey (parent mirror generation) per subfolder hop, unsealNode to render children - navigateUp/navigateToBreadcrumb restore prior levels from the in-memory nav stack with no network round-trip, re-seeding the SDK sharedFolderTree and zeroing discarded folderKeys - downloadSharedFile walks the full grant-to-leaf chain via sdk-core's navigateReadChain and decrypts with the raw fileKey (base64 iv); handles behind-retry/revoked without throwing - single-file shares (root kind file) switch to currentView file so the existing SharedFileBrowser effect downloads via the empty-path root-is-leaf case - bridges the hex-encoded readDescriptorRef API contract into navigateReadChain's base64 contract; folder-ipns write keys still resolved via the existing per-share key fan-out Entire-Checkpoint: 49728b53c7d7
Entire-Metadata-Task: none Entire-Checkpoint: 96152c1e5bab
Entire-Checkpoint: 1453cf63b5ac
RED phase: createFileMetadata builds+seals a v3 file Node under fresh readKey/writeKey, embeds sequenceNumber 1n in the built (not-yet-published) IPNS record, and ECIES-wraps the file ipnsPrivateKey under a TEE public key only when teeKeys is supplied (fail-closed otherwise). Entire-Checkpoint: b68332e60d36
GREEN phase: createFileMetadata mints fileReadKey/fileWriteKey/Ed25519 keypair, builds+seals a v3 file Node, uploads it, and builds (without publishing) the file's first IPNS record at sequenceNumber 1n for the caller to batch-publish. upload/index.ts now threads the raw fileKey + iv into createFileMetadata and surfaces fileNodeId/fileReadKey/ fileWriteKey on UploadResult (additive). Deviation (Rule 1): updated upload.test.ts mocks/assertions that hard-coded the retired fileKeyEncrypted/fileIv (hex) contract. Entire-Checkpoint: fe120ce26003
…leContent RED phase: resolveFileMetadata round-trips content published by createFileMetadata (and fails closed when the IPNS record is missing); downloadFileContent decrypts raw-fileKey content (base64 iv) for both GCM and CTR. Entire-Checkpoint: 7e293969a792
GREEN phase: resolveFileMetadata resolves the file IPNS name, fetches the PublishedNode, and unseals content under the file readKey (read-only, fails closed when the record is missing or the node is not kind:'file'). downloadFileContent fetches a CID and decrypts with the raw fileKey + base64 iv (GCM/CTR), mirroring the web layer's downloadSharedFile pattern. Entire-Checkpoint: f88062c6b322
…n file wrappers RED phase: updateFileMetadata pushes the superseded content into version history (capped at maxVersionsPerFile, returns prunedCids) and preserves id/generation/createdAt on republish; addFileToFolder/addFilesToFolder seal file read+write child refs into the parent and batch-publish the file record(s); replaceFileInFolder publishes only the file IPNS record. Entire-Checkpoint: e9dee7609765
GREEN phase: updateFileMetadata rebuilds the file Node preserving id/generation/createdAt, optionally folds the superseded content descriptor into version history (capped via capVersions, an adapter over the existing mergeVersions utility), and republishes directly at fileSequenceNumber+1 (single-shot, no CAS retry). registration.ts addFileToFolder/addFilesToFolder seal file read+write child refs into the parent (write-body-aware per 68.1-01) and batch-publish the file record(s); replaceFileInFolder delegates to updateFileMetadata (publishes only the file IPNS record, parent untouched). sdk-core uploadFile/createFileMetadata/updateFileMetadata/registration wrappers are now fully implemented — sdkCore.uploadFile no longer throws. Deviation (Rule 1): quarantined file.test.ts's legacy CAS+merge describe.skip block now fails typecheck against the new concrete updateFileMetadata signature (NodeContent/UpdateFileContentParams vs the old unknown/unknown stub params); added @ts-nocheck matching the existing folder.test.ts precedent for the same situation. Fixed test fixtures in file-node.test.ts to use valid UUID node ids (buildNodeAad requires parseable UUIDs) — surfaced only once real sealNode/sealChildReadKey calls executed in Task 3's GREEN phase. Entire-Checkpoint: c16fa402e0ca
Entire-Checkpoint: 5881e45a6e3d
Resolves the file node's readKey/writeKey/ipnsPrivateKey by walking the parent shared folder's write-body (WriteChildRef matched by UUID), falling back to getFileIpnsKeyFn only when no write link is recorded. Forwards originalCreatedAt/originalVersions from the unsealed current file Node and delegates to shareOps.updateSharedFile. Emits a file-only sharedFolder:updated mirroring refreshSharedFolder. Derived keys are zeroed in finally (D-09). Also ignores *.tsbuildinfo (tsc -b build artifact). Entire-Checkpoint: 7afb6964a66b
…hain Resolves the destination folder's read/write keys from share_keys (folder + folder-ipns entries, ECIES-unwrapped via vaultPrivateKey), loads fresh dest children via loadFolderMetadata (A1, never cached), and resolves the moved child's UUID/kind/generation/readKey by unsealing the item's PublishedNode through the source folderKey. Delegates to shareOps.moveInSharedFolder (dest-before-src publish) and adopts only the SOURCE result. T-49-01 write-capability guard throws before any publish if either share_keys entry is absent; derived dest/child keys are zeroed in finally, vaultPrivateKey is never zeroed (D-09). Extracted buildSharedWriteContextWithOverrides from buildSharedWriteContextFromState so both the current shared-folder state and a one-off destination context share the same addToIpfsFn/publishNodeFn seams. Entire-Checkpoint: 60d1c705f4c2
…wrappers plan Entire-Checkpoint: d8a0dfce25d4
…rite handlers - renameItemHandler routes through client.renameInSharedFolder - deleteItemHandler resolves childNodeId via PublishedNode envelope (NODE-03 plaintext id) before calling client.deleteFromSharedFolder, which fails closed on a missing childNodeId (T-68.1-10-01) - updateSharedFileHandler calls client.updateSharedFile directly (bypassing runWrite) so it throws on error, matching the file editor caller contract; getFileIpnsKeyFn falls back to share_keys keyType file-ipns - All three read nothing back -- sharedFolder:updated is the sole ref writer
…1-33 Rescores the phase 5/5 passed: both round-3 open items are fixed and independently corroborated — writable-shares 10.3 (68.1-32 updateSharedSingleFile + web wiring, 5/5 unit re-run, green in-suite in two full runs) and 10.4 (68.1-33 hybrid resolve-before-insert across all three owner projection sinks, 11/11 unit re-run, green in-suite). Closing full-suite artifact e2e-close-post33.txt is 2/205/2 with the failure set exactly equal to the two human-approved deferrals (recovery v2-tool port, rotation SC-4 API architecture), now recorded as frontmatter overrides; the rotation in-suite setup failure is isolated to hook flake via e2e-rotation-rerun.txt. Entire-Checkpoint: b210411d5149
Entire-Checkpoint: b614bc6399f1
Entire-Checkpoint: 0587f6220c94
Entire-Checkpoint: d3ac16dd0a2f
Entire-Checkpoint: c23b4600157f
Entire-Checkpoint: add07aba34d9
Un-quarantine the D-01 stub-era suites (folder-crud, file-operations, batch-upload, bin-operations, share-operations, invite-link, ipns-consistency, concurrent-operations, data-integrity, vault-lifecycle, error-cases) and modernize them to the node/v3 contracts: read plane keyed by ipnsName, current share/invite DTOs, removal of the redundant registerFolder that zeroed writeKeys, and seq baseline 1. 100/101 pass (tee-republish remains a local-TEE-env failure). Entire-Checkpoint: 9442ad88f927
…inputs On a 409 conflict, publishWithCas merged only the read-body and resealed the write-body with the stale closure writeChildren, silently dropping a concurrent writer's WriteChildRef (child stayed readable but lost write capability) — the write-plane clobber class this milestone exists to prevent, on the concurrent merge path. decodeRemote now unseals the remote write-body and merge unions writeChildren by childId (add-only, remote wins). Also add length validation to the file-node create path, and remove the dead post-cutover folder helpers (addFileToFolder/addFilesToFolder/replaceFileInFolder) superseded by inline client logic. Entire-Checkpoint: 60e3ce84cd36
createFolder now rejects duplicate sibling names, matching uploadFile's contract (the v2->v3 cutover had dropped this check for folders while files kept it). Thread publishedWriteChildren through adoptPublishedFolderState so the in-memory write mirror can't re-drop a merged WriteChildRef; require metadata.writeBody before descending a cached child in dfsFindFolder; zero the file node's fileKey after resolveFileWriteChainKeys; and order bin restoreFromBin/getWriteBodyParams before the durable metadata write so a throw can't leave a duplicate bin entry. Entire-Checkpoint: 3bd76e0b270e
Delete post-cutover dead code (collectChildKeys and its concurrency helpers, the createFileMetadata/updateFileMetadata delegates, the no-op share-permission and pending-rotation stubs, isFilePointer/setKind/withConflictRetry, the unused shareId preview option). Security/correctness review fixes: redactKeyPreview no longer renders raw private-key bytes (hashed device id); zero metadata.fileKey and newFileKey on all failure paths (D-09 terminal owner); add the stale-sequence guard to folder.store's synchronous fast-path; treat an absent writeDescriptorRef and undefined rootGeneration as fail-closed in ShareDialog; fix the shared-root download path double-count and the unwrap-before-try loading-stuck bug; per-row version download spinners; wire DetailsDialog to the kind cache; fresh array ref so the post-resolveKinds re-render fires. Entire-Checkpoint: f7952f2db912
isCoreKitTransientFailure now briefly waits for the error banner to render instead of a point-in-time isVisible() check that raced the alert. Entire-Checkpoint: 269e8a89167b
Entire-Checkpoint: 717d967efaf4
|
Important Review skippedToo many files! This PR contains 211 files, which is 61 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (6)
📒 Files selected for processing (211)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR connects the web client to the node/v3 read and write runtime. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (3): Last reviewed commit: "docs(68.1): capture nested shared-write ..." | Re-trigger Greptile |
Release Preview
Cascade Details
|
The updateGrant controller forwards writeDescriptorRef and clearWriteDescriptor to the service (6 args); the spec's toHaveBeenCalledWith still expected 4. Assert the full delegation so the strict arg-count check passes. Entire-Checkpoint: 48baec5d145d
Entire-Checkpoint: 8358cb400c89
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #588 +/- ##
===========================================
+ Coverage 66.42% 79.70% +13.27%
===========================================
Files 153 116 -37
Lines 12716 9734 -2982
Branches 1457 1599 +142
===========================================
- Hits 8447 7758 -689
+ Misses 4031 1734 -2297
- Partials 238 242 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Entire-Checkpoint: a1a6696949cc
Entire-Checkpoint: 529e47d62a38
Phase 68.1 — Web Client Runtime Integration
Wires the v2.0 web app onto the node/v3 read + write chain (sealed nodes, the
WriteChildRefwrite plane, the AAD triad) so the fulltests/web-e2ePlaywrightsuite passes against a real client → API → IPNS round-trip. Closes the phase's
WEB-01..04 success criteria.
Verification
recorded human-approved deferral overrides).
timeout, unrelated to this branch and pre-existing).
Write-plane correctness (the core of this phase)
The v2 → v3 cutover left a family of write-plane clobber bugs where a fresh device
or a concurrent writer could publish a record that silently erased another writer's
write capability. This branch fixes them and adds permanent regression coverage:
writeKeyrecovery; batch uploads now insertWriteChildRefs (everyweb-uploaded file was previously un-editable); write shares are actually writable;
moveItemre-homes the write link across folders (fixes the reportedmove-then-edit failure).
createFoldernow rejects duplicate sibling names, matchinguploadFile— thecutover had dropped this check for folders only.
publishWithCasmerged only the read-body and resealed the write-body with thestale
writeChildren, dropping a concurrent writer'sWriteChildRef. NowdecodeRemoteunseals the remote write-body andmergeunionswriteChildrenbychildId. Covered by concurrent-operations E2E.SDK E2E coverage restored
The 11 D-01 stub-era quarantined suites are un-quarantined and modernized to the v3
contracts (read plane keyed by ipnsName, current share/invite DTOs, seq baseline 1),
restoring full cross-package publish/resolve coverage.
Review + hardening (this ship pass)
redactKeyPreviewno longer renders rawprivate-key bytes; several key buffers zeroed on all failure paths (D-09);
fail-closed grant handling in ShareDialog; stale-sequence guard on folder.store's
fast path; file-node input validation; two bin-durability ordering fixes.
Deferred follow-ups (todos filed under
.planning/todos/pending/)approved verification overrides).
getWriteBodyParamstransient-resolve-miss could seal an empty write-body — needsa holistic fail-open/fail-closed decision (flagged, not one-sidedly flipped).
WriteChildRef(write-chain growth; the68.1-02 write-link removal).
encryptedIpnsPrivateKeyterminologyrename; the sdk-e2e quarantine sweep; the pre-existing (non-CI)
ipns.service.testmock gap.
Known non-blocking
navigating root → A → B and then going UP to A restores A without its per-depth
write key (only the share root's writeKey is re-derived), so a write from that
restored intermediate level fails at write-body unseal. Not data loss or a
security hole — the write throws, and re-navigating from the share root restores
write capability. The fix touches the writeKey lifecycle + zeroization and needs
navigate-up-then-write e2e coverage, so it's tracked as a follow-up rather than an
unverified tail-of-ship change.
identical on
main.apps/webunit tests are not run by CI (web is gated by web-e2e); a pre-existingbroken
ipns.service.test.tsmock is filed as a todo, not fixed here (out ofscope, non-gating).
Test plan
pnpm --filter @cipherbox/sdk-e2e test→ 100/101 (tee env only)🤖 Generated with Claude Code