Summary
PPE today cannot passively audit its own enforcement. Blocks, approval rejections, delegation failures, and injection-stops produce no audit record.
Goals
- Core owns decision emission; the plugin owns serialization. The executor emits one decision record per invocation — allow and deny and modify — that any audit sink consumes. The record exists whether or not an audit plugin is installed.
- Complete by construction. Fire on the verdict.
- Observation-only, enforced at the type level. A new
AuditHook family and DecisionLog.
- Auto-attach, no per-hook YAML. The manager attaches any audit-kind plugin to the single verdict-emit path.
- Provenance without leakage. Records carry hashes + taint labels + spans, not raw payloads.
- Crash-safe effect auditing. Irreversible external actions (token mints, approval grants) are recorded write-ahead:
begin_effect durably flushes intent before the act and fails closed; complete_effect records the outcome. Lifecycle prepared → confirmed | rejected | unknown; unknown is reconciled against the participant (IdP) via an idempotency key. Durability and audit granularity are the same mechanism.
Non-goals
- Not a general mediation kernel. v1 effect auditing is cooperative — a trusted plugin calls
begin_effect before its I/O. The framework does not force an opaque plugin to do so. Structural (mediated) enforcement is scoped to builtins only
- Not a new correlation scheme. Adopt W3C trace context / OTel spans — do not mint a bespoke PPE id. Host-side trace-context propagation across hops is out of core.
- Route-scoped audit is deferred. Global-audit-everywhere covers the real need; per-route instances fork the attestation chain (own fingerprint head). Ship global; add scoping when a use case demands it.
- Not a durable sink by itself.
stderr/tracing are not crash-durable. Core provides the write-ahead contract and a FileEffectLog; a production decision sink (fsync'd WAL or durable queue) is the operator's/consumer's responsibility.
Status
A completed version of this PR is available in the CPEX repo and could be ported over: contextforge-org/cpex#166
Discussion welcome on the goal/non-goal boundaries — especially (1) how far v2 mediation should reach and (5) whether any consumer needs route-scoped audit sooner than "on demand."
Summary
PPE today cannot passively audit its own enforcement. Blocks, approval rejections, delegation failures, and injection-stops produce no audit record.
Goals
AuditHookfamily andDecisionLog.begin_effectdurably flushes intent before the act and fails closed;complete_effectrecords the outcome. Lifecycleprepared → confirmed | rejected | unknown;unknownis reconciled against the participant (IdP) via an idempotency key. Durability and audit granularity are the same mechanism.Non-goals
begin_effectbefore its I/O. The framework does not force an opaque plugin to do so. Structural (mediated) enforcement is scoped to builtins onlystderr/tracingare not crash-durable. Core provides the write-ahead contract and aFileEffectLog; a production decision sink (fsync'd WAL or durable queue) is the operator's/consumer's responsibility.Status
A completed version of this PR is available in the CPEX repo and could be ported over: contextforge-org/cpex#166
Discussion welcome on the goal/non-goal boundaries — especially (1) how far v2 mediation should reach and (5) whether any consumer needs route-scoped audit sooner than "on demand."