Skip to content

refactor: consolidate crypto codecs, zeroization hygiene, and IPNS terminology#609

Merged
FSM1 merged 57 commits into
mainfrom
feat/crypto-hygiene-and-terminology-canonicalization
Jul 11, 2026
Merged

refactor: consolidate crypto codecs, zeroization hygiene, and IPNS terminology#609
FSM1 merged 57 commits into
mainfrom
feat/crypto-hygiene-and-terminology-canonicalization

Conversation

@FSM1

@FSM1 FSM1 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Phase 77 — Crypto Hygiene & Terminology Canonicalization

Refactor/hygiene phase: consolidate duplicated crypto codecs, tighten key-buffer zeroization, and canonicalize IPNS-key terminology across the monorepo. No behavior change to the crypto itself — every codec swap is byte-for-byte parity-gated.

What changed

  • Base64 codec consolidation — hoisted a single canonical bytesToBase64/base64ToBytes into @cipherbox/crypto; replaced ~10 copy-pasted codecs (core/node/{encode,decode,seal}, sdk-core/{rotation,share,file}) with imports. Golden-vector + node-codec parity gates prove byte-identical output.
  • AES key-import zeroization — new importAesKey helper owns a local key copy and zeroes it in a finally; all 7 AES-GCM/CTR functions route through it. D-09 preserved: the caller's borrowed buffer is never mutated.
  • wrapIpnsKeyForTee bytes-in/bytes-out — hex boundary pushed to the 3 callers; helper borrows ipnsPrivateKey (never zeroes it).
  • TEE wire-field canonicalizationencryptedIpnsKey to encryptedIpnsPrivateKey across API relay + tee-worker (sender and receiver renamed together).
  • In-memory field renameipnsPrivateKeyEncrypted to encryptedIpnsPrivateKey.
  • Dead-code removalShareCallbacks, addShareKeysFn, updateSharePermission, and the orphaned UpdatePermissionDto client model.
  • assertRootOwnership extraction — single auditable root-ownership gate shared by createShare + createInvite (behavior-preserving).
  • Error-path zeroizationcreateSubfolder and verify-filepointer.mts zero minted/loaded keys on throw.

Ship-phase gates

Gate Result
Verify (77-VERIFICATION.md) PASS
Secure (77-SECURITY.md) SECURED — 30 threats, threats_open: 0
Crypto/privacy review Clean on all primary axes; 1 LOW fixed (below)
Built-in security sweep No material findings
Validate (77-VALIDATION.md) Nyquist-compliant
SDK E2E 105/106 green — see caveat below
CodeRabbit CLI 8 findings: 1 fixed, 7 discarded

Fixes applied during ship (defects this phase itself introduced/left)

  • createSubfolder TEE-wrap error path (crypto review, LOW): the hexToBytes/wrapIpnsKeyForTee block sat just outside the try, so a malformed TEE pubkey throwing in hexToBytes leaked the 3 minted keys. Moved the try to open right after key generation so every post-mint throw (validation + wrap) zeroes. Added a forced-throw test.
  • verify-filepointer.mts (CodeRabbit, major): moved the try to open immediately after userPrivateKey allocation so a throw during vault load still reaches the finally and clears the private key; finally now tolerates an absent vault blob.
  • encoding.ts (simplify, minor): corrected a stale provenance comment pointing at a file this phase deleted.

SDK E2E caveat — tee-republish.test.ts Test A

The only non-passing e2e is tee-republish Test A (waitFor timeout). Root-caused and confirmed orthogonal to this phase: the republish batch logged processed=0 and the TEE worker received zero /republish calls — the batch found no due schedule row (a local makeScheduleDue-vs-enrollment-commit scheduling race), so the run never reached the renamed wire field. Reproduced identically on a clean, truncated DB (not state pollution). The renamed encryptedIpnsPrivateKey decode+re-sign contract is instead proven by the tee-worker unit suite (76 pass, real decryptWithFallback) and the api tee/republish specs. Deferred to the CI sdk-e2e gate, which provisions a clean stack.

