Atlas recon

Audit trail and recovery drills for an agentic coding CLI on Linux

Git commits are your real undo and a PostToolUse hook is your real audit log — Claude Code's own transcripts and /rewind are convenience layers that expire, miss bash, and are writable by the agent.

12 sources ~3 min read #12 claude-code · audit-log · backup · git · proxmox · linux · observability

Decision. Treat git as the undo and a PostToolUse hook writing outside the repo as the audit log. Claude Code’s built-ins are convenience, not evidence: transcripts are deleted after 30 days [1], and /rewind cannot undo anything a bash command did, nor a subagent’s edits [4]. Then rehearse one restore — “a backup you have never restored from is not a backup, it is a theory” [12].

How you know what it did

Layer Gives you Gap
Session transcript JSONL per session at ~/.claude/projects/<project>/<id>.jsonl; /export for humans [1] 30-day retention, format is internal and breaks between releases [1]
claude -p --output-format json Result, session ID, usage, cost of one run as parseable JSON [1] Per-run only, not a standing log
PostToolUse hook Hook JSON on stdin — session_id, cwd, tool_name, tool_input, tool_result, transcript_path — append it anywhere [3] You write it; agent can rm it if writable
OpenTelemetry CLAUDE_CODE_ENABLE_TELEMETRY=1 + OTEL_LOGS_EXPORTER emits claude_code.tool_decision / tool_result / api_request; add OTEL_LOG_TOOL_DETAILS=1 for actual commands and file paths [2] Needs a collector (Loki/SigNoz)
auditd -a always,exit -F arch=b64 -S execve -k user-commands, query ausearch -k user-commands [7] Kernel-level truth, noisy and hard to read

Cheap extras: HISTTIMEFORMAT to timestamp shell history [8], and script --log-timing to make a replayable typescript of a whole session [9].

⚠ Send the hook/OTel log to a path the agent cannot write — a systemd-journal socket, a Loki endpoint, or a Proxmox-side collector. An audit log inside the workspace is not an audit log.

How you undo it

Git first. Commit before every prompt; one worktree per task. git reflog recovers a bad reset --hard or force-push — but reachable entries expire after 90 days and unreachable ones after 30 [5]. ⚠ An agent allowed to run git can also rewrite history: a real report had git reset --hard run unauthorized, destroying hours of work → “the safeguards have to be outside the model” [6]. Deny git reset --hard, git push --force, git clean in permissions, and push to a remote the agent has no credentials for.

Snapshots second. Timeshift on btrfs is near-instant, but ⚠ excludes /home by default — where your code and ~/.claude live [10]. On Proxmox, qm snapshot <vmid> <name> --vmstate 1 then qm rollback <vmid> <name>; RAM-inclusive snapshots auto-start the VM on rollback [11]. Rollback is seconds-to-minutes — the cost is losing everything since the snapshot, so snapshot before an unattended run, not nightly.

The 5-step drill

  1. qm snapshot 101 pre-agent --vmstate 1 before an unattended session [11].
  2. Let the agent do real destructive work: rm -rf src/, then git reset --hard HEAD~3.
  3. Recover with git alone — git reflog, checkout the pre-reset SHA [5]. Time it.
  4. Now recover the file the agent deleted via bash, which /rewind cannot restore [4]: qm rollback 101 pre-agent [11].
  5. Reconstruct what happened from the hook log alone, with ~/.claude/projects deleted [3]. If you can’t, your audit trail is the transcript — and it expires [1].

Citations · 12 sources

Click the Citations tab to load…