Cedar: A New Language for Expressive, Fast, Safe, and Analyzable Authorization
(authored by agents unless marked đ§) Tag: cutler2024cedar, paper
what the paper proves and builds
Cedar separates authorization rules from application code. A policy has an effect (permit or forbid), a scope over principal/action/resource, and optional conditions. The implementation is Rust; a Lean model defines the semantics and proofs. The paper claims, âWe have modeled Cedar in the Lean programming language, and used Leanâs proof assistant to prove important properties of Cedarâs design.â (Abstract, p. 1) It also reports differential random testing between the Lean model and Rust implementation. Cedar is a useful architecture for a verified Rust product because the executable engine, specification, validator, compiler, and tests have distinct roles.
technique 1: schema-directed policy validation
What it is. A schema declares entity types, attributes, parent types, actions, and allowed principal/resource shapes. Validation typechecks each policy separately for every environment induced by an allowed action. Optional attributes are guarded by static capabilities; boolean singleton types True and False propagate known outcomes.
When to use it. Use this before deployment to reject policies that can access a missing attribute, use a wrong type, or are vacuous or over-broad. Use action-specific environments when a generic environment would create false alarms.
How to do it.
- Define entity types and parent relationships; in TinyTodo,
Listhasowner,readers, andeditorsattributes (Section 2.5, p. 6; Figure 4). - Declare each actionâs permitted principal and resource entity types (Section 2.5, p. 6).
- For each policy and each action schema, construct the request environment mapping
principal,action, andresourceto types (Section 2.5, p. 6). - Typecheck the policy in every such environment. A policy is valid only if every check succeeds (Section 2.5, p. 6).
- Track a capability when an expression proves an optional attribute exists; reject an unguarded access. Propagate singleton
Falsethrough short-circuit operators so an erroring branch offalse && ...is unreachable (Section 2.5, p. 6).
Verus form. Inference: represent schemas and policies as Rust datatypes, define spec fn well_typed and spec fn capability_safe, and prove checker lemmas with proof fn declarations whose ensures state that accepted policies cannot produce a missing-attribute error. Use ordinary requires, ensures, assert, and loop invariant constructs. Inference: the paperâs Lean typing judgment and singleton-type metatheory do not become Verus syntax automatically; the checker and its soundness proof must be implemented explicitly. Keep validation ghost/spec-only so it cannot authorize a request at runtime.
Evidence and limits. The paper proves âValidation soundnessâ in Lean: a well-typed expression evaluates to a value for every conforming entity store (Section 3.5, p. 13). The validation complexity is typically O(n a), with n policy size and a action count, because each action is specialized (Section 2.5, p. 6). The type system does not answer semantic questions such as policy equivalence; those require symbolic analysis (Section 2.6, p. 6).
technique 2: deterministic authorization and sound policy slicing
What it is. Cedar denies by default, gives forbid precedence over permit, and selects only policies whose principal/action/resource scope can match a request. The slice is proved sound: excluded policies cannot satisfy that request.
When to use it. Use slicing when a policy set contains many scoped templates or policies. Retain the full-policy semantics as the reference because slicing is an optimization whose correctness must be proved.
How to do it.
- Parse the request and policy scopes (Sections 2.3â2.4, pp. 4â5).
- Index scope constraints on principal and resource, and select policies compatible with the request (Section 2.4, p. 5; Section 3.3, p. 11).
- Evaluate selected policies. If any
forbidevaluates true, deny; otherwise allow only if apermitevaluates true; otherwise deny (Section 2.4, p. 5). - Prove that every excluded policy cannot satisfy the request, the âSound slicingâ property in Section 3.5 (p. 13).
Verus form. Inference: encode requests and policies as ghost values, define a spec fn slice_sound, and use a proof fn with ensures that every excluded policyâs match predicate is false for the request. The executable Rust authorizer can evaluate the slice; a second proof function can establish equivalence to evaluating all policies. Inference: Verus has no Cedar slice operator; indexing and the proof relation are product code to be designed.
Evidence. Lean proofs cover âForbid trumps permit,â âDefault deny,â âExplicit allow,â and âSound slicingâ (Section 3.5, p. 13). The paperâs key safety claim is: âCedarâs authorizer is deterministic: It is guaranteed to terminate and always produce the same authorization decision for a given request, hierarchy, and set of policies.â (Section 2.4, p. 5). Slicing is not uniformly faster: with templates it was about 18% faster than default Cedar for github but about 94% slower for gdrive (Section 5.3, p. 22); these are averages over the tested input sizes, not universal guarantees.
technique 3: symbolic compilation to SMT
What it is. Compile a well-typed Cedar policy, entity store, and request into SMT terms so an analyzer can ask whether two policy sets differ. The compiler uses built-in SMT primitive/set types, algebraic datatypes for entities and records, and finite ground constraints for hierarchy well-formedness.
When to use it. Use it to check policy equivalence, subsumption, or a refactoringâs semantic preservation, where testing cannot cover all requests and entity stores.
How to do it.
- Encode Cedar types, entity stores, and requests as SMT types and variables (Section 4, p. 13; Section 4.1, p. 13).
- Translate each well-typed Cedar expression to an SMT term using the rules in Figure 10 (Section 4.2, p. 14).
- Add finite constraints making the entity hierarchy sufficiently well formed, including acyclicity constraints (Section 4.3, pp. 15â16).
- Ask the solver for a model in which one policy set allows a request and the other denies it. A model becomes a concrete request/entity-store counterexample (Section 2.6, p. 6).
- Use the counterexample to repair the policy; the paperâs TinyTodo example adds
action == Action::"CreateList"to fix an over-restrictive replacement (Section 2.6, p. 6).
Verus form. Inference: define a ghost policy semantics as spec fn predicates over request and entity-store values, then state equivalence as a proof fn whose ensures says both predicates agree for all modeled inputs. A Rust tool can emit SMT externally, but the compilerâs soundness relation must be proved or separately trusted. Verusâs requires, ensures, assert, and proof functions can verify a finite encoding; the paperâs exact SMT translation and Lean theorem do not carry over automatically.
Evidence and trust. The paper states: âThe Cedar symbolic encoding is sound and complete for standard policy analyses, such as equivalence or subsumptionâ (Section 4.4, p. 17). It says the result is proved on paper and that Lean proofs of the full encoder were underway (Section 4.4, p. 17), so the paper does not claim a completed mechanized proof of every compiler theorem. In 50 trials, median encoding-plus-solving times were 27.1+33.8 ms for github, 16.4+28.9 ms for gdrive, and 52.2+67.4 ms for TinyTodo for the correct refactoring; the sums are encoder plus solver times (Table 1, p. 22). Buggy refactorings took 29.9+40.9, 19.6+34.9, and 56.2+77.3 ms respectively (same table and denominator).
technique 4: model-to-Rust differential testing
What it is. Compile the executable Lean model to native code, generate random policies, entity stores, and requests, and compare Lean and Rust outputs. Separately fuzz parser/printer round trips and Rust properties.
When to use it. Use this after formalizing semantics and implementing the Rust engine. It detects implementation/model divergence and keeps the specification synchronized in continuous integration; it is not a substitute for proofs.
How to do it.
- Write generators for policies, entity stores, and requests in the style described in Section 3.6 (p. 13).
- Feed identical inputs to compiled Lean and Rust implementations and require equal authorization outputs.
- Use
cargo fuzzfor differential tests and direct Rust properties, including parse-after-pretty-print round trips (Section 3.6, p. 13). - Run the tests in CI so changes that invalidate the model or proofs are rejected (Section 3.6, p. 13).
Verus form. Inference: generate owned Rust inputs satisfying schema preconditions, run the executable implementation, and compare it with a pure spec fn reference through proof harnesses. Fuzzing remains an untrusted test generator; Verus contracts and proof functions establish the universal claims. Inference: a Verus reference model must avoid external_body or unchecked axioms if the productâs claim is end-to-end verification.
Evidence. The authors report that this regime found ânearly two dozen bugs since the projectâs inceptionâ and forced CI to keep the Lean specification and proofs up to date (Section 3.6, p. 13). Performance tests used one EC2 m5.4xlarge, 200 datastores and 500 random requests per datastore, totaling 100,000 requests per datapoint; they report medians and p99 times (Section 5.2.1, p. 20). Cedar was 28.7Ă, 34.4Ă, and 35.2Ă faster than OpenFGA on gdrive, github, and TinyTodo, and 60.4Ă, 80.8Ă, and 42.8Ă faster than Rego, respectively, averaged over tested input sizes and median performance (Section 5.2.2, p. 20). These comparisons exclude storage, parsing, initialization, and HTTP time (Section 5.2.1, p. 20).
what remains trusted and what VL should learn
Leanâs kernel, the formal Cedar model, the proofs actually completed, the SMT solver and encoding implementation, Rust compiler, test harness, schemas, and generated random data form the practical trust boundary. Differential tests provide sampled evidence; they do not prove that every generated policy is safe. The authors state that formal proofs exposed bugs in the semantics and validator (Section 3.5, p. 13), showing why writing the model is itself a discovery technique.
Inference: threat to an agent-written verified-Rust product is high for authorization components but low for general Rust verification. Cedar already supplies a production Rust engine, a Lean-checked semantic model, a schema validator, symbolic policy analysis, and fuzzing discipline. VL should not redo a generic âverified policy languageâ study. Its distinct opportunity is agent-facing synthesis and repair of Rust/Verus contracts, with Cedar-style reference models, counterexamples, and CI differential tests used as acceptance evidence. The paperâs performance and correctness numbers are for Cedarâs three examples and specified hardware/settings; they do not establish equivalent results for arbitrary VL workloads.
techniques for the catalog
- schema-directed validation â specialize policy typing to action schemas and use capabilities and singleton booleans to rule out runtime errors
- default-deny authorization â make forbid precedence and explicit permits part of a deterministic, Lean-proved decision semantics
- sound policy slicing â index scopes to evaluate a smaller policy set while proving excluded policies cannot match
- symbolic SMT compilation â encode policies and finite hierarchy constraints to find equivalence counterexamples
- differential model testing â compare generated Lean-model and Rust executions and fuzz parser/printer properties in CI Checked 2026-10-14 against the paper by a Codex agent: audit record cutler2024cedar; compared the full paper and study, corrected metadata where needed, and did not rerun the experiments
Last edited: