Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Configuration

iron-proxy is configured via a single YAML file, passed at startup with the -config flag:

iron-proxy -config /etc/iron-proxy/proxy.yaml

Below is a complete reference for every configuration option.

Full Example

dns:
  listen: ":53"
  proxy_ip: "172.20.0.2"
  upstream_resolver: "8.8.8.8:53"
  passthrough:
    - "*.internal.corp"
  records:
    - name: "custom.local"
      type: A
      value: "10.0.0.5"
 
proxy:
  http_listen: ":80"
  https_listen: ":443"
  tunnel_listen: ":1080"
  max_request_body_bytes: 1048576
  max_response_body_bytes: 0
  upstream_response_header_timeout: "30s"
  upstream_deny_cidrs:
    - "169.254.169.254/32"
    - "fd00:ec2::254/128"
    - "127.0.0.0/8"
    - "::1/128"
 
tls:
  mode: "mitm"
  ca_cert: "/certs/ca.crt"
  ca_key: "/certs/ca.key"
  cert_cache_size: 1000
  leaf_cert_expiry_hours: 72
 
transforms:
  - name: allowlist
    config:
      domains:
        - "registry.npmjs.org"
      cidrs:
        - "10.0.0.0/8"
      rules:
        - host: "api.openai.com"
          methods: ["POST"]
          paths: ["/v1/*"]
        - host: "*.anthropic.com"
          methods: ["POST"]
          paths: ["/v1/messages", "/v1/complete"]
 
  - name: secrets
    config:
      secrets:
        - source:
            type: env
            var: OPENAI_API_KEY
          inject:
            header: "Authorization"
            formatter: "Bearer {{ .Value }}"
          rules:
            - host: "api.openai.com"
              methods: ["POST"]
              paths: ["/v1/*"]
        - source:
            type: aws_sm
            secret_id: "arn:aws:secretsmanager:us-west-1:123456789:secret:anthropic-key"
            region: "us-west-1"
            ttl: 5m
          replace:
            proxy_value: "pk-proxy-anthropic-xyz"
            match_headers: ["x-api-key"]
            require: true
          rules:
            - host: "api.anthropic.com"
        - source:
            type: aws_ssm
            name: "/myapp/openai-key"
            region: "us-east-1"
            ttl: 15m
          replace:
            proxy_value: "pk-proxy-openai-param"
            match_headers: ["Authorization"]
          rules:
            - host: "api.openai.com"
 
  - name: annotate
    config:
      annotations:
        - rules:
            - host: "api.openai.com"
              methods: ["POST"]
              paths: ["/v1/*"]
          headers: ["x-request-id"]
 
  - name: grpc
    config:
      name: "policy-engine"
      target: "localhost:9500"
      send_request_body: true
      send_response_body: true
      rules:
        - host: "api.openai.com"
          methods: ["POST"]
          paths: ["/v1/*"]
      tls:
        enabled: true
        ca_cert: "/etc/iron-proxy/grpc-ca.pem"
        cert: "/etc/iron-proxy/grpc-client.pem"
        key: "/etc/iron-proxy/grpc-client-key.pem"
 
  - name: judge
    config:
      name: "github-write-guard"
      fallback: "deny"
      timeout: "8s"
      rules:
        - host: "api.github.com"
          methods: ["POST", "PATCH", "DELETE", "PUT"]
      provider:
        type: "anthropic"
        model: "claude-haiku-4-5-20251001"
        api_key_env: "ANTHROPIC_API_KEY"
      prompt: |
        Allow writes to the repository under review. Deny writes to user
        settings, billing, or any other repository.
 
  - name: header_allowlist
    config:
      headers:
        - "Authorization"
        - "Content-Type"
        - "User-Agent"
        - "Accept"
        - "/^X-Trace-.*$/"
      rules:
        - host: "api.openai.com"
 
mcp:
  error:
    code: -32001
    message: "blocked by iron-proxy policy"
  servers:
    - name: github
      rules:
        - host: "mcp.github.com"
          paths: ["/mcp", "/mcp/*"]
      tools:
        - name: "search_repositories"
        - name: "create_issue"
          when:
            - path: "owner"
              equals: "ironsh"
            - path: "repo"
              in: ["iron-proxy"]
 
management:
  listen: "127.0.0.1:9092"
  api_key_env: "IRON_MANAGEMENT_API_KEY"
 
metrics:
  listen: ":9090"
 
log:
  level: "info"

dns

Configures the built-in DNS server. The DNS server returns proxy_ip for all lookups so that outbound traffic routes through the proxy.

FieldTypeDefaultDescription
listenstring":53"Address and port the DNS server binds to.
proxy_ipstringrequiredIP address where iron-proxy is running. All DNS responses resolve to this IP.
upstream_resolverstringOS defaultUpstream DNS resolver address (e.g., "8.8.8.8:53"). When set, both passthrough DNS queries and upstream HTTP connections resolve via this server instead of the OS default. Useful when iron-proxy owns the system DNS.
passthroughstring[][]Domain glob patterns that are forwarded to the upstream resolver instead of being intercepted. Useful for internal DNS names that should not route through the proxy.
recordsobject[][]Static DNS records. These take precedence over interception and passthrough. See below.

