Skip to main content

BrokerState

Struct BrokerState 

Source
pub struct BrokerState { /* private fields */ }
Expand description

The shared broker state: the loaded policy surface plus the backend manager.

Construct once with BrokerState::new and wrap in an Arc; the accept loop clones the Arc into every connection handler.

Implementations§

Source§

impl BrokerState

Source

pub fn new( catalog: Catalog, policy: ResolvedPolicy, config: Config, manager: BackendManager, backend_label: impl Into<String>, ) -> Self

Bundle the loaded policy surface (the load() 4-tuple’s first three elements) with an already-constructed BackendManager.

backend_label is the name a status response advertises (the backend kind(); with one backend today this is unambiguous). The manager is built separately because building backends from credentials is the binary’s job (vault-vh1); this layer only routes + authorizes.

Source

pub fn with_limits( catalog: impl Into<Arc<Catalog>>, policy: ResolvedPolicy, config: Config, manager: BackendManager, backend_label: impl Into<String>, limits: BrokerLimits, ) -> Self

Like BrokerState::new but with explicit broker BrokerLimits (the AEAD payload cap + rotation grace/retention policy the binary threads from its args). BrokerState::new uses BrokerLimits::default.

Source

pub fn with_audit_log(self, audit: Arc<AuditLog>) -> Self

Attach a JSONL audit sink (vault-vq5), consuming and returning self.

When set, BrokerState::audit appends one JSONL line per recorded decision to the open append-only file; when this is never called, file audit is disabled (tracing still logs every decision). The binary opens the file once at startup and threads the AuditLog in here.

Source

pub fn with_jwt_revocations(self, jwt_revocations: JwtRevocationStore) -> Self

Attach the JWT-SVID revocation deny-list loaded at startup.

Source

pub fn with_version(self, version: impl Into<String>) -> Self

Set the broker software version reported by status/health.

The binary (basil-bin) passes its own env!("CARGO_PKG_VERSION") so the served version follows the shipped basil binary rather than this library crate. Without this, the version defaults to basil-core’s.

Source

pub fn with_reload_inputs(self, inputs: ReloadInputs) -> Self

Record the configured on-disk catalog/policy paths so the SIGHUP reload engine (basil-y3e.2) can re-read from the same paths startup used.

Without this, BrokerState::reload_inputs is None and a reload fails closed with ReloadError::NoInputs (a SIGHUP then only reopens the audit log). The broker never reads catalog/policy from an unconfigured source.

Source

pub const fn reload_inputs(&self) -> Option<&ReloadInputs>

The configured catalog/policy paths the reload engine re-reads, if any.

Source

pub const fn reload_lock(&self) -> &Mutex<()>

The lock the reload engine holds across its whole validate→swap sequence, so concurrent triggers (SIGHUP + admin RPC) apply strictly one at a time and generation ids stay monotonic with no lost update.

Source

pub fn active_generation_id(&self) -> u64

The id of the currently serving generation (observable for the status probe, basil-s7h, and the reload audit). A monotonic counter bumped on each successful reload.

Source

pub fn cached_readiness(&self) -> Option<ReadinessOutcome>

Return the cached readiness outcome if one was computed for the currently-serving generation within READINESS_CACHE_TTL (basil-8nwy).

A cache hit lets the admin Readiness RPC answer without re-fanning-out one backend read per catalog key: the amplification a hostile, ungated socket peer could otherwise drive. The entry is bypassed (re-probe required) when it has expired or when the serving generation has changed since it was cached, so a hot reload that alters the key set can never be masked longer than it takes to recompute, and a stale generation’s counts are never served. Returns None to mean “no usable cache; probe the backend”.

The returned outcome is generation-independent; the caller stamps the current generation id onto the wire response.

Source

pub fn cache_readiness(&self, generation: u64, outcome: ReadinessOutcome)

Store a freshly-computed readiness outcome for generation, valid for READINESS_CACHE_TTL from now (basil-8nwy). A subsequent BrokerState::cached_readiness within that window (and on the same generation) reuses it instead of re-probing the backend.

A poisoned lock is swallowed (the cache is a best-effort optimization, never a correctness or liveness dependency): the worst case is a missed cache and one extra probe, never a panic on the serving path.

Source

pub fn cached_jwks(&self) -> Option<CachedJwks>

Return the cached JWKS document for the currently-serving generation.

Source

pub fn cache_jwks(&self, generation: u64, body: Vec<u8>, etag: String)

Store a freshly-built JWKS document for generation.

Source

pub fn swap_generation(&self, generation: Arc<Generation>)

Atomically swap in a new Generation, replacing the currently serving one. The only mutation point of the reloadable policy surface.

Callers (the reload engine, basil-y3e.2) must have already validated the new generation; this is the unconditional store. In-flight operations that pinned the previous generation via BrokerState::load_generation keep seeing it until they drop their guard. The swap never disturbs an op already in progress.

Source

pub fn load_generation(&self) -> Guard<Arc<Generation>>

Snapshot the active Generation for the lifetime of one operation.

This is the one point an RPC pins its generation: call it once at request entry, then drive the whole op off the returned guard (Generation::pdp, Generation::config, Generation::id) so the (catalog, policy, config) triple stays coherent even if a concurrent reload swaps in a newer generation mid-op. Hold the guard only as long as the op needs it (it pins the snapshot from being reclaimed); if the snapshot must survive an .await, clone the inner Arc out of the guard rather than holding the guard across the suspension point.

Source

pub fn record_decision(&self, record: &DecisionRecord)

Record one authorization decision (vault-vq5): emit it to tracing (allow=info, deny=warn) and, when a JSONL audit sink is configured, append it as one line to the append-only audit file.

The file append is best-effort: an IO error is logged and swallowed by the sink so an audit-disk problem can never block, deny, or panic the data plane: the trustworthy decision already happened and is in tracing. This is the single hook the handler calls for every gated op, allow or deny.

Source

pub fn record_provider_event(&self, event: &ProviderAuditEvent<'_>)

Record one software-custody provider operation (wuj.7): emit it to tracing (failure/deny at warn, allow/success at info) and, when a JSONL audit sink is configured, append the secret-free ProviderAuditEvent JSON as one line. The event carries the selected provider and algorithm and never any seed, signature, plaintext, or ciphertext bytes. Best-effort like Self::record_decision: it can never block, fail, or panic the data plane.

Source

pub fn record_reload( &self, previous_generation: u64, new_generation: u64, outcome: &str, reason: &str, actor: ReloadActor, )

Record a generation reload outcome (basil-y3e.2, basil-atq): emit it to tracing and, when a JSONL audit sink is configured, append one basil.audit.reload line.

outcome is one of "applied" (a real swap happened, logged at info), "checked" (an admin --check dry-run validated, no swap, info), or "rejected" (the candidate failed; previous generation keeps serving, warn). reason is a short, stable, non-secret token (e.g. signal, admin_rpc, or a ReloadError audit token). actor attributes the trigger (ReloadActor::Sighup for the signal path or ReloadActor::Caller with the attested uid for the admin RPC), the only field that differs in the audit trail between an otherwise-identical SIGHUP and RPC reload (basil-ftmc). Like BrokerState::record_decision, the file append is best-effort and can never block, fail, or panic the broker.

Source

pub const fn manager(&self) -> &BackendManager

The backend manager that routes an allowed op to its backend instance.

Source

pub fn backend_label(&self) -> &str

The backend label advertised in a status response.

Source

pub fn agent_version(&self) -> &str

The broker software version advertised in status/health responses.

Source

pub const fn limits(&self) -> BrokerLimits

The broker-wide payload caps + rotation grace/retention policy.

Source

pub const fn events(&self) -> &EventSource

Shared broker event source.

Source

pub const fn jwt_revocations(&self) -> &JwtRevocationStore

JWT-SVID revocation deny-list shared by Workload API validation.

Source

pub async fn refresh_jwt_revocations(&self) -> Result<(), ManagerError>

Re-read the catalog-backed JWT-SVID deny-list and merge it into memory.

This stays outside the hot-swapped Generation: the deny-list is mutable serving state, and refresh uses union semantics so a live revocation cannot be clobbered by a concurrent reload.

§Errors

Returns ManagerError if the configured store is malformed, unreadable, or the live deny-list locks are poisoned. On error the previous in-memory set remains serving.

Source

pub async fn revoke_jwt_svid( &self, trust_domain: &str, jti: &str, expires_at_unix: u64, ) -> Result<(), ManagerError>

Persist and publish a JWT-SVID revocation.

§Errors

Returns crate::manager::ManagerError if the optional catalog-backed deny-list write fails.

Trait Implementations§

Source§

impl Debug for BrokerState

Source§

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

Formats the value using the given formatter. 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<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> 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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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