Skip to content

firma-sidecar (root module)

Modules

  • audit - Audit event emitter.
  • authority_client - Authority stream client facade.
  • authority_credentials - Sidecar credentials presented to Authority RPCs.
  • body_encoding - Content-Encoding aware body decode/encode for secret placeholder
  • composio - Decodes supported Composio execution protocols into logical actions.
  • config - Sidecar configuration types.
  • connector - Connector layer for the sidecar dispatch hot path.
  • credential
  • enforcement - Two-phase enforcement engine.
  • handler - Request handler.
  • health - Lightweight HTTP health-check server for readiness probes.
  • interceptor - Interceptor.
  • local_exec - Local-exec governance endpoint.
  • normalizer - Intent Normalizer / Envelope Builder.
  • pipeline - Enforcement pipeline orchestrator.
  • run_audit - firma run audit channel ingest.
  • secret_rewrite - Content-Type-driven rewriter for the secret redact path.
  • secret_store - In-memory placeholder ↔ secret dictionary for the Sidecar MITM pipeline.
  • startup - Startup builders that translate the validated

Audit event emitter.

Produces a signed [ExecutionEvent] for every enforcement decision (ALLOW, DENY, ABORT). No call enters or exits the sidecar without a corresponding audit record.

The emitter is invoked by the RequestHandler after every handled call, regardless of outcome. Each event carries an ECDSA signature over all fields, making the audit log tamper-evident and independently verifiable.

Four output modes are supported, selectable via the [sidecar.audit] configuration section:

SinkDescription
stdoutStructured JSON lines (default for containers).
fileAppend-only file at a configured path.
grpcClient-streaming RPC to a downstream audit service.
walWrite-ahead log: buffers events locally when gRPC is
unavailable and replays on reconnect. Bounded by size cap;
oldest events are evicted when the cap is exceeded.

Every event is signed with an ECDSA private key loaded at startup from a file path or environment variable (mutually exclusive). The signature covers all event fields preceding it, enabling downstream consumers to verify event integrity without trusting the transport.

Authority stream client facade.

Spawns the background streams that keep policy bundles and revocations current without putting the Authority on the request hot path.

Sidecar credentials presented to Authority RPCs.

Content-Encoding aware body decode/encode for secret placeholder rehydration and secret masking.

Request and response bodies inspected for secrets (placeholder tokens on the way out, leaked/echoed values on the way back) must be scanned as plaintext, but may arrive or need to be forwarded compressed. This module decodes a supported Content-Encoding before scanning and re-encodes afterward so the forwarded body’s declared encoding still matches its bytes.

Decode/encode run on tokio::task::spawn_blocking rather than inline, so the CPU-bound (de)compression work never blocks the async task’s worker thread — mirroring this crate’s existing convention for offloading synchronous work from an async context (see Cedar policy evaluation in enforcement::constraint_enforcement). Bodies here are already fully buffered Vec<u8> by the time this module sees them, so an async (de)compression adapter over an in-memory buffer would still run synchronously during poll() without a real streaming source to await on — spawn_blocking is what actually avoids blocking a worker thread.

Decodes supported Composio execution protocols into logical actions.

The decoder runs before generic HTTP normalization. It recognizes the two exact Composio hosts, rejects ambiguous execution shapes, and never retains tool arguments in logical envelopes or diagnostics.

Sidecar configuration types.

All configuration for the sidecar binary is defined here. The behavior-free shape lives in [firma_config_schema::sidecar]; the validated types here are built from it via TryFrom, so an invalid configuration can never be constructed.

The top-level [SidecarConfig] groups the enforcement-specific [EnforcementConfig] after the behavior-free schema has deserialized its three direct tables ([sidecar.mapping], [sidecar.capability_validation], and [sidecar.constraint_enforcement]).

Validation runs eagerly when the schema is converted at startup, so misconfigurations surface before the first request arrives.

Connector layer for the sidecar dispatch hot path.

Combines three concerns:

  • a [ConnectorRegistry] that maps the target host to a Connector implementation;
  • a [provider] submodule containing the in-tree connector implementations (currently just the generic HTTP one);
  • (future) adapters for out-of-tree connectors plugged in as host overrides.