dns.records[]

FieldTypeDescription
namestringDomain name for the record.
typestringRecord type: A or CNAME.
valuestringIP address (for A records) or target hostname (for CNAME records).

proxy

Configures the HTTP/HTTPS proxy listeners.

FieldTypeDefaultDescription
http_listenstring":80"Address and port for HTTP traffic.
https_listenstring":443"Address and port for HTTPS traffic.
tunnel_listenstring(disabled)Address and port for the CONNECT/SOCKS5 tunnel listener. Accepts both HTTP CONNECT and SOCKS5 requests. See the SOCKS5 and CONNECT tunnels guide for details.
max_request_body_bytesinteger1048576 (1 MiB)Maximum request body size that the proxy will buffer. Bodies are only buffered when a transform needs to inspect them.
max_response_body_bytesinteger0 (unlimited)Maximum response body size that the proxy will buffer. Set to 0 to disable the limit.
upstream_response_header_timeoutduration"30s"Maximum time to wait for an upstream to begin sending response headers. Raise this for slow upstreams (e.g., long-running LLM calls). Invalid or non-positive durations are rejected at startup.
upstream_deny_cidrsstring[]IMDS + loopback (see below)CIDR ranges the proxy will refuse to dial regardless of allowlist contents. Enforced after DNS resolution, so hostnames that resolve into a denied range are refused before TCP connect. Set to [] to opt out.

upstream_deny_cidrs Defaults

When upstream_deny_cidrs is unset, iron-proxy blocks cloud instance metadata and loopback by default:

  • 169.254.169.254/32 (AWS / GCP / Azure IPv4 IMDS)
  • fd00:ec2::254/128 (AWS IPv6 IMDS)
  • 127.0.0.0/8 (IPv4 loopback)
  • ::1/128 (IPv6 loopback)

The defaults intentionally omit RFC1918 ranges, since many iron-proxy deployments target private corporate networks.

Provide an explicit list to override the defaults entirely, or an empty list to opt out:

proxy:
  upstream_deny_cidrs:
    - "169.254.169.254/32"
    - "127.0.0.0/8"

If a workload legitimately needs to talk to IMDS or loopback through the proxy, override the list before upgrading or the proxy will refuse those connections.

tls

Configures how iron-proxy handles HTTPS traffic. Two modes are supported: mitm (the default), which terminates TLS using a CA you provide and mints leaf certificates on the fly, and sni-only, which passes TLS through without termination.

FieldTypeDefaultDescription
modestring"mitm"TLS handling mode. One of mitm or sni-only. See TLS modes below.
ca_certstringrequired for mitmPath to the CA certificate file (PEM format). Not used in sni-only mode.
ca_keystringrequired for mitmPath to the CA private key file (PEM format). Not used in sni-only mode.
cert_cache_sizeinteger1000Number of generated leaf certificates to keep in the LRU cache. Not used in sni-only mode.
leaf_cert_expiry_hoursinteger72Validity duration (in hours) for generated leaf certificates. Not used in sni-only mode.

TLS Modes

mitm (default): iron-proxy terminates the client TLS connection, inspects the decrypted request, and opens a new TLS connection to the upstream server. Clients must trust iron-proxy's CA certificate. This is the only mode that lets transforms see request methods, paths, headers, and bodies.

sni-only: iron-proxy peeks at the TLS ClientHello SNI and TCP-passthroughs the connection to the upstream without terminating TLS. Clients do not need to trust a proxy CA. The transform pipeline still runs with a host-only synthetic request: method, path, headers, and body are empty, so host-based allowlist rules are the only things that can match. Body-inspecting transforms like secrets and grpc still run but have nothing to act on. The CONNECT/SOCKS5 tunnel's TLS branch also switches to passthrough in sni-only mode.

tls:
  mode: "sni-only"

Use sni-only when you need host-level egress control but cannot distribute a CA certificate to workloads. Use mitm when you need secret injection, body inspection, or method/path allowlists.

transforms

An ordered array of transforms that run on every request. All transforms must pass for the request to be forwarded upstream. Transforms execute in the order they appear in the configuration.

Each transform has a name and a config object. The available transforms are documented below.

allowlist

Controls which destinations are reachable through the proxy. Requests to destinations not in the allowlist receive an HTTP 403 response.

There are two ways to specify allowed destinations: flat lists (domains and cidrs) that allow all methods and paths, and rules that support method and path restrictions. Both can be used together in the same allowlist.

