firma-core (root module)
Module: firma_core
Section titled “Module: firma_core”Contents
Section titled “Contents”Modules
action_class- Canonical action-class identifiers.capability_seed- Canonical on-disk capability seed schema.cedar- Cedar entity UID types and shared schema for Firma policy evaluation.connector- Connector trait and shared types for outbound request dispatch.credential- Credential bundle types shared between the sidecar and connectordecisionenvelopepolicyrun_audit- Wire contract for thefirma run→ Sidecar audit channel.token- Capability token types and signing/verification traits.transport- Read-only transport view consumed by connector implementations.
Module: action_class
Section titled “Module: action_class”Canonical action-class identifiers.
Every intent.action_class in an ExecutionEnvelope, every capability
token action_set entry, and every Cedar action UID resolves to one of
these identifiers. The wire/config/PASETO surfaces are all string-based,
so [ActionClass] exists to give callers that construct those strings a
single, typo-proof source of truth via [ActionClass::as_str].
The set mirrors the sidecar’s runtime ActionClassRegistry; a drift test
there asserts the two stay in lockstep.
Module: capability_seed
Section titled “Module: capability_seed”Canonical on-disk capability seed schema.
Module: cedar
Section titled “Module: cedar”Cedar entity UID types and shared schema for Firma policy evaluation.
Encodes the entity roles used in Firma policy evaluation and produces the
Cedar entity UID via [TryFrom] using Cedar’s typed builder API. Both
the Authority (issuance) and the Sidecar (enforcement) must use identical
UID formats — keeping this type in firma-core makes that contract
explicit.
Entity UID conventions
Section titled “Entity UID conventions”| Variant | Cedar type name | ID source |
|---|---|---|
Agent | Firma::Agent | [AgentId] |
Action | Firma::Action | normalizer string |
Resource | Firma::Resource | normalizer string |
Injection safety
Section titled “Injection safety”[FirmaEntityUid] uses [EntityUid::from_type_name_and_id] rather than
string interpolation. [EntityId] stores the id opaquely and never
re-parses it as Cedar syntax, so special characters in the id cannot break
out of the entity binding. [AgentId] additionally enforces
[a-zA-Z0-9_-]{1,128} at construction time as a second defence layer.
Module: connector
Section titled “Module: connector”Connector trait and shared types for outbound request dispatch.
The connector is the layer that runs after enforcement and
credential injection have produced an authorised
TransportView. Its job is precise and bounded by FEP §6.2:
translate the envelope into the target wire format, apply
technical constraints (rate limit, timeout, connection pool),
merge injected credentials into the outbound request, and return
the target response.
The connector layer is not an enforcement layer. By the time a request reaches it, Stage 1 (capability validation), Stage 2 (constraint enforcement), and credential injection have all run. Implementations must not:
- make authorization decisions,
- modify intent or capability fields on the envelope,
- implement business logic,
- source credentials independently of the
TransportView.
Implementations may:
- translate the envelope into the target wire format,
- apply technical constraints (rate limit, timeout, connection pool),
- read credentials from the
TransportViewand merge them into the outbound request, - enrich audit metadata with outcome information returned via
[
ConnectorResponse].
Extensibility
Section titled “Extensibility”The trait and its associated types live in firma-core so that
external crates can implement specialized connectors (LLM
providers, databases, tool APIs) without pulling in the sidecar
binary. The sidecar owns the registry and the generic HTTP
implementation; specialized connectors plug in as host overrides.
Example
Section titled “Example”A minimal connector that echoes the request body back as the response body:
use std::collections::HashMap;use std::time::Duration;
use async_trait::async_trait;use firma_core::{ ActionParams, Connector, ConnectorError, ConnectorResponse, TransportView,};use firma_http::HeaderMap;
struct EchoConnector;
#[async_trait]impl Connector for EchoConnector { async fn dispatch( &self, view: &TransportView, ) -> Result<ConnectorResponse, ConnectorError> { let body = match &view.envelope().intent().params { ActionParams::Http(http) => http.body.clone().unwrap_or_default(), _ => Vec::new(), }; let response_size = body.len(); Ok(ConnectorResponse { status: 200, headers: HeaderMap::new(), body, dispatch_latency: Duration::from_millis(0), response_size, }) }}Module: credential
Section titled “Module: credential”Credential bundle types shared between the sidecar and connector implementations.
The sidecar runs credential injection after enforcement passes and
produces an [InjectedCredentials] value. That value is handed to
connectors (in-tree or out-of-tree) through a TransportView so
they can merge the injected headers into the outbound request.
Only the read-only bundle lives here. The CredentialInjector
trait and its implementations stay inside firma-sidecar because
they are consumed only by the enforcement pipeline.
Module: decision
Section titled “Module: decision”Module: envelope
Section titled “Module: envelope”Module: policy
Section titled “Module: policy”Module: run_audit
Section titled “Module: run_audit”Wire contract for the firma run → Sidecar audit channel.
Some enforcement happens outside the Sidecar’s request pipeline and so
never flows through an [crate::envelope::ExecutionEnvelope]. The canonical
example is a wrapped agent’s direct loopback connection: it bypasses
HTTP_PROXY, so the firma run egress guard blocks it at the sandbox
boundary and reports the fact to the Sidecar after the event. The Sidecar
turns each report into a signed audit event so it surfaces in
firma monitor alongside hot-path decisions.
This module holds only the wire types shared by the producer (firma-run)
and the consumer (firma-sidecar). It is intentionally semantics-free:
a [RunAuditEvent] states an observation, and the Sidecar — which signs the
audit log — owns the mapping from observation to (action_class, decision, deny_reason, resource). That split keeps the signature meaningful: the
reporter can never assert an arbitrary audit record.
Module: token
Section titled “Module: token”Capability token types and signing/verification traits.
Module: transport
Section titled “Module: transport”Read-only transport view consumed by connector implementations.
The sidecar assembles a TransportView after enforcement passes
and credential injection completes. The view pairs an approved
[ExecutionEnvelope] with its [InjectedCredentials] and is the
single value handed to the Connector layer at dispatch time.
Neither the envelope nor the credentials can be mutated through this view. This is intentional: by the time a connector runs, Stage 1, Stage 2, and credential injection have all executed, and the connector’s role is limited to protocol translation and dispatch (FEP §6.2).