pub struct X509Source { /* private fields */ }Expand description
Live source of X.509 SVIDs and bundles from the SPIFFE Workload API.
X509Source performs an initial sync before returning from X509Source::new or
X509SourceBuilder::build. Updates are applied atomically and can be observed via
X509Source::updated.
The source automatically:
- Maintains a cached view of the latest X.509 materials
- Handles SVID and bundle rotation transparently
- Reconnects with exponential backoff on transient failures
- Validates resource limits before publishing updates
- Publishes valid complete trust even without a usable selected SVID
- Withdraws permission until a valid complete response reauthorizes its material RPC
Use X509Source::shutdown or X509Source::shutdown_configured to stop the supervisor.
Implementations§
Source§impl X509Source
impl X509Source
Sourcepub async fn new() -> Result<Self, X509SourceError>
pub async fn new() -> Result<Self, X509SourceError>
Creates an X509Source using the default Workload API endpoint.
The endpoint is resolved from SPIFFE_ENDPOINT_SOCKET. The source selects the default
X.509 SVID when multiple SVIDs are available.
On success, the returned source is already synchronized with the agent and will keep updating in the background until it is closed.
§Errors
Returns an X509SourceError if:
- the Workload API endpoint cannot be resolved or connected to,
- the initial synchronization with the Workload API does not complete successfully,
- or no suitable X.509 SVID can be selected from the received context.
Sourcepub fn builder() -> X509SourceBuilder
pub fn builder() -> X509SourceBuilder
Creates a builder for configuring an X509Source.
The builder allows customizing how the source connects to the SPIFFE Workload API and how X.509 material is managed (e.g. endpoint selection, reconnection behavior, resource limits).
§Examples
use spiffe::X509Source;
let source = X509Source::builder()
.endpoint("unix:///tmp/spire-agent/public/api.sock")
.build()
.await?;
Sourcepub fn updated(&self) -> X509SourceUpdates
pub fn updated(&self) -> X509SourceUpdates
Returns a handle for receiving update notifications.
The handle yields a monotonically increasing sequence number on each authoritative material change, withdrawal, or recovery. This can be used to detect when the context has changed without polling.
Note: The initial sequence number is 0. Notifications are sent when the selected SVID or the order-insensitive SVID/bundle material changes. Reordering non-selected SVIDs or bundle authorities does not notify. The initial sync does not trigger a notification, and neither do re-deliveries of unchanged material (e.g. on reconnect after a dropped stream or agent restart). A byte-identical valid response following withdrawal restores permission and advances the sequence.
§Examples
let source = X509Source::new().await?;
let mut updates = source.updated();
// Wait for the first update notification
updates.changed().await?;
println!("Update sequence: {}", updates.last());Sourcepub fn is_healthy(&self) -> bool
pub fn is_healthy(&self) -> bool
Returns true if the source appears healthy and can likely provide an SVID.
This method checks that:
- The source is not closed or cancelled
- The update supervisor is still running
- The most recently accepted update produced a selectable SVID
- That selected SVID’s leaf certificate
notAfteris still in the future
Note: This check is inherently racy. Between calling is_healthy() and
svid(), the source may be shut down or the context may change. Use this
for best-effort health checks (e.g., monitoring), not for synchronization.
If you need a guaranteed check, call svid() directly and handle the error.
build() / builder build() only indicates that initial sync completed successfully;
is_healthy() is a runtime health signal, not a construction-success signal. There
may be a brief scheduler-dependent window immediately after construction where this
method returns false until the background supervisor task is first polled.
If a MetricsRecorder or picker callback panics, the supervisor task terminates,
update handles close, and this method reports false afterward. Rebuild the source
to resume updates.
§Examples
let source = X509Source::new().await?;
if source.is_healthy() {
println!("Source appears healthy");
} else {
println!("Source is unhealthy");
}Sourcepub fn x509_context(&self) -> Result<Arc<X509Context>, X509SourceError>
pub fn x509_context(&self) -> Result<Arc<X509Context>, X509SourceError>
Returns the current X.509 context (SVID + bundles) as a single value.
§Errors
Returns an X509SourceError if the X.509 context is not available or
cannot be constructed.
Sourcepub fn state(&self) -> X509SourceState
pub fn state(&self) -> X509SourceState
Acquires one coherent security view. The atomic snapshot load is the material acquisition linearization point. This accessor also works after terminal closure so consumers can apply an explicit final-material policy without resurrecting trust. The returned observation is immutable; reacquire it for current authorization.
Sourcepub fn svid(&self) -> Result<Arc<X509Svid>, X509SourceError>
pub fn svid(&self) -> Result<Arc<X509Svid>, X509SourceError>
Returns the current X.509 SVID selected by the picker (or default).
§Errors
Returns X509SourceError if the source is closed or no SVID is available.
Sourcepub fn try_svid(&self) -> Option<Arc<X509Svid>>
pub fn try_svid(&self) -> Option<Arc<X509Svid>>
Returns the current SVID, or None if unavailable.
Returns None instead of an error
when the SVID cannot be retrieved. Use this when None is an acceptable
value for your use case.
Note: This method swallows all errors, including Closed. If you need
to detect shutdown, use X509Source::svid instead.
§Examples
let source = X509Source::new().await?;
if let Some(svid) = source.try_svid() {
println!("Got SVID: {}", svid.spiffe_id());
} else {
println!("No SVID available");
}Sourcepub fn bundle_set(&self) -> Result<Arc<X509BundleSet>, X509SourceError>
pub fn bundle_set(&self) -> Result<Arc<X509BundleSet>, X509SourceError>
Sourcepub fn try_bundle_for_trust_domain(
&self,
td: &TrustDomain,
) -> Option<Arc<X509Bundle>>
pub fn try_bundle_for_trust_domain( &self, td: &TrustDomain, ) -> Option<Arc<X509Bundle>>
Returns the current bundle for the trust domain, or None if unavailable.
Returns None instead of an error
when the bundle cannot be retrieved. Use this when None is an acceptable
value for your use case.
Note: This method swallows all errors, including Closed. If you need
to detect shutdown, use X509Source::bundle_for_trust_domain instead.
§Examples
let source = X509Source::new().await?;
let trust_domain = TrustDomain::new("example.org")?;
if let Some(bundle) = source.try_bundle_for_trust_domain(&trust_domain) {
println!("Got bundle for {}", trust_domain);
} else {
println!("No bundle available for {}", trust_domain);
}Sourcepub async fn shutdown(&self)
pub async fn shutdown(&self)
Requests cancellation and waits for the source supervisor to terminate.
The first poll closes admission and requests cancellation. Dropping this shutdown future preserves that request and the source’s join ownership. Concurrent and repeated callers all observe actual supervisor completion.
This can wait indefinitely if cleanup cannot finish. For a graceful
deadline followed by abort and join, use X509Source::shutdown_with_timeout
or X509Source::shutdown_configured.
Completion covers the source supervisor, including destruction of its directly owned futures. It does not join dependency-owned transport workers.
Sourcepub async fn shutdown_with_timeout(
&self,
timeout: Duration,
) -> Result<(), X509SourceError>
pub async fn shutdown_with_timeout( &self, timeout: Duration, ) -> Result<(), X509SourceError>
Requests cancellation, then aborts and joins after a graceful deadline.
Each caller has its own timeout. When its grace period expires, it
requests abort and continues waiting for actual supervisor completion.
Another caller can observe that completion within its own grace period
and return Ok(()). Repeated calls after completion return promptly.
Dropping a shutdown future never loses join ownership. The grace period is not an absolute wall-clock bound: abort cannot preempt synchronous callbacks or destructors, and this method waits for them to finish.
§Errors
Returns X509SourceError::ShutdownTimeout after joining if this caller’s
grace period expired. Supervisor panic counts as termination under the
existing error contract.
§Examples
use spiffe::X509Source;
use std::time::Duration;
let source = X509Source::new().await?;
// Shutdown with 10 second timeout (graceful, then abort if needed)
source.shutdown_with_timeout(Duration::from_secs(10)).await?;Sourcepub async fn shutdown_configured(&self) -> Result<(), X509SourceError>
pub async fn shutdown_configured(&self) -> Result<(), X509SourceError>
Shuts down the supervisor using the builder’s graceful deadline.
The default is 30 seconds. With None, this waits indefinitely, like
X509Source::shutdown. With Some, it has the same abort-then-join and
cancellation guarantees as X509Source::shutdown_with_timeout.
§Errors
Returns X509SourceError::ShutdownTimeout after joining if this caller’s
configured grace period expired.
Trait Implementations§
Source§impl BundleSource for X509Source
impl BundleSource for X509Source
Source§type Item = X509Bundle
type Item = X509Bundle
Source§type Error = X509SourceError
type Error = X509SourceError
Source§fn bundle_for_trust_domain(
&self,
trust_domain: &TrustDomain,
) -> Result<Option<Arc<Self::Item>>, Self::Error>
fn bundle_for_trust_domain( &self, trust_domain: &TrustDomain, ) -> Result<Option<Arc<Self::Item>>, Self::Error>
TrustDomain. Read moreSource§impl Clone for X509Source
impl Clone for X509Source
Source§impl Debug for X509Source
impl Debug for X509Source
Auto Trait Implementations§
impl !RefUnwindSafe for X509Source
impl !UnwindSafe for X509Source
impl Freeze for X509Source
impl Send for X509Source
impl Sync for X509Source
impl Unpin for X509Source
impl UnsafeUnpin for X509Source
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request