Policy rails
A recipe can ship policy rails: Cedar rules for what its agent may do at a connector’s API. The platform’s egress evaluates them on every request to a connector host, outside the sandbox, so the agent can neither skip them nor change what they read.
| Cedar decides | What happens |
|---|---|
| deny | The agent gets 403 application/problem+json of type …/problems/policy-denied, with the rule’s @id and @reason. Nothing is signed or sent, and no one is asked to approve. |
| allow | The request goes on to the connector, which still decides: for AAuth, the person’s Person Server. |
A rail never grants more than the connector allows. Use rails for what holds for every customer, such as “never book first class”; leave each customer’s own limits and approvers to their Person Server.
You need: a recipe (Create a recipe) with a connector its agent calls.
Add rails to a recipe
policies/
schema.cedarschema # the entities and actions your rules see
*.cedar # the rules
routes.yaml # which requests become which Cedar action and resource- Map each connector’s requests to Cedar in
policies/routes.yaml, keyed by the connector’s slug:booking: - method: POST path: /v1/reserve # exact, or a prefix ending in * action: booking.reserve context: [mission] # the session's approved AAuth mission resource: type: Booking attribute_map: # JSON pointers into the request body purpose: /purpose payer: /payer claims: # claims of a signed token in the body from: /quote attributes: [cabin, total_cents] - Declare the entities and actions in
policies/schema.cedarschema. - Write the rules.
@idnames a rule;@reasonis what the agent is told:@id("no-first-class") @reason("first class is never booked: quote business or economy instead") forbid (principal, action == Action::"booking.reserve", resource) when { resource has cabin && resource.cabin == "first" }; - Commit and push. The recipe check validates the schema, routes and rules; a bad bundle marks the recipe invalid instead of reaching a runtime.
Validate locally with the Cedar CLI:
cargo install cedar-policy-cli --locked
cat policies/*.cedar > /tmp/policyset.cedar
cedar validate --schema policies/schema.cedarschema --policies /tmp/policyset.cedarWhat a rule can read
| Input | Comes from | Who can change it |
|---|---|---|
| The rules, schema and routes | The recipe at the commit the runtime was built from | A commit and a deploy |
principal | The attributes on the member the session acts for (policy_attributes) | An organization owner (members:manage) |
Entities such as a Company | The connector’s policy_entities | Whoever manages the connector |
resource | The request body, through routes.yaml | The agent, for the one request it sends |
context.mission | The session’s approved AAuth mission | The person’s Person Server, by approving it |
context.judgement | A judge’s answers about the request (below) | Nobody: it is computed outside the sandbox |
Set a member’s attributes when you create or update them:
{ "policy_attributes": { "level": 5, "company": { "__entity": { "type": "Company", "id": "acme" } } } }Claims read from a token in the body are not verified at the egress; the provider that signed the token checks it again and acts only on what it signed, so a forged one gains nothing.
Ask a judge about intent
Cedar cannot tell whether a booking is what the person asked for. A route can name a judge that answers questions about the request against the person’s own words, before Cedar decides:
# judges/booking.yaml
name: booking
type: mission
attributes: [city, check_in, check_out, total_cents]
questions:
requested:
instructions: Is the booking something the traveller asked for, or a natural part of that trip?
criteria: { "true": The same destination and dates., "false": Anything else. }Each question is a noul : a yes-or-no decision rule with instructions and criteria for each answer, as Jev decision models take them. By default the platform asks Jev, through the session’s managed inference, so a judge needs no key of its own.
Add judge: booking and judgement to the route’s context, and each answer
reaches Cedar as a percentage, such as context.judgement.requested. The
person’s words are recorded as they arrive and are never readable or writable
from the sandbox.
Fails closed
The gate denies when it cannot decide: rules or entities that do not validate,
a body that is not JSON, a claim that is missing, or a session it cannot read.
A request to a connector host that routes.yaml names, matching none of its
routes, is denied too. A recipe with no routes.yaml ships no rails, and its
Cedar files are ignored.
Rails run on data planes with the policy gate turned on; ask us to enable it for yours.
Example
The travel agent recipe refuses a booking outside an approved trip, first class, business class below the company’s seniority, and a personal booking on the company’s account, with two judges reading what the traveller asked for.