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    /// Optional revocation-freshness source. When set, a credential presentation is refused if the
99    /// locally-held copy of the issuer's logs is too stale to trust its revocation status (a `rev`
100    /// may have landed unseen). Default `None` — no staleness gate. Set via
101    /// [`AuthsContext::with_revocation_freshness`].
102    pub revocation_freshness:
103        Option<Arc<dyn crate::domains::credentials::RevocationFreshnessSource>>,
104}
105
106impl AuthsContext {
107    /// Set the revocation-freshness source (a fluent post-build override). When set, a credential
108    /// presentation is refused if the issuer's locally-held logs are too stale to trust their
109    /// revocation status.
110    ///
111    /// Args:
112    /// * `source`: the per-delegator staleness oracle (e.g. a `PolicyBoundRefresh`).
113    pub fn with_revocation_freshness(
114        mut self,
115        source: Arc<dyn crate::domains::credentials::RevocationFreshnessSource>,
116    ) -> Self {
117        self.revocation_freshness = Some(source);
118        self
119    }
120
121    /// Build witness params from the context's configuration.
122    ///
123    /// Returns `WitnessParams::Enabled` when both `witness_config` and `repo_path`
124    /// are set, `WitnessParams::Disabled` otherwise.
125    pub fn witness_params(&self) -> auths_id::witness_config::WitnessParams<'_> {
126        match (&self.witness_config, &self.repo_path) {
127            (Some(config), Some(path)) => auths_id::witness_config::WitnessParams::Enabled {
128                config,
129                repo_path: path,
130            },
131            _ => auths_id::witness_config::WitnessParams::Disabled,
132        }
133    }
134}
135
136impl AuthsContext {
137    /// Creates a builder for [`AuthsContext`].
138    ///
139    /// All six required fields (`registry`, `key_storage`, `clock`,
140    /// `identity_storage`, `attestation_sink`, `attestation_source`) are enforced
141    /// at compile time — the `build()` method is only available once all six are set.
142    ///
143    /// Usage:
144    /// ```ignore
145    /// let ctx = AuthsContext::builder()
146    ///     .registry(Arc::new(my_registry))
147    ///     .key_storage(Arc::new(my_keychain))
148    ///     .clock(Arc::new(SystemClock))
149    ///     .identity_storage(Arc::new(my_identity_storage))
150    ///     .attestation_sink(Arc::new(my_store.clone()))
151    ///     .attestation_source(Arc::new(my_store))
152    ///     .build();
153    /// ```
154    pub fn builder() -> AuthsContextBuilder<Missing, Missing, Missing, Missing, Missing, Missing> {
155        AuthsContextBuilder {
156            registry: Missing,
157            key_storage: Missing,
158            clock: Missing,
159            identity_storage: Missing,
160            attestation_sink: Missing,
161            attestation_source: Missing,
162            event_sink: None,
163            passphrase_provider: None,
164            uuid_provider: None,
165            agent_signing: None,
166            witness_config: None,
167            repo_path: None,
168        }
169    }
170}
171
172/// Typestate marker: required field not yet set.
173pub struct Missing;
174
175/// Typestate marker: required field has been set.
176pub struct Set<T>(T);
177
178/// Typestate builder for [`AuthsContext`].
179///
180/// Call [`AuthsContext::builder()`] to obtain an instance. The `build()` method
181/// is only available once all six required fields have been supplied — omitting any
182/// produces a compile-time error.
183pub struct AuthsContextBuilder<R, K, C, IS, AS, ASrc> {
184    registry: R,
185    key_storage: K,
186    clock: C,
187    identity_storage: IS,
188    attestation_sink: AS,
189    attestation_source: ASrc,
190    event_sink: Option<Arc<dyn EventSink>>,
191    passphrase_provider: Option<Arc<dyn PassphraseProvider + Send + Sync>>,
192    uuid_provider: Option<Arc<dyn UuidProvider + Send + Sync>>,
193    agent_signing: Option<Arc<dyn AgentSigningPort + Send + Sync>>,
194    witness_config: Option<auths_id::witness_config::WitnessConfig>,
195    repo_path: Option<std::path::PathBuf>,
196}
197
198// ── Required field setters (each transitions one typestate slot) ──────────────
199
200impl<K, C, IS, AS, ASrc> AuthsContextBuilder<Missing, K, C, IS, AS, ASrc> {
201    /// Set the registry storage backend.
202    ///
203    /// Args:
204    /// * `registry`: Pre-initialized registry backend.
205    ///
206    /// Usage:
207    /// ```ignore
208    /// builder.registry(Arc::new(my_git_backend))
209    /// ```
210    pub fn registry(
211        self,
212        registry: Arc<dyn RegistryBackend + Send + Sync>,
213    ) -> AuthsContextBuilder<Set<Arc<dyn RegistryBackend + Send + Sync>>, K, C, IS, AS, ASrc> {
214        AuthsContextBuilder {
215            registry: Set(registry),
216            key_storage: self.key_storage,
217            clock: self.clock,
218            identity_storage: self.identity_storage,
219            attestation_sink: self.attestation_sink,
220            attestation_source: self.attestation_source,
221            event_sink: self.event_sink,
222            passphrase_provider: self.passphrase_provider,
223            uuid_provider: self.uuid_provider,
224            agent_signing: self.agent_signing,
225            witness_config: self.witness_config,
226            repo_path: self.repo_path,
227        }
228    }
229}
230
231impl<R, C, IS, AS, ASrc> AuthsContextBuilder<R, Missing, C, IS, AS, ASrc> {
232    /// Set the key storage backend.
233    ///
234    /// Args:
235    /// * `key_storage`: Platform keychain or in-memory test fake.
236    ///
237    /// Usage:
238    /// ```ignore
239    /// builder.key_storage(Arc::new(my_keychain))
240    /// ```
241    pub fn key_storage(
242        self,
243        key_storage: Arc<dyn KeyStorage + Send + Sync>,
244    ) -> AuthsContextBuilder<R, Set<Arc<dyn KeyStorage + Send + Sync>>, C, IS, AS, ASrc> {
245        AuthsContextBuilder {
246            registry: self.registry,
247            key_storage: Set(key_storage),
248            clock: self.clock,
249            identity_storage: self.identity_storage,
250            attestation_sink: self.attestation_sink,
251            attestation_source: self.attestation_source,
252            event_sink: self.event_sink,
253            passphrase_provider: self.passphrase_provider,
254            uuid_provider: self.uuid_provider,
255            agent_signing: self.agent_signing,
256            witness_config: self.witness_config,
257            repo_path: self.repo_path,
258        }
259    }
260}
261
262impl<R, K, IS, AS, ASrc> AuthsContextBuilder<R, K, Missing, IS, AS, ASrc> {
263    /// Set the clock provider.
264    ///
265    /// Args:
266    /// * `clock`: Wall-clock implementation (`SystemClock` in production,
267    ///   `MockClock` in tests).
268    ///
269    /// Usage:
270    /// ```ignore
271    /// builder.clock(Arc::new(SystemClock))
272    /// ```
273    pub fn clock(
274        self,
275        clock: Arc<dyn ClockProvider + Send + Sync>,
276    ) -> AuthsContextBuilder<R, K, Set<Arc<dyn ClockProvider + Send + Sync>>, IS, AS, ASrc> {
277        AuthsContextBuilder {
278            registry: self.registry,
279            key_storage: self.key_storage,
280            clock: Set(clock),
281            identity_storage: self.identity_storage,
282            attestation_sink: self.attestation_sink,
283            attestation_source: self.attestation_source,
284            event_sink: self.event_sink,
285            passphrase_provider: self.passphrase_provider,
286            uuid_provider: self.uuid_provider,
287            agent_signing: self.agent_signing,
288            witness_config: self.witness_config,
289            repo_path: self.repo_path,
290        }
291    }
292}
293
294impl<R, K, C, AS, ASrc> AuthsContextBuilder<R, K, C, Missing, AS, ASrc> {
295    /// Set the identity storage adapter.
296    ///
297    /// Args:
298    /// * `storage`: Pre-initialized identity storage implementation.
299    ///
300    /// Usage:
301    /// ```ignore
302    /// builder.identity_storage(Arc::new(my_identity_storage))
303    /// ```
304    pub fn identity_storage(
305        self,
306        storage: Arc<dyn IdentityStorage + Send + Sync>,
307    ) -> AuthsContextBuilder<R, K, C, Set<Arc<dyn IdentityStorage + Send + Sync>>, AS, ASrc> {
308        AuthsContextBuilder {
309            registry: self.registry,
310            key_storage: self.key_storage,
311            clock: self.clock,
312            identity_storage: Set(storage),
313            attestation_sink: self.attestation_sink,
314            attestation_source: self.attestation_source,
315            event_sink: self.event_sink,
316            passphrase_provider: self.passphrase_provider,
317            uuid_provider: self.uuid_provider,
318            agent_signing: self.agent_signing,
319            witness_config: self.witness_config,
320            repo_path: self.repo_path,
321        }
322    }
323}
324
325impl<R, K, C, IS, ASrc> AuthsContextBuilder<R, K, C, IS, Missing, ASrc> {
326    /// Set the attestation sink adapter.
327    ///
328    /// Args:
329    /// * `sink`: Pre-initialized attestation sink implementation.
330    ///
331    /// Usage:
332    /// ```ignore
333    /// builder.attestation_sink(Arc::new(my_attestation_store))
334    /// ```
335    pub fn attestation_sink(
336        self,
337        sink: Arc<dyn AttestationSink + Send + Sync>,
338    ) -> AuthsContextBuilder<R, K, C, IS, Set<Arc<dyn AttestationSink + Send + Sync>>, ASrc> {
339        AuthsContextBuilder {
340            registry: self.registry,
341            key_storage: self.key_storage,
342            clock: self.clock,
343            identity_storage: self.identity_storage,
344            attestation_sink: Set(sink),
345            attestation_source: self.attestation_source,
346            event_sink: self.event_sink,
347            passphrase_provider: self.passphrase_provider,
348            uuid_provider: self.uuid_provider,
349            agent_signing: self.agent_signing,
350            witness_config: self.witness_config,
351            repo_path: self.repo_path,
352        }
353    }
354}
355
356impl<R, K, C, IS, AS> AuthsContextBuilder<R, K, C, IS, AS, Missing> {
357    /// Set the attestation source adapter.
358    ///
359    /// Args:
360    /// * `source`: Pre-initialized attestation source implementation.
361    ///
362    /// Usage:
363    /// ```ignore
364    /// builder.attestation_source(Arc::new(my_attestation_store))
365    /// ```
366    pub fn attestation_source(
367        self,
368        source: Arc<dyn AttestationSource + Send + Sync>,
369    ) -> AuthsContextBuilder<R, K, C, IS, AS, Set<Arc<dyn AttestationSource + Send + Sync>>> {
370        AuthsContextBuilder {
371            registry: self.registry,
372            key_storage: self.key_storage,
373            clock: self.clock,
374            identity_storage: self.identity_storage,
375            attestation_sink: self.attestation_sink,
376            attestation_source: Set(source),
377            event_sink: self.event_sink,
378            passphrase_provider: self.passphrase_provider,
379            uuid_provider: self.uuid_provider,
380            agent_signing: self.agent_signing,
381            witness_config: self.witness_config,
382            repo_path: self.repo_path,
383        }
384    }
385}
386
387// ── Optional field setters (available at any typestate, return Self) ──────────
388
389impl<R, K, C, IS, AS, ASrc> AuthsContextBuilder<R, K, C, IS, AS, ASrc> {
390    /// Set an optional event sink.
391    ///
392    /// Defaults to a no-op sink (all events discarded) when not called.
393    ///
394    /// Args:
395    /// * `sink`: Any type implementing [`EventSink`].
396    ///
397    /// Usage:
398    /// ```ignore
399    /// builder.event_sink(Arc::new(my_sink))
400    /// ```
401    pub fn event_sink(mut self, sink: Arc<dyn EventSink>) -> Self {
402        self.event_sink = Some(sink);
403        self
404    }
405
406    /// Set the passphrase provider for key decryption during signing operations.
407    ///
408    /// Defaults to a noop provider that returns an error. Set this when SDK
409    /// workflow functions will perform signing with encrypted key material.
410    ///
411    /// Args:
412    /// * `provider`: Any type implementing [`PassphraseProvider`].
413    ///
414    /// Usage:
415    /// ```ignore
416    /// builder.passphrase_provider(Arc::new(PrefilledPassphraseProvider::new(passphrase)))
417    /// ```
418    pub fn passphrase_provider(
419        mut self,
420        provider: Arc<dyn PassphraseProvider + Send + Sync>,
421    ) -> Self {
422        self.passphrase_provider = Some(provider);
423        self
424    }
425
426    /// Set the UUID provider.
427    ///
428    /// Defaults to [`SystemUuidProvider`] (random v4 UUIDs) when not called.
429    /// Override with a deterministic stub in tests.
430    ///
431    /// Args:
432    /// * `provider`: Any type implementing [`UuidProvider`].
433    ///
434    /// Usage:
435    /// ```ignore
436    /// builder.uuid_provider(Arc::new(my_uuid_stub))
437    /// ```
438    pub fn uuid_provider(mut self, provider: Arc<dyn UuidProvider + Send + Sync>) -> Self {
439        self.uuid_provider = Some(provider);
440        self
441    }
442
443    /// Set the agent signing port for delegating signing to a running agent process.
444    ///
445    /// Defaults to a noop provider (all operations return `Unavailable`)
446    /// when not called. Set this on Unix platforms where the auths-agent daemon
447    /// is available.
448    ///
449    /// Args:
450    /// * `provider`: Any type implementing [`AgentSigningPort`].
451    ///
452    /// Usage:
453    /// ```ignore
454    /// builder.agent_signing(Arc::new(CliAgentAdapter::new(socket_path)))
455    /// ```
456    pub fn agent_signing(mut self, provider: Arc<dyn AgentSigningPort + Send + Sync>) -> Self {
457        self.agent_signing = Some(provider);
458        self
459    }
460
461    /// Set witness configuration for KEL event receipting.
462    pub fn witness_config(mut self, config: auths_id::witness_config::WitnessConfig) -> Self {
463        self.witness_config = Some(config);
464        self
465    }
466
467    /// Set the repository path (needed for witness receipt storage).
468    pub fn repo_path(mut self, path: std::path::PathBuf) -> Self {
469        self.repo_path = Some(path);
470        self
471    }
472}
473
474// ── Infallible build — only available when all six required fields are set ────
475
476impl
477    AuthsContextBuilder<
478        Set<Arc<dyn RegistryBackend + Send + Sync>>,
479        Set<Arc<dyn KeyStorage + Send + Sync>>,
480        Set<Arc<dyn ClockProvider + Send + Sync>>,
481        Set<Arc<dyn IdentityStorage + Send + Sync>>,
482        Set<Arc<dyn AttestationSink + Send + Sync>>,
483        Set<Arc<dyn AttestationSource + Send + Sync>>,
484    >
485{
486    /// Build the [`AuthsContext`].
487    ///
488    /// Infallible — only callable once all six required fields are set.
489    /// Omitting any required field is a compile-time error.
490    ///
491    /// Usage:
492    /// ```ignore
493    /// let ctx = AuthsContext::builder()
494    ///     .registry(Arc::new(my_registry))
495    ///     .key_storage(Arc::new(my_keychain))
496    ///     .clock(Arc::new(SystemClock))
497    ///     .identity_storage(Arc::new(my_identity_storage))
498    ///     .attestation_sink(Arc::new(my_store.clone()))
499    ///     .attestation_source(Arc::new(my_store))
500    ///     .build();
501    /// ```
502    pub fn build(self) -> AuthsContext {
503        AuthsContext {
504            registry: self.registry.0,
505            key_storage: self.key_storage.0,
506            clock: self.clock.0,
507            identity_storage: self.identity_storage.0,
508            attestation_sink: self.attestation_sink.0,
509            attestation_source: self.attestation_source.0,
510            event_sink: self.event_sink.unwrap_or_else(|| Arc::new(NoopSink)),
511            passphrase_provider: self
512                .passphrase_provider
513                .unwrap_or_else(|| Arc::new(NoopPassphraseProvider)),
514            uuid_provider: self
515                .uuid_provider
516                .unwrap_or_else(|| Arc::new(SystemUuidProvider)),
517            agent_signing: self
518                .agent_signing
519                .unwrap_or_else(|| Arc::new(NoopAgentProvider)),
520            witness_config: self.witness_config,
521            repo_path: self.repo_path,
522            revocation_freshness: None,
523        }
524    }
525}