unexpected_user
Critical
An account outside ALLOWED_USERS ran an agent. The alert carries loginuid, so “alice ran an agent as root via sudo” arrives as a fact instead of a mystery.
Record of observation
agentwatchdog watches AI coding agents from outside the agent — from /proc, where cooperation isn’t a variable. It never reads your prompts, and there is a test that fails if it ever does.
claude -p "rotate the staging database credentials before Friday" --model opus
secret_flags-p takes a prompt
codex exec "summarise the incident timeline for the postmortem" --sandbox read-only
positional · default-denycodex carries prompts as bare arguments
aider -m "patch the auth bug" --api-key sk-EXAMPLE-NOT-A-REAL-KEY-000
secret_flags · credential shapemasked twice over
spindle chat "draft the migration plan" --task "internal"
no fingerprintan agent we have never seen — every positional denied anyway
Synthetic command lines, real rules. In the tool itself the raw string is never written at all — redaction happens before anything reaches disk, so there is no “before” on your host to reconstruct.
Writes nothing. Sends nothing. Safe on the host you’re suspicious about.
The gap
In-agent observability — OTel exporters, hooks, wrapper commands, proxies — works only while the agent plays along. Skip the environment variable, don’t install the hook, call the binary directly, and it goes blind. The runaway cron job, the misconfigured unit and the account that shouldn’t be here are precisely the cases that never play along.
/procBoth are worth having. If you want per-token cost dashboards, use an OTel-based tool — this is not that. This is the audit trail for the machine.
Detectors
Every alert carries the command to run next. Repeats are suppressed per situation rather than per scan — one account running fifty agents is one finding, because a monitor that fires every sixty seconds is a monitor somebody turns off.
unexpected_user
Critical
An account outside ALLOWED_USERS ran an agent. The alert carries loginuid, so “alice ran an agent as root via sudo” arrives as a fact instead of a mystery.
parent_spawn_storm
Critical
One parent process keeps spawning agents. No human does this — it is a restart loop, and it bills you for every attempt. The alert carries the ancestry, because the agent is the symptom and the parent is the bug.
user_high_frequency
Warning
One account is starting agents faster than a person types.
long_running_process
Warning
A non-interactive agent outlived its limit. Interactive and SDK sessions are exempt: a session someone is sitting in front of is not a hung process, and treating it as one makes this useless on exactly the hosts it is for.
high_cpu · high_mem
Warning
Sustained abnormal resource use. Measured as a lifetime average with a minimum sample age, so the spike every agent produces while starting up doesn’t become an alert every agent produces.
agents_during_high_load
Warning
Several agents landing on a host that is already struggling. No single process is at fault, which is exactly why nothing else reports it.
The privacy contract
This thing reads command lines for a living. That is only acceptable if the limits are enforced rather than described — so every one below is wired to a test, and CI runs them as their own job, where a failure is impossible to miss.
test_
/proc/PID/environ is not opened on any code path. The test records every open() during a scan and fails if that file appears. API keys living in the environment cannot pass through this tool.
test_
Command lines are redacted before they are written, using per-agent rules — because every CLI keeps its prompt somewhere different.
test_
An argument survives only if a fingerprint vouches for it as a known subcommand. An unrecognised flag is treated as boolean, so the token after it is covered too. An agent released after this version leaks nothing; it only shows up with less detail.
test_
Provider key prefixes, JWTs and long opaque tokens are masked even in positions no rule anticipated — which is where a credential ends up when somebody pastes a command wrong.
test_
The summary is assembled from scratch into a fixed shape rather than filtered out of an event, then checked against a key allowlist at runtime. An export that cannot be proven anonymous is refused, not published.
Run agentwatchdog selftest to watch these hold against the agents actually running on your machine, not against ours.
agents.d
Each agent is a JSON fingerprint describing how to recognise it and where it carries its prompt. Operators can add or correct one in /etc/agentwatchdog/agents.d without waiting for a release, and a fingerprint plus one redaction test is the easiest useful pull request this project takes.
| Agent | Flag table | Where the prompt lives |
|---|---|---|
| Claude Code | Verified against the binary | Bare positional, and -p |
| OpenAI Codex CLI | Verified against the binary | Positional, at top level and after exec |
| aider | Verified against 0.86.2 | -m, --message, --msg |
| Gemini CLI | From documentation | -p, --prompt |
| OpenCode | From documentation | Positional after run |
Two traps the verified tables caught, as a caution against writing these from memory. Codex takes a prompt as a positional at the top level, not only after exec. And Codex’s -p is --profile, while Claude Code’s -p is the prompt — port one agent’s rules to the other and the tool dutifully masks a profile name while writing the prompt to disk. Both are now regression tests.
Install
One file, nothing to build, no dependency beyond python3. Linux only — the design is /proc.
1 · Look
$ curl -fsSLO https://github.com/Ymakercc/agentwatchdog/releases/latest/download/agentwatchdog.pyz
$ chmod +x agentwatchdog.pyz
$ ./agentwatchdog.pyz dry-run
# writes nothing, sends nothing, updates no state
The complete output of a scan, on your host, before the tool has done anything at all.
2 · Check
$ ./agentwatchdog.pyz selftest
Proves the redaction rules against every built-in fingerprint and against the command lines actually running on the machine you are standing on.
3 · Install
$ sudo mv agentwatchdog.pyz /usr/local/bin/agentwatchdog
$ sudo agentwatchdog install
# systemd timer, config, log rotation. Alerts stay local
# until NOTIFY says otherwise.