cedar-policy crate and evaluated synchronously, in-process, with no I/O. The schema, the example policy pack, and the fixtures live in packages/cedar/ and are the cross-language contract between the Rust gateway and the Python control plane (ADR-0017).
Schema
packages/cedar/schema.cedarschema declares the whole Obol namespace:
Tool in Connection in Workspace, and Agent in Workspace. Policies are strictly validated against this schema at compile time — a policy that does not validate is never published.
Entity shape
Control publishes entities as JSON in exactly the shapeEntities::from_json_value accepts. Each entry is {uid: {type, id}, attrs, parents}. Decimal attributes use Cedar’s extension encoding with one to four fractional digits.
packages/cedar/fixtures/entities.json is the reference set: one workspace, two agents, two connections, four tools, and two models.
Request-state binding
Several virtual keys can share one agent identity. Publishing one key’s attributes onto that shared agent would let another key inherit its refund cap, so for live requests the gateway projects the principal rather than reading it from the published set (ADR-0040):- The
Obol::Agententity —env,key_prefix,tools_mode,max_refund_usdfrom the key’sattributes, plus workspace membership — is rebuilt from the authenticatedKeyContexton every evaluation. It replaces, rather than merges with, any published agent of the same id, so an omitted constraint cannot survive from another or a revoked key. - A
Toolentity missing from the published set is synthesized from itsToolSnapshot. - Published entities still supply every other identity and resource. Raw offline fixtures keep their explicit entity inputs untouched.
Example policies
packages/cedar/policies/support-agent.cedar is the shipped example pack. Every policy carries an @id annotation, because those ids are what land on receipts.
Argument conditions
The refund cap is the canonical argument condition: the amount comes from the call context, the cap from the principal.context.amount_usd is derived in the gateway by reading the tool’s amount_path out of the call arguments and dividing by its amount_divisor. The gateway rejects a negative amount before authorization, because a lessThanOrEqual cap on its own would allow one.
Resource sets
Cedar 4.x does not accept a set of entities in the policy scope. A set must move into thewhen clause.
Forbids
Aforbid overrides every permit. The approval gate and the absolute prohibition are both forbids, and only their @id tells them apart.
Authoring notes
decimalhas no<,<=,>or>=operators. UselessThan,lessThanOrEqual,greaterThan,greaterThanOrEqual. Decimals carry one to four fractional digits.- The policy scope accepts
== A,in Afor a single entity, oris T. Sets belong inwhen. - Annotate every policy with
@id("…"). Without it the receipt falls back to Cedar’s generatedpolicyN. - A workspace with no authored policy starts from a single
agents-list-and-completepermit. Everything else is authored.
Evaluation and caching
Authorizer::compile parses the PolicySet, strictly validates it against the schema, and loads the entities. The compiled authorizer is cached by (workspace_id, policy_id, revision_id, hash) — the publication identity, not just the bundle content — so an identical bundle republished under a new revision still produces a verdict that names the current revision. On a compile error the cache is untouched and the gateway keeps serving with the previous authorizer.
A Verdict carries the decision, the matched_policies (resolved @id annotations), any errors, and the policy_id and revision_id of the snapshot that decided.
Discovery and reachability
Listing a tool requires both thelist permission and call reachability. Reachability is a partial evaluation of the call action with the principal, resource, environment, and destructive classification known, and arguments and approval left unknown:
- A definite denial or an evaluation error hides the tool.
- An argument-dependent residual stays discoverable and undergoes full authorization at invocation.
- A virtual capability additionally needs at least one eligible, authorized concrete binding.
- The same filter runs before provider selection, so a permanently forbidden provider cannot outrank a callable alternative.
Fixtures
packages/cedar/fixtures/*.json cases are the executable contract. Each is {policy, entities, request{principal, action, resource, context}, expect{decision, policy_ids}}, run by obol-policy tests, by the obol-policy-check CLI, and by control’s Python tests. packages/fixtures/cedar/ is a byte-for-byte mirror, diff-checked in CI.
Run the suite, or validate a pack by hand:
matched_policies.
Related: Policy overview, CEL prefilter, Publishing, Receipts, Virtual keys.