FieldTypeDefaultDescription
domainsstring[][]Hostname glob patterns to allow (e.g., registry.npmjs.org, *.anthropic.com). A bare "*" matches any host as a catch-all. All methods and paths are permitted.
cidrsstring[][]CIDR ranges to allow (e.g., 10.0.0.0/8). All methods and paths are permitted.
rulesobject[][]Rules with optional method and path restrictions. See below.
warnbooleanfalseWhen true, violations are logged but not blocked. Useful for rolling out allowlists incrementally.

allowlist.rules[]

Each rule matches a single host or CIDR, with optional method and path filters. A request is allowed if it matches any rule (or any flat domains/cidrs entry).

FieldTypeDefaultDescription
hoststringHostname glob pattern. A bare "*" matches any host. Mutually exclusive with cidr.
cidrstringCIDR range. Mutually exclusive with host.
methodsstring[]allHTTP methods to allow (e.g., ["GET", "POST"]). Omit or set to ["*"] to allow all methods.
pathsstring[]allPath patterns to allow (e.g., ["/v1/*"]). Must start with /. Supports * wildcards. Omit to allow all paths.

secrets

Injects or replaces secret values at the egress boundary so that real credentials are never exposed to sandboxed workloads. Each secret declares its own source and either an inject or replace block.

See the Static Secrets reference for a full overview of inject and replace modes, secret sources, and credential rotation. The fields below are the canonical schema.

FieldTypeDefaultDescription
secretsobject[][]List of secret entries. See below.

secrets.secrets[]

FieldTypeDefaultDescription
sourceobjectrequiredWhere to read the secret value. Contains a type field (env, aws_sm, aws_ssm, 1password, or 1password_connect) plus type-specific fields. See sources below.
injectobjectInject the secret onto matching requests unconditionally. See inject mode. Mutually exclusive with replace.
replaceobjectReplace a proxy token with the real value. See replace mode. Mutually exclusive with inject.
rulesobject[][]Restrict this secret to specific destinations. Uses the same format as allowlist.rules[]. If empty, the secret applies to all destinations.

Secret Sources

Every source supports an optional json_key field. When set, the resolved value is parsed as JSON and the named field is extracted before use. This works with all source types (env, aws_sm, aws_ssm, 1password, 1password_connect) and is useful for pulling individual fields out of a single shared JSON secret.

env

Read the secret from an environment variable on the iron-proxy process.

FieldTypeDescription
typestringMust be env.
varstringEnvironment variable name containing the real secret value.
json_keystringOptional. When set, parse the value as JSON and extract this field.
source:
  type: env
  var: OPENAI_API_KEY
aws_sm

Read the secret from AWS Secrets Manager. The value is cached and refreshed in the background based on the configured TTL.

FieldTypeDefaultDescription
typestringMust be aws_sm.
secret_idstringrequiredSecret ARN or name in AWS Secrets Manager.
regionstringAWS SDK defaultAWS region where the secret is stored.
json_keystringWhen set, parse the fetched value as JSON and extract this field.
ttlduration0 (no refresh)Re-fetch interval. Set to 0 to read the value once at startup.
source:
  type: aws_sm
  secret_id: "arn:aws:secretsmanager:us-west-1:123456789:secret:my-key"
  region: "us-west-1"
  ttl: 10m
aws_ssm

Read the secret from AWS Systems Manager Parameter Store. Like aws_sm, values are cached and refreshed in the background based on the TTL.

FieldTypeDefaultDescription
typestringMust be aws_ssm.
namestringrequiredParameter name or ARN.
regionstringAWS SDK defaultAWS region where the parameter is stored.
with_decryptionbooleantrueDecrypt SecureString parameters.
json_keystringWhen set, parse the fetched value as JSON and extract this field.
ttlduration0 (no refresh)Re-fetch interval. Set to 0 to read the value once at startup.
source:
  type: aws_ssm
  name: "/myapp/api-key"
  region: "us-east-1"
  with_decryption: true
  ttl: 15m

iron-proxy uses the standard AWS credential chain (environment variables, instance profile, ECS task role, etc.) to authenticate with AWS.

1password

Read the secret from 1Password using a service account token. Like the AWS sources, values are cached and refreshed in the background based on the TTL. For most deployments, prefer 1password_connect: the hosted SDK service applies per-account rate limits that can stall request handling under load.

FieldTypeDefaultDescription
typestringMust be 1password.
secret_refstringrequired1Password reference using the op://vault/item/[section/]field syntax.
token_envstringOP_SERVICE_ACCOUNT_TOKENEnvironment variable holding the 1Password service account token.
json_keystringWhen set, parse the fetched value as JSON and extract this field.
ttlduration0 (no refresh)Re-fetch interval. Set to 0 to read the value once at startup.
source:
  type: 1password
  secret_ref: "op://Engineering/OpenAI/credential"
  token_env: OP_SERVICE_ACCOUNT_TOKEN
  ttl: 15m
1password_connect

Read the secret from a 1Password Connect server running in your own infrastructure. This is the recommended 1Password integration: Connect runs locally and avoids the per-account rate limits that the hosted SDK enforces. Values are cached and refreshed in the background based on the TTL.

