Skip to content

Conversation

@wksantiago
Copy link
Contributor

@wksantiago wksantiago commented Jan 13, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Audit logging records all key vault operations with cryptographic integrity verification and hash-chain validation
    • Export audit entries in JSON format for external analysis, reporting, and archival
    • Verify audit chain integrity and apply flexible retention policies to manage log storage
    • View comprehensive audit statistics aggregated by operation type and security event category

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

Warning

Rate limit exceeded

@wksantiago has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 9 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 9e0efbc and 962667a.

📒 Files selected for processing (2)
  • keep-core/src/audit.rs
  • keep-core/src/lib.rs

Walkthrough

A comprehensive audit logging system is being introduced to keep-cli and keep-core, featuring encrypted log persistence with hash-chain verification, retention policies, JSON export, and five new CLI subcommands for managing and inspecting vault audit events.

Changes

Cohort / File(s) Summary
Core Audit Infrastructure
keep-core/src/audit.rs, keep-core/Cargo.toml
Introduces AuditEventType enum, AuditEntry with fluent builder API for metadata (pubkey, key_type, message_hash, group_pubkey, threshold, etc.), and RetentionPolicy. Core AuditLog struct manages encrypted audit log persistence with methods for open(), log(), read_all(), verify_chain(), apply_retention(), and export() with hash-chain validation. Adds base64 dependency.
Keep Structure Integration
keep-core/src/lib.rs
Integrates AuditLog as optional field in Keep struct. Emits audit events across vault lifecycle (unlock/lock) and key operations (generate, import, frost split/share/sign). Updates frost_export_share() and frost_sign() signatures to &mut self to support audit logging. Exposes public audit APIs: audit_read_all(), audit_verify_chain(), audit_export(), audit_set_retention(), audit_apply_retention().
CLI Audit Commands
keep-cli/src/commands/audit.rs
Implements five audit subcommands: cmd_audit_list() (display audit entries with limit), cmd_audit_export() (JSON export to file/stdout), cmd_audit_verify() (hash-chain validation), cmd_audit_retention() (configure and apply retention policies), cmd_audit_stats() (aggregate operation statistics). Includes utility functions for path resolution and timestamp formatting.
CLI Command Wiring
keep-cli/src/commands/mod.rs, keep-cli/src/main.rs
Adds audit module export and new top-level Audit command variant with AuditCommands enum containing List, Export, Verify, Retention, and Stats variants. Implements dispatch_audit() router to delegate to command handlers.

Sequence Diagrams

sequenceDiagram
    participant User
    participant CLI
    participant Keep
    participant AuditLog
    participant File

    User->>CLI: unlock vault
    CLI->>Keep: unlock(password)
    Keep->>AuditLog: open(path, data_key)
    AuditLog->>File: read last_hash
    Keep->>AuditLog: log(VaultUnlock event)
    AuditLog->>AuditLog: compute_hash(entry + prev_hash)
    AuditLog->>File: encrypt & append entry
    Keep->>CLI: success
    CLI->>User: vault unlocked
Loading
sequenceDiagram
    participant User
    participant CLI
    participant Keep
    participant AuditLog
    participant File

    User->>CLI: audit list/export/verify
    CLI->>Keep: audit_read_all() / audit_verify_chain() / audit_export()
    Keep->>AuditLog: read_all(data_key) / verify_chain(data_key) / export(data_key)
    AuditLog->>File: read encrypted lines
    AuditLog->>AuditLog: decrypt & deserialize entries
    alt verify_chain path
        AuditLog->>AuditLog: validate hash chain integrity
    else export path
        AuditLog->>AuditLog: serialize entries to JSON
    end
    AuditLog->>Keep: entries / bool / JSON string
    Keep->>CLI: result
    CLI->>User: formatted output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hop-hop, the audit logs now bloom,
Encrypted chains dance through the gloom,
Hash by hash, each entry signed,
Retention policies gently aligned,
Verify, export, and list with glee—
Tamper-proof secrets for all to see! 🔐

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.35% 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 'Add audit logging' directly and clearly describes the main change: introducing a comprehensive audit logging system across keep-core and keep-cli with event tracking, hash-chain verification, retention policies, and export capabilities.

✏️ Tip: You can configure your own custom pre-merge checks in the 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 17:48
@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: 2

🤖 Fix all issues with AI agents
In @keep-core/src/lib.rs:
- Around line 296-304: The audit event for FrostShareImport is only emitted when
hex::decode and try_from succeed, leaving missing audit logs if group_pubkey
decoding fails; change the logic in the block that currently calls
self.audit_event(AuditEventType::FrostShareImport, ...) to always emit the event
by deriving a fallback group value when decoding/conversion fails (e.g., use a
zeroed [u8;32] or hash of export.group_pubkey) and then call
self.audit_event(AuditEventType::FrostShareImport) with
e.with_group(&group_or_fallback).with_participants(vec![export.identifier]) so
the audit is recorded regardless of hex decoding success.
🧹 Nitpick comments (4)
keep-core/src/audit.rs (2)

141-171: Consider using a stable discriminant for AuditEventType in hash computation.

The cast self.event_type as u8 (line 144) relies on implicit enum discriminants. If variants are reordered or inserted in the middle of AuditEventType, the discriminant values change, which would silently break hash verification for existing audit logs.

Consider either:

  1. Adding #[repr(u8)] with explicit discriminants to AuditEventType
  2. Using the Display string representation instead (already implemented)
Option 1: Add explicit discriminants
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
+#[repr(u8)]
 pub enum AuditEventType {
-    KeyGenerate,
-    KeyImport,
+    KeyGenerate = 0,
+    KeyImport = 1,
     // ... assign explicit values to all variants
Option 2: Use Display string in hash
     fn compute_hash(&self) -> [u8; 32] {
         let mut data = Vec::new();
         data.extend_from_slice(&self.timestamp.to_le_bytes());
-        data.extend_from_slice(&(self.event_type as u8).to_le_bytes());
+        data.extend_from_slice(self.event_type.to_string().as_bytes());

233-241: Consider flushing/syncing file for durability.

The file is written but not explicitly flushed or synced before returning. On system crash, the audit entry could be lost even though log() returned Ok.

Add explicit sync for durability
         let mut file = OpenOptions::new()
             .create(true)
             .append(true)
             .open(&self.path)?;
         file.write_all(line.as_bytes())?;
+        file.sync_all()?;

         self.last_hash = entry.hash;
         Ok(())
keep-core/src/lib.rs (1)

432-444: Audit logging failures are silently ignored.

Line 442 uses let _ = audit.log(...) which discards any errors. While this prevents audit failures from breaking primary operations, it means audit events could be silently lost.

Consider at minimum logging a warning when audit fails, so operators can detect audit system issues.

Log warning on audit failure
     fn audit_event<F>(&mut self, event_type: AuditEventType, builder: F)
     where
         F: FnOnce(AuditEntry) -> AuditEntry,
     {
         let data_key = match self.get_data_key() {
             Ok(k) => k,
             Err(_) => return,
         };
         if let Some(ref mut audit) = self.audit {
             let entry = builder(AuditEntry::new(event_type, audit.last_hash()));
-            let _ = audit.log(entry, &data_key);
+            if let Err(e) = audit.log(entry, &data_key) {
+                tracing::warn!("Failed to write audit event: {}", e);
+            }
         }
     }
keep-cli/src/commands/audit.rs (1)

182-199: Some event types are not included in statistics.

The match statement doesn't count FrostShareImport, FrostShareExport, FrostShareDelete, and VaultLock events. These are silently ignored by the _ => {} catch-all.

Consider adding counters for share operations and lock events, or at minimum displaying a count of "Other" events so users know the totals add up.

Add counters for missing event types
     let mut auth_fail = 0;
     let mut unlock = 0;
+    let mut lock = 0;
+    let mut share_ops = 0;

     for entry in &entries {
         match entry.event_type {
             // ... existing matches ...
             AuditEventType::VaultUnlock => unlock += 1,
+            AuditEventType::VaultLock => lock += 1,
+            AuditEventType::FrostShareImport
+            | AuditEventType::FrostShareExport
+            | AuditEventType::FrostShareDelete => share_ops += 1,
             _ => {}
         }
     }
📜 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 9e0efbc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • keep-cli/src/commands/audit.rs
  • keep-cli/src/commands/mod.rs
  • keep-cli/src/main.rs
  • keep-core/Cargo.toml
  • keep-core/src/audit.rs
  • keep-core/src/lib.rs
🧰 Additional context used
🧬 Code graph analysis (4)
keep-cli/src/commands/audit.rs (2)
keep-core/src/audit.rs (2)
  • open (203-216)
  • new (79-94)
keep-core/src/lib.rs (2)
  • open (50-58)
  • unlock (60-68)
keep-cli/src/main.rs (1)
keep-cli/src/commands/audit.rs (5)
  • cmd_audit_list (30-84)
  • cmd_audit_export (86-106)
  • cmd_audit_verify (108-123)
  • cmd_audit_retention (125-156)
  • cmd_audit_stats (158-235)
keep-core/src/lib.rs (1)
keep-core/src/audit.rs (3)
  • new (79-94)
  • open (203-216)
  • export (304-308)
keep-core/src/audit.rs (2)
keep-core/src/crypto.rs (2)
  • blake2b_256 (355-363)
  • from_slice (242-249)
keep-core/src/lib.rs (2)
  • open (50-58)
  • data_key (428-430)
⏰ 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 (15)
keep-core/Cargo.toml (1)

28-28: LGTM!

The base64 = "0.22" dependency is appropriately added to support the base64 encoding/decoding of encrypted audit log entries in the new audit module.

keep-core/src/audit.rs (3)

13-60: LGTM!

The AuditEventType enum provides comprehensive coverage of auditable events across key operations, signing, FROST threshold operations, authentication, and vault lifecycle. The Display implementation produces consistent snake_case identifiers suitable for logging and export.


181-194: LGTM!

Sensible default retention policy with 10,000 entries and 365 days. The optional fields provide flexibility for users who want only count-based or time-based retention.


370-455: LGTM - Tests cover core functionality.

The tests validate hash chain integrity, persistence, chain verification, and entry-count retention. Consider adding tests for:

  • max_age_days retention filtering
  • Detection of a broken/tampered chain (verify_chain returning false)
keep-core/src/lib.rs (2)

60-75: LGTM!

The audit log lifecycle is properly managed: initialized on unlock with a VaultUnlock event, and cleared on lock after emitting VaultLock. The event emission order ensures the lock event is recorded before the audit log is closed.


446-474: LGTM!

The public audit API methods correctly check for locked state and delegate to the audit log. The audit_set_retention silently no-ops when locked, which is reasonable since there's no audit log to configure.

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

15-28: LGTM!

The helper functions are clean. format_timestamp correctly handles edge cases via timestamp_opt().single() and provides a sensible fallback.


30-84: LGTM!

The list command correctly displays the most recent entries (when limited) in chronological order. The truncation of hex identifiers to 8 characters provides a good balance between readability and identification.


86-106: LGTM!

Simple and effective export implementation with stdout fallback when no output path is specified.


108-123: LGTM!

The verify command correctly exits with a non-zero status code when chain verification fails, making it suitable for use in scripts and CI pipelines.


125-156: LGTM!

The retention command properly separates policy configuration from application, requiring explicit --apply flag to make changes. Good UX for a destructive operation.

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

1-9: LGTM!

The audit module is properly exported and positioned alphabetically among the existing command modules.

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

69-72: LGTM!

The Audit subcommand is cleanly integrated following the existing pattern used for Frost, Bitcoin, and Enclave commands.


99-119: LGTM!

The AuditCommands enum is well-structured with clear help text for CLI arguments. The subcommands cover the essential audit operations: listing, export, verification, retention management, and statistics.


487-506: LGTM!

The dispatch_audit function correctly routes all audit subcommands to their respective handlers, following the established dispatch pattern used throughout the CLI.

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.

Add audit logging for signing operations

2 participants