# Configuration

> The settings every self-hosted ACOG installation needs: secrets, production mode, public address, email, trusted proxies, the semantic cache and DLP models.

Product: AI Cost Optimizer Gateway (ACOG). Web version: https://tessarac.com/docs/acog/self-hosted/configuration. Last updated: 2026-10-16.

ACOG is configured with environment variables, set through the chart's `infra.secret` (secrets) and `services.<service>.extraEnv` (everything else). Each service reads its environment once at start, so a change takes effect after a restart (`helm upgrade` restarts what changed).

## Secrets you generate

| Variable | Generate with | Notes |
|---|---|---|
| `ACOG_CREDS_KEY` | `openssl rand -base64 32` | Encrypts every stored provider credential, two-factor secrets, SSO client secrets and the DLP dictionary key. The control plane and data plane must have the same value. **If it is lost or changed, every stored credential must be entered again.** |
| `ACOG_OPERATOR_TOKEN` | `openssl rand -hex 32` | Authenticates internal calls between services. If empty, DLP still enforces but records nothing. |
| `ACOG_INGEST_TOKEN` | `openssl rand -hex 32` | Authenticates usage records sent to metering. Metering will not start without it in production mode. |
| `ACOG_POSTGRES_DSN` | | The PostgreSQL connection string |
| `ACOG_CLICKHOUSE_PASSWORD` | | Must match the ClickHouse password |

## Control plane

| Variable | Set to | Why |
|---|---|---|
| `ACOG_ENVIRONMENT` | `production` | The default, `development`, writes account-recovery links to the log instead of mailing them. Production mode requires an email relay and marks the session cookie `Secure`, so the console must be served over HTTPS. Set it on metering too. |
| `ACOG_SELF_SERVE_SIGNUP` | `false` | Public sign-up is on by default |
| `ACOG_WEB_ORIGIN`, `ACOG_PUBLIC_BASE_URL` | `https://acog.example.com` | Your console's real address. Email links, SSO redirects and passkeys are built from it. |
| `ACOG_TRUSTED_PROXY_CIDRS` | Your ingress and load-balancer networks | So per-key IP allowlists see the real client address |
| `ACOG_BOOTSTRAP_ADMIN_EMAIL` | The first administrator's email | First install only; see [Install on Kubernetes](https://tessarac.com/docs/acog/self-hosted/kubernetes.md#5-sign-in-as-the-first-administrator) |
| `ACOG_CLICKHOUSE_DATABASE` | `acog` (already set) | Leave as is |

### Email

| Variable | Notes |
|---|---|
| `ACOG_EMAIL_PROVIDER` | `smtp` or `ses`. In production the control plane refuses to start without one. |
| `ACOG_EMAIL_FROM` | The sender address |
| `ACOG_SMTP_HOST`, `ACOG_SMTP_PORT` | Port 587 uses STARTTLS. Set `ACOG_SMTP_TLS` only for implicit TLS on port 465. |
| `ACOG_SMTP_USERNAME`, `ACOG_SMTP_PASSWORD` | Put these in the infra Secret, not in a values file |
| `ACOG_SES_REGION` | For `ses` |

## Data plane

| Variable | Default | Notes |
|---|---|---|
| `ACOG_TRUSTED_PROXY_CIDRS` | | As for the control plane |
| `ACOG_MAX_REQUEST_BYTES` | `67108864` (64 MiB) | Keep in step with the web tier's body limit |
| `ACOG_BATCH_HOLD_SECS` | `600` | The longest batch wait served on the caller's held connection; a longer one gets a job handle to poll. The connection is silent while held, so keep this at 600 or below: the web tier waits 660 s, as should your load balancer (see [Install on Kubernetes](https://tessarac.com/docs/acog/self-hosted/kubernetes.md#2-write-your-own-overlay)). Otherwise held batches are cut off. |
| `ACOG_RATE_LIMIT_ORG_PER_MIN` | `600` | Default per-organization request limit; `0` disables |
| `ACOG_RATE_LIMIT_KEY_PER_MIN` | `300` | Default per-service-key request limit; `0` disables |

## Semantic cache

The [semantic cache](https://tessarac.com/docs/acog/optimize/semantic-cache.md) is off on a self-hosted installation until you add a vector store. You need all four of these:

```yaml
infra:
  bundled:
    milvus:
      enabled: true
    minio:                              # a sibling of milvus, not inside it
      rootPassword: "<a real password>"
services:
  dataplane:
    extraEnv:
      ACOG_EMBEDDING_ENDPOINT: "http://aco-embedder:8086/embed"   # the full path
      ACOG_MILVUS_URL: "http://aco-milvus:19530"
corpusTenancy:
  semanticNameKeySecret:
    name: aco-semantic-cache-name-key
```

```bash
kubectl -n aco create secret generic aco-semantic-cache-name-key \
  --from-literal=ACOG_SEMANTIC_CACHE_NAME_KEY="$(openssl rand -hex 32)"
```

Milvus brings an etcd and a MinIO with it: three more Deployments and volumes (20, 5 and 20 GiB). Keep the naming key; a different key starts every organization's cache empty again.

Each organization then switches the cache on or off under **Admin → Gateway settings**.

## Data loss prevention

The chart runs the DLP service and a named-entity model for detecting names and medical terms. The DLP service reports `ner_ready` once the model is loaded; until then those two data types are unavailable and the console says so.

## Keyless provider connections

To offer [keyless connections](https://tessarac.com/docs/acog/providers/keyless-connections.md), set the issuer address. See [Install identity](https://tessarac.com/docs/acog/self-hosted/install-identity.md).

| Variable | Notes |
|---|---|
| `ACOG_WORKLOAD_IDENTITY_ISSUER_BASE` | Your API's public address plus `/v1/identity`. Unset means keyless connections are not offered. |
| `ACOG_WORKLOAD_IDENTITY_KEY_SOURCE` | `local` (default): signing keys are generated and stored sealed by `ACOG_CREDS_KEY`. `kms`: AWS KMS. |
| `ACOG_WORKLOAD_IDENTITY_KEY_ROTATION` | Default `2160h` (90 days) |
| `ACOG_WORKLOAD_IDENTITY_KEY_ACTIVATION_DELAY` | Default `24h`. If you upload key sets to Google or Anthropic by hand, allow days. |
