Skip to main content

auths_sdk/
context.rs

1//! Runtime dependency container for auths-sdk operations.
2//!
3//! [`AuthsContext`] carries all injected infrastructure adapters. Config structs
4//! (e.g. [`crate::types::CreateDeveloperIdentityConfig`]) remain Plain Old Data with no
5//! trait objects.
6
7use std::sync::Arc;
8
9use auths_core::ports::clock::ClockProvider;
10use auths_core::ports::id::{SystemUuidProvider, UuidProvider};
11use auths_core::signing::PassphraseProvider;
12use auths_core::storage::keychain::KeyStorage;
13use auths_id::attestation::export::AttestationSink;
14use auths_id::ports::registry::RegistryBackend;
15use auths_id::storage::attestation::AttestationSource;
16use auths_id::storage::identity::IdentityStorage;
17
18use crate::ports::agent::{AgentSigningPort, NoopAgentProvider};
19
20/// Re-export the canonical `EventSink` trait from `auths-telemetry`.
21pub use auths_telemetry::EventSink;
22
23struct NoopSink;
24
25impl EventSink for NoopSink {
26    fn emit(&self, _payload: &str) {}
27    fn flush(&self) {}
28}
29
30struct NoopPassphraseProvider;
31
32impl PassphraseProvider for NoopPassphraseProvider {
33    fn get_passphrase(
34        &self,
35        _prompt: &str,
36    ) -> Result<zeroize::Zeroizing<String>, auths_core::AgentError> {
37        Err(auths_core::AgentError::SigningFailed(
38            "no passphrase provider configured — call .passphrase_provider(...) on AuthsContextBuilder".into(),
39        ))
40    }
41}
42
43/// All runtime dependencies for auths-sdk operations.
44///
45/// Construct via [`AuthsContext::builder()`]. Config structs carry serializable
46/// data; `AuthsContext` carries injected infrastructure adapters. This separation
47/// allows the SDK to operate as a headless, storage-agnostic library that can be
48/// embedded in cloud SaaS, WASM, or C-FFI runtimes without pulling in tokio,
49/// git2, or std::fs.
50///
51/// Usage:
52/// ```ignore
53/// use std::sync::Arc;
54/// use auths_sdk::context::AuthsContext;
55///
56/// let ctx = AuthsContext::builder()
57///     .registry(Arc::new(my_registry))
58///     .key_storage(Arc::new(my_keychain))
59///     .clock(Arc::new(SystemClock))
60///     .identity_storage(Arc::new(my_identity_storage))
61///     .attestation_sink(Arc::new(my_store.clone()))
62///     .attestation_source(Arc::new(my_store))
63///     .build();
64/// sdk::initialize(config, &ctx)?;
65/// ```
66pub struct AuthsContext {
67    /// Pre-initialized registry storage backend.
68    pub registry: Arc<dyn RegistryBackend + Send + Sync>,
69    /// Platform keychain or test fake for key material storage.
70    pub key_storage: Arc<dyn KeyStorage + Send + Sync>,
71    /// Wall-clock provider for deterministic testing.
72    pub clock: Arc<dyn ClockProvider + Send + Sync>,
73    /// Telemetry sink (defaults to `NoopSink` when not specified).
74    pub event_sink: Arc<dyn EventSink>,
75    /// Identity storage adapter (load/save managed identity).
76    pub identity_storage: Arc<dyn IdentityStorage + Send + Sync>,
77    /// Attestation sink for writing signed attestations.
78    pub attestation_sink: Arc<dyn AttestationSink + Send + Sync>,
79    /// Attestation source for reading existing attestations.
80    pub attestation_source: Arc<dyn AttestationSource + Send + Sync>,
81    /// Passphrase provider for key decryption during signing operations.
82    /// Defaults to `NoopPassphraseProvider` — set via `.passphrase_provider(...)` when
83    /// SDK functions need to sign with encrypted key material.
84    pub passphrase_provider: Arc<dyn PassphraseProvider + Send + Sync>,
85    /// UUID generator port. Defaults to [`SystemUuidProvider`] (random v4 UUIDs).
86    /// Override with a deterministic stub in tests.
87    pub uuid_provider: Arc<dyn UuidProvider + Send + Sync>,
88    /// Agent-based signing port for delegating operations to a running agent process.
89    /// Defaults to [`NoopAgentProvider`] (all operations return `Unavailable`)
90    /// when not called. Set this on Unix platforms where the auths-agent daemon
91    /// is available.
92    pub agent_signing: Arc<dyn AgentSigningPort + Send + Sync>,
93    /// Witness configuration for KEL event receipting.
94    /// When set and enabled, ixn events are submitted to witnesses after commit.
95    pub witness_config: Option<auths_id::witness_config::WitnessConfig>,
96    /// Path to the registry repository (needed for witness receipt storage).
97    pub repo_path: Option<std::path::PathBuf>,
98}
99
100impl AuthsContext {
101    /// Build witness params from the context's configuration.
102    ///
103    /// Returns `WitnessParams::Enabled` when both `witness_config` and `repo_path`
104    /// are set, `WitnessParams::Disabled` otherwise.
105    pub fn witness_params(&self) -> auths_id::witness_config::WitnessParams<'_> {
106        match (&self.witness_config, &self.repo_path) {
107            (Some(config), Some(path)) => auths_id::witness_config::WitnessParams::Enabled {
108                config,
109                repo_path: path,
110            },
111            _ => auths_id::witness_config::WitnessParams::Disabled,
112        }
113    }
114}
115
116impl AuthsContext {
117    /// Creates a builder for [`AuthsContext`].
118    ///
119    /// All six required fields (`registry`, `key_storage`, `clock`,
120    /// `identity_storage`, `attestation_sink`, `attestation_source`) are enforced
121    /// at compile time — the `build()` method is only available once all six are set.
122    ///
123    /// Usage:
124    /// ```ignore
125    /// let ctx = AuthsContext::builder()
126    ///     .registry(Arc::new(my_registry))
127    ///     .key_storage(Arc::new(my_keychain))
128    ///     .clock(Arc::new(SystemClock))
129    ///     .identity_storage(Arc::new(my_identity_storage))
130    ///     .attestation_sink(Arc::new(my_store.clone()))
131    ///     .attestation_source(Arc::new(my_store))
132    ///     .build();
133    /// ```
134    pub fn builder() -> AuthsContextBuilder<Missing, Missing, Missing, Missing, Missing, Missing> {
135        AuthsContextBuilder {
136            registry: Missing,
137            key_storage: Missing,
138            clock: Missing,
139            identity_storage: Missing,
140            attestation_sink: Missing,
141            attestation_source: Missing,
142            event_sink: None,
143            passphrase_provider: None,
144            uuid_provider: None,
145            agent_signing: None,
146            witness_config: None,
147            repo_path: None,
148        }
149    }
150}
151
152/// Typestate marker: required field not yet set.
153pub struct Missing;
154
155/// Typestate marker: required field has been set.
156pub struct Set<T>(T);
157
158/// Typestate builder for [`AuthsContext`].
159///
160/// Call [`AuthsContext::builder()`] to obtain an instance. The `build()` method
161/// is only available once all six required fields have been supplied — omitting any
162/// produces a compile-time error.
163pub struct AuthsContextBuilder<R, K, C, IS, AS, ASrc> {
164    registry: R,
165    key_storage: K,
166    clock: C,
167    identity_storage: IS,
168    attestation_sink: AS,
169    attestation_source: ASrc,
170    event_sink: Option<Arc<dyn EventSink>>,
171    passphrase_provider: Option<Arc<dyn PassphraseProvider + Send + Sync>>,
172    uuid_provider: Option<Arc<dyn UuidProvider + Send + Sync>>,
173    agent_signing: Option<Arc<dyn AgentSigningPort + Send + Sync>>,
174    witness_config: Option<auths_id::witness_config::WitnessConfig>,
175    repo_path: Option<std::path::PathBuf>,
176}
177
178// ── Required field setters (each transitions one typestate slot) ──────────────
179
180impl<K, C, IS, AS, ASrc> AuthsContextBuilder<Missing, K, C, IS, AS, ASrc> {
181    /// Set the registry storage backend.
182    ///
183    /// Args:
184    /// * `registry`: Pre-initialized registry backend.
185    ///
186    /// Usage:
187    /// ```ignore
188    /// builder.registry(Arc::new(my_git_backend))
189    /// ```
190    pub fn registry(
191        self,
192        registry: Arc<dyn RegistryBackend + Send + Sync>,
193    ) -> AuthsContextBuilder<Set<Arc<dyn RegistryBackend + Send + Sync>>, K, C, IS, AS, ASrc> {
194        AuthsContextBuilder {
195            registry: Set(registry),
196            key_storage: self.key_storage,
197            clock: self.clock,
198            identity_storage: self.identity_storage,
199            attestation_sink: self.attestation_sink,
200            attestation_source: self.attestation_source,
201            event_sink: self.event_sink,
202            passphrase_provider: self.passphrase_provider,
203            uuid_provider: self.uuid_provider,
204            agent_signing: self.agent_signing,
205            witness_config: self.witness_config,
206            repo_path: self.repo_path,
207        }
208    }
209}
210
211impl<R, C, IS, AS, ASrc> AuthsContextBuilder<R, Missing, C, IS, AS, ASrc> {
212    /// Set the key storage backend.
213    ///
214    /// Args:
215    /// * `key_storage`: Platform keychain or in-memory test fake.
216    ///
217    /// Usage:
218    /// ```ignore
219    /// builder.key_storage(Arc::new(my_keychain))
220    /// ```
221    pub fn key_storage(
222        self,
223        key_storage: Arc<dyn KeyStorage + Send + Sync>,
224    ) -> AuthsContextBuilder<R, Set<Arc<dyn KeyStorage + Send + Sync>>, C, IS, AS, ASrc> {
225        AuthsContextBuilder {
226            registry: self.registry,
227            key_storage: Set(key_storage),
228            clock: self.clock,
229            identity_storage: self.identity_storage,
230            attestation_sink: self.attestation_sink,
231            attestation_source: self.attestation_source,
232            event_sink: self.event_sink,
233            passphrase_provider: self.passphrase_provider,
234            uuid_provider: self.uuid_provider,
235            agent_signing: self.agent_signing,
236            witness_config: self.witness_config,
237            repo_path: self.repo_path,
238        }
239    }
240}
241
242impl<R, K, IS, AS, ASrc> AuthsContextBuilder<R, K, Missing, IS, AS, ASrc> {
243    /// Set the clock provider.
244    ///
245    /// Args:
246    /// * `clock`: Wall-clock implementation (`SystemClock` in production,
247    ///   `MockClock` in tests).
248    ///
249    /// Usage:
250    /// ```ignore
251    /// builder.clock(Arc::new(SystemClock))
252    /// ```
253    pub fn clock(
254        self,
255        clock: Arc<dyn ClockProvider + Send + Sync>,
256    ) -> AuthsContextBuilder<R, K, Set<Arc<dyn ClockProvider + Send + Sync>>, IS, AS, ASrc> {
257        AuthsContextBuilder {
258            registry: self.registry,
259            key_storage: self.key_storage,
260            clock: Set(clock),
261            identity_storage: self.identity_storage,
262            attestation_sink: self.attestation_sink,
263            attestation_source: self.attestation_source,
264            event_sink: self.event_sink,
265            passphrase_provider: self.passphrase_provider,
266            uuid_provider: self.uuid_provider,
267            agent_signing: self.agent_signing,
268            witness_config: self.witness_config,
269            repo_path: self.repo_path,
270        }
271    }
272}
273
274impl<R, K, C, AS, ASrc> AuthsContextBuilder<R, K, C, Missing, AS, ASrc> {
275    /// Set the identity storage adapter.
276    ///
277    /// Args:
278    /// * `storage`: Pre-initialized identity storage implementation.
279    ///
280    /// Usage:
281    /// ```ignore
282    /// builder.identity_storage(Arc::new(my_identity_storage))
283    /// ```
284    pub fn identity_storage(
285        self,
286        storage: Arc<dyn IdentityStorage + Send + Sync>,
287    ) -> AuthsContextBuilder<R, K, C, Set<Arc<dyn IdentityStorage + Send + Sync>>, AS, ASrc> {
288        AuthsContextBuilder {
289            registry: self.registry,
290            key_storage: self.key_storage,
291            clock: self.clock,
292            identity_storage: Set(storage),
293            attestation_sink: self.attestation_sink,
294            attestation_source: self.attestation_source,
295            event_sink: self.event_sink,
296            passphrase_provider: self.passphrase_provider,
297            uuid_provider: self.uuid_provider,
298            agent_signing: self.agent_signing,
299            witness_config: self.witness_config,
300            repo_path: self.repo_path,
301        }
302    }
303}
304
305impl<R, K, C, IS, ASrc> AuthsContextBuilder<R, K, C, IS, Missing, ASrc> {
306    /// Set the attestation sink adapter.
307    ///
308    /// Args:
309    /// * `sink`: Pre-initialized attestation sink implementation.
310    ///
311    /// Usage:
312    /// ```ignore
313    /// builder.attestation_sink(Arc::new(my_attestation_store))
314    /// ```
315    pub fn attestation_sink(
316        self,
317        sink: Arc<dyn AttestationSink + Send + Sync>,
318    ) -> AuthsContextBuilder<R, K, C, IS, Set<Arc<dyn AttestationSink + Send + Sync>>, ASrc> {
319        AuthsContextBuilder {
320            registry: self.registry,
321            key_storage: self.key_storage,
322            clock: self.clock,
323            identity_storage: self.identity_storage,
324            attestation_sink: Set(sink),
325            attestation_source: self.attestation_source,
326            event_sink: self.event_sink,
327            passphrase_provider: self.passphrase_provider,
328            uuid_provider: self.uuid_provider,
329            agent_signing: self.agent_signing,
330            witness_config: self.witness_config,
331            repo_path: self.repo_path,
332        }
333    }
334}
335
336impl<R, K, C, IS, AS> AuthsContextBuilder<R, K, C, IS, AS, Missing> {
337    /// Set the attestation source adapter.
338    ///
339    /// Args:
340    /// * `source`: Pre-initialized attestation source implementation.
341    ///
342    /// Usage:
343    /// ```ignore
344    /// builder.attestation_source(Arc::new(my_attestation_store))
345    /// ```
346    pub fn attestation_source(
347        self,
348        source: Arc<dyn AttestationSource + Send + Sync>,
349    ) -> AuthsContextBuilder<R, K, C, IS, AS, Set<Arc<dyn AttestationSource + Send + Sync>>> {
350        AuthsContextBuilder {
351            registry: self.registry,
352            key_storage: self.key_storage,
353            clock: self.clock,
354            identity_storage: self.identity_storage,
355            attestation_sink: self.attestation_sink,
356            attestation_source: Set(source),
357            event_sink: self.event_sink,
358            passphrase_provider: self.passphrase_provider,
359            uuid_provider: self.uuid_provider,
360            agent_signing: self.agent_signing,
361            witness_config: self.witness_config,
362            repo_path: self.repo_path,
363        }
364    }
365}
366
367// ── Optional field setters (available at any typestate, return Self) ──────────
368
369impl<R, K, C, IS, AS, ASrc> AuthsContextBuilder<R, K, C, IS, AS, ASrc> {
370    /// Set an optional event sink.
371    ///
372    /// Defaults to a no-op sink (all events discarded) when not called.
373    ///
374    /// Args:
375    /// * `sink`: Any type implementing [`EventSink`].
376    ///
377    /// Usage:
378    /// ```ignore
379    /// builder.event_sink(Arc::new(my_sink))
380    /// ```
381    pub fn event_sink(mut self, sink: Arc<dyn EventSink>) -> Self {
382        self.event_sink = Some(sink);
383        self
384    }
385
386    /// Set the passphrase provider for key decryption during signing operations.
387    ///
388    /// Defaults to a noop provider that returns an error. Set this when SDK
389    /// workflow functions will perform signing with encrypted key material.
390    ///
391    /// Args:
392    /// * `provider`: Any type implementing [`PassphraseProvider`].
393    ///
394    /// Usage:
395    /// ```ignore
396    /// builder.passphrase_provider(Arc::new(PrefilledPassphraseProvider::new(passphrase)))
397    /// ```
398    pub fn passphrase_provider(
399        mut self,
400        provider: Arc<dyn PassphraseProvider + Send + Sync>,
401    ) -> Self {
402        self.passphrase_provider = Some(provider);
403        self
404    }
405
406    /// Set the UUID provider.
407    ///
408    /// Defaults to [`SystemUuidProvider`] (random v4 UUIDs) when not called.
409    /// Override with a deterministic stub in tests.
410    ///
411    /// Args:
412    /// * `provider`: Any type implementing [`UuidProvider`].
413    ///
414    /// Usage:
415    /// ```ignore
416    /// builder.uuid_provider(Arc::new(my_uuid_stub))
417    /// ```
418    pub fn uuid_provider(mut self, provider: Arc<dyn UuidProvider + Send + Sync>) -> Self {
419        self.uuid_provider = Some(provider);
420        self
421    }
422
423    /// Set the agent signing port for delegating signing to a running agent process.
424    ///
425    /// Defaults to a noop provider (all operations return `Unavailable`)
426    /// when not called. Set this on Unix platforms where the auths-agent daemon
427    /// is available.
428    ///
429    /// Args:
430    /// * `provider`: Any type implementing [`AgentSigningPort`].
431    ///
432    /// Usage:
433    /// ```ignore
434    /// builder.agent_signing(Arc::new(CliAgentAdapter::new(socket_path)))
435    /// ```
436    pub fn agent_signing(mut self, provider: Arc<dyn AgentSigningPort + Send + Sync>) -> Self {
437        self.agent_signing = Some(provider);
438        self
439    }
440
441    /// Set witness configuration for KEL event receipting.
442    pub fn witness_config(mut self, config: auths_id::witness_config::WitnessConfig) -> Self {
443        self.witness_config = Some(config);
444        self
445    }
446
447    /// Set the repository path (needed for witness receipt storage).
448    pub fn repo_path(mut self, path: std::path::PathBuf) -> Self {
449        self.repo_path = Some(path);
450        self
451    }
452}
453
454// ── Infallible build — only available when all six required fields are set ────
455
456impl
457    AuthsContextBuilder<
458        Set<Arc<dyn RegistryBackend + Send + Sync>>,
459        Set<Arc<dyn KeyStorage + Send + Sync>>,
460        Set<Arc<dyn ClockProvider + Send + Sync>>,
461        Set<Arc<dyn IdentityStorage + Send + Sync>>,
462        Set<Arc<dyn AttestationSink + Send + Sync>>,
463        Set<Arc<dyn AttestationSource + Send + Sync>>,
464    >
465{
466    /// Build the [`AuthsContext`].
467    ///
468    /// Infallible — only callable once all six required fields are set.
469    /// Omitting any required field is a compile-time error.
470    ///
471    /// Usage:
472    /// ```ignore
473    /// let ctx = AuthsContext::builder()
474    ///     .registry(Arc::new(my_registry))
475    ///     .key_storage(Arc::new(my_keychain))
476    ///     .clock(Arc::new(SystemClock))
477    ///     .identity_storage(Arc::new(my_identity_storage))
478    ///     .attestation_sink(Arc::new(my_store.clone()))
479    ///     .attestation_source(Arc::new(my_store))
480    ///     .build();
481    /// ```
482    pub fn build(self) -> AuthsContext {
483        AuthsContext {
484            registry: self.registry.0,
485            key_storage: self.key_storage.0,
486            clock: self.clock.0,
487            identity_storage: self.identity_storage.0,
488            attestation_sink: self.attestation_sink.0,
489            attestation_source: self.attestation_source.0,
490            event_sink: self.event_sink.unwrap_or_else(|| Arc::new(NoopSink)),
491            passphrase_provider: self
492                .passphrase_provider
493                .unwrap_or_else(|| Arc::new(NoopPassphraseProvider)),
494            uuid_provider: self
495                .uuid_provider
496                .unwrap_or_else(|| Arc::new(SystemUuidProvider)),
497            agent_signing: self
498                .agent_signing
499                .unwrap_or_else(|| Arc::new(NoopAgentProvider)),
500            witness_config: self.witness_config,
501            repo_path: self.repo_path,
502        }
503    }
504}