This is Part 4 of Hardened Agentic Stack and the start of Phase 2: Runtime Integrity. Parts 1–3 hardened how the agent talks to the world and what authority it holds. This essay answers a different question: when the agent (or something it launched) still tries to exec npx, curl, or pip, who stops it — and how fast?
We pull from ClawQL’s sandboxing, Panguard ATR, and observability / SIEM modules, plus the practical TracingPolicy shapes used in Linux runtime protection (Tetragon). The pattern to name: Kernel-Level Kill-Switch.
The “Oh No” moment: the process tree that disagreed with the prompt
A ClawQL agent is summarizing a repository. ATR claims allow file_read, file_write under /workspace, and a narrow run_tests tool. Panguard allows the test call. Somewhere in a dependency README, a poisoned instruction nudges the model toward “install the missing helper”:
npx some-helper@latest
The MCP layer saw one approved tool: run_tests. The kernel saw a process tree:
node (agent / tool handler)
└─ sh
└─ npx
└─ npm
└─ curl | sh # postinstall from typosquat
By the time a human reads the Langfuse span, credentials may already be on a remote host. User-space “please don’t run curl” policy never got a vote on the grandchildren.
Panguard answered: was this tool call allowed?
The process tree answered: what actually ran?
Lesson: if your security story ends at the MCP dispatcher, you are auditing intent while the exfil rides execve.
| Layer | Takeaway |
|---|---|
| Problem | A compromised agent invokes a shell or package manager to exfiltrate (npx, curl, pip). |
| Infrastructure fix | Tetragon / eBPF on sys_execve — kill children outside the approved execution list instantly. |
| Architecture pattern | Kernel-Level Kill-Switch — move enforcement from user-space to kernel-space. |
Learning goal for this post
By the end you should be able to:
- Explain why protocol policy and kernel exec policy are complementary, not redundant.
- Roll out Tetragon TracingPolicies in observe → alert → Sigkill stages.
- Shape an exec allow/deny matrix for agent runtimes vs tool sidecars.
- Correlate kill events with Panguard WORM audits and SIEM.
- Know when macOS Seatbelt (laptop) vs Tetragon (Linux) vs Kata/gVisor (cluster) each apply.
ClawQL context: below the tool call
Part 3 put Panguard at the structured MCP boundary: ATR claims, JSON Schema, HITL, fail-closed nonces. That remains mandatory. Process containment covers the residual risk:
| Layer | Sees | Misses |
|---|---|---|
| Prompt / model | Natural language | Everything after the decision |
| Panguard | Named tool + params + claims | Children spawned inside an allowed handler |
| Tool handler | Its own spawn / exec | Scripts and postinstalls it did not expect |
| Kernel / Tetragon | Actual execve paths and process tree | Nothing that crossed execve without a hook match |
Classic residual cases:
- Approved tool, hostile grandchild.
run_tests→ npm script →npx→ postinstall. - Package managers at runtime.
pip install/npm installas “just fix the env.” - Shell pipes.
curl … \| bashafter a model follows untrusted text. - Compromised runtime. App logs lie or go dark; eBPF still sees syscalls.
Lesson: Panguard is the front door. Tetragon is the tripwire on every door the process tree opens afterward.
The architecture pattern: Kernel-Level Kill-Switch
Kernel-Level Kill-Switch means:
- Assume every agent workload will eventually attempt an unexpected
execve. - Put an eBPF policy on the host (Tetragon) that sees those attempts in-kernel.
- Start with
Post(observe), graduate high-confidence binaries toSigkill. - Emit forensic events into the same correlation path as Panguard (WORM + SIEM).
- Pair with image distrolessness, read-only rootfs, seccomp (Part 5), FIM (Part 6), and sandboxes (Part 7) — kill-switch is one layer, not the whole vault.
Agent / tool handler
│
▼
execve("/usr/bin/curl", …)
│
▼
Kernel hook (Tetragon eBPF)
│
├── no match → process continues (+ optional Post)
└── match forbidden binary
├── Sigkill (synchronous)
└── Post → Fluent Bit / OTel → WORM + SIEM
Tetragon nuance worth teaching: for execve, Sigkill stops the unauthorized process before useful work continues. For syscalls with side effects already underway (some writes), official guidance pairs Override where supported. Part 4’s primary control is kill on forbidden exec.
Observe first: what is the agent already spawning?
Do not jump to kill on day one. Install Tetragon on Linux nodes (or /etc/tetragon/tetragon.tp.d/ on edge hosts) and watch the agent binary’s children:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: clawql-process-exec-observe
spec:
kprobes:
- call: 'sys_execve'
syscall: true
args:
- index: 0
type: 'string'
- index: 1
type: 'string'
selectors:
- matchBinaries:
- operator: 'In'
values:
- '/usr/bin/node'
- '/usr/local/bin/node'
- '/app/clawql-agent'
matchActions:
- action: Post
Then baseline dangerous command and supply-chain families without enforcing:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: clawql-dangerous-commands-observe
spec:
kprobes:
- call: 'sys_execve'
syscall: true
args:
- index: 0
type: 'string'
- index: 1
type: 'string'
selectors:
- matchArgs:
- index: 0
operator: 'Postfix'
values:
- '/rm'
- '/dd'
- '/nc'
- '/netcat'
- '/ncat'
- '/curl'
- '/wget'
- '/chmod'
- '/chown'
matchActions:
- action: Post
---
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: clawql-supply-chain-observe
spec:
kprobes:
- call: 'sys_execve'
syscall: true
args:
- index: 0
type: 'string'
- index: 1
type: 'string'
selectors:
- matchBinaries:
- operator: 'In'
values:
- '/usr/bin/node'
- '/usr/local/bin/node'
matchArgs:
- index: 0
operator: 'In'
values:
- '/usr/bin/npm'
- '/usr/bin/npx'
- '/usr/bin/yarn'
- '/usr/bin/pnpm'
- '/usr/bin/pip'
- '/usr/bin/pip3'
matchActions:
- action: Post
Run observe-only long enough to build a per-image, per-role process baseline (a week is common). False positives from test runners and npm scripts are policy engineering problems — treat them that way instead of blaming the probe.
Local edge/Linux:
sudo mkdir -p /etc/tetragon/tetragon.tp.d/clawql
sudo systemctl enable --now tetragon
sudo tetra getevents -o compact
Enforce: Sigkill the high-confidence set
After the baseline, promote what should never run in production agent/tool images:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: clawql-kill-unauthorized-child-exec
spec:
kprobes:
- call: 'sys_execve'
syscall: true
args:
- index: 0
type: 'string'
- index: 1
type: 'string'
selectors:
- matchArgs:
- index: 0
operator: 'Postfix'
values:
- '/curl'
- '/wget'
- '/nc'
- '/netcat'
- '/ncat'
- '/npx'
- '/pip'
- '/pip3'
# add /bash /sh only after you confirm agents do not need them
matchActions:
- action: Sigkill
- action: Post
Agent-facing contract: surface exit 137 / SIGKILL to the user as a policy block — do not silently retry with wget instead of curl. Silent retries turn your kill-switch into a whack-a-mole.
Lesson: denylist + distroless beats denylist on a fat image. Renamed or copied binaries will evade string Postfix; remove the tools from the filesystem when you can.
Exec matrix: agent runtime vs tool sidecars
Don’t paste one allowlist onto every container. Role-shape the diets:
| Role | Allow (examples) | Forbid at runtime (examples) |
|---|---|---|
| Agent runtime | node, clawql-agent, maybe git | curl, wget, npx, pip, shells |
| Test sidecar | node, npm (if contract), git | npx, curl, pip, arbitrary bash |
| Python tool sidecar | python3, pytest | Runtime pip install, curl, shells |
| Build image (CI only) | toolchain as needed | — — never ship this image as the agent |
Layer pairing that actually holds:
- Distroless / minimal image — unauthorized binaries absent
- Read-only root filesystem
- Panguard command / ATR allowlists
- Tetragon kill on forbidden exec
- Falco as complementary detection
- Wazuh / SIEM correlation + WORM
Seccomp (Part 5) shrinks which syscalls remain; this Part shrinks which programs may start. Sandboxes (Part 7) shrink where those programs can reach if they somehow start.
Correlating kills with Panguard and SIEM
A kill without session context is a pager noisy orphan. ClawQL’s observability module expects canonical security events. Map Tetragon Posts into that shape:
{
"schemaVersion": "1.0",
"source": { "component": "tetragon", "namespace": "clawql-agents" },
"principal": { "agentId": "agent_123", "sessionId": "sess_456" },
"event": {
"type": "POLICY",
"subtype": "UNAUTHORIZED_EXEC_KILLED",
"outcome": "BLOCKED",
"severity": "HIGH"
},
"detail": {
"policy": "clawql-kill-unauthorized-child-exec",
"binary": "/usr/bin/curl",
"parentBinary": "/usr/bin/node",
"action": "SIGKILL",
"exitCode": 137
},
"traceContext": { "traceId": "…" }
}
Correlation patterns worth shipping:
| Pattern | Reading |
|---|---|
Panguard allow(run_tests) + Tetragon kill | Approved tool spawned unauthorized child |
| Panguard block + Tetragon dangerous exec | Injection or runtime probing both layers |
Tetragon kill(npx/pip) + egress/DNS deny | Supply-chain / exfil attempt |
Flow:
Tetragon / Falco
→ Fluent Bit or OTel Collector
→ Presidio redaction if needed
→ WORM audit
→ Wazuh / SIEM dashboards + quarantine hooks
Part 8 will deepen TraceID propagation from Langfuse into kernel events. For now: never drop the session / pod / policy labels that make a SIGKILL explainable.
Cluster vs edge vs macOS laptop
| Environment | Primary containment |
|---|---|
| Kubernetes | Tetragon on Linux nodes + Kata/gVisor RuntimeClass |
| Linux edge / bare | Local Tetragon TracingPolicies under /etc/tetragon/ |
| macOS laptop agent | Seatbelt / clawql sandbox — not Tetragon |
Cluster sketch (ClawQL runtime-class containment):
helm upgrade --install clawql ./charts/clawql-mcp -n openclaw --create-namespace \
--set security.kata.enabled=true \
--set security.kata.runtimeClassName=kata-qemu \
--set kyverno.runtimeClassPolicy.enabled=true
Seatbelt limits workspace paths and fork/exec on developer Macs. It is not a production substitute for Kata or Tetragon. Same defense-in-depth idea, different OS primitives — say that explicitly so laptop policy does not get cargo-culted onto nodes.
Trade-offs and operational gotchas
Denylists are incomplete. Renamed curl to /tmp/helper bypasses Postfix. Prefer absent binaries + read-only rootfs + kill of remaining high-confidence names.
Sigkill is blunt. Agents see 137. Teach the runtime to report “blocked by process policy” instead of inventing workarounds.
Observe before enforce. npm scripts spawning sh, pytest helpers, and git hooks will surprise you. Stage: Post → alert → Sigkill.
Platform constraints. Tetragon needs Linux eBPF/BTF. Some managed node images restrict probes. Kernel function names for advanced hooks can vary — pin policy versions to platform AMI/OS.
Noise. Logging every execve without selectors will drown SIEM budget. Scope selectors to agent binaries and namespaces.
Break-glass. Emergency namespaces or labels that skip kill policies must themselves be audited and time-bounded — same four-eyes mindset as ATR scope expansion in Part 3.
Safety check: what “trusted enough” looks like
You are in a healthier place when:
- Agent and tool images cannot find
curl/npx/pipon$PATHin production roles that do not need them. - Tetragon policies for those roles are in Sigkill for the high-confidence set, still Post for ambiguous helpers under review.
- Every kill event lands in WORM with policy name, binary, parent, session/pod identity.
- Dashboards show joinable incidents: Panguard decision ↔ Tetragon kill within the same session window.
- Laptop agents use Seatbelt fail-closed; cluster agents use RuntimeClass + Tetragon — no “Seatbelt on the node” fantasy.
You are not done because the system prompt says “never run curl.”
How this connects
| Part | Boundary |
|---|---|
| 1–3 | Ingest, secrets, scopes — authority and entry |
| 4 | Which programs may start after authority says yes |
| 5 | Which syscalls remain (seccomp) |
| 6 | Which files may be touched (FIM) |
| 7 | Where leftover code runs (sidecars / Kata / gVisor) |
| 8–9 | Proving intent ↔ kill in one timeline |
Process containment without scoped credentials is a fast kick of an agent that should not have had Vault admin. Scoped credentials without process containment is a polite JWT that still piped secrets to the internet via curl.
Getting started checklist
- Inventory agent/tool images — remove shells and package managers from runtime roles you can.
- Deploy Tetragon; ship observe-only TracingPolicies for agent parents + dangerous/supply-chain sets.
- Baseline one week; document legitimate children per role.
- Promote high-confidence binaries to
Sigkill+Post. - Wire events into Fluent Bit / OTel → WORM → SIEM with session correlation.
- Teach the agent UX to surface policy kills, not mute-retry.
Companion sketches: DevSecOps-boilerplate. Docs: Sandboxing · Panguard · Observability.
Next: Syscall Filtering — blocking binaries is not enough when the binary you allow still speaks too many syscalls. Series hub: Hardened Agentic Stack.