Audit logging & change data capture#

Mutations are captured to an audit_log table via CockroachDB change-data-capture (CHANGEFEED) and read back over HTTP. Tracked tables: devices, racks, crac_units, cables, and fabric_links.

Reading the history#

serve exposes the change history on the HTTP port:

curl -s 'localhost:8081/audit/history?table=racks&limit=50' | jq

Two delivery paths, one destination#

Both paths land each change in audit_log; pick one.

  • In-process core changefeed (default in k8s, license-free). Set AUDIT_CHANGEFEED=true and serve runs an EXPERIMENTAL CHANGEFEED over its own connection, recording each change. No inbound network needed — ideal for self-hosted/dev. Single-replica only: this feed runs inside every replica, so at more than one replica each mutation is recorded once per replica (duplicate audit_log rows). serve warns about this at startup. Run it on exactly one replica, or use the webhook sink below.

    AUDIT_CHANGEFEED=true rackattack serve
    # ... perform some mutations ...
    curl -s 'localhost:8081/audit/history?table=racks' | jq
  • Native webhook sink (CockroachDB owns delivery, retries, and checkpointing). Apply deploy/changefeed.sql, which creates a CHANGEFEED … INTO 'webhook-https://…/audit/webhook' pointing at the POST /audit/webhook sink. This is the right choice at more than one replica: it’s a single cluster-owned feed regardless of replica count, so each mutation is recorded exactly once. Leave AUDIT_CHANGEFEED unset so changes aren’t double-recorded, and authenticate the sink with AUDIT_WEBHOOK_SECRET.

Licensing note#

On CockroachDB v24.3+ (the v26 CCL build used here) the webhook sink runs without an enterprise license key; a long-running production changefeed should still install the free Enterprise license to avoid grace-period throttling. The core-changefeed path needs no license.

The implementation lives in internal/audit.