Skip to content

Audit log resource: do we need it? #292

Description

@martsokha

Context

Phase 5 of the retention rollout was originally scoped to add a durable audit-log resource:

  • New nvisy-core::audit::AuditEvent type with a kind union (starting with FileDeleted { reason }).
  • New fjall keyspace audit_events keyed by (actor_id, event_id) (UUIDv7 → time-ordered).
  • Engine::write_audit_event / Engine::list_audit_events methods.
  • GET /api/v1/audit-events route with cursor pagination.
  • Sweeper emits FileDeleted { reason: RetentionExpired, source_run_id, at } after each successful sweep.
  • Engine::delete_file (or the DELETE /files/{id} handler) emits FileDeleted { reason: UserRequested }.
  • Wires up RetentionScope::AuditLogs — currently the sweeper's AuditLogs branch defers.

I built a first pass (AuditEvent + AuditRegistry trait + tests) then backed it out because I couldn't answer why we need it today.

The three real use cases

  1. Compliance evidence. A regulator asks "prove you deleted this file when the retention deadline hit." Audit log is the answer. Without it, "we ran a sweeper that probably deleted things" is unfalsifiable.
  2. Operator forensics. User says "my file disappeared" — operator queries by actor to see when/why. Today they'd have to correlate tracing output with fjall snapshots.
  3. Runtime debugging. "Did the sweeper delete this today?" — audit log is durable; tracing scrolls.

Load-bearing today?

  • (1) — no. Nothing in Nvisy.toml claims SOC2/GDPR/HIPAA. Retention policies exist on the wire but no consumer is guaranteed to be watching. If nobody's asking "prove it," durable audit is over-engineering.
  • (2) — no. No user-visible complaint mechanism exists. No support ticket flow.
  • (3) — tracing::warn! on the sweeper's sweep_row failures already covers this. Persisting to fjall is redundant unless (1) or (2) becomes real.

Also: RetentionScope::AuditLogs

Policy.retention[AuditLogs] was in the schema from day one but it's aspirational — audit events aren't policy-scoped, so "this policy sets audit retention to N days" doesn't cleanly map. The natural home for audit retention is deployment config (server-wide setting), not per-policy. When phase 5 lands, we should either:

  • Reinterpret AuditLogs scope as deployment-wide (ignore the policy value at compile time; use server config), or
  • Deprecate the enum variant (leave for wire back-compat, don't use it).

Options when we revisit

  1. Minimal audit — only FileDeleted { RetentionExpired } emitted by the sweeper. GET /api/v1/audit-events for read. Skip user-triggered emits (they duplicate DELETE /files/{id} returning success). Skip audit retention (accept audit rows accumulating; file another issue when it matters). Cheapest way to unlock (2) and (3).
  2. Full audit — plus UserRequested emits, plus audit retention wired through server config, plus any other event kinds (rule fired, override applied, run cancelled, etc.).
  3. No audit — keep the current tracing output as the operator surface. Delete RetentionScope::AuditLogs from nvisy-core to shrink the schema.

Decision needed

Whether to build any of this, and if so which slice. Blocked on a real user asking for it (compliance officer, support workflow, or debugging pain).

What's already in the repo

Nothing. First pass was backed out; only this issue remains.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architecturearchitectural decision records and cross-cutting design issuesengineredaction engine, pipeline runtime, orchestration, configuration

    Type

    No type

    Fields

    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