AI Workload Observer — watches what AI tools are doing on your machine.
Everyone's running AI agents now — Cursor, Copilot, local LLMs, MCP servers, coding assistants. These tools read your files, execute commands, make network calls, install plugins. Nobody has visibility into what they're actually doing at the OS level.
ClawGuard sits on your machine and observes. It monitors process execution,
network connections, and application behavior — but it never kills processes,
blocks connections, executes commands, or modifies anything on the host. It
reads OS state and writes only to its own state directory (~/.clawguard/).
ClawGuard has three components: a collector (every 15s, free), a reactive path (Haiku, per event), and a three-phase sweep (Sonnet, on-demand).
The collector observes the OS and compares everything against a world model
(known.json) that the LLM builds and maintains. When the collector sees
something not in the world model, it fires a detection and queues a wake event.
The reactive path stubs unknowns immediately. The sweep wakes itself when
new findings or unclassified stubs appear — there is no fixed schedule.
Every 15s (collector — no LLM, zero cost):
→ snapshot processes, connections, apps
→ enrich new processes (codesign, lsof, parent chain, DNS)
→ detect 8 deviation types against known.json
→ build sessions.json (agent process trees)
→ emit per-session events (spawn, exit, LLM connection)
→ log changes, queue wake events for reactive
Per wake event (reactive — Haiku, ~$0.001 each):
→ stub in stubs.json (binary path, silences detection #1)
→ evaluate process: create finding if suspicious
On demand (sweep — 3 phases, each gets only the data it needs):
Phase 1 — Observe (Sonnet, every sweep):
→ analyze snapshot + detections + findings
→ investigate with tools (traces execute same-cycle)
→ discover LLM endpoints from connections
Phase 2 — Learn (Sonnet, when stubs/detections/findings exist):
→ classify stubs into known.json with display names
→ fix connection patterns and watch thresholds
→ evaluate detections: TP/FP/FN
→ map identities to LLM providers
Phase 3 — Assess Identities (Sonnet, when identities have events):
→ judge per-identity behavior from merged event timeline
→ determine intent, risk level, anomalies
→ write last_risk / last_assessment onto known.json entry
→ OTel-ready: merges os + otel event sources
Sweep self-triggers (no fixed schedule):
→ first sweep at 60s (let reactive accumulate context)
→ new finding appears (60s cooldown)
→ unclassified stubs exist (60s cooldown)
→ no fallback — sweep only runs when there's work to do
Auto-resolve:
→ findings resolve when their process is reviewed (trusted)
→ deterministic, no LLM decision needed
End of run (report — Sonnet):
→ executive summary, AI tools, concerns, cost
First run: sweep learns the environment. Subsequent sweeps refine the world model. Between sweeps, the collector detects autonomously at zero cost.
ClawGuard's reasoning engine cost is dominated by sweeps; reactive Haiku calls are ~$0.001 each. A typical sweep is $0.30–0.50 end-to-end (Phase 1 + Phase 2 + Phase 3). The total depends on how many sweeps fire, which depends on how much new activity appears on your machine.
| Run length | Stable machine | Active dev workflow |
|---|---|---|
| 10 min | $0.40–0.80 | $0.80–1.50 |
| 60 min | $1.50–2.50 | $3.50–5.00 |
Measured on a developer laptop with multiple AI agents running concurrently (7+ Claude Code sessions, MCP servers, parallel tool calls). 10 sweeps fired over 60 minutes; the world model never fully converged because of unidentified high-CPU processes that re-trigger investigation each sweep.
The numbers are the same on Anthropic and Bedrock — both providers price
Sonnet at $3/M input + $15/M output and Haiku 4.5 at $1/M / $5/M. Bedrock
also offers Nova models for cheaper runs (--provider bedrock --model us.amazon.nova-pro-v1:0).
Optimizations on the roadmap (prompt caching, cost-budget cap, smaller Phase 3 input) target the active-workload number specifically.
pip install -e .
export ANTHROPIC_API_KEY=sk-ant-...Requires Python 3.10+. By default ClawGuard uses the Anthropic API.
For AWS Bedrock instead: pip install -e '.[bedrock]' and pass
--provider bedrock.
After install, the clawguard command is available in your PATH.
Platform support. Collector, reactive path, sweep, and dashboard work on
macOS and Linux. macOS has extra investigation tools the sweep can call
(codesign_verifier, launchagent_enumerator) and an Applications scan;
on Linux those tools no-op and the core detection set still runs.
# One-shot snapshot of everything
clawguard snapshot -v
# Show AI processes (grouped by tool)
clawguard ps
# Show network connections (LLM endpoints highlighted)
clawguard net
# Current state summary (text dashboard)
clawguard status
# Timed run (recommended for evaluation)
clawguard watch -m 10
# Or via module
python -m clawguard.run --minutes 10
# Use Bedrock instead of Anthropic
python -m clawguard.run --minutes 10 --provider bedrock --profile myprofile
# Dashboard (separate terminal)
python -m clawguard.dashboardOptions:
--minutes N— run duration (default: 10)--interval N— seconds between collection cycles (default: 15)--provider {anthropic,bedrock}— LLM provider (default: auto-detect from env)--model MODEL— model alias for sweep/report (default:claude-sonnet-4-5)--reactive-model MODEL— model alias for reactive (default:claude-haiku-4-5)--profile NAME— AWS profile (Bedrock only)--region REGION— AWS region (Bedrock only)--seeds— use seed lists (default: discover everything from scratch)
The collector runs 8 autonomous detection patterns against the world model:
| # | Detection | Fires when | Severity |
|---|---|---|---|
| 1 | Unknown process | Not in known.json + external connections (skip if reviewed) | MEDIUM |
| 2 | Baseline violation | Connection outside known patterns | MEDIUM |
| 3 | Threshold breach | CPU/instances/connections exceed watch limits | HIGH |
| 4 | Statistical anomaly | Metric >3σ from rolling baseline | LOW→MEDIUM |
| 5 | File modification | Watched file mtime changed | HIGH |
| 6 | Unsigned + network | Fails codesign + external connections | HIGH |
| 7 | Sensitive file access | AI process has credentials/keys open | HIGH |
| 8 | LLM endpoint connection | Any process connects to known LLM API | LOW |
Reviewed processes still trigger detections #2–#8. Only detection #1 (unknown process) is suppressed for reviewed entries.
The sweep uses investigation tools to go deeper:
| Tool | What it does |
|---|---|
| process_inspector | PID → cmdline, open files, connections |
| process_tree_builder | PID → full ancestry chain to launchd |
| network_connection_resolver | IP → reverse DNS, cloud provider |
| file_inspector | path → stat, directory listing, or content |
| codesign_verifier | PID → binary signature, authority, team ID |
| file_hash_verifier | path → SHA256 hash |
| loopback_port_resolver | port → process listening on localhost |
| launchagent_enumerator | all LaunchAgents/Daemons with label, program, schedule |
Web dashboard at localhost:8384 (run python -m clawguard.dashboard):
- Agent Sessions — process trees with LLM calls, per-session event timeline
- AI Activity — AI tools and LLM-connected processes with provider badges
- Non-AI Activity — tools with active findings shown expanded, other tools collapsed under "Other ▸"
- Active findings with severity, risk tags, and resolve button
- Recent detections and capability requests
- Live process counts and CPU from
live.json - Auto-refreshes every 5 seconds
~/.clawguard/
├── known.json Identity model — LLM-chosen names, types, binary paths
├── stubs.json Unclassified processes (binary path keyed, sweep promotes)
├── sessions.json Live agent sessions (collector writes each tick, ephemeral)
├── events/ Per-session event logs (spawn, exit, LLM — JSONL, append-only)
├── detections.jsonl Collector signals (autonomous, append-only)
├── findings/ LLM findings (curated, one JSON per finding)
├── sweeps/ Sweep results (one JSON per sweep)
├── requests.jsonl Capability requests (append-only)
├── queue/ Wake events for reactive (transient)
├── observations/ Raw JSONL daily logs
├── audit/ All LLM call logs
├── runs/ Per-run archives
├── rules/ Sweep-authored config (LLM endpoints, usage mappings)
└── reports/ End-of-run reports
- OS telemetry (process list, network connections) — medium trust. Harder to spoof, but process names are attacker-controllable.
- Observed artifacts (AI config files, traces) — low trust. Read-only, never executed, tagged as untrusted in LLM prompts.
- LLM-generated output (findings, classifications) — hypotheses. Every finding carries provenance: source, generationMethod, modelId, timestamps. Raw observations are append-only and always preserved.
Prompts define GOAL + CAPABILITIES + SCHEMA only — no behavioral hints. The data model makes good decisions obvious:
reviewed/review_notes— sweep remembers its own investigation conclusionstype— sweep classifies identity type (platform, agent, mcp-server, process)binaries— full binary paths ground identity in OS truthparent— OS parent chain forms tool groups automatically (tracked in sessions.json)sweepCount— findings track how many sweeps they've persistedfirst_seen— preserved across phase 2 merges- Resolved findings visible to phase 1 — prevents re-escalation
- Auto-resolve: findings resolve when process is reviewed (trusted)
Giving the LLM fewer options produces better results. When phase 2 could both "fix the world model" and "resolve findings," it always chose the easier option (resolve) without fixing the root cause. Removing the shortcut forced it to do the real work — 0 active findings after 2 sweeps vs 11-17 previously.
We tried splitting the sweep into 5 focused phases (Name, Tune, Assess, Map, each with minimal context). It was worse. Each phase couldn't see what the others did — phase 3 (Tune) couldn't see what phase 2 (Name) just added, phase 5 (Map) ran on stale data. More LLM calls, more cost, worse holistic decisions. The LLM needs full context to make good tradeoffs.
Collapsed back to 3 phases: Observe, Learn, Assess Sessions. Phase 2 (Learn) gets stubs + detections + findings + connections in one call. Phase 3 (Assess) stays separate because it genuinely needs different data (event timelines, not detections). The rule: split phases by data concern, not by task.
Bedrock connections close before the 15s snapshot catches them. Connection-level
LLM detection doesn't work for HTTP/2 request-response patterns. Solution:
rules/llm_usage.json maps identities to providers at the identity level.
Phase 2 identifies providers from the session's child process connections
(EC2 reverse-DNS hostnames), and the collector reads the mapping to attribute
LLM activity to sessions even without live connections.
Some tools install multiple versions side by side (e.g. tool/1.28.1/,
tool/2.0.0/). The LLM only sees one version's paths when naming.
Binary index normalizes version segments to /* so all versions match
the same identity. Without this, sessions for older versions are invisible.
The collector diffs each tick against the previous to emit events (spawn,
exit, LLM connection) per session. Events use an OTel-compatible format
with a source field ("os" for ClawGuard, "otel" for future agent traces).
Phase 3 (Assess) reads event timelines to judge agent behavior — this is
the foundation for anomaly detection when OTel correlation is added.
clawguard/
├── run.py Main loop: reactive, sweep, report (590)
├── tools.py Investigation tools + input sanitization (277)
├── prompts.py Prompt builders (goal + schema, no behavioral hints) (377)
├── dashboard.py Web dashboard (localhost:8384) (525)
├── __main__.py CLI commands: snapshot, ps, net, status, watch (272)
├── agent_discovery.py Discover AI agent configs and MCP servers (151)
├── collector/ Snapshot, enrich, detect deviations (762)
├── observe/ OS-level observers (processes, network, apps)
├── reasoning/ LLM client (Anthropic + Bedrock) + cost tracking
├── investigation/ Finding types, validation, category mapping (116)
├── state/ State management, shared helpers, run archiving
└── audit/ JSONL audit logging