Skip to main content

X509Source

Struct X509Source 

Source
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

Source

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.
Source

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?;
Source

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());
Source

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 notAfter is 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");
}
Source

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.

Source

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.

Source

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.

Source

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");
}
Source

pub fn bundle_set(&self) -> Result<Arc<X509BundleSet>, X509SourceError>

Returns the current X.509 bundle set.

§Errors

Returns X509SourceError if the source is closed.

Source

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);
}
Source

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.

Source

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?;
Source

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

Source§

type Item = X509Bundle

The bundle type provided by the source.
Source§

type Error = X509SourceError

The error type returned by the source.
Source§

fn bundle_for_trust_domain( &self, trust_domain: &TrustDomain, ) -> Result<Option<Arc<Self::Item>>, Self::Error>

Returns the bundle associated with the given TrustDomain. Read more
Source§

impl Clone for X509Source

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for X509Source

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl SvidSource for X509Source

Source§

type Item = X509Svid

The type of the SVIDs provided by the source.
Source§

type Error = X509SourceError

The error type returned by the source.
Source§

fn svid(&self) -> Result<Arc<Self::Item>, Self::Error>

Returns the current SVID. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more