cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit ae95e9c5c397c6a68e7d19c555cfece6ab80a93f
parent a5566584db9b4d4a858ae66e44ea704680c7c22f
Author: triesap <tyson@radroots.org>
Date:   Sat, 25 Apr 2026 04:31:06 +0000

runtime: remove memory cli secret backend

- reject memory for persistent account backend config
- keep fallback diagnostics limited to host_vault encrypted_file or none
- update runtime and render allowed-backend evidence
- prove rejected account creation writes no store state

Diffstat:
Msrc/render/mod.rs | 12++----------
Msrc/runtime/config.rs | 24++++++++++++------------
Mtests/identity_commands.rs | 43++++++++++++++++++++++++++++++++++++++++---
Mtests/runtime_show.rs | 7+++++++
4 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/src/render/mod.rs b/src/render/mod.rs @@ -4345,11 +4345,7 @@ mod tests { account_secret_contract: AccountSecretContractConfig { default_backend: "host_vault".into(), default_fallback: Some("encrypted_file".into()), - allowed_backends: vec![ - "host_vault".into(), - "encrypted_file".into(), - "memory".into(), - ], + allowed_backends: vec!["host_vault".into(), "encrypted_file".into()], host_vault_policy: Some("desktop".into()), uses_protected_store: true, }, @@ -4506,11 +4502,7 @@ mod tests { account_secret_contract: AccountSecretContractConfig { default_backend: "host_vault".into(), default_fallback: Some("encrypted_file".into()), - allowed_backends: vec![ - "host_vault".into(), - "encrypted_file".into(), - "memory".into(), - ], + allowed_backends: vec!["host_vault".into(), "encrypted_file".into()], host_vault_policy: Some("desktop".into()), uses_protected_store: true, }, diff --git a/src/runtime/config.rs b/src/runtime/config.rs @@ -29,7 +29,7 @@ const DEFAULT_RPC_URL: &str = "http://127.0.0.1:7070"; const CLI_HOST_VAULT_POLICY: &str = "desktop"; const CLI_DEFAULT_SECRET_BACKEND: &str = "host_vault"; const CLI_DEFAULT_SECRET_FALLBACK: &str = "encrypted_file"; -const CLI_ALLOWED_SHARED_SECRET_BACKENDS: &[&str] = &["host_vault", "encrypted_file", "memory"]; +const CLI_ALLOWED_SHARED_SECRET_BACKENDS: &[&str] = &["host_vault", "encrypted_file"]; const CLI_USES_PROTECTED_STORE: bool = true; const ENV_FILE_PATH: &str = "RADROOTS_ENV_FILE"; const ENV_OUTPUT: &str = "RADROOTS_OUTPUT"; @@ -1332,11 +1332,16 @@ fn parse_account_secret_fallback( key: &str, value: &str, ) -> Result<Option<RadrootsSecretBackend>, RuntimeError> { - if value.trim().eq_ignore_ascii_case("none") { - return Ok(None); + match value.trim().to_ascii_lowercase().as_str() { + "none" => Ok(None), + "host_vault" => Ok(Some(RadrootsSecretBackend::HostVault( + RadrootsHostVaultPolicy::desktop(), + ))), + "encrypted_file" => Ok(Some(RadrootsSecretBackend::EncryptedFile)), + other => Err(RuntimeError::Config(format!( + "{key} must be `host_vault`, `encrypted_file`, or `none`, got `{other}`" + ))), } - - parse_account_secret_backend(key, value).map(Some) } fn parse_account_secret_backend( @@ -1348,9 +1353,8 @@ fn parse_account_secret_backend( RadrootsHostVaultPolicy::desktop(), )), "encrypted_file" => Ok(RadrootsSecretBackend::EncryptedFile), - "memory" => Ok(RadrootsSecretBackend::Memory), other => Err(RuntimeError::Config(format!( - "{key} must be `host_vault`, `encrypted_file`, `memory`, or `none` for fallback, got `{other}`" + "{key} must be `host_vault` or `encrypted_file`, got `{other}`" ))), } } @@ -1556,11 +1560,7 @@ mod tests { AccountSecretContractConfig { default_backend: "host_vault".to_owned(), default_fallback: Some("encrypted_file".to_owned()), - allowed_backends: vec![ - "host_vault".to_owned(), - "encrypted_file".to_owned(), - "memory".to_owned(), - ], + allowed_backends: vec!["host_vault".to_owned(), "encrypted_file".to_owned(),], host_vault_policy: Some("desktop".to_owned()), uses_protected_store: true, } diff --git a/tests/identity_commands.rs b/tests/identity_commands.rs @@ -211,9 +211,46 @@ fn account_new_rejects_plaintext_fallback_backend() { assert_eq!(output.status.code(), Some(2)); let stderr = String::from_utf8(output.stderr).expect("utf8 stderr"); - assert!( - stderr.contains("must be `host_vault`, `encrypted_file`, `memory`, or `none` for fallback") - ); + assert!(stderr.contains("must be `host_vault`, `encrypted_file`, or `none`")); +} + +#[test] +fn account_new_rejects_memory_secret_backend_without_creating_store_state() { + let dir = tempdir().expect("tempdir"); + let store_path = data_root(dir.path()).join("shared/accounts/store.json"); + + let output = cli_command_in(dir.path()) + .env("RADROOTS_ACCOUNT_SECRET_BACKEND", "memory") + .args(["account", "new"]) + .output() + .expect("run account new"); + + assert_eq!(output.status.code(), Some(2)); + assert!(!store_path.exists()); + assert!(output.stdout.is_empty()); + let stderr = String::from_utf8(output.stderr).expect("utf8 stderr"); + assert!(stderr.contains("RADROOTS_ACCOUNT_SECRET_BACKEND")); + assert!(stderr.contains("must be `host_vault` or `encrypted_file`")); +} + +#[test] +fn account_new_rejects_memory_secret_fallback_without_creating_store_state() { + let dir = tempdir().expect("tempdir"); + let store_path = data_root(dir.path()).join("shared/accounts/store.json"); + + let output = cli_command_in(dir.path()) + .env("RADROOTS_ACCOUNT_SECRET_BACKEND", "host_vault") + .env("RADROOTS_ACCOUNT_SECRET_FALLBACK", "memory") + .args(["account", "new"]) + .output() + .expect("run account new"); + + assert_eq!(output.status.code(), Some(2)); + assert!(!store_path.exists()); + assert!(output.stdout.is_empty()); + let stderr = String::from_utf8(output.stderr).expect("utf8 stderr"); + assert!(stderr.contains("RADROOTS_ACCOUNT_SECRET_FALLBACK")); + assert!(stderr.contains("must be `host_vault`, `encrypted_file`, or `none`")); } #[test] diff --git a/tests/runtime_show.rs b/tests/runtime_show.rs @@ -237,6 +237,13 @@ fn config_show_json_reports_default_bootstrap_state() { "encrypted_file" ); assert_eq!( + json["account"]["secret_backend"]["allowed_backends"] + .as_array() + .expect("allowed backends") + .len(), + 2 + ); + assert_eq!( json["account"]["secret_backend"]["allowed_backends"][0], "host_vault" );