---
title: "LLM Gateway Configuration"
description: "Chart values, environment variables, and startup validation for the LLM Gateway."
source: "Guardian Platform"
---
The LLM Gateway is configured through the `gateway-stack` values of the platform chart, under `gateway-stack.llmGateway`. This page describes the chart values operators set, the environment variables the service reads outside the chart, and the checks it runs at startup.

## Required Configuration Areas

- Gateway database connection, the same PostgreSQL database the Control Plane uses. The platform chart supplies it from the `gateway-dsn` key of the platform database Secret.
- Agent registration turned on, with a credential signer seed. The chart gives the gateway the Control Plane's registration token secret and issuer, so set those once, in [Control Plane Configuration](../control-plane/configuration.mdx#chart-values).
- The Auth Service URL, for agents that register with an API key.
- The plugin runtime, with the same trusted signer keys, artifact storage, and plugin secrets key as the Control Plane, before you configure policies.
- Project context, so project configurations and memberships apply.

## Chart Values

The platform chart already points the gateway at the platform database and sets `deploymentEnvironment` to `production`. Set the rest yourself. `<release>` is your platform release name, such as `guardian-platform`.

```yaml title="values.yaml"
gateway-stack:
  enabled: true
  llmGateway:
    registration:
      enabled: true
      credentialSigner:
        existingSecret: "guardian-gateway-credential-signer" # holds seed
    authServiceBaseURL: "http://<release>-auth-service:8080"
    plugins:
      enabled: true
      artifactStorage:
        provider: "gcs"
        bucket: "guardian-plugins"
      trustedSignerKeys:
        signer-v1: "did:key:z6Mk..."
      secrets:
        resolutionOrder: ["store"]
        encryptionKey:
          existingSecret: "guardian-plugin-secrets-key" # holds encryption-key
    projectContext:
      mode: "enforce"
  ingress:
    enabled: true
    className: "nginx"
    hosts:
      llmGateway: "gateway.example.com"
```

| Value                                                      | Default                            | Notes                                                                                                                                                                                                     |
| ---------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signature.label`                                          | `sig`                              | The label Viper signs with. With any other label, every signed request is refused.                                                                                                                        |
| `signature.defaultSigMaxAge` / `defaultClockSkew`          | `60s` / `120s`                     | How old a signature may be, and how far a client's clock may drift.                                                                                                                                       |
| `registration.enabled`                                     | `false`                            | Must be `true` for agents to register, with a registration token or an API key.                                                                                                                           |
| `registration.credentialSigner.existingSecret` / `seed`    | none                               | A Secret with the Ed25519 seed the gateway signs registration credentials with, under `secretKey` (default `seed`), or the seed inline. With registration on, the gateway exits at startup without it.    |
| `authServiceBaseURL`                                       | none                               | Turns on API-key registration. Use the Auth Service URL the Control Plane uses, `http://<release>-auth-service:8080` in the platform chart, so both derive the same identity issuer.                      |
| `authServiceIdentityIssuer`                                | `<authServiceBaseURL>/api/v1/auth` | Override only when the Control Plane reaches Auth Service at a different URL.                                                                                                                             |
| `upstreams.anthropicURL` / `openaiURL`                     | the public provider APIs           | The ChatGPT backend has no chart value; override it with `extraArgs: ["-chatgpt-backend-upstream-url=…"]`.                                                                                                |
| `audit.enabled`                                            | `true`                             |                                                                                                                                                                                                           |
| `audit.retentionDays`                                      | `90`                               | Exchanges older than this are deleted every 24 hours, counted from pod start.                                                                                                                             |
| `audit.queueMaxBytes`                                      | `10737418240` (10 GiB)             | The on-disk queue, an `emptyDir`. Once it is full, new audit records are dropped.                                                                                                                         |
| `audit.batchSize` / `flushInterval`                        | `100` / `1s`                       |                                                                                                                                                                                                           |
| `plugins.enabled`                                          | `false`                            | Runs the global and project plugin configurations. Without a trusted signer key, the gateway exits at startup.                                                                                            |
| `plugins.pollInterval`                                     | `15s`                              | How often the gateway reloads the active configurations.                                                                                                                                                  |
| `plugins.authorizerShadowEnabled`                          | `false`                            | Compares plugin decisions with the legacy authorizer, without enforcing the legacy result.                                                                                                                |
| `plugins.artifactStorage.*`                                | none                               | The same provider, bucket, and prefix as `controlPlane.plugins.artifactStorage`, with credentials that can read them. The same keys as the Control Plane's, except `maxUploadBytes` and `gcs.kmsKeyName`. |
| `serviceAccount.annotations`                               | none                               | The gateway runs as its own service account. Without a storage credentials Secret, bind the cloud identity that reads the artifact bucket here.                                                           |
| `plugins.trustedSignerKeys`                                | none                               | Map of key ID to `did:key`. The same keys as `controlPlane.plugins.trustedSignerKeys`, including the Control Plane's publisher signer.                                                                    |
| `plugins.secrets.resolutionOrder`                          | none                               | Set `["store"]` so plugins can read the secrets stored through the Control Plane. Without it, plugins that need a secret fail.                                                                            |
| `plugins.secrets.encryptionKey.existingSecret`             | none                               | The same Secret as `controlPlane.plugins.secrets.encryptionKey`. Required when `resolutionOrder` includes `store`.                                                                                        |
| `plugins.secretEnvs` / `secretEnvsSecret.name`             | none                               | Plugin secrets as environment variables, inline or from a Secret, for local development. With either set and `resolutionOrder` empty, the chart sets the resolution order to `env`.                       |
| `projectContext.mode`                                      | `disabled`                         | `observe` or `enforce` turns on project context; see [Project Context](#project-context).                                                                                                                 |
| `extraArgs`                                                | none                               | Command-line flags for settings without a chart value.                                                                                                                                                    |
| `extraEnv`                                                 | none                               | Extra environment variables, such as the `OTEL_*` exporter settings. See [Observability](../deployment/observability.mdx#agent-gateway-telemetry).                                                        |
| `replicaCount` / `autoscaling.*` / `podDisruptionBudget.*` | `1` / off / off                    |                                                                                                                                                                                                           |
| `probes.startup.failureThreshold` / `periodSeconds`        | `180` / `5`                        | Allows 15 minutes for database migrations before liveness checks start.                                                                                                                                   |

### Project Context

Project context is off unless you turn it on with `projectContext.mode`. While it is off, the gateway ignores the project an agent presents: project configurations, project membership, and project trust do not apply, and every request runs under the global configuration only. Set it to `enforce` (or `observe`, which currently behaves the same) once plugins are on; the chart refuses to render with project context on and plugins off. The gateway's mode denies nothing itself: with either value, a request that presents a project is denied when no project membership entry can admit it, and otherwise the Project Membership entry's own `mode` decides whether a failed membership check denies it; see [Gateway Policies](../user-guide/gateway-policies-and-plugins.mdx#project-membership).

## Environment Variables

Outside the chart, the gateway reads these variables. Every name starts with `LLM_GATEWAY_`, left out below. The service also reads a TOML file named by `-config` or `LLM_GATEWAY_CONFIG_FILE`, and accepts a command-line flag for most settings. A flag overrides its environment variable, which overrides the file.

| Category     | Variable                                               | Default                                                                             |
| ------------ | ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| Server       | `LISTEN`                                               | `:10000`                                                                            |
| Server       | `METRICS_LISTEN`                                       | `:10001`                                                                            |
| Server       | `DEPLOYMENT_ENVIRONMENT`                               | none                                                                                |
| Server       | `SERVICE_VERSION`                                      | none; reported as `dev`                                                             |
| Server       | `MAX_REQUEST_BODY_BYTES`                               | `33554432` (32 MiB)                                                                 |
| Database     | `REGISTRY_DB_DSN`                                      | `AUDIT_DB_DSN`; one of the two is required                                          |
| Database     | `AUDIT_DB_DSN`                                         | none; required while audit is enabled                                               |
| Database     | `BLOB_STORE_DB_DSN`                                    | `REGISTRY_DB_DSN`                                                                   |
| Registration | `AGENT_REGISTRATION_ENABLED`                           | `true`                                                                              |
| Registration | `REGISTRATION_TOKEN_SECRET`                            | none; required when registration is enabled                                         |
| Registration | `REGISTRATION_TOKEN_ISSUER`                            | `control-plane`                                                                     |
| Registration | `CREDENTIAL_SIGNER_SEED`                               | none; required when registration is enabled                                         |
| Registration | `AUTH_SERVICE_BASE_URL`                                | none; API-key registration is off without it                                        |
| Registration | `AUTH_SERVICE_IDENTITY_ISSUER`                         | `<AUTH_SERVICE_BASE_URL>/api/v1/auth`                                               |
| Registration | `AUTH_SERVICE_HTTP_TIMEOUT` / `AUTH_SERVICE_CACHE_TTL` | `10s` / `30s`                                                                       |
| Verification | `SIGNATURE_LABEL`                                      | `sig`                                                                               |
| Verification | `DEFAULT_SIG_MAX_AGE` / `DEFAULT_CLOCK_SKEW`           | `60s` / `120s`                                                                      |
| Verification | `TRUSTED_YUBIKEY_ROOT_CERTS`                           | none                                                                                |
| Upstreams    | `ANTHROPIC_UPSTREAM_URL`                               | `https://api.anthropic.com`                                                         |
| Upstreams    | `OPENAI_UPSTREAM_URL`                                  | `https://api.openai.com`                                                            |
| Upstreams    | `CHATGPT_BACKEND_UPSTREAM_URL`                         | `https://chatgpt.com/backend-api`                                                   |
| Audit        | `AUDIT_ENABLED`                                        | `true`                                                                              |
| Audit        | `AUDIT_QUEUE_DIR` / `AUDIT_QUEUE_MAX_BYTES`            | `data/audit-queue` / `10737418240` (10 GiB)                                         |
| Audit        | `AUDIT_BATCH_SIZE` / `AUDIT_FLUSH_INTERVAL`            | `100` / `1s`                                                                        |
| Audit        | `AUDIT_RETENTION_DAYS`                                 | `90`                                                                                |
| Blobs        | `BLOB_MAX_BYTES`                                       | `67108864` (64 MiB)                                                                 |
| Plugins      | `PLUGINS_ENABLED`                                      | `false`                                                                             |
| Plugins      | `PLUGIN_POLL_INTERVAL`                                 | `15s`                                                                               |
| Plugins      | `PLUGIN_END_QUEUE_DIR` / `PLUGIN_END_QUEUE_MAX_BYTES`  | `data/plugin-end-queue` / `134217728` (128 MiB)                                     |
| Plugins      | `PLUGIN_REDACT_HEADERS`                                | none                                                                                |
| Plugins      | `PLUGIN_ARTIFACT_CACHE_DIR`                            | `data/plugin-artifacts`                                                             |
| Plugins      | `PLUGIN_TRUSTED_SIGNER_KEYS`                           | none; at least one is required when plugins are enabled                             |
| Plugins      | `PLUGIN_SECRETS_RESOLUTION_ORDER`                      | none                                                                                |
| Plugins      | `PLUGIN_SECRETS_ENCRYPTION_KEY`                        | none                                                                                |
| Plugins      | `PLUGIN_SECRETS_ENV_PREFIX`                            | `GUARDIAN_SECRET_`                                                                  |
| Plugins      | `PLUGIN_AUTHORIZER_SHADOW_ENABLED`                     | `false`                                                                             |
| Plugins      | `PLUGIN_WASM_INTERPRETER`                              | `false`                                                                             |
| Plugins      | `PLUGIN_ARTIFACT_STORAGE_*`                            | none; the Control Plane's settings except `MAX_UPLOAD_BYTES` and `GCS_KMS_KEY_NAME` |
| Projects     | `PROJECT_CONTEXT_MODE`                                 | `disabled`                                                                          |

`PLUGIN_TRUSTED_SIGNER_KEYS` is a comma-separated list of `key_id=public_key` pairs, where the public key may be a `did:key`; its flag, `-plugin-trusted-signer-key`, takes one pair and repeats. `TRUSTED_YUBIKEY_ROOT_CERTS` is a comma-separated list of PEM file paths. `PLUGIN_SECRETS_RESOLUTION_ORDER` is a comma-separated list of `store` and `env`: `store` reads the secrets stored through the Control Plane, and `env` reads environment variables named with `PLUGIN_SECRETS_ENV_PREFIX`, for local development. The credential signer seed and the plugin secrets key are base64, base64url, or hex; the seed may also be a path to a file that holds it.

`PLUGIN_REDACT_HEADERS` is a comma-separated list of extra header names, matched case-insensitively, whose values the gateway masks before `request.end` plugins and their on-disk queue see them. Headers whose names contain `auth`, `token`, `secret`, `password`, `session`, `cookie`, `api-key` or `apikey`, or end in `-key`, are always masked; use this setting for other upstream credential headers.

`PLUGIN_WASM_INTERPRETER` has no flag: `true` interprets plugin WebAssembly instead of compiling it to native code.

`DEPLOYMENT_ENVIRONMENT` is also reported to OpenTelemetry as `deployment.environment` unless `OTEL_RESOURCE_ATTRIBUTES` sets it. The service reads a few variables that are not listed here: `REGISTRY_BACKEND`, which accepts only `postgres`; the `EXT_REGISTRY_*` external registry settings, which are not supported in production; and `PROJECT_CONTEXT_REQUIRE_MEMBERSHIP` and `PROJECT_TRUSTED_ISSUER_DIDS`, which are deprecated and have no effect.

The Control Plane must use the same registration token secret and issuer, trusted signer keys, and plugin secrets key as the gateway, listed in [Control Plane Configuration](../control-plane/configuration.mdx#environment-variables), as well as the same artifact storage and Auth Service URL.

## Startup Validation

The gateway refuses to start when its configuration is inconsistent, and logs `LLM Gateway failed: invalid config:` followed by the setting at fault, usually in flag form. The checks include:

- A database DSN is required.
- The upstream URLs and the signature label must be set, and the upstream URLs must be `http` or `https` URLs with a host.
- The request body and blob size limits must be above zero.
- Registration requires a registration token secret.
- An Auth Service identity issuer requires an Auth Service URL, the URL must be valid, and its timeout and cache TTL must be above zero.
- Audit capture requires an audit DSN and a queue directory, and its queue size, batch size, flush interval, and retention must be above zero.
- Plugins require at least one trusted signer key, a positive poll interval, their queue and cache directories, and a queue size above zero.
- Project context must be `disabled`, `observe`, or `enforce`, and any mode other than `disabled` requires plugins.
- Artifact storage needs a bucket once a provider is set, a region and credentials for `s3`, and exactly one of a connection string, an account key, or managed identity for Azure.

These also stop startup, with `LLM Gateway failed:` and the reason:

- a missing credential signer seed while registration is on, or an unreadable seed
- a `store` secret resolver without the plugin secrets key, or a resolver other than `store` or `env`
- a trusted signer key that is not a valid public key
- unreadable YubiKey root certificates
- an active plugin configuration that cannot be loaded, such as one whose artifact is unreachable or signed by an untrusted key
- an unreachable database

Keep secrets such as the credential signer seed, the plugin secrets key, and storage credentials in the deployment's secret management system, not in values files.

## Related

- [Chart Values](../deployment/chart-values.mdx) — where the gateway stack settings live in values.yaml
- [Control Plane Configuration](../control-plane/configuration.mdx) — the registration token secret and the plugin settings the gateway shares
- [Register Agents](../user-guide/register-agents.mdx) — the registration paths these settings turn on
- [LLM Gateway Operations](./operations.mdx) — checks to run once the service is configured
- [Observability](../deployment/observability.mdx) — metrics and OpenTelemetry settings