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.
What gets logged
Section titled “What gets logged”| Category | Examples |
|---|---|
| Authentication | Login success/failure, SSO handshake, 2FA verify, session end |
| RBAC | Role create/update/delete, user role assignment, permission check failures |
| Stacks | Create, edit, deploy, stop, scale, migrate, remove |
| Containers | Exec session (not the content), start/stop/kill, remove |
| Hosts | Add, remove, drain, cordon |
| Backups | Job create/edit/delete, run (success/failure), restore |
| Settings | OIDC config change, env var change, TLS cert rotation |
| Security | CVE 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.
Entry shape
Section titled “Entry shape”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).
Hash chain integrity
Section titled “Hash chain integrity”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.
Search and filter
Section titled “Search and filter”Audit → Search supports:
- Actor (user, SSO group, API token)
- Action (e.g.
stack.deploy, or prefixstack.*) - Target (stack, host, container by name or ID)
- Result (success / failure)
- Time range
- IP / session
Combine filters with AND. Results paginate at 100 entries.
Export
Section titled “Export”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.
Retention
Section titled “Retention”Default: keep forever. For teams that need less, configure a retention policy under Settings → Audit → Retention:
| Mode | Behaviour |
|---|---|
forever (default) | Never prune. |
days | Keep last N days; older rows are deleted on the 03:00 daily job. |
archive_local | Write older rows as NDJSON to a configurable local directory, then prune from DB. |
archive_target | Same, 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.
Webhook
Section titled “Webhook”Send every audit entry to a webhook in real time:
Settings → Audit → Webhook:
- URL —
http(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"}See also
Section titled “See also”- RBAC — the
audit.viewandaudit.exportpermissions - Hardening — audit log protection best practices
- API · Audit — programmatic query interface