feat: extract core crypto SDK as shared packages#296
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (3)
WalkthroughAdds three new packages ( Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/Browser
participant Web as Web App
participant Auth as useAuth Hook
participant SDK as CipherBoxClient
participant SdkCore as `@cipherbox/sdk-core`
participant API as Backend API
participant IPFS as IPFS Relay
User->>Web: Login
Web->>Auth: trigger vault load
Auth->>Auth: decrypt vault, derive keys
Auth->>SDK: initSdkClient(config)
SDK->>SdkCore: loadFolder / uploadFile / downloadFromIpns
SdkCore->>IPFS: addToIpfs / fetchFromIpfs
SdkCore->>API: publish/resolve IPNS records
IPFS-->>SdkCore: return cid / bytes
API-->>SdkCore: publish/resolve response
SdkCore-->>SDK: results (metadataCid, resolved record)
SDK-->>Web: emit events (folder:loaded, file:uploaded, bin:updated)
sequenceDiagram
participant User as User/Browser
participant Web as Web App
participant SDK as CipherBoxClient
participant SdkCore as `@cipherbox/sdk-core`
participant Crypto as `@cipherbox/crypto`
participant IPFS as IPFS Relay
User->>Web: Upload file (data, name)
Web->>SDK: uploadFile(folderIpnsName, data, name)
SDK->>SdkCore: uploadFile(...)
SdkCore->>Crypto: generateFileKey(), encrypt, wrapKey()
Crypto-->>SdkCore: keys/encrypted
SdkCore->>IPFS: addToIpfs(ciphertext)
IPFS-->>SdkCore: { cid }
SdkCore->>SdkCore: createFileMetadata(...)
SdkCore->>IPFS: addToIpfs(metadata)
IPFS-->>SdkCore: { metadataCid }
SdkCore->>API: createAndPublishIpnsRecord(metadataCid)
API-->>SdkCore: success + sequenceNumber
SdkCore-->>SDK: fileMetaIpnsName
SDK-->>Web: emit file:uploaded, folder:updated
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Pull request overview
This PR extracts CipherBox client-side crypto/domain logic into a layered set of shared packages (@cipherbox/core, @cipherbox/api-client, @cipherbox/sdk-core, @cipherbox/sdk) and rewires the web app to consume the new SDK packages, while updating Release Please and Codecov configuration to support the new package structure.
Changes:
- Added new shared packages for domain logic (
core), generated API client (api-client), stateless operations (sdk-core), and stateful client orchestration (sdk). - Updated
@cipherbox/cryptoto retain only pure primitives/derivation and moved CipherBox domain-aware modules into@cipherbox/core. - Updated CI/coverage/release configuration to include the new packages and added basic test/coverage configs per package.
Reviewed changes
Copilot reviewed 260 out of 278 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| release-please-config.json | Adds Release Please package entries for the new shared packages. |
| .release-please-manifest.json | Adds per-package version tracking for the new packages. |
| codecov.yml | Adds Codecov flags/targets for core/api-client/sdk-core/sdk. |
| .github/workflows/ci.yml | Runs coverage for the new packages and uploads to Codecov. |
| .github/workflows/codecov-base.yml | Extends base-branch coverage upload to include new packages. |
| packages/crypto/src/index.ts | Removes domain-aware exports from crypto, leaving primitives and deriveIpnsName. |
| packages/crypto/src/vault/index.ts | Keeps only vault IPNS derivation in crypto; removes vault init exports. |
| packages/crypto/src/tests/vault-ipns.test.ts | Updates tests to import vault/registry functions from @cipherbox/core. |
| packages/crypto/package.json | Adds @cipherbox/core as a devDependency for tests. |
| packages/crypto/README.md | Documents crypto as primitives-only after extraction. |
| packages/core/src/index.ts | Introduces @cipherbox/core public API surface for domain logic. |
| packages/core/src/vault/init.ts | Moves vault init/encrypt/decrypt into core (imports primitives from crypto). |
| packages/core/src/vault/types.ts | Adjusts core vault types to depend on crypto’s Ed25519 types. |
| packages/core/src/vault/index.ts | Exposes vault management from core. |
| packages/core/src/folder/metadata.ts | Switches folder metadata crypto imports to @cipherbox/crypto. |
| packages/core/src/file/metadata.ts | Switches file metadata crypto imports to @cipherbox/crypto. |
| packages/core/src/file/derive-ipns.ts | Switches derivation primitives/constants to @cipherbox/crypto. |
| packages/core/src/registry/* | Moves/retargets registry types/encryption/derivation to core with crypto primitives from @cipherbox/crypto. |
| packages/core/src/bin/* | Moves/retargets bin types/encryption/derivation to core with crypto primitives from @cipherbox/crypto. |
| packages/core/src/ipns/* | Moves IPNS record utilities into core; re-exports name derivation via crypto. |
| packages/core/src/tests/* | Updates tests to match new package boundaries and imports. |
| packages/core/package.json | Adds new @cipherbox/core package manifest. |
| packages/core/README.md | Documents core package responsibilities and API. |
| packages/core/vitest.config.ts | Adds coverage configuration for core tests. |
| packages/core/tsup.config.ts | Adds build config for core. |
| packages/core/tsconfig.json | Adds TS config for core. |
| packages/api-client/src/instance.ts | Adds configurable axios mutator + config setters for orval-generated client. |
| packages/api-client/src/index.ts | Exposes instance utilities plus re-exports generated functions/models. |
| packages/api-client/orval.config.ts | Adds orval config using the custom mutator. |
| packages/api-client/src/models/* | Adds orval-generated DTO/model types. |
| packages/api-client/src/generated/* | Adds orval-generated endpoint functions (axios-functions). |
| packages/api-client/package.json | Adds new @cipherbox/api-client package manifest. |
| packages/api-client/README.md | Documents API client package usage (needs alignment with actual exports). |
| packages/api-client/vitest.config.ts | Adds coverage configuration for api-client tests. |
| packages/api-client/tsup.config.ts | Adds build config for api-client. |
| packages/api-client/tsconfig.json | Adds TS config for api-client. |
| packages/sdk-core/src/index.ts | Exposes sdk-core stateless operations (folder/ipns/ipfs/file/upload/download). |
| packages/sdk-core/src/types.ts | Defines injected SdkContext and shared callback/result types. |
| packages/sdk-core/src/ipfs/index.ts | Implements IPFS relay upload/unpin/fetch with progress support. |
| packages/sdk-core/src/upload/index.ts | Implements core encrypt+upload+metadata creation pipeline returning file pointer inputs. |
| packages/sdk-core/src/download/index.ts | Implements fetch+unwrap+decrypt pipeline for file content. |
| packages/sdk-core/src/tests/* | Adds unit tests for IPFS and download flows. |
| packages/sdk-core/package.json | Adds new @cipherbox/sdk-core package manifest. |
| packages/sdk-core/README.md | Documents sdk-core package (currently references non-existent API names). |
| packages/sdk-core/vitest.config.ts | Adds coverage configuration for sdk-core tests. |
| packages/sdk-core/tsup.config.ts | Adds build config for sdk-core. |
| packages/sdk-core/tsconfig.json | Adds TS config for sdk-core. |
| packages/sdk/src/index.ts | Exposes CipherBoxClient, errors, events, and public types. |
| packages/sdk/src/types.ts | Defines CipherBoxClientConfig and internal FolderState. |
| packages/sdk/src/events.ts | Adds typed synchronous event emitter for SDK state changes. |
| packages/sdk/src/state/key-cache.ts | Adds derived-key cache with zeroing on clear. |
| packages/sdk/src/state/folder-tree.ts | Adds internal folder state storage with key material zeroing on clear/delete. |
| packages/sdk/src/tests/events.test.ts | Adds unit tests for the event emitter behavior. |
| packages/sdk/package.json | Adds new @cipherbox/sdk package manifest. |
| packages/sdk/README.md | Documents sdk package (API/events section currently out of sync with implementation). |
| packages/sdk/vitest.config.ts | Adds coverage configuration for sdk tests. |
| packages/sdk/tsup.config.ts | Adds build config for sdk. |
| packages/sdk/tsconfig.json | Adds TS config for sdk. |
| apps/web/package.json | Adds dependencies on the new workspace packages. |
| apps/web/src/lib/sdk-provider.ts | Adds singleton SDK client lifecycle and folder registration bridge. |
| apps/web/src/lib/clear-user-stores.ts | Destroys the SDK client on logout before clearing key-bearing stores. |
| apps/web/src/hooks/useAuth.ts | Initializes SDK client after vault load and wires store subscriptions; configures api-client. |
| apps/web/src/stores/folder.store.ts | Adds subscription wiring to SDK folder events and unsubscribes on clear. |
| apps/web/src/stores/bin.store.ts | Adds subscription wiring to SDK bin events and unsubscribes on clear. |
| apps/web/src/services/* | Updates imports to use @cipherbox/core for domain types/metadata; marks some services deprecated. |
| apps/web/src/lib/device/info.ts | Moves DevicePlatform type import to @cipherbox/core. |
| apps/web/src/lib/crypto/key-wrapping.ts | Splits domain types (core) vs primitives (crypto) imports. |
| apps/web/src/hooks/useFileDownload.ts | Switches per-file IPNS download flow to use CipherBoxClient.downloadFromIpns(). |
| apps/web/src/components/file-browser/* | Updates domain type imports from @cipherbox/crypto to @cipherbox/core and adopts SDK download where applicable. |
| apps/web/src/utils/fileTypes.ts | Updates FolderChild/FilePointer type imports to @cipherbox/core. |
| apps/web/src/api/* | Adjusts orval-generated import paths (e.g., .././models, .././custom-instance). |
| .planning/STATE.md | Updates project state to reflect Phase 19.1 completion. |
| .planning/ROADMAP.md | Marks Phase 19.1 plans as complete. |
| .planning/REQUIREMENTS.md | Marks SDK-01/02/03/04/05/06/07/10/11 as complete. |
| .planning/phases/19.1-extract-core-crypto-sdk-as-shared-package/deferred-items.md | Notes deferred DTS circular dependency build issue. |
| .learnings/2026-03-20-playwright-mcp-login-and-upload.md | Adds Playwright MCP learnings for OTP login and file upload. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #296 +/- ##
==========================================
+ Coverage 48.24% 49.79% +1.55%
==========================================
Files 109 115 +6
Lines 8405 9818 +1413
Branches 653 699 +46
==========================================
+ Hits 4055 4889 +834
- Misses 4177 4753 +576
- Partials 173 176 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
@coderabbitai full review please |
|
🧠 Learnings used✅ Actions performedFull review triggered. |
|
@coderabbitai please review the PR paying particular attention to the excluded paths so that your file limits are not breached |
🧩 Analysis chain
🛠 Debug Information Gathered
Already excluded via
Potential file-limit pressure
These large planning docs will consume file slots that could otherwise go to the actual source code in Recommended addition to reviews:
path_filters:
# existing filters ...
- '!.planning/**'
- '!.learnings/**'This would free up slot capacity for the 4 new package sources and the web app changes, which are the high-value review targets. The planning docs are still accessible to me as context via git sparse-checkout — they just won't count against the file limit. Would you like me to add this to your config? The full review I triggered is running now, so if you want to apply this before it completes, let me know and I can update the YAML. 🧠 Learnings used✅ Actions performedFull review triggered. |
… configurable axios instance - Create package.json, tsconfig, tsup, vitest configs matching crypto package pattern - Add framework-agnostic instance.ts with setApiClientConfig/createAxiosInstance (no Zustand/React) - Configure orval for axios-functions client generating plain typed functions - Generate typed API functions and model types from openapi.json - Create barrel index.ts re-exporting all generated functions and types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add @cipherbox/api-client as workspace dependency in web package.json - Regenerate web app react-query hooks via orval (picks up latest orval formatting) - Web app orval config unchanged, still generates react-query hooks with local custom-instance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…m crypto - Create packages/core with package.json, tsconfig, tsup, vitest configs - Move folder, file, registry, bin, ipns, vault/init modules from crypto to core - Update all moved files to import crypto primitives from @cipherbox/crypto - Keep ipns/derive-name.ts in crypto (exported) to avoid circular dependency - Core re-exports deriveIpnsName from crypto for internal use - Trim crypto vault/index.ts to only export deriveVaultIpnsKeypair - Update crypto index.ts to remove moved module exports, add deriveIpnsName - Both packages build successfully (pnpm --filter @cipherbox/core build) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…o core - Add transitional re-exports from @cipherbox/core in crypto's index.ts - Mark re-exports with TODO(19.1-06) for removal in cleanup plan - Add @cipherbox/core as workspace dependency of crypto - Move 7 domain test files from crypto to core (folder, file, registry, bin, ipns, vault) - Update test imports to use @cipherbox/crypto for primitives, relative for domain modules - Fix vault-ipns.test.ts in crypto to import from @cipherbox/core - Add @noble/secp256k1 as devDep of core for test keypair generation - All 7 crypto tests pass (138 tests), all 7 core tests pass (146 tests) - Full monorepo builds successfully including web app via transitional re-exports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…S modules - Package scaffold with deps on @cipherbox/crypto, @cipherbox/core, @cipherbox/api-client - SdkContext type replaces Zustand store access pattern - Parameterized IPFS upload/download/unpin extracted from web app - IPNS publish/resolve/verify extracted using @cipherbox/api-client generated functions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…or-safe teardown - registerFolder: store defensive copies of folderKey and ipnsKeypair so destroy() doesn't zero caller-owned buffers - getFolderIpnsPrivateKey: return a copy to prevent external mutation - createFolder: move duplicate-name check before key generation, wrap post-keygen path in try/catch to clearBytes on failure - TextEditorDialog: cancel requestAnimationFrame in effect cleanup - multi-account-wallet: swallow context.close() errors in catch blocks so cleanup doesn't mask the original failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: e6709971c0f9
…stency - ROADMAP: align Phase 19.1 row columns (was shifted left, missing milestone) - STATE: update status to completed and completed_phases to 6/6 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f9262455dd40
* docs: start work on todo - SDK client self-bootstrap folder tree from root IPNS key Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 489e091e0cc7 * feat(sdk): self-bootstrap folder tree from root IPNS key The SDK client owned folder state (folderTree) but its config carried only rootIpnsName + rootFolderKey, not the root IPNS signing key. So the client could not resolve/publish root or lazy-load folders on its own, and the web app had to seed folderTree via ensureFolderRegistered before every folderTree-dependent method. That asymmetry is the root cause of the whole "Folder not loaded" class — most visibly, restoring a recycle-bin item after a reload threw 'Target folder not loaded' when the item's subfolder parent was never navigated to that session (pre-existing since #296; recycle-bin.spec.ts misses it because it uploads in-session and never reloads). Changes: - Add optional rootIpnsKeypair to CipherBoxClientConfig; defensively copied in the constructor and zeroed in destroy(), mirroring vaultKeypair/rootFolderKey. - Add CipherBoxClient.ensureFolderLoaded(target): when a root IPNS keypair is configured, walk the folder tree from root (DFS, early exit), unwrapping each subfolder's folderKeyEncrypted/ipnsPrivateKeyEncrypted with the vault keypair and loading metadata until the target is registered. Every folder on the path is cached. A corrupt sibling entry is skipped, not fatal. - Wire ensureFolderLoaded as a self-heal fallback into every folderTree-dependent method (create/rename/move/delete/upload(s)/replace/version ops/share) and the bin deleteToBin/restoreFromBin wrappers. When no rootIpnsKeypair is configured it returns null before any crypto, preserving the prior 'Folder not loaded' behavior. - Web: pass vaultStore.rootIpnsKeypair into the SDK config at initSdkClient (already guaranteed non-null by the existing guard). Security: no new key exposure — the client already holds vaultKeypair.privateKey, rootFolderKey, and every loaded folder's ipnsPrivateKey in memory (zeroed on destroy). Reviewed: no key transposition, cycles bounded by a visited set, errors leak no key bytes. Tests: - SDK unit: ensureFolderLoaded — already-loaded no-op, no-keypair null, root-as-target, deep walk, unreachable target, skip unresolvable/corrupt sibling. - Web E2E: restore a subfolder item after reload without navigating into the subfolder (the scenario recycle-bin.spec.ts misses). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 78b22528e949 * refactor(sdk): route folder mutations through a requireFolder chokepoint /simplify cleanup of the self-bootstrap change. Behavior-preserving: - Collapse the 11 duplicated `folderTree.get(x) ?? ensureFolderLoaded(x); if (!folder) throw 'X not loaded'` blocks into one private requireFolder(ipnsName, label) helper. Mutations and the two bin wrappers now route through it, so the get-or-self-load-or-throw contract lives in one place and a new method can't silently drop the self-heal fallback (the #494 drift class). - Drop the redundant `as FolderEntry` cast — the `type === 'folder'` guard already narrows the discriminated union. - Simplify the root load (single `?? await loadFolder` expression) and the constructor's rootIpnsKeypair assignment (`?? undefined` vs conditional spread). - Drop a brittle unwrapKey call-count assertion from the deep-walk test; the hasFolder assertions already cover path registration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 6c13c23c3e2b * chore(release): set release targets for PR #498 --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
@cipherbox/core,@cipherbox/api-client,@cipherbox/sdk-core,@cipherbox/sdkCipherBoxClientSDK instead of calling service functions directlyPackage Architecture
Plans Executed (6/6)
@cipherbox/cryptointo crypto + core packages@cipherbox/api-clientwith orval codegen@cipherbox/sdk-corewith stateless operations@cipherbox/sdkwith CipherBoxClientKey Decisions
downloadFromIpns()to fix decryption failures from mismatched resolution pathsloadBin()returns empty state — instead of null on fresh accounts, preventing "Bin not loaded" fallback to hard deleteupdateFolderMetadataAndPublishretries once on stale sequence numberBinNotLoadedErrortyped error — replaces fragile string matchingdestroy()— defense-in-depth for session key cleanupSecurity Reviews
.planning/security/REVIEW-2026-03-20-phase19.1-sdk-extraction-plans.md.planning/security/REVIEW-2026-03-20-phase19.1-post-implementation.mdTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor
Documentation
Tests
Chores