Skip to content

feat(plugins): add ocsf-audit — OCSF API Activity emitter with attestation chain - #128

Open
Levaj2000 wants to merge 3 commits into
contextforge-org:devfrom
Levaj2000:feat/ocsf-audit-plugin
Open

feat(plugins): add ocsf-audit — OCSF API Activity emitter with attestation chain#128
Levaj2000 wants to merge 3 commits into
contextforge-org:devfrom
Levaj2000:feat/ocsf-audit-plugin

Conversation

@Levaj2000

@Levaj2000 Levaj2000 commented Jul 20, 2026

Copy link
Copy Markdown

Adds builtins/plugins/ocsf-audit — a CMF audit plugin that emits each dispatched
request as an OCSF API Activity event (class 6003, ai_operation +
security_control profiles) instead of a free-form JSON line. Near-twin of
audit-logger: same observation-only, always-allow contract, same factory + hook
wiring; only the record shape differs.

What it does

  • Maps CMF Message + Extensions onto OCSF: ai_agent, ai_model,
    message_context, delegation, actor/user, tool/resource coordinates.
  • Activity ids follow API Activity's real enum (readOnlyHint: true tools,
    resources, prompts → 2 Read; other tool calls → 99 + "Invoke Tool";
    completions → 99 + "Completion"). The passive post-hook stream carries
    action_id: 3 (Observed) / disposition_id: 17 (Logged).
  • Optional tamper-evident attestation chain in the merged OCSF 1.9 record_integrity
    shape (attestation_list[] with fingerprint / prev_event / signatures) — the
    fingerprint hashes the whole event (chain_uid and prev_event inside; only
    fingerprint/signatures excluded) over JCS-style canonical bytes, so any
    schema-following verifier can recompute it from the emitted JSON, no shared secret.
    DSSE signing is real: ECDSA-P256-SHA256 over the DSSE PAE of the same bytes,
    deterministic (RFC 6979), operator-provided key, with authority_uid inside the
    hashed bytes.
  • CMF fields with no OCSF home yet (stop_reason, mcp.*, framework.*, monotonic
    labels, workload identity) ride under OCSF unmapped, so the open schema gaps stay
    self-documenting; upstream OCSF issues for them are being filed.

Scope / deliberate limits

  • Registered as a feature-gated builtin (ocsf-audit), not in default features.
  • Deny/modify records (action_id 2/4) need the framework to surface its decision
    to a plugin — per the production-readiness plan, not attempted here.
  • Chain head is in-process (one chain per instance, reset on restart); durable /
    replica-safe chaining and checkpoint signing are follow-on work.

Validation: cargo build/test -p cpex-plugin-ocsf-audit (13 tests),
cargo clippy --all-targets clean against the workspace lint wall, cargo fmt
clean, cargo build -p cpex-builtins --features ocsf-audit clean.
SAMPLE-OUTPUT.md holds the deterministic example output.

Follows the plan in ocsf-plugin-production-plan.md (P0 schema-validity items are
in; P1+ tracked there).

…ation chain

New builtin CMF plugin (audit/ocsf), a near-twin of audit-logger that
serializes each dispatched request as an OCSF API Activity event
(class 6003, ai_operation + security_control profiles) instead of a
free-form JSON line.

- Activity ids follow API Activity's real enum: resources/prompts and
  readOnlyHint tools -> 2 (Read); other tool calls -> 99 (Other) +
  activity_name; completions -> 99. Passive post-hook stream carries
  action_id 3 (Observed) / disposition_id 17 (Logged); deny/modify
  mapping waits on the framework decision event.
- Optional tamper-evident attestation chain with predecessor binding:
  entry_hash = sha256(canonical{chain_uid, event, prev_entry_hash})
  over JCS-style canonical bytes — independently recomputable from the
  emitted JSON, no shared secret. DSSE signing is a seam (stub) that
  consumes the same binding bytes.
- CMF gap fields (stop_reason, mcp.*, framework.*, monotonic labels,
  workload identity) ride under OCSF unmapped, keeping the open schema
  gaps self-documenting; upstream OCSF issues are being filed.
- Registered as feature-gated builtin (not in default features);
  13 unit tests + deterministic emit_sample example.
@Levaj2000
Levaj2000 force-pushed the feat/ocsf-audit-plugin branch from 9fe56e5 to 85a06c4 Compare August 2, 2026 22:50
…ty_uid

Sync the plugin to its current revision (v0.0.3):

- Emit the attestation carrier as merged into OCSF 1.9
  (ocsf/ocsf-schema#1661): attestation_list[] with fingerprint /
  prev_event / signatures objects, replacing the draft string
  entry_hash / prev_entry_hash shape. The fingerprint now covers the
  whole event per the merged semantics (chain_uid and prev_event inside
  the hashed bytes; only fingerprint/signatures excluded), so a
  verifier following the schema can reproduce it with no knowledge of
  this crate's conventions. metadata.uid is emitted (prev_event.uid
  needs a target) and correlation_uid moved to metadata, where OCSF
  defines it.
- Replace the DSSE signer stub with a real signer: ECDSA-P256-SHA256
  over the DSSE PAE of the same canonical bytes the fingerprint covers,
  deterministic per RFC 6979 so SAMPLE-OUTPUT.md stays byte-identical
  across runs. Key is operator-provided PKCS#8 PEM (key handle, not a
  key service); a configured signer that fails to construct is a loud
  startup error, never silently-unsigned records.
- Emit authority_uid inside the hashed bytes, so the claimed signing
  authority cannot be swapped post-hoc. Signature bytes + key id ride
  unmapped pending ocsf/ocsf-schema#1709.
- Offline verification ships as running code (sign::signing_input +
  sign::dsse_pae) and is exercised end-to-end by the
  signed_event_verifies_offline test and the example's verify lines.
- Add Authors: headers to source files.

Verified against current dev (92ee83a): build, 21/21 tests, clippy
clean, example output reproducible and offline-verifiable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Levaj2000
Levaj2000 force-pushed the feat/ocsf-audit-plugin branch from 85a06c4 to 2ba722d Compare August 2, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant