Skip to main content

auths_sdk/domains/identity/
types.rs

1use auths_core::storage::keychain::{IdentityDID, KeyAlias};
2use auths_verifier::Capability;
3use auths_verifier::types::CanonicalDid;
4use std::path::PathBuf;
5
6use crate::domains::ci::types::{CiEnvironment, CiIdentityConfig};
7
8/// Policy for handling an existing identity during developer setup.
9///
10/// Replaces interactive CLI prompts with a typed enum that headless consumers
11/// can set programmatically.
12///
13/// Usage:
14/// ```ignore
15/// let config = CreateDeveloperIdentityConfig::builder("my-key")
16///     .with_conflict_policy(IdentityConflictPolicy::ReuseExisting)
17///     .build();
18/// ```
19#[derive(Debug, Clone, Default)]
20pub enum IdentityConflictPolicy {
21    /// Return an error if an identity already exists (default).
22    #[default]
23    Error,
24    /// Reuse the existing identity silently.
25    ReuseExisting,
26    /// Overwrite the existing identity with a new one.
27    ForceNew,
28}
29
30/// Configuration for provisioning a new developer identity.
31///
32/// Use [`CreateDeveloperIdentityConfigBuilder`] to construct this with optional fields.
33/// The registry backend is injected via [`crate::context::AuthsContext`] — this
34/// struct carries only serializable configuration values.
35///
36/// Args:
37/// * `key_alias`: Human-readable name for the key (e.g. "work-laptop").
38///
39/// Usage:
40/// ```ignore
41/// let config = CreateDeveloperIdentityConfig::builder("work-laptop")
42///     .with_platform(crate::types::PlatformVerification::GitHub { access_token: "ghp_abc".into() })
43///     .with_git_signing_scope(crate::types::GitSigningScope::Global)
44///     .build();
45/// ```
46#[derive(Debug)]
47pub struct CreateDeveloperIdentityConfig {
48    /// Human-readable name for the key (e.g. "work-laptop").
49    pub key_alias: KeyAlias,
50    /// Optional platform verification configuration.
51    pub platform: Option<crate::types::PlatformVerification>,
52    /// How to configure git commit signing.
53    pub git_signing_scope: crate::types::GitSigningScope,
54    /// Whether to register the identity on a remote registry.
55    pub register_on_registry: bool,
56    /// Remote registry URL, if registration is enabled.
57    pub registry_url: Option<String>,
58    /// What to do if an identity already exists.
59    pub conflict_policy: IdentityConflictPolicy,
60    /// Optional KERI witness configuration for the inception event.
61    pub witness_config: Option<auths_id::witness_config::WitnessConfig>,
62    /// Optional JSON metadata to attach to the identity.
63    pub metadata: Option<serde_json::Value>,
64    /// Path to the `auths-sign` binary, required when git signing is configured.
65    /// The CLI resolves this via `which::which("auths-sign")`.
66    pub sign_binary_path: Option<PathBuf>,
67    /// Override the default curve for key generation. Defaults to `CurveType::default()` (P-256).
68    pub curve: auths_crypto::CurveType,
69}
70
71impl CreateDeveloperIdentityConfig {
72    /// Creates a builder with the required key alias.
73    ///
74    /// Args:
75    /// * `key_alias`: Human-readable name for the key.
76    ///
77    /// Usage:
78    /// ```ignore
79    /// let builder = CreateDeveloperIdentityConfig::builder("my-key");
80    /// ```
81    pub fn builder(key_alias: KeyAlias) -> CreateDeveloperIdentityConfigBuilder {
82        CreateDeveloperIdentityConfigBuilder {
83            key_alias,
84            platform: None,
85            git_signing_scope: crate::types::GitSigningScope::Global,
86            register_on_registry: false,
87            registry_url: None,
88            conflict_policy: IdentityConflictPolicy::Error,
89            witness_config: None,
90            metadata: None,
91            sign_binary_path: None,
92            curve: auths_crypto::CurveType::default(),
93        }
94    }
95}
96
97/// Builder for [`CreateDeveloperIdentityConfig`].
98#[derive(Debug)]
99pub struct CreateDeveloperIdentityConfigBuilder {
100    key_alias: KeyAlias,
101    platform: Option<crate::types::PlatformVerification>,
102    git_signing_scope: crate::types::GitSigningScope,
103    register_on_registry: bool,
104    registry_url: Option<String>,
105    conflict_policy: IdentityConflictPolicy,
106    witness_config: Option<auths_id::witness_config::WitnessConfig>,
107    metadata: Option<serde_json::Value>,
108    sign_binary_path: Option<PathBuf>,
109    curve: auths_crypto::CurveType,
110}
111
112impl CreateDeveloperIdentityConfigBuilder {
113    /// Configures platform identity verification for the new identity.
114    ///
115    /// The SDK never opens a browser or runs OAuth flows. The caller must
116    /// obtain the access token beforehand and pass it here.
117    ///
118    /// Args:
119    /// * `platform`: The platform and access token to verify against.
120    ///
121    /// Usage:
122    /// ```ignore
123    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
124    ///     .with_platform(crate::types::PlatformVerification::GitHub {
125    ///         access_token: "ghp_abc123".into(),
126    ///     })
127    ///     .build();
128    /// ```
129    pub fn with_platform(mut self, platform: crate::types::PlatformVerification) -> Self {
130        self.platform = Some(platform);
131        self
132    }
133
134    /// Sets the Git signing scope (local, global, or skip).
135    ///
136    /// Args:
137    /// * `scope`: How to configure `git config` for commit signing.
138    ///
139    /// Usage:
140    /// ```ignore
141    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
142    ///     .with_git_signing_scope(crate::types::GitSigningScope::Local {
143    ///         repo_path: PathBuf::from("/path/to/repo"),
144    ///     })
145    ///     .build();
146    /// ```
147    pub fn with_git_signing_scope(mut self, scope: crate::types::GitSigningScope) -> Self {
148        self.git_signing_scope = scope;
149        self
150    }
151
152    /// Enables registration on a remote auths registry after identity creation.
153    ///
154    /// Args:
155    /// * `url`: The registry URL to register with.
156    ///
157    /// Usage:
158    /// ```ignore
159    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
160    ///     .with_registration("https://registry.auths.dev")
161    ///     .build();
162    /// ```
163    pub fn with_registration(mut self, url: impl Into<String>) -> Self {
164        self.register_on_registry = true;
165        self.registry_url = Some(url.into());
166        self
167    }
168
169    /// Sets the policy for handling an existing identity at the registry path.
170    ///
171    /// Args:
172    /// * `policy`: What to do if an identity already exists.
173    ///
174    /// Usage:
175    /// ```ignore
176    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
177    ///     .with_conflict_policy(IdentityConflictPolicy::ReuseExisting)
178    ///     .build();
179    /// ```
180    pub fn with_conflict_policy(mut self, policy: IdentityConflictPolicy) -> Self {
181        self.conflict_policy = policy;
182        self
183    }
184
185    /// Sets the witness configuration for the KERI inception event.
186    ///
187    /// Args:
188    /// * `config`: Witness endpoints and thresholds.
189    ///
190    /// Usage:
191    /// ```ignore
192    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
193    ///     .with_witness_config(witness_cfg)
194    ///     .build();
195    /// ```
196    pub fn with_witness_config(mut self, config: auths_id::witness_config::WitnessConfig) -> Self {
197        self.witness_config = Some(config);
198        self
199    }
200
201    /// Attaches custom metadata to the identity (e.g. `created_at`, `setup_profile`).
202    ///
203    /// Args:
204    /// * `metadata`: Arbitrary JSON metadata.
205    ///
206    /// Usage:
207    /// ```ignore
208    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
209    ///     .with_metadata(serde_json::json!({"team": "platform"}))
210    ///     .build();
211    /// ```
212    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
213        self.metadata = Some(metadata);
214        self
215    }
216
217    /// Sets the path to the `auths-sign` binary used for git signing configuration.
218    ///
219    /// Required when `git_signing_scope` is not `Skip`. The CLI resolves this via
220    /// `which::which("auths-sign")`.
221    ///
222    /// Args:
223    /// * `path`: Absolute path to the `auths-sign` binary.
224    ///
225    /// Usage:
226    /// ```ignore
227    /// let config = CreateDeveloperIdentityConfig::builder("my-key")
228    ///     .with_sign_binary_path(PathBuf::from("/usr/local/bin/auths-sign"))
229    ///     .build();
230    /// ```
231    pub fn with_sign_binary_path(mut self, path: PathBuf) -> Self {
232        self.sign_binary_path = Some(path);
233        self
234    }
235
236    /// Override the curve for key generation (default: P-256).
237    pub fn with_curve(mut self, curve: auths_crypto::CurveType) -> Self {
238        self.curve = curve;
239        self
240    }
241
242    /// Builds the final [`CreateDeveloperIdentityConfig`].
243    ///
244    /// Usage:
245    /// ```ignore
246    /// let config = CreateDeveloperIdentityConfig::builder("my-key").build();
247    /// ```
248    pub fn build(self) -> CreateDeveloperIdentityConfig {
249        CreateDeveloperIdentityConfig {
250            key_alias: self.key_alias,
251            platform: self.platform,
252            git_signing_scope: self.git_signing_scope,
253            register_on_registry: self.register_on_registry,
254            registry_url: self.registry_url,
255            conflict_policy: self.conflict_policy,
256            witness_config: self.witness_config,
257            metadata: self.metadata,
258            sign_binary_path: self.sign_binary_path,
259            curve: self.curve,
260        }
261    }
262}
263
264/// Selects which identity persona to provision via [`crate::setup::initialize`].
265///
266/// Usage:
267/// ```ignore
268/// // Developer preset (platform keychain, git signing):
269/// let config = IdentityConfig::developer(KeyAlias::new_unchecked("work-laptop"));
270///
271/// // CI preset (memory keychain, ephemeral):
272/// let config = IdentityConfig::ci(PathBuf::from("/tmp/.auths-ci"));
273///
274/// // Agent preset (minimal capabilities, long-lived):
275/// let config = IdentityConfig::agent(KeyAlias::new_unchecked("deploy-bot"), registry_path);
276///
277/// // Custom configuration:
278/// let config = IdentityConfig::Developer(
279///     CreateDeveloperIdentityConfig::builder(alias)
280///         .with_platform(crate::types::PlatformVerification::GitHub { access_token: token })
281///         .build()
282/// );
283/// ```
284#[derive(Debug)]
285pub enum IdentityConfig {
286    /// Full local developer setup: platform keychain, git signing, passphrase.
287    Developer(CreateDeveloperIdentityConfig),
288    /// Ephemeral CI setup: memory keychain, no git signing.
289    Ci(CiIdentityConfig),
290    /// Agent setup: file keychain, scoped capabilities, long-lived.
291    Agent(CreateAgentIdentityConfig),
292}
293
294impl IdentityConfig {
295    /// Create a developer identity config with sensible defaults.
296    ///
297    /// Args:
298    /// * `alias`: Human-readable key alias (e.g. `"work-laptop"`).
299    ///
300    /// Usage:
301    /// ```ignore
302    /// let config = IdentityConfig::developer(KeyAlias::new_unchecked("work-laptop"));
303    /// ```
304    pub fn developer(alias: KeyAlias) -> Self {
305        Self::Developer(CreateDeveloperIdentityConfig::builder(alias).build())
306    }
307
308    /// Create a CI/ephemeral identity config.
309    ///
310    /// Args:
311    /// * `registry_path`: Path to the ephemeral auths registry directory.
312    ///
313    /// Usage:
314    /// ```ignore
315    /// let config = IdentityConfig::ci(PathBuf::from("/tmp/.auths-ci"));
316    /// ```
317    pub fn ci(registry_path: impl Into<PathBuf>) -> Self {
318        let registry_path = registry_path.into();
319        let keychain_file = registry_path.join("keys.enc");
320        Self::Ci(CiIdentityConfig {
321            ci_environment: CiEnvironment::Unknown,
322            registry_path,
323            keychain_file,
324            passphrase: crate::domains::ci::types::DEFAULT_CI_PASSPHRASE.to_string(),
325        })
326    }
327
328    /// Create an agent identity config with sensible defaults.
329    ///
330    /// Args:
331    /// * `alias`: Human-readable agent name.
332    /// * `registry_path`: Path to the auths registry directory.
333    ///
334    /// Usage:
335    /// ```ignore
336    /// let config = IdentityConfig::agent(KeyAlias::new_unchecked("deploy-bot"), registry_path);
337    /// ```
338    pub fn agent(alias: KeyAlias, registry_path: impl Into<PathBuf>) -> Self {
339        Self::Agent(CreateAgentIdentityConfig::builder(alias, registry_path).build())
340    }
341}
342
343/// Configuration for agent identity.
344///
345/// Use [`CreateAgentIdentityConfigBuilder`] to construct this with optional fields.
346///
347/// Args:
348/// * `alias`: Human-readable name for the agent.
349/// * `parent_identity_did`: The DID of the identity that owns this agent.
350/// * `registry_path`: Path to the auths registry.
351///
352/// Usage:
353/// ```ignore
354/// let config = CreateAgentIdentityConfig::builder("deploy-bot", "did:keri:abc123", path)
355///     .with_capabilities(vec!["sign-commit".into()])
356///     .build();
357/// ```
358#[derive(Debug)]
359pub struct CreateAgentIdentityConfig {
360    /// Human-readable name for the agent.
361    pub alias: KeyAlias,
362    /// Capabilities granted to the agent.
363    pub capabilities: Vec<Capability>,
364    /// DID of the parent identity that delegates authority.
365    pub parent_identity_did: Option<String>,
366    /// Path to the auths registry directory.
367    pub registry_path: PathBuf,
368    /// Duration in seconds until expiration (per RFC 6749).
369    pub expires_in: Option<u64>,
370    /// If true, construct state without persisting.
371    pub dry_run: bool,
372}
373
374impl CreateAgentIdentityConfig {
375    /// Creates a builder with alias and registry path.
376    ///
377    /// Args:
378    /// * `alias`: Human-readable name for the agent.
379    /// * `registry_path`: Path to the auths registry directory.
380    ///
381    /// Usage:
382    /// ```ignore
383    /// let builder = CreateAgentIdentityConfig::builder("deploy-bot", path);
384    /// ```
385    pub fn builder(
386        alias: KeyAlias,
387        registry_path: impl Into<PathBuf>,
388    ) -> CreateAgentIdentityConfigBuilder {
389        CreateAgentIdentityConfigBuilder {
390            alias,
391            capabilities: Vec::new(),
392            parent_identity_did: None,
393            registry_path: registry_path.into(),
394            expires_in: None,
395            dry_run: false,
396        }
397    }
398}
399
400/// Builder for [`CreateAgentIdentityConfig`].
401#[derive(Debug)]
402pub struct CreateAgentIdentityConfigBuilder {
403    alias: KeyAlias,
404    capabilities: Vec<Capability>,
405    parent_identity_did: Option<String>,
406    registry_path: PathBuf,
407    expires_in: Option<u64>,
408    dry_run: bool,
409}
410
411impl CreateAgentIdentityConfigBuilder {
412    /// Sets the parent identity DID that delegates authority to this agent.
413    ///
414    /// Args:
415    /// * `did`: The DID of the owning identity.
416    ///
417    /// Usage:
418    /// ```ignore
419    /// let config = CreateAgentIdentityConfig::builder("bot", path)
420    ///     .with_parent_did("did:keri:abc123")
421    ///     .build();
422    /// ```
423    pub fn with_parent_did(mut self, did: impl Into<String>) -> Self {
424        self.parent_identity_did = Some(did.into());
425        self
426    }
427
428    /// Sets the capabilities granted to the agent.
429    ///
430    /// Args:
431    /// * `capabilities`: List of capabilities.
432    ///
433    /// Usage:
434    /// ```ignore
435    /// let config = CreateAgentIdentityConfig::builder("bot", path)
436    ///     .with_capabilities(vec![Capability::sign_commit()])
437    ///     .build();
438    /// ```
439    pub fn with_capabilities(mut self, capabilities: Vec<Capability>) -> Self {
440        self.capabilities = capabilities;
441        self
442    }
443
444    /// Sets the agent key expiration time in seconds.
445    ///
446    /// Args:
447    /// * `secs`: Seconds until the agent identity expires.
448    ///
449    /// Usage:
450    /// ```ignore
451    /// let config = CreateAgentIdentityConfig::builder("bot", path)
452    ///     .with_expiry(86400) // 24 hours
453    ///     .build();
454    /// ```
455    pub fn with_expiry(mut self, secs: u64) -> Self {
456        self.expires_in = Some(secs);
457        self
458    }
459
460    /// Enables dry-run mode (constructs state without persisting).
461    ///
462    /// Usage:
463    /// ```ignore
464    /// let config = CreateAgentIdentityConfig::builder("bot", path)
465    ///     .dry_run(true)
466    ///     .build();
467    /// ```
468    pub fn dry_run(mut self, enabled: bool) -> Self {
469        self.dry_run = enabled;
470        self
471    }
472
473    /// Builds the final [`CreateAgentIdentityConfig`].
474    ///
475    /// Usage:
476    /// ```ignore
477    /// let config = CreateAgentIdentityConfig::builder("bot", path).build();
478    /// ```
479    pub fn build(self) -> CreateAgentIdentityConfig {
480        CreateAgentIdentityConfig {
481            alias: self.alias,
482            capabilities: self.capabilities,
483            parent_identity_did: self.parent_identity_did,
484            registry_path: self.registry_path,
485            expires_in: self.expires_in,
486            dry_run: self.dry_run,
487        }
488    }
489}
490
491/// Configuration for rotating an identity's signing keys.
492///
493/// Args:
494/// * `repo_path`: Path to the auths registry (typically `~/.auths`).
495/// * `identity_key_alias`: Keychain alias of the current signing key.
496///   If `None`, the first non-next alias for the identity is used.
497/// * `next_key_alias`: Keychain alias to store the new key under.
498///   Defaults to `<identity_key_alias>-rotated-<timestamp>`.
499///
500/// Usage:
501/// ```ignore
502/// let config = IdentityRotationConfig {
503///     repo_path: PathBuf::from("/home/user/.auths"),
504///     identity_key_alias: Some("main".into()),
505///     next_key_alias: None,
506/// };
507/// ```
508#[derive(Debug)]
509pub struct IdentityRotationConfig {
510    /// Path to the auths registry (typically `~/.auths`).
511    pub repo_path: PathBuf,
512    /// Keychain alias of the current signing key (auto-detected if `None`).
513    pub identity_key_alias: Option<KeyAlias>,
514    /// Keychain alias for the new rotated key (auto-generated if `None`).
515    pub next_key_alias: Option<KeyAlias>,
516}
517
518// Result types for identity operations
519
520/// Outcome of a successful developer identity setup.
521///
522/// Usage:
523/// ```ignore
524/// let result = initialize(IdentityConfig::developer(alias), &ctx, keychain, &signer, &provider, git_cfg)?;
525/// if let InitializeResult::Developer(r) = result {
526///     println!("Created identity: {}", r.identity_did);
527/// }
528/// ```
529#[derive(Debug, Clone)]
530pub struct DeveloperIdentityResult {
531    /// The controller DID of the created identity.
532    pub identity_did: IdentityDID,
533    /// The device DID bound to this identity.
534    pub device_did: CanonicalDid,
535    /// The keychain alias used for the signing key.
536    pub key_alias: KeyAlias,
537    /// Result of platform verification, if performed.
538    pub platform_claim: Option<crate::result::PlatformClaimResult>,
539    /// Whether git commit signing was configured.
540    pub git_signing_configured: bool,
541    /// Result of registry registration, if performed.
542    pub registered: Option<RegistrationOutcome>,
543}
544
545/// Outcome of a successful CI/ephemeral identity setup.
546///
547/// Usage:
548/// ```ignore
549/// let result = initialize(IdentityConfig::ci(registry_path), &ctx, keychain, &signer, &provider, None)?;
550/// if let InitializeResult::Ci(r) = result {
551///     for line in &r.env_block { println!("{line}"); }
552/// }
553/// ```
554#[derive(Debug, Clone)]
555pub struct CiIdentityResult {
556    /// The controller DID of the CI identity.
557    pub identity_did: IdentityDID,
558    /// The device DID bound to this CI identity.
559    pub device_did: CanonicalDid,
560    /// Shell `export` lines for configuring CI environment variables.
561    pub env_block: Vec<String>,
562}
563
564/// Outcome of a successful agent identity setup.
565///
566/// Usage:
567/// ```ignore
568/// let result = initialize(IdentityConfig::agent(alias, path), &ctx, keychain, &signer, &provider, None)?;
569/// if let InitializeResult::Agent(r) = result {
570///     println!("Agent {:?} delegated by {:?}", r.agent_did, r.parent_did);
571/// }
572/// ```
573#[derive(Debug, Clone)]
574pub struct AgentIdentityResult {
575    /// The DID of the newly created agent identity (None for dry-run proposals).
576    pub agent_did: Option<IdentityDID>,
577    /// The DID of the parent identity that delegated authority (None if no parent).
578    pub parent_did: Option<IdentityDID>,
579    /// The capabilities granted to the agent.
580    pub capabilities: Vec<Capability>,
581}
582
583/// Outcome of [`crate::setup::initialize`] — one variant per identity persona.
584///
585/// Usage:
586/// ```ignore
587/// match initialize(config, &ctx, keychain, &signer, &provider, git_cfg)? {
588///     InitializeResult::Developer(r) => display_developer_result(r),
589///     InitializeResult::Ci(r) => display_ci_result(r),
590///     InitializeResult::Agent(r) => display_agent_result(r),
591/// }
592/// ```
593#[derive(Debug, Clone)]
594pub enum InitializeResult {
595    /// Developer identity result.
596    Developer(DeveloperIdentityResult),
597    /// CI/ephemeral identity result.
598    Ci(CiIdentityResult),
599    /// Agent identity result.
600    Agent(AgentIdentityResult),
601}
602
603/// Outcome of a successful identity rotation.
604///
605/// Usage:
606/// ```ignore
607/// let result: IdentityRotationResult = rotate_identity(config, provider)?;
608/// println!("Rotated DID: {}", result.controller_did);
609/// println!("New key:  {}...", result.new_key_fingerprint);
610/// println!("Old key:  {}...", result.previous_key_fingerprint);
611/// ```
612#[derive(Debug, Clone)]
613pub struct IdentityRotationResult {
614    /// The controller DID of the rotated identity.
615    pub controller_did: IdentityDID,
616    /// Hex-encoded fingerprint of the new signing key.
617    pub new_key_fingerprint: String,
618    /// Hex-encoded fingerprint of the previous signing key.
619    pub previous_key_fingerprint: String,
620    /// KERI sequence number after this rotation event.
621    pub sequence: u128,
622    /// The alias the rotated-in signing key is stored under. Stays equal to the
623    /// pre-rotation alias by default (stable alias); differs only when the
624    /// caller passed an explicit `next_key_alias`.
625    pub new_key_alias: String,
626}
627
628/// Outcome of a successful registry registration.
629///
630/// Usage:
631/// ```ignore
632/// if let Some(reg) = result.registered {
633///     println!("Registered {} at {}", reg.did, reg.registry);
634/// }
635/// ```
636#[derive(Debug, Clone)]
637pub struct RegistrationOutcome {
638    /// The DID returned by the registry (e.g. `did:keri:EABC...`).
639    pub did: IdentityDID,
640    /// The registry URL where the identity was registered.
641    pub registry: String,
642    /// Number of platform claims indexed by the registry.
643    pub platform_claims_indexed: usize,
644}