Policy reference
Every name a policy may use is here, in both policy languages. This page is generated by asking the engines: the actions are the vocabulary the admin API refuses an unknown name against, and each condition scope’s variables are what that scope’s CEL environment declares. A name absent here is a name the gateway refuses.
There are two languages because there are two planes, and they are not alike. The admin plane decides who may change the deployment. That is infrequent, high-stakes, and a question about a principal, and it is written in Cedar, whose policies are analysable and whose default is deny. The data plane decides what a request may do. That happens on every request, at latency that shows up in a percentile, and it is a question about a payload, so it is written in CEL, the expression language Kubernetes admission policy already put in front of the same operators. Neither reaches the other. A Cedar policy cannot read a request body and a CEL condition cannot authorize an API call. See Restrict what an admin caller can do for the longer argument.
The admin plane: Cedar
Section titled “The admin plane: Cedar”There is one policy file, named by admin.policy_file. Every request to
the admin API is one authorization question (a principal, an action, a
resource), and the answer is deny unless a policy permits it and no
policy forbids it.
Entity types
Section titled “Entity types”There are four, and they are few by design. A vocabulary an operator can learn in one sitting is one they will write correct policies in.
| entity | written | is |
|---|---|---|
Admin |
Admin::"<issuer>/<subject>" |
the caller. Its roles are its Cedar parents |
Role |
Role::"platform" |
a role. Membership is the only thing about a principal that policies test |
Action |
Action::"createKey" |
one operation, named by its OpenAPI operation ID |
Resource |
Resource::"keys", Resource::"keys/team-a", Resource::"profiles/team-a" |
a resource class, one instance within it, or one configuration object |
Every action is also a member of exactly one group, either Action::"read"
or Action::"write", so a policy can say “may read everything”
without an endpoint list that goes stale the next time one is added. And
every action acts on exactly one resource class, so a policy scoped to
Resource::"keys" covers every action on keys and every key.
The same vocabulary is published as a Cedar schema, admin.cedarschema, which is what the gateway validates a policy file against at load. Check a policy before it reaches a gateway with:
$ cedar validate --schema admin.cedarschema --policies admin.cedarActions
Section titled “Actions”50 actions across 13 resource classes. The names are the OpenAPI operation IDs, so a policy, a generated client and the audit trail all spell an operation the same way.
Resource::"a2a"
Section titled “Resource::"a2a"”| action | group | |
|---|---|---|
getA2AAgentCard |
read | Project an agent’s card |
listA2AAgents |
read | List configured A2A agents |
Resource::"backends"
Section titled “Resource::"backends"”| action | group | |
|---|---|---|
listBackends |
read | List configured backends |
Resource::"budgets"
Section titled “Resource::"budgets"”| action | group | |
|---|---|---|
listBudgets |
read | List budgets and what has been spent against them |
Resource::"catalog"
Section titled “Resource::"catalog"”| action | group | |
|---|---|---|
getCatalogReport |
read | Report every routable channel and the provenance of its capability claim |
Resource::"cluster"
Section titled “Resource::"cluster"”| action | group | |
|---|---|---|
listAuditHeads |
read | List witnessed audit chain heads |
listClusterMembers |
read | List cluster members |
removeClusterMember |
write | Remove a cluster member |
restoreClusterSnapshot |
write | Restore the cluster state from a snapshot |
saveClusterSnapshot |
write | Save a snapshot of the cluster state |
Resource::"config"
Section titled “Resource::"config"”| action | group | |
|---|---|---|
checkCondition |
read | Compile a CEL condition the way a write would, and say what is wrong where |
getConfig |
read | Summarize the active configuration |
getConfigObject |
read | Read one named object |
getConfigSchema |
read | Read the configuration document schema |
getConfigSource |
read | Read one configuration source |
inspectGuardrails |
read | Run the guardrails over text without a request behind it |
listConfigObjects |
read | List the stored objects of one kind |
listConfigSourceRevisions |
read | List a source’s retained revisions |
listConfigSources |
read | List configuration sources |
listGuardrailDetectors |
read | List the compiled guardrail detectors |
listGuardrailEntityTypes |
read | List the entity types the active guardrails can annotate |
listGuardrailRules |
read | List the compiled guardrail rules |
deleteConfigField |
write | Remove one block or scalar |
deleteConfigObject |
write | Remove one named object |
deleteConfigSource |
write | Remove one configuration source |
putConfigField |
write | Write one block or scalar |
putConfigObject |
write | Write one named object |
putConfigSource |
write | Write one configuration source |
reloadConfig |
write | Reload the configuration file |
Resource::"keys"
Section titled “Resource::"keys"”| action | group | |
|---|---|---|
listKeys |
read | List virtual keys |
listProfiles |
read | List key profiles |
preflightRequest |
read | Resolve a request without sending it, on behalf of a named key |
createKey |
write | Mint a virtual key |
revokeKey |
write | Revoke a virtual key |
Resource::"mcp"
Section titled “Resource::"mcp"”| action | group | |
|---|---|---|
discoverMCPServer |
read | Probe an MCP server |
listMCPServers |
read | List configured MCP servers |
Resource::"models"
Section titled “Resource::"models"”| action | group | |
|---|---|---|
listModels |
read | List declared models |
Resource::"reconcile"
Section titled “Resource::"reconcile"”| action | group | |
|---|---|---|
getReconcileStatus |
read | Reconciliation status |
Resource::"registry"
Section titled “Resource::"registry"”| action | group | |
|---|---|---|
getRegistryServerVersion |
read | Read one registry server version |
listRegistryServerVersions |
read | List a registry server’s versions |
listRegistryServers |
read | List registry servers |
publishRegistryServer |
write | Publish a server to the registry |
Resource::"secrets"
Section titled “Resource::"secrets"”| action | group | |
|---|---|---|
getKeyring |
read | Describe the keyring |
listSecrets |
read | List the secrets this node can resolve |
deleteSecret |
write | Remove a secret |
putSecret |
write | Store a secret |
rewrapKeyring |
write | Change the keyring |
Resource::"suspensions"
Section titled “Resource::"suspensions"”| action | group | |
|---|---|---|
listSuspensions |
read | List what is out of service |
suspend |
write | Suspend a key, provider or rule |
unsuspend |
write | Put something back into service early |
Configuration objects
Section titled “Configuration objects”A write to a configuration source is decided once per object it adds,
removes or changes, and each object is a resource of its kind’s class:
Resource::"profiles/team-a" is the profile, whichever source holds it. An
object has two parents, the kind container and the source container
Resource::"config/<source>", so a policy may scope by either, and resource.source
names the source. Creating or deleting a source itself is decided on the
source container, which == names and in covers with everything inside it.
| kind | instances | is |
|---|---|---|
Resource::"credentials" |
Resource::"credentials/<name>" |
one element of the credentials list |
Resource::"providers" |
Resource::"providers/<name>" |
one element of the providers list |
Resource::"budgets" |
Resource::"budgets/<name>" |
one element of the budgets list |
Resource::"profiles" |
Resource::"profiles/<name>" |
one element of the profiles list |
Resource::"mcp" |
Resource::"mcp/<name>" |
one element of the mcp list |
Resource::"access_rules" |
Resource::"access_rules/<name>" |
one element of the access_rules list |
Resource::"a2a" |
Resource::"a2a/<name>" |
one element of the a2a list |
Resource::"guardrail_detectors" |
Resource::"guardrail_detectors/<name>" |
one element of the guardrail_detectors list |
Resource::"guardrail_rules" |
Resource::"guardrail_rules/<name>" |
one element of the guardrail_rules list |
Resource::"auth" |
none: the block is one object | the auth block or scalar, one owner |
Resource::"a2a_cards" |
none: the block is one object | the a2a_cards block or scalar, one owner |
Resource::"reconcile" |
none: the block is one object | the reconcile block or scalar, one owner |
Resource::"guardrails" |
none: the block is one object | the guardrails block or scalar, one owner |
Resource::"models" |
none: the block is one object | the models block or scalar, one owner |
Resource::"catalog_overlays" |
none: the list is one object | the catalog_overlays list, one per source that carries it |
Resource::"max_body_bytes" |
none: the block is one object | the max_body_bytes block or scalar, one owner |
Resource::"default_provider" |
none: the block is one object | the default_provider block or scalar, one owner |
Resource::"on_fidelity_loss" |
none: the block is one object | the on_fidelity_loss block or scalar, one owner |
Resource::"public_url" |
none: the block is one object | the public_url block or scalar, one owner |
Where a kind shares a name with an action class, they are one container on
purpose: Resource::"mcp/issues" is the server both to probe and to edit.
Attributes
Section titled “Attributes”These are the only attributes the gateway sets, and a policy reading
anything else is refused when the file is compiled rather than left to
fail at evaluation. That matters more than it looks. Cedar skips a policy
that errors, so an unguarded attribute read inside a forbid means that
forbid silently stops forbidding.
| variable | attributes |
|---|---|
principal |
issuer, kind, name, subject |
resource |
class, id, source |
There is no context, no entity carries tags, and an action carries no
attributes: its group and its class are membership, action in Action::"read"
and resource in Resource::"keys". The engine passes none of these, so
reads of them are refused outright.
resource.source is the configuration source an object or a source
container sits in, and empty on every other resource. resource.id is the
instance name and empty on a class or a block.
principal.kind is how the caller was identified: oidc for a token,
local on the Unix socket, loopback on an issuerless loopback listener.
A deployment can hold one to a different standard than another. On the
local socket principal.subject is the caller’s pid, recorded for the
trail and not a thing to decide on, because a pid is reused.
The data plane: CEL
Section titled “The data plane: CEL”There are three kinds of condition, each compiled against its own
vocabulary. An access rule’s is compiled against the hops its at names. A
condition must evaluate to bool, is checked when it is written rather
than when it first matters, and carries a cost budget, so an expression
over arguments the client sized cannot become the request that stalls
the gateway.
POST /admin/v1/config/check-condition answers all of this for one
expression, in the same compile the write performs: the errors with their
positions, the type it evaluates to, and the vocabulary it was checked
against. The console’s editors complete from it, and this page is
generated from it.
access_rules[].condition, no at``
Section titled “access_rules[].condition, no at``”Admits or refuses a request at every hop, deployment-wide or inside a profile, and so sees only who is calling. Its scope name, for check-condition, is access_rules.
| variable | type |
|---|---|
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
access_rules[].condition, at: [llm]``
Section titled “access_rules[].condition, at: [llm]``”Admits or refuses a model request before it is routed. Its scope name, for check-condition, is access_rules with at: [llm]; a rule at several hops may read only what they all declare.
| variable | type |
|---|---|
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
model |
string |
provider |
map(string, dyn) |
route |
string |
access_rules[].condition, at: [mcp]``
Section titled “access_rules[].condition, at: [mcp]``”Makes the same decision one hop lower, at the MCP tool call. Its scope name, for check-condition, is access_rules with at: [mcp]; a rule at several hops may read only what they all declare.
| variable | type |
|---|---|
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
server |
string |
method |
string |
name |
string |
args |
map(string, dyn) |
access_rules[].condition, at: [a2a]``
Section titled “access_rules[].condition, at: [a2a]``”Makes the same decision at the peer-agent hop, over an A2A request. Its scope name, for check-condition, is access_rules with at: [a2a]; a rule at several hops may read only what they all declare.
| variable | type |
|---|---|
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
peer |
string |
method |
string |
role |
string |
task_id |
string |
context_id |
string |
parts |
list(map(string, dyn)) |
guardrails.rules[].when
Section titled “guardrails.rules[].when”Decides what to do about what the detectors found. Its scope name, for check-condition, is guardrails.
| variable | type |
|---|---|
annotations |
list(map(string, dyn)) |
types |
list(string) |
entities |
list(string) |
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
model |
string |
provider |
map(string, dyn) |
route |
string |
side |
string |
path |
string |
server |
string |
tool |
string |
guardrails.rules[].select
Section titled “guardrails.rules[].select”Narrows a redact rule to some of the annotations it matched, one at a time. Its scope name, for check-condition, is guardrails_select.
| variable | type |
|---|---|
a |
map(string, dyn) |
key |
map(string, dyn) |
agent |
map(string, dyn) |
user |
map(string, dyn) |
model |
string |
provider |
map(string, dyn) |
route |
string |
side |
string |
path |
string |
server |
string |
tool |
string |
What is in the maps
Section titled “What is in the maps”map(string, dyn) says a variable is a map and nothing about what is in
it. Two of them are fixed:
key is the virtual key the request presented.
| field | |
|---|---|
name |
the key’s name |
profile |
the profile it was minted into, empty when it names none |
metadata |
map(string, string), whatever the key was minted with |
Metadata is the one a rule normally reads, and it is also where a
condition most often fails. key.metadata.team on a key that has no team
is an error, not a false, so guard it with has(), or leave on_error
at its default and have the rule refuse.
provider is the provider the request is routed to, at the LLM hop and in
every guardrail condition.
| field | |
|---|---|
name |
the provider’s name in the configuration |
metadata |
map(string, string), the provider’s metadata block, verbatim; empty when it declared none |
A rule about where data may go reads the destination’s facts rather than
its name: !(has(provider.metadata.residency) && provider.metadata.residency == "sa")
holds when a provider is added without the field, where a list of names
would not.
annotations has one entry per finding, in guardrails.rules[].when.
a in a select predicate is one of these.
| field | |
|---|---|
category |
the annotation namespace: pii, credential, injection, topic, lang |
context_supported |
one of the recognizer’s context words was near the match and raised its score. With validated this is how a rule asks for a corroborated finding without naming a threshold: `a.validated |
detector |
the name of the detector that produced it, as configured |
end |
byte offset into the segment’s text, exclusive |
entity_type |
Presidio’s value verbatim for PII (PERSON, CREDIT_CARD); for other categories, that category’s label (JAILBREAK, AWS_ACCESS_KEY) |
kind |
what the segmenter asserted the segment is: system, user, assistant, tool_args, tool_result, output, header, query |
recognizer |
the recognizer within that detector, where there is one |
ref |
the segment these offsets belong to, as a path into the request body |
score |
the detector’s confidence, in [0,1]. See PII coverage for what each built-in recognizer can produce, and note that nothing is dropped by default |
start |
byte offset into the segment’s text, inclusive |
text |
the matched text itself, which never leaves the process. An allow-list rule (“ignore our own support number”) is the common case |
type |
category and entity joined, pii/CREDIT_CARD, and usually what a rule should match on |
validated |
a checksum confirmed the match. false also covers a recognizer with no validator and one that abstained |
types and entities are the same findings flattened to lists of
strings, for the common rule that only asks whether something was found
at all.
Functions and macros
Section titled “Functions and macros”Every scope has the same 49 functions and 7 macros: the CEL standard library plus the strings, lists and sets extensions, which are the ones Kubernetes admission policy also enables.
Macros:
all exists exists_one filter has map sortByFunctions and methods:
!_ -_ bool bytes charAt contains distinct double duration dynendsWith flatten format getDate getDayOfMonth getDayOfWeekgetDayOfYear getFullYear getHours getMilliseconds getMinutesgetMonth getSeconds in indexOf int join lastIndexOf lists.rangelowerAscii matches replace reverse sets.contains sets.equivalentsets.intersects size slice sort split startsWith stringstrings.quote substring timestamp trim type uint upperAscii