FieldTypeDefaultDescription
typestringMust be 1password_connect.
secret_refstringrequired1Password reference using the op://vault/item/[section/]field syntax.
host_envstringOP_CONNECT_HOSTEnvironment variable holding the Connect server URL.
token_envstringOP_CONNECT_TOKENEnvironment variable holding the Connect API token.
json_keystringWhen set, parse the fetched value as JSON and extract this field.
ttlduration0 (no refresh)Re-fetch interval. Set to 0 to read the value once at startup.
source:
  type: 1password_connect
  secret_ref: "op://Engineering/OpenAI/credential"
  host_env: OP_CONNECT_HOST
  token_env: OP_CONNECT_TOKEN
  ttl: 15m

Inject Mode

In inject mode, the proxy unconditionally sets a header or query parameter on every request that matches the secret's rules. The workload never sees or sends any credential. This is useful when sandboxed workloads should have no knowledge of credentials at all.

FieldTypeDescription
headerstringHeader name to set on matching requests. Sent upstream with the exact casing written here (HTTP/1.x only: HTTP/2 lowercases header names regardless). Mutually exclusive with query_param.
query_paramstringQuery parameter name to set on matching requests. Mutually exclusive with header.
formatterstringGo template for the header value. Receives .Value (the resolved secret) and a base64 helper. Not used with query_param.

The formatter field supports Go templates. Use {{ .Value }} to insert the raw secret. The base64 helper concatenates and base64-encodes its arguments:

# Set Authorization: Bearer <secret>
inject:
  header: "Authorization"
  formatter: "Bearer {{ .Value }}"
 
# Set a query parameter (no formatter needed)
inject:
  query_param: "key"

Replace Mode

In replace mode, the workload sends a proxy token that the proxy swaps for the real value before forwarding upstream.

FieldTypeDefaultDescription
proxy_valuestringrequiredToken that the sandboxed environment sends. The proxy replaces this with the real value before forwarding.
match_headersstring[][]Header names to scan for the proxy token. An empty list scans all headers. Each entry is either a literal header name (case-insensitive) or a /regex/ pattern compiled at config time as a case-insensitive regular expression matched against canonical header names. Literal and regex entries can be mixed. Headers are forwarded upstream with the exact casing written here (HTTP/1.x only: HTTP/2 lowercases header names regardless).
match_bodybooleanfalseWhen true, scan the request body for the proxy token.
match_pathbooleanfalseWhen true, scan req.URL.Path for the proxy token and swap in the resolved secret. Off by default because URL paths often appear in access logs on either side of the proxy.
match_querybooleanfalseWhen true, scan the URL query string for the proxy token and swap in the resolved secret. Off by default because query strings often appear in access logs.
requirebooleanfalseWhen true, requests matching a configured rule are rejected with HTTP 403 if the proxy token is not present in any scanned location. Prevents workloads from bypassing secret management.

The proxy_value, match_headers, match_body, match_path, match_query, and require fields may also be set at the top level of a secret entry for backwards compatibility. New configurations should use the replace block.

gcp_auth

Mints short-lived GCP OAuth2 access tokens from a service account keyfile and injects them as Authorization: Bearer on matching requests. Token minting, caching, and refresh are handled automatically. Requires MITM mode.

iron-proxy also stubs Google's OAuth2 token endpoints (oauth2.googleapis.com/token and the GCE/GKE metadata server's service-account token endpoints) so client SDKs that complete their own token dance reach the proxy with a placeholder token. The real token is minted separately and swapped onto the upstream API call.

See the GCP Service Accounts reference for a full overview, including keyfile sourcing, domain-wide delegation, and metadata server stubbing.

FieldTypeDefaultDescription
keyfile_pathstringPath to a Google service account JSON keyfile on disk.
keyfileobjectSecret source that resolves to the keyfile JSON. Uses the same shape as a secret source.
credentials_providerobjectResolves credentials through the Google Cloud default chain (GKE Workload Identity, GOOGLE_APPLICATION_CREDENTIALS, Workload Identity Federation). See Workload Identity.
subjectstringWorkspace user email to impersonate via domain-wide delegation. When set, the minted token acts as the subject rather than the service account. Incompatible with credentials_provider.
scopesstring[]requiredOAuth2 scopes to request. Baked into the minted token.
rulesobject[][]Restrict the transform to specific destinations. Uses the same format as allowlist.rules[].

Exactly one of keyfile_path, keyfile, or credentials_provider must be set.

credentials_provider accepts {type: workload_identity}. The proxy holds the rotating credentials minted by the cloud SDK; the workload runs against the stubbed metadata server as usual.

- name: gcp_auth
  config:
    keyfile:
      type: aws_sm
      secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gcp-sa-key"
    subject: "user@workspace.example.com"
    scopes:
      - "https://www.googleapis.com/auth/cloud-platform"
    rules:
      - host: "*.googleapis.com"

oauth_token

