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},
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 WitnessOverride {
51 #[serde(default)]
53 pub urls: Vec<String>,
54
55 #[serde(default = "default_threshold")]
57 pub threshold: usize,
58
59 #[serde(default = "default_timeout_ms")]
61 pub timeout_ms: u64,
62
63 #[serde(default = "default_policy")]
65 pub policy: String,
66}
67
68fn default_key_alias() -> String {
69 "main".to_string()
70}
71
72fn default_repo_path() -> String {
73 auths_core::paths::auths_home()
74 .map(|p| p.display().to_string())
75 .unwrap_or_else(|_| "~/.auths".to_string())
76}
77
78fn default_preset() -> String {
79 "default".to_string()
80}
81
82fn default_threshold() -> usize {
83 1
84}
85
86fn default_timeout_ms() -> u64 {
87 5000
88}
89
90fn default_policy() -> String {
91 "enforce".to_string()
92}
93
94#[derive(Debug)]
96pub struct ProvisionResult {
97 pub controller_did: String,
99 pub key_alias: KeyAlias,
101}
102
103#[derive(Debug, thiserror::Error)]
105pub enum ProvisionError {
106 #[error("failed to access platform keychain: {0}")]
108 KeychainUnavailable(String),
109
110 #[error("failed to initialize identity: {0}")]
112 IdentityInit(String),
113
114 #[error("identity already exists (use force=true to overwrite)")]
116 IdentityExists,
117}
118
119pub fn enforce_identity_state(
137 config: &NodeConfig,
138 force: bool,
139 passphrase_provider: &dyn PassphraseProvider,
140 keychain: &(dyn KeyStorage + Send + Sync),
141 registry: Arc<dyn RegistryBackend + Send + Sync>,
142 identity_storage: Arc<dyn IdentityStorage + Send + Sync>,
143) -> Result<Option<ProvisionResult>, ProvisionError> {
144 if identity_storage.load_identity().is_ok() && !force {
145 return Ok(None);
146 }
147
148 let witness_config = build_witness_config(config.witness.as_ref());
149
150 let alias = KeyAlias::new_unchecked(&config.identity.key_alias);
151 let (controller_did, key_alias) = initialize_registry_identity(
152 registry,
153 &alias,
154 passphrase_provider,
155 keychain,
156 witness_config.as_ref(),
157 )
158 .map_err(|e| ProvisionError::IdentityInit(e.to_string()))?;
159
160 Ok(Some(ProvisionResult {
161 controller_did: controller_did.into_inner(),
162 key_alias,
163 }))
164}
165
166fn build_witness_config(witness: Option<&WitnessOverride>) -> Option<WitnessConfig> {
167 let w = witness?;
168 if w.urls.is_empty() {
169 return None;
170 }
171 let policy = match w.policy.as_str() {
172 "warn" => WitnessPolicy::Warn,
173 "skip" => WitnessPolicy::Skip,
174 _ => WitnessPolicy::Enforce,
175 };
176 Some(WitnessConfig {
177 witness_urls: w.urls.iter().filter_map(|u| u.parse().ok()).collect(),
178 threshold: w.threshold,
179 timeout_ms: w.timeout_ms,
180 policy,
181 })
182}