The trait and its associated types live in firma-core so that external crates can implement connectors without depending on the sidecar binary. The registry and the concrete implementations are sidecar-private: they are consumed only by the RequestHandler.

Two-phase enforcement engine.

Contains both enforcement stages that evaluate an already-normalized ExecutionEnvelope. Intent normalization and interception live in sibling modules ([crate::normalizer], [crate::interceptor]); the [crate::pipeline] module orchestrates the full flow.

Stage 1 and Stage 2 are two sequential enforcement phases inside the same Sidecar process, not two separate components. The Authority is never contacted on the hot path — all evaluation is local.

  • [capability_validation] — Stage 1: capability token selection and validation (parse, signature verify, expiry, revocation).
  • [constraint_enforcement] — Stage 2: Constraint Enforcement Engine (CEE) — Cedar policy evaluation, scope and threshold checks.
  • [capability_map] — Pre-provisioned capability tokens indexed by action class for fast selection.
  • [decision] — Unified ALLOW/DENY result type for every enforcement call.
  • [error] — Internal error types; every variant maps to a DENY decision (fail-closed boundary).
  • [registry] — Canonical Action Class Registry v0.1 (52 action classes).
  • [revocation] — Bloom filter + LRU revocation cache.
  • [session] — Per-session runtime state store (LRU + persistent backend).

Request handler.

Owns the post-enforcement call path shared by all interceptors: enforcement, dispatch for allowed traffic, denial translation, and audit payload emission. When a secret gateway is configured (see [RequestHandler::with_gateway_client]), the same path also rehydrates outbound secret placeholders and masks inbound secret values; when HTTP secret providers are configured (see [RequestHandler::with_http_secret_providers]), it additionally intercepts and mints placeholders for matching HTTP-vault responses.

Lightweight HTTP health-check server for readiness probes.

Exposes a single GET /healthz endpoint that returns 200 OK after the sidecar has emitted its ready signal. All other paths return 404 Not Found.

Interceptor.

Captures outbound agent traffic before it reaches the external system. Three modes: HTTP proxy (port 8080 default), gRPC hook (programmatic interceptor within the agent process), and Unix socket (avoids port binding in containers). eBPF capture is on the roadmap.

Regardless of interception mode, the raw intercepted request is converted into a RawRequest and passed to the [RequestHandler] for enforcement and dispatch. If the intercepted request cannot be parsed into a valid RawRequest, the interceptor returns a structured DENY with reason MALFORMED_REQUEST (fail-closed).

Local-exec governance endpoint.

This module implements the Sidecar-owned UDS endpoint that firma-run contacts for pre-execution governance decisions on local tool invocations.

It is the authoritative implementation of the “mediator” role described in the canonical docs:

  • docs/architecture/linux-local-command-enforcement.md
  • docs/architecture/command-governance-local-exec-contract.md

Principle: one control plane and one audit surface. The mock Python scripts in examples/ exercise the wire protocol but are not production components.

  • [token_store] — Approval token state machine (Pending → Approved → Consumed / Expired / Revoked). Enforces single-use, short-lived, context-bound tokens; operator must explicitly approve before a token can be consumed.
  • [handler] — Decision logic. Processes one [handler::LocalExecRequest] and returns a [handler::LocalExecResponse], and processes management commands ([handler::LocalExecManagementRequest]) via decide_management.
  • [endpoint] — Async UDS listener. Binds the socket, accepts connections, dispatches to the handler (governance or management), and manages the pruning task lifecycle.

Intent Normalizer / Envelope Builder.

Runs in the Sidecar hot path immediately after interception and before token validation. Deterministically maps the raw intercepted event into a canonical [NormalizedEnvelope] with a normalized intent.action_class.

This step performs deterministic rule-based canonicalization only — no language model, SLM, probabilistic classifier, or similarity-based inference is permitted on the hot path. It makes no policy decisions.

Intent sub-fields produced: action_class (canonical semantic type), resource (normalized target resource identifier), params (action-specific parameters), raw_transport (original transport form — observational, not used by policy), raw_action_ref (original tool name / route / method — observational only).

