Skip to content

add upstream audit event streaming interfaces #883

Description

@lukehinds

nono already has a local audit model with append-only audit-events.ndjson, per-event hashes, a session hash chain/Merkle root, session metadata, and a global audit ledger. That local model is the right source of truth.

The missing upstream interface is a general way for code outside the core CLI modules to observe the same canonical audit records as they are written, without patching AuditRecorder or coupling to private pub(crate) types.

PR #831 / issue #798 moves proxy network audit events toward live local NDJSON recording by adding a proxy-specific NetworkAuditSink. That is useful and related, but it is event-type-specific. For downstream integrations, nono needs a broader open interface over the canonical audit event stream.

nono already has a local audit model with append-only audit-events.ndjson, per-event hashes, a session hash chain and Merkle root, session metadata, and a global audit ledger. That local model should remain the source of truth.

The missing upstream interface is a general way for integrations to observe the same canonical audit records as they are written, without patching AuditRecorder or coupling to private pub(crate) types.

PR #831 / issue #798 moves proxy network audit events toward live local NDJSON recording by adding a proxy-specific NetworkAuditSink. That is useful and related, but it is event-type-specific. A broader upstream interface would let integrations observe the complete audit event stream consistently.

Goals

  • Provide a stable upstream interface for observing audit records as they are appended locally.
  • Keep local audit recording, integrity summaries, and ledger append as the authoritative audit path.
  • Support all current audit event classes, not only proxy network events.
  • Preserve audit ordering and hash-chain semantics for consumers.
  • Allow external crates or applications to implement their own buffering, export, alerting, or analysis behavior without changing core audit code.
  • Keep crates/nono policy-free unless there is a strong reason to expose a minimal shared type there.

Non-goals

  • Do not replace local audit recording.
  • Do not change the existing on-disk audit format as part of this issue.
  • Do not let untrusted profiles or packs configure arbitrary external audit destinations.

Proposed direction

Add an upstream audit sink or fanout abstraction around the canonical local audit writer.

The interface should expose stable event metadata that allows consumers to correlate and verify ordering, including the session identifier, event sequence, event type, event hash, chain hash, and canonical event payload.

The sink should be called only after the local record has been successfully appended and flushed. Local audit remains authoritative; sink failures should not corrupt local audit state.

This issue should settle the shape and ownership of the open interface before choosing concrete method signatures or data structures.

Event coverage

The interface should cover all current audit event classes:

  • session start
  • session end
  • capability approval decisions
  • URL-open requests
  • proxy network events
  • command-policy / ETI events

Integration points

nono-cli::audit_integrity::AuditRecorder should likely own the fanout point because it already produces the canonical local audit records and hash-chain state.

nono-cli::supervised_runtime should attach any configured sinks when it constructs the local recorder. Sink configuration should be trusted parent-side configuration, not sandboxed child state.

nono-cli::rollback_runtime::finalize_supervised_exit should notify sinks when final session metadata and audit integrity data are known, while preserving the existing ledger and attestation flow.

nono-proxy should continue to work standalone when no sink is attached. If live proxy event streaming is available, those events should enter the same canonical audit stream without duplicate recording at finalization.

Error and blocking semantics

Default behavior should be fail-open for external sinks:

  • A sink failure must not grant extra sandbox permissions.
  • A sink failure must not abort an in-flight proxy request.
  • A sink failure must not prevent local audit recording.
  • A sink failure should be visible through tracing::warn and, where appropriate, final session metadata.

Any fail-closed behavior should be explicit, separately configured, and carefully reviewed because it changes runtime availability semantics.

Privacy and configuration constraints

Audit payloads can include command arguments, paths, hostnames, URL origins, process IDs, and denial reasons. The open interface should document that sinks receive sensitive metadata and are responsible for any downstream filtering or redaction policy. #782 adds a scrubber API

Configuration for external sinks should come from trusted user, administrator, or embedding application configuration. It should not be controlled by untrusted profiles, packs, or sandboxed children.

Audit payloads can include command arguments, paths, hostnames, URL origins, process IDs, and denial reasons. The open interface should document that sinks receive sensitive metadata and are responsible for any downstream redaction policy.

Compatibility

  • No on-disk format change is required.
  • Existing audit-events.ndjson verification should continue to work unchanged.
  • Existing nono audit show / nono audit verify behavior should not depend on any sink.
  • Existing standalone nono-proxy behavior should remain unchanged when no sink is attached.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions