Skip to content

Policies and Policy Sets

The platform decision contract decides whether the STS may issue a mandate. The contract is a signed, versioned Rego module that Caracal owns and embeds in the STS; it is the only thing that emits an authorization decision. You author policy data documents — grants, bindings, confinement, and restrictions — that the contract reads. Policies are versioned immutably, bundled into policy sets, and activated per zone.

ObjectPurpose
PolicyNamed policy data document with immutable versions.
Policy versionA specific content hash and schema version.
Policy setA named bundle of policy versions.
Policy set versionA specific manifest of policy versions.
Active policy set versionThe version the STS evaluates for a zone.

The platform decision contract owns the result object in package caracal.authz. It is deny by default and only allows an exchange that matches one of its vetted rules: a bootstrap mandate, a delegated mint narrowed to the delegation edge, or a mandate presented at the Gateway. You never write result; you supply the data the contract reads.

# caracal:data-document
package caracal.authz
import rego.v1
grants := {
"resource://mercury-bank": {
"application": "payments",
"roles": {"payment-execution": ["payments:read", "payments:write"]},
},
}

The contract reads four adopter documents from package caracal.authz:

DocumentShapeEffect
app_ids{binding_key: application_id}Binds the application key used in grants to the control-plane id the STS sees as input.principal.id.
grants{resource: {application, roles: {role: [scopes]}}}Declares which application owns a resource view and which scopes each role may hold.
confinement[{label_prefix, scopes}]Caps every agent session whose principal carries a matching label prefix to a fixed scope set. Optional; omitting it confines nothing.
restrictset of reasonsDeny overlay. Any entry denies every exchange in the zone. Optional; keep it empty to authorize normally.

A zone that supplies no data authorizes nothing: every allow rule collapses to the default deny.

Example input values the contract evaluates:

InputExample
input.principal.idapp_lynx_control (the acting application, user, or agent)
input.principal.registration_methodmanaged or dcr
input.resource.identifierresource://mercury-bank
input.context.requested_scopes["payments:read", "payments:write"]
input.delegation_edgePresent when authority was delegated from another agent session.

Every evaluation receives this exact input shape (schema_version 2026-05-20). There is no input.application or input.grant object — the acting application is the principal, and grant bounds are enforced by the STS before the contract runs. The platform decision contract evaluates the fields below; your data documents map applications, roles, and scopes onto them.

PathTypeMeaning
input.principal.typestringApplication, user, service, or agent principal class.
input.principal.idstringStable identifier of the acting application/user/agent.
input.principal.zone_idstringZone the principal belongs to.
input.principal.registration_methodstringmanaged or dcr; match dcr to scope dynamically registered workloads.
input.principal.lifecyclestringtask or service for agent sessions.
input.principal.labelsstring[]Role descriptors set at spawn(labels=[...]); drive least-privilege role checks.
input.principal.agent_session_idstringAgent session that initiated the exchange, when present.
input.resource.typestringResource class, e.g. Resource.
input.resource.idstringInternal resource id.
input.resource.identifierstringStable audience URI, e.g. resource://mercury-bank.
input.resource.scopesstring[]Scopes the resource defines (the full allowlist).
input.action.idstringThe exchange action, currently TokenExchange.
input.action.methodstringUpstream HTTP method (upper-cased) the gateway is authorizing. Present only on gateway-authenticated exchanges; absent on direct exchanges. Gate operation authority against this.
input.action.pathstringUpstream request path the gateway is authorizing. Present only on gateway-authenticated exchanges; absent on direct exchanges.
input.session.idstringPresent when the request is bound to a session.
input.delegation_edgeobjectPresent only on delegated exchange (see below).
input.context.requested_scopesstring[]Scopes the caller is requesting now; gate these against the allowlist.
input.context.actor_claimsobjectVerified claims of the acting credential.
input.context.subject_claimsobjectVerified claims of the delegated subject, when present.
input.context.challenge_resolvedbooltrue after a step-up challenge is satisfied.
input.context.trace_idstringRequest trace id for audit correlation.

When input.delegation_edge is present it carries: id, source_session_id, target_session_id, issuer_application_id, receiver_application_id, resource_id, scopes (the narrowed grant), edge_version, path (the full delegation chain), graph_epoch, and constraints_json (typed delegation constraints such as a cost budget). The contract enforces hop membership and the scope-subset narrowing against these fields.

The contract reads your data documents through data.caracal.authz.*grants, app_ids, confinement, and restrict. You supply that data; the platform contract reads it and decides.

OutcomeEffect
allowSTS signs a mandate if token and session checks also pass.
denySTS refuses the exchange and records diagnostics.
step-up diagnosticSTS returns interaction_required with a challenge ID.

Step-up is expressed as a diagnostic such as {"step_up_required": "mfa"}. The STS creates a challenge and expects a later exchange with the challenge proof.

  • Author data documents only; mark each with # caracal:data-document. The platform decision contract owns every result.
  • Validate through the web console or Admin API before activation — a data document must define data and must not define result.
  • Activate through a policy set version, not by editing active content in place.
  • Keep grants, app_ids, confinement, and restrict in separate documents so ownership and review stay clear.
  • Tighten authority through confinement and restrict; neither can widen what the contract already allows.

Read Step-Up Challenges to understand how policies pause sensitive exchanges for fresh proof.