auths_id/
agent_identity.rs1use std::path::PathBuf;
11
12use auths_core::storage::keychain::IdentityDID;
13
14#[derive(Debug, Clone)]
16pub enum AgentStorageMode {
17 Persistent {
20 repo_path: Option<PathBuf>,
22 },
23 InMemory,
26}
27
28#[derive(Debug, Clone)]
30pub struct AgentProvisioningConfig {
31 pub agent_name: String,
33 pub capabilities: Vec<String>,
35 pub expires_in: Option<u64>,
37 pub delegated_by: Option<IdentityDID>,
39 pub storage_mode: AgentStorageMode,
41}
42
43pub fn format_agent_toml(did: &str, key_alias: &str, config: &AgentProvisioningConfig) -> String {
55 let caps = config
56 .capabilities
57 .iter()
58 .map(|c| format!("\"{}\"", c))
59 .collect::<Vec<_>>()
60 .join(", ");
61
62 let mut out = format!(
63 "# Auths Agent Configuration\n\
64 # An agent is a KERI delegated identifier (dip) — create with `auths id agent add`\n\n\
65 [agent]\n\
66 name = \"{}\"\n\
67 did = \"{}\"\n\
68 key_alias = \"{}\"\n\
69 signer_type = \"Agent\"\n",
70 config.agent_name, did, key_alias,
71 );
72
73 if let Some(ref delegator) = config.delegated_by {
74 out.push_str(&format!("delegated_by = \"{}\"\n", delegator));
75 }
76
77 out.push_str(&format!("\n[capabilities]\ngranted = [{}]\n", caps));
78
79 if let Some(secs) = config.expires_in {
80 out.push_str(&format!("\n[expiry]\nexpires_in = {}\n", secs));
81 }
82
83 out
84}
85
86#[cfg(test)]
87#[allow(clippy::disallowed_methods)] mod tests {
89 use super::*;
90
91 #[test]
92 fn format_agent_toml_with_all_fields() {
93 let config = AgentProvisioningConfig {
94 agent_name: "ci-bot".to_string(),
95 capabilities: vec!["sign_commit".to_string(), "pr:create".to_string()],
96 expires_in: Some(86400),
97 delegated_by: Some(IdentityDID::new_unchecked("did:keri:Eabc123")),
98 storage_mode: AgentStorageMode::Persistent { repo_path: None },
99 };
100 let toml = format_agent_toml("did:keri:Eagent", "agent-key", &config);
101 assert!(toml.contains("name = \"ci-bot\""));
102 assert!(toml.contains("did = \"did:keri:Eagent\""));
103 assert!(toml.contains("delegated_by = \"did:keri:Eabc123\""));
104 assert!(toml.contains("\"sign_commit\", \"pr:create\""));
105 assert!(toml.contains("expires_in = 86400"));
106 }
107
108 #[test]
109 fn format_agent_toml_minimal() {
110 let config = AgentProvisioningConfig {
111 agent_name: "solo".to_string(),
112 capabilities: vec![],
113 expires_in: None,
114 delegated_by: None,
115 storage_mode: AgentStorageMode::InMemory,
116 };
117 let toml = format_agent_toml("did:keri:E1", "k", &config);
118 assert!(!toml.contains("delegated_by"));
119 assert!(!toml.contains("[expiry]"));
120 }
121
122 #[test]
123 fn init_dryrun_shows_delegated_agent() {
124 let config = AgentProvisioningConfig {
128 agent_name: "deploy-bot".to_string(),
129 capabilities: vec!["sign_commit".to_string()],
130 expires_in: None,
131 delegated_by: Some(IdentityDID::new_unchecked("did:keri:Eroot")),
132 storage_mode: AgentStorageMode::Persistent { repo_path: None },
133 };
134 let toml = format_agent_toml("did:keri:E<pending>", "agent-key", &config);
135 assert!(
136 toml.contains("delegated identifier"),
137 "dry-run must frame the agent as a delegated identifier"
138 );
139 assert!(
140 toml.contains("delegated_by = \"did:keri:Eroot\""),
141 "dry-run must name the delegating root"
142 );
143 }
144}