Failure behaviour: if classification fails or yields an ambiguous action class for a protected operation, the normalizer returns DENY: UNCLASSIFIED_INTENT and no Connector dispatch occurs (fail-closed). Conforms to the FEP [I-N1] enforcement invariant.

Enforcement pipeline orchestrator.

Wires the [IntentNormalizer], Stage 1 ([CapabilityValidator]), and Stage 2 ([ConstraintEnforcer]) into a single enforce() entry point.

The pipeline is the ONLY public entry point for enforcement; callers never interact with individual stages directly.

Every code path returns ALLOW, DENY, ABORT, or PASSTHROUGH. PASSTHROUGH means the request targets a non-protected host and should be forwarded without enforcement. The pipeline short-circuits on any DENY or PASSTHROUGH.

Target: < 3 ms p95 end-to-end overhead (interceptor + Stage 1 + Stage 2 + credential injection + audit emit, excluding connector and external system latency).

firma run audit channel ingest.

firma run performs some enforcement outside the Sidecar’s request pipeline — most notably the egress guard, which blocks an agent’s direct loopback connections at the sandbox boundary before they could reach HTTP_PROXY. To keep the audit trail complete, firma run reports each such fact to the Sidecar over a local control socket as a [RunAuditMessage]. This module turns those messages into signed audit events by feeding the same [AuditPayload] channel the enforcement hot path uses, so an out-of-band block surfaces in firma monitor exactly like a hot-path DENY.

The Sidecar owns the audit semantics: [AuditPayload]::from(&msg) maps each [RunAuditEvent] variant to a fixed (action_class, decision, deny_reason, resource). The producer only states observations, so the signature over the resulting event keeps meaning.

The listener is mode-independent (it sits beside the audit channel, not inside the interceptor) and Unix-only (the control socket is a UDS).

Content-Type-driven rewriter for the secret redact path.

Two operations:

  • Rehydrate (outbound): replace placeholder tokens in a request body with the real secret bytes, encoded to fit the surrounding content type.
  • Mask (inbound): replace occurrences of known secret values in a response body with their placeholder tokens.

Both operations work on a flat byte buffer. The caller is responsible for chunking, streaming overlap buffers, and supplying the match positions (from a SecretStore-style scanner). This module only handles the rewrite math and encoding.

In-memory placeholder ↔ secret dictionary for the Sidecar MITM pipeline.

In the pull model the Sidecar queries the firma-run secret gateway ([firma_secret_provider::gateway::client::GatewayClient]) for each outbound request and builds a SidecarSecretStore from the returned (placeholder, secret_bytes) pairs. The store is scoped to one request-response cycle and discarded after forwarding. firma-run remains the single source of truth; the Sidecar never caches secrets persistently.

The design mirrors [firma_secret_provider::store::SecretStore] but lives in the Sidecar crate to avoid a circular dependency.

The two directions use different matching strategies. Rehydration scans for placeholder tokens, which are fixed-format and never need decoding, so it uses an Aho-Corasick automaton. Masking scans for secret values, which the upstream response may have re-encoded for its content type (JSON escaping, percent-encoding, XML entities); a raw byte scan would miss those re-echoes, so masking instead uses the content-type-aware [crate::secret_rewrite::find_decoded_secret_spans] per secret, gated by [MIN_MASKABLE_SECRET_LEN] to avoid corrupting unrelated response content that coincidentally equals a short secret value.

Startup builders that translate the validated SidecarConfig into the runtime subsystems the main.rs entry point wires together.

Each submodule owns one subsystem so that main.rs stays short and readable:

  • [pipeline] — enforcement pipeline + stubs for Authority / Cedar.
  • [connector] — ConnectorRegistry built from the [sidecar.connector] section.
  • [credential] — CredentialInjector built from the [sidecar.credentials] section.
  • [audit] — audit event builder + sink spawn helpers.
  • [interceptor] — interceptor mode selection and spawn.
  • [authority] — Authority stream clients (WatchPolicyBundle / WatchRevocations).