What ShadowClaw is¶
ShadowClaw detects unsanctioned AI use on an endpoint by correlating what a process is computing, where it is talking, and what it does to the machine — then ships the result over OpenTelemetry.
It is provider-agnostic on purpose. A catalog of 59 known providers gives precise attribution, but detection does not depend on it: a scriptable runtime that burns sustained CPU, or reaches an inference-shaped endpoint outside the approved gateway, is flagged whether the vendor is OpenAI, a self-hosted Llama server, or a company that launches next week.
Detection only — enforcement lives downstream¶
ShadowClaw is a detector, not an enforcer. It observes, scores, and records shadow-AI activity on the endpoint. It does not block processes, terminate agents, quarantine hosts, or honour policy exceptions.
That split is deliberate. A detector whose blind spots are configurable is a detector you cannot reason about, and a complete tamper-evident ledger is the input enforcement needs.
There is no allowlist, by design
No process and no domain can be configured into silence, and tests/ fails the build if anyone adds one back. sanctioned_endpoints is a label, not a mute — see Configuration.
The intended downstream path is the DefenseClaw ecosystem. ShadowClaw exports structured findings over OTLP into DefenseClaw's local observability stack — or any compatible collector — where they sit alongside DefenseClaw's own AI discovery and security telemetry. No modification to DefenseClaw or Cisco AI Defense is required: ShadowClaw uses integrations those products already expose. See DefenseClaw integration.
Enforcement and response — isolating a process, revoking credentials, blocking egress, orchestrating remediation — belong to components built for that job. ShadowClaw hands them a finding with process identity, attribution confidence, and kill-chain context over telemetry paths those products already consume. What happens next is policy, not detection.
Why a sensor as well as the Collector¶
macOS has no eBPF, and this is the constraint that shapes the whole design.
The OpenTelemetry Collector's hostmetrics network scraper is host-wide: it tells you bytes moved, never which pid moved them. So it can show a burst of egress that coincides with a CPU spike, but it cannot say the burst belongs to rogue_agent.py. Attribution is the entire value of a shadow-AI control, so the sensor supplies it using the tools macOS does give you.
| Source | Gives us | Needs root |
|---|---|---|
lsof -iTCP |
per-process peer IP, port, TCP state | no (own user only) |
ps CPU-time deltas |
true instantaneous CPU per pid | no (every user) |
| catalog forward resolution | IP → provider hostname | no |
tcpdump on port 53 |
exact IP → hostname from real DNS answers | yes |
eslogger (Endpoint Security) |
process, file and identity events | yes, plus Full Disk Access |
The two planes are not scoped alike, and that asymmetry is why root changes what this can see. Unprivileged ps -Ao returns RSS and CPU time for the entire process table. Unprivileged lsof returns established sockets for your user only. So without root you watch the whole machine compute and only your own share of it talk — and a finding needs both halves for the same pid.
What makes it different¶
Per-process attribution
The thing host-wide OTel host metrics cannot provide on macOS. A finding names a pid, an executable, an owner, and a peer.
Provider-agnostic behaviour
The catalog sharpens attribution; it is not a prerequisite. Behavioural signals still fire for a vendor nobody has catalogued.
Kill-chain correlation
Plane C scores an ordered sequence across process descendants and time, not five disconnected alerts nobody joins up.
Durable local evidence
A hash-chained ledger is written before network export, so a collector outage can never cost a finding.
No virtualenv, no wheels
Standard-library Python only, speaking OTLP/HTTP JSON directly. It runs on the stock macOS python3.
Degraded state is reported
A sensor that could not look never reports a clean host. Missing coverage is named in the startup banner.
Where it runs¶
macOS, Linux, and Windows all run all three planes. Linux planes A and B — compute and egress — go through the same sensor loop, reading /proc directly: no ps, no lsof, no third-party dependencies, which is exactly what a minimal container or cloud host has. Plane C (agent actions) comes from the netlink process connector (cn_proc), which needs no root, composed with fanotify via ctypes for credential and persistence file events, which does. An unprivileged Linux host therefore still gets Plane C's process half, with the file half honestly reported absent rather than a false all-clear — and says so in the banner. Windows runs Plane C from a private SystemTraceProvider process stream whose documented process payload includes command line, plus a Security event log subscription with event 4688 as an independent argv fallback; Advanced Audit Policy gates the latter. If the argv stream is unavailable, the banner reports Plane C as degraded rather than letting four command-line-derived signals look quiet. The raw sensor runs on Linux and Windows today; systemd units and .deb/.rpm packaging are the remaining Linux port work. See Multi-platform architecture.