> ## 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.

# Secret Providers: Where ironrun Resolves Credentials

> Configure ironrun to read secrets from 1Password, Doppler, Infisical, HashiCorp Vault, env files, or the shell environment. One provider per policy file.

Set `provider:` once in `ironrun.yml`, then reference secrets by path in each command's `env:` block. When the agent calls `run_sealed`, ironrun resolves each reference, injects the values into the child process environment, runs the command, and redacts all matching values from the output before the agent sees it. The agent never touches the resolution step; it only sees the cleaned result.

## Provider reference table

| Provider      | Reference format                      | Example                                    |
| ------------- | ------------------------------------- | ------------------------------------------ |
| `envfile`     | `envfile:<path>` (set on `provider:`) | `provider: "envfile:~/.secrets/myapp.env"` |
| `1password`   | `op://vault/item/field`               | `op://Engineering/stripe/secret_key`       |
| `vault`       | `vault://<path>#<field>`              | `vault://secret/myapp#DATABASE_URL`        |
| `doppler`     | `doppler://project/config/NAME`       | `doppler://myapp/prod/STRIPE_KEY`          |
| `infisical`   | `infisical://projectId/env/NAME`      | `infisical://abc123/prod/DB_URL`           |
| `env`         | `env:NAME`                            | `env:DATABASE_URL`                         |
| `passthrough` | the literal value                     | `postgres://localhost/dev`                 |

For `doppler`, `infisical`, and `env` providers you can also use just the name (`NAME` or `DATABASE_URL`) without the scheme prefix.

## Recommended for local dev: envfile

The `env` provider reads from your shell environment — which is exactly what an agent can dump with `printenv`. The `envfile` provider avoids this entirely: it keeps secrets in a file outside your repo and reads them only at the moment a sealed command runs, so they are never in the agent's environment at all.

<Steps>
  <Step title="Create a secure secrets directory">
    ```bash theme={null}
    mkdir -p ~/.secrets && chmod 700 ~/.secrets
    ```

    The `700` permission means only your user can read or enter this directory.
  </Step>

  <Step title="Write your secrets file">
    ```bash theme={null}
    cat > ~/.secrets/myapp.env << 'EOF'
    DATABASE_URL=postgres://app:s3cr3t@db.internal:5432/myapp
    STRIPE_SECRET_KEY=sk_live_51Nabc123def456
    RESEND_API_KEY=re_8Hk2Qa9xY
    EOF
    chmod 600 ~/.secrets/myapp.env
    ```

    `chmod 600` means only your user can read this file. ironrun refuses to read files with group or world permissions.
  </Step>

  <Step title="Point your policy at the file">
    ```yaml theme={null}
    # ironrun.yml
    version: "1"
    provider: "envfile:~/.secrets/myapp.env"

    commands:
      - id: test
        argv: [go, test, ./...]
        ttl: 10m
        env:
          DATABASE_URL: DATABASE_URL   # reads the key by name from the file
          STRIPE_SECRET_KEY: STRIPE_SECRET_KEY
    ```
  </Step>
</Steps>

Now `ironrun run test` still has every secret, but the agent's shell has none of them. `printenv` and `cat .env` come up empty in the agent's session.

## Per-provider setup

### 1Password

ironrun uses the `op` CLI to resolve `op://` references. You need the 1Password CLI installed and authenticated.

```bash theme={null}
# Install from https://1password.com/downloads/command-line/
op signin
```

```yaml theme={null}
version: "1"
provider: 1password

commands:
  - id: deploy
    argv: [./scripts/deploy.sh]
    ttl: 30m
    env:
      FLY_API_TOKEN: "op://Engineering/fly/token"
      DATABASE_URL:  "op://Engineering/prod-db/url"
```

Provider overhead is \~100ms per secret resolution due to the CLI call and 1Password authentication round-trip.

### Doppler

ironrun uses the `doppler` CLI to resolve `doppler://` references.

```bash theme={null}
# Install from https://docs.doppler.com/docs/install-cli
doppler login
```

```yaml theme={null}
version: "1"
provider: doppler

commands:
  - id: test
    argv: [npm, test]
    ttl: 120s
    env:
      DATABASE_URL: "doppler://myapp/prod/DATABASE_URL"
      STRIPE_KEY:   "doppler://myapp/prod/STRIPE_KEY"
```

### Infisical

ironrun uses the `infisical` CLI to resolve `infisical://` references.

```bash theme={null}
# Install from https://infisical.com/docs/cli/overview
infisical login
```

```yaml theme={null}
version: "1"
provider: infisical

commands:
  - id: test
    argv: [npm, test]
    ttl: 120s
    env:
      DATABASE_URL: "infisical://abc123/prod/DATABASE_URL"
```

### HashiCorp Vault

ironrun reads `VAULT_ADDR` and `VAULT_TOKEN` from the environment for Vault KV v2 references. No additional CLI is needed.

```bash theme={null}
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN=hvs.XXXXXXXXXXXX
```

```yaml theme={null}
version: "1"
provider: vault

commands:
  - id: migrate
    argv: [./scripts/migrate.sh]
    ttl: 15m
    env:
      DATABASE_URL: "vault://secret/myapp#DATABASE_URL"
```

The reference format is `vault://<kv-path>#<field-name>`.

### env (shell environment)

The `env` provider reads named variables from the shell environment that ironrun itself runs in.

```yaml theme={null}
version: "1"
provider: env

commands:
  - id: test
    argv: [npm, test]
    env:
      DATABASE_URL: "env:DATABASE_URL"
```

<Warning>
  The `env` provider reads from the shell environment. Agents can dump that environment with `printenv`, `env`, or `echo $DATABASE_URL`. This defeats the core protection ironrun provides. Use `envfile` for local development instead.
</Warning>

### passthrough

Use `passthrough` for non-secret values you want to inject as environment variables. The value in the `env:` block is used literally.

```yaml theme={null}
version: "1"
provider: passthrough

commands:
  - id: dev
    argv: [npm, run, dev]
    env:
      DATABASE_URL: "postgres://localhost/myapp_dev"
      LOG_LEVEL: "debug"
```

This is also useful for mixing non-secret config values with a different provider: set `provider:` to your secret manager and use literal values for non-sensitive entries.

## Choosing a provider

<Tip>
  Use **`envfile`** for local development with AI agents — secrets stay in a file that agents cannot access, and setup requires no external service. Use **1Password**, **Doppler**, or **Infisical** for team-shared secrets and CI environments where multiple people or machines need the same credentials.
</Tip>

<CardGroup cols={2}>
  <Card title="Local dev" icon="laptop">
    **envfile** — secrets in a local file, outside the repo, outside the agent's shell environment. Fast resolution (\~2ms), no external service required.
  </Card>

  <Card title="Team secrets" icon="users">
    **1Password / Doppler / Infisical** — secrets stored in a managed service, shared across team members and CI. Requires the provider's CLI and authentication.
  </Card>

  <Card title="Infrastructure" icon="server">
    **HashiCorp Vault** — secrets from a self-hosted or HCP Vault cluster. Reads `VAULT_ADDR` and `VAULT_TOKEN` from the environment; no extra CLI needed.
  </Card>

  <Card title="Non-secrets" icon="circle-info">
    **passthrough** — inject literal values (feature flags, non-sensitive config) without a secret provider. Values appear in the policy file in plaintext.
  </Card>
</CardGroup>
