grithdocs

Logging & audit retention

Where logs live, how big they grow, and how to manage retention.

grith produces two kinds of log: the operational log (daemon health, errors, configuration changes) and the audit log (every supervised call decision). They live in different places and have different retention characteristics.

Operational log

LocationPurposeFormat
stderr (foreground daemon)LiveText
~/.local/share/grith/daemon.log (detached)RotatingText
journald (systemd)Live & retainedStructured

Tune verbosity:

[general]
log_level = "info"     # "trace" | "debug" | "info" | "warn" | "error"

trace is verbose enough to noticeably slow the supervisor. Use debug for filter tuning; leave at info otherwise.

Operational log rotation defaults: 10MB cap, 5 generations.

Audit log

The big one. Lives at ~/.local/share/grith/audit/audit.db. Each record is ~200–500 bytes. A busy agent (~50 calls/minute) produces:

WindowApprox size
1 hour~1.5MB
1 day~40MB
1 week~250MB
1 month~1GB
1 year~12GB

Retention

Those figures are the raw rate at which records accumulate; the active database does not grow without bound, because grith prunes it on a schedule. The active audit database keeps a bounded window of recent records — older records are archived (when cold storage is enabled) and then deleted from the active database in a contiguous prune that preserves the audit chain's integrity. Retention lives under the [audit] section:

[audit]
retain_full_days     = 30     # days of records kept in the active database
                              # (0 disables retention — keep everything)
retain_compact_days  = 7      # reserved; the single hash chain currently
                              # prunes on one cutoff, so this tracks
                              # retain_full_days today
cold_storage_enabled = true   # archive pruned records before deleting them
prune_interval_hours = 24     # how often the prune runs (0 = once on startup)

The daemon prunes once on startup and then every prune_interval_hours. With retain_full_days = 0, nothing is ever pruned and the active database keeps every record.

Cold archives

When cold_storage_enabled = true, each prune first writes the records it is about to delete to ~/.local/share/grith/audit/cold/YYYY-MM-DD.jsonl.zst — date-partitioned, zstd-compressed NDJSON — and only then removes them from the active database. The archive is written before the delete, so an interrupted prune leaves the records in both places rather than losing them.

Reclaiming disk space

Deleting rows from SQLite does not shrink the database file: the freed pages go on an internal freelist and are reused by later writes, so the file can stay large after a prune. To rewrite the database and reclaim that space, run:

grith audit compact

compact rewrites the database into a fresh copy, verifies the copy's audit chain, and atomically swaps it in — on any failure the original is left untouched. It is a manual maintenance operation and never runs on a timer. Because it rewrites the file, it needs exclusive write access: stop the daemon first (grith daemon stop), and it refuses to run on a quarantined chain.

To inspect the audit chain's integrity — the verification result and any forks or gaps — without modifying anything, use the read-only grith audit diagnose.

Export and offload

Export records for offline retention or ad-hoc analysis:

grith audit export --format json > audit.jsonl
grith audit export --format csv --offset 0 --limit 5000 > audit.csv

export takes --format (json or csv), --offset, and --limit. Records removed by retention are already kept automatically in the cold archive described above, so there is no separate "prune to offload" step.

Or stream to a SIEM continuously (Enterprise): SIEM integration.

What's in an audit record

{
  "id": "uuid-...",
  "ts": "...",
  "session": "...",
  "operation": "file_read",
  "target": "...",
  "decision": "allow|queue|deny",
  "resolved": "allow|deny|null",
  "composite_score": 4.2,
  "filters": [{ "name": "...", "score": ... }, ...],
  "profile": "...",
  "pid": 17421,
  "command": "claude",
  "user": "alice",        // for multi-user setups
  "annotations": {...}    // free-form, e.g. from filter contributions
}

Sensitive content (file bytes, payload contents) is not stored — grith records the fact of each operation, never the data it moved. How much routine activity is recorded is controlled by audit.completeness (decisions, spawns, io, all); higher tiers record more events for forensics at the cost of database growth, but none of them capture file or payload bytes.

Privacy

The audit log captures what the agent did. By design it doesn't include prompts, model outputs, or human-typed messages. For full session capture (e.g. for compliance), see Pro's session-recording feature (planned v0.2).

See also

Last updated: 2026-05-14Edit this page on GitHub →