# Configuration reference

> Every field of the OpenWAF Egress JSON configuration, with its default and what the gateway does with it.

OpenWAF Egress reads one JSON file at startup (`-config`, `config.json` by default; the image passes `-config /app/config.json`). Changes need a restart. This page lists every field of `pkg/egress/config.go` in v0.1.1 and its default; a field you leave out takes the default.

## Full example

The `config.example.json` of the repository, which is also the file built into the image:

```json
{
  "listen_addr": "0.0.0.0:8070",
  "openbao": {
    "addr": "https://vault.example.com:8200",
    "ca_file": "",
    "approle_file": "",
    "kv_mount": "secret"
  },
  "targets": {
    "openrouter": {
      "base_url": "https://openrouter.ai/api/v1",
      "host": "openrouter.ai",
      "auth_header": "Authorization",
      "auth_prefix": "Bearer "
    },
    "openai": {
      "base_url": "https://api.openai.com/v1",
      "host": "api.openai.com",
      "auth_header": "Authorization",
      "auth_prefix": "Bearer "
    },
    "anthropic": {
      "base_url": "https://api.anthropic.com/v1",
      "host": "api.anthropic.com",
      "auth_header": "x-api-key",
      "auth_prefix": ""
    },
    "huggingface": {
      "base_url": "https://router.huggingface.co/v1",
      "host": "router.huggingface.co",
      "auth_header": "Authorization",
      "auth_prefix": "Bearer "
    }
  },
  "agents": {
    "my-support-agent": {
      "token": "OWE_TOKEN_SUPPORT_AGENT",
      "allowed_targets": ["openrouter", "openai"],
      "credential_mapping": {
        "openrouter": { "path": "agents/support", "key": "OPENROUTER_API_KEY" },
        "openai": { "path": "agents/support", "key": "OPENAI_API_KEY" }
      },
      "enforce_injection": true,
      "block_dlp": true
    }
  }
}
```

`OWE_TOKEN_SUPPORT_AGENT` is public: replace it with a long random value per agent.

The README adds a Gemini target, whose key goes in `x-goog-api-key`:

```json
"gemini": {
  "base_url": "https://generativelanguage.googleapis.com",
  "host": "generativelanguage.googleapis.com",
  "auth_header": "x-goog-api-key"
}
```

## Top level

| Field | Type | Default | What it does |
|---|---|---|---|
| `listen_addr` | string | `127.0.0.1:8070` | Address and port the gateway listens on. The `-addr` flag overrides it. Inside a container use `0.0.0.0:8070` and publish the port on loopback or a private network only. |
| `openbao` | object | (empty) | Connection to OpenBao or HashiCorp Vault. Without `openbao.addr` the gateway logs `credential injection disabled` and injects no keys. |
| `targets` | object | (none) | Upstream APIs by alias. The alias is the `<target>` of the URL. |
| `agents` | object | (none) | Agent policies by name. The name is the `<agent>` of the URL. |

## openbao

| Field | Type | Default | What it does |
|---|---|---|---|
| `addr` | string | (none) | Base URL of OpenBao or Vault, e.g. `https://openbao.example.com:8200`. Required for key injection. |
| `ca_file` | string | system roots | PEM file with the CA that signed the server certificate. The connection requires TLS 1.3. |
| `approle_file` | string | (none) | JSON file with `{"role_id": "…", "secret_id": "…"}`. Read at startup when `role_id` or `secret_id` is not set inline. |
| `role_id` | string | (none) | AppRole role ID, inline. |
| `secret_id` | string | (none) | AppRole secret ID, inline. Prefer `approle_file` so the secret stays out of the main configuration. |
| `kv_mount` | string | `kv` | Mount of the KV v2 engine. A secret is read from `GET /v1/<kv_mount>/data/<path>`. Vault's default mount is `secret`. |

The gateway logs in at `POST /v1/auth/approle/login` and renews the token two minutes before its lease ends (one hour when the server returns no lease). Each secret is cached for five minutes, so a rotated key reaches the gateway within five minutes. Requests to OpenBao time out after ten seconds.

## targets.<alias>

| Field | Type | Default | What it does |
|---|---|---|---|
| `base_url` | string | (none) | Where requests go. The rest of the agent's path and its query string are appended: `/v1/agent/a/proxy/openrouter/chat/completions` → `<base_url>/chat/completions`. |
| `host` | string | (none) | `Host` header sent upstream, and the name the SSRF check resolves. The connection itself goes to the host of `base_url`: set both to the same name. |
| `auth_header` | string | `Authorization` | Header that receives the key. Anthropic uses `x-api-key`, Gemini `x-goog-api-key`. Keys in the query string (`?key=`) are not injected: use the header. |
| `auth_prefix` | string | `Bearer ` when `auth_header` is `Authorization`, otherwise empty | Text put before the key. An empty value with `Authorization` becomes `Bearer `. |

## agents.<name>

| Field | Type | Default | What it does |
|---|---|---|---|
| `token` | string | (none) | The agent's placeholder. When set, every request must carry `Authorization: Bearer <token>`, and that header is removed before forwarding. When empty, anyone who knows the agent's name can use it: always set one. |
| `allowed_targets` | list of strings | `[]` | Target aliases this agent may call. `"*"` allows every target. With an empty list every request gets `403 egress_blocked`. |
| `credential_mapping` | object | `{}` | Target alias → `{"path": "…", "key": "…"}`: the secret path under `kv_mount` and the field inside it. A target without a mapping gets no injected key. |
| `enforce_injection` | boolean | `false` | Run the prompt-injection detectors over the body; a hit gets `403 injection_detected`. `cost.amplification` hits are ignored. |
| `block_dlp` | boolean | `false` | Scan the body for card numbers, private keys and API keys; a hit gets `403 data_leak_blocked`. |

With `enforce_injection` or `block_dlp` on, the text of the body must fit the 1 MiB inspection window. Inline media does not count: `data:image/…;base64,…` (also audio and video) URIs and JSON `"data"` members of at least 1 KiB of bare base64 (OpenAI `input_audio`, Gemini `inlineData`, Anthropic base64 sources) are forwarded but not inspected.

## Command-line flags

| Flag | Default | What it does |
|---|---|---|
| `-config` | `config.json` | Path to the configuration file. |
| `-addr` | (none) | Overrides `listen_addr`. |
| `-version` | | Prints the version and exits. |

## Fixed limits

These are constants in v0.1.1, not configuration:

| Limit | Value |
|---|---|
| Request body | 8 MiB (`413 body_too_large`) |
| Inspection window, with `enforce_injection` or `block_dlp` | 1 MiB of text; `data:` media URIs and JSON `"data"` members of ≥ 1 KiB base64 excluded (`413 body_too_large`) |
| Detections returned per request | 16, each snippet at most 160 bytes |
| Secret cache | 5 minutes |
| OpenBao requests | 10 s timeout; 3 s for the `/health` check |
| Upstream | 10 s TLS handshake, 5 min until the provider's response headers, 15 min per exchange (streamed body included) |
| Gateway server | 10 s to read request headers, 120 s to read the request, 15 min to write the response, 120 s idle |
| Redirects | never followed: the `3xx` goes back to the agent |

Written from [github.com/openwaf-dev/egress@v0.1.1](https://github.com/openwaf-dev/egress/tree/v0.1.1).
