Skip to content

Conversation

@wksantiago
Copy link
Contributor

@wksantiago wksantiago commented Jan 13, 2026

Refactors flat KeepError enum into hierarchical error system with StorageError, CryptoError, KeyError, FrostError, NetworkError, and PolicyError types. Each error has unique codes for debugging.

Summary by CodeRabbit

  • Refactor
    • Reworked the error system into multiple focused error types (storage, crypto, key, frost, network, policy, etc.) for clearer, more structured reporting.
    • Standardized and enriched error variants and messages to provide more actionable, contextual feedback.
    • Preserved public APIs and behavior while updating internal error propagation and tests to match the new taxonomy.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Walkthrough

Refactors error handling across core and CLI: the monolithic KeepError is split into domain-specific enums (StorageError, CryptoError, KeyError, FrostError, NetworkError, PolicyError); From conversions added; call sites across keep-core and keep-cli updated to construct and propagate the new error variants without changing public function signatures.

Changes

Cohort / File(s) Summary
Core error definitions
keep-core/src/error.rs
Introduces domain-specific enums (StorageError, CryptoError, KeyError, FrostError, NetworkError, PolicyError) and reworks KeepError as a composite wrapper with From mappings (redb, std::io, bincode, etc.).
Crypto primitives & keys
keep-core/src/crypto.rs, keep-core/src/keys.rs
Replace generic KeepError usage with CryptoError/KeyError variants for RAM encryption/decryption, key derivation, secret/key parsing, and signing; add new CryptoError variants.
Storage & hidden volumes
keep-core/src/storage.rs, keep-core/src/hidden/volume.rs, keep-core/src/hidden/header.rs
Map header/volume/path errors to StorageError; decryption/auth/rate-limit to CryptoError; locked/not-found to KeyError; update storage CRUD, header parsing, and tests to new error variants.
FROST protocol modules
keep-core/src/frost/... (coordinator.rs, dealer.rs, share.rs, signing.rs, transport.rs)
Standardize FROST error handling to FrostError variants (Protocol, InvalidConfig, InsufficientSigners, SigningFailed, etc.) across generation, splitting, coordination, signing, and transport/serialization.
Keyring & public lib surface
keep-core/src/keyring.rs, keep-core/src/lib.rs
Key-related errors are now nested under KeepError::Key(KeyError::...); public-facing mappings for Locked/NotFound/AlreadyExists/HomeNotFound now route through KeyError/StorageError/FrostError.
CLI — core commands
keep-cli/src/commands/* (agent.rs, bitcoin.rs, enclave.rs, mod.rs, vault.rs, serve.rs)
Replace many KeepError::Other/KeyNotFound mappings with KeyError, FrostError, KeepError::Runtime, or KeepError::Input; adjust password/confirm prompts, key lookups, PSBT flows, enclave ops, and serve runtime/frost-node error mapping.
CLI — FROST / hardware / network
keep-cli/src/commands/frost.rs, .../frost_hardware.rs, .../frost_network.rs
Route protocol/network/hardware errors to FrostError, NetworkError, HardwareOperation/HardwareConnection, and KeyError; refactor many map_err closures into specific error constructors.
Server & network flows
keep-cli/src/server.rs
Map transport/relay/subscribe/publish/NIP-46 crypto steps to NetworkError and KeyError variants; runtime/notification loops emit KeepError::Runtime consistently.
Signer & handler
keep-cli/src/signer/frost_signer.rs, keep-cli/src/signer/handler.rs
Use FrostError, KeyError, CryptoError, and PolicyError for validation, signing, pubkey mismatch, permission and signature errors; update error returns and imports.
Warden / Policy client
keep-cli/src/warden.rs
Introduce PolicyError variants for HTTP/Warden requests, invalid responses, approval timeouts, and workflow failures; replace many generic KeepError::Other paths with structured PolicyError.

Sequence Diagram(s)

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • kwsantiago

Poem

🐇
I hopped through stacks of tangled error lore,
Split “Other” neat and gave each crate a door.
Crypto, Key, Storage, Frost — I lined them in a row,
Network and Policy now know where errors go.
A joyful nibble, carrot cheer — tidy logs and fewer woe!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor error types into domain-specific hierarchy' directly and clearly describes the main objective of the pull request—restructuring the KeepError enum into multiple domain-specific error types.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
keep-core/src/error.rs (1)

119-160: FrostError code E4013 breaks sequential ordering.

The Transport variant uses error code [E4013] but is placed between InvalidNetwork ([E4003]) and SessionNotFound ([E4004]). This breaks the sequential pattern and could cause confusion during debugging.

Consider either:

  1. Renumbering to maintain sequential order (e.g., E4004 for Transport, then shift others)
  2. Or moving the Transport variant to the end of the enum to match its code position
♻️ Suggested reordering
 #[derive(Error, Debug)]
 #[non_exhaustive]
 pub enum FrostError {
     #[error("[E4001] FROST protocol error: {0}")]
     Protocol(String),

     #[error("[E4002] Invalid configuration: {0}")]
     InvalidConfig(String),

     #[error("[E4003] Invalid network: {0}")]
     InvalidNetwork(String),

-    #[error("[E4013] FROST transport error: {0}")]
-    Transport(String),
-
     #[error("[E4004] Session not found: {0}")]
     SessionNotFound(String),
     // ... other variants ...

     #[error("[E4012] Share info unavailable: {0}")]
     ShareInfoUnavailable(String),
+
+    #[error("[E4013] FROST transport error: {0}")]
+    Transport(String),
 }
keep-cli/src/commands/frost_network.rs (2)

55-57: Consider using granular error mapping for FrostNetError.

keep-cli/src/commands/serve.rs introduces map_frost_net_error to distinguish transport/connectivity errors from protocol errors. Here, all KfpNode::new() errors are collapsed into FrostError::Protocol, which loses context about whether the failure was a connection issue vs. a protocol error.

Consider extracting map_frost_net_error to a shared location (e.g., a common module or keep_core) and using it here for consistency.

♻️ Example usage with shared helper
+use crate::commands::serve::map_frost_net_error; // or shared module
+
         let node = keep_frost_net::KfpNode::new(share, vec![relay.to_string()])
             .await
-            .map_err(|e| FrostError::Protocol(e.to_string()))?;
+            .map_err(map_frost_net_error)?;

224-230: Consider extracting repeated threshold validation logic.

The threshold validation pattern (if threshold < 2 || threshold > participants) appears multiple times across cmd_frost_network_sign (line 224), cmd_frost_network_dkg (line 705), and cmd_frost_network_group_create (line 1050).

A helper function would reduce duplication and ensure consistent error messages:

♻️ Suggested helper
fn validate_threshold(threshold: u16, participants: u16) -> Result<()> {
    if threshold < 2 || threshold > participants {
        return Err(FrostError::InvalidConfig(format!(
            "Invalid threshold: must be 2 <= threshold ({}) <= participants ({})",
            threshold, participants
        )).into());
    }
    Ok(())
}

Also applies to: 705-711

keep-cli/src/commands/serve.rs (1)

19-64: Good implementation addressing previous review feedback.

This helper addresses the prior review concern about conflating transport and protocol errors. The granular mapping distinguishes connectivity issues (Transport, Timeout) from protocol-level failures.

One minor concern: the cast from usize to u16 on lines 28-29 could silently truncate large values. While peer counts should practically be small, consider using try_into().unwrap_or(u16::MAX) for safety:

♻️ Safer casting
         FrostNetError::InsufficientPeers { needed, available } => FrostError::InsufficientSigners {
-            needed: needed as u16,
-            available: available as u16,
+            needed: needed.try_into().unwrap_or(u16::MAX),
+            available: available.try_into().unwrap_or(u16::MAX),
         },

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7ee4f7 and adf1dd8.

📒 Files selected for processing (4)
  • keep-cli/src/commands/frost_network.rs
  • keep-cli/src/commands/serve.rs
  • keep-cli/src/server.rs
  • keep-core/src/error.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • keep-cli/src/server.rs
🧰 Additional context used
🧬 Code graph analysis (2)
keep-cli/src/commands/frost_network.rs (3)
keep-core/src/frost/dealer.rs (1)
  • new (18-35)
keep-cli/src/commands/frost.rs (1)
  • check_warden_policy (253-333)
keep-core/src/error.rs (7)
  • from (277-279)
  • from (283-285)
  • from (289-291)
  • from (295-297)
  • from (301-303)
  • from (307-309)
  • from (313-315)
keep-cli/src/commands/serve.rs (2)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-core/src/lib.rs (1)
  • data_key (375-377)
🔇 Additional comments (9)
keep-core/src/error.rs (2)

224-274: LGTM! Well-structured error hierarchy.

The composite KeepError with #[error(transparent)] for domain errors provides clean error propagation while maintaining granular error types. The additional variants (Runtime, Input, Hardware*, etc.) appropriately cover cross-cutting concerns that don't fit neatly into a single domain.


276-316: LGTM! Consistent From implementations for external error types.

The From implementations correctly funnel redb errors, io::Error, and bincode::Error through the appropriate domain error types before wrapping in KeepError. This maintains the hierarchical structure while providing convenient conversions.

keep-cli/src/commands/frost_network.rs (4)

209-217: LGTM! Consistent hardware error handling.

The distinction between HardwareConnection for initial connection failures and HardwareOperation { op, reason } for subsequent operation failures provides good diagnostic granularity.


440-549: LGTM! Explicit error conversions in async blocks.

The use of KeepError::from(NetworkError::...) rather than .into() in async blocks aligns with the PR objective to "use explicit KeepError::from() for async block error conversion" and improves clarity.


539-544: LGTM! Clear timeout error handling.

The FrostError::Timeout variant with descriptive messages ("peer commitments", "peer packages", "peer shares") provides good context for debugging timeout failures.


252-257: LGTM! Appropriate use of NotImplemented for feature gates.

Using KeepError::NotImplemented with static string messages correctly signals unimplemented functionality with compile-time string references, avoiding unnecessary allocations.

Also applies to: 680-685

keep-cli/src/commands/serve.rs (3)

290-290: LGTM! Correct use of KeyError::Locked for missing data key.

Using KeyError::Locked when data_key() returns None is semantically accurate—the storage is unlocked when the data key is available. The error will propagate correctly through KeepError::Key(KeyError::Locked) via the From implementation.

Also applies to: 388-388


108-114: LGTM! Proper use of map_frost_net_error for FROST operations.

The map_frost_net_error helper is correctly applied to both KfpNode::new() and node.announce(), ensuring granular error mapping for FROST network operations.


105-106: LGTM! Consistent runtime error mapping.

KeepError::Runtime is appropriately used for tokio runtime initialization failures and TUI errors, which are infrastructure-level concerns that don't fit into domain-specific error categories.

Also applies to: 168-169

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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 and usage tips.

@wksantiago wksantiago requested a review from kwsantiago January 13, 2026 22:27
@wksantiago wksantiago self-assigned this Jan 13, 2026
@wksantiago wksantiago linked an issue Jan 13, 2026 that may be closed by this pull request
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (4)
keep-cli/src/commands/bitcoin.rs (1)

213-224: Use KeepError::Input instead of FrostError::InvalidNetwork for Bitcoin network validation.

FrostError is semantically tied to the FROST threshold signature protocol (error codes E4xxx). Using FrostError::InvalidNetwork for CLI input validation conflates two unrelated concerns and produces misleading error messages. The codebase provides KeepError::Input for exactly this purpose—validating user-provided arguments. Replace the error with:

_ => Err(KeepError::Input(format!(
    "invalid Bitcoin network '{}' (valid: mainnet, testnet, signet, regtest)",
    s
)).into()),
keep-core/src/hidden/header.rs (1)

1-307: Run cargo fmt to fix formatting issues.

The CI pipeline indicates formatting differences in this file. Run cargo fmt before merging.

keep-core/src/frost/share.rs (1)

1-134: Run cargo fmt to fix formatting issues.

The CI pipeline indicates formatting differences in this file.

keep-core/src/frost/transport.rs (1)

1-391: Run cargo fmt to fix formatting issues.

The CI pipeline indicates formatting differences in this file.

🤖 Fix all issues with AI agents
In @keep-core/src/frost/dealer.rs:
- Around line 155-163: Formatting in the match arm that constructs the Err uses
a line that exceeds the formatter width; run cargo fmt to reflow the long return
Err(...) line (or manually break the long FrostError::Protocol(...) call into
multiple lines) so the match arm fits within the line length limit—look for the
match on bytes.len() and the usage of pubkey and FrostError::Protocol to locate
and reformat the block.

In @keep-core/src/frost/signing.rs:
- Around line 184-199: The function signature_bytes in signing.rs is
misformatted; run rustfmt to apply canonical formatting (e.g., run `cargo fmt`)
so the method's let-else, chaining and block indentation match project style;
ensure the file is saved after formatting and commit the changes referencing
signature_bytes for reviewers.

In @keep-core/src/hidden/header.rs:
- Around line 108-110: Change the StorageError::UnsupportedVersion variant to
take a u16 instead of u8 in the StorageError enum (update its definition in
error.rs to UnsupportedVersion(u16)), then update both call sites that currently
cast the parsed version with as u8 (in the header handling logic and the storage
validation logic) to pass the raw u16 value instead; ensure any pattern matches
or error constructions (e.g., StorageError::UnsupportedVersion(...)) and tests
are updated to expect a u16 payload.

In @keep-core/src/hidden/volume.rs:
- Around line 285-286: The CI failure is due to formatting of the multi-line
matches! patterns (e.g., the expression matching
KeepError::Crypto(CryptoError::InvalidPassword) |
KeepError::Crypto(CryptoError::DecryptionFailed)) in
keep-core/src/hidden/volume.rs (also similar occurrences around the ranges
370-371 and 401-402); run cargo fmt to auto-fix and ensure each matches! arm is
formatted per rustfmt (or manually reformat the multi-line matches! into a
rustfmt-friendly layout) so the patterns and vertical bar align correctly and
pass cargo fmt --check.
🧹 Nitpick comments (9)
keep-core/src/error.rs (1)

273-301: Consider preserving structured error info for redb errors.

All redb error types are converted to StorageError::Database(String) via .to_string(). This loses the ability to programmatically distinguish between DatabaseError, TransactionError, TableError, StorageError, and CommitError at the StorageError level.

If downstream code needs to handle these differently (e.g., retry on transient transaction errors vs. fail on corruption), consider using separate variants or an enum within Database.

keep-cli/src/signer/handler.rs (3)

200-206: Redundant .into() on KeepError variant.

KeepError::EventIdFailed is already the expected error type for Result<_, KeepError>, so the .into() call is unnecessary.

Suggested fix
             let event_id = frost_event
                 .id
-                .ok_or_else(|| KeepError::EventIdFailed.into())?;
+                .ok_or(KeepError::EventIdFailed)?;

227-232: Same redundant .into() pattern in local FROST path.

Same issue as above—KeepError::EventIdFailed and KeepError::InvalidSignature don't need .into().

Suggested fix
             let event_id = frost_event
                 .id
-                .ok_or_else(|| KeepError::EventIdFailed.into())?;
+                .ok_or(KeepError::EventIdFailed)?;
             let sig_bytes = signer.sign(event_id.as_bytes())?;
             let sig = nostr_sdk::secp256k1::schnorr::Signature::from_slice(&sig_bytes)
-                .map_err(|e| KeepError::InvalidSignature(e.to_string()))?;
+                .map_err(|e| KeepError::InvalidSignature(e.to_string()))?;

253-257: FrostError::SigningFailed used for non-FROST signing path.

This code path handles regular Nostr keypair signing (non-FROST), but the error is mapped to FrostError::SigningFailed. This is semantically misleading—consider using a more general error variant like KeepError::InvalidSignature or adding a CryptoError::SigningFailed variant.

Suggested fix
             unsigned_event
                 .sign(&keys)
                 .await
-                .map_err(|e| FrostError::SigningFailed(e.to_string()))?
+                .map_err(|e| KeepError::InvalidSignature(e.to_string()))?
keep-cli/src/signer/frost_signer.rs (1)

24-26: Consider InvalidConfig for "no shares provided" error.

"No shares provided" is a configuration/setup issue rather than a protocol error. FrostError::Protocol typically indicates runtime protocol violations. Using FrostError::InvalidConfig("No shares provided") would be more semantically consistent with lines 40-47.

Suggested fix
         if shares.is_empty() {
-            return Err(FrostError::Protocol("No shares provided".to_string()).into());
+            return Err(FrostError::InvalidConfig("No shares provided".to_string()).into());
         }
keep-core/src/hidden/header.rs (1)

258-261: Inconsistent error wrapping pattern.

This explicitly wraps StorageError in KeepError::Storage(...), while all other error sites in this file use .into() for the conversion. This inconsistency could confuse future maintainers.

Suggested fix for consistency
         if bytes.len() < Self::COMPACT_SIZE {
-            return Err(KeepError::Storage(StorageError::HeaderSizeMismatch {
+            return Err(StorageError::HeaderSizeMismatch {
                 expected: Self::COMPACT_SIZE,
                 actual: bytes.len(),
-            }));
+            }.into());
         }
keep-core/src/frost/transport.rs (2)

115-120: Minor inconsistency in error conversion pattern.

Line 117 relies on ? to convert FrostError while line 119 explicitly calls .into() inside map_err. Both work but the pattern is inconsistent.

Align the patterns
     pub fn to_bech32(&self) -> Result<String> {
         let json = self.to_json()?;
         let hrp = Hrp::parse(SHARE_HRP).map_err(|e| FrostError::Protocol(format!("Invalid HRP: {e}")))?;
         bech32::encode::<Bech32m>(hrp, json.as_bytes())
-            .map_err(|e| FrostError::Protocol(format!("Bech32 encoding failed: {e}")).into())
+            .map_err(|e| FrostError::Protocol(format!("Bech32 encoding failed: {e}")))
     }

202-216: Inconsistent error conversion in payload/session parsing.

Line 204 uses .into() inside map_err while line 209 doesn't, despite both being hex decode errors in the same struct.

Align the patterns
     pub fn payload_bytes(&self) -> Result<Vec<u8>> {
         hex::decode(&self.payload)
-            .map_err(|_| FrostError::Protocol("Invalid payload hex".into()).into())
+            .map_err(|_| FrostError::Protocol("Invalid payload hex".into()))
     }
keep-cli/src/commands/frost.rs (1)

284-287: Consider using a dedicated PolicyError variant for Warden policy denials.

The PR introduces PolicyError in the error hierarchy, but these Warden policy-related errors still use KeepError::Runtime. For consistency with the domain-specific error taxonomy, consider using PolicyError variants here.

Also applies to: 310-310, 324-324

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9b5abd and 2861e06.

📒 Files selected for processing (26)
  • keep-cli/src/commands/agent.rs
  • keep-cli/src/commands/bitcoin.rs
  • keep-cli/src/commands/enclave.rs
  • keep-cli/src/commands/frost.rs
  • keep-cli/src/commands/frost_hardware.rs
  • keep-cli/src/commands/frost_network.rs
  • keep-cli/src/commands/mod.rs
  • keep-cli/src/commands/serve.rs
  • keep-cli/src/commands/vault.rs
  • keep-cli/src/server.rs
  • keep-cli/src/signer/frost_signer.rs
  • keep-cli/src/signer/handler.rs
  • keep-cli/src/warden.rs
  • keep-core/src/crypto.rs
  • keep-core/src/error.rs
  • keep-core/src/frost/coordinator.rs
  • keep-core/src/frost/dealer.rs
  • keep-core/src/frost/share.rs
  • keep-core/src/frost/signing.rs
  • keep-core/src/frost/transport.rs
  • keep-core/src/hidden/header.rs
  • keep-core/src/hidden/volume.rs
  • keep-core/src/keyring.rs
  • keep-core/src/keys.rs
  • keep-core/src/lib.rs
  • keep-core/src/storage.rs
🧰 Additional context used
🧬 Code graph analysis (10)
keep-core/src/frost/coordinator.rs (2)
keep-cli/src/signer/frost_signer.rs (4)
  • new (19-57)
  • new (80-85)
  • sign (63-70)
  • sign (95-100)
keep-core/src/frost/dealer.rs (2)
  • new (18-33)
  • new (64-66)
keep-cli/src/commands/frost_hardware.rs (2)
keep-core/src/frost/share.rs (1)
  • pubkey_package (90-93)
keep-core/src/frost/transport.rs (1)
  • session_id_bytes (207-216)
keep-cli/src/signer/handler.rs (1)
keep-core/src/crypto.rs (8)
  • from_slice (242-253)
  • new (30-46)
  • new (101-115)
  • new (159-166)
  • new (218-225)
  • encrypt (335-348)
  • decrypt (233-240)
  • decrypt (350-360)
keep-cli/src/commands/serve.rs (3)
keep-cli/src/signer/frost_signer.rs (2)
  • new (19-57)
  • new (80-85)
keep-core/src/lib.rs (1)
  • data_key (365-367)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-core/src/frost/transport.rs (1)
keep-core/src/frost/share.rs (2)
  • decrypt (123-133)
  • key_package (85-88)
keep-cli/src/commands/vault.rs (2)
keep-core/src/lib.rs (1)
  • data_key (365-367)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-core/src/lib.rs (1)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-core/src/frost/dealer.rs (1)
keep-core/src/frost/share.rs (1)
  • key_package (85-88)
keep-cli/src/commands/frost.rs (2)
keep-cli/src/signer/frost_signer.rs (2)
  • new (19-57)
  • new (80-85)
keep-core/src/frost/transport.rs (2)
  • from_json (110-113)
  • from_json (197-200)
keep-core/src/frost/signing.rs (4)
keep-cli/src/signer/frost_signer.rs (4)
  • new (19-57)
  • new (80-85)
  • sign (63-70)
  • sign (95-100)
keep-core/src/frost/coordinator.rs (2)
  • new (35-45)
  • aggregate (156-171)
keep-core/src/frost/dealer.rs (1)
  • new (18-33)
keep-core/src/frost/share.rs (1)
  • key_package (85-88)
🪛 GitHub Actions: CI
keep-core/src/hidden/header.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in hidden/header.rs. Run 'cargo fmt' to fix code style issues.

keep-core/src/frost/share.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in frost/share.rs. Run 'cargo fmt' to fix code style issues.

keep-core/src/frost/transport.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in frost/transport.rs. Run 'cargo fmt' to fix code style issues.

keep-core/src/frost/dealer.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in frost/dealer.rs. Run 'cargo fmt' to fix code style issues.

keep-core/src/frost/signing.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in frost/signing.rs. Run 'cargo fmt' to fix code style issues.

keep-core/src/hidden/volume.rs

[error] 1-999: cargo fmt --check failed. Formatting differences detected in hidden/volume.rs. Run 'cargo fmt' to fix code style issues.

🔇 Additional comments (153)
keep-core/src/error.rs (7)

5-46: Well-structured StorageError with clear error codes.

The StorageError enum is well-designed with unique error codes (E1xxx series) and appropriate variants for storage-related failures. The #[non_exhaustive] attribute is a good choice for future extensibility.


48-86: CryptoError covers the crypto domain comprehensively.

Good coverage of cryptographic failure modes with descriptive error codes (E2xxx series). The variants align well with the usage patterns in keep-core/src/crypto.rs.


88-117: KeyError variants appropriately scoped for key management.

The E3xxx series covers key lifecycle operations. The distinction between NotFound, AlreadyExists, and format errors (InvalidNsec, InvalidNpub) is clear.


119-157: FrostError provides granular FROST protocol error handling.

The E4xxx series covers DKG, signing, session management, and threshold validation. The InsufficientSigners variant with needed/available fields provides actionable debugging info.


159-194: NetworkError and PolicyError complete the domain coverage.

NetworkError (E5xxx) and PolicyError (E6xxx) appropriately separate network transport concerns from policy/authorization concerns.


221-271: KeepError as composite wrapper is well-structured.

The use of #[error(transparent)] for domain-specific errors allows clean error propagation while maintaining the ability to match on specific domains. The E9xxx series for miscellaneous runtime errors is appropriate.


38-39: Potential conflicting From<std::io::Error> implementations.

StorageError::Io uses #[from] std::io::Error (line 39), and there's also a manual From<std::io::Error> for KeepError (lines 303-307). While this compiles because they target different types, it creates two conversion paths:

  1. std::io::ErrorStorageError::Io (via #[from])
  2. std::io::ErrorKeepError::Storage(StorageError::Io(_)) (via manual impl)

This is intentional and correct—the manual impl enables ? to convert directly to KeepError in functions returning Result<T, KeepError>. Just ensure this dual-path behavior is understood by contributors.

Also applies to: 303-307

keep-cli/src/signer/handler.rs (4)

10-10: Appropriate error type imports for the handler.

The expanded import correctly pulls in all domain-specific error types needed for granular error handling in the signer pathways.


77-91: Error mapping in handle_connect is semantically correct.

The error types appropriately distinguish between FROST pubkey issues (FrostError::InvalidFrostPubkey), key availability (KeyError::NoSigningKey), key format (KeyError::InvalidPubkey), and key mismatch (KeyError::PubkeyMismatch).


288-318: NIP-44 encrypt handler uses consistent error taxonomy.

The error mapping for permission denial (PolicyError), missing key (KeyError::NoSigningKey), invalid secret (CryptoError::InvalidSecretKey), and NIP-44 operation failure (KeepError::Nip44) is appropriate.


354-385: NIP-04 handlers mirror NIP-44 pattern correctly.

Consistent error handling between NIP-04 and NIP-44 paths.

keep-core/src/crypto.rs (6)

13-13: Clean import of domain-specific error type.

Importing CryptoError directly allows the module to construct specific variants without prefixing.


159-173: SecretVec RAM encryption/decryption errors are appropriately specific.

Using CryptoError::RamEncryptionFailed and CryptoError::RamDecryptionFailed clearly indicates the failure occurred in the memory-security layer rather than general cryptographic operations.


218-248: SecretKey error handling covers all failure modes.

  • RamEncryptionFailed for key creation
  • RamDecryptionFailed for key access
  • InvalidKeyLength with expected/actual for slice construction

This provides actionable diagnostics for debugging key-related issues.


268-289: Key derivation errors preserve underlying cause.

CryptoError::KeyDerivation(e.to_string()) captures the Argon2 error details, which is helpful for diagnosing parameter or memory issues during password hashing.


317-332: EncryptedData::from_bytes validates minimum length.

CryptoError::DataTooShort { min } clearly communicates the validation failure. This is a good defensive check against truncated or malformed ciphertext.


335-360: Encrypt/decrypt functions use clear error variants.

  • CryptoError::Encryption("cipher failed") for encryption failures
  • CryptoError::DecryptionFailed for decryption failures (no string since it's typically wrong key or corruption)

This distinction is appropriate—encryption failures are rare and indicate implementation issues, while decryption failures are common (wrong password).

keep-cli/src/commands/mod.rs (2)

16-26: Input errors correctly classified as KeepError::Input.

Using KeepError::Input for password read failures is semantically appropriate—these are user input/interaction errors, not runtime or I/O errors.


28-50: Consistent error handling across input functions.

All three input functions (get_password, get_password_with_confirm, get_confirm) now use the same KeepError::Input pattern with descriptive messages.

keep-cli/src/signer/frost_signer.rs (4)

6-6: Focused import of FrostError for the FROST signer module.

Importing only FrostError (rather than all error types) reflects this module's single-domain focus.


24-36: FrostSigner validation uses appropriate error variants.

  • FrostError::Protocol for no shares (though see note below)
  • FrostError::InsufficientSigners with structured needed/available fields

The structured InsufficientSigners variant provides actionable diagnostics.


38-49: Config validation errors are correctly classified.

FrostError::InvalidConfig is appropriate for share/threshold mismatches detected during signer construction.


95-100: NetworkFrostSigner signing error correctly maps to FrostError.

Unlike the non-FROST path in handler.rs, this is genuinely FROST-based signing via KfpNode, so FrostError::SigningFailed is the correct error type.

keep-cli/src/commands/agent.rs (5)

7-7: LGTM!

Import correctly adds KeyError to support the new domain-specific error hierarchy.


21-24: LGTM!

Using KeepError::NotImplemented with a static string for unsupported MCP in hidden mode is appropriate for this feature gate.


35-38: LGTM!

Replacing the generic key-not-found error with KeyError::NotFound aligns with the domain-specific error hierarchy and provides better error classification.


50-62: LGTM!

The runtime and session creation errors are appropriately mapped to KeepError::Runtime with descriptive messages. The error handling flow is correct.


72-84: LGTM!

I/O errors (read, write, flush) are consistently mapped to KeepError::Runtime, which is appropriate for operational failures in the MCP command flow.

keep-core/src/keys.rs (5)

8-8: LGTM!

Import correctly adds CryptoError and KeyError for granular error classification in key operations.


34-36: LGTM!

CryptoError::InvalidSecretKey is the appropriate error type for SigningKey::from_bytes failures, clearly indicating a cryptographic validation issue.


45-54: LGTM!

The from_nsec function now uses KeyError::InvalidNsec consistently for all validation failures (decode, HRP mismatch, length mismatch). This provides uniform error handling for malformed nsec inputs.


83-87: LGTM!

Using CryptoError::InvalidSigningKey for signing failures correctly categorizes this as a cryptographic error distinct from key format errors.


148-157: LGTM!

The npub_to_bytes function mirrors the validation pattern in from_nsec, using KeyError::InvalidNpub consistently for all npub validation failures.

keep-cli/src/commands/bitcoin.rs (7)

6-6: LGTM!

Import correctly adds domain-specific error types (FrostError, KeyError) for granular error classification.


27-36: LGTM!

Error handling for key lookup and signer creation is appropriate: KeyError::NotFound for missing keys and KeepError::Runtime for signer initialization failures.


46-49: LGTM!

KeepError::Runtime is appropriate for address derivation failures from the Bitcoin signer.


70-85: LGTM!

Consistent use of KeyError::NotFound for key lookup and KeepError::Runtime for signer and descriptor operations.


98-100: LGTM!

KeepError::Runtime is appropriate for internal descriptor generation failures.


121-150: LGTM!

The PSBT signing flow correctly uses KeyError::NotFound for key lookup and KeepError::Runtime for file I/O, PSBT parsing, signing, and output writing operations.


164-178: LGTM!

The analyze command correctly maps file I/O, PSBT parsing, signer creation, and analysis errors to KeepError::Runtime.

keep-cli/src/commands/enclave.rs (8)

6-6: LGTM!

Import correctly adds KeyError for key-related error handling.


107-116: LGTM!

Enclave attestation and PCR parsing errors are correctly mapped to KeepError::Runtime for operational failures.


166-171: LGTM!

Using KeepError::Runtime for invalid pubkey length from enclave response is appropriate—this is an operational/protocol error rather than user input error.


193-203: LGTM!

Consistent use of KeepError::Runtime for key generation and pubkey length validation errors from the enclave.


231-232: LGTM!

Using KeepError::Input for invalid message hex is semantically correct—this is a user input validation error.


280-282: LGTM!

KeepError::Runtime is appropriate for enclave signing operation failures.


320-334: LGTM!

Error handling distinguishes appropriately between:

  • KeyError::NotFound for missing vault key
  • KeepError::Runtime for stdin I/O errors
  • KeepError::Input for invalid hex format

This classification aids debugging by indicating whether the error is a missing resource, operational failure, or invalid input.


376-378: LGTM!

KeepError::Runtime is appropriate for enclave key import failures.

keep-cli/src/warden.rs (7)

8-8: LGTM!

Import correctly adds PolicyError for policy/Warden-related error handling.


88-99: LGTM!

Using PolicyError::HttpClient for reqwest client builder failures is appropriate—this clearly indicates a client setup issue rather than a request-time failure.


110-128: LGTM!

Error handling correctly distinguishes between:

  • PolicyError::WardenRequest for HTTP send/status failures
  • PolicyError::InvalidResponse for JSON deserialization failures

The error messages include relevant context (status code, body text).


199-201: LGTM!

PolicyError::ApprovalTimeout clearly signals a local timeout waiting for approval, distinct from server-side workflow timeouts.


214-228: Double .into() call creates unnecessary indirection.

On lines 215-216 and 224-228, PolicyError::WardenRequest(...) is constructed and then .into() is called. However, the map_err closure already returns a type that should convert properly. The pattern PolicyError::...(...).into() suggests converting PolicyError to another error type (likely KeepError), but wrapped in a closure that itself returns a Result<_, PolicyError>.

Looking at line 214-216:

let response = req.send().await.map_err(|e| {
    PolicyError::WardenRequest(format!("Workflow status check: {}", e)).into()
})?;

The .into() here converts to KeepError. This is fine and consistent with other usages in the file.


236-238: LGTM!

PolicyError::InvalidResponse for status JSON parsing failure is consistent with the evaluate method's error handling.


240-265: LGTM!

The PolicyError::WorkflowFailed variant with status and details fields provides excellent debugging context for terminal workflow states. The match arms correctly handle:

  • APPROVED / REJECTED: Return success with boolean
  • TIMED_OUT / CANCELLED: Return error with specific status
  • PENDING: Continue polling
  • Unknown status: Return error with the actual status value

This comprehensive handling prevents silent failures from unexpected server states.

keep-core/src/frost/coordinator.rs (2)

14-14: LGTM!

The import change from KeepError to FrostError aligns with the PR's domain-specific error hierarchy.


51-187: Error handling migration looks correct.

The consistent use of FrostError::Protocol for all FROST protocol-related failures maintains clear domain separation. The error messages are descriptive and include relevant context (e.g., threshold counts, underlying error details).

keep-core/src/frost/share.rs (2)

70-77: LGTM!

The error handling for key package serialization is appropriate. The FrostError::Protocol variant correctly categorizes these as protocol-level failures.


85-93: The suggested fix is incomplete and would not compile.

Lines 72 and 76 use .map_err(...)? (with ? operator after), while lines 87 and 92 use .map_err(...).into() (explicit conversion inside). Both patterns are valid:

  • Pattern A (lines 72, 76): Error wrapped in map_err, then converted by ?
  • Pattern B (lines 87, 92): Error wrapped and converted in one step with .into()

The proposed diff removes only the .into() without adding ?, which would return Result<T, FrostError> instead of the required Result<T, KeepError>, causing a type mismatch. To align with pattern A, you'd need to add ? at the end of the chain:

     pub fn key_package(&self) -> Result<KeyPackage> {
         KeyPackage::deserialize(&self.key_package_bytes)
-            .map_err(|e| FrostError::Protocol(format!("Failed to deserialize key package: {e}")).into())
+            .map_err(|e| FrostError::Protocol(format!("Failed to deserialize key package: {e}")))?
     }
 
     pub fn pubkey_package(&self) -> Result<PublicKeyPackage> {
         PublicKeyPackage::deserialize(&self.pubkey_package_bytes)
-            .map_err(|e| FrostError::Protocol(format!("Failed to deserialize pubkey package: {e}")).into())
+            .map_err(|e| FrostError::Protocol(format!("Failed to deserialize pubkey package: {e}")))?
     }

Likely an incorrect or invalid review comment.

keep-cli/src/commands/frost_hardware.rs (3)

6-6: LGTM!

The import correctly brings in both FrostError and KeepError variants, enabling appropriate domain-specific error handling for this module that bridges hardware operations with FROST protocol logic.


21-29: Good separation of connection vs operation errors.

The distinction between HardwareConnection for initial connection failures and HardwareOperation with operation context for subsequent failures provides clear error categorization for debugging.


113-131: Appropriate use of FrostError for Frost-specific serialization.

The serialization of Frost key material (secret share, verifying share, group key) correctly uses FrostError::Protocol since these are FROST protocol-level operations, distinct from the hardware communication layer.

keep-core/src/frost/transport.rs (2)

7-7: LGTM!

The import correctly brings in FrostError for this transport layer that handles FROST share serialization and messaging.


238-275: LGTM!

The animated frame parsing has good error handling with descriptive messages for each failure mode (empty frames, missing index, missing data, invalid hex, invalid UTF-8).

keep-core/src/keyring.rs (5)

7-7: LGTM!

Import statement correctly includes KeyError to support the new error hierarchy.


61-67: LGTM!

Error handling in load_key correctly wraps KeyError::KeyringFull and KeyError::AlreadyExists within KeepError::Key(), maintaining consistency with the new error hierarchy.


103-109: LGTM!

Error handling in set_primary correctly returns KeepError::Key(KeyError::NotFound(...)) when the key doesn't exist.


111-121: LGTM!

Error handling in remove is consistent with set_primary, using the same wrapped KeyError::NotFound variant.


188-191: LGTM!

Test assertion correctly updated to match the new nested error variant pattern KeepError::Key(KeyError::KeyringFull(_)).

keep-cli/src/server.rs (6)

10-10: LGTM!

Import statement correctly includes NetworkError and KeyError for the refined error handling.


58-62: LGTM!

Relay connection errors are correctly mapped to NetworkError::RelayAdd, providing clear context for debugging connection issues.


147-151: LGTM!

Subscription failures are appropriately mapped to NetworkError::Subscribe.


193-196: LGTM!

Runtime notification handler errors are correctly mapped to KeepError::Runtime with descriptive context.


210-244: LGTM!

NIP-46 event handling errors are well-categorized:

  • Decryption → NetworkError::Decrypt
  • JSON parsing → NetworkError::Parse
  • Encryption → NetworkError::Encrypt
  • Event signing → NetworkError::SignRequest

This provides granular error classification for debugging protocol issues.


42-54: Error conversions are handled correctly by thiserror.

The KeepError enum in keep-core/src/error.rs uses the #[from] attribute on its KeyError (line 231) and NetworkError (line 237) variants. The thiserror crate automatically generates From trait implementations for these, enabling the implicit conversion via the ? operator. No explicit wrapping is required.

Likely an incorrect or invalid review comment.

keep-core/src/frost/dealer.rs (4)

7-7: LGTM!

Import correctly updated to use FrostError for domain-specific error handling.


18-33: LGTM!

Configuration validation errors appropriately use FrostError::InvalidConfig with descriptive messages for threshold constraints.


68-100: LGTM!

Key generation and KeyPackage conversion errors are consistently mapped to FrostError::Protocol, aligning with the pattern in share.rs (per the relevant code snippet at lines 84-87).


103-144: LGTM!

Key splitting logic mirrors the generation path with consistent FrostError::Protocol error mapping for signing key deserialization and split operation failures.

keep-cli/src/commands/vault.rs (14)

8-8: LGTM!

Import correctly updated to include KeyError for key-related error handling.


33-37: LGTM!

Input validation error for password length correctly uses KeepError::Input instead of a generic error variant.


48-51: LGTM!

Password read failure appropriately mapped to KeepError::Runtime with descriptive context.


53-63: LGTM!

Hidden password validation errors consistently use KeepError::Input for user input issues.


149-149: LGTM!

Data key access correctly returns KeyError::Locked when the vault is not unlocked, converted via .into().


188-188: LGTM!

Consistent with cmd_generate_outer - uses KeyError::Locked for locked vault state.


263-263: LGTM!

Import operation correctly checks for locked state using KeyError::Locked.


304-304: LGTM!

Consistent locked state handling in hidden import path.


475-479: LGTM!

Key lookup failures correctly return KeyError::NotFound with the key name for debugging.


509-515: LGTM!

Export from outer volume correctly handles both not-found and locked error cases with appropriate KeyError variants.


552-558: LGTM!

Hidden export path mirrors outer volume handling with consistent error types.


597-601: LGTM!

Delete operation correctly returns KeyError::NotFound when key doesn't exist.


631-636: LGTM!

Outer volume delete path uses consistent KeyError::NotFound handling.


666-671: LGTM!

Hidden volume delete path maintains consistency with other delete operations.

keep-core/src/storage.rs (14)

10-10: LGTM!

Import correctly updated to include CryptoError, KeyError, and StorageError for comprehensive error handling.


79-86: LGTM!

Header validation errors appropriately use StorageError::InvalidMagic and StorageError::UnsupportedVersion for clear error classification.


122-125: LGTM!

Storage creation correctly returns StorageError::AlreadyExists with path context when the vault already exists.


167-181: LGTM!

Storage open validates both path existence (StorageError::NotFound) and header size (StorageError::HeaderSizeMismatch) with appropriate error variants and context.


200-228: LGTM!

Unlock method correctly:

  1. Returns CryptoError::RateLimited with remaining seconds when rate-limited
  2. Checks for CryptoError::DecryptionFailed to record failed attempts
  3. Propagates the original error for proper error classification

256-274: LGTM!

store_key correctly uses explicit KeepError::Key(KeyError::Locked) for locked state checks on both data_key and db access.


276-293: LGTM!

load_key correctly returns KeyError::NotFound with hex-encoded ID when the key doesn't exist.


295-317: LGTM!

list_keys maintains consistent locked state checking pattern.


319-336: LGTM!

delete_key correctly checks for key existence and returns KeyError::NotFound if deletion fails.


342-362: LGTM!

Share storage operations follow the same locked state checking pattern as key operations.


364-386: LGTM!

list_shares maintains consistency with list_keys for locked state handling.


388-410: LGTM!

delete_share provides a descriptive error message including the share identifier when not found.


498-506: LGTM!

Test assertions correctly updated to match the new nested error variant pattern KeepError::Crypto(CryptoError::DecryptionFailed) and KeepError::Crypto(CryptoError::RateLimited(_)).


525-529: LGTM!

Rate limit reset test correctly verifies CryptoError::DecryptionFailed behavior after successful unlock.

keep-cli/src/commands/frost_network.rs (10)

7-7: LGTM - Updated imports align with new error hierarchy.

The import now includes FrostError and NetworkError alongside KeepError and Result, enabling domain-specific error handling throughout this module.


49-50: Consistent runtime error handling across all tokio runtime creations.

All Runtime::new() calls consistently map to KeepError::Runtime, which is appropriate since runtime initialization is a general infrastructure concern rather than domain-specific.

Also applies to: 131-132, 245-246, 290-291, 437-438, 760-761, 1085-1086, 1245-1246


55-57: LGTM - FROST node and protocol errors correctly mapped to FrostError::Protocol.

Network node creation and protocol-level operations appropriately use FrostError::Protocol for failures, maintaining clear domain boundaries.

Also applies to: 137-140, 143-145, 293-295, 336-339


209-217: Hardware operation errors consistently use KeepError::HardwareOperation.

Hardware signer operations (ping, frost_sign, get_share_pubkey, etc.) uniformly map errors to KeepError::HardwareOperation with operation name and reason, providing good debuggability.

Also applies to: 398-401, 611-614, 725-728, 1202-1205, 1209-1215


225-229: LGTM - Configuration validation errors use FrostError::InvalidConfig.

Threshold and participant validation errors consistently use FrostError::InvalidConfig, which is semantically appropriate for configuration-time validation failures.

Also applies to: 425-429, 704-708, 712-716, 1059-1063, 1067-1072


444-447: LGTM - Network operations correctly use NetworkError variants.

Relay add, publish, sign request, fetch, and encrypt operations appropriately map to domain-specific NetworkError variants (RelayAdd, Publish, SignRequest, Fetch, Encrypt), providing clear categorization of network-related failures.

Also applies to: 476-477, 482-483, 515-516, 521-522, 648-649, 654-655, 769-770, 791-792, 797-798, 908-908, 920-921, 925-926, 1096-1096, 1141-1141, 1146-1147, 1255-1255, 1284-1284, 1289-1290


406-408: LGTM - FROST-specific errors use appropriate FrostError variants.

Commitment failures use FrostError::CommitmentFailed, timeouts use FrostError::Timeout, and protocol violations use FrostError::Protocol. This provides clear error categorization for FROST operations.

Also applies to: 414-417, 541-541, 820-820, 950-950, 1227-1227


735-741: LGTM - DKG operations use FrostError::DkgFailed with stage context.

All DKG-related errors consistently use FrostError::DkgFailed { stage, reason }, providing clear indication of which DKG phase failed and why.

Also applies to: 745-751, 844-853, 875-881, 888-897, 1016-1022


254-256: LGTM - Not-implemented features use KeepError::NotImplemented.

Features not available (Warden without feature flag, network event signing) correctly return KeepError::NotImplemented with descriptive messages.

Also applies to: 682-682


321-326: The FrostError::InsufficientSigners variant is properly defined with both fields typed as u16. All usages across the codebase—in frost_signer.rs, frost.rs, frost_network.rs, lib.rs, and signing.rs—consistently assign values with appropriate type casting. No field type inconsistencies exist.

Likely an incorrect or invalid review comment.

keep-cli/src/commands/serve.rs (5)

10-10: LGTM - Updated imports for new error hierarchy.

The import now includes FrostError and KeyError for domain-specific error handling in FROST and key-related operations.


58-59: Consistent runtime error handling.

All Runtime::new() failures consistently map to KeepError::Runtime, maintaining uniformity across the module.

Also applies to: 123-124, 260-261, 358-359


63-65: LGTM - FROST node operations use FrostError::Protocol.

Node creation and announcement errors appropriately use FrostError::Protocol, consistent with the error hierarchy used in frost_network.rs.

Also applies to: 67-69


227-227: LGTM - TUI errors consistently mapped to KeepError::Runtime.

TUI execution failures are appropriately categorized as runtime errors since they relate to terminal/UI runtime rather than domain-specific operations.

Also applies to: 323-323, 421-421


245-245: The KeyError::Locked.into() conversion is properly supported.

The #[from] attribute on the Key variant in the KeepError enum (keep-core/src/error.rs:231) automatically generates the From<KeyError> for KeepError implementation. KeyError::Locked exists (line 107), and the conversion is valid and will compile correctly.

keep-cli/src/commands/frost.rs (8)

6-6: LGTM - Updated imports for domain-specific errors.

The import includes FrostError and KeyError for appropriate error categorization in FROST operations.


42-43: LGTM - Empty shares result uses FrostError::Protocol.

When frost_generate or frost_split returns no shares, this is correctly treated as a protocol-level error since it indicates an unexpected internal state.

Also applies to: 92-93


221-222: LGTM - Input handling errors appropriately categorized.

Read failures use KeepError::Runtime for I/O issues, and input size violations use KeepError::Input for validation errors. This distinction is semantically correct.

Also applies to: 228-231


350-351: LGTM - Input validation and feature errors appropriately categorized.

Invalid hex input uses KeepError::Input, runtime initialization uses KeepError::Runtime, and missing Warden feature uses KeepError::NotImplemented.

Also applies to: 355-356, 362-364


382-383: LGTM - Missing shares use KeyError::NotFound.

When no shares are found for a group, using KeyError::NotFound is semantically appropriate as it indicates a missing key resource.


393-398: LGTM - Insufficient signers uses FrostError::InsufficientSigners.

This correctly uses the structured error variant with needed and available fields, consistent with usage in other modules.


449-451: LGTM - Interactive signing round 1 errors use appropriate FrostError variants.

Commitment serialization uses FrostError::Protocol, read errors use KeepError::Runtime, session ID mismatches use FrostError::InvalidSessionId, and commitment deserialization uses FrostError::CommitmentFailed.

Also applies to: 477-478, 481-482, 486-487, 489-490, 498-499


522-523: LGTM - Interactive signing round 2 and aggregation errors appropriately categorized.

Read errors, session ID validation, signature share handling, and aggregation all use appropriate FrostError variants (Protocol, InvalidSessionId, SigningFailed).

Also applies to: 526-527, 531-532, 534-535, 543-544, 547-548, 550-551

keep-core/src/frost/signing.rs (6)

16-16: LGTM - Simplified imports to domain-specific types only.

The import now uses only FrostError and Result, removing the unused KeepError import since all errors in this module are FROST-specific.


84-100: LGTM - Commitment generation errors use FrostError::Protocol.

Invalid session state and duplicate commitment generation correctly return protocol-level errors, as these represent violations of the FROST protocol state machine.


102-118: LGTM - Commitment addition errors appropriately categorized.

State validation and duplicate detection correctly use FrostError::Protocol, maintaining clear protocol semantics.


124-143: Consistent error variant usage for signature share generation.

State validation uses FrostError::Protocol, missing nonces use FrostError::Protocol, and signing failures use FrostError::SigningFailed. This provides clear differentiation between protocol state errors and cryptographic operation failures.


145-178: LGTM - Signature share addition and aggregation errors.

Duplicate detection and state validation use FrostError::Protocol, and aggregation failures correctly propagate as protocol errors with the underlying cause.


206-267: LGTM - sign_with_local_shares error handling migration.

All error conditions appropriately use FrostError variants:

  • Empty shares → FrostError::Protocol
  • Insufficient signers → FrostError::InsufficientSigners with structured fields
  • Missing nonces → FrostError::Protocol
  • Signing failures → FrostError::SigningFailed
  • Aggregation/serialization failures → FrostError::Protocol
keep-core/src/lib.rs (7)

25-25: LGTM - Updated imports for domain-specific error types.

The import includes FrostError, KeyError, and StorageError for the new hierarchical error system.


78-80: LGTM - Locked state errors consistently use KeepError::Key(KeyError::Locked).

All methods requiring an unlocked state consistently check and return the wrapped KeyError::Locked variant, providing clear error categorization.

Also applies to: 110-111, 177-178, 200-201, 244-245, 256-257, 273-274, 292-293, 302-303


117-118: LGTM - Duplicate key uses KeepError::Key(KeyError::AlreadyExists).

The import_nsec method correctly returns KeyError::AlreadyExists when attempting to import a key that already exists in the keyring.


206-207: LGTM - Not found errors use KeepError::Key(KeyError::NotFound).

Missing keys, shares, and groups consistently use KeyError::NotFound with descriptive messages.

Also applies to: 234-235, 281-282, 313-314


319-324: LGTM - Insufficient signers uses KeepError::Frost(FrostError::InsufficientSigners).

The frost_sign method correctly wraps the FrostError::InsufficientSigners variant under KeepError::Frost, maintaining the hierarchical structure.


355-356: LGTM - Internal data key access returns wrapped KeyError::Locked.

The get_data_key method correctly returns KeepError::Key(KeyError::Locked) when the vault is not unlocked, with updated documentation.

Also applies to: 362-362


371-374: LGTM - Home directory error uses KeepError::Storage(StorageError::HomeNotFound).

The default_keep_path function correctly uses the storage-specific error variant for missing home directory, aligning with the domain-specific error hierarchy.

keep-core/src/hidden/volume.rs (10)

41-41: LGTM!

The import correctly brings in the new domain-specific error types required for the error hierarchy refactoring.


76-101: LGTM!

The storage-related error conversions are appropriate:

  • AlreadyExists for path collision
  • VolumeSizeOverflow for arithmetic overflow protection
  • VolumeTooSmall with named fields for clear error reporting

215-218: LGTM!

StorageError::NotFound is the correct domain-specific error for a missing vault path.


274-291: LGTM!

The rate limiting and authentication error handling correctly uses the new error hierarchy:

  • CryptoError::RateLimited for throttling
  • Pattern matching against wrapped KeepError::Crypto(...) variants for recording failures

323-333: LGTM!

Returning CryptoError::InvalidPassword for both decryption failure and checksum mismatch is correct—this maintains the documented security property that a wrong hidden password produces the same error as "no hidden volume."


354-416: LGTM!

The unlock_hidden() and unlock() methods consistently apply the new error hierarchy with proper rate limiting checks and authentication error handling.


446-451: LGTM!

KeepError::Key(KeyError::Locked) is the correct error for operations attempted on a locked volume.


618-620: LGTM!

KeyError::NotFound with the hex-encoded key ID provides clear error context for debugging while using the appropriate domain-specific error type.

Also applies to: 633-635


543-545: LGTM!

StorageError::VolumeFull correctly indicates when the hidden volume's data area is exhausted.


919-965: LGTM!

Test assertions correctly verify the new error hierarchy structure, checking for both the wrapper (KeepError::Crypto) and the specific variant (CryptoError::RateLimited, CryptoError::InvalidPassword).

Copy link

@coderabbitai coderabbitai bot left a comment

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)
keep-core/src/storage.rs (1)

75-86: Same truncation issue: version as u8.

Like in header.rs, this casts u16 to u8, potentially losing information for versions > 255. This should be consistent with whatever decision is made for header.rs.

🤖 Fix all issues with AI agents
In @keep-core/src/hidden/header.rs:
- Around line 103-110: The UnsupportedVersion error currently takes a u8 which
causes truncation when a u16 version >255 is passed; update the enum/variant
definition StorageError::UnsupportedVersion in error.rs to accept a u16 instead
of u8, then remove the casts to u8 where that variant is constructed (e.g., in
hidden::header.rs where local variable version: u16 is passed, and in
keep-core/src/storage.rs around the previous line 85) so you pass the raw u16
version value into UnsupportedVersion. Ensure all call sites and pattern matches
referencing UnsupportedVersion are updated to use u16.

In @keep-core/src/hidden/volume.rs:
- Around line 92-94: Replace the unnecessary closure using ok_or_else for the
unit variant: where required_min is computed from
DATA_START_OFFSET.checked_add(hidden_size), change the call from ok_or_else(||
StorageError::VolumeSizeOverflow) to ok_or(StorageError::VolumeSizeOverflow) so
clippy no longer flags the unnecessary closure for the unit enum variant.
🧹 Nitpick comments (2)
keep-core/src/hidden/header.rs (1)

262-268: Inconsistent error wrapping pattern.

Here KeepError::Storage(StorageError::HeaderSizeMismatch {...}) is used explicitly, while elsewhere in this file StorageError::InvalidMagic.into() is used. Consider using .into() consistently for cleaner code:

-            return Err(KeepError::Storage(StorageError::HeaderSizeMismatch {
-                expected: Self::COMPACT_SIZE,
-                actual: bytes.len(),
-            }));
+            return Err(StorageError::HeaderSizeMismatch {
+                expected: Self::COMPACT_SIZE,
+                actual: bytes.len(),
+            }
+            .into());
keep-core/src/lib.rs (1)

235-241: Consider using FrostError for missing FROST shares.

The error for a missing share currently uses KeyError::NotFound, but since this is FROST-specific functionality, FrostError might be more semantically appropriate for domain consistency. This would align better with the error taxonomy where FROST-related issues use FrostError.

That said, if shares are conceptually treated as key material throughout the codebase, KeyError could be intentional.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2861e06 and 2173575.

📒 Files selected for processing (10)
  • keep-core/src/frost/dealer.rs
  • keep-core/src/frost/share.rs
  • keep-core/src/frost/signing.rs
  • keep-core/src/frost/transport.rs
  • keep-core/src/hidden/header.rs
  • keep-core/src/hidden/volume.rs
  • keep-core/src/keyring.rs
  • keep-core/src/keys.rs
  • keep-core/src/lib.rs
  • keep-core/src/storage.rs
🧰 Additional context used
🧬 Code graph analysis (4)
keep-core/src/frost/signing.rs (1)
keep-core/src/frost/dealer.rs (2)
  • new (18-35)
  • new (66-68)
keep-core/src/frost/transport.rs (2)
keep-core/src/crypto.rs (3)
  • derive_key (268-289)
  • decrypt (233-240)
  • decrypt (350-360)
keep-core/src/frost/share.rs (2)
  • decrypt (125-135)
  • key_package (85-89)
keep-core/src/frost/share.rs (1)
keep-cli/src/server.rs (1)
  • error (425-431)
keep-core/src/lib.rs (1)
keep-core/src/storage.rs (1)
  • data_key (252-254)
🪛 GitHub Actions: CI
keep-core/src/storage.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/frost/signing.rs

[error] 29-29: value assigned to session_id is never read


[error] 31-31: value assigned to message is never read


[error] 33-33: value assigned to threshold is never read


[error] 35-35: value assigned to state is never read


[error] 38-38: value assigned to commitments is never read


[error] 40-40: value assigned to signature_shares is never read


[error] 45-45: value assigned to signature is never read


[error] 33-33: value assigned to threshold is never read


[error] 35-35: value assigned to state is never read


[error] 38-38: value assigned to commitments is never read


[error] 40-40: value assigned to signature_shares is never read


[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/hidden/header.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/keyring.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/frost/transport.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/frost/share.rs

[error] 58-58: value assigned to metadata is never read


[error] 61-61: value assigned to pubkey_package_bytes is never read


[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/lib.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/keys.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/hidden/volume.rs

[error] 92-92: unnecessary closure used to substitute value for Option::None


[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

keep-core/src/frost/dealer.rs

[error] 1-1: cargo clippy failed: could not compile keep-core due to 16 previous errors

🔇 Additional comments (57)
keep-core/src/keys.rs (5)

8-8: LGTM: Import updated for new error hierarchy.

The import now uses domain-specific CryptoError and KeyError types, aligning with the PR's goal of decomposing the monolithic KeepError.


35-36: LGTM: Appropriate error type for invalid secret key.

Mapping SigningKey::from_bytes failure to CryptoError::InvalidSecretKey is semantically correct for this cryptographic operation.


83-91: LGTM: Sign operation uses CryptoError::InvalidSigningKey.

The error mapping is appropriate since this is a cryptographic signing operation failure.


148-162: LGTM: npub_to_bytes error handling mirrors from_nsec.

Consistent use of KeyError::InvalidNpub for all npub validation failures.


45-58: LGTM: Consistent error handling in from_nsec.

All validation failures (decode error, wrong HRP, wrong length) correctly map to KeyError::InvalidNsec. The .into() conversion is valid because KeepError includes a Key(#[from] KeyError) variant in the enum definition, which automatically implements From<KeyError> for KeepError via the #[from] attribute.

keep-core/src/keyring.rs (4)

7-7: LGTM: Import updated for KeyError.

Adding KeyError to imports enables the domain-specific key-related error wrapping.


61-67: LGTM: Key operation errors properly wrapped.

KeyringFull and AlreadyExists are correctly wrapped in KeepError::Key(KeyError::...), providing a clear error hierarchy.


103-113: LGTM: set_primary and remove use consistent error wrapping.

Both methods use KeepError::Key(KeyError::NotFound(...)) for missing keys, maintaining consistency.


190-193: LGTM: Test updated to match new error structure.

The test correctly expects the wrapped error variant KeepError::Key(KeyError::KeyringFull(_)).

keep-core/src/hidden/header.rs (3)

6-6: LGTM: Import updated for StorageError.

The import enables domain-specific storage error handling for header parsing.


131-166: LGTM: Field-specific error messages for header parsing.

Using StorageError::InvalidHeader { field: "..." } provides clear diagnostics for which header field failed to parse. The try_into() conversions for fixed-size arrays are infallible given the slice lengths, but the error handling is defensive.


290-307: LGTM: Hidden header field parsing errors.

Consistent use of StorageError::InvalidHeader with specific field names for the hidden header's compact format.

keep-core/src/hidden/volume.rs (10)

41-41: LGTM: Imports updated for new error types.

Adding CryptoError, KeyError, and StorageError enables the domain-specific error handling throughout this file.


75-101: LGTM: Storage error types for volume creation validation.

AlreadyExists, VolumeSizeOverflow, and VolumeTooSmall are appropriate StorageError variants for these storage-level validation failures.


215-218: LGTM: StorageError::NotFound for missing path.

Correct error type for a file-system level "not found" condition.


269-294: LGTM: Rate limiting and authentication error handling.

The unlock_outer method correctly:

  1. Returns CryptoError::RateLimited when rate limit is exceeded
  2. Records failure only on authentication-related errors (InvalidPassword or DecryptionFailed)

326-336: LGTM: Hidden volume decryption errors.

Returning CryptoError::InvalidPassword for both decryption failure and checksum mismatch maintains plausible deniability by not revealing whether a hidden volume exists.


385-425: LGTM: Combined unlock with proper error classification.

The is_auth_error helper correctly identifies authentication failures, and the method returns CryptoError::InvalidPassword when both volumes fail authentication.


455-471: LGTM: KeyError::Locked for locked state.

Using KeepError::Key(KeyError::Locked) consistently when operations are attempted on a locked volume is semantically correct.


569-572: LGTM: StorageError::VolumeFull for capacity exceeded.

Appropriate error type when the hidden volume cannot accommodate more data.


654-656: LGTM: KeyError::NotFound for missing keys.

Consistent use of KeepError::Key(KeyError::NotFound(...)) when a key ID is not found in the storage.

Also applies to: 674-677


965-1000: LGTM: Tests updated for new error types.

The test assertions correctly use matches! with the new wrapped error variants like KeepError::Crypto(CryptoError::RateLimited(_)) and KeepError::Crypto(CryptoError::InvalidPassword).

keep-core/src/storage.rs (9)

10-10: LGTM: Imports updated for domain-specific error types.

The import enables CryptoError, KeyError, and StorageError usage throughout the storage module.


122-125: LGTM: StorageError::AlreadyExists for existing path.

Correct domain-specific error for attempting to create storage at an existing path.


167-181: LGTM: Storage validation errors.

NotFound and HeaderSizeMismatch are appropriate StorageError variants for these validation checks during open().


195-228: LGTM: Unlock flow with rate limiting and error handling.

The unlock method correctly:

  1. Checks rate limit and returns CryptoError::RateLimited
  2. Records failure only on DecryptionFailed (authentication failure)
  3. Records success on successful unlock

256-262: LGTM: KeyError::Locked for locked storage operations.

Consistently returns KeepError::Key(KeyError::Locked) when attempting operations on locked storage.


279-292: LGTM: load_key error handling.

Returns KeyError::Locked when storage is locked and KeyError::NotFound when the key ID doesn't exist.


328-342: LGTM: delete_key error handling.

Correctly checks for locked state first, then returns KeyError::NotFound if the key wasn't in the database.


403-422: LGTM: Share deletion error handling.

The delete_share method correctly uses KeyError::NotFound with a descriptive message for missing shares.


505-527: LGTM: Rate limiting tests updated.

Tests correctly assert the new error variants CryptoError::DecryptionFailed and CryptoError::RateLimited.

keep-core/src/lib.rs (5)

25-25: LGTM! Import updated for new error hierarchy.

The import correctly brings in the new domain-specific error types (FrostError, KeyError, StorageError) alongside KeepError and Result.


78-81: LGTM! Consistent error handling for locked state.

The error migration from KeepError::Locked to KeepError::Key(KeyError::Locked) is applied consistently across all methods that check the unlock state.


369-377: LGTM! Data key access error handling updated.

The get_data_key method correctly returns the new hierarchical error type, and the docstring is updated to reflect the change. The error semantics are appropriate since a missing data key indicates the Keep is locked.


380-385: LGTM! Storage error for path resolution.

The default_keep_path function correctly uses StorageError::HomeNotFound for the missing home directory case, which is semantically appropriate as a storage/filesystem concern.


326-331: LGTM! Well-structured error with explicit fields.

The FrostError::InsufficientSigners variant with needed and available fields is a good design choice. It makes the error information programmatically accessible rather than embedding it in a formatted string.

keep-core/src/frost/share.rs (4)

11-11: LGTM! Import updated for FrostError.

The import correctly switches from KeepError to FrostError for protocol-level error handling in this module.


70-76: LGTM! Consistent serialization error handling.

Both key_package and pubkey_package serialization failures correctly map to FrostError::Protocol with descriptive error messages.


85-95: LGTM! Deserialization errors properly converted.

The .into() calls correctly convert FrostError::Protocol to the crate's Result error type. This follows the error hierarchy pattern where domain-specific errors implement Into<KeepError>.


55-62: Pipeline warnings about unused fields are false positives.

Both metadata and pubkey_package_bytes are actively read in the struct's methods: metadata is accessed at lines 103, 119, and 131; pubkey_package_bytes is deserialized at line 92 and cloned at lines 121 and 133. The "never read" warnings likely originate from the compilation context or test code and do not reflect actual usage in the production code.

keep-core/src/frost/transport.rs (6)

7-7: LGTM! Import updated for FrostError.

The import correctly brings in FrostError for protocol-level error handling in the transport module.


31-33: LGTM! Serialization error handling.

The key package serialization error correctly maps to FrostError::Protocol with a descriptive message.


49-89: LGTM! Comprehensive validation with consistent error handling.

The to_share method has thorough validation for version, salt/nonce/ciphertext lengths, and group pubkey. All validation failures consistently use FrostError::Protocol with the INVALID_SHARE constant for generic cases and more specific messages where appropriate (e.g., version mismatch).


107-142: LGTM! JSON and Bech32 error handling.

The JSON serialization/deserialization and Bech32 encoding/decoding methods consistently use FrostError::Protocol with descriptive error messages that include the underlying error details.


195-219: LGTM! FrostMessage error handling.

The FrostMessage methods consistently use FrostError::Protocol for JSON and hex decoding errors. The session_id_bytes method properly validates both hex decoding and length constraints.


246-283: LGTM! Animated frames parsing with comprehensive error handling.

The from_animated_frames method properly validates each step of the parsing process with appropriate FrostError::Protocol errors for empty input, invalid JSON, missing fields, invalid hex, and UTF-8 decoding failures.

keep-core/src/frost/dealer.rs (5)

7-7: LGTM! Import updated for FrostError.

The import correctly brings in FrostError for FROST-specific error handling.


17-35: LGTM! Configuration validation with InvalidConfig variant.

The ThresholdConfig::new method appropriately uses FrostError::InvalidConfig for all configuration validation errors. This semantic distinction from FrostError::Protocol (used for runtime errors) is a good design choice.


70-103: LGTM! Key generation error handling.

The generate method correctly uses FrostError::Protocol for runtime operation failures (key generation and KeyPackage conversion), with error messages that include the underlying error details.


106-148: LGTM! Key split error handling.

The split_existing method follows the same error handling pattern as generate, consistently using FrostError::Protocol for runtime operation failures.


151-170: LGTM! Group pubkey extraction with format handling.

The extract_group_pubkey function properly handles both 33-byte (compressed) and 32-byte (x-only) public key formats. The explicit match with a descriptive error for unexpected lengths is a good defensive practice.

keep-core/src/frost/signing.rs (6)

16-16: LGTM! Import updated for FrostError.

The import correctly brings in FrostError for signing-related error handling.


84-121: LGTM! Commitment phase error handling.

The commitment methods consistently use FrostError::Protocol for session state violations and duplicate commitment errors. Error messages clearly indicate the issue.


127-183: LGTM! Signature phase error handling with appropriate variants.

The signature share methods make good semantic distinctions:

  • FrostError::Protocol for workflow/state errors
  • FrostError::SigningFailed for cryptographic signing failures
  • Session state correctly transitions to Failed on aggregation errors

189-204: LGTM! Clean refactoring of signature_bytes.

The method uses the let-else pattern for cleaner early return handling. Serialization and length validation errors appropriately use FrostError::Protocol.


211-272: LGTM! Comprehensive error handling in sign_with_local_shares.

The function uses appropriate error variants:

  • FrostError::Protocol for no shares provided
  • FrostError::InsufficientSigners with structured needed/available fields for threshold violations
  • FrostError::SigningFailed for cryptographic signing errors
  • FrostError::Protocol for aggregation and serialization errors

26-46: All SigningSession struct fields are legitimately used in methods—the clippy warnings are false positives.

The fields marked with #[zeroize(skip)] (session_id, message, threshold, state, commitments, signature_shares, signature) are all read through getter methods or accessed directly in field operations. The session_id() method returns a reference to the field; state() returns the state value; commitments() and signature_shares() return references; and signature is read via the signature_bytes() method. If clippy warnings persist, suppress them locally using #[allow(unused_assignments)] on individual fields or the struct itself.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @keep-core/src/frost/coordinator.rs:
- Around line 17-18: The #[allow(unused_assignments)] attribute on the Zeroize
derive is incorrect; remove that attribute and apply the same fix used in
signing.rs: stop silencing the wrong lint and instead add the precise allow or
suppression that targets the generated zeroize code (mirror the exact change
from signing.rs for the #[derive(Zeroize, ZeroizeOnDrop)] use). Update the
attribute on the derive for the coordinator (the #[derive(Zeroize,
ZeroizeOnDrop)] block) to match the corrected lint suppression used in
signing.rs.

In @keep-core/src/frost/signing.rs:
- Around line 26-27: The attribute on the item with #[derive(ZeroizeOnDrop)] is
wrong: replace #[allow(unused_assignments)] with #[allow(dead_code)] because the
ZeroizeOnDrop derive causes fields to be read only for side effects (zeroing)
which triggers dead_code warnings; update the attribute adjacent to the
ZeroizeOnDrop derive accordingly so the compiler suppresses dead_code rather
than unused_assignments.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd8b36f and 24d85a7.

📒 Files selected for processing (3)
  • keep-core/src/frost/coordinator.rs
  • keep-core/src/frost/share.rs
  • keep-core/src/frost/signing.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • keep-core/src/frost/share.rs
🧰 Additional context used
🧬 Code graph analysis (1)
keep-core/src/frost/signing.rs (3)
keep-core/src/frost/coordinator.rs (2)
  • new (36-46)
  • aggregate (157-172)
keep-core/src/frost/share.rs (3)
  • new (26-43)
  • new (66-84)
  • key_package (86-90)
keep-core/src/frost/dealer.rs (2)
  • new (18-35)
  • new (66-68)
🪛 GitHub Actions: CI
keep-core/src/frost/coordinator.rs

[error] 21-21: value assigned to session_id is never read


[error] 23-23: value assigned to message is never read


[error] 25-25: value assigned to threshold is never read


[error] 27-27: value assigned to commitments is never read


[error] 29-29: value assigned to signature_shares is never read


[error] 31-31: value assigned to our_identifier is never read


[error] 30-30: value assigned to session_id is never read


[error] 32-32: value assigned to message is never read


[error] 34-34: value assigned to threshold is never read


[error] 36-36: value assigned to state is never read


[error] 39-39: value assigned to commitments is never read


[error] 41-41: value assigned to signature_shares is never read


[error] 46-46: value assigned to signature is never read

keep-core/src/frost/signing.rs

[error] 30-30: value assigned to session_id is never read


[error] 32-32: value assigned to message is never read


[error] 34-34: value assigned to threshold is never read


[error] 36-36: value assigned to state is never read


[error] 39-39: value assigned to commitments is never read


[error] 41-41: value assigned to signature_shares is never read


[error] 46-46: value assigned to signature is never read

🔇 Additional comments (15)
keep-core/src/frost/signing.rs (8)

16-16: LGTM - Import change aligns with error refactoring.

The import correctly brings in FrostError and Result from the crate's error module, consistent with the domain-specific error hierarchy introduced by this PR.


85-104: LGTM - Error handling in generate_commitment.

The error paths correctly use FrostError::Protocol for invalid session states. The control flow and nonce/commitment generation logic remain unchanged.


106-122: LGTM - Error handling in add_commitment.

Duplicate commitment and invalid state errors are properly mapped to FrostError::Protocol. State transition logic to CollectingShares is preserved.


128-149: LGTM - Error handling in generate_signature_share.

Error variants are appropriately differentiated:

  • FrostError::Protocol for state/nonce issues
  • FrostError::SigningFailed for cryptographic signing failures

This distinction aids debugging by categorizing error sources.


151-184: LGTM - Error handling in add_signature_share.

Aggregation failures correctly transition the session to SessionState::Failed and return FrostError::Protocol. The logic for collecting shares and triggering aggregation is preserved.


190-205: LGTM - Refactored signature_bytes with cleaner pattern matching.

The let Some(...) else pattern improves readability. Error handling for serialization and length validation correctly uses FrostError::Protocol.


212-226: LGTM - Appropriate use of FrostError::InsufficientSigners.

Using a specific error variant with needed and available fields provides better diagnostics than a generic protocol error. This aligns with the PR's goal of introducing domain-specific error types.


246-268: LGTM - Consistent error handling in sign_with_local_shares.

The function correctly uses:

  • FrostError::Protocol for missing nonces
  • FrostError::SigningFailed for signing failures
  • FrostError::Protocol for aggregation and serialization errors

This matches the patterns established in SigningSession methods.

keep-core/src/frost/coordinator.rs (7)

14-14: LGTM - Import change aligns with error refactoring.

Consistent with the pattern in signing.rs.


52-71: LGTM - Error handling in add_local_share.

Serialization errors for commitments correctly use FrostError::Protocol. The nonce and commitment generation logic is preserved.


73-91: LGTM - Error handling in add_remote_commitment.

Validation errors for message type, session ID, commitment deserialization, and identifier parsing all correctly use FrostError::Protocol with descriptive messages.


97-131: LGTM - Error handling in generate_signature_share.

Error paths for insufficient commitments, missing identifier/nonces, and signing failures all use FrostError::Protocol. The signing flow is preserved.


133-151: LGTM - Error handling in add_remote_signature_share.

Consistent with add_remote_commitment - uses FrostError::Protocol for validation and deserialization errors.


157-172: LGTM - Error handling in aggregate.

The aggregation error path matches the pattern in signing.rs and the relevant code snippet from this file's context.


174-188: LGTM - Error handling in aggregate_to_bytes.

Serialization and length validation errors correctly use FrostError::Protocol. The pattern matches signature_bytes in signing.rs.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @keep-cli/src/commands/serve.rs:
- Around line 63-69: The current mapping collapses all keep_frost_net errors
into FrostError::Protocol for both KfpNode::new() and node.announce(); update
the error handling to distinguish transport/connectivity errors from protocol
errors by matching the FrostNetError variants returned by KfpNode::new and
node.announce: introduce a new FrostError::Network (or FrostError::Transport)
variant and map FrostNetError::Transport and Timeout (or other network-related
variants) to that, while keeping serialization/deserialization or protocol-level
errors mapped to FrostError::Protocol; adjust the .map_err closures around
KfpNode::new() and node.announce() to perform this match and return the
appropriate FrostError variant instead of always using FrostError::Protocol.
🧹 Nitpick comments (3)
keep-cli/src/signer/handler.rs (2)

77-89: Inconsistent error type for FROST pubkey conversions.

Lines 77 and 81 correctly use FrostError::InvalidFrostPubkey for FROST group pubkey conversions. However, in handle_sign_event (line 187 and 212), the same PublicKey::from_slice(signer.group_pubkey()) operation uses KeyError::InvalidPubkey instead.

For consistency, consider using FrostError::InvalidFrostPubkey for all FROST group pubkey conversions, reserving KeyError::InvalidPubkey for non-FROST key operations (like line 86).


196-199: Consider moving signature-related errors to domain-specific types.

KeepError::EventIdFailed and KeepError::InvalidSignature remain as generic KeepError variants. For consistency with the domain-specific error hierarchy introduced in this PR, consider whether these should be moved to FrostError or CryptoError variants.

Also applies to: 221-224

keep-cli/src/commands/frost_network.rs (1)

892-892: Consider using CryptoError for encryption failures.

NetworkError::Encrypt is used for NIP-44 encryption failures during DKG. Since encryption is a cryptographic operation rather than a network operation, CryptoError might be more semantically appropriate. However, since the encryption occurs in the context of preparing network messages, this is a reasonable design choice.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e95c543 and e7ee4f7.

📒 Files selected for processing (6)
  • keep-cli/src/commands/frost.rs
  • keep-cli/src/commands/frost_hardware.rs
  • keep-cli/src/commands/frost_network.rs
  • keep-cli/src/commands/serve.rs
  • keep-cli/src/commands/vault.rs
  • keep-cli/src/signer/handler.rs
🧰 Additional context used
🧬 Code graph analysis (5)
keep-cli/src/commands/frost.rs (2)
keep-core/src/frost/dealer.rs (2)
  • new (18-35)
  • new (66-68)
keep-core/src/frost/transport.rs (2)
  • from_json (112-115)
  • from_json (200-203)
keep-cli/src/commands/serve.rs (4)
keep-core/src/frost/share.rs (2)
  • new (27-44)
  • new (66-84)
keep-core/src/frost/dealer.rs (2)
  • new (18-35)
  • new (66-68)
keep-core/src/lib.rs (1)
  • data_key (375-377)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-cli/src/commands/vault.rs (2)
keep-core/src/lib.rs (1)
  • data_key (375-377)
keep-core/src/storage.rs (1)
  • data_key (252-254)
keep-cli/src/commands/frost_network.rs (2)
keep-core/src/frost/share.rs (2)
  • new (27-44)
  • new (66-84)
keep-core/src/frost/dealer.rs (3)
  • new (18-35)
  • new (66-68)
  • generate (70-104)
keep-cli/src/commands/frost_hardware.rs (5)
keep-cli/src/server.rs (1)
  • error (425-431)
keep-core/src/frost/share.rs (1)
  • pubkey_package (92-96)
keep-core/src/frost/transport.rs (1)
  • session_id_bytes (210-219)
keep-core/src/frost/coordinator.rs (1)
  • session_id (48-50)
keep-core/src/frost/signing.rs (1)
  • session_id (77-79)
🔇 Additional comments (34)
keep-cli/src/commands/vault.rs (6)

8-8: LGTM!

Import correctly adds KeyError to support the new domain-specific error types used throughout this file.


33-63: LGTM!

Good categorization of errors:

  • KeepError::Input for validation failures (password length, mismatch)
  • KeepError::Runtime for IO failures (password read)

This improves error handling granularity and aligns with the domain-specific error hierarchy.


149-149: LGTM!

Using KeyError::Locked when data_key() returns None is semantically correct—the vault hasn't been unlocked yet. This pattern is applied consistently across cmd_generate_outer, cmd_generate_hidden, cmd_import_outer, and cmd_import_hidden.


475-478: LGTM!

KeyError::NotFound(name.into()) provides meaningful error context when a key lookup fails. This pattern is applied consistently across all export and delete commands.


508-516: LGTM!

Correct error flow: KeyError::NotFound when the key doesn't exist, KeyError::Locked when the vault data key is unavailable. Same pattern correctly applied in cmd_export_hidden.


631-635: The From<KeyError> implementation is correctly in place via the #[from] attribute on KeepError::Key (line 231 in keep-core/src/error.rs), which automatically generates the conversion. The ? operator at line 635 will properly convert KeyError to KeepError. The code is correct and will compile as expected.

keep-cli/src/signer/handler.rs (5)

10-10: LGTM!

The expanded imports for domain-specific error types align well with the refactoring goals of this PR.


114-114: LGTM!

Using PolicyError::PermissionDenied for permission-related errors is appropriate and improves error categorization.


240-246: LGTM!

The error mappings for secret key validation (CryptoError::InvalidSecretKey) and signing failures (FrostError::SigningFailed) are appropriate for their contexts.


278-307: LGTM!

The handle_nip44_encrypt function correctly uses:

  • PolicyError::PermissionDenied for permission checks
  • KeyError::NoSigningKey for missing keys
  • CryptoError::InvalidSecretKey for invalid secret keys
  • KeepError::Nip44 for NIP-44 specific errors

The error taxonomy is consistent and appropriate.


340-369: LGTM!

The NIP-04 handlers follow the same consistent pattern as NIP-44, using appropriate domain-specific error types.

keep-cli/src/commands/frost_network.rs (7)

7-7: LGTM!

The import of FrostError, NetworkError along with KeepError and Result sets up the domain-specific error types used throughout this file.


49-57: LGTM!

Appropriate error categorization:

  • KeepError::Runtime for tokio runtime creation failures
  • FrostError::Protocol for FROST node initialization errors

209-229: LGTM!

Hardware-related errors are properly categorized:

  • KeepError::HardwareConnection for connection failures
  • KeepError::HardwareOperation with structured op and reason fields for operation failures
  • FrostError::InvalidConfig for threshold validation

This provides clear error context for debugging hardware signer issues.


321-327: LGTM!

Using FrostError::InsufficientSigners with needed and available fields provides actionable error information when peer discovery fails.


406-422: LGTM!

The commitment flow uses appropriate error types:

  • FrostError::CommitmentFailed for commitment generation errors
  • FrostError::Protocol for nonce reuse detection (security-critical)
  • KeepError::Runtime for nonce tracking failures

443-447: LGTM!

Network operations correctly use NetworkError variants:

  • NetworkError::RelayAdd for relay connection errors
  • NetworkError::SignRequest for event signing errors
  • NetworkError::Publish for event publishing errors

This provides clear categorization of network-related failures.


735-745: LGTM!

DKG errors use FrostError::DkgFailed with structured stage and reason fields, providing clear context for which DKG stage failed and why.

keep-cli/src/commands/frost.rs (6)

6-6: LGTM!

The import of FrostError and KeyError alongside KeepError enables the domain-specific error handling throughout this file.


42-44: LGTM!

Using FrostError::Protocol for the edge case where frost_generate or frost_split returns an empty shares vector is appropriate, as this indicates an unexpected protocol-level failure.

Also applies to: 92-94


222-232: LGTM!

The input handling correctly uses:

  • KeepError::Runtime for I/O read errors
  • KeepError::Input for size limit violations

This provides clear distinction between system errors and user input errors.


382-399: LGTM!

Appropriate error types for share validation:

  • KeyError::NotFound for missing shares (key-related error)
  • FrostError::InsufficientSigners with structured fields for threshold violations

The structured InsufficientSigners error with needed and available fields provides actionable diagnostic information.


449-491: LGTM!

The interactive signing round 1 uses appropriate error types:

  • FrostError::Protocol for serialization errors
  • KeepError::Runtime for I/O read errors
  • FrostError::InvalidSessionId for session mismatch
  • FrostError::CommitmentFailed for commitment deserialization

The error categorization clearly distinguishes protocol errors from runtime/I/O errors.


497-552: LGTM!

The interactive signing round 2 and aggregation phase correctly use:

  • FrostError::SigningFailed for signing errors
  • FrostError::Protocol for serialization/validation errors
  • FrostError::InvalidSessionId for session mismatches

The error handling is comprehensive and consistent with the domain-specific error hierarchy.

keep-cli/src/commands/frost_hardware.rs (6)

6-6: LGTM!

The import of FrostError alongside KeepError enables domain-specific error handling for FROST protocol errors in hardware operations.


21-29: LGTM!

The ping command uses appropriate error categorization:

  • KeepError::HardwareConnection for connection failures
  • KeepError::HardwareOperation with structured op and reason fields for ping failures

This provides clear debugging context for hardware signer issues.


43-54: LGTM!

Consistent error handling pattern for hardware list operations, matching the structure used in cmd_frost_hardware_ping.


113-129: LGTM!

Using FrostError::Protocol for FROST-specific serialization errors (secret share, verifying share, group key) is appropriate as these are protocol-level data format errors.


145-164: LGTM!

The import flow correctly categorizes errors:

  • KeepError::HardwareConnection for connection
  • KeepError::HardwareOperation with op: "ping" and op: "import_share" for respective operations

This maintains consistency with the error patterns established elsewhere in the file.


209-235: LGTM!

The hardware sign command uses appropriate error types:

  • KeepError::Input for session_id hex validation (user input error)
  • KeepError::HardwareConnection for connection failures
  • KeepError::HardwareOperation with op: "frost_sign" for signing failures

The error categorization is consistent and provides clear context for each failure mode.

keep-cli/src/commands/serve.rs (4)

10-10: LGTM!

Import statement correctly expanded to include FrostError and KeyError, both of which are used in the refactored error handling paths below.


58-59: LGTM — consistent runtime error categorization.

The mapping from tokio runtime creation errors to KeepError::Runtime is appropriate and applied consistently across all occurrences in this file (lines 59, 124, 227, 261, 323, 359, 421).


245-245: LGTM — correct domain-specific error type for locked key state.

Using KeyError::Locked instead of KeepError::Locked properly categorizes this as a key-related error, aligning with the hierarchical error structure. The same pattern at line 343 is equally correct.


227-228: LGTM!

TUI execution errors appropriately categorized as KeepError::Runtime. The pattern is consistent across all three command functions.

@wksantiago
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refine error types with domain-specific variants

2 participants