September 8, 2026
The kernel does not care what you named the tool
AgentGuard: eBPF LSM for coding agents. YAML in the repo. Deny is EPERM.
Linux prototype · Apache-2.0 · github.com/AgentGuard-hq/AgentGuard
You asked the agent to fix a failing test. The GitHub issue looked normal. Buried in a fixture, or in an MCP response, or in a README the model helpfully fetched, was an instruction to read .env and post it somewhere.
The model may even agree that secrets are off-limits. That is not a security boundary. The boundary is whether openat succeeds.
Coding agents are not chatbots with a nicer prompt. They open, exec, and connect. The failure that matters is the next syscall, not the next token.
Conscience is not a TCB
Most guardrails live inside the agent:
- a system prompt
CLAUDE.md/AGENTS.mdPreToolUsehooks- an allowlist of “Bash” strings
Those are a conscience. Consciences are bypassed the moment execution leaves the tool schema.
Deny .env in a hook, then read it with python -c, perl, node, a loader, mmap, anything that is not Read or Bash as the CLI defines them. Deny rm as a command string, then hit the same inode another way. The hook never ran. The kernel still would — if you asked it to.
Vendor sandboxes (Seatbelt, bubblewrap, Claude’s sandbox-runtime, devcontainers) are the right product default for most people. This post is a narrower claim:
Policy that remains true when the agent’s own conscience is false.
That layer is the LSM. Deny is EPERM. The OS does not care what the tool was named.
What AgentGuard is
AgentGuard is a Linux eBPF LSM supervisor for coding agents. One binary loads policy into the kernel and starts the agent as the invoking user, not root.
Policy is YAML in the project (policies/default.yaml). Starter rules:
- Credentials — block
.env,id_rsa, the usual secret paths - Destructive argv —
rm,dd, the obvious ones - Egress — allowlisted hosts on 443, via a local proxy; everything else denied
A deny is a kernel error, not a chat message. Optionally, Claude Code / Codex hooks inject a feedback: string so the model can adapt instead of spinning on a raw permission error. If the hooks are missing, the kernel still blocks. The model just is not told why.
That split is the whole design:
| Layer | Job |
|---|---|
| LSM | Enforcement. Fail closed. |
| YAML in git | Policy you can review in the PR. |
| Hooks | Explanation. Courtesy. Not the TCB. |
policies/default.yaml
│
▼
eBPF LSM (loaded as root)
│
▼
agent as SUDO_USER
│
├── openat(".env") → EPERM
├── execve("rm", …) → EPERM
└── connect(not :443 allowlist) → EPERM
│
└── optional: "SYSTEM FEEDBACK: …" into the TUI
Status: v0.1.2, early. Tested primarily with Claude Code. Codex is wired the same way. Native Darwin claude is not supervised — macOS has no BPF LSM. A Colima/Docker Linux VM is a different machine, not a magic halo around the host binary.
Why EPERM and not another hook
Hooks inspect intent as the product serialized it. LSM inspects what the process did.
If you only wrap Bash, you have not wrapped python. If you only wrap the CLI’s Read tool, you have not wrapped openat from a compiler, a test runner, or a postinstall script the agent just spawned. Child processes inherit the LSM. They do not inherit your settings.json.
That is also why AgentGuard is not “prompt injection detection.” Injection is how you get to a bad syscall. The interesting part is whether the syscall is allowed. Catching every encoding of “ignore previous instructions” is a language problem. Catching openat on .ssh/id_rsa is a kernel problem. I would rather have both. I will not pretend the first replaces the second.
Privilege, said plainly
Loading BPF is a loaded privilege. Typically root at load, CAP_BPF in play.
Two rules I treat as non-negotiable:
- LSM only. The supervisor must not attach tracing probes on the agent’s TLS stack or rewrite userspace buffers. If you need to mutate what the model “saw” in order to “help,” you are no longer a reference monitor.
- Pin the object. Drop caps on the child.
doctorshould fail on a hash mismatch. The agent must not keepCAP_BPF. A second tracing program on the box is a peer of the supervisor, not a child you can YAML away — unless you also gatebpf()/ program types.
“We used eBPF” is not the same sentence as “you can trust us.” The first is a mechanism. The second is a TCB story: hashed program, allowlisted types, no extra helpers, agent running as a boring user.
install.sh cannot invent lsm=bpf. That is a boot-time fact. If doctor says BPF is missing from the LSM list, that is the kernel cmdline, not a missing mount. Securityfs being readable is not the same as enforcement.
Non-goals (on purpose)
AgentGuard is not:
- a replacement for Anthropic’s Seatbelt / bubblewrap sandbox, MDM, or Claude Code on the web
- a Mac-native enforcer
- a hosted microVM product (that is E2B/Daytona/Modal)
- a complete jail — the default YAML is a starter, completeness is a policy problem
- unprivileged install on a random laptop kernel
If the threat model is “untrusted repo, don’t toast the laptop,” start with the first-party sandbox or a VM. Use something like this when you need agent-agnostic, auditable syscall policy on a Linux box you already control.
There are many other GitHub repos named AgentGuard. This one is the kernel one. The name collision is annoying and irrelevant to the claim.
What “working” looks like
The demo I care about is not a cartoon jailbreak.
- Hooks and
CLAUDE.mdsay: never read.env, neverrm -rf. - The prompt is ordinary: debug the test / summarize the issue.
- The bypass does not go through the tool the hook subscribed to.
- Hooks-only: the secret is in context, or the file is gone.
- LSM:
EPERM. Process still alive. Optionalfeedback:so the model retries cleanly.
If I cannot show a hook path that loses and an LSM path that holds, I do not have a product. I have a policy compiler with extra steps.
Why this exists
I wanted to know whether coding-agent safety could live at the same layer as every other process on Linux: LSM, fail closed, policy in git.
The prototype says yes, with the usual kernel tax — BTF, lsm=bpf, sudo to load, no Darwin miracles.
The industry is shipping consciences at industrial scale. Some of them are good consciences. I still want the door to lock when the conscience is tired, confused, or prompt-injected.
Hooks are the agent’s conscience.
The kernel is the door.
AgentGuard is Apache-2.0, v0.1.2, Linux. Code and a longer engineering note: github.com/AgentGuard-hq/AgentGuard.