Triage tally

  • Fixed: 3 (2 real zeroization gaps + 1 stale comment) + 1 test added
  • Todos logged: 0
  • Discarded: CodeRabbit — 2 test-only zeroization nits, 1 canonical-codec micro-opt (byte-parity risk), 1 false positive (hex-string field), 1 ciphertext-zeroization, 1 dev-script error diagnostic, 1 premature STATE.md cursor bump; simplify — wrapIpnsKeyForTee 1:1 passthrough (deliberate D-09 seam) and a pre-existing client.ts codec dedup (out of phase scope); security sweep — 1 unused-import false positive.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security

    • Improved cleanup of sensitive encryption keys when operations fail or scripts exit.
    • Standardized AES key handling to reduce exposure of temporary key material.
  • Improvements

    • Consolidated Base64 encoding and decoding for consistent results across encryption workflows.
    • Updated encrypted IPNS key terminology across SDK and TEE integrations.
    • Simplified TEE key wrapping and shared-folder processing.
  • Breaking Changes

    • Removed deprecated share callback configuration and permission-update helper APIs.

FSM1 and others added 30 commits July 11, 2026 21:09
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: bca41a8e1287
- Round-trip cases: empty, 1-byte, 40000-byte (crosses chunk boundary), fixed pattern
- Known-vector parity oracle: (bytes, base64String) pair
- Output type assertions for bytesToBase64/base64ToBytes
- RED confirmed: helpers not yet defined in @cipherbox/crypto

Entire-Checkpoint: dca3bce0f15c
- bytesToBase64/base64ToBytes added to utils/encoding.ts, copied verbatim
  from packages/core/src/node/encode.ts chunked-btoa implementation
- Re-exported from utils/index.ts and top-level index.ts alongside hex helpers
- Golden-vector suite green, build and typecheck pass

Entire-Checkpoint: 26f069806a90
- Add caller-key-unchanged aliasing assertion (D-09)
- Add round-trip parity assertion via importAesKey + decryptAesGcm

Entire-Checkpoint: f77eeb523158
- Shared internal helper owns a local copy of the caller key and zeroes
  it in a finally block after crypto.subtle.importKey consumes the bytes
- Caller-supplied key is never mutated (D-09: caller is terminal owner)
- Exported from the aes barrel for test access only, not top-level

Entire-Checkpoint: e43fe751bda7
- encryptAesGcm, encryptAesGcmAad, decryptAesGcm, decryptAesGcmAad,
  encryptAesCtr, decryptAesCtr, decryptAesCtrRange now import their key
  via the shared importAesKey helper instead of an inline unzeroed copy
- Widen importAesKey algorithm param to AlgorithmIdentifier so it
  accepts the plain { name } algorithm object used by GCM and CTR
- seal.ts untouched (no independent key copy); no other logic changed

Entire-Checkpoint: 7da812e34303
- Add plan summary documenting importAesKey helper + 7-function routing

Entire-Checkpoint: 2f7bb032b048
…encryptedIpnsPrivateKey

- Renamed RepublishEntry field in apps/api/src/tee/tee.service.ts
- Renamed the teeEntries build key in apps/api/src/republish/republish.service.ts (value unchanged)
- Renamed request-body field and decode call in apps/tee-worker/src/routes/republish.ts
- Renamed decryptIpnsKey and decryptWithFallback params plus JSDoc in apps/tee-worker/src/services/key-manager.ts

Entire-Checkpoint: e1b35c88096c
…vateKey field

- Renamed fixtures, makeEntry() helper, and assertions in all three spec files
- Updated 4 negative not.toHaveProperty assertions in republish.service.spec.ts to the canonical name
- API + tee-worker suites verified green (72 + 76 passing)

Entire-Checkpoint: db29e88fb50d
Consolidates the duplicated Phase 71 root-ownership authorization
gate from shares.service.ts createShare and share-invite.service.ts
createInvite into a single exported assertRootOwnership function in
apps/api/src/shares/root-ownership.util.ts. Same repository query,
identical ForbiddenException message — no behavior change.

Entire-Checkpoint: 8517a8fda79e
Round-trip test for the bytes-in/bytes-out wrapIpnsKeyForTee signature
(todo 2026-07-10-wrapipnskeyfortee-bytes-in-bytes-out). RED against the
current hex-string signature.

Entire-Checkpoint: 479ba6a9fa3c
Change wrapIpnsKeyForTee to accept and return raw Uint8Array, renaming
the TEE public key param to the canonical teePublicKey. Removes the
internal hex encode/decode so the helper matches the bytes-internal
convention used everywhere else in sdk-core; hex now lives only at the
3 call sites.

Implements todo 2026-07-10-wrapipnskeyfortee-bytes-in-bytes-out.

Entire-Checkpoint: 36bb13f9b5c1
Move hex encode/decode of the TEE public key and wrapped result out of
wrapIpnsKeyForTee and into folder/registration.ts, vault/index.ts, and
file/index.ts — the call sites now hex-decode teeKeys.currentPublicKey
before calling and hex-encode the returned bytes into
encryptedIpnsPrivateKey. Also trims a stray hexToBytes mention from
wrap.ts's JSDoc so the helper reads as fully bytes-only.

sdk-core typechecks and the full unit suite passes with no behavior
change.

Entire-Checkpoint: 75c8f6688692
The v2.0 grant model superseded the per-recipient share re-wrap fan-out
(shareCallbacks/ShareCallbacks) and the addShareKeysFn threaded through
SharedWriteContext/SharedFolderState — every production construction site
already passed a no-op. Removed the dead type, field, and every construction
site across the SDK and web hooks, plus the now-meaningless
.not.toHaveBeenCalled() test assertions.

Rebuilt packages/sdk dist before running apps/web typecheck to avoid
cross-package dist staleness.

Entire-Checkpoint: 3c788b72c382
…onDto

updateSharePermission/updatePermissionFn had no live caller in apps/web or
client.ts (only its own test), and UpdatePermissionDto/
UpdatePermissionDtoPermission are generated api-client models with no
backing route in the current openapi.json. Removed both, plus the
deprecated ReceivedShare.encryptedIpnsKey field it was paired with
(unread anywhere in the web app).

No pnpm api:generate run — these are orphaned artifacts with no source
route, so regeneration would not recreate them.

Entire-Checkpoint: 056f4c090f43
Reword the ReceivedShare doc comment to stop naming the now-deleted
updateSharePermission wrapper literally, keeping the plan's whole-tree
drift grep clean.

Entire-Checkpoint: cbae022041af
FSM1 and others added 11 commits July 11, 2026 21:13
Wraps main()'s body from after the vaultKeyBlob load onward in
try/finally and clears userPrivateKey, vaultKeyBlob.rootReadKey,
vaultKeyBlob.rootWriteKey, and the derived fileReadKey/subReadKey
(when the --folder-name branch runs) via clearBytes in the finally
block — mirrors the existing edit-filepointer.mts/rename-folder.mts
pattern. verify-filepointer.mts was the one script in this family
that never cleared its key material.

Entire-Checkpoint: 5584dff73442
Entire-Checkpoint: 4dde6fd97965
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 56f9c434304b
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 8d730bd7279c
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Z3zThQZPm6bVk2476WURL
Entire-Checkpoint: 7be0d29834e0
Entire-Checkpoint: cf8dda6f85c2
Entire-Checkpoint: 312291dbf346
…ify-filepointer

Entire-Checkpoint: f04f9add91a2
…heduling caveat

Entire-Checkpoint: a34aa33b85e0
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Walkthrough

Phase 77 completes crypto hygiene and terminology canonicalization. It centralizes Base64 and AES key handling, renames encrypted IPNS key fields, removes obsolete SDK share plumbing, extracts root-ownership validation, and adds error-path key zeroization.

Changes

Crypto hygiene and codec consolidation

