Gate One configuration

Generated from schemas/api-routes.schema.json by scripts/gen-config-docs. Do not edit this page: edit the schema or the generator.

The api-routes.json file consumed by the gate-one binary. Relative paths (certificates, keys, plugin artefacts, WAL directories) are resolved from the configuration file.

Fields

FieldTypeRequiredDescription

securityHeaders

object

no

Platform-wide response hardening, emitted by the gateway on EVERY response it serves (proxied or generated: health, stats, 401/403, MCP metadata, PWA assets, CSP collector). Declaring the block applies the secure defaults; each field can be tightened or disabled. Referrer-Policy is only set when the response does not already carry one (a proxified app’s stricter policy wins). Absent = nothing (an install whose edge proxy already sets them can opt out).

license

object

no

Optional offline signed licence (Ed25519), one token per period. Absent, invalid or expired = evaluation/personal mode: the gateway keeps serving; the user badge shows a notice. The private signing key NEVER ships (only publicKey is embedded).

authProvider

object

no

Authentication provider. dev is the embedded loopback-only IdP used for onboarding/tests; http-bridge is any external service implementing the Gate One HTTP bridge contract (auth-bridge is the reference implementation; a non-conforming service needs an adapter); oidc is the native Relying Party (discovery, code+PKCE, JWKS, session, logout, MCP resource server); jwt is a stateless bearer verifier. Absent = legacy oidc + auth-bridge translation, so existing configs are unchanged. The legacy type http is accepted as a deprecated alias of http-bridge.

authProviders

array of object

no

Named authentication providers for per-route binding. Mutually exclusive with the legacy singular authProvider. Each item uses the same fields as authProvider plus id/cookieName; a route selects one with auth.provider.

defaultAuthProvider

string

no

Provider id used by routes that do not set auth.provider. Required when more than one authProviders entry is declared.

defaultAppUrl

string

no

Global fallback for a bare visit to a login host root (302), preserving the query string. A provider’s own defaultAppUrl wins for its login host. Email verification/account links must carry a return target; use defaultAppUrl only as an operational fallback. Point it at a neutral page (e.g. your landing), not at one application.

oidc

object

no

LEGACY platform IdP block. When authProvider is absent, it is translated into an http provider (auth-bridge) at load; existing configs keep working. New configurations should use authProvider.

tokenBroker

object

no

Internal per-user credential broker. Required when a route declares upstream.tokenBroker; a configured block also keeps the Services page (/connections: pause/resume/erase) available when no route delegates. The shared secret is read from the named environment variable at load (never from this file) and is never logged.

mcpDefaults

object

no

Optional defaults shared by every MCP access unit.

mcpSets

map of object

no

Named groups of MCP services exposed with their own access rights (resource/scope/identity) and per-member tool filters. A member endpoint is set.host + set.endpoint + member.path.

publisherFeed

object

no

Optional signed publisher feed: compatible Gate One versions, external components and plugins, with their revocation status. Loaded and verified at startup; the operator’s trust store stays authoritative. Absent = no feed is consulted.

turnstile

object

no

Platform-wide Cloudflare Turnstile bot check for form pages. Presence enables it (explicit enabled=false disables it in templated configs). The widget is injected into matching HTML pages and every matching request is verified against the Siteverify API BEFORE the backend: a missing/invalid token is answered 403 (readable page for a navigation, JSON otherwise), a Siteverify outage 503 (fail closed). The block is global on purpose: one place to change, no per-host duplication. LIMITATION: it protects only traffic that passes through this Gate One instance; a second ingress, an internal caller or a direct port reaching the application bypasses it. Network isolation (only Gate One can reach the application) remains required. The token is never logged nor written to usage events.

usage

object

no

Out-of-process usage-event bus (WAL + extension plugins). Presence enables it. Events are produced only for the subscribed types; a full durable share is answered with a targeted 503 (health/readyz/auth-probe exempt).

routes

array of object

yes

Routes served by this gateway. Order is not significant: the most specific host/path match wins.

ssoGroups

array of object

no

Cross-domain SSO groups. Shared OIDC/client/session settings are declared once per group; providers[] declares only what varies per application domain. Expanded into authProviders at load. Auth-registry is exhaustive: a shared field placed in a provider entry is rejected by the strict decoder.

securityHeaders

Platform-wide response hardening, emitted by the gateway on EVERY response it serves (proxied or generated: health, stats, 401/403, MCP metadata, PWA assets, CSP collector). Declaring the block applies the secure defaults; each field can be tightened or disabled. Referrer-Policy is only set when the response does not already carry one (a proxified app’s stricter policy wins). Absent = nothing (an install whose edge proxy already sets them can opt out).

FieldTypeRequiredDescription

hstsMaxAge

integer

no

Strict-Transport-Security max-age, in seconds. Absent = 15552000 (180 days); 0 = header omitted (e.g. a host that must stay reachable in plain HTTP). Keep includeSubDomains/preload off until every subdomain is HTTPS-only: with preload the choice becomes permanent in browsers. Minimum: 0. Maximum: 63072000.

hstsIncludeSubDomains

boolean

no

Append includeSubDomains. Default false.

hstsPreload

boolean

no

Append preload. Requires hstsIncludeSubDomains (refused at load otherwise). Default false.

nosniff

boolean

no

X-Content-Type-Options: nosniff. Default true.

referrerPolicy

string (one of: `, `no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url)

no

Referrer-Policy value. Absent = strict-origin-when-cross-origin; empty string = header omitted.

license

Optional offline signed licence (Ed25519), one token per period. Absent, invalid or expired = evaluation/personal mode: the gateway keeps serving; the user badge shows a notice. The private signing key NEVER ships (only publicKey is embedded).

FieldTypeRequiredDescription

token

string

no

Signed licence token: v1.<base64url(JSON claims)>.<base64url(signature)>.

tokenFile

string

no

Deprecated single-token file. Prefer tokenDir (one token per period). Overridden by GATE_ONE_LICENSE_TOKEN; falls back to token when unreadable.

publicKey

string

no

Ed25519 public key, base64 (raw 32 bytes). Public by design, safe to commit. Legacy single-key form (tokens without kid).

publicKeys

map of string to string

no

kid → Ed25519 public key (base64). Several keys can be valid at once during a rotation; tokens carry their signed kid. An unknown kid is rejected.

url

string

no

Registration/pricing URL shown by the badge.

tokenDir

string

no

Directory holding one signed token per period: every regular file is read (convention licence-<start>-<end>.txt). The active periods are merged (nbf ⇐ now < exp): domains and features are unioned, domainCount and maxReplicas take the maximum. Dropping a file + SIGHUP is enough.

enforce

string (one of: info, warn, strict)

no

What to do when a configured host is not covered by the active licence: info (default, logged), warn (logged as a warning), strict (503 on that host). Eval (no active token) is never blocked.

authProvider

Authentication provider. dev is the embedded loopback-only IdP used for onboarding/tests; http-bridge is any external service implementing the Gate One HTTP bridge contract (auth-bridge is the reference implementation; a non-conforming service needs an adapter); oidc is the native Relying Party (discovery, code+PKCE, JWKS, session, logout, MCP resource server); jwt is a stateless bearer verifier. Absent = legacy oidc + auth-bridge translation, so existing configs are unchanged. The legacy type http is accepted as a deprecated alias of http-bridge.

FieldTypeRequiredDescription

type

string (one of: dev, http-bridge, http, oidc, jwt)

no

Provider implementation. Default: dev when a dev block is present, oidc when clientId is present, jwt when jwksUrl is present, http-bridge otherwise. http is a deprecated alias of http-bridge.

id

string

no

Unique provider id when declared in authProviders. The legacy authProvider block is normalized to id "default".

cookieName

string

no

Session cookie name for type oidc. Empty = gate_one_session for the default provider, a provider-scoped name for additional providers.

baseUrl

string

no

