Skip to content

Kind 21101, 21106, 21104/21105 relay signing#63

Merged
kwsantiago merged 1 commit intomainfrom
feat/frost-nostr-protocol
Jan 6, 2026
Merged

Kind 21101, 21106, 21104/21105 relay signing#63
kwsantiago merged 1 commit intomainfrom
feat/frost-nostr-protocol

Conversation

@kwsantiago
Copy link
Contributor

@kwsantiago kwsantiago commented Jan 6, 2026

Closes #57, #55, #56

Summary by CodeRabbit

  • New Features

    • Create and announce FROST groups with configurable name, threshold, participants and relay hints.
    • Pre-generate and publish nonce commitments for groups.
  • Enhancements

    • Hardware signing now supports relay-backed multi-step flows with nonce lifecycle tracking, relay-backed request/commitment publishing, and clearer relay/session/request outputs.
    • Local persistent nonce store and share-info exposure to support multi-peer signing flows.

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

@kwsantiago kwsantiago self-assigned this Jan 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

Warning

Rate limit exceeded

@kwsantiago has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 3 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 68350b8 and a19f059.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • keep-cli/Cargo.toml
  • keep-cli/src/main.rs
  • keep-cli/src/signer/hardware.rs
  • keep-cli/src/signer/mod.rs
  • keep-cli/src/signer/nonce_store.rs

Walkthrough

Adds FROST GroupCreate and NoncePrecommit commands, a persistent nonce store, hardware share-info API, and converts hardware signing to a relay-backed multi-step flow with new event kinds (21101, 21104–21106) and propagated threshold/participant parameters.

Changes