Mints short-lived OAuth2 access tokens and injects them as Authorization: Bearer on matching requests. Each entry under tokens declares a grant type, its credential fields, and the hosts it applies to. Token exchange, caching, refresh, and single-flight deduplication are handled automatically. Requires MITM mode. If token minting fails the request is rejected with HTTP 502.

Each configured token_endpoint is also stubbed, so sandboxed client SDKs can complete their own OAuth2 handshake against the proxy with a placeholder token while the proxy injects the real one upstream.

See the OAuth2 Token Injection reference for a full overview, including per-vendor recipes (DocuSign, Salesforce, Box, Zoom, Gmail, AlphaSense) and credential rotation behavior.

FieldTypeDefaultDescription
tokensobject[]requiredList of token entries. At least one is required. See below.

oauth_token.tokens[]

FieldTypeDefaultDescription
grantstringrequiredOne of refresh_token, client_credentials, password, jwt_bearer.
token_endpointstringrequiredURL the proxy POSTs to in order to exchange credentials for an access token. Also stubbed for client-side token requests.
scopesstring[][]OAuth2 scopes requested at the token endpoint.
rulesobject[]requiredDestinations this entry applies to. Uses the same format as allowlist.rules[]. At least one rule is required.
headerstringAuthorizationHeader to set on matching requests.
value_prefixstringBearer Prefix prepended to the token in the injected header value.
token_endpoint_headersobjectMap of header name to secret source. Each resolved value is sent on the token POST itself, for vendors that require an API key alongside the standard form-body client auth. Header casing is preserved on the wire.

Required credential fields depend on the grant:

GrantRequired credentialsOptional credentialsOther required
refresh_tokenrefresh_token, client_idclient_secret
client_credentialsclient_id, client_secret
passwordusername, password, client_idclient_secret
jwt_bearerissuer, subject, private_keyprivate_key_idaudience

Each credential field is a discrete secret source. private_key for jwt_bearer must resolve to a PEM-encoded RSA private key; private_key_id, when set, is emitted as the JWT kid header.

For Google service-account auth, use the gcp_auth transform instead: it wraps the same JWT-bearer flow with Google's keyfile format.

- name: oauth_token
  config:
    tokens:
      - grant: refresh_token
        refresh_token:
          type: aws_sm
          secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gsuite-oauth"
          json_key: "refresh_token"
        client_id:
          type: aws_sm
          secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gsuite-oauth"
          json_key: "client_id"
        client_secret:
          type: aws_sm
          secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:gsuite-oauth"
          json_key: "client_secret"
        token_endpoint: "https://oauth2.googleapis.com/token"
        scopes:
          - "https://www.googleapis.com/auth/gmail.readonly"
        rules:
          - host: "gmail.googleapis.com"
 
      - grant: jwt_bearer
        issuer:  {type: env, var: DOCUSIGN_INTEGRATION_KEY}
        subject: {type: env, var: DOCUSIGN_USER_GUID}
        private_key:
          type: aws_sm
          secret_id: "arn:aws:secretsmanager:us-east-1:123456789:secret:docusign-private-key"
        audience: "account.docusign.com"
        token_endpoint: "https://account.docusign.com/oauth/token"
        scopes: ["signature", "impersonation"]
        rules:
          - host: "*.docusign.net"

aws_auth

Re-signs inbound AWS SigV4 requests with real credentials. The workload's AWS SDK signs with placeholders; iron-proxy reads the region and service from the inbound credential scope and re-signs with real credentials drawn from any secret source. One config entry covers every AWS service the client speaks to. Requires MITM mode.

See the AWS Request Signing reference for a full overview, including scope gating, body handling modes, and worked examples.

FieldTypeDefaultDescription
access_key_idobjectSecret source resolving to the real AWS access key ID. Required unless credentials_provider is set.
secret_access_keyobjectSecret source resolving to the real AWS secret access key. Required unless credentials_provider is set.
session_tokenobjectSecret source resolving to a session token (STS, assumed roles). Omit for long-lived IAM users.
credentials_providerobjectResolves credentials through the AWS SDK default chain (IRSA, EKS Pod Identity, IMDSv2). Mutually exclusive with access_key_id/secret_access_key. Accepts {type: workload_identity, region?: string}. See Workload Identity.
allowed_regionsstring[]any regionAllowlist of AWS regions. Requests with a credential scope outside this set are rejected with HTTP 403.
allowed_servicesstring[]any serviceAllowlist of AWS services. Requests with a credential scope outside this set are rejected with HTTP 403.
unsigned_payloadbooleanfalseSend UNSIGNED-PAYLOAD as the payload hash instead of reading and hashing the body. Required for S3 multipart and similar streaming uploads.
allow_chunked_bodybooleanfalseSign chunked-encoding bodies without length verification.
rulesobject[]requiredDestinations this transform applies to. Uses the same format as allowlist.rules[]. At least one rule is required.
- name: aws_auth
  config:
    access_key_id:     {type: env, var: AWS_ACCESS_KEY_ID}
    secret_access_key: {type: env, var: AWS_SECRET_ACCESS_KEY}
    allowed_regions:   ["us-east-1", "eu-west-1"]
    allowed_services:  ["bedrock", "s3", "dynamodb"]
    rules:
      - host: "*.amazonaws.com"

