Policy Model
APF policy is a set of YAML documents, each with one concern and usually one owning team. Together they express three things: what an agent can reach at all, which tools and actions it may use, and what content may flow through those actions. This page explains the model. The pages that follow cover each document kind.
A First Example
This rule lets an agent post to Slack only in one approved channel and only short messages:
The Cedar statement decides. text_len is derived by APF from the text argument’s length. The demo bundle’s copy of this rule reads textLen instead, which only a caller can supply. The data block holds the values an operator tunes, so changing the approved channel is a data change, not a logic change. The role: capability marks the rule as a grant for a tool. Any role other than exactly control, including a misspelling or Control, also makes the rule a grant, so validate rules against the JSON Schemas.
Three Control Families
Document Kinds
A bundle may also carry OpenShell-native provider profiles. Refer to Sandbox Policies.
Policy information providers are not documents. They are registered with the gateway, and their facts reach rules through the context in Stage 1. Refer to Policy Information Providers.
Every document shares the same envelope. metadata.name is required and identifies the document within its kind. metadata.title, metadata.description, and metadata.version are optional. The authoring schemas reject metadata.namespace.
Principal, Action, Resource, and Context
Cedar evaluates each request as a principal performing an action on a resource, in a context.
On the OpenShell middleware path, the action and resource come from the MCP tool name, and the principal subject is empty. Refer to Request Evaluation for the exact mapping and every context attribute.
Provider Context
Facts from policy information providers appear as context.<domain>, one record per registered domain. The record holds the provider’s claims and a _meta record with provider, verified, age_seconds, degraded, cached, status, and evidence_ref. Policies name domains, not providers.
The roots args, annotations, data, body_text, has_tool_name, target_host, target_method, target_path, and target_scheme belong to the request and the engine. A domain can never use one of them.
Guard a permit with has. When the provider degraded or holds nothing for the request, the record carries only _meta, and the guard keeps the grant off. A forbid that reads a missing provider fact fails to evaluate and denies the request. Providers are registered with the gateway, not in the bundle. Refer to Policy Information Providers.
Decisions
Every evaluation ends in one of three outcomes.
An allow can also carry an advisory verdict of warn or escalate from an effect. The verdict is recorded but does not block the request. No component acts on escalate with a review step yet.
Ordering, Conflicts, and Defaults
- Manifest order is semantic. Rules are evaluated in the order the bundle manifest lists them. The first
forbidthat fires stops evaluation and determines the deny reason. - Default deny is a skip. A rule in which no statement matches does not deny the request. Evaluation continues with the next rule.
- Grants are required for tool calls. An MCP tool call is denied unless at least one
capabilityrule fires apermit. Acontrolrule’s permit does not count. - Plain HTTP requests are allowed by default. A request that is not a tool call and that reaches APF is allowed unless a rule forbids it. Use the sandbox policy to keep the perimeter deny-by-default.
- Effects aggregate deterministically. Patches apply in firing order, but any body replacement discards them and the last replacement wins. Headers accumulate, and the strongest verdict wins.
Evaluation Errors
APF fails closed at both load time and request time.
- Load time. A Cedar syntax error, a schema type error, an
@apf_effecton aforbid, an unresolved effect or finding reference, an invalid annotator pattern, or a duplicate annotation name stops the evaluator from being built. The gateway refuses to start with that policy, or keeps its current evaluator when the bad policy arrives as an update. - Request time. On the OpenShell path, an evaluation error, a missing annotation that an effect reads with
source, or an effect that cannot be applied denies the request.EvaluationServicereturns anINTERNALerror for an evaluation error instead. A Cedar error inside apermit, such as reading a missing attribute, means that permit grants nothing. The same error inside aforbiddenies the request, because a forbid that silently stops applying would turn a missing attribute into a grant. A policy information provider registered to fail closed that cannot answer denies the request with the reasonpip_<status>:<provider>before any rule runs.
Refer to Validate and Test Policies to catch load-time errors before you sign.
Why Cedar
APF uses Cedar as its decision language because governance policy must be auditable by the teams it affects, not only by the team that wrote it. Cedar trades expressiveness for auditability. Its deliberate constraints of no recursion, no side effects, and no I/O keep that guarantee as bundles grow.
- Readable decisions. A
permitorforbidstatement can be reviewed by security, legal, and compliance teams without policy-engineering expertise. - Static type checking. When a rule supplies
spec.schema, every policy in the rule is validated against it when the evaluator is built, so a misspelled field fails before any request is evaluated. - Analyzable policy sets. Cedar’s analysis tooling can reason about a policy set’s coverage and conflicts. APF does not ship that tooling.
- Safe composition. Each
PolicyRuleis its own policy set with its own data, so one team’s rule cannot change how another team’s rule evaluates.
Cedar alone cannot express detection, such as regular expressions, length checks, or numeric coercion, or mutation, such as body redaction or header injection. APF adds an annotator stage before Cedar and an effect stage after it for those concerns. spec.language is the extension point for other languages. Cedar is the only language registered today, and any other value fails compilation.
Next Steps
- To divide ownership across teams, refer to Federated Authoring.
- To write the perimeter, refer to Sandbox Policies.
- To write tool grants, refer to Capability Grants.