OPA policies

Field reference for the four Rego evaluation layers, the stored policy definition, the input document a policy evaluates against, the pipe-level policy block, and VP evidence.

See OPA policies → for the conceptual model: the embedded policy engine, deny-overrides, and why an in-process policy engine adds no network hop. This page is the field-level reference: the Rego package each layer queries, the stored policy-definition shape, the input document a policy is evaluated against, and the two surface-level blocks (OPA Policy, VP Evidence) that turn evaluation on for a surface.

Evaluation layers

Rego policy is evaluated at four layers, each a fixed Rego package queried through a single in-process evaluation path. A deny at a layer stops the request immediately (fail-closed); an earlier layer’s deny can never be overridden by a later layer’s allow:

LayerRego packageQuery pathEvaluated by
Trust Fabric (labeled in the dashboard; the internal package name is retained unchanged)agentstream.policydata.agentstream.policy.allowAppliance-to-appliance (DIDComm gateway) traffic, evaluated before any surface-level policy.
Surface / fabricsurface.policydata.surface.policy.allowA2A/MCP-style fabric surfaces. Renamed from the legacy channel.policy package; a definition still declaring channel.policy is migrated in place at startup.
LLM Surfacellm.policydata.llm.policy.allowLLM Surfaces.
IDE front door + per-member Model Policyide.policy (front door) / llm.policy (each catalogue member)data.ide.policy.allow / data.llm.policy.allowThe IDE catalogue’s front door is checked first. Once it passes, each catalogue member is checked under its own llm.policy.

A policy definition’s type (appliance, llm, ide) determines the package its Rego must declare, and a mismatch is rejected at write time. The surface.policy / fabric layer is not one of these three creatable types; it is written directly onto a surface, not as a reusable definition.

A gateway or surface can enforce more than one policy definition at once. The set is evaluated deny-overrides: every member must independently return allow = true, and the first to deny (or error) blocks the request. An administrator can additionally mark one or more llm/ide-typed definitions as enforced globally across every surface of that type, ANDed with each surface’s own set. Each global assignment carries its own monitor-only flag so a would-be deny is evaluated and logged, but does not block the request, while the policy’s effect is observed before it goes live.

A missing or failed-to-compile engine for an enforced gateway or surface fails closed: the request is denied rather than let through. This is recorded in policy audit evidence as a missing_engine outcome.

Rego companion reason rules

Alongside allow, a policy’s package may declare allow_reason and deny_reason rules (derived from the query path by swapping the trailing .allow) to supply a human-readable message that flows into policy audit logs and observability:

package llm.policy
default allow = false
allow if { input.source_auth.claims.role == "admin" }
allow_reason := "Admin bypass" if allow
deny_reason := "Only admin callers may use this surface" if not allow

On an allow with no allow_reason rule, reason stays absent. On a deny with no deny_reason rule, the fixed fallback "Policy denied request" is used.

A handful of Rego builtins are rejected at write time regardless of layer:

BuiltinRejected because
numbers.range(Can allocate an attacker-sized collection.
http.send(No network access from a policy.
opa.runtime(No runtime metadata.
print(No policy-authored output.

Compiled policies also run under a fixed evaluation time limit and a fixed source-size limit, both non-configurable.

Policy definitions

A reusable Rego policy is what you create and edit on the Policies page: write it once, then reference it from as many surfaces (or a global assignment) as need the same rule, and every enforcement point picks up the exact version you activate. Every save is kept as a numbered version behind the scenes, but the editor itself always shows you the current version.

  • Type has to match where you’ll enforce the policy: Trust Fabric for appliance-to-appliance traffic, LLM for LLM Surfaces, IDE for the IDE catalogue front door and its per-model policies. The Rego package has to match too, or the save is rejected. Type is locked once you save; changing it later means creating a new definition.
  • Turn Enabled off instead of deleting a policy you might reuse later. A disabled definition is simply skipped from any set that references it.
  • Keep Sample Input current as you edit the policy, so the Test panel’s dry-run keeps reflecting the case you’re actually trying to catch.
FieldDefaultDescription
IDUnique identifier.
NameHuman-readable name.
DescriptionNoneWhat the policy does.
TypeWhich layer this definition may be applied to: Trust Fabric (appliance), LLM (llm), or IDE (ide). Its Rego must declare the matching package. Locked after creation.
PolicyThe Rego source of the current version, edited directly as text.
EnabledOnWhether this policy is compiled and enforced. A gateway or surface referencing a disabled definition is skipped from its set (and fails closed if that leaves the set empty).
Created AtDocument creation timestamp.
Updated AtLast-updated timestamp (metadata edit or new version).
VersionThe version number currently enforced.
Content HashA hash of the enforced revision’s content, used to prove which exact policy bytes produced a given audited decision.
Sample InputSaved sample input JSON for the policy dry-run (Test panel), persisted with the policy across edits. See Sample Input below.

A save that changes the Rego body appends a new version behind the current one and advances which version is enforced; a save that only changes metadata (name, description, enabled) updates the current version in place without adding a new one. Every past version is kept permanently, so renaming a policy never breaks anything that enforces it: bindings resolve a policy by its ID, not its name.

Sample Input

The Sample Input field feeds the Test panel’s dry-run, letting you re-run a stored request against your policy before activating an edit. A good sample:

  • Is real JSON matching the input document shape below, not a partial fragment. A policy that reads input.source_auth.claims.role needs a sample that actually populates source_auth.claims, or the dry-run won’t reflect what a live request looks like.
  • Covers the specific caller and request shape your policy branches on. If your Rego has different rules for JWT-authenticated versus mTLS-authenticated callers, save (or keep swapping in) a sample for each path you need to check.
  • Is updated whenever you add a new condition to the policy, so the dry-run keeps testing the case that condition is meant to catch, rather than a stale sample from an earlier version of the policy.

Global (appliance-wide) policy assignments

Held per policy type (appliance, llm, ide), enforced on every surface (or, for appliance, every gateway connection) of that type, independent of each surface’s own OPA Policy configuration and evaluated deny-overrides alongside it. It’s the right tool for an organization-wide requirement, such as always checking a trust registry, that shouldn’t depend on each surface owner remembering to add it.

There is no separate global-assignment screen: on the Policies page, switch to the tab matching the policy’s type, and each policy’s own row carries a Global switch. Turning it on opens a confirmation popover; confirming enforces that policy across every surface (or gateway connection) of that type.

  • Turn on Monitor only when first rolling out a new global policy. It evaluates and logs the outcome without blocking anything, so you can confirm it behaves as expected before it’s allowed to deny real traffic.
  • Turn it off once you’ve confirmed the policy’s decisions look right, so it actually enforces.
FieldDefaultDescription
GlobalOffToggle on the policy’s own row in the Policies list. Enforces this policy definition across every surface (or gateway connection) of its type.
Monitor onlyOffWhen on, the policy is evaluated and its outcome recorded, but a would-be deny does not block the request. This allows safely rolling out a new global policy before it enforces.

Input document

Every policy layer evaluates against a single, versioned input document (schema version currently 1). Only the fields relevant to the current request are populated; everything else is omitted.

FieldPopulated whenDescription
httpAlwaysInbound HTTP method, path, and headers (sensitive headers containing authorization, cookie, or token substrings are stripped before injection).
requestIDE front doorpurpose is discovery (GET /v1/models) or dispatch (POST /v1/chat/completions), letting a policy allow unauthenticated catalogue discovery while requiring auth to dispatch. forwarded_upstream_auth is true when the caller supplied a distinct upstream credential alongside the front-door token.
gatewayGateway (Trust Fabric) layerdirection is inbound (self gateway) or outbound (remote gateway); source_id/target_id identify the connection points at each end of the exchange.
channelSurface / fabric layerconfig_id/name identify the surface configuration itself. variant_alias carries the active surface variant selected by the request URL (/route$alias/...); None for the default variant. The LLM and IDE layers don’t populate variant_alias.
source_authAn authenticated callerExactly one of three shapes, depending on the surface’s authentication method: jwt_bearer (subject, claims), api_key (key_name), or mtls (principal, fingerprint, subject_dn, issuer_dn, sans).
mcpMCP trafficNormalized MCP request shape: method, tool_name, resource_uri, prompt_name, params.
extension_identityAgent-identity extension presentThe caller’s decentralized identifier and its hashed form, when the agent-identity extension resolved one.
paymentA payment extension is activeWhether the payment attached to this request was verified, and the response header used to communicate that back.
agentAgent-card / A2A traffictrust_verification is Some(true)/Some(false) when trust registry recognition ran, None when unavailable. did is the caller’s decentralized identifier and agent_dna its agent-card payload.
metadataArbitrary extension metadataFree-form key/value data attached by whichever extension populated it; shape varies by extension.
identity_bindingVP evidence enabled and a VP was presentedThe verified holder/issuer DIDs and VC claims from an upstream gateway’s request-path verifiable presentation.
resourceAlways, though each field is populated only when relevantNormalized request target: surface_type (llm/ide/api/mcp/webhook), model, reasoning_effort, reasoning_max_tokens, requested_models, requested_providers, tool, server_tools, requested_transforms, document_types, audio_formats, image_formats, route, modality.
actionAlways when resolvedNormalized action verb (dispatch, discovery), consistent across surface types; supersedes the IDE-only request.purpose, kept as a compatibility alias.
environmentAlwaysnow is the RFC 3339 evaluation timestamp, for time-window policies.
schema_versionAlwaysVersion of this input contract (currently 1).

Pipe-level policy block

The OPA Policy block is the surface-level block, available on an LLM Surface, that connects a surface to the policies you’ve written and turns Rego enforcement on for its inbound traffic. There is no separate enabled toggle: dragging the OPA Policy element onto the canvas is what turns it on, and removing the element turns it off. When present and a policy is referenced, requests are evaluated after source authentication, with source authentication’s identity, the request’s headers, and everything else in the input document available to the Rego rule, and denied (403) on allow = false.

  • Reference a single Policy Definition to enforce it on this surface. To combine more than one policy on the same surface, enforce the additional policy or policies globally instead (see Global (appliance-wide) policy assignments); a global assignment is ANDed with this surface’s own policy, deny-overrides.
  • Log OPA decisions logs the full input JSON, including request headers and caller identity, which makes it a debugging aid rather than something to run in production.
FieldDefaultDescription
Policy DefinitionThe stored policy definition to evaluate against, chosen by name.
Log OPA decisionsOffEmit a log line for every OPA evaluation containing the full input JSON and the resulting allow/deny. A debugging aid for policy authoring: leave off in production, since input carries request headers and caller identity.

A fabric surface (the surface.policy layer, for A2A/MCP-style traffic) separately resolves its own response-side policy, independent of its request-side policy. This response-side mechanism is not available on an LLM Surface’s OPA Policy block.

VP evidence

The VP Evidence block governs per-surface verifiable-presentation evidence. When enabled, an inbound VP is verified and its holder/issuer/claims are injected as identity-binding data (above) for the surface’s OPA policy. That lets a Rego rule check input.identity_binding for a VP from a specific issuer (for example, “allow only if a VP from a known issuer is present”) without having to verify the presentation itself.

  • Deny when no VP is presented denies the request outright, before the policy even runs, whenever a VP is mandatory for the surface: a belt-and-braces gate on top of whatever the policy decides.
  • If a VP is only optional context that some callers provide, leave that toggle off and let your Rego decide what to do when input.identity_binding is absent.
  • VP carried in defaults to Request header; switch it to JSON body field only for callers that send the presentation there instead.
FieldDefaultDescription
Evaluate VP evidenceOffWhether VP evidence is evaluated for this surface.
Deny when no VP is presentedOffDeny the request outright when no VP is presented: a belt-and-braces gate on top of whatever the policy decides.
VP carried inRequest headerWhere the VP is carried on the inbound request: Request header or JSON body field.
Header nameX-Agent-Stream-PresentationHeader name to read when VP carried in is Request header.

Versioning and immutability

Every save of a policy definition that changes its Rego body appends a new, immutable, monotonically numbered version. Prior versions are retained permanently and never rewritten (the sole exception is a one-time legacy channel.policysurface.policy package migration, applied in place at startup). A metadata-only edit (name, description, enabled) updates the current version’s record without appending.

Every policy decision is recorded in audit evidence against the exact policy definition ID, resolved version, and content hash that was enforced, so a past decision can be proven against the precise policy bytes that produced it. The content hash binds both the policy’s type and its Rego body, so identical Rego stored under a different scope hashes differently.

A policy carries a saved dry-run/Test-panel fixture alongside its Rego, letting an operator re-run a stored sample against an edited policy before activating it. There is no separate “dry-run mode” flag on the definition itself, only the saved sample and the monitor-only rollout flag on a global assignment.