~/arcana/governance/pdp-pep.md
02
PDP ⇄ PEP SPLIT
Deciding and enforcing are never the same component
[ PDP — POLICY DECISION POINT ]
- Pure function of request + immutable serializable snapshot
- No store calls, no state mutation
- Deterministic: same input, same verdict — always
⇄
[ PEP — POLICY ENFORCEMENT POINT ]
- Obtains fresh context at the effect boundary
- Detects and rejects stale decisions
- Atomically claims grant / approval / use
- Calls the effect exactly once
Fails closed. If required stores are absent or unavailable in REQUIRED mode, the policy provider denies by default. LEGACY_COMPAT is explicit migration behavior and must degrade assurance.
# 02 · PDP ⇄ PEP Split > Deciding and enforcing are never the same component. ## Overview Arcana separates the question *"may this run?"* (PDP) from the statement *"this will now run, exactly once"* (PEP). Collapsing the two is how agent frameworks end up with security that depends on the model's cooperation. Splitting them gives three properties the merged design cannot have: determinism, auditability, and single-effect guarantees. ## PDP — Policy Decision Point A **pure function**: decision = f(request, immutable policy snapshot). - Reads no stores, mutates nothing, calls nothing. - Evaluated against a frozen serializable snapshot of policy, contracts, and capability state. - Same input ⇒ same verdict, every time, on every machine. Verdicts are reproducible in CI and in court. ## PEP — Policy Enforcement Point The component standing at the effect boundary (just before the shell spawns, the file writes, the HTTP call fires): - Obtains a **fresh context** — re-reads current policy/capability state at execution time, not at proposal time. - **Rejects stale decisions**: if policy moved between decision and execution, the old verdict is void and the request re-enters evaluation. - **Atomically claims** the grant/approval/use — concurrent claims produce exactly one winner. - Invokes the protected effect **exactly once**, then records the receipt. ## Decision lifecycle ```text propose q → PDP evaluates q against snapshot S₁ → verdict d → PEP re-reads state at execution moment S₂ → S₂.policyVersion ≠ S₁.policyVersion? → STALE → re-decide → claim grant/approval (atomic) → loser blocked → effect executes exactly once → receipt appended to evidence ``` ## Fail-closed modes | Mode | Behavior when required stores are unavailable | |---|---| | REQUIRED | Deny. The policy provider fails closed; nothing executes without its brain. | | LEGACY_COMPAT | Explicit migration behavior only. Assurance is degraded by definition and must be visible as degraded. | LEGACY_COMPAT exists so existing projects can adopt incrementally; it is never a silent default and never counts toward assurance claims. ## Why the split matters - **Testability**: the PDP is pure, so thousands of hostile requests can be evaluated in CI without touching a filesystem (see the conformance vectors). - **Auditability**: decisions are functions of (request, snapshot) — both are recorded, so any verdict can be re-derived later. - **Safety under concurrency**: two racing executions cannot both consume the same approval; the claim is atomic. ## Related - Previous: [The Authorization Request](/governance/request.md) - Next: [Risk Classes](/governance/risk.md)