Skip to content

Audit Log

Every action in dockmesh — deploy, scale, migrate, user invite, role change, SSO login, backup run — is written to an audit log. Entries chain together with SHA-256 hashes, so altering any row breaks the chain and the UI flags the tampering.

CategoryExamples
AuthenticationLogin success/failure, SSO handshake, 2FA verify, session end
RBACRole create/update/delete, user role assignment, permission check failures
StacksCreate, edit, deploy, stop, scale, migrate, remove
ContainersExec session (not the content), start/stop/kill, remove
HostsAdd, remove, drain, cordon
BackupsJob create/edit/delete, run (success/failure), restore
SettingsOIDC config change, env var change, TLS cert rotation
SecurityCVE suppress/accept, failed mTLS handshake, revoked cert

Read-only actions (list pages, inspect) are not logged by default — would create too much noise. Enable audit.include_reads for strict compliance modes.

Each row:

{
"id": 24318,
"timestamp": "2026-04-17T14:02:31.448Z",
"actor": {
"type": "user",
"id": 7,
"username": "alice",
"ip": "10.0.2.18",
"session_id": "sess_abc123",
"sso_provider": "azure-ad"
},
"action": "stack.deploy",
"target": {
"type": "stack",
"id": 42,
"name": "analytics",
"host": "prod-01"
},
"result": "success",
"before": { "state": "stopped" },
"after": { "state": "running", "image_digest": "sha256:..." },
"prev_hash": "8d21...e3f0",
"hash": "a7f3...9c4b"
}

The hash is SHA-256(prev_hash || canonical_json_of_this_row).

On every page load, the UI runs a verification pass on the visible range. If any entry’s hash doesn’t match the computed hash of its prev_hash + content, the UI shows a red “Chain broken at entry #N” banner.

Common reasons a chain might break:

  • Manual DB edit (not through dockmesh)
  • Restoration from an older backup that overwrites later entries
  • Disk corruption

A broken chain is never “fixed automatically” — investigating is on you. The UI preserves both the computed and stored hash so you can see exactly where divergence happened.

Audit → Search supports:

  • Actor (user, SSO group, API token)
  • Action (e.g. stack.deploy, or prefix stack.*)
  • Target (stack, host, container by name or ID)
  • Result (success / failure)
  • Time range
  • IP / session

Combine filters with AND. Results paginate at 100 entries.

Audit → Export produces CSV or JSON Lines:

  • Live range — whatever the current filters show
  • Full dump — every entry from a date, chained hashes included for independent verification

Pipe JSON Lines to your SIEM (Splunk, Datadog, ELK) via a cron job hitting /api/v1/audit/export.

Default: keep forever. For teams that need less, configure a retention policy under Settings → Audit → Retention:

ModeBehaviour
forever (default)Never prune.
daysKeep last N days; older rows are deleted on the 03:00 daily job.
archive_localWrite older rows as NDJSON to a configurable local directory, then prune from DB.
archive_targetSame, but write to any configured Backup Target (SFTP, SMB, WebDAV, S3, local).

Archive-before-prune means a write failure halts the job — data is never lost to a broken archive destination. Before each prune, dockmesh writes a special audit.chain_bridge audit entry that records the prune count, last-pruned row hash, and cutoff timestamp. The hash chain remains verifiable across prune boundaries: GET /audit/verify still walks from the oldest remaining row forward and never surfaces a break.

You can also trigger a run immediately (after changing policy or for a one-off cleanup) via the Run now button on the same panel, or POST /api/v1/audit/retention/run.

Send every audit entry to a webhook in real time:

Settings → Audit → Webhook:

  • URLhttp(s)://…. Empty disables dispatch entirely.
  • HMAC secret — optional. When set, every request carries X-Audit-Signature: sha256=<hex> computed over the raw body.
  • Filter actions — list of exact action names (stack.deploy) or wildcard prefixes (stack.*, rbac.*). Empty = every entry.

Delivery is decoupled from the request path: a dispatcher goroutine drains a bounded queue (256 entries) so a slow receiver never blocks your stack deploys. On queue overflow the oldest pending event is dropped (logged). On failure the dispatcher retries up to 5 times with 500ms × 2^n backoff; 4xx responses are terminal (the receiver rejected the payload), 5xx and network errors retry.

Use the Send test event button to verify the receiver before flipping on the live feed — it bypasses the filter and posts a synthetic audit.webhook_test entry.

Payload shape:

{
"id": 24318,
"ts": "2026-04-17T14:02:31.448Z",
"user_id": "...",
"action": "stack.deploy",
"target": "analytics",
"details": "{...}",
"row_hash": "a7f3...9c4b"
}
  • RBAC — the audit.view and audit.export permissions
  • Hardening — audit log protection best practices
  • API · Audit — programmatic query interface