Service root for type http-bridge (default http://auth-bridge:3000, or AUTH_BRIDGE_URL).

loginBaseUrl

string

no

Public origin of the login service. For dev it hosts the embedded login page, e.g. http://login.demo.localhost:3000 (no path/query).

loginUrl

string

no

Full login page URL. Defaults to loginBaseUrl + /login, then (type http-bridge) the legacy oidc.loginUrl, then baseUrl + /login.

issuer

string

no

OIDC issuer advertised in MCP protected-resource metadata. Defaults to the legacy oidc.issuer, or loginBaseUrl + /dev for dev.

cookieDomain

string

no

Shared parent domain of the session cookie (dev), e.g. demo.localhost. Required for dev.

defaultAppUrl

string

no

Application to send a bare visit to THIS provider’s login host root to. Overrides the global defaultAppUrl for this login host. Must be a route host bound to this provider or a host covered by its cookieDomain. Prefer a neutral page rather than one application.

verifyUrl

string

no

Identity verification endpoint (type http-bridge). Default baseUrl + /auth.

mcpVerifyUrl

string

no

MCP bearer verification endpoint (type http-bridge). Default baseUrl + /auth/mcp.

healthUrl

string

no

Readiness endpoint (type http-bridge). Default baseUrl + /health.

headers

object

no

Maps the identity response headers of an external provider (type http-bridge). Empty = auth-bridge defaults.

clientId

string

no

OIDC client ID (type oidc). The client must be registered at the IdP with the redirect URI below.

clientSecretEnv

string

no

Environment variable holding the OIDC client secret (type oidc). Absent/empty = public client (PKCE only). Never put the secret in this file.

scopes

array of string

no

OIDC scopes requested at the authorization endpoint. Default: openid, profile, email.

redirectUrl

string

no

OIDC callback URL registered at the IdP. Default: loginBaseUrl + /auth/callback.

roleClaims

array of string

no

Dot paths in the ID token whose values become roles (default: realm_access.roles, resource_access.<clientId>.roles, groups).

scopesClaim

string

no

Claim holding the granted scopes (default scope; scp is always also read).

mcpAudience

string

no

MCP access-token audience policy. Empty = scope-only (compatible default). A literal value requires that value in aud. "auto" requires the endpoint’s own protected-resource identifier (recommended, and used with Keycloak audience mappers).

jwksUrl

string

no

JWKS endpoint for type jwt. Required for jwt; the provider never hosts a login page.

audience

string

no

Expected audience for type jwt (empty = no audience check, or the MCP resource when VerifyMCP is used).

algorithms

array of string

no

Accepted JWT signing algorithms. Default: the algorithms advertised by the JWKS/issuer.

header

string

no

Bearer header name for type jwt. Default Authorization.

cacheTtlSeconds

integer

no

JWKS cache TTL for type jwt. Default 300. Minimum: 0.

sessionTtlSeconds

integer

no

OIDC RP session lifetime. Default 3600 (1h): a short TTL bounds revocation when no shared store is used. The refresh/reauth flow renews it. Minimum: 1.

links

object

no

Links owned by the authentication connector, displayed in the user badge. account is the IdP’s own account console: NO OIDC standard, so the URL is explicit (Keycloak /realms/<realm>/account/, Entra myaccount.microsoft.com, Google myaccount.google.com, Okta /enduser/settings…).

sessionKeyEnv

string

no

Environment variable holding the RP cookie signing key (base64/random string, hashed to 32 bytes). Empty = the key is generated and persisted in the bbolt store. Required for multi-instance (shared key).

sessionStore

object

no

RP-side revocation store (back-channel logout, RP logout). Default: bbolt at ./gate-one-state.db, relative to the config file. bbolt is single-writer: a second Gate One instance on the same file is refused. Redis is the shared, multi-instance backend and makes revocation visible on the next request on every instance.

backchannelLogout

boolean

no

OIDC Back-Channel Logout. Absent = auto: enabled when the IdP discovery advertises backchannel_logout_supported. false disables it. Register <loginBaseUrl>/backchannel-logout at the IdP.

legacyTokenCookies

boolean

no

Migration bridge: also set auth-bridge’s access_token/refresh_token/id_token cookies on cookieDomain, so auth-bridge’s token-broker proxy routes (/connections, /connect, /oauth) resolve the same session when both share the login host. Default false. The native session cookie remains authoritative for Gate One.

backchannelForwardUrl

string

no

Migration bridge: after a valid local back-channel revocation, POST the same logout_token to this auth-bridge /backchannel-logout URL so its persistent sid list stays in sync for /auth and /connections. Best effort.

revocationTtlSeconds

integer

no

Retention of revocation entries. Default: the issuer’s advertised refresh-token TTL (cladone_refresh_token_ttl_seconds, e.g. the embedded IAM), else 30 days; never shorter than the session TTL. Must cover the refresh-token lifetime for a global logout. A revocation covers only sessions/tokens issued before it, so a later sign-in is accepted (a user is never locked out by their own logout). Minimum: 1.

dev

object

no

Embedded development IdP. Loopback-only; never production.

authProvider.headers

Maps the identity response headers of an external provider (type http-bridge). Empty = auth-bridge defaults.

FieldTypeRequiredDescription

userId

string

no

Default X-User-Id.

username

string

no

Default X-Username.

email

string

no

Default X-Email.

roles

string

no

Default X-Roles (comma-separated).

scopes

string

no

Default X-Scopes (comma-separated).

claims

string

no

Default X-Claims (base64 JSON).

Links owned by the authentication connector, displayed in the user badge. account is the IdP’s own account console: NO OIDC standard, so the URL is explicit (Keycloak /realms/<realm>/account/, Entra myaccount.microsoft.com, Google myaccount.google.com, Okta /enduser/settings…).

FieldTypeRequiredDescription

account

object

no

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

FieldTypeRequiredDescription

label

string

no

Displayed text. Optional for the connector links (default Account/Services); required for userBadge.links. Maximum length: 64.

url

string

yes

http(s) absolute URL or local absolute path (/…​). Maximum length: 2048.

target

string (one of: _blank, _self)

no

Link target. Default _blank.

authProvider.sessionStore

RP-side revocation store (back-channel logout, RP logout). Default: bbolt at ./gate-one-state.db, relative to the config file. bbolt is single-writer: a second Gate One instance on the same file is refused. Redis is the shared, multi-instance backend and makes revocation visible on the next request on every instance.

FieldTypeRequiredDescription

type

string (one of: bolt, redis)

no

bolt (default, single instance) or redis (shared, multi-instance, immediate revocation).

path

string

no

bbolt file path. Default ./gate-one-state.db (relative to the config file).

url

string

no

Redis URL for type redis (redis:// or rediss://). Mutually exclusive with sentinel mode.

sentinelAddrs

array of string

no

Redis Sentinel addresses (host:port). Required with masterName.

masterName

string

no

Redis Sentinel master name. Requires sentinelAddrs.

sentinelUsername

string

no

ACL user used to authenticate to the Sentinels themselves (sentinel mode only). Separate from username, which is the Redis data user.

sentinelPasswordEnv

string

no

Environment variable holding the Sentinel password (sentinel mode only). Never put the password in this file.

tls

boolean

no

Use TLS in sentinel mode (Sentinels and the discovered master). In URL mode use a rediss:// URL instead.

tlsCaFile

string

no

PEM CA bundle for the Sentinel TLS connections. Requires tls=true; empty = system roots.

tlsServerName

string

no

TLS server name override (useful when the Sentinels are addressed by IP). Requires tls=true.

username

string

no

Redis ACL username.

passwordEnv

string

no

Environment variable holding the Redis password. Never put the password in this file.

db

integer

no

Redis logical database. Default 0. Minimum: 0.

prefix

string

no

Shared revocation key prefix. Default cladone:auth:v1:. Must match the embedded IAM’s redis.authPrefix.

dialTimeoutMs

integer

no

Redis dial timeout in milliseconds. Default 500. Minimum: 0.

readTimeoutMs

integer

no

Redis read timeout in milliseconds. Default 300. Minimum: 0.

writeTimeoutMs

integer

no

Redis write timeout in milliseconds. Default 300. Minimum: 0.

poolSize

integer

no

Redis connection pool size. Default 64. Minimum: 0.

authProvider.dev

Embedded development IdP. Loopback-only; never production.

FieldTypeRequiredDescription

users

array of object

no

Development users exposed by the embedded login form. Loopback only.

sessionTtlSeconds

integer

no

Session lifetime. Default 43200 (12h). Minimum: 1.

authProvider.dev.users
FieldTypeRequiredDescription

username

string

yes

Login name for the development user.

password

string

yes

Plaintext, dev only.

roles

array of string

no

Roles granted to this development user.

scopes

array of string

no

OAuth scopes granted to this development user.

claims

map of string to any

no

Additional claims returned for this development user (string values).

mcpToken

string

no

Bearer accepted by MCP routes. Default dev-<username>.

oidc

LEGACY platform IdP block. When authProvider is absent, it is translated into an http provider (auth-bridge) at load; existing configs keep working. New configurations should use authProvider.

FieldTypeRequiredDescription

loginUrl

string

no

Platform login page (e.g. "https://login.example.com/login"). Used by the server-side redirect of frontend routes and by the guard script (per-route authGuard.loginUrl takes precedence).

issuer

string

no

OIDC issuer (e.g. "https://auth.example.com/realms/example") — announced as the authorization_server of the MCP OAuth metadata.

tokenBroker

Internal per-user credential broker. Required when a route declares upstream.tokenBroker; a configured block also keeps the Services page (/connections: pause/resume/erase) available when no route delegates. The shared secret is read from the named environment variable at load (never from this file) and is never logged.

FieldTypeRequiredDescription

url

string

no

Broker base URL (default http://token-broker:4000, the Docker internal service name). Never expose it through an ingress: the broker is an internal service.

secretEnv

string

no

Environment variable holding the X-Broker-Secret shared secret (default TOKEN_BROKER_SHARED_SECRET).

links

object

no

Links owned by the token broker, displayed in the user badge. services is the services/connections page; it is rendered whenever the broker is configured (the gateway computes the final list at load).

browserHost

string

no

Host on which Gate One serves the token broker’s browser routes (/connections, /connect/, /oauth//callback), authenticating the user and injecting the identity. Empty = derived from links.services.url (its hostname). If both are absent, Gate One does not serve them; an ingress must never proxy those paths directly to the broker (it would answer 401). auth-bridge does not receive the shared secret.

Links owned by the token broker, displayed in the user badge. services is the services/connections page; it is rendered whenever the broker is configured (the gateway computes the final list at load).

FieldTypeRequiredDescription

services

object

no

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

FieldTypeRequiredDescription

label

string

no

Displayed text. Optional for the connector links (default Account/Services); required for userBadge.links. Maximum length: 64.

url

string

yes

http(s) absolute URL or local absolute path (/…​). Maximum length: 2048.

target

string (one of: _blank, _self)

no

Link target. Default _blank.

mcpDefaults

Optional defaults shared by every MCP access unit.

FieldTypeRequiredDescription

scope

string

no

Default MCP scope when a service/set declares none.

mcpSets

An MCP access unit grouping services under one endpoint; carries the audience (resource), scope and identity of the group.

FieldTypeRequiredDescription

host

string

yes

Public host serving the set.

endpoint

string

yes

Mount path of the set on that host ("/" allowed).

resource

string

no

OAuth protected-resource identifier (audience) of the set. Empty = the set endpoint URL.

scope

string

no

MCP scope required from clients of this set. Default: mcpDefaults.scope, else mcp-tools.

identity

object

no

Identity policy conditions, evaluated AFTER authentication on the verified token (failure = 403 forbidden). All conditions combine with AND.

enabled

boolean

no

Expose the set (default true). false = the whole set is skipped (no endpoint, no validation).

members

array of object

yes

—

mcpSets.identity

Identity policy conditions, evaluated AFTER authentication on the verified token (failure = 403 forbidden). All conditions combine with AND.

FieldTypeRequiredDescription

roles

array of string

no

Required Keycloak roles (realm roles + flattened client roles, see auth-bridge’s X-Roles). AND between them. Example of a fixed name: offline_access (realm role granted to every user via default-roles-<realm>).

scopes

array of string

no

Required granted scopes (token scope claim, see auth-bridge’s X-Scopes). AND between them. Example of a fixed name: profile (default client scope, always present).

claims

map of string to string or array of string

no

Token claims: key = claim name, value = one accepted string or a list of accepted strings (OR / anyOf on the same claim). "*" = the claim is present. All keys combine with AND. A token claim that is itself a list (groups, roles…) matches when one of its elements matches. Example: {"email": ["alice@example.com", "a.rolland@example.com"], "email_verified": "true"}.

user

string

no

Exact username required (preferred_username).

mcpSets.members

One service mounted under a set.

FieldTypeRequiredDescription

service

string

yes

mcp.name of the referenced service.

path

string

yes

Path relative to the set endpoint ("/" = the set endpoint itself).

tools

array of string

no

Optional glob allow-list restricting the service’s tools inside this set.

enabled

boolean

no

Expose this member (default true). false = not materialized, and its service is neither compiled (no OpenAPI fetch) nor provider-validated.

publisherFeed

Optional signed publisher feed: compatible Gate One versions, external components and plugins, with their revocation status. Loaded and verified at startup; the operator’s trust store stays authoritative. Absent = no feed is consulted.

FieldTypeRequiredDescription

file

string

yes

Feed path, relative to the config file. Minimum length: 1.

keys

map of string to string

yes

Feed signing keys: key id → base64 Ed25519 public key. The feed’s signature key id must be present. Provisioned out-of-band.

mode

string (one of: warn, enforce)

no

warn (default): report revocations and incompatibilities only. enforce: apply the feed’s revocations (digest and key) to the plugin trust store, so a revoked plugin refuses to load.

turnstile

Platform-wide Cloudflare Turnstile bot check for form pages. Presence enables it (explicit enabled=false disables it in templated configs). The widget is injected into matching HTML pages and every matching request is verified against the Siteverify API BEFORE the backend: a missing/invalid token is answered 403 (readable page for a navigation, JSON otherwise), a Siteverify outage 503 (fail closed). The block is global on purpose: one place to change, no per-host duplication. LIMITATION: it protects only traffic that passes through this Gate One instance; a second ingress, an internal caller or a direct port reaching the application bypasses it. Network isolation (only Gate One can reach the application) remains required. The token is never logged nor written to usage events.

FieldTypeRequiredDescription

enabled

boolean

no

Explicit switch for templated configurations. Absent = enabled when the block is present.

mode

string (one of: forms, gate, both)

no

Protection mode. forms: inject the widget into forms and verify the configured methods. gate: challenge the first request to a protected path, set a signed clearance cookie (clearanceTtlSeconds) and let the following requests through without a new Siteverify call; a form POST without clearance is refused (the submitted data cannot be replayed), an API/fetch call gets 403 turnstile_required. both: gate AND form verification. Default forms.

siteKey

string

yes

Public Turnstile site key (safe in the page). Cloudflare binds it to a domain list: every public hostname serving the form must be declared there. Minimum length: 1. Maximum length: 256.

secretEnv

string

yes

Name of the environment variable holding the Turnstile secret key. Required: the secret is only read from the environment, never from this file, and never sent to the browser. Missing/empty variable refuses startup. Minimum length: 1.

hosts

array of string

no

Hosts the block applies to: exact hostnames, or a leading . wildcard on a label boundary (.example.com). Empty = every route.

paths

array of string

no

Paths the block applies to, with the route.path grammar: /contact/* (prefix on a path boundary), an exact path, or /* (everything). Empty = every path. The widget is injected only on matching pages; verification only on matching requests.

methods

array of string (one of: POST, PUT, PATCH, DELETE)

no

Request methods verified. Default POST. GET/HEAD are never challenged (the page must load before the widget can run).

forms

array of string

no

CSS selectors of the forms that receive the widget. Empty = every POST form on the page. A form marked data-gtw-turnstile="off" is always skipped. An invalid selector is ignored (no injection, no error).

action

string

no

Optional action sent with the widget and required in the Siteverify answer (1-32 letters, digits, _ or -). Binds the token to this form: a token obtained for another action is refused. Pattern: ^[A-Za-z0-9_-]{1,32}$.

hostname

string

no

Optional expected hostname in the Siteverify answer. Empty = no check (the site key’s domain list already constrains it). Useful when one site key serves several hosts and each must be pinned.

tokenHeader

string

no

Header accepted in addition to the cf-turnstile-response form field, for fetch/SPA clients. Default X-Turnstile-Response. The injected script attaches the token to same-origin mutating fetches automatically. Pattern: ^[A-Za-z0-9-]+$.

exemptPaths

array of string

no

Paths never gated in gate/both mode (machine endpoints, webhooks, MCP). The route.path grammar applies (/api/webhooks/, exact path). Built-in exemptions always apply: /healthz, /readyz, /__gate-one/, /favicon.ico, /robots.txt. OPTIONS requests are never gated.

clearanceTtlSeconds

integer

no

Lifetime of the gate clearance cookie. Default 1800 (30 minutes). A visitor who solved the challenge is not challenged again before it expires; rotating the signing key invalidates every clearance. Minimum: 60. Maximum: 604800.

clearanceSecretEnv

string

no

Environment variable holding the HMAC key that signs the clearance cookie. Optional: when empty, the key is derived from the Turnstile secret with domain separation (rotating that secret then invalidates every clearance). A dedicated key lets you rotate clearances without touching the widget secret. Must be identical on every replica.

challenge

object

no

Challenge page tuning (gate/both).

preclearance

boolean

no

gate/both: the injected script solves the challenge in the background when a page is served without clearance, so the first user action needs a single backend call. Default true. The 403 turnstile_required + replay remains the fallback.

turnstile.challenge

Challenge page tuning (gate/both).

FieldTypeRequiredDescription

appearance

string (one of: always, execute, interaction-only)

no

Turnstile render appearance of the challenge widget. Default interaction-only in gate/both: the widget stays hidden unless Cloudflare requires an interaction. always shows it; execute renders without running the challenge until executed.

usage

Out-of-process usage-event bus (WAL + extension plugins). Presence enables it. Events are produced only for the subscribed types; a full durable share is answered with a targeted 503 (health/readyz/auth-probe exempt).

FieldTypeRequiredDescription

dir

string

no

WAL directory (one subdirectory per event type). Relative paths are resolved from the config file. Default ./gate-one-usage.

nodeId

string

no

Instance identifier written in every event. Default: hostname.

subjectSecretEnv

string

no

Environment variable holding the HMAC key deriving the opaque subjectId. Required.

pluginSecretEnv

string

no

Environment variable holding the HMAC key signing host→plugin assertions. Required.

budgetBytes

integer

no

Node WAL budget in bytes, split across the type shares. Default 52428800 (50 MiB). Minimum: 1.

trustStore

string

no

Path of the signed plugin trust store (JSON: keys, allowedNames, allowedDependencies, revokedDigests, allowAuth, coreVersion). Required when a plugin declares a signed manifest. Relative paths are resolved from the config file.

requireSigned

boolean

no

Refuse any plugin without a signed manifest. Default false: an unsigned plugin is allowed for development and logged as a warning.

pluginsDir

string

no

Directory scanned for plugin descriptors: every *.json file is a plugin entry (same shape as usage.plugins[]). Manifest and artifact paths are relative to the descriptor. Merged with usage.plugins; duplicate names are refused. Rescanned on SIGHUP.

types

map of object

no

Durable event type → WAL policy. A plugin can only subscribe to a configured type.

capture

array of object

no

Opt-in capture of allow-listed query parameters and JSON body fields into matching events (default: billing). Scoped by host/route/method; values are capped and sensitive names skipped.

profile

string (one of: online, airgap)

no

Spending authorisation profile: online (a lease refilled from the managed ledger) or airgap (a signed allowance file, valid for a period). Default online. The airgap profile bypasses the managed settlement split and therefore requires the "billing-byo" licence feature; the gateway refuses to start without it.

metrics

map of object

no

Billable metrics, keyed by metric code. A route references one with routes[].metric. Empty = the metering path is disabled.

tariffs

map of object

no

Versioned rate tables, keyed by version. Each entry names its metric and effective date.

outbox

object

no

Bounded local delivery buffer for billing records. It survives a transport outage for a few hours, then refuses new billable requests; it is not a financial archive.

sink

object

no

Delivery of billing records: signed push to the managed endpoint, or a signed export bundle when the endpoint is unreachable (airgap).

allowanceFile

string

no

Signed allowance file (airgap profile). Resolved relative to the configuration file.

allowanceEnv

string

no

Environment variable holding the signed allowance JSON. Mutually exclusive with allowanceFile.

allowancePublicKey

string

no

Base64 Ed25519 public key verifying the allowance (airgap profile).

plugins

array of object

no

Out-of-process event consumers (HTTP/JSON), each with its own subscription or signed manifest.

usage.types

FieldTypeRequiredDescription

shareBytes

integer

yes

Maximum size of this type’s WAL on this node. Minimum: 1.

fsync

string (one of: none, window_ms, always)

no

Durability: none, group commit every windowMs, or fsync before the response (zero_loss). always is refused for request/error (the proxy emitter cannot fsync before a streaming response).

windowMs

integer

no

Group-commit interval for fsync=window_ms. Default 100. Minimum: 1.

segmentBytes

integer

no

Append-file rotation size. 0 = share/4 capped at 8 MiB. Minimum: 0.

usage.capture

FieldTypeRequiredDescription

hosts

array of string

no

Request hosts or glob patterns to capture. Empty = all hosts.

routes

array of string

no

Request path patterns to capture. Empty = all paths.

methods

array of string

no

HTTP methods to capture. Empty = all methods.

types

array of string

no

Event types to attach attrs to. Default: [billing].

query

array of string

no

Allow-listed query parameter names whose values are copied as q.<name>. Sensitive names are skipped.

body

object

no

Optional JSON-body extraction.

maxAttrsBytes

integer

no

Total attrs budget in bytes. Default 1024, clamped to 2048. Minimum: 0. Maximum: 2048.

usage.capture.body

Optional JSON-body extraction.

FieldTypeRequiredDescription

format

string (one of: json)

no

Body parser; only json is supported.

paths

array of string

no

Dotted JSON paths ($.a.b or a.b) whose scalar values are copied as b.<path>.

maxBytes

integer

no

Skip body capture when the raw body is larger. 0 = no limit. Minimum: 0.

usage.metrics

FieldTypeRequiredDescription

unit

string

yes

Free-form unit label (request, token, byte, second, …​). Minimum length: 1.

scale

integer (one of: 0, 3, 6)

yes

Decimal scale of the quantity: 0 (units), 3 (milli) or 6 (micro).

maxDimensions

integer

no

Maximum number of dimensions accepted per measure; must not exceed the declared dimensions. Minimum: 0.

dimensions

map of object

no

Declared dimension names and their types; an undeclared dimension is rejected.

measurement

string (one of: event, periodic)

no

event (discrete fact) or periodic (continuous quantity emitted by heartbeat, e.g. time, bytes, storage). Default event.

heartbeatSeconds

integer

no

Heartbeat period of a periodic metric. Default 60. Minimum: 10. Maximum: 3600.

pricing

string (one of: declarative, plugin)

no

declarative (core rate table) or plugin (signed pricing plugin). Default declarative.

fallback

string

no

Declarative tariff version used when pricing=plugin is unavailable. Required with pricing=plugin.

usage.metrics.dimensions
FieldTypeRequiredDescription

type

string (one of: string, enum)

yes

—

maxLength

integer

no

Minimum: 0.

values

array of string

no

—

usage.tariffs

FieldTypeRequiredDescription

metric

string

yes

Minimum length: 1.

version

string

yes

Minimum length: 1.

currency

string

yes

Minimum length: 3. Maximum length: 3.

effectiveFrom

string

yes

—

charges

array of object

yes

—

usage.tariffs.charges
FieldTypeRequiredDescription

model

string (one of: unit, tiered, volume, package, percentage_of)

yes

—

amountMicro

integer

no

Minimum: 0.

filter

map of string to array of string

no

—

tiers

array of object

no

—

packageSize

integer

no

Minimum: 0.

freeUnits

integer

no

Minimum: 0.

rateBasisPoints

integer

no

Minimum: 0.

fixedAmountMicro

integer

no

Minimum: 0.

usage.tariffs.charges.tiers
FieldTypeRequiredDescription

fromValue

integer

yes

Minimum: 0.

toValue

integer

yes

—

perUnitMicro

integer

yes

Minimum: 0.

flatAmountMicro

integer

no

Minimum: 0.

usage.outbox

Bounded local delivery buffer for billing records. It survives a transport outage for a few hours, then refuses new billable requests; it is not a financial archive.

FieldTypeRequiredDescription

dir

string

no

Outbox directory. Default ./usage-outbox, resolved from the configuration file.

maxAge

string

no

Maximum age of the oldest pending record (duration, e.g. 6h). Default 6h.

maxBytes

integer

no

Maximum pending bytes. Default 512 MiB. Minimum: 0.

maxRecords

integer

no

Maximum pending records. Default 50000. Minimum: 0.

onFull

string (one of: block, drop)

no

block (default): new billable requests answer 503 when the buffer is full. drop: discard (counted, logged) — never silent but loses revenue.

usage.sink

Delivery of billing records: signed push to the managed endpoint, or a signed export bundle when the endpoint is unreachable (airgap).

FieldTypeRequiredDescription

url

string

no

Managed ingest endpoint (http/https). Empty = export only.

secretEnv

string

no

Environment variable holding the Bearer secret sent with every push.

signingKeyEnv

string

no

Environment variable holding the base64url 32-byte Ed25519 seed that signs each batch and each export bundle. Required when metrics are configured.

batchRecords

integer

no

Maximum records per batch. Default 500. Minimum: 1.

batchBytes

integer

no

Maximum batch size in bytes. Default 1 MiB. Minimum: 1.

batchWaitMs

integer

no

Maximum wait before flushing a partial batch. Default 1000 ms. Minimum: 1.

exportDir

string

no

Directory receiving signed export bundles (bundle .jsonl, .sig and manifest). Required for the airgap profile.

exportAfterSeconds

integer

no

How long the push may fail before an export bundle is written. Default 300 s. Minimum: 0.

receiptPublicKey

string

no

Base64 Ed25519 public key of the ingest service, verifies the signed receipts that acknowledge an air-gap bundle (gate-one billing ack). Required for the airgap profile.

usage.plugins

FieldTypeRequiredDescription

name

string

yes

Unique plugin name (assertion audience). Minimum length: 1.

url

string

yes

Absolute http(s) base URL. /health and /events are appended.

manifest

string

no

Path of the signed plugin manifest. When set, the manifest is authoritative for the event subscription and the licence feature; artifact is required and usage.trustStore must be configured.

artifact

string

no

Path of the signed plugin artefact (binary/container). Required with manifest; its digest is verified against the manifest.

types

array of string (one of: request, auth, error, billing, audit)

no

Subscribed event types when no signed manifest is used; each must be configured in usage.types.

filters

object

no

Narrow the subscription by host, route, tenant, method, status class or sample rate. Route patterns match the path only, so use hosts to avoid matching the same path on every host.

maxLagBytes

integer

no

Allowed subscriber lag. Beyond it, onMaxLag applies. Minimum: 0.

onMaxLag

string (one of: block, drop)

no

block (default): 503 until catch-up; drop: move the cursor to the lag limit and let eviction proceed.

licenseFeature

string

no

Required signed-licence feature; the plugin is refused without it.

usage.plugins.filters

Narrow the subscription by host, route, tenant, method, status class or sample rate. Route patterns match the path only, so use hosts to avoid matching the same path on every host.

FieldTypeRequiredDescription

hosts

array of string

no

Request hosts or glob patterns to include (e.g. app.example.com, *.example.com). Empty = all hosts.

routes

array of string

no

Route patterns to include, for example /api/*. Empty = all routes.

tenants

array of string

no

Tenant or auth-provider ids to include. Empty = all tenants.

methods

array of string

no

HTTP methods to include. Empty = all methods.

statusClasses

array of string

no

HTTP status classes to include (1xx..5xx). Empty = all classes.

sampleRate

number

no

Sampling rate for matching events, 0..1. Default 1.0. Minimum: 0. Maximum: 1.

routes

FieldTypeRequiredDescription

host

string

no

Single literal application host (or wildcard pattern when wildcard is set). Mutually exclusive with hosts.

hosts

array of string

no

Literal application hosts sharing this route body. Expanded at load into one concrete route per host; auth.provider is inferred from the ssoGroups provider whose cookieDomain covers each host. Mutually exclusive with host and wildcard.

wildcard

map of string to array of string

no

Named host/backend expansion — one route, N sites. Keys = capture names ({name} in host and backend), values = the ALLOW-LISTS of DNS labels they may take (a-z0-9, hyphens, no dots). The route expands at load into one concrete route per label; a label outside the lists has no route (fail-closed: the backend is never attacker-derived). With rewriteBody enabled, the expansion also compiles the cross-route map: an origin of another variant (e.g. es.wikipedia.org in the English edition) is rewritten to that sibling’s public host; ambiguous alias mappings are left alone, and route-local except prefixes do not apply to siblings.

path

string

yes

Prefix; "/*" = wildcard (whole host).

stripPrefix

string

no

Exact PUBLIC path prefix removed from the request before it reaches the backend. Path only (no query, fragment or wildcard), matched on segment boundaries and normalized at load (trailing slash removed; root "/" forbidden). Example: path "/api/*", stripPrefix "/api", backend "http://api:8080/v1" turns /api/foo into /v1/foo. The backend also receives X-Forwarded-Prefix when the ingress did not set one. Response paths are rewritten symmetrically: Location, Content-Location, Refresh/Link headers, Set-Cookie Path, and HTML URL attributes (href/src/action/srcset/poster, style url(), meta refresh) are brought back under /api; relative references and other origins are left untouched. Script strings and CSS @import are out of scope. Maximum length: 256.

backend

string

yes

Backend base URL (http/https) that serves the route.

inject

string

no

Path of the route’s custom injection bundle (JSON), relative to the config file. The bundle declares HTML/JS/CSS insertions by named target ("head" = after <head>/meta charset, "body-end" = before </body>) with per-page targeting (when = path prefix) and data-* attribute patches applied client-side by the guard. Inline content or file: (relative to the bundle, read at load — a faulty bundle refuses startup). Its formal grammar is inject-bundle.schema.json. See the doc’s § upstream.inject.

upstream

object

no

Join to a backend outside the internal network (proxified external site — e.g. Cloudflare Pages/Workers). Absent = unchanged historical behavior: public Host preserved, client headers passed as-is.

frontend

boolean

no

Page route: auth failure → 302 to the login (API routes get a JSON 401); eligible for the guard script and the pwa policy.

usage

boolean

no

Emit usage events for this route. Default true; set false to serve the route without producing ANY usage event — no WAL reservation and no admission 503 on this route. Use it for control or monitoring routes (a status page, an events viewer) that must not pollute, or depend on, the event bus.

metric

string

no

Billable metric produced by this route (a key of usage.metrics). The amount is rated and reserved BEFORE the backend call; an unaffordable request answers 402 instead of running. Empty = the route does not use the metering path.

autoHead

boolean

no

HEAD requests are served as GET, body removed.

auth

object

no

Authentication policy. Empty ({}) = authentication ALWAYS required. auth.when holds TECHNICAL conditions (method/query/header/body) deciding when authentication is required: one object (AND of its conditions) or an array (OR of ANDs — required if ANY element matches); absent/empty = always required. auth.identity holds IDENTITY conditions (roles/scopes/claims/user), checked after successful authentication (403 otherwise) and only on requests that were actually authenticated.

rateLimit

object

no

Rate limiting: fixed window per identity (authenticated user, else real IP via X-Forwarded-For), per route. Full window = 429 + Retry-After (seconds). Periods: second|minute|hour|day.

quota

object

no

Cumulative quota: same mechanics as rateLimit with a longer window in practice (day). Full window = 429 + Retry-After.

openapi

object

no

OpenAPI document used to derive REST operations and MCP tools for this route.

mcp

object

no

Expose a route as an MCP service: protected-resource identifier, scope, tools and metadata.

pwa

object

no

PWA shell, manifest, icons and service-worker cache policy for a frontend route.

csp

object

no

Content-Security-Policy emitted (or amended) by the gateway — the only component able to generate a per-response nonce and write it into the HTML it already rewrites (guard, PWA, custom injections). By default the origin’s policy is NEVER removed or weakened: the gateway’s policy is added as a SECOND header (browsers enforce all of them, so the effective rule is the strictest) and the hashes of the gateway’s own injected script and guard stylesheet are APPENDED to the origin’s script-src / style-src (script-src-elem > script-src > default-src, style-src-elem > style-src > default-src). Set own=true to remove the origin policy explicitly and let the gateway own it (opt-in, logged, requires mode nonce/hash and reportOnly=false). Applied to HTML responses only.

userBadge

object

no

User badge injected by the guard script once authenticated: collapsed on the top edge (one line = username), expanded on hover/keyboard focus — chosen claims + logout button (OIDC end_session + cookie clearing). Absent = no badge.

uiGate

array of object

no

UI gating by config (CSS selectors) — embedded in the injected guard script. Only for non-modifiable fronts; an element’s data-gtw-* markup wins over the rules.

authGuard

object

no

401 guard script (login without reloading: tab + replay + banner + declarative gating). Active by default on frontend routes; no-op elsewhere. The guard intercepts the 401 (login) and, when authGuard.forbidden.banner is set, the gateway’s OWN identity 403; a 403 coming from the backend is never concerned.

routes.upstream

Join to a backend outside the internal network (proxified external site — e.g. Cloudflare Pages/Workers). Absent = unchanged historical behavior: public Host preserved, client headers passed as-is.

FieldTypeRequiredDescription

host

boolean

no

true = the Host header sent to the backend is that of the backend’s origin (required for an external site that routes by Host, e.g. Cloudflare — otherwise the request arrives with the public host and the origin does not recognize it). Absent/false = public Host passed as-is (internal backends). TLS/SNI always targets the backend’s origin.

hostOverride

string

no

Explicit value of the Host header sent to the backend, when it must differ from both the public host and the backend’s origin (rare). Precedence over host.

headers

map of string to string

no

Static request headers set toward the backend (Set: overwrite any value the client may have sent). Uses: get through an origin basic auth ("Authorization": "Basic ${MYSITE_BASIC_AUTH}") or the Cloudflare Access service tokens (CF-Access-Client-Id / CF-Access-Client-Secret). Values with ${VAR} interpolation from the container environment — missing variable = fatal error at load (never a silent empty credential); empty value = header removed from the request. Values are never logged.

rewriteRedirects

boolean

no

true = ABSOLUTE Locations targeting the backend’s origin are rewritten to the public host (https scheme, path/query preserved) — otherwise the browser would leave the proxy at the target site’s first absolute redirect (e.g. 301 of the trailing slash). Relative Locations and those targeting other origins (e.g. a Cloudflare Access team domain) pass intact. Default: false.

responseHeaders

object

no

Adjustment of the backend’s RESPONSE headers before sending to the browser — typically remove an origin Content-Security-Policy that would block the injected guard script, or set a deployment header. Prefer csp.merge (hashes) or csp.own (explicit removal, logged) for CSP: removing the origin’s CSP here is a blunt, unlogged operation and loses the origin’s protections.

rewriteBody

object

no

Rewriting of the HTML body (200 text/html only, STREAMING — O(1) memory): the origin’s absolute URLs cited in the pages ("https://origin/…", "http://origin/…", "//origin/…") are rewritten to the route’s public host, so the browser stays in the proxy. Any explicit port is part of the rewritten prefix and is lost (target served on the standard port). The path, query and fragment follow as-is. Routes expanded from one wildcard automatically rewrite sibling editions toward their public hosts. targets and followRoutes add explicit cross-route mappings (bare hosts only: the request’s scheme and port are inherited). At runtime (fetch/XHR/sendBeacon launched by the site), the client SW served on /__gate-one_pwa/sw.js applies the same policy — it is auto-registered by the guard script, served pre-authentication. One same policy (origins × exceptions × targets) is implemented three times (Go engine, client SW, script patch) — parity tested. Default: absent (no body rewriting).

rewriteCookies

object

no

Translation of the Set-Cookie set by the origin: a Domain attribute targeting its own namespace ("Domain=original…domain.dev") would be rejected by the browser from the public host — the cookie would be silently lost. Active: a Domain targeting the origin host (equal or parent) is rewritten to the public host in "rewrite" mode (the sharing scope — host + subdomains — is transposed as-is), or the attribute is removed in "strip" mode (host-only on the public host). Other cookies pass intact (surgery of the Domain attribute only: Path/Secure/HttpOnly/SameSite/Expires come out byte-exact). Auth protection: an origin Set-Cookie bearing a name reserved for the gateway session (access_token, refresh_token, id_token, session) is removed with a warning — set on the same public host, it would overwrite the session cookies. Default: absent (no cookie rewriting).

stripCookies

string (one of: all, none)

no

REQUEST-side cookie filtering toward the backend. The gateway session cookies (access_token, refresh_token, id_token, session) carry Domain=<parent domain> (COOKIE_DOMAIN, e.g. .cladone.com): the browser presents them to EVERY public host, so without this filter a proxified third-party site receives the user’s tokens on every request. "all" = every cookie removed except the names in keep (the proxified app’s own cookies); "none" = historical behavior, everything forwarded. Default: "all" as soon as an upstream policy is declared (fail-closed).

keep

array of string

no

Cookie names preserved when stripCookies is "all" (typically the proxified application’s own session). Names reserved for the gateway session (access_token, refresh_token, id_token, session) are refused at load: they must never reach an external origin.

browserCache

object

no

HTTP-cache orientation to the BROWSER for assets the origin serves with no Cache-Control (and no Last-Modified either: without these two headers the browser has no heuristic cache — every re-view re-fetches or revalidates each asset, one full round-trip each). Only 200 responses of content-type image/*, Cache-Control absent only: the origin’s own declarations pass untouched. Sent as "private, max-age=…" — private: resources under authentication stay out of any shared cache; the origin’s ETag remains for revalidation after expiry. Default: absent (headers preserved as-is).

tokenBroker

object

no

Per-user credentials supplied by the token broker for the authenticated caller. Requires the top-level tokenBroker block and route.auth (the broker needs an authenticated user id). A missing connection produces a 401/403/409 with a connect_url (403 when the user paused the delegation, 409 when reauthorization is required), or a redirect for frontend routes, never a request without credentials. The same policy applies to REST tool calls produced by an MCP route.

routes.upstream.responseHeaders

Adjustment of the backend’s RESPONSE headers before sending to the browser — typically remove an origin Content-Security-Policy that would block the injected guard script, or set a deployment header. Prefer csp.merge (hashes) or csp.own (explicit removal, logged) for CSP: removing the origin’s CSP here is a blunt, unlogged operation and loses the origin’s protections.

FieldTypeRequiredDescription

set

map of string to string

no

Headers added/overwritten on the response; values with ${VAR} interpolation (container environment); empty value = header removed (never transmitted empty).

remove

array of string

no

Header names removed from the response (e.g. Content-Security-Policy).

routes.upstream.rewriteBody

Rewriting of the HTML body (200 text/html only, STREAMING — O(1) memory): the origin’s absolute URLs cited in the pages ("https://origin/…", "http://origin/…", "//origin/…") are rewritten to the route’s public host, so the browser stays in the proxy. Any explicit port is part of the rewritten prefix and is lost (target served on the standard port). The path, query and fragment follow as-is. Routes expanded from one wildcard automatically rewrite sibling editions toward their public hosts. targets and followRoutes add explicit cross-route mappings (bare hosts only: the request’s scheme and port are inherited). At runtime (fetch/XHR/sendBeacon launched by the site), the client SW served on /__gate-one_pwa/sw.js applies the same policy — it is auto-registered by the guard script, served pre-authentication. One same policy (origins × exceptions × targets) is implemented three times (Go engine, client SW, script patch) — parity tested. Default: absent (no body rewriting).

FieldTypeRequiredDescription

enabled

boolean

no

true = engine active (the block is present to stay declarative; absent = disabled).

except

array of string or object

no

Exceptions by path prefix: string entry ("/reading/…") = these URLs stay at the ORIGIN (out-of-proxy reading link); object entry {path, to} = rewritten to an ALTERNATIVE public base. Longest prefix wins. '/…' prefix mandatory, '?'/'#' forbidden, 256 bytes max; 'to' = absolute http(s) URL, trailing slashes trimmed.

targets

map of string to string

no

Explicit cross-route mappings: backend origin host → public route host. Bare hosts only (no scheme, port, path or wildcard); the key must be an origin of a declared route and the value the public host of a declared route. The request’s scheme and port are inherited. Automatic wildcard siblings are added before these entries; a conflicting mapping is refused at load.

followRoutes

array of string

no

Selectors over declared routes BY PUBLIC HOST (exact host or '.suffix'; a bare '' is refused). Each selected route’s backend origins are rewritten to its public host. Resolved after wildcard expansion; an unmatched selector, a self-only match or an ambiguous origin is refused at load.

routes.upstream.rewriteCookies

Translation of the Set-Cookie set by the origin: a Domain attribute targeting its own namespace ("Domain=original…domain.dev") would be rejected by the browser from the public host — the cookie would be silently lost. Active: a Domain targeting the origin host (equal or parent) is rewritten to the public host in "rewrite" mode (the sharing scope — host + subdomains — is transposed as-is), or the attribute is removed in "strip" mode (host-only on the public host). Other cookies pass intact (surgery of the Domain attribute only: Path/Secure/HttpOnly/SameSite/Expires come out byte-exact). Auth protection: an origin Set-Cookie bearing a name reserved for the gateway session (access_token, refresh_token, id_token, session) is removed with a warning — set on the same public host, it would overwrite the session cookies. Default: absent (no cookie rewriting).

FieldTypeRequiredDescription

domain

string (one of: rewrite, strip)

yes

"rewrite" (recommended) = Domain transposed to the public host; "strip" = Domain attribute removed.

routes.upstream.browserCache

HTTP-cache orientation to the BROWSER for assets the origin serves with no Cache-Control (and no Last-Modified either: without these two headers the browser has no heuristic cache — every re-view re-fetches or revalidates each asset, one full round-trip each). Only 200 responses of content-type image/*, Cache-Control absent only: the origin’s own declarations pass untouched. Sent as "private, max-age=…" — private: resources under authentication stay out of any shared cache; the origin’s ETag remains for revalidation after expiry. Default: absent (headers preserved as-is).

FieldTypeRequiredDescription

imagesMaxAgeSeconds

integer

yes

Freshness duration (seconds) granted to the images (content-type image/*). E.g. 604800 = a week; 31536000 = a year (only if these files are immutable: the image names followed here are the capture timestamps, not content hashes — prefer a moderate duration if the app can re-edite a photo). Minimum: 1.

routes.upstream.tokenBroker

Per-user credentials supplied by the token broker for the authenticated caller. Requires the top-level tokenBroker block and route.auth (the broker needs an authenticated user id). A missing connection produces a 401/403/409 with a connect_url (403 when the user paused the delegation, 409 when reauthorization is required), or a redirect for frontend routes, never a request without credentials. The same policy applies to REST tool calls produced by an MCP route.

FieldTypeRequiredDescription

provider

string

yes

Broker provider name (e.g. github). Validated against GET /providers at startup; Gate One never stores the provider’s client secret. Pattern: ^[a-z0-9][a-z0-9_-]{0,63}$.

header

string

no

Backend request header receiving the access token. Default Authorization. Host, Cookie, X-User-, X-Forwarded- and X-Gate-One-* names are refused.

prefix

string

no

Value prepended to the access token. Default "Bearer ". An explicit empty string sends the raw token.

routes.auth

Authentication policy. Empty ({}) = authentication ALWAYS required. auth.when holds TECHNICAL conditions (method/query/header/body) deciding when authentication is required: one object (AND of its conditions) or an array (OR of ANDs — required if ANY element matches); absent/empty = always required. auth.identity holds IDENTITY conditions (roles/scopes/claims/user), checked after successful authentication (403 otherwise) and only on requests that were actually authenticated.

FieldTypeRequiredDescription

provider

string

no

ID of the authProviders entry used to verify this route. Empty = defaultAuthProvider. The binding is static and compiled at load; a request cannot choose another provider.

when

object or array of object

no

Technical conditions deciding WHEN authentication is required. Empty = always. One object = AND; an array = OR of ANDs.

identity

object

no

Identity policy conditions, evaluated AFTER authentication on the verified token (failure = 403 forbidden). All conditions combine with AND.

routes.auth.identity

Identity policy conditions, evaluated AFTER authentication on the verified token (failure = 403 forbidden). All conditions combine with AND.

FieldTypeRequiredDescription

roles

array of string

no

Required Keycloak roles (realm roles + flattened client roles, see auth-bridge’s X-Roles). AND between them. Example of a fixed name: offline_access (realm role granted to every user via default-roles-<realm>).

scopes

array of string

no

Required granted scopes (token scope claim, see auth-bridge’s X-Scopes). AND between them. Example of a fixed name: profile (default client scope, always present).

claims

map of string to string or array of string

no

Token claims: key = claim name, value = one accepted string or a list of accepted strings (OR / anyOf on the same claim). "*" = the claim is present. All keys combine with AND. A token claim that is itself a list (groups, roles…) matches when one of its elements matches. Example: {"email": ["alice@example.com", "a.rolland@example.com"], "email_verified": "true"}.

user

string

no

Exact username required (preferred_username).

routes.rateLimit

Rate limiting: fixed window per identity (authenticated user, else real IP via X-Forwarded-For), per route. Full window = 429 + Retry-After (seconds). Periods: second|minute|hour|day.

FieldTypeRequiredDescription

requests

integer

yes

Maximum number of requests in the period.

period

string

yes

Rate-limit period: second, minute, hour or day.

when

object

no

Rate-limit / quota triggering conditions (rateLimit.when, quota.when). Two families: technical (method/query/header/body — evaluated BEFORE authentication) and identity (roles/scopes/claims/user — evaluated AFTER authentication on the verified token). All conditions combine with AND. NB: the authentication policy (auth) does NOT use this type: auth.when takes technical conditions only, auth.identity takes identity conditions only.

routes.rateLimit.when

Rate-limit / quota triggering conditions (rateLimit.when, quota.when). Two families: technical (method/query/header/body — evaluated BEFORE authentication) and identity (roles/scopes/claims/user — evaluated AFTER authentication on the verified token). All conditions combine with AND. NB: the authentication policy (auth) does NOT use this type: auth.when takes technical conditions only, auth.identity takes identity conditions only.

FieldTypeRequiredDescription

method

string

no

Exact HTTP method (e.g. "POST").

query

map of string to string

no

Exact equality of URL parameters.

header

map of string to string

no

Exact equality of headers. Caution: a header is forgeable by the client — never put an authorization there (use roles instead).

body

map of string to any

no

Exact equality of fields of the parsed JSON body (flattened one level).

roles

array of string

no

Required Keycloak roles (realm roles + flattened client roles, see auth-bridge’s X-Roles). AND between them. Example of a fixed name: offline_access (realm role granted to every user via default-roles-<realm>).

scopes

array of string

no

Required granted scopes (token scope claim, see auth-bridge’s X-Scopes). AND between them. Example of a fixed name: profile (default client scope, always present).

claims

map of string to string or array of string

no

Token claims: key = claim name, value = one accepted string or a list of accepted strings (OR / anyOf on the same claim). "*" = the claim is present. All keys combine with AND. A token claim that is itself a list (groups, roles…) matches when one of its elements matches. Example: {"email": ["alice@example.com", "a.rolland@example.com"], "email_verified": "true"}.

user

string

no

Exact username required (preferred_username).

routes.quota

Cumulative quota: same mechanics as rateLimit with a longer window in practice (day). Full window = 429 + Retry-After.

FieldTypeRequiredDescription

limit

integer

yes

Maximum billable units in the period.

period

string

yes

Quota period: second, minute, hour or day.

when

object

no

Rate-limit / quota triggering conditions (rateLimit.when, quota.when). Two families: technical (method/query/header/body — evaluated BEFORE authentication) and identity (roles/scopes/claims/user — evaluated AFTER authentication on the verified token). All conditions combine with AND. NB: the authentication policy (auth) does NOT use this type: auth.when takes technical conditions only, auth.identity takes identity conditions only.

routes.quota.when

Rate-limit / quota triggering conditions (rateLimit.when, quota.when). Two families: technical (method/query/header/body — evaluated BEFORE authentication) and identity (roles/scopes/claims/user — evaluated AFTER authentication on the verified token). All conditions combine with AND. NB: the authentication policy (auth) does NOT use this type: auth.when takes technical conditions only, auth.identity takes identity conditions only.

FieldTypeRequiredDescription

method

string

no

Exact HTTP method (e.g. "POST").

query

map of string to string

no

Exact equality of URL parameters.

header

map of string to string

no

Exact equality of headers. Caution: a header is forgeable by the client — never put an authorization there (use roles instead).

body

map of string to any

no

Exact equality of fields of the parsed JSON body (flattened one level).

roles

array of string

no

Required Keycloak roles (realm roles + flattened client roles, see auth-bridge’s X-Roles). AND between them. Example of a fixed name: offline_access (realm role granted to every user via default-roles-<realm>).

scopes

array of string

no

Required granted scopes (token scope claim, see auth-bridge’s X-Scopes). AND between them. Example of a fixed name: profile (default client scope, always present).

claims

map of string to string or array of string

no

Token claims: key = claim name, value = one accepted string or a list of accepted strings (OR / anyOf on the same claim). "*" = the claim is present. All keys combine with AND. A token claim that is itself a list (groups, roles…) matches when one of its elements matches. Example: {"email": ["alice@example.com", "a.rolland@example.com"], "email_verified": "true"}.

user

string

no

Exact username required (preferred_username).

routes.openapi

OpenAPI document used to derive REST operations and MCP tools for this route.

FieldTypeRequiredDescription

url

string

yes

OpenAPI 3.x JSON document of the host (absolute URL, or path relative to the route backend). Also the document used to resolve the mcp policy’s operations.

public

boolean

no

false = the openapi.json is only served authenticated.

routes.mcp

Expose a route as an MCP service: protected-resource identifier, scope, tools and metadata.

FieldTypeRequiredDescription

enabled

boolean

yes

Enable the MCP service on this route.

path

string

no

Default: /mcp.

name

string

no

Service name, referenced by mcpSets members. Empty = the route cannot be a set member.

standalone

boolean

no

Expose this service on its own endpoint (default true). false = served only through the mcpSets referencing it (the set carries the access rights).

resource

string

no

OAuth protected-resource identifier (audience) of the standalone service. Empty = the endpoint URL. Ignored when the service is reached through a set.

scope

string

no

Keycloak client scope required from MCP clients (bearer scope claim), announced in /.well-known/oauth-protected-resource and checked by auth-bridge (internal X-MCP-Scope header). The realm must grant it. Default: mcp-tools.

operations

array of object

no

REST operations exposed as MCP tools. Empty = no tool (the MCP endpoint still serves metadata and initialization). Mutually exclusive with upstream.

upstream

object

no

Proxy an external MCP server over Streamable HTTP instead of deriving local REST tools. The upstream receives the per-user token of upstream.tokenBroker (or static upstream.headers), never the Gate One bearer. Tool filtering is enforced on both tools/list and tools/call.

routes.mcp.operations
FieldTypeRequiredDescription

name

string

yes

Tool name exposed to MCP clients (unique per route).

operationId

string

yes

operationId resolved in the host’s OpenAPI document; the tool method, path, parameters and body schema are derived from it. An unresolved operationId refuses the configuration load.

routes.mcp.upstream

Proxy an external MCP server over Streamable HTTP instead of deriving local REST tools. The upstream receives the per-user token of upstream.tokenBroker (or static upstream.headers), never the Gate One bearer. Tool filtering is enforced on both tools/list and tools/call.

FieldTypeRequiredDescription

url

string

no

Full upstream Streamable HTTP endpoint. Default: the route backend. The incoming /mcp path is not appended automatically.

tools

object

no

Glob patterns (path.Match syntax) over MCP tool names. Include non-empty = only matching tools are exposed; Exclude always wins. Hidden tools are also refused on tools/call.

routes.mcp.upstream.tools

Glob patterns (path.Match syntax) over MCP tool names. Include non-empty = only matching tools are exposed; Exclude always wins. Hidden tools are also refused on tools/call.

FieldTypeRequiredDescription

include

array of string

no

Allow-list patterns (e.g. "list_*", "get_*").

exclude

array of string

no

Deny-list patterns (e.g. "delete_*", "*_secret").

routes.pwa

PWA shell, manifest, icons and service-worker cache policy for a frontend route.

FieldTypeRequiredDescription

enabled

boolean

yes

Enable the PWA shell on this route.

name

string

no

Application name shown in the PWA manifest.

shortName

string

no

Short application name for the home screen.

themeColor

string

no

Theme color for the PWA manifest and browser UI.

icon

string

no

Legacy singular (still read).

icons

array of string

no

Same-host paths referenced by the manifest (Chrome: 192×192 and 512×512 PNG for full install).

apiCache

object

no

One service-worker API cache policy (path prefix, method, retention).

apiCaches

array of object

no

API cache policies for the service worker.

apiGETCache

object

no

Its presence activates the universal GET cache (everything nothing else covers).

networkOnly

object

no

Its presence activates the "never cached" route. When a PWA route shares the authentication scope, the identity-provider endpoints (discovery, authorize, token, JWKS, end-session) and the RP callback carrying a one-time code/state MUST be excluded, or the service worker can replay a stale identity or a one-time code.

assets

object

no

Strategy of same-origin pages/assets (documents, scripts, styles). Default: staleWhileRevalidate without timeout.

routes.pwa.apiCache

One service-worker API cache policy (path prefix, method, retention).

FieldTypeRequiredDescription

path

string

yes

Path prefix of the API responses cached by the service worker.

method

string (one of: POST, GET, none)

no

POST (default) = network-first offline replay of POST/SSE; GET = SWR cache of this exact path; none = full opt-out (never cached, excluded from the apiGETCache catch-all).

maxEntries

integer

no

Default 50.

maxAgeSeconds

integer

no

Default 86400 (24 h).

maxEntrySizeBytes

integer

no

Default 2 MiB — beyond, streamed but not cached.

version

string

no

Semantic invalidation of the endpoint’s cache (bump it when the backend result changes).

idleFinalizeMs

integer

no

Default 1500 — finalizes the cached response after this SSE silence.

networkTimeoutSeconds

integer

no

Default 3 — network delay before the cache fallback (headers only).

offlineFastPath

boolean

no

Default true: offline, serves the cache directly. false = the network failure (bounded by networkTimeoutSeconds) triggers the fallback — for endpoints where replaying a write would be wrong.

routes.pwa.apiCaches

One service-worker API cache policy (path prefix, method, retention).

FieldTypeRequiredDescription

path

string

yes

Path prefix of the API responses cached by the service worker.

method

string (one of: POST, GET, none)

no

POST (default) = network-first offline replay of POST/SSE; GET = SWR cache of this exact path; none = full opt-out (never cached, excluded from the apiGETCache catch-all).

maxEntries

integer

no

Default 50.

maxAgeSeconds

integer

no

Default 86400 (24 h).

maxEntrySizeBytes

integer

no

Default 2 MiB — beyond, streamed but not cached.

version

string

no

Semantic invalidation of the endpoint’s cache (bump it when the backend result changes).

idleFinalizeMs

integer

no

Default 1500 — finalizes the cached response after this SSE silence.

networkTimeoutSeconds

integer

no

Default 3 — network delay before the cache fallback (headers only).

offlineFastPath

boolean

no

Default true: offline, serves the cache directly. false = the network failure (bounded by networkTimeoutSeconds) triggers the fallback — for endpoints where replaying a write would be wrong.

routes.pwa.apiGETCache

Its presence activates the universal GET cache (everything nothing else covers).

FieldTypeRequiredDescription

excludes

array of string

no

Path prefixes never cached by the catch-all.

excludePatterns

array of string

no

JS regexes (full URL) never cached — invalid syntax rejected at load.

routes.pwa.networkOnly

Its presence activates the "never cached" route. When a PWA route shares the authentication scope, the identity-provider endpoints (discovery, authorize, token, JWKS, end-session) and the RP callback carrying a one-time code/state MUST be excluded, or the service worker can replay a stale identity or a one-time code.

FieldTypeRequiredDescription

excludePatterns

array of string

no

JS regexes (full URL) → straight to the network, no cache.

routes.pwa.assets

Strategy of same-origin pages/assets (documents, scripts, styles). Default: staleWhileRevalidate without timeout.

FieldTypeRequiredDescription

strategy

string

no

staleWhileRevalidate (default) | networkFirst | cacheFirst (case-free, normalized).

networkTimeoutSeconds

integer

no

For networkFirst (default 3).

routes.csp

Content-Security-Policy emitted (or amended) by the gateway — the only component able to generate a per-response nonce and write it into the HTML it already rewrites (guard, PWA, custom injections). By default the origin’s policy is NEVER removed or weakened: the gateway’s policy is added as a SECOND header (browsers enforce all of them, so the effective rule is the strictest) and the hashes of the gateway’s own injected script and guard stylesheet are APPENDED to the origin’s script-src / style-src (script-src-elem > script-src > default-src, style-src-elem > style-src > default-src). Set own=true to remove the origin policy explicitly and let the gateway own it (opt-in, logged, requires mode nonce/hash and reportOnly=false). Applied to HTML responses only.

FieldTypeRequiredDescription

mode

string (one of: off, merge, hash, nonce)

yes

"off" = nothing (the origin’s policy passes as-is); "merge" = keep the origin policy and only add the hashes of the gateway’s injected scripts (no policy of our own); "hash" = emit our policy with those hashes, no body rewrite; "nonce" = emit our policy with a per-response nonce and stamp the HTML in STREAMING mode (O(1) memory, upstream forced to gzip so the body is plain text).

reportOnly

boolean

no

Emit Content-Security-Policy-Report-Only instead of the enforcing header (progressive rollout: violations are collected, nothing is blocked). Requires mode nonce or hash.

own

boolean

no

The gateway OWNS the policy: the origin’s enforced and report-only CSP headers are removed before ours is emitted. Opt-in and logged (never silent). Requires mode nonce or hash and reportOnly=false: removing the origin’s enforcing policy to replace it with a report-only one would remove protection without enforcing anything. Use only for a site you control/accept, with a complete policy.

reportTo

string

no

Local path served by the gateway collecting the violations (report-uri). Default: /__gate-one_csp/report. Bounded ring buffer (256 entries), 16 KiB body limit, 50 reports/second, never authenticated. Pattern: /[\s?#]*$.

stampScripts

string (one of: inline, all)

no

"inline" (default) = only <script> tags WITHOUT src receive the nonce (external scripts stay governed by the host allow-list); "all" = every <script> tag. Nonce mode only. A tag that already carries a nonce is never rewritten.

directives

map of string to string or array of string

no

Directive name → sources, as one string ("'self' https://x") or an array. The gateway appends the nonce (nonce mode) and the injected scripts' / guard stylesheet’s hashes to the effective script-src / style-src, and report-uri when reportTo is set. To let the guard’s nonce/hash pass, declare style-src (e.g. "'self'").

routes.userBadge

User badge injected by the guard script once authenticated: collapsed on the top edge (one line = username), expanded on hover/keyboard focus — chosen claims + logout button (OIDC end_session + cookie clearing). Absent = no badge.

FieldTypeRequiredDescription

position

string (one of: left, right)

no

Top edge (default right).

claims

boolean or array of string

no

What the expanded badge displays as claims: true = every claim of the session, false = none, a list = only these (missing ones omitted).

roles

boolean or array of string

no

Session roles displayed as chips in the expanded badge: true = every role, false = none, a list = only these (same data as the auth.identity.roles conditions, served by the /__gate-one/auth probe).

scopes

boolean or array of string

no

Granted scopes displayed as chips in the expanded badge: true = every scope, false = none, a list = only these.

logoutLabel

string

no

Label of the logout button (default Log out).

links

array of object

no

Application-specific links, appended after the connector links (account, then services when the deployment delegates). Duplicate URLs are dropped (the connector link wins); at most 8 links after merge.

loginBadge

boolean

no

true = the badge ALSO appears when anonymous, from the very first visit, in invitation mode ("👤 Sign in") — clicked, it opens the login tab: offer sign-in without touching the site’s markup. Default false (badge only once authenticated; when anonymous it only appears after a detected session death).

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

FieldTypeRequiredDescription

label

string

no

Displayed text. Optional for the connector links (default Account/Services); required for userBadge.links. Maximum length: 64.

url

string

yes

http(s) absolute URL or local absolute path (/…​). Maximum length: 2048.

target

string (one of: _blank, _self)

no

Link target. Default _blank.

routes.uiGate

UI gating rule by config — for fronts whose markup cannot be modified (if you control the HTML, prefer the data-gtw-* attributes). The selected element is visible only if the session satisfies the conditions (OR on roles, OR on scopes, AND on claims). PRECEDENCE: an element carrying at least one data-gtw-* attribute ignores uiGate rules; several rules on a same element combine with AND; invalid selector = rule ignored.

FieldTypeRequiredDescription

selector

string

yes

CSS selector (querySelectorAll) of the targeted elements.

roles

array of string

no

Visible if AT LEAST ONE of these roles is held.

scopes

array of string

no

Visible if AT LEAST ONE of these scopes is granted.

claims

map of string to string or array of string

no

All keys must match; each value is one accepted string or a list (OR / anyOf); "*" = presence. A token claim that is itself a list matches when one element matches. Example: {"email": ["alice@example.com", "a.rolland@example.com"]}.

auth

string (one of: after, before)

no

Turns the selected elements into login triggers: after = native action preserved + invitation tab; before = click neutralized + direct tab. Activation only: deactivations (unchecking, closing details, reset, returning a value control to its OFF value) are never gated. Value controls (select/range/date/…) are gated on change, with their default value as the OFF value (data-gtw-auth-off on the element overrides it).

hidden

string (one of: before, after)

no

Hiding: before = hidden before authentication (any click reaching it is neutralized → banner); after = hidden once authenticated.

routes.authGuard

401 guard script (login without reloading: tab + replay + banner + declarative gating). Active by default on frontend routes; no-op elsewhere. The guard intercepts the 401 (login) and, when authGuard.forbidden.banner is set, the gateway’s OWN identity 403; a 403 coming from the backend is never concerned.

FieldTypeRequiredDescription

enabled

boolean

no

false = opt-out (the front handles its own 401).

loginUrl

string

no

Default: https://login.cladone.com/login (auth-bridge page).

forbidden

object

no

Opt-in UX for the gateway’s OWN identity 403 (rights refusal — auth.identity not satisfied). A backend 403 is never concerned. Browser navigations get a readable HTML page (or a redirect to pageUrl); fetch()/XHR get the JSON 403 unchanged, plus an optional dismissable banner hinting the front authors to hide the feature.

routes.authGuard.forbidden

Opt-in UX for the gateway’s OWN identity 403 (rights refusal — auth.identity not satisfied). A backend 403 is never concerned. Browser navigations get a readable HTML page (or a redirect to pageUrl); fetch()/XHR get the JSON 403 unchanged, plus an optional dismissable banner hinting the front authors to hide the feature.

FieldTypeRequiredDescription

banner

boolean

no

true = the injected guard script shows a dismissable banner when a fetch() sees the gateway’s identity 403. Default false.

pageUrl

string

no

Browser navigations refused on identity are redirected there (302) instead of the built-in HTML page.

message

string

no

Overrides the built-in forbidden page’s message text.

ssoGroups

One cross-domain SSO group. Every provider in the group shares issuer, client, scopes, session key and session store; only login host, cookie domain and domain-specific links may differ.

FieldTypeRequiredDescription

id

string

yes

Group identifier used in diagnostics and -check output. Minimum length: 1.

type

string (one of: oidc)

no

Provider type. Only oidc is allowed in a group; JWT providers stay standalone authProviders entries. Default: oidc.

issuer

string

yes

OIDC issuer shared by every provider in the group.

clientId

string

yes

OIDC client id shared by every provider. Register every provider callback URL at the IdP. Minimum length: 1.

clientSecretEnv

string

no

Environment variable holding the shared OIDC client secret. Empty = public client (PKCE only).

scopes

array of string

no

OIDC scopes requested for every provider.

roleClaims

array of string

no

Dot paths in the ID token whose values become roles, shared by every provider.

scopesClaim

string

no

Claim holding granted scopes. Default: scope; scp is always also read.

sessionKeyEnv

string

no

Environment variable holding the RP cookie signing key. Required with Redis; identical for every provider in the group.

sessionStore

object

no

RP-side revocation store (back-channel logout, RP logout). Default: bbolt at ./gate-one-state.db, relative to the config file. bbolt is single-writer: a second Gate One instance on the same file is refused. Redis is the shared, multi-instance backend and makes revocation visible on the next request on every instance.

sessionTtlSeconds

integer

no

OIDC RP session lifetime. Default 3600 (1h): a short TTL bounds revocation when no shared store is used. The refresh/reauth flow renews it. Minimum: 1.

backchannelLogout

boolean

no

OIDC Back-Channel Logout. Absent = auto: enabled when the IdP discovery advertises backchannel_logout_supported. false disables it. Register <loginBaseUrl>/backchannel-logout at the IdP.

backchannelForwardUrl

string

no

Migration bridge: after a valid local back-channel revocation, POST the same logout_token to this auth-bridge /backchannel-logout URL so its persistent sid list stays in sync for /auth and /connections. Best effort.

revocationTtlSeconds

integer

no

Retention of revocation entries. Default: the issuer’s advertised refresh-token TTL (cladone_refresh_token_ttl_seconds, e.g. the embedded IAM), else 30 days; never shorter than the session TTL. Must cover the refresh-token lifetime for a global logout. A revocation covers only sessions/tokens issued before it, so a later sign-in is accepted (a user is never locked out by their own logout). Minimum: 1.

legacyTokenCookies

boolean

no

Migration bridge: also set auth-bridge’s access_token/refresh_token/id_token cookies on cookieDomain, so auth-bridge’s token-broker proxy routes (/connections, /connect, /oauth) resolve the same session when both share the login host. Default false. The native session cookie remains authoritative for Gate One.

providers

array of object

yes

One entry per application domain/provider. Only fields that vary per domain are allowed here.

ssoGroups.sessionStore

RP-side revocation store (back-channel logout, RP logout). Default: bbolt at ./gate-one-state.db, relative to the config file. bbolt is single-writer: a second Gate One instance on the same file is refused. Redis is the shared, multi-instance backend and makes revocation visible on the next request on every instance.

FieldTypeRequiredDescription

type

string (one of: bolt, redis)

no

bolt (default, single instance) or redis (shared, multi-instance, immediate revocation).

path

string

no

bbolt file path. Default ./gate-one-state.db (relative to the config file).

url

string

no

Redis URL for type redis (redis:// or rediss://). Mutually exclusive with sentinel mode.

sentinelAddrs

array of string

no

Redis Sentinel addresses (host:port). Required with masterName.

masterName

string

no

Redis Sentinel master name. Requires sentinelAddrs.

sentinelUsername

string

no

ACL user used to authenticate to the Sentinels themselves (sentinel mode only). Separate from username, which is the Redis data user.

sentinelPasswordEnv

string

no

Environment variable holding the Sentinel password (sentinel mode only). Never put the password in this file.

tls

boolean

no

Use TLS in sentinel mode (Sentinels and the discovered master). In URL mode use a rediss:// URL instead.

tlsCaFile

string

no

PEM CA bundle for the Sentinel TLS connections. Requires tls=true; empty = system roots.

tlsServerName

string

no

TLS server name override (useful when the Sentinels are addressed by IP). Requires tls=true.

username

string

no

Redis ACL username.

passwordEnv

string

no

Environment variable holding the Redis password. Never put the password in this file.

db

integer

no

Redis logical database. Default 0. Minimum: 0.

prefix

string

no

Shared revocation key prefix. Default cladone:auth:v1:. Must match the embedded IAM’s redis.authPrefix.

dialTimeoutMs

integer

no

Redis dial timeout in milliseconds. Default 500. Minimum: 0.

readTimeoutMs

integer

no

Redis read timeout in milliseconds. Default 300. Minimum: 0.

writeTimeoutMs

integer

no

Redis write timeout in milliseconds. Default 300. Minimum: 0.

poolSize

integer

no

Redis connection pool size. Default 64. Minimum: 0.

ssoGroups.providers

One domain/provider inside an SSO group. Shared fields (issuer, clientId, sessionStore, …) are deliberately absent: put them on the group.

FieldTypeRequiredDescription

id

string

yes

Provider id used by routes (auth.provider). Must be globally unique. Minimum length: 1.

loginBaseUrl

string

yes

Public origin of this domain’s login service. Must be the cookieDomain or one of its subdomains.

loginUrl

string

no

Optional full login page URL. Defaults to loginBaseUrl + /login.

cookieDomain

string

yes

Registrable parent domain for this provider’s session cookie (e.g. app-a.com or app-b.fr). Distinct and non-overlapping within the group; route hosts must be covered by it. Minimum length: 1.

cookieName

string

no

Optional session cookie name. Empty = provider-scoped default.

redirectUrl

string

no

Optional OIDC callback URL. Defaults to loginBaseUrl + /auth/callback. Must be on cookieDomain or a subdomain.

links

object

no

Links owned by the authentication connector, displayed in the user badge. account is the IdP’s own account console: NO OIDC standard, so the URL is explicit (Keycloak /realms/<realm>/account/, Entra myaccount.microsoft.com, Google myaccount.google.com, Okta /enduser/settings…).

mcpAudience

string

no

MCP access-token audience policy. Empty = scope-only (compatible default). A literal value requires that value in aud. "auto" requires the endpoint’s own protected-resource identifier (recommended, and used with Keycloak audience mappers).

defaultAppUrl

string

no

Application to send a bare visit to this provider’s login host root to. Must be a route host of this provider (or a host covered by its cookieDomain). Overrides the group/global fallback for this domain. Prefer a neutral page rather than one application.

Links owned by the authentication connector, displayed in the user badge. account is the IdP’s own account console: NO OIDC standard, so the URL is explicit (Keycloak /realms/<realm>/account/, Entra myaccount.microsoft.com, Google myaccount.google.com, Okta /enduser/settings…).

FieldTypeRequiredDescription

account

object

no

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

A link rendered in the expanded user badge. URL: http(s) absolute or a local absolute path (/…​); javascript:/data: are refused. target defaults to _blank; rel is forced to noopener noreferrer.

FieldTypeRequiredDescription

label

string

no

Displayed text. Optional for the connector links (default Account/Services); required for userBadge.links. Maximum length: 64.

url

string

yes

http(s) absolute URL or local absolute path (/…​). Maximum length: 2048.

target

string (one of: _blank, _self)

no

Link target. Default _blank.