ADR-0006: Cedar as policy engine¶
- Status: Accepted
- Date: 2026-04-12
Context¶
Capability invocations need authorization decisions: "may this principal invoke this capability on this resource with this context?" Options:
- Code-based guards.
if not user.is_admin: raise. Rails-default. Invisible to framework, no audit trail, policy logic scattered. - OPA / Rego. Industry standard for policy-as-code. Gopher-based, widely adopted. Rego is Turing-complete and notoriously hard to read/debug.
- Cedar (AWS). Typed policy language, formally verified decision procedure, designed for authorization specifically (not general policy).
- XACML. Enterprise standard. Heavyweight, XML-based, poor DX.
- Custom DSL. Tailored but yet-another-policy-language in the world.
Requirements: - Decisions must be fast (< 1 ms typical). - Decisions must be explainable — the decision log entry should say why this was permit/deny. - Policies should be static-analyzable — we want to know at framework-level whether a capability is reachable with valid policies. - Policy bodies should be separate from Python code — readable by non-developers (security, compliance). - Policy language should be bounded (total, terminating) — no Turing-completeness DoS risk.
Decision¶
Cedar as the v1 policy engine.
Policies live in policies/*.cedar. Capabilities reference them via @policy("file::rule_name"). The framework's PEP evaluates decisions before handler execution. Decisions are logged to an append-only decision log with full Cedar diagnostics.
Cedar is embedded as a Rust dependency in trails-policy; PyO3 exposes its authorize() surface to Python decorators.
Consequences¶
Positive¶
- Typed policy language. Cedar policies have schemas; misuse is caught statically.
- Fast. Cedar is designed for sub-ms decisions.
- Explainable. Cedar's decision includes which policies were determining — drops directly into the decision log.
- Bounded. Cedar is total; no infinite loops.
- Formally verified. AWS has formalized Cedar's decision procedure; reduces "did we get policy right" risk.
- Readable. Cedar syntax is closer to English than Rego; compliance reviewers can audit.
- Trait-abstracted.
PolicyEnginetrait means Cedar can be swapped later without API changes.
Negative¶
- Cedar is newer than OPA. Smaller community, less tooling, less prior art in agent contexts. Mitigated by AWS's stewardship + formal basis.
- Cedar is AWS-branded. Some orgs may resist Amazon-affiliated dependencies. Mitigated by Cedar being open source under Apache 2.0 with no AWS-coupling.
- Policy authoring ergonomics still require a learning curve. Mitigated by shipping policy library presets (GDPR, HIPAA, EU AI Act) as templates.
- Rust-native — Python-based admin UIs for policy editing don't exist off-the-shelf. Mitigated by policies being plain text files.
Non-consequences¶
- Application code doesn't import Cedar — only the framework does.
- Policies are decoupled from handler code; changing a policy doesn't require Python changes.
Revisit conditions¶
- If the Cedar ecosystem stalls or AWS de-invests, evaluate OPA or returning to in-Python policy DSLs.
- If Cedar's expressivity proves insufficient for agent-specific patterns (e.g., capability-chain authority), consider layering a higher-level abstraction that compiles to Cedar.