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>
impl<P: PolicyBackend> CapabilityEngine<P>
Sourcepub fn new(policy: P, keypair: KeyPair) -> Self
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.
Sourcepub fn with_generated_keys(policy: P) -> Self
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.
Sourcepub fn with_schema(self, schema: SchemaRegistry) -> Result<Self, EngineError>
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.
Sourcepub fn with_resolver<R>(self, resolver: R) -> Selfwhere
R: DesignationResolver + 'static,
pub fn with_resolver<R>(self, resolver: R) -> Selfwhere
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.
Sourcepub fn with_facets(self) -> Self
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.
Sourcepub fn facet_map(&self) -> FacetMap
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.
Sourcepub fn facets_enabled(&self) -> bool
pub fn facets_enabled(&self) -> bool
Whether forwarding facets are enabled on this engine.
Sourcepub fn public_key(&self) -> PublicKey
pub fn public_key(&self) -> PublicKey
Get the engine’s public key (for token verification).
Sourcepub fn schema(&self) -> &SchemaRegistry
pub fn schema(&self) -> &SchemaRegistry
Get a reference to the schema registry.
Sourcepub fn evaluate(
&self,
subject: &ObjectId,
target: &ObjectId,
operation: &Operation,
context: Option<&ContextToken>,
) -> PolicyDecision
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?).
Sourcepub fn mint_capability(
&self,
subject: &ObjectId,
target: &ObjectId,
operation: &Operation,
context: Option<&ContextToken>,
) -> Result<MintResult, EngineError>
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:
- Evaluates the policy (capability space + exposure restrictions)
- If granted, mints a capability token via
hessra-cap-token - If the target has data classifications, auto-applies exposure to the context
Returns a MintResult containing the token and optionally an updated context.
Sourcepub fn mint_with_context(
&self,
target: &ObjectId,
operation: &Operation,
ctx: &DesignationContext,
context: Option<&ContextToken>,
) -> Result<MintResult, EngineError>
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:
- Evaluate policy. The matched declaration may carry static designations and an anchor.
- Call
resolver.resolve(target, operation, ctx)to get runtime designations. - 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_designationslabel (anchor and other reserved labels excluded; they are handled separately). - 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.
Sourcepub fn verify_capability(
&self,
token: &str,
target: &ObjectId,
operation: &Operation,
) -> Result<(), EngineError>
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.
Sourcepub fn verify_and_consume_capability(
&self,
token: &str,
target: &ObjectId,
operation: &Operation,
) -> Result<(), EngineError>
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.
Sourcepub fn mint_capability_with_options(
&self,
subject: &ObjectId,
target: &ObjectId,
operation: &Operation,
context: Option<&ContextToken>,
options: MintOptions,
) -> Result<MintResult, EngineError>
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.
Sourcepub fn issue_capability(
&self,
subject: &ObjectId,
target: &ObjectId,
operation: &Operation,
options: MintOptions,
) -> Result<String, EngineError>
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.
Sourcepub fn attenuate_with_designations(
&self,
token: &str,
designations: &[Designation],
) -> Result<String, EngineError>
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.
Sourcepub fn mint_designated_capability(
&self,
subject: &ObjectId,
target: &ObjectId,
operation: &Operation,
designations: &[Designation],
context: Option<&ContextToken>,
) -> Result<MintResult, EngineError>
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:
- Evaluate policy. The matched declaration may carry static designations (author-time bindings) and an anchor.
- Combine static designations with the caller-supplied ones.
- If the target has a schema entry for the operation, enforce that
every
required_designationslabel is present in the union. Reserved labels (e.g.,anchor) are excluded from this check; they are handled through the dedicated anchor path. - Mint the token, attaching the anchor (if configured) at the authority block, then attenuate with the union of designations.
Sourcepub fn verify_designated_capability(
&self,
token: &str,
target: &ObjectId,
operation: &Operation,
designations: &[Designation],
) -> Result<(), EngineError>
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.
Sourcepub fn verify_and_consume_designated_capability(
&self,
token: &str,
target: &ObjectId,
operation: &Operation,
designations: &[Designation],
) -> Result<(), EngineError>
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.
Sourcepub fn mint_identity(
&self,
subject: &ObjectId,
config: IdentityConfig,
) -> Result<String, EngineError>
pub fn mint_identity( &self, subject: &ObjectId, config: IdentityConfig, ) -> Result<String, EngineError>
Mint an identity token for a subject.
Sourcepub fn authenticate(&self, token: &str) -> Result<ObjectId, EngineError>
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).
Sourcepub fn verify_identity(
&self,
token: &str,
expected_identity: &ObjectId,
) -> Result<(), EngineError>
pub fn verify_identity( &self, token: &str, expected_identity: &ObjectId, ) -> Result<(), EngineError>
Verify an identity token for a specific identity.
Sourcepub fn mint_context(
&self,
subject: &ObjectId,
session_config: SessionConfig,
) -> Result<ContextToken, EngineError>
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).
Sourcepub fn add_exposure(
&self,
context: &ContextToken,
data_source: &ObjectId,
) -> Result<ContextToken, EngineError>
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.
Sourcepub fn add_exposure_label(
&self,
context: &ContextToken,
label: ExposureLabel,
source: &ObjectId,
) -> Result<ContextToken, EngineError>
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.
Sourcepub fn fork_context(
&self,
parent: &ContextToken,
child_subject: &ObjectId,
session_config: SessionConfig,
) -> Result<ContextToken, EngineError>
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.
Sourcepub fn extract_exposure(
&self,
context: &ContextToken,
) -> Result<Vec<ExposureLabel>, EngineError>
pub fn extract_exposure( &self, context: &ContextToken, ) -> Result<Vec<ExposureLabel>, EngineError>
Extract exposure labels from a context token by re-parsing the Biscuit.
Sourcepub fn list_grants(&self, subject: &ObjectId) -> Vec<CapabilityGrant>
pub fn list_grants(&self, subject: &ObjectId) -> Vec<CapabilityGrant>
List all capability grants for a subject.
Sourcepub fn can_delegate(&self, subject: &ObjectId) -> bool
pub fn can_delegate(&self, subject: &ObjectId) -> bool
Check if a subject can delegate capabilities.