Skip to content

feat: tamper evident OCSF auditing #12

Description

@terylt

Summary

Tracking issue for an OCSF auditing plugin for PPE. This is the serializer / sink that renders PPE's audit stream into OCSF and protects it — the counterpart to the audit-seam issue (#11). The seam owns what is emitted and when; this plugin owns how it looks on the wire and how its integrity is proven.

What it is

An AuditHook consumer that turns each DecisionLog (and each EffectRecord) into OCSF events, signs them, and chains them. It attaches via Plugin::as_audit_handler and does no message-lifecycle interception. Ported and validated against #11.

Goals

  1. Faithful OCSF rendering of every verdict. Allow / deny / modify
  2. Class-correct effect events. A token mint is an OCSF Authentication / Account Change event.
  3. Tamper-evidence via signed, chained records. ECDSA-P256 DSSE (deterministic, RFC 6979) over the DSSE PAE, with authority_uid so a verifier confirms which signer — not merely a valid credential. Each event carries a fingerprint (SHA-256 / JCS over the whole event) and a prev_event link (OCSF #1661 attestation_list[] shape). Seam sequence fields (epoch, stream_seq, emission_seq) bind into the attested event so the exported stream's completeness and order are provable.
  4. Hashes, not content. Args and results are not logged — only digests (per the seam's provenance posture).

Non-goals

  1. Not the seam. This plugin cannot see a verdict the seam doesn't emit; it adds no execution semantics. Completeness of the event set is the seam's job — this plugin's job is fidelity and integrity of the rendering.
  2. Not tamper-proof. DSSE + chaining are tamper-evident (detect post-emission modification from emit onward, plus origin authenticity and non-repudiation) — they do not prevent tampering, do not guarantee truthful/complete emission (corroborate via IdP reconciliation / off-box copies), and do not recover overwritten values. Tamper-evidence against the signer's own rewrite needs external anchoring (published checkpoints), which is out of scope here.
  3. Not a durable transport. Rendering + signing is this plugin's boundary; getting bytes to a crash-durable destination (fsync'd WAL, durable queue, SIEM) is the sink/operator's concern. The seam provides effect write-ahead; this plugin does not re-implement it.
  4. No key management / rotation policy. The plugin signs with the key it's given; issuance, rotation, and authority_uid trust roots are an operator/PKI concern.
  5. Not the OCSF schema authority. Where the PPE shape needs a schema element that doesn't exist yet (e.g. AI-tool metadata gaps), that's pursued upstream in OCSF, not worked around with a bespoke local field.
  6. Route-scoped instances are deferred. A per-route instance would fork the fingerprint chain (per-instance chain head); global-first matches the seam's decision. Add scoping only when a use case demands it.

Status

Implementation in CPEX: contextforge-org/cpex#128 for testing. Would need to be ported in PPE.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions