fix: separate vault key blob from root folder IPNS name - #349
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 3753c1b0a245
The vault IPNS name was serving double duty: storing the v2 key blob on init, then being overwritten with v1 JSON on any root folder operation. This broke login after the first folder modification. Fix: derive a dedicated IPNS name for the vault key blob using HKDF info "cipherbox-vault-key-ipns-v1" (distinct from the root folder's "cipherbox-vault-ipns-v1"). The root folder now uses standard v1 JSON like all other folders. Changes: - crypto: add deriveVaultKeyIpnsKeypair (TS + Rust) - web useAuth: login reads key from vault key IPNS; new user init publishes to both vault key IPNS (v2 blob) and root folder IPNS (v1) - desktop vault.rs: same two-IPNS-name init and fetch separation - desktop fuse: remove encrypt_root_metadata_to_v2_blob, root folder uses encrypt_metadata_to_json like all subfolders - folder.service.ts + sdk-core: remove v2 blob detection (all v1 now) - recovery.html: derive vault key IPNS name for IPFS-direct recovery Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 22bb08999bbb
|
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 selected for processing (3)
WalkthroughSplits the vault-key blob publication from root-folder metadata by introducing a dedicated HKDF-derived IPNS identity for the vault key blob (v2 key-only envelope containing the ECIES-wrapped rootFolderKey) while publishing root folder metadata as v1 JSON under a separate root-folder IPNS name. Documentation, crypto derivations, init/fetch flows, and blob formats updated accordingly. Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Web as Web Client
participant Crypto as Crypto Package
participant IPFS as IPFS/IPNS
User->>Web: Initialize vault
Note over Web,Crypto: Stage 1 — Vault Key Blob (v2 key-only)
Web->>Crypto: deriveVaultKeyIpnsKeypair(userPrivateKey)
Crypto-->>Web: vaultKeyPriv, vaultKeyIpnsName
Web->>Web: ECIES-wrap rootFolderKey -> v2 key blob
Web->>IPFS: Upload v2 vault-key blob
IPFS-->>Web: IPFS hash
Web->>IPFS: Publish hash to vaultKeyIpnsName
IPFS-->>Web: Publish success
Note over Web,Crypto: Stage 2 — Root Folder Metadata (v1 JSON)
Web->>Crypto: deriveVaultIpnsKeypair(userPrivateKey)
Crypto-->>Web: rootPriv, rootIpnsName
Web->>Web: Encrypt root metadata as v1 JSON
Web->>IPFS: Upload v1 metadata JSON
IPFS-->>Web: IPFS hash
Web->>IPFS: Publish hash to rootIpnsName
IPFS-->>Web: Publish success
User->>Web: Login (existing vault)
Web->>Crypto: deriveVaultKeyIpnsKeypair(userPrivateKey)
Crypto-->>Web: vaultKeyPriv, vaultKeyIpnsName
Web->>IPFS: Resolve vaultKeyIpnsName -> v2 blob
IPFS-->>Web: v2 blob
Web->>Web: Decrypt ECIES -> rootFolderKey
Web->>Crypto: deriveVaultIpnsKeypair(userPrivateKey)
Crypto-->>Web: rootPriv, rootIpnsName
Web->>IPFS: Resolve rootIpnsName -> v1 JSON
IPFS-->>Web: v1 JSON
Web->>Web: Decrypt metadata using rootFolderKey
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #349 +/- ##
==========================================
- Coverage 51.20% 51.17% -0.03%
==========================================
Files 115 117 +2
Lines 9259 10167 +908
Branches 745 747 +2
==========================================
+ Hits 4741 5203 +462
- Misses 4339 4785 +446
Partials 179 179
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
- Add VaultKeyBlob (v2) section with binary format, lifecycle, and cross-platform notes - Mark EncryptedVaultKeys as removed (DB crypto columns dropped) - Add vault key blob IPNS to derivation summary with separation rationale - Add vault_blob to cross-implementation parity table - Renumber sections to accommodate new VaultKeyBlob entry - Bump document version to 1.1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: f83dad09c4e5
There was a problem hiding this comment.
Pull request overview
This PR fixes a vault-bricking bug by separating the vault key blob (v2 envelope containing the ECIES-wrapped rootFolderKey) from the root folder IPNS record, so normal root folder metadata publishes can’t overwrite key material.
Changes:
- Added a dedicated HKDF context + derivation helper for a “vault key blob IPNS” (
cipherbox-vault-key-ipns-v1) alongside the existing “root folder IPNS” derivation. - Updated web + desktop initialization/login/recovery flows to use two IPNS names (vault-key-blob vs root-folder-metadata) and removed folder-side v2 blob detection.
- Simplified desktop FUSE metadata publishing/decryption to treat all folder metadata payloads as v1 JSON
{iv,data}.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/sdk-core/src/folder/index.ts | Removes v2 blob detection when reading folder metadata from IPFS. |
| packages/crypto/src/vault/index.ts | Re-exports the new vault-key-blob IPNS derivation helper. |
| packages/crypto/src/vault/derive-ipns.ts | Adds deriveVaultKeyIpnsKeypair() using HKDF info cipherbox-vault-key-ipns-v1. |
| packages/crypto/src/index.ts | Exposes deriveVaultKeyIpnsKeypair from the top-level crypto package. |
| apps/web/src/services/folder.service.ts | Removes v2 blob detection from web folder metadata fetch/decrypt. |
| apps/web/src/hooks/useAuth.ts | Login reads root key from vault-key-blob IPNS; init publishes both key blob + root folder metadata. |
| apps/web/public/recovery.html | Updates recovery to derive the vault-key-blob IPNS name (not root folder IPNS). |
| apps/desktop/src-tauri/src/fuse/mod.rs | Removes root-folder v2 blob encryption path; root publishes use v1 JSON like other folders. |
| apps/desktop/src-tauri/src/fuse/decrypt.rs | Removes v2 blob detection for folder metadata decryption. |
| apps/desktop/src-tauri/src/crypto/hkdf.rs | Adds Rust derive_vault_key_ipns_keypair mirroring TypeScript derivation. |
| apps/desktop/src-tauri/src/commands/vault.rs | Desktop init publishes both key blob + root metadata; fetch reads key from vault-key-blob IPNS. |
| .planning/todos/pending/2026-03-24-separate-vault-key-blob-from-root-folder-ipns-name.md | Tracks the design/implementation plan for the two-IPNS separation. |
Comments suppressed due to low confidence (2)
apps/desktop/src-tauri/src/fuse/decrypt.rs:21
- By removing v2 blob detection here, decrypt_metadata_from_ipfs_public will fail to parse legacy root-folder payloads that are still stored as v2 vault blobs (JSON parse error on binary). If existing vaults may still have root metadata in v2 format, consider keeping v2 detection as a backwards-compatible fallback (deserialize_vault_blob_v2 → use metadata_json) until all vaults have been migrated to v1 JSON root metadata.
/// All folder metadata (including root) uses v1 JSON `{"iv":"...","data":"..."}`.
/// The v2 blob format is only used for the vault key blob (separate IPNS name).
pub fn decrypt_metadata_from_ipfs_public(
encrypted_bytes: &[u8],
folder_key: &[u8],
) -> Result<crate::crypto::folder::FolderMetadata, String> {
let json_portion = encrypted_bytes;
#[derive(serde::Deserialize)]
struct EncryptedFolderMetadata {
iv: String,
data: String,
}
let encrypted: EncryptedFolderMetadata = serde_json::from_slice(json_portion)
.map_err(|e| format!("Failed to parse encrypted metadata JSON: {}", e))?;
apps/web/public/recovery.html:575
- deriveVaultIpnsName() now uses the vault key blob HKDF info (cipherbox-vault-key-ipns-v1), but the function name and docstring still say it derives the "vault root IPNS" and matches deriveVaultIpnsKeypair. This is misleading for recovery and could cause using the wrong IPNS name; rename/update the comment to reflect that it derives the vault-key-blob IPNS (matches deriveVaultKeyIpnsKeypair).
/**
* Derive the vault root IPNS name from user's secp256k1 private key.
* Matches the derivation in @cipherbox/crypto deriveVaultIpnsKeypair.
*
* Path: secp256k1 privateKey -> HKDF-SHA256 -> 32-byte Ed25519 seed
* -> Ed25519 public key -> IPNS name (k51...)
*/
async function deriveVaultIpnsName(userPrivateKey) {
// 1. HKDF-SHA256 to derive 32-byte Ed25519 seed
const keyMaterial = await crypto.subtle.importKey(
'raw', userPrivateKey, 'HKDF', false, ['deriveBits']
);
const derivedBits = await crypto.subtle.deriveBits(
{ name: 'HKDF', hash: 'SHA-256', salt: VAULT_HKDF_SALT, info: VAULT_KEY_HKDF_INFO },
keyMaterial, 256
);
const ed25519Seed = new Uint8Array(derivedBits);
// 2. Derive Ed25519 public key from seed
const ed25519PublicKey = await ed.getPublicKeyAsync(ed25519Seed);
// 3. Derive IPNS name from Ed25519 public key
return ed25519PublicKeyToIpnsName(ed25519PublicKey);
}
The vault key blob v2 format now stores only the ECIES-wrapped rootFolderKey — no folder metadata. Root folder metadata is created separately as standard v1 JSON during vault init. Format: 0x02 | uint16_BE(key_len) | ECIES_encrypted_key - serialize/deserialize take/return a single Uint8Array (no metadata) - Remove VaultBlobV2 type (deserialize returns Uint8Array directly) - Update cross-platform test vectors (TS + Rust) for key-only format - Simplify init flows: key blob and folder metadata are independent - Remove v2 detection from recovery tool folder traversal - Update METADATA_SCHEMAS.md format table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 37632b4f3079
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/desktop/src-tauri/src/commands/vault.rs (1)
90-95: Consider aborting on vault key blob publish conflict.A conflict at sequence 0 during vault init indicates a serious issue (e.g., race condition with another device or corrupted state). The current code logs a warning but continues, potentially leaving the vault in an inconsistent state if the subsequent folder publish or backend registration fails.
For vault initialization, consider returning an error on conflict rather than proceeding:
match crate::api::ipns::publish_ipns(&state.api, &key_publish_req).await? { crate::api::ipns::PublishResult::Success => {} crate::api::ipns::PublishResult::Conflict { .. } => { - log::warn!("Unexpected conflict on vault key blob publish (sequence 0)"); + return Err("Unexpected conflict on vault key blob publish (sequence 0) - vault may already exist".into()); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src-tauri/src/commands/vault.rs` around lines 90 - 95, The match handling the result of crate::api::ipns::publish_ipns(&state.api, &key_publish_req) currently swallows PublishResult::Conflict and only logs a warning; change it to abort vault initialization by returning an Err when PublishResult::Conflict is returned (instead of just log), so the calling init routine fails fast; update the match arm handling crate::api::ipns::PublishResult::Conflict { .. } to construct and return a descriptive error (propagate the error type used in the surrounding function) referencing publish_ipns, PublishResult::Conflict, key_publish_req and state.api.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/METADATA_SCHEMAS.md`:
- Line 210: The cross-reference text "Section 14" in the sentence referencing
IPNS key derivation is out of sync with the anchor
`#15-ipns-key-derivation-summary`; update the display text to "Section 15" (or
alternatively change the anchor to `#14-ipns-key-derivation-summary` if you
intended Section 14) so the link and label match; locate the string in
METADATA_SCHEMAS.md that contains "Section 14" and
`#15-ipns-key-derivation-summary` and make the label/anchor consistent.
---
Nitpick comments:
In `@apps/desktop/src-tauri/src/commands/vault.rs`:
- Around line 90-95: The match handling the result of
crate::api::ipns::publish_ipns(&state.api, &key_publish_req) currently swallows
PublishResult::Conflict and only logs a warning; change it to abort vault
initialization by returning an Err when PublishResult::Conflict is returned
(instead of just log), so the calling init routine fails fast; update the match
arm handling crate::api::ipns::PublishResult::Conflict { .. } to construct and
return a descriptive error (propagate the error type used in the surrounding
function) referencing publish_ipns, PublishResult::Conflict, key_publish_req and
state.api.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 47af1885-0040-464a-81cf-b74d2d9bebf1
📒 Files selected for processing (13)
.planning/todos/pending/2026-03-24-separate-vault-key-blob-from-root-folder-ipns-name.mdapps/desktop/src-tauri/src/commands/vault.rsapps/desktop/src-tauri/src/crypto/hkdf.rsapps/desktop/src-tauri/src/fuse/decrypt.rsapps/desktop/src-tauri/src/fuse/mod.rsapps/web/public/recovery.htmlapps/web/src/hooks/useAuth.tsapps/web/src/services/folder.service.tsdocs/METADATA_SCHEMAS.mdpackages/crypto/src/index.tspackages/crypto/src/vault/derive-ipns.tspackages/crypto/src/vault/index.tspackages/sdk-core/src/folder/index.ts
- Remove dead user_public_key param from spawn_metadata_publish and both call sites (no longer needed after v2 root removal) - Use vaultKeyKeypair.ipnsName directly instead of re-deriving via deriveIpnsName (login + init paths) - Fix section cross-reference "Section 14" -> "Section 15" in METADATA_SCHEMAS.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 50257f9b7a08
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
apps/web/public/recovery.html:572
- deriveVaultIpnsName() is now using VAULT_KEY_HKDF_INFO, but the function name/docs still say it derives the root folder vault IPNS name and that it matches deriveVaultIpnsKeypair. This will make the recovery flow derive the vault-key-blob IPNS name while later treating it as the root folder IPNS name (breaking folder traversal). Update the recovery tool to derive and use two distinct names: vault-key-blob IPNS for fetching the v2 key blob, and root-folder IPNS for fetching v1 folder metadata.
// Crypto: Derive Vault IPNS Name from Private Key (HKDF)
// =========================================================================
const VAULT_HKDF_SALT = new TextEncoder().encode('CipherBox-v1');
// Vault KEY blob IPNS (stores ECIES-wrapped rootFolderKey) — NOT the root folder IPNS
const VAULT_KEY_HKDF_INFO = new TextEncoder().encode('cipherbox-vault-key-ipns-v1');
/**
* Derive the vault root IPNS name from user's secp256k1 private key.
* Matches the derivation in @cipherbox/crypto deriveVaultIpnsKeypair.
*
* Path: secp256k1 privateKey -> HKDF-SHA256 -> 32-byte Ed25519 seed
* -> Ed25519 public key -> IPNS name (k51...)
*/
async function deriveVaultIpnsName(userPrivateKey) {
// 1. HKDF-SHA256 to derive 32-byte Ed25519 seed
const keyMaterial = await crypto.subtle.importKey(
'raw', userPrivateKey, 'HKDF', false, ['deriveBits']
);
const derivedBits = await crypto.subtle.deriveBits(
{ name: 'HKDF', hash: 'SHA-256', salt: VAULT_HKDF_SALT, info: VAULT_KEY_HKDF_INFO },
keyMaterial, 256
);
const ed25519Seed = new Uint8Array(derivedBits);
// 2. Derive Ed25519 public key from seed
const ed25519PublicKey = await ed.getPublicKeyAsync(ed25519Seed);
// 3. Derive IPNS name from Ed25519 public key
return ed25519PublicKeyToIpnsName(ed25519PublicKey);
}
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/public/recovery.html (1)
546-563:⚠️ Potential issue | 🟠 MajorDerive the root-folder IPNS name separately from the vault-key IPNS name.
deriveVaultIpnsName()now usescipherbox-vault-key-ipns-v1, so the IPFS-direct path is deriving the vault-key blob location and later reusing it asrootIpnsName. Step 4 then resolves the key blob as if it were folder metadata, so migrated vault recovery breaks as soon as it starts traversing folders.🔧 Proposed fix
const VAULT_HKDF_SALT = new TextEncoder().encode('CipherBox-v1'); -// Vault KEY blob IPNS (stores ECIES-wrapped rootFolderKey) — NOT the root folder IPNS -const VAULT_KEY_HKDF_INFO = new TextEncoder().encode('cipherbox-vault-key-ipns-v1'); +const VAULT_ROOT_HKDF_INFO = new TextEncoder().encode('cipherbox-vault-ipns-v1'); +const VAULT_KEY_HKDF_INFO = new TextEncoder().encode('cipherbox-vault-key-ipns-v1'); -async function deriveVaultIpnsName(userPrivateKey) { +async function deriveVaultRootIpnsName(userPrivateKey) { + return deriveVaultIpnsNameWithInfo(userPrivateKey, VAULT_ROOT_HKDF_INFO); +} + +async function deriveVaultKeyIpnsName(userPrivateKey) { + return deriveVaultIpnsNameWithInfo(userPrivateKey, VAULT_KEY_HKDF_INFO); +} + +async function deriveVaultIpnsNameWithInfo(userPrivateKey, info) { const keyMaterial = await crypto.subtle.importKey( 'raw', userPrivateKey, 'HKDF', false, ['deriveBits'] ); const derivedBits = await crypto.subtle.deriveBits( - { name: 'HKDF', hash: 'SHA-256', salt: VAULT_HKDF_SALT, info: VAULT_KEY_HKDF_INFO }, + { name: 'HKDF', hash: 'SHA-256', salt: VAULT_HKDF_SALT, info }, keyMaterial, 256 ); const ed25519Seed = new Uint8Array(derivedBits); const ed25519PublicKey = await ed.getPublicKeyAsync(ed25519Seed); return ed25519PublicKeyToIpnsName(ed25519PublicKey); }Then fetch the v2 key blob with
deriveVaultKeyIpnsName(privateKeyBytes), but keepexportData.rootIpnsNamesourced fromderiveVaultRootIpnsName(privateKeyBytes).Also applies to: 1266-1320
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/public/recovery.html` around lines 546 - 563, deriveVaultIpnsName is currently using the VAULT_KEY_HKDF_INFO (cipherbox-vault-key-ipns-v1) so the code derives the vault-key IPNS name and later incorrectly reuses it as the root-folder IPNS name; split the derivation into two distinct functions and uses: keep deriveVaultKeyIpnsName (uses VAULT_KEY_HKDF_INFO) to locate/fetch the v2 key blob, and introduce/restore deriveVaultRootIpnsName (uses the root-folder HKDF info) to produce the actual root folder IPNS name; ensure exportData.rootIpnsName is assigned from deriveVaultRootIpnsName(privateKeyBytes) while any key-blob fetch uses deriveVaultKeyIpnsName(privateKeyBytes) so folder resolution uses the correct IPNS name.apps/web/src/hooks/useAuth.ts (1)
102-146:⚠️ Potential issue | 🟠 MajorOnly treat
vaultApi.getVault()404 as “new user”.This
catchnow wraps the dedicated key-blob resolve/fetch too. If an existing vault has a missing or lagging vault-key CID and one of those downstream calls returns 404, we’ll re-enter initialization against the same deterministic IPNS names instead of surfacing a broken/missing vault-key record.🔧 Proposed fix
- try { + let existingVaultLoaded = false; + try { // Try to fetch existing vault const existingVault = await vaultApi.getVault(); + existingVaultLoaded = true; @@ - if (is404) { + if (is404 && !existingVaultLoaded) { // New user -- initialize vault with separate key blob + folder metadata console.log('[Auth] New user -- initializing vault'); const newVault = await initializeVault(userKeypair.privateKey);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/hooks/useAuth.ts` around lines 102 - 146, The catch block is too broad and treats any downstream 404 (e.g., from resolveIpnsRecord or fetchFromIpfs) as "new user"; isolate the vaultApi.getVault() call so only its 404 triggers initialization. Concretely, wrap vaultApi.getVault() in its own try/catch (or check error origin) and if that call returns a 404 run initializeVault(userKeypair.privateKey); for all other errors from subsequent calls (deriveVaultKeyIpnsKeypair, resolveIpnsRecord, fetchFromIpfs, unwrapKey, etc.) rethrow or let them propagate so they are not misclassified as new user; keep references to vaultApi.getVault, deriveVaultKeyIpnsKeypair, resolveIpnsRecord, fetchFromIpfs, deserializeVaultBlobV2, unwrapKey, deriveVaultIpnsKeypair, and initializeVault to locate the code to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/METADATA_SCHEMAS.md`:
- Around line 437-443: Update the table row for "Vault key blob IPNS" so its
"Stores" column accurately reflects that the v2 blob is the ECIES-wrapped
rootFolderKey only (no appended metadata); align this with the VaultKeyBlob
section and the implementation in derive-ipns.ts
(packages/crypto/src/vault/derive-ipns.ts) and ensure any mention of "ECIES key
+ metadata" is replaced with "ECIES-wrapped rootFolderKey (key-only, v2
format)".
---
Outside diff comments:
In `@apps/web/public/recovery.html`:
- Around line 546-563: deriveVaultIpnsName is currently using the
VAULT_KEY_HKDF_INFO (cipherbox-vault-key-ipns-v1) so the code derives the
vault-key IPNS name and later incorrectly reuses it as the root-folder IPNS
name; split the derivation into two distinct functions and uses: keep
deriveVaultKeyIpnsName (uses VAULT_KEY_HKDF_INFO) to locate/fetch the v2 key
blob, and introduce/restore deriveVaultRootIpnsName (uses the root-folder HKDF
info) to produce the actual root folder IPNS name; ensure
exportData.rootIpnsName is assigned from
deriveVaultRootIpnsName(privateKeyBytes) while any key-blob fetch uses
deriveVaultKeyIpnsName(privateKeyBytes) so folder resolution uses the correct
IPNS name.
In `@apps/web/src/hooks/useAuth.ts`:
- Around line 102-146: The catch block is too broad and treats any downstream
404 (e.g., from resolveIpnsRecord or fetchFromIpfs) as "new user"; isolate the
vaultApi.getVault() call so only its 404 triggers initialization. Concretely,
wrap vaultApi.getVault() in its own try/catch (or check error origin) and if
that call returns a 404 run initializeVault(userKeypair.privateKey); for all
other errors from subsequent calls (deriveVaultKeyIpnsKeypair,
resolveIpnsRecord, fetchFromIpfs, unwrapKey, etc.) rethrow or let them propagate
so they are not misclassified as new user; keep references to vaultApi.getVault,
deriveVaultKeyIpnsKeypair, resolveIpnsRecord, fetchFromIpfs,
deserializeVaultBlobV2, unwrapKey, deriveVaultIpnsKeypair, and initializeVault
to locate the code to change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a0eed504-8825-4e03-aede-f110d3a6ba5a
📒 Files selected for processing (12)
apps/desktop/src-tauri/src/commands/vault.rsapps/desktop/src-tauri/src/crypto/vault_blob.rsapps/desktop/src-tauri/src/fuse/mod.rsapps/web/public/recovery.htmlapps/web/src/hooks/useAuth.tsdocs/METADATA_SCHEMAS.mdpackages/core/src/__tests__/vault-blob-vectors.test.tspackages/core/src/__tests__/vault-blob.test.tspackages/core/src/index.tspackages/core/src/vault/blob.tspackages/core/src/vault/index.tspackages/core/src/vault/types.ts
Recovery.html was using the vault key blob IPNS name for folder metadata traversal. Now derives both IPNS names separately — vault key IPNS for fetching the encrypted rootFolderKey, root folder IPNS for recovery folder traversal. Also updates METADATA_SCHEMAS.md to reflect key-only v2 blob format (no bundled metadata). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: d2b9f4987bb4
The catch block was too broad — any 404 from resolveIpnsRecord or fetchFromIpfs would be misclassified as "new user" and trigger vault re-initialization. Now getVault() is in its own try/catch so only its 404 triggers the init path; IPNS/IPFS errors propagate correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 716c90051044
|
Addressed 2 outside-diff-range comments from CodeRabbit review:
|
- METADATA_SCHEMAS: distinguish immutable blob content from IPNS record republishing (handled by TEE every 3h) - recovery.html: update label to "v2 vault key blob + v1 folder metadata" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 41ca9c6985ab
- Clarify "v1 folder metadata" comments to distinguish IPFS envelope
format (v1 {iv, data}) from FolderMetadata schema version (v2)
- Use subarray() instead of slice() in deserializeVaultBlobV2 for
zero-copy (matches Rust borrowed-slice behavior)
- Abort vault init on sequence-0 conflict instead of warn-and-continue
to prevent mismatched rootFolderKey
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: ed4ac2357b62
Summary
updateFolderMetadataoverwrote it with v1 JSON on any root folder operation, breaking login after the first folder modificationcipherbox-vault-key-ipns-v1(distinct from root folder'scipherbox-vault-ipns-v1). Root folder uses standard v1 JSON like all other folders.What changed
@cipherbox/cryptoderiveVaultKeyIpnsKeypairwith dedicated HKDF contextuseAuth.tsvault.rs(desktop)fuse/mod.rsencrypt_root_metadata_to_v2_blob— root uses v1 like all foldersfuse/decrypt.rsfolder.service.tsfetchAndDecryptMetadatasdk-corerecovery.htmlhkdf.rs(Rust)derive_vault_key_ipns_keypairmatching TypeScriptTest plan
cargo checkpasses🤖 Generated with Claude Code
Summary by CodeRabbit
Improvements
Documentation