Layer / File(s) Summary
Canonical crypto utilities
packages/crypto/*, packages/core/src/node/*, packages/sdk-core/src/{rotation,share,file}/*
Adds shared Base64 helpers, routes AES imports through zeroizing importAesKey, and removes duplicate codec implementations while preserving decoding length checks.
Key lifecycle cleanup
packages/sdk-core/src/folder/registration.ts, packages/sdk-core/scripts/verify-filepointer.mts, packages/sdk-core/src/__tests__/*
Zeroizes minted keys on subfolder failure and clears script-held key material in finally blocks, with regression tests.
TEE and IPNS terminology
packages/sdk-core/src/{tee,file,upload,vault}/*, apps/{api,tee-worker}/src/*, packages/*/src/__tests__/*
Changes TEE wrapping to bytes-in/bytes-out and standardizes encrypted IPNS private-key field names across payloads, return types, fixtures, and assertions.
Share API cleanup and authorization extraction
packages/sdk/src/*, apps/web/src/*, apps/api/src/shares/*
Removes obsolete share callback and permission APIs, updates shared-write wiring, and centralizes root ownership validation in assertRootOwnership.
Phase records and verification
.planning/*
Marks Phase 77 complete, records its ten plans, and adds research, security, validation, pattern, and verification documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main themes: crypto codec consolidation, zeroization hardening, and IPNS terminology renames.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/crypto-hygiene-and-terminology-canonicalization

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


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

❤️ Share

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

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

Copy link
Copy Markdown
Contributor

Release Preview

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

Cascade Details

  • crypto minor -> desktop patch (direct dependency)
  • api minor -> cipherbox-fuse patch (direct dependency)
  • api minor -> cipherbox-sdk patch (direct dependency)

@github-actions github-actions Bot added the release:cipherbox-sdk:fix Patch version bump (bug fix) for cipherbox-sdk label Jul 11, 2026
@greptile-apps

greptile-apps Bot commented Jul 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR consolidates crypto helpers and standardizes IPNS private-key naming across the monorepo. The main changes are:

  • Shared Base64 encode/decode helpers in @cipherbox/crypto.
  • AES key import routed through a zeroizing helper.
  • TEE republish fields renamed to encryptedIpnsPrivateKey.
  • wrapIpnsKeyForTee changed to bytes-in and bytes-out.
  • Dead share callback and permission-update scaffolding removed.
  • Shared root-ownership helper extracted for share creation and invites.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The accepted follow-up scope did not surface a separate issue beyond the already covered TEE field rename path.

Important Files Changed

Filename Overview
apps/api/src/republish/republish.service.ts Builds TEE republish entries with the canonical encryptedIpnsPrivateKey field.
apps/tee-worker/src/routes/republish.ts Reads the canonical encryptedIpnsPrivateKey field from republish requests.
packages/crypto/src/aes/import-key.ts Adds a shared AES key-import helper that clears its local key copy.
packages/sdk-core/src/tee/wrap.ts Updates TEE key wrapping to take and return byte arrays.

Reviews (2): Last reviewed commit: "docs(phase-77): extract phase learnings" | Re-trigger Greptile

Comment thread apps/tee-worker/src/routes/republish.ts
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.62%. Comparing base (77e52cb) to head (8d07c72).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main     #609       +/-   ##
===========================================
+ Coverage   70.16%   84.62%   +14.45%     
===========================================
  Files         157      123       -34     
  Lines       15426    11128     -4298     
  Branches     1952     1950        -2     
===========================================
- Hits        10824     9417     -1407     
+ Misses       4348     1457     -2891     
  Partials      254      254               
Flag Coverage Δ
api 84.62% <100.00%> (-0.12%) ⬇️
api-client 84.62% <100.00%> (-0.12%) ⬇️
core 84.62% <100.00%> (-0.12%) ⬇️
crypto 84.62% <100.00%> (-0.12%) ⬇️
desktop ?
sdk 84.62% <100.00%> (-0.12%) ⬇️
sdk-core 84.62% <100.00%> (-0.12%) ⬇️

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

Files with missing lines Coverage Δ
apps/api/src/republish/republish.service.ts 86.89% <ø> (ø)
apps/api/src/shares/root-ownership.util.ts 100.00% <100.00%> (ø)
apps/api/src/shares/share-invite.service.ts 80.89% <100.00%> (-0.22%) ⬇️
apps/api/src/shares/shares.service.ts 96.25% <100.00%> (-0.05%) ⬇️
apps/api/src/tee/tee.service.ts 98.41% <ø> (ø)
packages/core/src/node/decode.ts 31.83% <100.00%> (-1.10%) ⬇️
packages/core/src/node/encode.ts 87.32% <100.00%> (-1.43%) ⬇️
packages/core/src/node/seal.ts 95.31% <100.00%> (-0.55%) ⬇️
packages/crypto/src/aes/decrypt-ctr.ts 91.76% <100.00%> (-1.10%) ⬇️
packages/crypto/src/aes/decrypt.ts 85.24% <100.00%> (-2.60%) ⬇️
... and 12 more

... and 36 files with indirect coverage changes

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

Entire-Checkpoint: d9e2d7aa46c2
@FSM1 FSM1 merged commit e804443 into main Jul 11, 2026
30 checks passed
@FSM1 FSM1 deleted the feat/crypto-hygiene-and-terminology-canonicalization branch July 11, 2026 20:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/sdk/src/client.ts (1)

5134-5135: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale shared-context documentation.

The buildSharedWriteContextFromState comment still says addShareKeys is carried into the context, but this call no longer passes it and SharedFolderState no longer defines it. Remove that reference so the documented contract matches the implementation.

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

In `@packages/sdk/src/client.ts` around lines 5134 - 5135, Update the
documentation comment for buildSharedWriteContextFromState to remove the stale
reference to addShareKeys, ensuring it matches the current call and
SharedFolderState contract.
🧹 Nitpick comments (2)
packages/sdk-core/src/__tests__/rotation/write-revocation.test.ts (1)

84-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider moving unwrapKey/reWrapKey into mockFns for consistency.

Unlike grant.test.ts and navigate.test.ts where all mocked functions are sourced from mockFns, unwrapKey and reWrapKey here are inline vi.fn() (lines 94-95). If any test case later needs to assert on their call arguments, the mock references won't be accessible. Moving them into mockFns aligns with the established pattern and keeps the door open for assertions.

♻️ Suggested refactor
 const mockFns = vi.hoisted(() => ({
   // ...existing mocks...
   wrapKey: vi.fn(),
+  unwrapKey: vi.fn(),
+  reWrapKey: vi.fn(),
 }));

And in the mock return:

     wrapKey: mockFns.wrapKey,
-    unwrapKey: vi.fn(),
-    reWrapKey: vi.fn(),
+    unwrapKey: mockFns.unwrapKey,
+    reWrapKey: mockFns.reWrapKey,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk-core/src/__tests__/rotation/write-revocation.test.ts` around
lines 84 - 97, Move the unwrapKey and reWrapKey mock references into the
existing mockFns collection, then use those shared references in the
`@cipherbox/crypto` mock alongside the other mocked functions. Preserve their
current vi.fn() behavior while making the mocks accessible for call-argument
assertions.
packages/crypto/src/__tests__/encoding.test.ts (1)

15-62: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add exact-boundary and invalid-input cases.

The current coverage is good, but add tests for 32,768/32,769-byte inputs and malformed Base64 so the chunking boundary and decoder failure behavior remain explicit.

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

In `@packages/crypto/src/__tests__/encoding.test.ts` around lines 15 - 62, Add two
new test cases in the 'Base64 Codec — Round-Trip' describe block to test the
exact chunking boundary: one for a 32,768-byte array and one for a 32,769-byte
array, both using the bytesToBase64 and base64ToBytes round-trip pattern similar
to the existing 40000-byte test. Additionally, add a new describe block or test
cases to validate that base64ToBytes properly handles malformed or invalid
Base64 input strings and fails gracefully with appropriate error behavior.

Source: Path instructions

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

Inline comments:
In @.planning/STATE.md:
- Around line 5-16: Update or remove the stale legacy footer in STATE.md so it
no longer reports the June 27–28, 2026 handoff; keep the July 11, 2026 metadata
near current_phase as the single authoritative session handoff.

In `@packages/sdk-core/src/vault/index.ts`:
- Around line 149-154: Update the wrapping flow around wrapIpnsKeyForTee in the
current method to zeroize the locally owned wrappedBytes buffer in a finally
block after bytesToHex completes, while preserving encryptedIpnsPrivateKey and
never clearing the caller-owned params.rootIpnsKeypair.privateKey.

---

Outside diff comments:
In `@packages/sdk/src/client.ts`:
- Around line 5134-5135: Update the documentation comment for
buildSharedWriteContextFromState to remove the stale reference to addShareKeys,
ensuring it matches the current call and SharedFolderState contract.

---

Nitpick comments:
In `@packages/crypto/src/__tests__/encoding.test.ts`:
- Around line 15-62: Add two new test cases in the 'Base64 Codec — Round-Trip'
describe block to test the exact chunking boundary: one for a 32,768-byte array
and one for a 32,769-byte array, both using the bytesToBase64 and base64ToBytes
round-trip pattern similar to the existing 40000-byte test. Additionally, add a
new describe block or test cases to validate that base64ToBytes properly handles
malformed or invalid Base64 input strings and fails gracefully with appropriate
error behavior.

In `@packages/sdk-core/src/__tests__/rotation/write-revocation.test.ts`:
- Around line 84-97: Move the unwrapKey and reWrapKey mock references into the
existing mockFns collection, then use those shared references in the
`@cipherbox/crypto` mock alongside the other mocked functions. Preserve their
current vi.fn() behavior while making the mocks accessible for call-argument
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 66fa8ca0-eab2-49ab-8363-a8556edc84b3

📥 Commits

Reviewing files that changed from the base of the PR and between 77e52cb and c5b118a.

⛔ Files ignored due to path filters (3)
  • packages/api-client/src/models/index.ts is excluded by !packages/api-client/**
  • packages/api-client/src/models/updatePermissionDto.ts is excluded by !packages/api-client/**
  • packages/api-client/src/models/updatePermissionDtoPermission.ts is excluded by !packages/api-client/**
📒 Files selected for processing (108)
  • .planning/PROJECT.md
  • .planning/ROADMAP.md
  • .planning/STATE.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-01-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-01-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-02-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-02-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-03-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-03-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-04-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-04-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-05-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-05-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-06-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-06-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-07-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-07-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-08-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-08-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-09-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-09-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-10-PLAN.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-10-SUMMARY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-PATTERNS.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-RESEARCH.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-SECURITY.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-VALIDATION.md
  • .planning/phases/77-crypto-hygiene-and-terminology-canonicalization/77-VERIFICATION.md
  • .planning/todos/completed/2026-06-20-e2e-helper-scripts-zeroize-userprivatekey.md
  • .planning/todos/completed/2026-06-28-zeroize-local-key-plaintext-copies-in-aes-helpers.md
  • .planning/todos/completed/2026-06-29-dedup-base64-helpers-sdk-core-share.md
  • .planning/todos/completed/2026-06-29-node-codec-base64-helper-dedup.md
  • .planning/todos/completed/2026-07-01-rename-encrypted-ipns-key-canonical-field.md
  • .planning/todos/completed/2026-07-02-retire-dead-sdk-share-scaffolding.md
  • .planning/todos/completed/2026-07-03-drop-discarded-per-upload-ecies-wrapkey.md
  • .planning/todos/completed/2026-07-03-hoist-base64tobytes-into-crypto-package.md
  • .planning/todos/completed/2026-07-04-rename-ipnsprivatekeyencrypted-to-encryptedipnsprivatekey.md
  • .planning/todos/completed/2026-07-10-extract-assert-root-ownership-helper.md
  • .planning/todos/completed/2026-07-10-wrapipnskeyfortee-bytes-in-bytes-out.md
  • .planning/todos/completed/2026-07-10-zeroize-createsubfolder-keys-on-error-path.md
  • apps/api/src/republish/republish.service.spec.ts
  • apps/api/src/republish/republish.service.ts
  • apps/api/src/shares/root-ownership.util.ts
  • apps/api/src/shares/share-invite.service.ts
  • apps/api/src/shares/shares.service.ts
  • apps/api/src/tee/tee.service.spec.ts
  • apps/api/src/tee/tee.service.ts
  • apps/tee-worker/src/__tests__/republish.test.ts
  • apps/tee-worker/src/routes/republish.ts
  • apps/tee-worker/src/services/key-manager.ts
  • apps/web/src/hooks/__tests__/useSharedWriteOps.test.ts
  • apps/web/src/hooks/shared-folder-projection.ts
  • apps/web/src/hooks/useAuth.ts
  • apps/web/src/hooks/useFolderMutations.ts
  • apps/web/src/hooks/useSharedNavigation.ts
  • apps/web/src/hooks/useSharedNavigationActions.ts
  • apps/web/src/stores/share.store.ts
  • packages/core/src/node/decode.ts
  • packages/core/src/node/encode.ts
  • packages/core/src/node/seal.ts
  • packages/crypto/src/__tests__/aes.test.ts
  • packages/crypto/src/__tests__/encoding.test.ts
  • packages/crypto/src/aes/decrypt-ctr.ts
  • packages/crypto/src/aes/decrypt.ts
  • packages/crypto/src/aes/encrypt-ctr.ts
  • packages/crypto/src/aes/encrypt.ts
  • packages/crypto/src/aes/import-key.ts
  • packages/crypto/src/aes/index.ts
  • packages/crypto/src/index.ts
  • packages/crypto/src/utils/encoding.ts
  • packages/crypto/src/utils/index.ts
  • packages/sdk-core/scripts/verify-filepointer.mts
  • packages/sdk-core/src/__tests__/file/file-node.test.ts
  • packages/sdk-core/src/__tests__/folder.test.ts
  • packages/sdk-core/src/__tests__/rotation/grant-remint.test.ts
  • packages/sdk-core/src/__tests__/rotation/write-revocation.test.ts
  • packages/sdk-core/src/__tests__/share/grant.test.ts
  • packages/sdk-core/src/__tests__/share/navigate.test.ts
  • packages/sdk-core/src/__tests__/upload.test.ts
  • packages/sdk-core/src/file/index.ts
  • packages/sdk-core/src/folder/registration.ts
  • packages/sdk-core/src/rotation/engine.ts
  • packages/sdk-core/src/share/grant.ts
  • packages/sdk-core/src/share/navigate.ts
  • packages/sdk-core/src/tee/__tests__/wrap.test.ts
  • packages/sdk-core/src/tee/wrap.ts
  • packages/sdk-core/src/upload/index.ts
  • packages/sdk-core/src/vault/index.ts
  • packages/sdk/src/__tests__/client-extended.test.ts
  • packages/sdk/src/__tests__/client-pinning.test.ts
  • packages/sdk/src/__tests__/client-shared-write.test.ts
  • packages/sdk/src/__tests__/client-upload-concurrency.test.ts
  • packages/sdk/src/__tests__/context.test.ts
  • packages/sdk/src/__tests__/enumerate-shared-subtree.test.ts
  • packages/sdk/src/__tests__/folder-listing.test.ts
  • packages/sdk/src/__tests__/helpers.ts
  • packages/sdk/src/__tests__/move-in-shared-folder.test.ts
  • packages/sdk/src/__tests__/owner-reconcile.test.ts
  • packages/sdk/src/__tests__/resolve-shared-subfolder-write-key.test.ts
  • packages/sdk/src/__tests__/shared-folder-tree.test.ts
  • packages/sdk/src/__tests__/shared-write.test.ts
  • packages/sdk/src/__tests__/upload-batch.test.ts
  • packages/sdk/src/client.ts
  • packages/sdk/src/index.ts
  • packages/sdk/src/share/context.ts
  • packages/sdk/src/share/index.ts
  • packages/sdk/src/share/shared-write.ts
  • packages/sdk/src/types.ts
💤 Files with no reviewable changes (10)
  • packages/sdk/src/tests/enumerate-shared-subtree.test.ts
  • packages/sdk/src/tests/resolve-shared-subfolder-write-key.test.ts
  • packages/sdk/src/tests/folder-listing.test.ts
  • packages/sdk/src/tests/client-shared-write.test.ts
  • packages/sdk/src/index.ts
  • packages/sdk/src/tests/context.test.ts
  • packages/sdk/src/share/index.ts
  • apps/web/src/hooks/tests/useSharedWriteOps.test.ts
  • packages/sdk/src/tests/move-in-shared-folder.test.ts
  • packages/sdk/src/tests/shared-write.test.ts

Comment thread .planning/STATE.md
Comment on lines +5 to +16
current_phase: 78
current_phase_name: Recovery Tool v3, Vault-Load Guards, Web UX and CI Guards
status: verifying
stopped_at: Phase 75 complete (75-05-PLAN.md), shipping PR
last_updated: "2026-07-11T20:30:00.000Z"
stopped_at: Completed 77-09-PLAN.md
last_updated: "2026-07-11T10:03:19.194Z"
last_activity: 2026-07-11
last_activity_desc: Phase 75 complete and shipping, Phase 76 pending
last_activity_desc: Phase 77 complete, transitioned to Phase 78
progress:
total_phases: 22
completed_phases: 17
total_plans: 193
completed_plans: 193
total_plans: 198
completed_plans: 198

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Synchronize the duplicated session metadata.

The new Phase 77/78 handoff records July 11, 2026, but the legacy footer still reports June 27–28, 2026. Update or remove the stale footer so the planning state has one authoritative handoff point.

Also applies to: 599-622

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

In @.planning/STATE.md around lines 5 - 16, Update or remove the stale legacy
footer in STATE.md so it no longer reports the June 27–28, 2026 handoff; keep
the July 11, 2026 metadata near current_phase as the single authoritative
session handoff.

Comment on lines +149 to +154
const teePublicKeyBytes = hexToBytes(currentPublicKey);
const wrappedBytes = await wrapIpnsKeyForTee(
params.rootIpnsKeypair.privateKey,
currentPublicKey
teePublicKeyBytes
);
encryptedIpnsPrivateKey = bytesToHex(wrappedBytes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Zeroize wrappedBytes after hex encoding.

wrappedBytes contains the ECIES-wrapped IPNS private key and remains live after bytesToHex returns. Clear this locally-owned buffer in a finally block without clearing the caller-owned private key.

Proposed fix
 const wrappedBytes = await wrapIpnsKeyForTee(
   params.rootIpnsKeypair.privateKey,
   teePublicKeyBytes
 );
- encryptedIpnsPrivateKey = bytesToHex(wrappedBytes);
+ try {
+   encryptedIpnsPrivateKey = bytesToHex(wrappedBytes);
+ } finally {
+   clearBytes(wrappedBytes);
+ }

As per coding guidelines, sensitive key material must be cleared from memory after use.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const teePublicKeyBytes = hexToBytes(currentPublicKey);
const wrappedBytes = await wrapIpnsKeyForTee(
params.rootIpnsKeypair.privateKey,
currentPublicKey
teePublicKeyBytes
);
encryptedIpnsPrivateKey = bytesToHex(wrappedBytes);
const teePublicKeyBytes = hexToBytes(currentPublicKey);
const wrappedBytes = await wrapIpnsKeyForTee(
params.rootIpnsKeypair.privateKey,
teePublicKeyBytes
);
try {
encryptedIpnsPrivateKey = bytesToHex(wrappedBytes);
} finally {
clearBytes(wrappedBytes);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sdk-core/src/vault/index.ts` around lines 149 - 154, Update the
wrapping flow around wrapIpnsKeyForTee in the current method to zeroize the
locally owned wrappedBytes buffer in a finally block after bytesToHex completes,
while preserving encryptedIpnsPrivateKey and never clearing the caller-owned
params.rootIpnsKeypair.privateKey.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant