# Ironrun

> Agent-safe sealed command execution — secrets injected behind airgap

- **Type:** MCP server
- **Install:** `agentstack add mcp-generalized-labs-ironrun`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Generalized-Labs](https://agentstack.voostack.com/s/generalized-labs)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Generalized-Labs](https://github.com/Generalized-Labs)
- **Source:** https://github.com/Generalized-Labs/ironrun

## Install

```sh
agentstack add mcp-generalized-labs-ironrun
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# ironrun

**Manage project environments in the terminal and let AI agents use secrets without reading them.**

[](https://github.com/generalized-labs/ironrun/actions/workflows/ci.yml)
[](https://goreportcard.com/report/github.com/generalized-labs/ironrun)
[](https://github.com/generalized-labs/ironrun/blob/main/LICENSE)

---

ironrun is a local-first encrypted environment workspace. It gives humans one terminal UI for projects, environments, `.env` imports, and agent access. Trust an agent session once for a project’s `dev` environment, then it can work normally while ironrun injects values only into child processes and strips them back out of output before the agent sees them.

Your tests still get a live `DATABASE_URL`. The agent gets back `exit_code: 0` and `tests passed` — and never the connection string.

---

## Why you might want this

Coding agents (Claude Code, Codex, Cursor, and others) run shell commands on your machine, and those commands can see your environment. Most of the time that's fine. The problem is the small set of commands that *print* the environment:

```bash
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. From there it's in the model's context, it may be in the provider's logs, and it may be in a screen recording or a shared session. You can't un-send it, so the safe response is to rotate the credential.

This isn't hypothetical. From the Claude Code issue tracker:

> *"Three confirmed incidents at one operator workstation in ~6 days. Each incident forced a credential rotation. None of these are exotic — they're shapes that came up during normal diagnosis work the model was asked to do."*
> — [anthropics/claude-code#65122](https://github.com/anthropics/claude-code/issues/65122)

The agents don't redact their own output, and the secret managers you already use (`op run`, `doppler run`, `infisical run`) inject secrets but don't redact either. ironrun adds the missing piece: it sits between the agent and the command so secrets flow **in** but never flow back **out**.

### The threat you probably haven't seen yet

When Claude Code runs a command that uses a secret — a `curl` with an Authorization header, a deploy script that reads DATABASE_URL — that command invocation is logged verbatim to:

```
~/.claude/projects//YYYYMMDD_HHMMSS_*.jsonl
```

These JSONL files are Claude Code's conversation history. They contain every tool call, every shell command, every piece of output — including the secret values that appeared in those outputs. They persist between sessions and across project restarts.

One developer found this by accident:

> *"It will just grep the logs and try to find a working secret from other projects/past sessions"*

The logs are readable by any process with access to your home directory. If you've ever run a command through Claude Code that touched a secret, assume that value is in those logs.

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

---

## How it works

```
┌─────────────────────────────────────────────────────────────┐
│  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://…            │
└─────────────────────────────────────────────────────────────┘
```

By default, an agent can use arbitrary argv only during a temporary session you explicitly trust. Strict policy commands remain available for CI, production, and sensitive projects. Ironrun never has a tool that returns a secret’s value.

> ironrun adds approximately 5-10ms per command invocation: ~2ms for provider lookup (env/envfile) up to ~100ms for a 1Password CLI call. The redaction layer adds under 1ms for typical output sizes.

---

## Install

```bash
# curl (Linux/macOS — inspectable script, mandatory checksum verification)
curl -fsSL https://ironrun.dev/install.sh -o /tmp/ironrun-install.sh
bash /tmp/ironrun-install.sh

# Go (any platform)
go install github.com/generalized-labs/ironrun/cmd/ironrun@latest

# Verified npm launcher (Node 20+; native Go security boundary)
npx @generalized-labs/ironrun@latest
```

Windows (amd64) is a beta: download `ironrun_Windows_x86_64.zip` from the
[latest release](https://github.com/generalized-labs/ironrun/releases/latest)
and put `ironrun.exe` on your `PATH`.

Check it's on your path:

```bash
ironrun version
# ironrun 0.4.0   (a source build prints "ironrun dev")
```

---

## Quickstart

Set up a project once:

```bash
cd your-project
ironrun setup
```

Setup previews every file it will write, detects `.env` key names without
displaying values, creates an encrypted `dev` environment, registers MCP
clients, and optionally installs the value-blind per-user service.

Then use bare `ironrun` from anywhere. It opens the global Projects and Inbox
workspace. Agent requests appear automatically; Enter opens the exact review
and resumes the waiting MCP call after approval or masked secret entry.

The everyday CLI is also intentionally short:

```bash
ironrun add OPENAI_API_KEY   # masked prompt; saves to the active environment
ironrun import .env          # preview names, confirm, encrypt, verify
ironrun new staging         # create and switch to a persistent environment
ironrun session             # create and switch to a 24-hour environment
ironrun use dev             # switch environments
ironrun envs                # list names and keys, never values
ironrun run test            # run the approved command named "test"
ironrun trust list          # inspect active trusted agent sessions
ironrun trust revoke ID     # stop one immediately
ironrun status              # value-blind project summary
```

Run `ironrun setup` when you also want project agent instructions and MCP
configuration. It looks at your project and writes three files:

- **`ironrun.yml`** — a starter policy. It detects your stack (npm/pnpm/yarn/bun, Go, Rust, Python) and your `.env`, and pre-fills commands like `test`, `dev`, and `build` with the env vars it found.
- **`.mcp.json`** — wires Claude Code up to ironrun (project-scoped MCP server, merged into any existing file).
- **`CLAUDE.md`, `AGENTS.md`, `.cursorrules`** — tell the agent (Claude Code, Codex, Cursor) to run commands via `run_sealed` instead of typing them into a shell.

New policies bind approved commands directly to encrypted environment entry
names. Storing or rotating a value never requires a policy edit:

```yaml
version: "2"
environment_set: active
require_agent_leases: true
allow_proposals: true

commands:
  - id: dev
    argv: [npm, run, dev]
    ttl: 0
    secrets: [DATABASE_URL, STRIPE_SECRET_KEY]

  - id: test
    argv: [npm, test]
    ttl: 120s
    secrets: [DATABASE_URL, STRIPE_SECRET_KEY]

  - id: build
    argv: [npm, run, build]
    ttl: 120s
```

Import or add values locally, then check the workspace:

```bash
chmod 600 .env
ironrun import .env
ironrun status
```

The import shows key names only, asks for confirmation, verifies the encrypted
copy, and warns that the plaintext source remains. Ironrun never offers reveal,
clipboard copy, or plaintext export.

Run one yourself to see the redaction:

```bash
ironrun run test
# npm test runs with DATABASE_URL and STRIPE_SECRET_KEY set.
# If a test logs the connection string, you'll see [REDACTED] instead.
```

### Existing version-1 policies

Version-1 provider aliases remain supported. Preview a reversible migration:

```bash
ironrun migrate
ironrun migrate --apply
```

Migration copies values transactionally, verifies encrypted storage, preserves
policy comments through an AST edit, keeps an ignored backup, and does not
delete legacy values. `ironrun migrate rollback ID` remains available until an
explicit `ironrun migrate cleanup ID --yes`.

### Project environment sets

For projects with more than one environment, manage the values entirely from
the terminal. On a new project, simply run `ironrun`: it creates a valid
local-vault policy, registers the project identity, creates `dev`, and opens the
control room. The original nested CLI remains available for scripts and
advanced operations:

```bash
ironrun env init dev
ironrun env set dev HYDRA_DB_API_KEY
ironrun env create staging
ironrun env clone dev staging
ironrun env use staging
ironrun env status
ironrun run hydra-bootstrap
```

Temporary session sets expire automatically (24 hours by default):

```bash
ironrun env create session --temporary --ttl 8h
ironrun env use session
ironrun env prune
```

Use `ironrun run --set staging ` for a one-run override. `env list`,
`env rotate`, `env delete`, `env remove`, `env doctor`, and `env import` cover
the remaining lifecycle. Imports accept owner-only dotenv files, display key
names but never values, and refuse project-local or group/other-readable files.
`env export` writes only a `KEY=` template; it never exports plaintext values.

Project metadata lives under `.ironrun/` and contains no secret values. The
encrypted vault lives outside the repository under `~/.ironrun/vaults/`. Every
environment has a rotating data key wrapped by a project root key in the native
OS credential manager. Existing per-value credential-manager records migrate
into the vault on first successful read, with vault commit before legacy delete.

### Terminal control room

Run Ironrun with no arguments in a terminal, or use the explicit command:

```bash
ironrun
ironrun tui
```

The TUI opens on the encrypted workspace: environments, masked environment/file
secret names, approved commands, and the actions people need most. Use arrows,
Enter, Escape, and Tab; press `/` for the action palette and `?` for help. A
detected `.env` can be reviewed by key name, partially selected, confirmed, and
verified after encrypted import. Ironrun never deletes the plaintext source for
you and warns while it remains. Requests, leases, and audit state live on
secondary tabs. There is no reveal, clipboard-copy, or plaintext-export action.

### Encrypted file secrets

File-backed secrets are stored as opaque encrypted bytes and materialized only
while an approved command runs. Declare a safe basename and the environment
variable that receives its temporary path:

```yaml
secrets:
  service-account:
    env: GOOGLE_APPLICATION_CREDENTIALS
    kind: file
    filename: service-account.json
    allow: [integration-test]
commands:
  - id: integration-test
    argv: [go, test, ./integration/...]
    secrets: [service-account]
```

Choose **Add secret file** in the TUI. Ironrun rejects symlinks, traversal,
unsafe basenames, permissive source files, and duplicate targets. At execution
it creates a unique owner-only directory outside the repository, writes the
file with owner-only permissions, injects only its path, redacts literal and
common encoded forms of the contents, and removes the directory after success,
failure, timeout, or cancellation. Validated stale crash remnants are removed
on startup.

Temporary plaintext necessarily exists on disk while the child process uses a
file secret. Cleanup limits its lifetime but cannot guarantee physical erasure
from SSD media. Use short command timeouts and revoke agent leases when access
is no longer needed.

### Revocable agent leases

Agent leases are opt-in for compatibility. Require them for MCP execution:

```yaml
version: "1"
provider: passthrough
require_agent_leases: true
```

An agent calls `request_lease` with policy command IDs, a reason, and a desired
TTL. The command remains blocked until a human approves it:

```bash
ironrun access list
ironrun access approve req_abc123...
ironrun access leases
ironrun access revoke lease_abc123...
```

Leases are bound to the exact MCP server session, environment, command set, and
expiry. Restarting the MCP server creates a new session; old leases do not
transfer. Revocation is checked before the next run.

### Trusted workspace sessions (the fast agent path)

For normal local development, an agent can call `request_workspace_access` or
attempt `run_sealed` with an `argv` array. Ironrun shows one request in the
global Inbox. Approve it once and the same MCP session can run normal commands
for the selected project and environment for two hours:

```bash
ironrun trust list
ironrun trust grant req_abc123
ironrun trust pause trust_abc123
ironrun trust extend trust_abc123 --ttl 2h
ironrun trust revoke trust_abc123
```

Trusted sessions are pinned to the MCP session, project, and environment. A
server restart creates a new session; an old grant cannot transfer. The default
scope is the current `dev` environment. `staging` and `prod` require a separate
explicit grant. Normal development network access is enabled, which means a
trusted agent could deliberately exfiltrate a secret through network or file
actions. Ironrun protects agent context, logs, and routine output; it is not an
OS sandbox for a process you choose to trust.

### Secret requests and encrypted chat capsules

The `request_secret` MCP tool accepts only a declared alias and reason. It has no
plaintext value field. Fulfill it directly through the TUI or masked CLI:

```bash
ironrun access fulfill req_abc123...
```

If the workflow specifically requires pasting through chat, encrypt the value
*before* it enters the transcript:

```bash
ironrun capsule create req_abc123...
# masked prompt; prints ir1.
```

Paste only the `ir1.` ciphertext. The agent passes it to `claim_capsule`; Ironrun
decrypts and stores it locally. Capsules are project-bound, MCP-session-bound,
request-bound, expire within ten minutes, and become unusable after the request
is fulfilled. A plaintext key already pasted into chat cannot be retroactively
removed from model-provider logs and should be rotated.

### Local curl API

Start the owner-only Unix-socket API:

```bash
ironrun serve
curl --unix-socket .ironrun/ironrun.sock http://localhost/v1/status
curl --unix-socket .ironrun/ironrun.sock \
  -H 'Content-Type: application/json' \
  -d '{"command_id":"test","environment":"dev"}' \
  http://localhost/v1/run
```

The API exposes status, environment metadata, access requests, leases,
revocation, denial, and sealed execution. It refuses unknown JSON fields and
has no endpoint accepting plaintext secret values.

Now start your agent (`claude`, `cursor`, …). It sees `run_sealed` and asks for
one trusted workspace session before normal development work. It can then

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Generalized-Labs](https://github.com/Generalized-Labs)
- **Source:** [Generalized-Labs/ironrun](https://github.com/Generalized-Labs/ironrun)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-generalized-labs-ironrun
- Seller: https://agentstack.voostack.com/s/generalized-labs
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