hmac_sign

Signs outbound requests with HMAC before forwarding them upstream. Computes a signature over a Go-template message derived from the request and injects the signature plus any auxiliary credentials into a configurable set of headers. The four signature enums (algorithm, key_encoding, output_encoding, timestamp.format) plus the message template cover most signing schemes. Requires MITM mode.

See the HMAC Request Signing reference for a full overview, including template variable details, body integrity rules, and worked examples.

Because a truncated body would produce an invalid signature, the transform enforces body integrity:

  • Bodies whose buffered length falls short of Content-Length (truncated by proxy.max_request_body_bytes) are rejected with HTTP 413.
  • Chunked bodies are rejected with HTTP 400 unless allow_chunked_body: true.
FieldTypeDefaultDescription
timestamp.formatstringrequiredOne of unix_seconds, unix_millis, unix_nanos, rfc3339.
signature.algorithmstringrequiredHMAC hash. One of sha256, sha512, sha1.
signature.key_encodingstringrequiredEncoding of the HMAC key before keying. One of raw, base64, hex.
signature.output_encodingstringrequiredEncoding of the computed signature. One of base64, hex.
signature.messagestringrequiredGo template for the signed message. Available fields: .Timestamp, .Method, .Path, .PathWithQuery, .Query, .Host, .Body.
credentialsobjectrequiredMap of credential name to secret source. Must include secret (the HMAC key). Other entries are addressable from header templates as .Credentials.<name>.
headersobject[]requiredOrdered list of {name, value} entries to inject. value is a Go template with .Timestamp, .Signature, and .Credentials.<name> available. Header casing is preserved on the wire.
allow_chunked_bodybooleanfalseOpt in to signing chunked-encoding bodies.
rulesobject[]requiredDestinations this transform applies to. At least one rule is required.
- name: hmac_sign
  config:
    timestamp:
      format: unix_seconds
    signature:
      algorithm: sha256
      key_encoding: base64
      output_encoding: base64
      message: "{{.Timestamp}}{{.Method}}{{.PathWithQuery}}{{.Body}}"
    credentials:
      key:        {type: env, var: FALCONX_API_KEY}
      secret:     {type: env, var: FALCONX_SECRET}
      passphrase: {type: env, var: FALCONX_PASSPHRASE}
    headers:
      - {name: "FX-ACCESS-KEY",        value: "{{.Credentials.key}}"}
      - {name: "FX-ACCESS-SIGN",       value: "{{.Signature}}"}
      - {name: "FX-ACCESS-TIMESTAMP",  value: "{{.Timestamp}}"}
      - {name: "FX-ACCESS-PASSPHRASE", value: "{{.Credentials.passphrase}}"}
    rules:
      - host: "api.falconx.io"

body_capture

Records decoded request bodies of matching requests onto the audit log under body_capture.request_body (and body_capture.request_body_truncated when the body exceeds the cap). Observation-only: it never rejects a request, and read errors are annotated on the trace rather than failing the request. Response bodies are not captured.

FieldTypeDefaultDescription
max_request_body_bytesinteger16384 (16 KiB)Per-request capture cap. Bodies larger than this are truncated to the prefix and request_body_truncated is set to true. Independent of proxy.max_request_body_bytes.
rulesobject[][]Restrict capture to specific destinations. Uses the same format as allowlist.rules[].
- name: body_capture
  config:
    max_request_body_bytes: 16384
    rules:
      - host: "api.anthropic.com"
        methods: ["POST"]
        paths: ["/v1/messages"]

When secrets runs with match_body: true, place body_capture before secrets so the audit log records the workload's proxy tokens rather than the real credentials swapped in.

annotate

Captures HTTP request header values into audit log annotations based on host, method, and path rules. This is useful for enriching audit logs with request-specific context like request IDs or authorization tokens. This transform never rejects requests.

FieldTypeDefaultDescription
annotationsobject[][]List of annotation groups. See below.

annotate.annotations[]

FieldTypeDescription
rulesobject[]Rules to match requests against. Uses the same format as allowlist.rules[].
headersstring[]Header names to capture from matching requests. Values are written as header:<Name> entries in the transform trace.
- name: annotate
  config:
    annotations:
      - rules:
          - host: "api.openai.com"
            methods: ["POST"]
            paths: ["/v1/*"]
        headers: ["x-request-id", "authorization"]

Example audit log annotation output:

{
  "header:X-Request-Id": "req-abc123",
  "header:Authorization": "Bearer sk-ant-..."
}

grpc

Delegates request and response processing to an external gRPC server implementing the TransformService API. You can define multiple grpc transforms to pipeline through several servers.

