Last updated 2026-06-29
Configuration
This page lists every configuration knob the service reads. All
configuration is via environment variables with the GATEWAY_
prefix — the same convention as identity, so the two services deploy with
one consistent style. Defaults are tuned so the service runs with
zero configuration for local development; production
deployments override the Postgres DSN and the service-auth tokens.
The reference table is generated directly from the service's configuration
source (internal/config/config.go), so the variable names,
defaults, and validation floors here can never drift from the code.
All variables
| Variable | Type | Default | Description | Constraint |
|---|---|---|---|---|
GATEWAY_ADMIN_API_SECRET | string | (empty) | AdminAPISecret gates the AdminService (project configuration), presented as the `X-Admin-Secret` header. Empty disables the admin RPCs entirely. | must be a high-entropy value of at least 32 characters |
GATEWAY_ADMIN_RATE_LIMIT_PER_MINUTE | int | 30 | AdminRateLimitPerMinute throttles the admin API per caller (online brute-force protection); non-positive disables the limiter. | |
GATEWAY_ALLOWED_ORIGINS | csv | (empty) | AllowedOrigins is the CORS allow-list of browser origins permitted to call the API. Empty allows none. | |
GATEWAY_AUDIT_LOG | bool | false | AuditLog enables an async, append-only audit log of every relation-tuple change and admin mutation (to the structured logger). Default false. | |
GATEWAY_CONNECT_PORT | int | 8080 | ConnectPort is the Connect/HTTP listener serving JSON, gRPC, and gRPC-Web RPCs. | |
GATEWAY_DATA_REGION | string | (empty) | DataRegion is the region this instance serves: it refuses to operate on a project pinned to a different data_region (fail closed). Empty (default) is region-agnostic — serves every project, today's behavior. | |
GATEWAY_DECISION_LOG | bool | false | DecisionLog enables an async, append-only audit log of every Check/ CheckSet decision (to the structured logger). Default false. | |
GATEWAY_DEFAULT_PROJECT_ID | string | default | DefaultProjectID is the project shard used for any request whose project_id is empty. | |
GATEWAY_DEFAULT_TENANT_ID | string | (empty) | DefaultTenantID is the tenant pinned for requests that omit tenant_id (the project's default tenant). Empty is the conventional default. | |
GATEWAY_HTTP_MAX_BODY_BYTES | int | 1048576 | HTTPMaxBodyBytes is the maximum accepted request body, in bytes. Must be positive. | must be positive |
GATEWAY_MAX_BATCH_CHECK_ITEMS | int | 1000 | MaxBatchCheckItems caps the number of items in a single BatchCheck request, bounding per-request cost. | |
GATEWAY_MAX_CHECK_READS | int | 5000 | MaxCheckReads caps the number of store reads (tuple lookups) a single Check/CheckSet/Expand/ListObjects evaluation may perform, bounding the per-request cost a pathological cyclic/branching graph can inflict. Non-positive uses the service default. | when set, must be >= 100 (use 0/negative for the default) |
GATEWAY_MAX_EXPAND_NODES | int | 10000 | MaxExpandNodes caps the size of an Expand result tree, bounding the response a single cheap request can amplify into. | |
GATEWAY_MAX_LIST_OBJECTS | int | 1000 | MaxListObjects caps the candidate set a single ListObjects call scans, bounding its full-scan + per-object Check cost. | |
GATEWAY_METRICS_PORT | int | 9090 | MetricsPort serves the Prometheus /metrics endpoint and the health probes. | |
GATEWAY_POSTGRES_AUTO_MIGRATE | bool | false | PostgresAutoMigrate runs the expand migration on boot when true. It defaults to FALSE: migrations are a deliberate operator step (out-of-band `workspace migrate`, an init container, or a migrate Job), so a large existing DB's first deploy can never livelock on a bounded CONCURRENTLY build inside the boot window. Opt in (true) only for small/dev DBs. | |
GATEWAY_POSTGRES_DSN | string | (empty) | PostgresDSN selects the postgres driver when set; empty uses memory. | |
GATEWAY_SERVICE_AUTH_TOKENS | csv | (empty) | ServiceAuthTokens are the accepted service credentials presented by calling backends as `Authorization: Bearer <token>`. This is an internal service authenticated service-to-service (not by end-user tokens); the user is passed as data in the request. Empty disables the requirement — trust the network/mesh — and the service logs a warning. | |
GATEWAY_TENANT_RATE_LIMIT_PER_MINUTE | int | 0 | TenantRateLimitPerMinute throttles authz data-plane RPCs per (project, tenant); non-positive (the default) disables the limiter. | when enabled, must be >= 60 (use 0 to disable) |
Choosing a store: memory vs. Postgres
- In-memory (
GATEWAY_POSTGRES_DSNempty) — the default, and the conformance reference both drivers are pinned against. Data lives in process memory and is lost on restart. Use it for local development, tests, and ephemeral demos. - Postgres (DSN set) — durable, the only choice for
production and for running more than one replica. Every table and index
leads with
project_id. Migrations are a deliberate operator step:GATEWAY_POSTGRES_AUTO_MIGRATEdefaults tofalse, so runworkspace migrateexplicitly (see Deploy with Docker). Opt in to boot-time migration only for small or dev databases.
Service authentication
Workspaces is an internal service-to-service API: the caller is a trusted
product backend, and the end user is passed as data
(acting_user_id / subject_user_id) in the request
body. The bearer token authenticates the calling service, not an
end user. A missing or wrong credential returns HTTP 401
(Connect code Unauthenticated).
GATEWAY_SERVICE_AUTH_TOKENS is empty the service logs a
service_auth_disabled warning at startup and accepts every
caller — acceptable only behind a service mesh, mTLS, or an otherwise
private network. Anywhere else, set at least one token. The list accepts
several values so you can rotate credentials without downtime: add the new
token, roll callers over, then drop the old one. See
Security & Service Auth.
A production example
The minimum production override: a durable store and at least one service token.
docker run -p 8080:8080 -p 9090:9090 \ -e GATEWAY_POSTGRES_DSN='postgres://workspace:password@db:5432/workspace?sslmode=disable' \ -e GATEWAY_SERVICE_AUTH_TOKENS='prod-token-a,prod-token-b' \ -e GATEWAY_DEFAULT_PROJECT_ID=my-product \ -e GATEWAY_ALLOWED_ORIGINS='https://app.my-product.com' \ ghcr.io/elloloop/workspace