Skip to main content

CapabilityEngine

Struct CapabilityEngine 

Source
pub struct CapabilityEngine<P: PolicyBackend> { /* private fields */ }
Expand description

The Hessra Capability Engine.

Evaluates policy, orchestrates token minting/verification, and manages information flow control via context tokens.

The engine is generic over a PolicyBackend implementation, allowing different policy models (CList, RBAC, ABAC, etc.) to be plugged in. An optional SchemaRegistry declares per-target required_designations that the engine enforces at mint time. An optional DesignationResolver supplies runtime designation values during mint_with_context. The defaults (empty schema, NoopResolver) preserve the basic mint behavior for use cases that don’t need either.

Implementations§

Source§

impl<P: PolicyBackend> CapabilityEngine<P>

Source

pub fn new(policy: P, keypair: KeyPair) -> Self

Create a new engine with a policy backend and signing keypair. Defaults to an empty schema and a no-op resolver; chain Self::with_schema and Self::with_resolver to attach them.

Source

pub fn with_generated_keys(policy: P) -> Self

Create a new engine that generates its own keypair.

Useful for local/development use where the engine manages its own keys. Defaults to an empty schema and a no-op resolver; chain Self::with_schema and Self::with_resolver to attach them.

Source

pub fn with_schema(self, schema: SchemaRegistry) -> Result<Self, EngineError>

Attach a schema registry to this engine. Runs cross-validation against the policy backend: every static designation declared in policy must appear in the target’s schema for the matching operation.

Returns the engine on success or an EngineError::UnknownLabelInPolicy (or other EngineError::SchemaPolicyMismatch variant) on the first label that does not exist in the schema.

Source

pub fn with_resolver<R>(self, resolver: R) -> Self
where R: DesignationResolver + 'static,

Attach a designation resolver to this engine. The resolver is consulted by Self::mint_with_context to supply runtime designation values for the current (target, operation). Replaces any previously attached resolver.

Source

pub fn with_facets(self) -> Self

Enable forwarding facets on this engine. Once enabled, every minted capability gets a fresh designation("facet", <uuid>) attached and the engine records (authority-block revocation id, facet uuid) in its in-memory FacetMap.

The non-consuming verify path (Self::verify_capability / Self::verify_designated_capability) auto-supplies the matching fact from the map when present, so existing callers continue to work unchanged. The consuming variants (Self::verify_and_consume_capability / Self::verify_and_consume_designated_capability) additionally remove the entry on a successful verification, giving single-use-on-ack semantics suitable for JIT-mint-at-dispatch.

§Scope: forward-only, per-token

Enabling facets affects capabilities minted by this engine after facets are enabled. It does not retroactively require existing non-faceted capabilities to appear in the facet map. The token itself carries its verification requirements:

  • A faceted capability has a designation("facet", _) check embedded in its biscuit. If the engine’s facet map has the entry, the engine auto-supplies the matching fact and verification proceeds. If the entry is absent (consumed, restart-wiped, or never registered) the embedded check cannot be satisfied and verification fails closed. This is the revocation, single-use, and restart-invalidation behavior facets exist to provide.
  • A non-faceted capability has no facet check. Even on a facets-enabled engine with an empty map, verification succeeds for such a token because there is no embedded fact to satisfy. The capability never opted into facet enforcement.

In short: map miss is fine only when the token itself does not require a facet fact. This is what makes with_facets() safe to turn on mid-deployment — it changes future issuance, not the meaning of capabilities already in circulation.

Source

pub fn facet_map(&self) -> FacetMap

A handle to the facet map. The map is shared by clone, so the returned handle observes the same state as the engine.

Source

pub fn facets_enabled(&self) -> bool

Whether forwarding facets are enabled on this engine.

Source

pub fn public_key(&self) -> PublicKey

Get the engine’s public key (for token verification).

Source

pub fn policy(&self) -> &P

Get a reference to the policy backend.

Source

pub fn schema(&self) -> &SchemaRegistry

Get a reference to the schema registry.

Source

pub fn evaluate( &self, subject: &ObjectId, target: &ObjectId, operation: &Operation, context: Option<&ContextToken>, ) -> PolicyDecision

Evaluate whether a capability request would be granted, without minting.

Checks both the capability space (does the subject hold this capability?) and exposure restrictions (would context exposure block this?).

Source

pub fn mint_capability( &self, subject: &ObjectId, target: &ObjectId, operation: &Operation, context: Option<&ContextToken>, ) -> Result<MintResult, EngineError>

Mint a capability token for a subject to access a target with an operation.

The engine:

  1. Evaluates the policy (capability space + exposure restrictions)
  2. If granted, mints a capability token via hessra-cap-token
  3. If the target has data classifications, auto-applies exposure to the context

Returns a MintResult containing the token and optionally an updated context.

Source

pub fn mint_with_context( &self, target: &ObjectId, operation: &Operation, ctx: &DesignationContext, context: Option<&ContextToken>, ) -> Result<MintResult, EngineError>

Mint a capability, asking the attached DesignationResolver to supply runtime designations from the given DesignationContext.

The full pipeline:

  1. Evaluate policy. The matched declaration may carry static designations and an anchor.
  2. Call resolver.resolve(target, operation, ctx) to get runtime designations.
  3. Combine static, resolver-supplied, and an empty caller list. If the target has a schema entry for the operation, the union must cover every required_designations label (anchor and other reserved labels excluded; they are handled separately).
  4. Mint the token with the anchor (if configured) at the authority block, then attenuate with the union of designations.

Use this when the engine should drive resolution. Callers that already have designation values can keep using Self::mint_designated_capability and pre-resolve themselves.

Source

pub fn verify_capability( &self, token: &str, target: &ObjectId, operation: &Operation, ) -> Result<(), EngineError>

Verify a capability token for a target and operation.

This is capability-first verification: no subject is required. The token IS the proof of authorization.

When forwarding facets are enabled on the engine, this method auto-supplies the matching designation("facet", <uuid>) fact from the facet map (if the token’s authority-block revocation id is registered). This is the non-consuming path; the entry stays in the map for subsequent verifications. Use Self::verify_and_consume_capability for single-use semantics.

Source

pub fn verify_and_consume_capability( &self, token: &str, target: &ObjectId, operation: &Operation, ) -> Result<(), EngineError>

Verify a capability and atomically remove its facet entry from the engine’s facet map on success. Single-use-on-ack: a second call sees no entry and the cap fails verification.

If forwarding facets are not enabled this method behaves exactly like Self::verify_capability.

Source

pub fn mint_capability_with_options( &self, subject: &ObjectId, target: &ObjectId, operation: &Operation, context: Option<&ContextToken>, options: MintOptions, ) -> Result<MintResult, EngineError>

Mint a capability token with additional restrictions.

Like mint_capability, but supports overriding the policy’s anchor binding or supplying a custom time config. When options.anchor is set, it takes precedence over the policy’s anchor decision.

Source

pub fn issue_capability( &self, subject: &ObjectId, target: &ObjectId, operation: &Operation, options: MintOptions, ) -> Result<String, EngineError>

Issue a capability token directly, without policy evaluation.

Use this when the caller has already performed authorization checks through its own mechanisms (e.g., enterprise RBAC, custom domain logic). For the fully-managed path that includes policy evaluation, use mint_capability or mint_capability_with_options instead.

Engine-wide invariants still apply: if Self::with_facets is set, the issued capability gets a fresh facet attached and registered in the engine’s facet map. Policy, schema, and chain checks are deliberately bypassed (that is the point of this path); facets are an engine-level revocation mechanism rather than a policy-level check, so they continue to apply.

Source

pub fn attenuate_with_designations( &self, token: &str, designations: &[Designation], ) -> Result<String, EngineError>

Attenuate a capability token with designations.

Adds designation checks to narrow the token’s scope to specific object instances. The verifier must provide matching designation facts.

Source

pub fn mint_designated_capability( &self, subject: &ObjectId, target: &ObjectId, operation: &Operation, designations: &[Designation], context: Option<&ContextToken>, ) -> Result<MintResult, EngineError>

Mint a capability with caller-supplied designations attached.

The full pipeline:

  1. Evaluate policy. The matched declaration may carry static designations (author-time bindings) and an anchor.
  2. Combine static designations with the caller-supplied ones.
  3. If the target has a schema entry for the operation, enforce that every required_designations label is present in the union. Reserved labels (e.g., anchor) are excluded from this check; they are handled through the dedicated anchor path.
  4. Mint the token, attaching the anchor (if configured) at the authority block, then attenuate with the union of designations.
Source

pub fn verify_designated_capability( &self, token: &str, target: &ObjectId, operation: &Operation, designations: &[Designation], ) -> Result<(), EngineError>

Verify a capability token that includes designation checks.

For anchor-bound capabilities (minted from a declaration with anchor_to_subject or explicit anchor in policy, or via MintOptions.anchor), the verifier MUST assert its own principal identity by including Designation { label: "anchor", value: <its-own-principal-name> } in designations. The capability verifies if and only if the anchor designation supplied here matches the anchor value embedded at mint time. In plain language, the verifier is proving “I am the principal this capability is anchored at.” Anchor is treated as a regular designation at verify time; the engine does not auto-supply the verifier’s identity.

When forwarding facets are enabled on the engine and the token’s authority-block revocation id is present in the facet map, the engine automatically supplies the matching designation("facet", <uuid>) fact alongside the caller-supplied designations. This is the non-consuming path; the entry stays in the map. Use Self::verify_and_consume_designated_capability for the single-use-on-ack variant.

Source

pub fn verify_and_consume_designated_capability( &self, token: &str, target: &ObjectId, operation: &Operation, designations: &[Designation], ) -> Result<(), EngineError>

Verify a designated capability and atomically remove its facet entry from the engine’s facet map on success. Single-use-on-ack semantics. If forwarding facets are not enabled this is equivalent to Self::verify_designated_capability.

Source

pub fn mint_identity( &self, subject: &ObjectId, config: IdentityConfig, ) -> Result<String, EngineError>

Mint an identity token for a subject.

Source

pub fn authenticate(&self, token: &str) -> Result<ObjectId, EngineError>

Verify an identity token and return the authenticated object ID.

This verifies the token as a bearer token (no specific identity required).

Source

pub fn verify_identity( &self, token: &str, expected_identity: &ObjectId, ) -> Result<(), EngineError>

Verify an identity token for a specific identity.

Source

pub fn mint_context( &self, subject: &ObjectId, session_config: SessionConfig, ) -> Result<ContextToken, EngineError>

Mint a fresh context token for a subject (new session, no exposure).

Source

pub fn add_exposure( &self, context: &ContextToken, data_source: &ObjectId, ) -> Result<ContextToken, EngineError>

Add exposure to a context token from a specific data source.

Looks up the data source’s classification in the policy and adds the corresponding exposure labels to the context token.

Source

pub fn add_exposure_label( &self, context: &ContextToken, label: ExposureLabel, source: &ObjectId, ) -> Result<ContextToken, EngineError>

Add a specific exposure label directly to a context token.

Source

pub fn fork_context( &self, parent: &ContextToken, child_subject: &ObjectId, session_config: SessionConfig, ) -> Result<ContextToken, EngineError>

Fork a context token for a sub-agent, inheriting the parent’s exposure.

Source

pub fn extract_exposure( &self, context: &ContextToken, ) -> Result<Vec<ExposureLabel>, EngineError>

Extract exposure labels from a context token by re-parsing the Biscuit.

Source

pub fn list_grants(&self, subject: &ObjectId) -> Vec<CapabilityGrant>

List all capability grants for a subject.

Source

pub fn can_delegate(&self, subject: &ObjectId) -> bool

Check if a subject can delegate capabilities.

Auto Trait Implementations§

§

impl<P> Freeze for CapabilityEngine<P>
where P: Freeze,

§

impl<P> !RefUnwindSafe for CapabilityEngine<P>

§

impl<P> Send for CapabilityEngine<P>

§

impl<P> Sync for CapabilityEngine<P>

§

impl<P> Unpin for CapabilityEngine<P>
where P: Unpin,

§

impl<P> UnsafeUnpin for CapabilityEngine<P>
where P: UnsafeUnpin,

§

impl<P> !UnwindSafe for CapabilityEngine<P>

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