FieldTypeDefaultDescription
namestringrequiredIdentifier for this transform, used in logs and error messages.
targetstringrequiredgRPC server address (e.g., localhost:9500).
send_request_bodybooleanfalseWhen true, forward the full request body to the gRPC server. When false, only headers are sent.
send_response_bodybooleanfalseWhen true, forward the full response body to the gRPC server. When false, only headers are sent.
rulesobject[][]Restrict which requests are forwarded to this server. Uses the same rule format as allowlist.rules[]. If empty, all requests are forwarded.
tlsobjectTLS configuration for the gRPC connection. See below.

grpc.tls

FieldTypeDefaultDescription
enabledbooleanfalseEnable TLS for the gRPC connection. When false, the connection uses plaintext.
ca_certstringsystem defaultPath to a custom CA certificate for server verification.
certstringPath to a client certificate for mTLS. Must be set together with key.
keystringPath to the client private key for mTLS. Must be set together with cert.

judge

Calls a large language model to produce an allow or deny decision for outbound requests that match the instance's rules. Each judge entry is an independent instance with its own natural-language policy, LLM backend, semaphore, and circuit breaker. See the LLM Judge reference for a full overview, including writing policies, pipeline ordering, and envelope limits.

The judge can only reject. It never approves a request that the static allowlist would have denied.

FieldTypeDefaultDescription
namestringrequiredIdentifier for this instance, used in audit logs. Must be unique across judge instances.
promptstringrequiredNatural-language policy sent to the LLM. Describes what is allowed and what is not.
rulesobject[]requiredDestinations this judge applies to. Uses the same format as allowlist.rules[]. At least one rule is required.
providerobjectrequiredLLM backend configuration. See providers below.
fallbackstring"deny"Behavior when the LLM call fails, times out, returns malformed output, or the circuit breaker is open. One of deny (reject with HTTP 403) or skip (continue to the next transform).
timeoutduration"8s"Maximum time for a single LLM call.
max_concurrentinteger100Maximum concurrent in-flight LLM calls for this instance. Additional requests wait for a slot.
circuit_breakerobjectConsecutive-failure breaker settings. See below.

judge.circuit_breaker

FieldTypeDefaultDescription
consecutive_failuresinteger5Number of consecutive failures that trips the breaker open.
cooldownduration"10s"How long the breaker stays open before admitting a single probe call.

Judge Providers

The provider block selects and configures the LLM backend. The type field discriminates the shape of the block. Two providers are supported: anthropic and openai.

anthropic

Calls the Anthropic Messages API.

FieldTypeDefaultDescription
typestringMust be anthropic.
modelstringrequiredAnthropic model ID (e.g., claude-haiku-4-5-20251001).
api_key_envstringrequiredEnvironment variable holding the Anthropic API key.
base_urlstringhttps://api.anthropic.comOverride the API base URL.
max_tokensinteger256Maximum tokens in the model response.
provider:
  type: "anthropic"
  model: "claude-haiku-4-5-20251001"
  api_key_env: "ANTHROPIC_API_KEY"
  max_tokens: 256
openai

Calls the OpenAI Chat Completions API.

FieldTypeDefaultDescription
typestringMust be openai.
modelstringrequiredOpenAI model ID (e.g., gpt-5.4-nano).
api_key_envstringrequiredEnvironment variable holding the OpenAI API key.
base_urlstringhttps://api.openai.comOverride the API base URL. Useful for Azure OpenAI or gateway deployments.
max_tokensinteger256Maximum tokens in the model response. Sent as max_completion_tokens.
provider:
  type: "openai"
  model: "gpt-5.4-nano"
  api_key_env: "OPENAI_API_KEY"
  max_tokens: 256

Full Judge Example

- name: judge
  config:
    name: "github-write-guard"
    fallback: "deny"
    timeout: "8s"
    max_concurrent: 100
    circuit_breaker:
      consecutive_failures: 5
      cooldown: "10s"
    rules:
      - host: "api.github.com"
        methods: ["POST", "PATCH", "DELETE", "PUT"]
    provider:
      type: "anthropic"
      model: "claude-haiku-4-5-20251001"
      api_key_env: "ANTHROPIC_API_KEY"
      max_tokens: 256
    prompt: |
      This agent performs code review on the repository under review.
      Allow writes to the comments and reviews endpoints of the specific
      repository under review. Deny writes to user settings, organization
      management, billing, or any repository the agent is not reviewing.

Place the judge transform before the secrets transform so the LLM provider sees proxy tokens rather than real credentials. See pipeline ordering for details.

header_allowlist

Strips any request header not present in a configured allowlist before forwarding the request upstream. The allowlist uses a default-deny model: every header must match either a literal name or a regex pattern to pass through. Stripped header names are recorded in the trace annotation stripped_headers.

Place this transform after any transforms that inject headers (such as secrets) so injected headers survive the allowlist.

