commit 3f856c6368cf5841c4ceac5e4860b042a38bcb1d
parent 3a292865c8260e4b15731c224a25eb754be0ad0d
Author: triesap <tyson@radroots.org>
Date: Wed, 8 Apr 2026 16:08:31 +0000
runtime: align cli contract reporting
Diffstat:
10 files changed, 226 insertions(+), 40 deletions(-)
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
@@ -4,8 +4,8 @@ use crate::domain::runtime::{
};
use crate::runtime::RuntimeError;
use crate::runtime::accounts::{
- AccountCreateMode, AccountRecordView, create_or_migrate_selected_account, resolve_account,
- select_account, snapshot,
+ AccountCreateMode, AccountRecordView, SHARED_ACCOUNT_STORE_SOURCE,
+ create_or_migrate_selected_account, resolve_account, select_account, snapshot,
};
use crate::runtime::config::RuntimeConfig;
@@ -18,8 +18,8 @@ pub fn init(config: &RuntimeConfig) -> Result<AccountNewView, RuntimeError> {
AccountCreateMode::Migrated => "migrated".to_owned(),
},
source: match result.mode {
- AccountCreateMode::Created => "local account store · local first".to_owned(),
- AccountCreateMode::Migrated => "legacy identity import · local first".to_owned(),
+ AccountCreateMode::Created => SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
+ AccountCreateMode::Migrated => "legacy shared identity import · local first".to_owned(),
},
public_identity: IdentityPublicView::from_public_identity(
&result.account.record.public_identity,
@@ -36,7 +36,7 @@ pub fn show(config: &RuntimeConfig) -> Result<CommandOutput, RuntimeError> {
let view = match resolve_account(config)? {
Some(account) => AccountWhoamiView {
state: "ready".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
reason: None,
public_identity: Some(IdentityPublicView::from_public_identity(
&account.record.public_identity,
@@ -45,7 +45,7 @@ pub fn show(config: &RuntimeConfig) -> Result<CommandOutput, RuntimeError> {
},
None => AccountWhoamiView {
state: "unconfigured".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
reason: Some(format!(
"no local account is selected in {}",
config.account.store_path.display()
@@ -83,7 +83,7 @@ pub fn list(config: &RuntimeConfig) -> Result<CommandOutput, RuntimeError> {
};
Ok(CommandOutput::success(CommandView::AccountList(
AccountListView {
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
count: accounts.len(),
accounts,
actions,
@@ -95,7 +95,7 @@ pub fn use_account(config: &RuntimeConfig, selector: &str) -> Result<AccountUseV
let account = select_account(config, selector)?;
Ok(AccountUseView {
state: "active".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
active_account_id: account.record.account_id.to_string(),
account: account_summary(&account),
})
diff --git a/src/commands/runtime.rs b/src/commands/runtime.rs
@@ -26,6 +26,10 @@ pub fn show(
},
paths: PathsRuntimeView {
profile: config.paths.profile.clone(),
+ allowed_profiles: config.paths.allowed_profiles.clone(),
+ app_namespace: config.paths.app_namespace.clone(),
+ shared_accounts_namespace: config.paths.shared_accounts_namespace.clone(),
+ shared_identities_namespace: config.paths.shared_identities_namespace.clone(),
app_config_path: config.paths.app_config_path.display().to_string(),
workspace_config_path: config.paths.workspace_config_path.display().to_string(),
app_data_root: config.paths.app_data_root.display().to_string(),
@@ -58,6 +62,11 @@ pub fn show(
secrets_dir: config.account.secrets_dir.display().to_string(),
identity_path: config.identity.path.display().to_string(),
secret_backend: AccountSecretRuntimeView {
+ contract_default_backend: config.account_secret_contract.default_backend.clone(),
+ contract_default_fallback: config.account_secret_contract.default_fallback.clone(),
+ allowed_backends: config.account_secret_contract.allowed_backends.clone(),
+ host_vault_policy: config.account_secret_contract.host_vault_policy.clone(),
+ uses_protected_store: config.account_secret_contract.uses_protected_store,
configured_primary: secret_backend.configured_primary,
configured_fallback: secret_backend.configured_fallback,
state: secret_backend.state,
diff --git a/src/domain/runtime.rs b/src/domain/runtime.rs
@@ -146,6 +146,10 @@ pub struct LoggingRuntimeView {
#[derive(Debug, Clone, Serialize)]
pub struct PathsRuntimeView {
pub profile: String,
+ pub allowed_profiles: Vec<String>,
+ pub app_namespace: String,
+ pub shared_accounts_namespace: String,
+ pub shared_identities_namespace: String,
pub app_config_path: String,
pub workspace_config_path: String,
pub app_data_root: String,
@@ -167,6 +171,13 @@ pub struct AccountRuntimeView {
#[derive(Debug, Clone, Serialize)]
pub struct AccountSecretRuntimeView {
+ pub contract_default_backend: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub contract_default_fallback: Option<String>,
+ pub allowed_backends: Vec<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub host_vault_policy: Option<String>,
+ pub uses_protected_store: bool,
pub configured_primary: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub configured_fallback: Option<String>,
diff --git a/src/render/mod.rs b/src/render/mod.rs
@@ -496,11 +496,22 @@ fn render_config_show(
present_absent(view.config_files.workspace_present),
view.paths.workspace_config_path
);
+ let allowed_profiles = view.paths.allowed_profiles.join(", ");
render_pairs(
stdout,
- "config roots",
+ "runtime roots",
&[
("profile", view.paths.profile.as_str()),
+ ("allowed profiles", allowed_profiles.as_str()),
+ ("app namespace", view.paths.app_namespace.as_str()),
+ (
+ "shared accounts namespace",
+ view.paths.shared_accounts_namespace.as_str(),
+ ),
+ (
+ "shared identities namespace",
+ view.paths.shared_identities_namespace.as_str(),
+ ),
("app config", user_config.as_str()),
("workspace config", workspace_config.as_str()),
("app data root", view.paths.app_data_root.as_str()),
@@ -536,14 +547,30 @@ fn render_config_show(
("store path", view.account.store_path.as_str()),
("secrets dir", view.account.secrets_dir.as_str()),
(
- "secret backend",
+ "contract default secret backend",
+ view.account.secret_backend.contract_default_backend.as_str(),
+ ),
+ (
+ "configured secret backend",
view.account.secret_backend.configured_primary.as_str(),
),
("identity path", view.account.identity_path.as_str()),
];
+ if let Some(fallback) = &view.account.secret_backend.contract_default_fallback {
+ account_rows.push(("contract default fallback", fallback.as_str()));
+ }
if let Some(fallback) = &view.account.secret_backend.configured_fallback {
- account_rows.push(("secret fallback", fallback.as_str()));
+ account_rows.push(("configured secret fallback", fallback.as_str()));
+ }
+ let allowed_backends = view.account.secret_backend.allowed_backends.join(", ");
+ account_rows.push(("allowed secret backends", allowed_backends.as_str()));
+ if let Some(policy) = &view.account.secret_backend.host_vault_policy {
+ account_rows.push(("host vault policy", policy.as_str()));
}
+ account_rows.push((
+ "uses protected store",
+ yes_no(view.account.secret_backend.uses_protected_store),
+ ));
account_rows.push(("secret status", view.account.secret_backend.state.as_str()));
if let Some(active_backend) = &view.account.secret_backend.active_backend {
account_rows.push(("active secret backend", active_backend.as_str()));
@@ -2059,9 +2086,9 @@ mod tests {
RelayEntryView, RelayListView,
};
use crate::runtime::config::{
- AccountConfig, IdentityConfig, LocalConfig, LoggingConfig, MycConfig, OutputConfig,
- OutputFormat, PathsConfig, RelayConfig, RelayConfigSource, RelayPublishPolicy, RpcConfig,
- RuntimeConfig, SignerBackend, SignerConfig, Verbosity,
+ AccountConfig, AccountSecretContractConfig, IdentityConfig, LocalConfig, LoggingConfig,
+ MycConfig, OutputConfig, OutputFormat, PathsConfig, RelayConfig, RelayConfigSource,
+ RelayPublishPolicy, RpcConfig, RuntimeConfig, SignerBackend, SignerConfig, Verbosity,
};
use crate::runtime::logging::LoggingState;
use radroots_secret_vault::RadrootsSecretBackend;
@@ -2078,6 +2105,10 @@ mod tests {
},
paths: PathsConfig {
profile: "interactive_user".into(),
+ allowed_profiles: vec!["interactive_user".into()],
+ app_namespace: "apps/cli".into(),
+ shared_accounts_namespace: "shared/accounts".into(),
+ shared_identities_namespace: "shared/identities".into(),
app_config_path: "/home/tester/.radroots/config/apps/cli/config.toml".into(),
workspace_config_path: "/workspace/.radroots/config.toml".into(),
app_data_root: "/home/tester/.radroots/data/apps/cli".into(),
@@ -2100,6 +2131,18 @@ mod tests {
secret_backend: RadrootsSecretBackend::EncryptedFile,
secret_fallback: None,
},
+ 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(),
+ "plaintext_file".into(),
+ ],
+ host_vault_policy: Some("desktop".into()),
+ uses_protected_store: true,
+ },
identity: IdentityConfig {
path: "/home/tester/.radroots/secrets/shared/identities/default.json".into(),
},
@@ -2134,6 +2177,8 @@ mod tests {
.expect("runtime show");
assert_eq!(view.output.format, "human");
assert_eq!(view.paths.profile, "interactive_user");
+ assert_eq!(view.paths.app_namespace, "apps/cli");
+ assert_eq!(view.paths.shared_accounts_namespace, "shared/accounts");
assert_eq!(
view.paths.workspace_config_path,
"/workspace/.radroots/config.toml"
@@ -2146,6 +2191,7 @@ mod tests {
);
assert_eq!(view.relay.count, 2);
assert_eq!(view.relay.publish_policy, "any");
+ assert_eq!(view.account.secret_backend.contract_default_backend, "host_vault");
assert!(
view.local
.replica_db_path
@@ -2187,6 +2233,10 @@ mod tests {
},
paths: PathsConfig {
profile: "interactive_user".into(),
+ allowed_profiles: vec!["interactive_user".into()],
+ app_namespace: "apps/cli".into(),
+ shared_accounts_namespace: "shared/accounts".into(),
+ shared_identities_namespace: "shared/identities".into(),
app_config_path: "/home/tester/.radroots/config/apps/cli/config.toml"
.into(),
workspace_config_path: "/workspace/.radroots/config.toml".into(),
@@ -2211,6 +2261,18 @@ mod tests {
secret_backend: RadrootsSecretBackend::EncryptedFile,
secret_fallback: None,
},
+ 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(),
+ "plaintext_file".into(),
+ ],
+ host_vault_policy: Some("desktop".into()),
+ uses_protected_store: true,
+ },
identity: IdentityConfig {
path: "/home/tester/.radroots/secrets/shared/identities/default.json"
.into(),
@@ -2257,7 +2319,7 @@ mod tests {
#[test]
fn account_list_ndjson_emits_one_json_object_per_account() {
let output = CommandOutput::success(CommandView::AccountList(AccountListView {
- source: "local account store · local first".to_owned(),
+ source: "shared account store · local first".to_owned(),
count: 2,
accounts: vec![
crate::domain::runtime::AccountSummaryView {
diff --git a/src/runtime/accounts.rs b/src/runtime/accounts.rs
@@ -30,6 +30,7 @@ const ENCRYPTED_FILE_MASTER_KEY_FILE: &str = ".vault.key";
const ENCRYPTED_FILE_SECRET_SUFFIX: &str = ".secret.json";
const PLAINTEXT_FILE_SECRET_SUFFIX: &str = ".secret";
const WRAPPED_KEY_VERSION: u8 = 1;
+pub const SHARED_ACCOUNT_STORE_SOURCE: &str = "shared account store · local first";
#[derive(Debug, Clone)]
pub struct AccountSnapshot {
diff --git a/src/runtime/config.rs b/src/runtime/config.rs
@@ -3,10 +3,9 @@ use std::fs;
use std::path::Path;
use std::path::PathBuf;
-use radroots_identity::DEFAULT_IDENTITY_PATH;
use radroots_runtime_paths::{
DEFAULT_CONFIG_FILE_NAME, RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver,
- RadrootsRuntimeNamespace,
+ RadrootsRuntimeNamespace, default_shared_identity_path,
};
use radroots_secret_vault::{RadrootsHostVaultPolicy, RadrootsSecretBackend};
use serde::Deserialize;
@@ -24,6 +23,21 @@ const DEFAULT_LOCAL_BACKUPS_DIR: &str = "backups";
const DEFAULT_LOCAL_EXPORTS_DIR: &str = "exports";
const DEFAULT_SHARED_ACCOUNTS_STORE_FILE: &str = "store.json";
const DEFAULT_RPC_URL: &str = "http://127.0.0.1:7070";
+const CLI_PROFILE: &str = "interactive_user";
+const CLI_APP_NAMESPACE_VALUE: &str = "cli";
+const SHARED_ACCOUNTS_NAMESPACE_VALUE: &str = "accounts";
+const SHARED_IDENTITIES_NAMESPACE_VALUE: &str = "identities";
+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_PROFILES: &[&str] = &[CLI_PROFILE];
+const CLI_ALLOWED_SHARED_SECRET_BACKENDS: &[&str] = &[
+ "host_vault",
+ "encrypted_file",
+ "memory",
+ "plaintext_file",
+];
+const CLI_USES_PROTECTED_STORE: bool = true;
const ENV_FILE_PATH: &str = "RADROOTS_ENV_FILE";
const ENV_OUTPUT: &str = "RADROOTS_OUTPUT";
const ENV_CLI_LOG_FILTER: &str = "RADROOTS_CLI_LOGGING_FILTER";
@@ -125,6 +139,15 @@ pub struct AccountConfig {
pub secret_fallback: Option<RadrootsSecretBackend>,
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AccountSecretContractConfig {
+ pub default_backend: String,
+ pub default_fallback: Option<String>,
+ pub allowed_backends: Vec<String>,
+ pub host_vault_policy: Option<String>,
+ pub uses_protected_store: bool,
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignerBackend {
Local,
@@ -211,6 +234,7 @@ pub struct RuntimeConfig {
pub paths: PathsConfig,
pub logging: LoggingConfig,
pub account: AccountConfig,
+ pub account_secret_contract: AccountSecretContractConfig,
pub identity: IdentityConfig,
pub signer: SignerConfig,
pub relay: RelayConfig,
@@ -222,6 +246,10 @@ pub struct RuntimeConfig {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathsConfig {
pub profile: String,
+ pub allowed_profiles: Vec<String>,
+ pub app_namespace: String,
+ pub shared_accounts_namespace: String,
+ pub shared_identities_namespace: String,
pub app_config_path: PathBuf,
pub workspace_config_path: PathBuf,
pub app_data_root: PathBuf,
@@ -342,6 +370,16 @@ impl RuntimeConfig {
secret_backend: account_secret_backend,
secret_fallback: account_secret_fallback,
},
+ account_secret_contract: AccountSecretContractConfig {
+ default_backend: CLI_DEFAULT_SECRET_BACKEND.to_owned(),
+ default_fallback: Some(CLI_DEFAULT_SECRET_FALLBACK.to_owned()),
+ allowed_backends: CLI_ALLOWED_SHARED_SECRET_BACKENDS
+ .iter()
+ .map(|value| (*value).to_owned())
+ .collect(),
+ host_vault_policy: Some(CLI_HOST_VAULT_POLICY.to_owned()),
+ uses_protected_store: CLI_USES_PROTECTED_STORE,
+ },
identity: IdentityConfig {
path: args
.identity_path
@@ -400,26 +438,39 @@ impl RuntimeConfig {
fn resolve_paths(env: &dyn Environment) -> Result<PathsConfig, RuntimeError> {
let current_dir = env.current_dir()?;
let resolver = env.path_resolver();
+ let profile = RadrootsPathProfile::InteractiveUser;
+ let overrides = RadrootsPathOverrides::default();
let resolved = resolver
- .resolve(
- RadrootsPathProfile::InteractiveUser,
- &RadrootsPathOverrides::default(),
- )
+ .resolve(profile, &overrides)
.map_err(|err| RuntimeError::Config(format!("resolve Radroots path roots: {err}")))?;
- let app_namespace = RadrootsRuntimeNamespace::app("cli")
+ let app_namespace = RadrootsRuntimeNamespace::app(CLI_APP_NAMESPACE_VALUE)
.map_err(|err| RuntimeError::Config(format!("resolve cli namespace: {err}")))?;
- let shared_accounts_namespace = RadrootsRuntimeNamespace::shared("accounts")
+ let shared_accounts_namespace = RadrootsRuntimeNamespace::shared(SHARED_ACCOUNTS_NAMESPACE_VALUE)
.map_err(|err| RuntimeError::Config(format!("resolve shared accounts namespace: {err}")))?;
+ let shared_identity_namespace =
+ RadrootsRuntimeNamespace::shared(SHARED_IDENTITIES_NAMESPACE_VALUE).map_err(|err| {
+ RuntimeError::Config(format!("resolve shared identities namespace: {err}"))
+ })?;
let app_paths = resolved.namespaced(&app_namespace);
let shared_accounts_paths = resolved.namespaced(&shared_accounts_namespace);
- let default_identity_path = resolved
- .secrets
- .join("shared")
- .join("identities")
- .join(DEFAULT_IDENTITY_PATH);
+ let default_identity_path = default_shared_identity_path(&resolver, profile, &overrides)
+ .map_err(|err| RuntimeError::Config(format!("resolve shared identity path: {err}")))?;
Ok(PathsConfig {
- profile: RadrootsPathProfile::InteractiveUser.to_string(),
+ profile: CLI_PROFILE.to_owned(),
+ allowed_profiles: CLI_ALLOWED_PROFILES
+ .iter()
+ .map(|value| (*value).to_owned())
+ .collect(),
+ app_namespace: app_namespace.relative_path().display().to_string(),
+ shared_accounts_namespace: shared_accounts_namespace
+ .relative_path()
+ .display()
+ .to_string(),
+ shared_identities_namespace: shared_identity_namespace
+ .relative_path()
+ .display()
+ .to_string(),
app_config_path: app_paths.config.join(DEFAULT_CONFIG_FILE_NAME),
workspace_config_path: current_dir.join(DEFAULT_WORKSPACE_CONFIG_PATH),
app_data_root: app_paths.data,
@@ -867,8 +918,9 @@ fn parse_bool_env(key: &str, value: &str) -> Result<bool, RuntimeError> {
#[cfg(test)]
mod tests {
use super::{
- AccountConfig, EnvFileValues, Environment, OutputConfig, OutputFormat, PathsConfig,
- RelayConfigSource, RelayPublishPolicy, RuntimeConfig, SignerBackend, Verbosity,
+ AccountConfig, AccountSecretContractConfig, EnvFileValues, Environment, OutputConfig,
+ OutputFormat, PathsConfig, RelayConfigSource, RelayPublishPolicy, RuntimeConfig,
+ SignerBackend, Verbosity,
parse_env_file_values,
};
use crate::cli::CliArgs;
@@ -968,6 +1020,10 @@ mod tests {
resolved.paths,
PathsConfig {
profile: "interactive_user".to_owned(),
+ allowed_profiles: vec!["interactive_user".to_owned()],
+ app_namespace: "apps/cli".to_owned(),
+ shared_accounts_namespace: "shared/accounts".to_owned(),
+ shared_identities_namespace: "shared/identities".to_owned(),
app_config_path: PathBuf::from(
"/home/tester/.radroots/config/apps/cli/config.toml"
),
@@ -1005,6 +1061,21 @@ mod tests {
secret_fallback: Some(RadrootsSecretBackend::EncryptedFile),
}
);
+ assert_eq!(
+ resolved.account_secret_contract,
+ 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(),
+ "plaintext_file".to_owned(),
+ ],
+ host_vault_policy: Some("desktop".to_owned()),
+ uses_protected_store: true,
+ }
+ );
assert_eq!(resolved.signer.backend, SignerBackend::Local);
assert_eq!(
resolved.relay.urls,
@@ -1261,6 +1332,9 @@ RADROOTS_CLI_LOGGING_STDOUT=false
resolved.paths.app_config_path,
PathBuf::from("/home/tester/.radroots/config/apps/cli/config.toml")
);
+ assert_eq!(resolved.paths.app_namespace, "apps/cli");
+ assert_eq!(resolved.paths.shared_accounts_namespace, "shared/accounts");
+ assert_eq!(resolved.paths.shared_identities_namespace, "shared/identities");
assert_eq!(
resolved.paths.workspace_config_path,
PathBuf::from("/workspaces/radroots-cli/.radroots/config.toml")
diff --git a/src/runtime/signer.rs b/src/runtime/signer.rs
@@ -1,5 +1,6 @@
use crate::domain::runtime::{IdentityPublicView, LocalSignerStatusView, SignerStatusView};
use crate::runtime::config::{RuntimeConfig, SignerBackend};
+use crate::runtime::accounts::SHARED_ACCOUNT_STORE_SOURCE;
use radroots_nostr_accounts::prelude::RadrootsNostrSelectedAccountStatus;
use radroots_nostr_signer::prelude::{
RadrootsNostrLocalSignerAvailability, RadrootsNostrLocalSignerCapability,
@@ -19,7 +20,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
return SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "unavailable".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: None,
reason: secret_backend.reason,
local: None,
@@ -31,7 +32,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
return SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "error".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: None,
reason: secret_backend.reason,
local: None,
@@ -60,7 +61,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "ready".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: Some(local.account_id.to_string()),
reason: None,
local: Some(LocalSignerStatusView {
@@ -79,7 +80,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
Ok(RadrootsNostrSelectedAccountStatus::PublicOnly { account }) => SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "unconfigured".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: Some(account.account_id.to_string()),
reason: Some(format!(
"local account {} is present but not secret-backed",
@@ -99,7 +100,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
Ok(RadrootsNostrSelectedAccountStatus::NotConfigured) => SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "unconfigured".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: None,
reason: Some(format!(
"no local account is selected in {}",
@@ -111,7 +112,7 @@ fn resolve_local_signer_status(config: &RuntimeConfig) -> SignerStatusView {
Err(error) => SignerStatusView {
mode: config.signer.backend.as_str().to_owned(),
state: "error".to_owned(),
- source: "local account store · local first".to_owned(),
+ source: SHARED_ACCOUNT_STORE_SOURCE.to_owned(),
account_id: None,
reason: Some(error.to_string()),
local: None,
diff --git a/tests/identity_commands.rs b/tests/identity_commands.rs
@@ -70,7 +70,7 @@ fn account_new_json_creates_local_account_store_entry() {
let stdout = String::from_utf8(output.stdout).expect("utf8 stdout");
let json: Value = serde_json::from_str(stdout.as_str()).expect("json output");
assert_eq!(json["state"], "created");
- assert_eq!(json["source"], "local account store · local first");
+ assert_eq!(json["source"], "shared account store · local first");
assert!(json["account"]["id"].is_string());
assert_eq!(json["account"]["signer"], "local");
assert_eq!(json["account"]["is_default"], true);
@@ -161,7 +161,7 @@ fn account_whoami_json_reads_selected_account() {
let stdout = String::from_utf8(output.stdout).expect("utf8 stdout");
let json: Value = serde_json::from_str(stdout.as_str()).expect("json output");
assert_eq!(json["state"], "ready");
- assert_eq!(json["source"], "local account store · local first");
+ assert_eq!(json["source"], "shared account store · local first");
assert!(json["account"]["id"].is_string());
assert_eq!(json["account"]["signer"], "local");
assert_eq!(json["account"]["is_default"], true);
diff --git a/tests/runtime_show.rs b/tests/runtime_show.rs
@@ -104,6 +104,10 @@ fn config_show_json_reports_default_bootstrap_state() {
assert_eq!(json["output"]["color"], true);
assert_eq!(json["output"]["dry_run"], false);
assert_eq!(json["paths"]["profile"], "interactive_user");
+ assert_eq!(json["paths"]["allowed_profiles"][0], "interactive_user");
+ assert_eq!(json["paths"]["app_namespace"], "apps/cli");
+ assert_eq!(json["paths"]["shared_accounts_namespace"], "shared/accounts");
+ assert_eq!(json["paths"]["shared_identities_namespace"], "shared/identities");
assert_eq!(
json["paths"]["app_config_path"],
config_root(dir.path())
@@ -178,6 +182,30 @@ fn config_show_json_reports_default_bootstrap_state() {
.to_string()
);
assert_eq!(
+ json["account"]["secret_backend"]["contract_default_backend"],
+ "host_vault"
+ );
+ assert_eq!(
+ json["account"]["secret_backend"]["contract_default_fallback"],
+ "encrypted_file"
+ );
+ assert_eq!(
+ json["account"]["secret_backend"]["allowed_backends"][0],
+ "host_vault"
+ );
+ assert_eq!(
+ json["account"]["secret_backend"]["allowed_backends"][1],
+ "encrypted_file"
+ );
+ assert_eq!(
+ json["account"]["secret_backend"]["host_vault_policy"],
+ "desktop"
+ );
+ assert_eq!(
+ json["account"]["secret_backend"]["uses_protected_store"],
+ true
+ );
+ assert_eq!(
json["account"]["secret_backend"]["configured_primary"],
"host_vault"
);
diff --git a/tests/signer_status.rs b/tests/signer_status.rs
@@ -66,7 +66,7 @@ fn signer_status_reports_local_ready_when_account_exists() {
let json: Value = serde_json::from_str(stdout.as_str()).expect("json output");
assert_eq!(json["mode"], "local");
assert_eq!(json["state"], "ready");
- assert_eq!(json["source"], "local account store · local first");
+ assert_eq!(json["source"], "shared account store · local first");
assert_eq!(json["account_id"], json["local"]["account_id"]);
assert_eq!(json["reason"], Value::Null);
assert_eq!(json["local"]["availability"], "secret_backed");