auths_sdk/workflows/
provision.rs1use std::collections::HashMap;
7use std::sync::Arc;
8
9use auths_core::signing::PassphraseProvider;
10use auths_core::storage::keychain::{KeyAlias, KeyStorage};
11use auths_id::{
12 identity::initialize::initialize_registry_identity,
13 ports::registry::RegistryBackend,
14 storage::identity::IdentityStorage,
15 witness_config::{WitnessConfig, WitnessPolicy, WitnessRef},
16};
17use serde::Deserialize;
18
19#[derive(Debug, Deserialize)]
21pub struct NodeConfig {
22 pub identity: IdentityConfig,
24 pub witness: Option<WitnessOverride>,
26}
27
28#[derive(Debug, Deserialize)]
30pub struct IdentityConfig {
31 #[serde(default = "default_key_alias")]
33 pub key_alias: String,
34
35 #[serde(default = "default_repo_path")]
37 pub repo_path: String,
38
39 #[serde(default = "default_preset")]
41 pub preset: String,
42
43 #[serde(default)]
45 pub metadata: HashMap<String, String>,
46}
47
48#[derive(Debug, Deserialize)]
50pub struct WitnessTomlEntry {
51 pub url: String,
53 pub aid: String,
55}
56
57#[derive(Debug, Deserialize)]
59pub struct WitnessOverride {
60 #[serde(default)]
62 pub witnesses: Vec<WitnessTomlEntry>,
63
64 #[serde(default = "default_threshold")]
66 pub threshold: usize,
67
68 #[serde(default = "default_timeout_ms")]
70 pub timeout_ms: u64,
71
72 #[serde(default = "default_policy")]
74 pub policy: String,
75}
76
77fn default_key_alias() -> String {
78 "main".to_string()
79}
80
81fn default_repo_path() -> String {
82 auths_core::paths::auths_home()
83 .map(|p| p.display().to_string())
84 .unwrap_or_else(|_| "~/.auths".to_string())
85}
86
87fn default_preset() -> String {
88 "default".to_string()
89}
90
91fn default_threshold() -> usize {
92 1
93}
94
95fn default_timeout_ms() -> u64 {
96 5000
97}
98
99fn default_policy() -> String {
100 "enforce".to_string()
101}
102
103#[derive(Debug)]
105pub struct ProvisionResult {
106 pub controller_did: String,
108 pub key_alias: KeyAlias,
110}
111
112#[derive(Debug, thiserror::Error)]
114pub enum ProvisionError {
115 #[error("failed to access platform keychain: {0}")]
117 KeychainUnavailable(String),
118
119 #[error("failed to initialize identity: {0}")]
121 IdentityInit(String),
122
123 #[error("identity already exists (use force=true to overwrite)")]
125 IdentityExists,
126}
127
128pub fn enforce_identity_state(
146 config: &NodeConfig,
147 force: bool,
148 passphrase_provider: &dyn PassphraseProvider,
149 keychain: &(dyn KeyStorage + Send + Sync),
150 registry: Arc<dyn RegistryBackend + Send + Sync>,
151 identity_storage: Arc<dyn IdentityStorage + Send + Sync>,
152) -> Result<Option<ProvisionResult>, ProvisionError> {
153 if identity_storage.load_identity().is_ok() && !force {
154 return Ok(None);
155 }
156
157 let witness_config = build_witness_config(config.witness.as_ref());
158
159 let alias = KeyAlias::new_unchecked(&config.identity.key_alias);
160 let (controller_did, key_alias) = initialize_registry_identity(
161 registry,
162 &alias,
163 passphrase_provider,
164 keychain,
165 witness_config.as_ref(),
166 auths_crypto::CurveType::default(),
167 )
168 .map_err(|e| ProvisionError::IdentityInit(e.to_string()))?;
169
170 Ok(Some(ProvisionResult {
171 controller_did: controller_did.into_inner(),
172 key_alias,
173 }))
174}
175
176fn build_witness_config(witness: Option<&WitnessOverride>) -> Option<WitnessConfig> {
177 let w = witness?;
178 if w.witnesses.is_empty() {
179 return None;
180 }
181 let policy = match w.policy.as_str() {
182 "warn" => WitnessPolicy::Warn,
183 "skip" => WitnessPolicy::Skip,
184 _ => WitnessPolicy::Enforce,
185 };
186 let witnesses: Vec<WitnessRef> = w
187 .witnesses
188 .iter()
189 .filter_map(|e| {
190 let url = e.url.parse().ok()?;
191 let aid = auths_keri::Prefix::new(e.aid.clone()).ok()?;
192 Some(WitnessRef {
193 url,
194 aid,
195 operator_info: None,
196 })
197 })
198 .collect();
199 if witnesses.is_empty() {
200 return None;
201 }
202 Some(WitnessConfig {
203 witnesses,
204 threshold: w.threshold,
205 timeout_ms: w.timeout_ms,
206 policy,
207 ..Default::default()
208 })
209}