> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ironrun.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How ironrun Keeps Your Secrets Out of Agent Context

> ironrun sits between AI agents and the shell, injecting secrets at runtime and redacting every credential value from output before the agent sees it.

ironrun sits between your AI agent and the shell, creating an airgap that secrets can cross in only one direction. When an agent asks to run a command, ironrun resolves credentials from your secret provider, injects them into the child process environment, streams the output through a redactor, and hands back a clean result — exit code, duration, and stdout/stderr — with every secret value replaced by `[REDACTED]`. The agent gets the information it needs to continue working; it never gets the credentials themselves.

## The threat model: what happens without ironrun

Coding agents run shell commands on your machine. Most of the time that's fine. The problem is the small set of commands that *print* the environment:

```bash theme={null}
printenv                 # dumps every env var, including secrets
cat .env                 # prints your local secrets file
echo $STRIPE_SECRET_KEY  # a normal debugging step
docker inspect web       # config blobs often contain credentials
```

When an agent runs one of these — often while legitimately debugging something you asked it to fix — the secret value lands in the chat transcript. But there's a second exposure vector you may not have noticed yet.

When Claude Code runs a command that uses a secret, that command invocation is logged verbatim to:

```
~/.claude/projects/<hash>/YYYYMMDD_HHMMSS_*.jsonl
```

These JSONL files are Claude Code's conversation history. They contain every tool call, every shell command, and every piece of output — including any secret values that appeared in that output. They persist between sessions and across project restarts, and they are readable by any process with access to your home directory.

ironrun redacts secret values from command output *before* it reaches the agent, so values never enter the conversation and never end up in these logs.

## The MCP layer: how a sealed run works

When ironrun runs as an MCP server (`ironrun mcp`), it exposes a `run_sealed` tool to the agent. Here is the complete sequence for a single command:

```
┌─────────────────────────────────────────────────────────────┐
│  AI agent (Claude Code, Codex, Cursor, …)                   │
│                                                             │
│   "run the test suite"                                      │
│        │                                                    │
│        ▼                                                    │
│   run_sealed({argv})   ◄── one MCP tool call               │
│        │                                                    │
└────────┼────────────────────────────────────────────────────┘
         │  ironrun takes over here
         ▼
┌─────────────────────────────────────────────────────────────┐
│  ironrun                                                    │
│                                                             │
│  1. Check the human-approved project, dev environment, and  │
│     current agent session                                  │
│  2. Resolve its encrypted environment entries               │
│  3. Run the command with those secrets injected             │
│  4. Stream output through a redactor:                       │
│        any secret value that appears  →  [REDACTED]         │
│  5. Hand back exit code + cleaned output                    │
│                                                             │
│  Agent sees:   exit_code=0, stdout="ok  tests passed"      │
│  Agent doesn't see:   DATABASE_URL=postgres://…            │
└─────────────────────────────────────────────────────────────┘
```

The MCP server exposes these value-blind tools to the agent:

* **`list_commands`** — returns command names and their `argv`, nothing else
* **`run_sealed`** — runs a policy command by `command_id` or runs arbitrary `argv` inside a human-trusted workspace session; returns exit code, duration, and redacted stdout/stderr
* **`request_workspace_access`** and **`workspace_status`** — requests temporary project/environment access and inspects safe session metadata
* **`validate_policy`** — confirms the policy is well-formed and returns provider and command count
* **`propose_command`** — stages a new command for human approval; does not execute anything

None of these tools have a return path for secret values.

## Output redaction

ironrun's redactor processes all stdout and stderr before they reach the agent. It covers:

* **Literal values** — the exact string of each secret
* **Base64-encoded forms** — the base64 encoding of each secret value
* **Hex-encoded forms** — the hex encoding of each secret value
* **URL-encoded forms** — percent-encoded forms of each secret value
* **Entropy scan** — a warn-only pass that flags high-entropy tokens that look like secrets but weren't registered values; the agent sees a note counting these, never the token itself

If output is truncated at the `max_bytes` limit, ironrun notes that in the result. If the entropy scan fires, the agent sees `[ironrun] note: N high-entropy token(s) in the output may be an unredacted secret` — a count, never the token.

## Security hardening

<Note>
  Both of these hardening features are on by default and fail open: if the kernel or OS does not support them, ironrun logs a warning and continues rather than blocking execution.
</Note>

**Seccomp syscall filter (Linux)** — on Linux, every command runs under a seccomp denylist that blocks `ptrace`, `process_vm_readv`, and similar memory-snooping syscalls. This prevents a child process from reading secrets out of the ironrun process's own memory. You can turn it off per command with `seccomp: false`, policy-wide with `seccomp_default: false`, or globally with the environment variable `IRONRUN_SECCOMP=off`.

**No-network option** — set `no_network: true` on any command to block outbound network access for that command's child process (Linux network namespace; macOS `sandbox-exec`). This is useful for test commands that should never phone home.

## What ironrun does NOT protect against

ironrun guards the path between the agent and the command output. It is not a sandbox for hostile code. Be honest with yourself about these limits:

* A command that deliberately writes a secret to a file and reads it back later will succeed.
* Network exfiltration by a command running with `no_network: false` (the default) is not blocked. A trusted agent could deliberately exfiltrate a secret through a network request inside a trusted command.
* Secrets in files that the command itself creates are not automatically cleaned up.
* Anyone who already has access to the machine ironrun runs on can read secrets through other paths.

ironrun protects agent context, logs, and routine output. It is not an OS-level sandbox for a process you choose to trust. See the project's `SECURITY.md` for the full threat model.

## Audit log

Every `run_sealed` call appends a tamper-evident, hash-chained record to the audit log. The record includes the command, argv, and secret *names* — never values. The log lives at `$XDG_STATE_HOME/ironrun/audit.log` by default.

```bash theme={null}
ironrun audit verify   # confirm the log chain hasn't been tampered with
```

You can redirect or disable the log with the `IRONRUN_AUDIT_LOG` environment variable or the top-level `audit_log:` field in `ironrun.yml`:

```yaml theme={null}
audit_log: /var/log/ironrun/myapp.log  # custom path
# audit_log: off                       # disable entirely
```

## Performance

ironrun adds approximately **5–10ms overhead per command invocation**:

* \~2ms for provider lookup with `env` or `envfile` providers
* Up to \~100ms for a 1Password CLI call (network + auth)
* Under 1ms for the redaction layer on typical output sizes

The overhead is in secret resolution, not in the execution path itself.