FieldTypeDefaultDescription
headersstring[][]Allowed header names. Each entry is either a literal name (case-insensitive) or a /regex/ pattern compiled at config time as a case-insensitive regular expression matched against canonical header names.
rulesobject[][]Restrict the allowlist to specific destinations. Uses the same format as allowlist.rules[]. When omitted, the allowlist applies to all requests.
- name: header_allowlist
  config:
    headers:
      - "Authorization"
      - "Content-Type"
      - "User-Agent"
      - "Accept"
      - "/^X-Trace-.*$/"
    rules:
      - host: "api.openai.com"

mcp

Top-level block that enforces a default-deny tool allowlist on Streamable HTTP MCP servers. The interceptor runs after the transform pipeline, so allowlist still gates which hosts can be reached and secrets has already swapped proxy tokens before the interceptor evaluates the body.

See the MCP Interception reference for a full overview, including matchers, audit log behavior, and current limitations.

FieldTypeDefaultDescription
errorobjectJSON-RPC error envelope returned for denied calls. See below.
serversobject[][]List of MCP server policies. See below.

mcp.error

FieldTypeDefaultDescription
codeinteger-32001JSON-RPC error code returned to the agent when a tools/call is denied.
messagestring"blocked by iron-proxy policy"Error message returned alongside code.

mcp.servers[]

FieldTypeDefaultDescription
namestringrequiredIdentifier for this server policy, used in audit logs.
rulesobject[]requiredDestinations this policy applies to. Uses the same format as allowlist.rules[].
toolsobject[][]Tools allowed on this server. Anything not listed is denied (default-deny).

mcp.servers[].tools[]

FieldTypeDefaultDescription
namestringrequiredTool name to allow. Matches the name field in the MCP tools/call request and the tools/list response.
whenobject[][]Argument matchers. All matchers must pass for the call to be allowed. See below.

mcp.servers[].tools[].when[]

FieldTypeDefaultDescription
pathstringrequiredDotted path within params.arguments to evaluate (e.g., "owner", "repo").
equalsanyExact match. Mutually exclusive with in and matches.
inarrayMatch any of the listed values. Mutually exclusive with equals and matches.
matchesstringRegular expression match against the stringified value. Mutually exclusive with equals and in.
mcp:
  error:
    code: -32001
    message: "blocked by iron-proxy policy"
  servers:
    - name: github
      rules:
        - host: "mcp.github.com"
          paths: ["/mcp", "/mcp/*"]
      tools:
        - name: "search_repositories"
        - name: "create_issue"
          when:
            - path: "owner"
              equals: "ironsh"
            - path: "repo"
              in: ["iron-proxy", "tunis-v2"]

management

Configures an opt-in, bearer-authenticated HTTP management API. When set, iron-proxy listens on the configured address and exposes endpoints that operate on the running process. The API is disabled when the management block is omitted.

The first available endpoint is POST /v1/reload, which re-reads the YAML config from disk and atomically swaps in a freshly built transform pipeline with no restart and no dropped connections. Parse or build errors return HTTP 422 and leave the running pipeline untouched.

FieldTypeDefaultDescription
listenstringrequiredAddress and port for the management API. Bind to a loopback address (e.g., 127.0.0.1:9092) unless the API is exposed via a separate network.
api_key_envstringIRON_MANAGEMENT_API_KEYEnvironment variable holding the bearer token clients must present in the Authorization: Bearer ... header.
management:
  listen: "127.0.0.1:9092"
  api_key_env: "IRON_MANAGEMENT_API_KEY"

Reload a running proxy:

curl -X POST http://127.0.0.1:9092/v1/reload \
  -H "Authorization: Bearer $IRON_MANAGEMENT_API_KEY"

metrics

Configures the OpenTelemetry/Prometheus metrics endpoint.

FieldTypeDefaultDescription
listenstring":9090"Address and port for the metrics endpoint.

log

Configures logging output.

FieldTypeDefaultDescription
levelstring"info"Log verbosity. One of debug, info, warn, or error.

OpenTelemetry Environment Variables

OTEL log export is configured through environment variables, not the YAML config file. Set OTEL_EXPORTER_OTLP_ENDPOINT to enable it. See the OTEL export guide for usage examples.

VariableTypeDefaultDescription
OTEL_EXPORTER_OTLP_ENDPOINTstring(disabled)OTLP collector URL (e.g., https://otel-collector.example.com:4318). Export is disabled when unset.
OTEL_EXPORTER_OTLP_PROTOCOLstringhttp/protobufTransport protocol: http/protobuf or grpc.
OTEL_EXPORTER_OTLP_HEADERSstring(none)Comma-separated key=value pairs sent as headers on every export request. Typically used for authentication.
OTEL_SERVICE_NAMEstringiron-proxyService name attached to all log records.
OTEL_RESOURCE_ATTRIBUTESstring(none)Comma-separated key=value resource attributes added to all log records (e.g., deployment.environment=staging).