Cohort / File(s) Summary
CLI entry & dispatcher
keep-cli/src/main.rs
Added FrostNetworkCommands::GroupCreate and ::NoncePrecommit variants; extended dispatcher to route to new handlers and pass threshold/participants into sign flows.
Group & Nonce handlers
keep-cli/src/main.rs
New cmd_frost_network_group_create(...) publishes Kind 21101 group announcements; new cmd_frost_network_nonce_precommit(...) requests device nonces and publishes Kind 21106 commitments with session metadata.
Hardware signing (relay-backed)
keep-cli/src/main.rs
Expanded cmd_frost_network_sign and cmd_frost_network_sign_hardware signatures to accept relay, threshold, participants; added relay-backed flow that publishes 21104/21105 events, collects peer commitments, manages nonce lifecycle, and surfaces Relay/Session/IDs.
Hardware signer API
keep-cli/src/signer/hardware.rs
Added HardwareSigner::get_share_info(group: &str) -> Result<ShareInfo> and new ShareInfo struct (pubkey, index, threshold, participants).
Nonce store module
keep-cli/src/signer/mod.rs, keep-cli/src/signer/nonce_store.rs
Exposed nonce_store module and re-exported NonceStore; implemented persistent NonceStore with NonceEntry, open/save, add/is_used/mark_used/get_available/get_used/nonce_stats/clear_used operations persisted as JSON.
Public API surface
keep-cli/src/main.rs, keep-cli/src/signer/*
New public enum variants, public functions (cmd_frost_network_group_create, cmd_frost_network_nonce_precommit), public structs (ShareInfo, NonceStore, NonceEntry) and adjusted public function signatures for hardware signing paths.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI User
    participant Handler as Nonce Precommit Handler
    participant Device as Hardware Device
    participant Relay as Relay

    User->>Handler: cmd_frost_network_nonce_precommit(group, relay, device, count)
    Handler->>Device: Request nonce generation (count)
    Device-->>Handler: Return nonce commitments
    Handler->>Relay: Publish Kind 21106 (commitments + session metadata)
    Relay-->>Handler: Confirmation / Event ID
    Handler-->>User: Confirmation + Event ID
Loading
sequenceDiagram
    participant User as CLI User
    participant Handler as Hardware Sign Handler
    participant Device as Hardware Device
    participant Relay as Relay
    participant Peers as Peer Participants

    User->>Handler: cmd_frost_network_sign_hardware(group, relay, device, ...)
    Handler->>Device: Create local commitment / prepare signing
    Handler->>Relay: Publish Kind 21104 (sign request, session id)
    Handler->>Relay: Publish Kind 21105 (local commitment)
    Relay-->>Peers: Broadcast request/commitments
    Peers-->>Relay: Peer commitments/shares
    Relay-->>Handler: Peer events received
    Handler->>Device: Combine shares / finalize signature
    Handler->>Relay: Publish Kind 21105 (final response)
    Relay-->>Handler: Confirmation / Event IDs
    Handler-->>User: Relay, Session ID, Request/Response Event IDs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I nibble bytes and hop through code,
I plant group seeds on the relay road.
Nonces lined up, commitments made,
Devices hum and peers cascade,
A frosty hop — signatures parade!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly references the specific Kind numbers (21101, 21106, 21104/21105) and relay signing functionality that constitute the main changes in the PR.
Linked Issues check ✅ Passed The PR implements all core requirements: nonce pre-commitment generation (21106), nonce usage tracking via NonceStore, and relay-based signing flow integration for reduced latency.
Out of Scope Changes check ✅ Passed All changes align with issue #57 objectives: nonce store implementation, group creation (21101), nonce precommit (21106), and relay signing integration are all in-scope.

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.

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-cli/src/main.rs:
- Around line 2120-2121: The warning about nonce reuse is correct but no
tracking exists: add a NonceStore (e.g., struct NonceStore) that persists
pre-committed nonces and their state, implement methods is_nonce_used(&self,
nonce: &Nonce) -> bool and mark_nonce_used(&mut self, nonce: &Nonce) ->
Result<(), Error>, and call these from the signing path (where signing requests
consume a pre-committed nonce—update the function handling sign requests, e.g.,
sign_request/sign_message) to check and prevent reuse; also update the code that
prints the pre-committed list (out.info, out.warn) to reference the NonceStore
so the CLI can show which nonces are still available and which are spent, and
ensure persistence/backing storage (file or DB) so state survives restarts per
Issue #57.
- Around line 1322-1326: The loop uses a hardcoded threshold (threshold = 2u16)
which breaks groups with different signing thresholds; replace this by
retrieving the actual threshold from the group metadata or hardware signer (or
accept it as a CLI/func parameter) and use that value when comparing
peer_commitments.len(); locate and update the variable named threshold and the
while loop referencing peer_commitments so it uses the fetched/passed threshold
(e.g., group.threshold or a provided threshold_arg) instead of the fixed 2u16,
and ensure any error handling/log message reflects if the group metadata/CLI arg
is missing.

Caution

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

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

1-4368: Fix formatting issues flagged by CI pipeline.

The CI pipeline failed with cargo fmt -- --check. Run cargo fmt to fix formatting issues before merging.

#!/bin/bash
# Check which files need formatting
cd keep-cli && cargo fmt -- --check
🧹 Nitpick comments (3)
keep-cli/src/main.rs (3)

1256-1270: Ephemeral keys generated per session may complicate peer verification.

Each signing session generates new ephemeral Keys, meaning the coordinator identity changes every time. While this provides unlinkability, it may make it harder for peers to verify legitimate signing requests versus spam/attacks.

Consider whether a persistent coordinator identity (perhaps derived from the hardware signer) would improve the security model for relay-coordinated signing.


1336-1354: Silent handling of malformed peer commitments.

When parsing peer commitments, a participant_index of 0 (from failed parsing) is silently skipped along with empty commitments. Consider logging warnings for malformed messages to aid debugging.

🔎 Suggested improvement
                     if peer_idx > 0 && !peer_commitment.is_empty() && !peer_commitments.contains_key(&peer_idx) {
                         peer_commitments.insert(peer_idx, peer_commitment.to_string());
                         out.success(&format!("Received commitment from participant {}", peer_idx));
+                    } else if peer_idx == 0 || peer_commitment.is_empty() {
+                        // Log for debugging malformed messages
+                        tracing::debug!("Skipping malformed commitment: idx={}, empty={}", peer_idx, peer_commitment.is_empty());
                     }

1959-1969: Ephemeral coordinator keys for group announcements.

Similar to signing, this generates ephemeral keys. For group announcements (Kind 21101), consider whether the coordinator identity should be stable/verifiable so participants can trust the announcement source.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cafaa77 and ea4bdbb.

📒 Files selected for processing (1)
  • keep-cli/src/main.rs
🧰 Additional context used
🪛 GitHub Actions: CI
keep-cli/src/main.rs

[error] 1-1: cargo fmt -- --check failed. Rust formatting changes are needed in this file (keep-cli/src/main.rs). Run 'cargo fmt' or commit pre-commit formatting to fix.

🔇 Additional comments (4)
keep-cli/src/main.rs (4)

258-279: Command definitions look reasonable.

The new GroupCreate and NoncePrecommit subcommands are well-structured with appropriate argument types and defaults.


649-661: Dispatch logic correctly routes to new handlers.

The match arms properly extract parameters and delegate to the new handler functions.


1940-1946: Good validation of participant count.

The check ensuring the number of provided npubs matches the declared participant count prevents misconfiguration.


2066-2078: The concern about message binding in pre-generated nonces is based on a misunderstanding of the FROST protocol.

RFC 9591 (FROST) explicitly supports pre-generating nonces and their commitments before any signing message is chosen. The nonces themselves are random and don't need to be message-specific at generation time. Instead, the per-message binding occurs in Round 2 through binding factors computed from the message, commitment list, and group public key—not at commit time.

The code correctly follows this pattern:

  1. frost_commit() generates fresh random nonces on the hardware device and returns only their public commitments
  2. These commitments are stored and can be published
  3. Later, frost_sign() uses those same commitments with the actual message and session ID
  4. The hardware signer computes binding factors in Round 2 to bind the nonces to the specific message being signed

The dummy session and message values in frost_commit() are just RPC parameters to trigger nonce generation on the hardware device—they don't affect which nonces are actually generated (those are random). The actual security binding happens when frost_sign() is called with the real message.

The warning "Each nonce commitment can only be used once" is correct and already documented in the code.

@kwsantiago kwsantiago force-pushed the feat/frost-nostr-protocol branch from ea4bdbb to 952798f Compare January 6, 2026 00:32
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

Fix all issues with AI Agents 🤖
In @keep-cli/src/main.rs:
- Around line 2069-2108: The group_id in cmd_frost_network_group_create is
currently derived only from name and participant_npubs which allows collisions
for groups that differ by threshold or participants; include the threshold and
participants values in the Sha256 input (e.g., update the hasher with threshold
and participants as bytes or a canonical string) before finalizing the hash so
different parameter combinations produce distinct group_id values (also consider
including relays if you want relay-specific uniqueness).
- Around line 1422-1470: The loop accepts peer commitments without checking the
response's request_id; update the event handling inside the for ev in events
loop to parse the "request_id" (e.g., content.get("request_id").and_then(|v|
v.as_str())) and ignore the event unless it equals our session_id variable, then
proceed to validate participant_index and commitment as before; ensure you check
request_id before inserting into peer_commitments or emitting the success
message so commitments from other sessions are rejected.
- Around line 1529-1588: The loop collecting signature shares accepts any
"signed" events without verifying they belong to our signing session; add a
check in the events processing (inside the for ev in events.iter() loop, before
accepting a share and inserting into peer_shares) that reads
content.get("request_id") (or the exact field name used for session id) and
compares it to our session_id (the variable holding our session/request
identifier); if it does not match, skip the event, otherwise continue the
existing participant_index/partial_signature validation and insertion into
peer_shares.
🧹 Nitpick comments (2)
keep-cli/src/main.rs (2)

1429-1433: Consider subscription-based event monitoring.

The current implementation uses fetch_events polling (lines 1429-1433 and 1542-1545) which could miss events if:

  • The relay connection is temporarily disrupted
  • Events arrive between polling intervals
  • The relay applies rate limiting

For a more robust implementation, consider using client.subscribe() with a channel receiver to get real-time event notifications. This would eliminate polling delays and improve reliability.


1346-1360: Note: Signing coordination is unencrypted.

Unlike the DKG flow which encrypts Round 2 shares with NIP-44 (see lines 1939-1941), the signing coordination publishes Kind 21104 and 21105 events in plaintext. This means:

  • The message being signed is visible to relay operators and observers
  • Participant indices and commitments are public
  • Signature shares are public before aggregation

This is acceptable if the signing group intends for the coordination to be public, but worth documenting clearly for users who may expect privacy.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea4bdbb and 952798f.

📒 Files selected for processing (1)
  • keep-cli/src/main.rs
🧰 Additional context used
🧬 Code graph analysis (1)
keep-cli/src/main.rs (2)
keep-frost-net/src/event.rs (1)
  • commitment (79-99)
keep-core/src/keys.rs (2)
  • generate (23-32)
  • clone (95-101)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
keep-cli/src/main.rs (8)

270-301: LGTM on CLI command definitions.

The new GroupCreate and NoncePrecommit commands are well-structured with appropriate help text and defaults. The use of Vec<String> for relay and participant_npub properly supports multiple values.


1126-1151: Good validation for hardware signing parameters.

The logic correctly enforces that --threshold and --participants are required for hardware signing and validates that 2 <= threshold <= participants. This prevents common configuration errors.


1256-1318: Hardware signing setup and validation looks correct.

The function now properly accepts threshold and participants as parameters, and validates the hardware-returned share index (lines 1308-1313). This addresses the previous concern about hardcoded thresholds.


1339-1368: Sign request event structure looks correct.

The Kind 21104 event includes appropriate tags (d, request_id, message_type) and uses the session_id as the request identifier.


1370-1418: Commitment response event published correctly.

The Kind 21105 event properly references the request event and includes the participant's commitment with correct tags.


2128-2176: Group announcement event structure looks correct.

The Kind 21101 event includes all necessary tags (group_id, threshold, participants, per-participant p tags, and relay tags) and properly formats the content as JSON with name and description.


644-694: Dispatch logic correctly routes new commands.

The handler properly forwards threshold and participants parameters to cmd_frost_network_sign and correctly dispatches the new GroupCreate and NoncePrecommit commands to their respective handlers.


243-254: CLI arguments properly defined as optional.

The new --threshold and --participants arguments are correctly defined as Option<u16> and include helpful documentation. The runtime validation in the handler (lines 1130-1141) appropriately enforces these as required for hardware signing while keeping them optional for software-based signing.

@kwsantiago kwsantiago force-pushed the feat/frost-nostr-protocol branch 2 times, most recently from 68350b8 to 08f75b6 Compare January 6, 2026 00:46
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: 6

Fix all issues with AI Agents 🤖
In @keep-cli/src/main.rs:
- Around line 1301-1304: The code is misformatted around NonceStore::open /
nonce_stats / out.info in main.rs; run `cargo fmt` to reformat the file, then
review and commit the resulting changes (ensure the lines involving
NonceStore::open, nonce_store.nonce_stats(group_npub) and out.info(...) are
properly formatted per rustfmt). After formatting, re-run CI to confirm the
issue is resolved.
- Around line 1575-1611: The signature-share loop handling events does not
verify the response's request_id against our session_id; before accepting a
share in the for ev in events.iter() block (where you parse ev.content into
serde_json::Value, extract status, participant_index, and partial_signature and
insert into peer_shares via peer_shares.entry(peer_idx)), add the same guard as
in the commitment phase: extract content.get("request_id").and_then(|v|
v.as_str()).unwrap_or("") and compare it to hex::encode(session_id); if it does
not match, continue and skip the event. Ensure this check runs early
(immediately after parsing content and before
status/participant_index/partial_signature validation) so only shares for the
current session are accepted.
- Around line 2134-2140: The group_id is currently derived from Sha256::new()
updated with the literal "frost-group-id-v1", name, and each participant_npubs,
which allows collisions when threshold or participants differ; update the
hashing in the group_id computation (the hasher used where group_id: [u8; 32] is
produced) to also incorporate the threshold value and the participants list (or
canonicalized participants metadata) into the hasher updates (e.g., call
hasher.update for threshold and any participants metadata) so the final group_id
uniquely reflects name, participant_npubs, threshold, and participants.

In @keep-cli/src/signer/nonce_store.rs:
- Around line 22-25: NonceStore reads/writes the JSON file without locking,
risking race conditions and nonce reuse; update NonceStore to acquire an
exclusive file lock around read/write operations (e.g., in open() and save()) by
opening the file with OpenOptions and using a cross-platform locker
(fs2::FileExt or fd-lock) before reading/parsing or writing/serializing, hold
the lock for the duration of the operation (and unlock/close afterwards), and
consider storing the file handle on the NonceStore struct while the lock is held
to ensure uniqueness and proper release.
- Around line 27-28: Formatting in NonceStore (e.g., the impl block and function
like pub fn open in nonce_store.rs) does not conform to rustfmt; run `cargo fmt`
in the repository root to reformat the file (and any others flagged at the CI
lines 28, 44, 75, 86), then commit the formatted changes so the impl NonceStore,
pub fn open, and surrounding code match the project's rustfmt rules; if you have
a rustfmt.toml, ensure it is applied before formatting.
🧹 Nitpick comments (1)
keep-cli/src/signer/hardware.rs (1)

159-189: Minor inconsistency in error messages.

The error messages for index, threshold, and participants don't include the actual out-of-range value, unlike the existing get_share_pubkey method (line 155) which includes the value for better debugging.

🔎 Suggested improvement for consistency
         let index = result["index"]
             .as_u64()
             .ok_or_else(|| anyhow!("Missing index in response"))?
             .try_into()
-            .map_err(|_| anyhow!("index out of range"))?;
+            .map_err(|e| anyhow!("index out of range: {}", e))?;
         let threshold = result["threshold"]
             .as_u64()
             .ok_or_else(|| anyhow!("Missing threshold in response"))?
             .try_into()
-            .map_err(|_| anyhow!("threshold out of range"))?;
+            .map_err(|e| anyhow!("threshold out of range: {}", e))?;
         let participants = result["participants"]
             .as_u64()
             .ok_or_else(|| anyhow!("Missing participants in response"))?
             .try_into()
-            .map_err(|_| anyhow!("participants out of range"))?;
+            .map_err(|e| anyhow!("participants out of range: {}", e))?;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 952798f and 68350b8.

📒 Files selected for processing (4)
  • keep-cli/src/main.rs
  • keep-cli/src/signer/hardware.rs
  • keep-cli/src/signer/mod.rs
  • keep-cli/src/signer/nonce_store.rs
🧰 Additional context used
🧬 Code graph analysis (1)
keep-cli/src/signer/hardware.rs (1)
keep-frost-net/src/session.rs (2)
  • threshold (152-154)
  • participants (148-150)
🪛 GitHub Actions: CI
keep-cli/src/main.rs

[error] 1301-1301: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.


[error] 2342-2342: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.

keep-cli/src/signer/nonce_store.rs

[error] 28-28: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.


[error] 44-44: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.


[error] 75-75: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.


[error] 86-86: cargo fmt --check: formatting differences detected. Please run 'cargo fmt' to fix code style issues.

🔇 Additional comments (11)
keep-cli/src/signer/mod.rs (1)

7-14: LGTM!

The module declaration and re-export follow the established pattern used for other modules in this file. Clean integration of the new nonce store functionality.

keep-cli/src/signer/hardware.rs (1)

364-371: LGTM!

The ShareInfo struct is well-designed with appropriate field types matching the hardware response format. The #[allow(dead_code)] annotation is acceptable for a newly introduced API.

keep-cli/src/signer/nonce_store.rs (2)

53-72: LGTM with note on idempotency.

The duplicate check at lines 55-57 provides good idempotency - adding the same commitment twice is a no-op. The timestamp fallback to 0 on system clock issues is a reasonable defensive choice.


43-51: LGTM!

The save() method is appropriately private, ensuring it's called consistently as an internal implementation detail when state changes.

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

243-254: LGTM!

The new threshold and participants CLI arguments for the Sign command are well-documented with appropriate help text indicating they're required for hardware signing.


270-301: LGTM!

The GroupCreate and NoncePrecommit command definitions are well-structured with clear help text. The default count of 10 nonces is a reasonable starting point.


1130-1159: Good resolution of threshold from hardware.

This addresses the previous review concern about hardcoded thresholds. The code now:

  1. Falls back to querying the hardware signer for threshold and participants if not provided via CLI
  2. Validates that 2 <= threshold <= participants

The validation at lines 1144-1149 correctly rejects invalid configurations.


1326-1334: Nonce tracking is well-integrated.

The implementation correctly:

  1. Checks if nonce was previously used (line 1326) - prevents reuse
  2. Adds the nonce to the store immediately (lines 1332-1334)
  3. Marks it as used after signing completes (lines 1640-1643)

This addresses Issue #57's requirement to "Track nonce usage to prevent reuse."

Note: If signing fails after the nonce is generated but before completion, the nonce remains in the store as "unused." This is acceptable since the nonce wasn't actually consumed in a signature, and the hardware signer should maintain its own nonce state.


1336-1341: Good defensive validation of participant indices.

The validation ensures the hardware-returned index is within the valid range [1, participants]. Similar checks are applied to peer indices (lines 1477-1479, 1595-1597), preventing acceptance of malformed data.


1447-1455: LGTM!

The 120-second timeout for collecting peer commitments is reasonable for multi-peer coordination scenarios. The timeout is properly checked in the loop with a clear error message on expiration.


2267-2279: Pre-committed nonces use dummy session/message - verify hardware compatibility.

The nonces are generated using random dummy_session and dummy_message values (lines 2268-2269). However, frost_commit typically binds the nonce to a specific signing context. When these pre-committed nonces are later used in actual signing:

  1. The hardware signer may expect the original session_id during frost_sign
  2. The commitment may be bound to the dummy message, making it invalid for the real message

This implementation publishes commitments to the relay but may not be compatible with how the hardware signer handles the nonce lifecycle. The signing flow in cmd_frost_network_sign_hardware generates fresh nonces (line 1319) rather than consuming pre-committed ones.

Please verify:

  1. Does the hardware signer support decoupled nonce generation and signing?
  2. Is there a mechanism to use pre-committed nonces during frost_sign instead of generating fresh ones?
#!/bin/bash
# Search for how pre-committed nonces are consumed during signing
rg -n "precommit|pre_commit|PreCommit" --type rust
rg -n "21106" --type rust -A 5 -B 5

@kwsantiago kwsantiago force-pushed the feat/frost-nostr-protocol branch from 08f75b6 to a19f059 Compare January 6, 2026 00:52
@kwsantiago kwsantiago merged commit d9eb4a3 into main Jan 6, 2026
2 checks passed
@kwsantiago kwsantiago deleted the feat/frost-nostr-protocol branch January 6, 2026 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant