Skip to content

Conversation

@wksantiago
Copy link
Contributor

@wksantiago wksantiago commented Jan 15, 2026

Summary

  • Add optional ~/.config/keep/config.toml support
  • Configuration options: vault_path, argon2_profile, log_level, relays, timeout
  • Precedence: CLI flags > env vars > config file > defaults
  • Add keep config subcommand (show, path, init)

Test plan

  • Run keep config init to create config file
  • Run keep config show to verify settings
  • Test CLI flag override of config values
  • Test relay defaults in FROST commands

Summary by CodeRabbit

  • New Features

    • Added TOML-based configuration with an example, typed config parsing, validation, defaults, and a new Config command (show/path/init).
    • CLI now uses config-derived defaults for vault path and relay settings when flags are omitted.
  • Chores

    • App now loads and propagates configuration at startup; command dispatch updated to thread config through handlers.
    • Added two new dependencies for config parsing and path expansion.
  • Style

    • Minor cleanup of an output warning attribute.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Walkthrough

Adds a TOML-backed configuration subsystem to keep-cli: new dependencies, example config, a typed Config module with parsing/validation/defaults, keep config subcommands, and wiring to apply config defaults (relay, vault path, timeout) across CLI dispatch.

Changes

Cohort / File(s) Summary
Dependencies
\keep-cli/Cargo.toml``
Added toml = "0.8" and shellexpand = "3.1".
Configuration Template
\keep-cli/contrib/config.toml.example``
New example TOML config documenting vault_path, argon2_profile, log_level, relays, timeout, and precedence rules.
Configuration Module
\keep-cli/src/config.rs``
New typed Config with Argon2Profile and LogLevel enums, serde deserialization deny_unknown_fields, custom deserializers (tilde expansion, relay URL validation, non-zero/max timeout), load/parse/from_file/default_path helpers, runtime accessors, and unit tests.
CLI Integration
\keep-cli/src/main.rs``
Added mod config and use crate::config::Config; load Config at startup; added config command (Show, Path, Init); changed several relay/path CLI options from String to Option<String> and threaded &Config through dispatch functions to apply config-derived defaults.
Output Cleanup
\keep-cli/src/output.rs``
Removed #[allow(dead_code)] on Output::warn (attribute deletion only).

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Client (keep CLI)
  participant Config as Config module
  participant FS as Filesystem
  participant Handler as Command dispatch / Handlers
  participant Output as Output

  CLI->>Config: Config::load()
  Config->>FS: read default config path
  FS-->>Config: file contents / NotFound
  Config->>Config: parse TOML, expand ~, validate relays/timeout
  Config-->>CLI: Config instance
  CLI->>Handler: dispatch command with &Config
  Handler->>Config: default_relay(), vault_path(), timeout_secs()
  Handler->>FS: (Init) create dir & write sample config
  Handler->>Output: emit results
  Output-->>CLI: display response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Poem

🐰 I sniffed a TOML patch today,

Relays and paths in tidy array,
Tilde expands, defaults hum along,
Configs in place — a hopping song! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.29% 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 TOML configuration file support' directly and clearly summarizes the main objective of the changeset, which introduces TOML configuration file functionality to the keep-cli project.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 1

🤖 Fix all issues with AI agents
In `@keep-cli/src/main.rs`:
- Around line 422-424: The code silently converts a potential error from
cfg.vault_path() into an empty PathBuf via unwrap_or_default(), which yields
confusing failures later; change the logic around let path =
cli.path.unwrap_or_else(|| cfg.vault_path().unwrap_or_default()) to instead
handle the Result from cfg.vault_path() explicitly: if cli.path is Some use it,
otherwise call cfg.vault_path() and propagate or surface any error (return Err
or .expect with a clear message) so failures like missing $HOME are reported
immediately and not converted into an empty path.
🧹 Nitpick comments (2)
keep-cli/src/config.rs (2)

57-71: Consider stricter URL validation.

The current validation checks only the scheme prefix. Malformed URLs like wss:// (empty host) or wss://[invalid would pass validation but fail at runtime when connecting.

Consider using the url crate (already a dependency) for full URL parsing:

♻️ Optional: Use url::Url for stricter validation
 fn deserialize_relays<'de, D>(deserializer: D) -> std::result::Result<Vec<String>, D::Error>
 where
     D: Deserializer<'de>,
 {
     let relays: Vec<String> = Vec::deserialize(deserializer)?;
     for relay in &relays {
-        if !relay.starts_with("wss://") && !relay.starts_with("ws://") {
-            return Err(serde::de::Error::custom(format!(
-                "Invalid relay URL: '{}'. Must start with wss:// or ws://",
-                relay
-            )));
+        let url = url::Url::parse(relay).map_err(|e| {
+            serde::de::Error::custom(format!("Invalid relay URL '{}': {}", relay, e))
+        })?;
+        if url.scheme() != "ws" && url.scheme() != "wss" {
+            return Err(serde::de::Error::custom(format!(
+                "Invalid relay URL: '{}'. Must use ws:// or wss:// scheme",
+                relay
+            )));
         }
     }
     Ok(relays)
 }

136-140: Minor: Error variant may be misleading.

default_path() returns KeepError::HomeNotFound when dirs::config_dir() returns None. This could happen when both $XDG_CONFIG_HOME and $HOME are unset. While technically correct (home is needed to derive config dir), a more specific error message could help users diagnose the issue.

♻️ Optional: More descriptive error
     pub fn default_path() -> Result<PathBuf> {
         dirs::config_dir()
             .map(|p| p.join("keep").join("config.toml"))
-            .ok_or(KeepError::HomeNotFound)
+            .ok_or_else(|| KeepError::Other(
+                "Could not determine config directory (HOME or XDG_CONFIG_HOME not set)".into()
+            ))
     }
📜 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 73afdc8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • keep-cli/Cargo.toml
  • keep-cli/contrib/config.toml.example
  • keep-cli/src/config.rs
  • keep-cli/src/main.rs
  • keep-cli/src/output.rs
💤 Files with no reviewable changes (1)
  • keep-cli/src/output.rs
🧰 Additional context used
🧬 Code graph analysis (2)
keep-cli/src/config.rs (1)
keep-core/src/lib.rs (1)
  • default_keep_path (372-376)
keep-cli/src/main.rs (1)
keep-cli/src/config.rs (4)
  • load (112-119)
  • default_relay (149-154)
  • default_path (136-140)
  • timeout_secs (156-158)
🔇 Additional comments (13)
keep-cli/Cargo.toml (1)

38-38: LGTM!

The toml and shellexpand dependencies are appropriate choices for configuration parsing and tilde expansion respectively. Version constraints are reasonable.

Also applies to: 56-58

keep-cli/contrib/config.toml.example (1)

1-21: LGTM!

The example configuration template is well-documented with clear descriptions of each option, valid values, and defaults. All options are commented out by default, which is appropriate for an example file that users can selectively enable.

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

7-47: LGTM!

The Argon2Profile and LogLevel enums are well-designed with appropriate defaults, consistent Display implementations, and case-insensitive deserialization via rename_all = "lowercase".


49-55: LGTM!

The path deserializer correctly expands tilde (~) to the home directory. Not validating path existence at parse time is the right choice—the path may be created later.


73-82: LGTM!

Rejecting zero timeout is appropriate—it would cause immediate failures. The validation is sufficient for practical use.


84-109: LGTM!

The Config struct is well-designed with deny_unknown_fields to catch typos, appropriate field defaults, and a consistent manual Default implementation.


142-158: LGTM!

The helper methods vault_path(), default_relay(), and timeout_secs() provide sensible fallbacks that are consistent with the documented defaults in config.toml.example.


161-245: LGTM! Good test coverage.

The tests comprehensively cover parsing, defaults, validation errors, and edge cases. Consider adding a test for ws:// relays (non-TLS) to verify they're accepted alongside wss://.

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

436-453: LGTM!

The Serve command correctly applies the precedence: CLI flags override config defaults. The pattern of unwrap_or_else(|| cfg.default_relay().to_string()) is appropriate.


513-628: LGTM!

The dispatch_frost_network function consistently applies the config default relay across all network commands. The GroupCreate special case for Vec<String> is handled correctly.


736-785: LGTM!

The dispatch_config handler is well-implemented:

  • Show displays effective configuration with defaults applied
  • Path provides the config file location
  • Init safely checks for existing files before creating, uses include_str! for compile-time embedding of the example config

100-105: LGTM!

The ConfigCommands enum is clean and matches the implementation. The subcommands cover the essential operations for config management.


61-68: Backward compatibility is maintained—relay defaults now come from TOML config.

The relay arguments are now Option<String>, and when not provided, they fall back to Config::default_relay(), which returns the first configured relay or "wss://nos.lol" if none exist. Users can still pass --relay explicitly, and those without explicit values benefit from configuration-driven defaults.

This is a good change—no breaking changes for existing CLI users, only improved flexibility.

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

@wksantiago wksantiago requested a review from kwsantiago January 15, 2026 15:28
@wksantiago wksantiago self-assigned this Jan 15, 2026
@wksantiago wksantiago linked an issue Jan 15, 2026 that may be closed by this pull request
@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.

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/config.rs`:
- Around line 114-148: Config::load currently fails if Config::default_path()
returns HomeNotFound, aborting startup even though config is optional; update
Config::load to treat a HomeNotFound from default_path() as "no config" and
return Ok(Self::default()) instead of propagating the error—specifically, call
Self::default_path(), match the Result, and if Err(KeepError::HomeNotFound)
return Ok(Self::default()), otherwise propagate other errors and proceed with
the existing path.exists()/from_file logic; reference functions: Config::load,
Config::default_path, KeepError::HomeNotFound, and Config::default.

@kwsantiago kwsantiago merged commit 0229abe into main Jan 21, 2026
9 checks passed
@kwsantiago kwsantiago deleted the TOML-configuration branch January 21, 2026 03:38
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 TOML configuration file support

3 participants