---
summary: Configure SigID runtime, database, networking, secrets, production guards, KMS, delivery providers, integrations, and observability.
tags:
  - server configuration
  - deployment
  - database
  - secrets
  - observability
categories:
  - Reference
---

# Server Configuration

<!-- agent:page
You are an AI agent using this reference to configure or operate a self-hosted SigID deployment.
- Use exact TOML section and key names as written here (`[deployment]`, `[server]`, `[database]`, `[kms]`, `security.production`); environment overrides use the `SIGID_` prefix with `__` between nested keys.
- Precedence is built-in defaults, then `sigid.toml` (or `SIGID_CONFIG_FILE`), then environment variables.
- Respect the JWT table's validation ranges; out-of-range values fail startup, and refresh lifetimes must be at least the matching access lifetime.
- Production mode (`security.production = true`) hard-enforces the requirements table: KMS configured, no plain HTTP, allowed hosts, trusted proxies or explicit direct exposure, database TLS, OpenAPI and OAuth proxy disabled, fail-closed CAPTCHA and HIBP. Never advise disabling these to silence a startup error.
- Map startup failures against the Common Startup Errors table before editing configuration.
- Secrets reported as "moved to sigid-kms" must be provisioned through KMS, not re-added as environment variables.
-->

Use this page when you run SigID yourself or operate a SigID deployment. It is
for platform engineers, SREs, and security owners who need to configure the
runtime, database, networking, secrets, production guards, and observability.

For workspace launch decisions, use
[Launch Your Workspace](../business/launch-workspace.md). For application
integration, use [Add SigID Login](../developers/add-login.md).

## Configuration Sources

SigID loads configuration in this order:

1. built-in defaults
2. `sigid.toml` in the working directory, or the file pointed to by
   `SIGID_CONFIG_FILE`
3. environment variable overrides

The repository root includes these starting points:

| File | Use |
|---|---|
| `sigid.example.toml` | Complete commented server configuration |
| `.env.example` | Local environment variable examples |
| `docker/sigid.org.toml` | Docker development configuration |

Environment variables use `SIGID_` with `__` between nested keys. For example:

```sh
SIGID_SERVER__PORT=3000
SIGID_SECURITY__PRODUCTION=true
SIGID_OPENAPI__ENABLED=false
```

`DATABASE_URL` is also accepted as a direct override for `[database].url`.

For single-tenant local development, `[tenant].default_tenant_slug = "dev"`
skips subdomain-based tenant resolution and routes requests to that tenant slug.

## Choose a Deployment Topology

<!-- agent:action Choose a deployment topology
Pick all-in-one or split plane based on scaling and isolation needs, then set the `[deployment]` keys accordingly.
- all-in-one: keep `deployment.allow_all_in_one_runtime_plane = true`
- split plane: set it to `false` and run one process per `SIGID_RUNTIME_PLANE` (`auth`, `control`, `async`)
- choose `coordination_backend` (`postgres`, `redis`, or `memcached`) and configure its companion section (`[redis].url` or `[memcached].address`)
- set `database_topology` to `shared` or `split_by_plane` to match
-->

SigID can run as one process or as separate runtime planes.

| Topology | Use when | Configuration |
|---|---|---|
| All-in-one | Local development, small deployments, first production rollout | Keep `deployment.allow_all_in_one_runtime_plane = true` |
| Split plane | You want separate scaling or isolation for auth, control, and async workloads | Set `deployment.allow_all_in_one_runtime_plane = false` and run one process per `SIGID_RUNTIME_PLANE` |

The `[deployment]` section controls runtime shape:

| Setting | Purpose |
|---|---|
| `allow_all_in_one_runtime_plane` | Allows one process to serve every plane |
| `background_tasks_enabled` | Lets this process run background workers; disable for canary request-serving slots |
| `embedded_chain_monitor` | Runs chain monitoring in the server process when chain monitoring is configured |
| `coordination_backend` | Shared state backend: `postgres`, `redis`, or `memcached` |
| `database_topology` | Database routing mode: `shared` or `split_by_plane` |

Coordination backends use these companion sections:

| Backend | Required config | Other settings |
|---|---|---|
| `postgres` | No extra section; uses the configured database | Default backend |
| `redis` | `[redis].url` | `[redis].pool_size` |
| `memcached` | `[memcached].address` | `pool_size`, `max_idle`, `connect_timeout_secs`, `command_timeout_secs` |

Redis is active when `deployment.coordination_backend = "redis"` and
`redis.url` is set. Memcached is active when
`deployment.coordination_backend = "memcached"` and `memcached.address` is set.

## Runtime Planes

Set `SIGID_RUNTIME_PLANE` when you run split-plane deployments.

| Value | Purpose |
|---|---|
| `all` | Serve all runtime planes in one process; only valid when all-in-one mode is allowed |
| `auth` | Authentication, OAuth, sessions, hosted login, user-facing identity routes |
| `control` | Management APIs, admin routes, OpenAPI docs when enabled |
| `async` | Background workers, event processing, async maintenance work |

When `deployment.allow_all_in_one_runtime_plane = false`, startup requires
`SIGID_RUNTIME_PLANE=auth`, `SIGID_RUNTIME_PLANE=control`, or
`SIGID_RUNTIME_PLANE=async`.

## Server And Networking

The `[server]` section controls the HTTP listener, first-party origins, CORS,
proxy trust, host validation, metrics authentication, throttling, and shutdown
behavior.

| Setting | Purpose |
|---|---|
| `host`, `port` | Listen address and port |
| `transport` | `plain_http` for local/non-production, `tls_h2_only` for production |
| `base_domain` | Primary domain for cookie scoping and tenant routing |
| `identity_app_origin` | First-party identity app origin |
| `dashboard_app_origin` | First-party dashboard app origin |
| `allowed_hosts` | Host-header allowlist; required in production |
| `trusted_proxies` | Proxy IPs/CIDRs allowed to supply `X-Forwarded-For` |
| `direct_client_exposure` | Explicit acknowledgement that the process is directly exposed without a trusted proxy |
| `metrics_bearer_token` | Optional static bearer token for Prometheus scrapes |
| `endpoint_throttle` | Per-endpoint throttling rules |
| `request_timeout_secs` | Per-request deadline; production rejects `0` |
| `shutdown_deadline_secs` | Graceful shutdown deadline; production rejects `0` |

`server.transport = "tls_h2_only"` terminates TLS in-process and requires HTTP/2
ALPN. Configure `[server.tls]` with `cert_pem_path` and `key_pem_path`. Set
`ca_pem_path` when local clients, such as container health checks, need an
explicit trust bundle for the direct listener.

Production mode rejects `server.transport = "plain_http"`. Local development
can still use plain HTTP.

## Database

The `[database]` section configures PostgreSQL URLs, pool sizing, timeouts, and
SSL requirements.

| Setting | Purpose |
|---|---|
| `url` | Shared runtime database URL |
| `identity_url` | Required identity/session database URL |
| `auth_url` | Optional auth-plane database URL |
| `control_url` | Optional control-plane database URL |
| `async_url` | Optional async-plane database URL |
| `migration_url` | Optional migrator database URL |
| `maintenance_url` | Optional maintenance database URL |
| `max_connections`, `min_connections` | Default pool sizing |
| `connect_timeout_secs` | Database connection timeout |
| `idle_timeout_secs`, `max_lifetime_secs` | Connection recycling controls |
| `statement_cache_capacity` | Prepared statement cache size per connection |
| `test_before_acquire` | Validate pooled connections before use |
| `statement_timeout_secs` | Request-serving SQL statement timeout |
| `idle_in_transaction_timeout_secs` | Timeout for sessions idle inside transactions |
| `maintenance_statement_timeout_secs` | Longer timeout for maintenance operations |
| `require_ssl` | Require TLS for database connections |

For shared topology, set `[database].url` or `DATABASE_URL`. For split-plane
topology, set `deployment.database_topology = "split_by_plane"` and provide
plane URLs as needed. Missing plane URLs fall back to `[database].url`.

Plane-specific pool sections override pool sizing when the matching runtime
plane is active:

```toml
[database.auth_pool]
max_connections = 64
min_connections = 8

[database.control_pool]
max_connections = 24
min_connections = 4

[database.async_pool]
max_connections = 16
min_connections = 2
```

Production mode requires `database.require_ssl = true` unless the database URL
explicitly includes an `sslmode=` parameter.

## KMS And Production Secrets

Production deployments require `sigid-kms`. Without `[kms]`, non-production
starts with ephemeral in-memory keys, which are not suitable for production.

| Setting | Purpose |
|---|---|
| `socket_path` | Unix socket path for the local `sigid-kms` service |
| `server_public_key_path` | Pinned Noise public key published by `sigid-kms` |
| `client_private_key_path` | This runtime's persistent Noise client private key |

Several historical secret environment variables are intentionally rejected at
startup because those secrets have moved into KMS. If startup reports that a
`SIGID_*` secret has moved to `sigid-kms`, rotate or provision it through the KMS
path instead of adding it back to the process environment.

## Auth, Email, Phone, Captcha, And HIBP

The `[auth]` section controls password hashing, brute-force protection, and
registration limits.

| Setting | Purpose |
|---|---|
| `argon2_memory_kib` | Password hashing memory cost |
| `argon2_iterations`, `argon2_parallelism` | Password hashing time and parallelism |
| `argon2_max_concurrent` | Concurrent Argon2 verification limit |
| `brute_force_max_attempts`, `brute_force_lockout_secs` | Account lockout rules |
| `password_verify_ip_*` | IP-weighted budget for expensive password verification |
| `registration_rate_limit_*` | Registration rate limiting |

In production on the auth plane, startup enforces the production Argon2
throughput profile, including 64 MiB memory, one parallel lane, bounded
iterations, and bounded concurrency.

Production auth-plane deployments also require:

- `email.provider` is not `log`
- SMTP uses `email.smtp.starttls = true` when `email.provider = "smtp"`
- `phone.provider` is not `log`
- `captcha.enabled = true`
- `captcha.secret_key` is set
- `captcha.fail_mode` is not `fail-open`
- `hibp.enabled = true`
- `hibp.fail_mode` is not `fail-open`

For local development, `phone.provider = "log"` writes only redacted structured
logs unless `[phone.log].sink` is set. Supported sink values are `stdout`,
`stderr`, and `file:/path/to/mailbox.ndjson`; the file sink appends JSON Lines
with full SMS bodies for browserless simulations. Do not use this provider in
production.

## JWT

The `[jwt]` section controls token signing defaults and token lifetimes.

| Setting | Default | Validation |
|---|---|---|
| `default_algorithm` | `ES256` | Must be `ES256` or `EdDSA` |
| `access_token_lifetime_human_secs` | `900` | `60..=86400` |
| `access_token_lifetime_agent_secs` | `300` | `60..=86400` |
| `refresh_token_lifetime_human_secs` | `604800` | `3600..=7776000` |
| `refresh_token_lifetime_agent_secs` | `86400` | `3600..=7776000` |
| `id_token_lifetime_secs` | `300` | `60..=3600` |
| `key_rotation_days` | `30` | `1..=365` |

Refresh-token lifetimes must be greater than or equal to the matching access
token lifetime. JWT signing-key encryption is handled by `sigid-kms` in
production.

## OIDC, First-Party Origins, And Passkeys

The `[oidc]` section defines the issuer URL and authorization code lifetime.
Production mode requires `oidc.issuer_url` to use HTTPS.

The `[passkey]` section configures WebAuthn relying-party settings. Keep the
passkey origin aligned with the browser origin that users actually visit.

The first-party origins in `[server]` should match the deployed identity and
dashboard frontends:

```toml
[server]
identity_app_origin = "https://identity.example.com"
dashboard_app_origin = "https://dashboard.example.com"
```

## Feature Sections

Most feature modules have their own configuration section. Keep this page as
the server-level map, then use feature guides for behavior and API details.

| Section | Purpose |
|---|---|
| `[social]` | Global social OAuth settings |
| `[enterprise_sso]` | Enterprise OIDC SSO state secret and callback settings |
| `[generic_oauth]` | Database-backed arbitrary OAuth/OIDC providers |
| `[phone]`, `[sms]` | Phone/SMS OTP delivery |
| `[email_otp]` | Email OTP settings |
| `[magic_link]` | Magic link passwordless authentication |
| `[anonymous]` | Anonymous/guest authentication |
| `[two_factor]` | TOTP, backup codes, trusted devices |
| `[adaptive_mfa]` | Risk-based step-up authentication |
| `[challenge]` | Challenge nonce TTL, agent PoW difficulty, agent limits |
| `[device_authorization]` | RFC 8628 device flow |
| `[session_cookie]` | Session limits, cache cookies, binding |
| `[admin]` | Admin panel roles and bootstrap admin user credentials |
| `[access_control]` | RBAC/ABAC role, policy, and scope limits |
| `[proxy]` | Outbound proxy mTLS |
| `[oauth_proxy]` | Development-only OAuth proxy for redirect callbacks |
| `[wallet]` | Managed wallet signing |
| `[[chains]]` | Blockchain RPC configuration |
| `[siwe]` | Sign-In with Ethereum enablement, domain pin, and accepted EIP-155 chain IDs |
| `[api_keys]` | API key authentication |
| `[i18n]` | Localization and message catalogs |
| `[billing]`, `[commerce]` | Billing, plans, checkout, and payment-of-record |
| `[dpop]` | DPoP sender-constrained token validation |
| `[organizations]`, `[invitations]`, `[verified_domains]` | B2B organization and domain settings |
| `[vault]` | Credential storage limits |
| `[webhooks]` | Webhook subscription limits |
| `[scim]` | SCIM 2.0 provisioning |
| `[telemetry]` | Metrics, tracing, audit batching, client telemetry |
| `[refresh_family_cache]` | Refresh-token family revocation cache |
| `[user_self_service]` | Account deletion and password-change behavior |

## Observability: Health And Metrics

Health probes are available under `/health`:

| Route | Use | What it checks |
|---|---|---|
| `GET /health/live` | Liveness probe | Process is running |
| `GET /health/startup` | Startup probe | Migrations have run and signing keys are loaded |
| `GET /health/ready` | Readiness probe | Database, signing keys, configured chains, and critical background tasks |

The health response only exposes `status`. Detailed failing checks are written
to logs.

Metrics are served from `GET /metrics` in Prometheus format. Authentication
depends on configuration:

| Configuration | Metrics auth behavior |
|---|---|
| `server.metrics_bearer_token` is set | Accepts `Authorization: Bearer <token>` |
| `server.metrics_bearer_token` is not set | Requires a SigID token with `metrics:read` |

## OpenAPI Documentation Endpoints

When `[openapi].enabled = true`, the OpenAPI router serves:

| Route | Purpose |
|---|---|
| `GET /openapi.json` | Raw OpenAPI JSON |
| router root | Scalar interactive API UI |

Scalar is the browser UI used to explore the generated OpenAPI specification
interactively.

Treat OpenAPI as a development and internal review surface. Production mode
rejects `openapi.enabled = true` because it exposes the full API surface.

## Environment Variable Overrides

Environment variables take precedence over TOML. Use generic `SIGID_` overrides
for ordinary config fields:

```sh
SIGID_SERVER__ALLOWED_HOSTS='["identity.example.com","dashboard.example.com"]'
SIGID_SERVER__DIRECT_CLIENT_EXPOSURE=false
SIGID_DEPLOYMENT__DATABASE_TOPOLOGY=split_by_plane
```

Database URL shortcuts are also supported:

| Variable | TOML equivalent |
|---|---|
| `SIGID_CONFIG_FILE` | Config file path |
| `SIGID_RUNTIME_PLANE` | Runtime plane selection |
| `DATABASE_URL` | `[database].url` |
| `SIGID_DATABASE__URL` | `[database].url` |
| `DATABASE_IDENTITY_URL` | `[database].identity_url` |
| `DATABASE_AUTH_URL` | `[database].auth_url` |
| `DATABASE_CONTROL_URL` | `[database].control_url` |
| `DATABASE_ASYNC_URL` | `[database].async_url` |
| `MIGRATION_DATABASE_URL` | `[database].migration_url` |
| `MAINTENANCE_DATABASE_URL` | `[database].maintenance_url` |
| `SIGID_SECURITY__PRODUCTION` | `[security].production` |
| `SIGID_SERVER__PORT` | `[server].port` |
| `SIGID_OPENAPI__ENABLED` | `[openapi].enabled` |

Use `SIGID_CONFIG_FILE=/path/to/sigid.toml` when the config file is not in the
working directory.

## Database Migrations

<!-- agent:action Run database migrations
Run `sigid-runtime --migrate-only` as a separate initialization step before traffic reaches the new version.
- set `MIGRATION_DATABASE_URL` when the migrator should connect as a separate database principal
- `MAINTENANCE_DATABASE_URL` is optional; maintenance falls back to the migration URL, then the runtime URL
-->

Run migrations as a separate initialization step:

```sh
sigid-runtime --migrate-only
```

Use `MIGRATION_DATABASE_URL` when the migrator should connect with a separate
database principal. `MAINTENANCE_DATABASE_URL` is optional; when it is not set,
maintenance operations fall back to the migration URL and then the runtime URL.

## Production Mode

Set `security.production = true` for non-local deployments. Startup refuses to
continue when non-local indicators, such as public issuer or app origins, are
present without production mode.

Production mode enforces these server-level requirements:

| Area | Startup requirement |
|---|---|
| KMS | `[kms]` is configured |
| Transport | `server.transport` is not `plain_http` |
| Host validation | `server.allowed_hosts` is populated |
| Proxy trust | `server.trusted_proxies` is populated, or `server.direct_client_exposure = true` |
| Timeouts | `server.request_timeout_secs` and `server.shutdown_deadline_secs` are greater than `0` |
| Database | `database.require_ssl = true` unless the URL sets `sslmode=` |
| OIDC | `oidc.issuer_url` uses HTTPS |
| OpenAPI | `openapi.enabled = false` |
| OAuth proxy | `oauth_proxy.enabled = false` |
| Webhooks | `security.webhook_allow_non_standard_ports = false` |
| Auth abuse controls | CAPTCHA and HIBP are enabled and do not fail open |
| Email/phone | Providers are not `log`; SMTP uses STARTTLS |

## Production Hardening Checklist

<!-- agent:action Harden for production
Confirm every checklist item before exposing SigID to production traffic; each maps to a startup requirement or operational guard.
- `security.production = true`, `sigid-kms` running with `[kms]` pointed at the correct socket and keys, and `server.transport = "tls_h2_only"` with cert and key paths
- `allowed_hosts` and `trusted_proxies` contain only expected values; issuer, app origins, and passkey origin use HTTPS; database TLS via `require_ssl` or `sslmode=`
- OpenAPI and OAuth proxy disabled; email, phone, CAPTCHA, and HIBP use production providers; webhooks restricted to HTTPS default ports
- wire `/health/startup`, `/health/ready`, and `/metrics` into monitoring, and run migrations before traffic
-->

Before exposing SigID to production traffic, confirm:

- `security.production = true`
- `sigid-kms` is running and `[kms]` points to the correct socket and keys
- `server.transport = "tls_h2_only"` is configured with the certificate and key
  paths required by the direct SigID listener
- `server.allowed_hosts` contains only expected hostnames
- `server.trusted_proxies` contains only your ingress or load balancer ranges,
  unless `direct_client_exposure = true` is intentional
- `oidc.issuer_url`, identity origin, dashboard origin, and passkey origin use HTTPS
- database connections require TLS through `database.require_ssl` or URL
  `sslmode=`
- OpenAPI and OAuth proxy are disabled
- email, phone, CAPTCHA, and HIBP providers are production providers
- webhook delivery is restricted to HTTPS default ports
- `/health/startup`, `/health/ready`, and `/metrics` are wired into your
  monitoring system
- migrations run before traffic reaches the new version

## Common Startup Errors

<!-- agent:action Diagnose a startup failure
Match the startup error text against the table's "Error includes" column and apply the listed fix exactly.
- fix the configuration the error names; do not disable production mode or its guards to make the error go away
- for "moved to sigid-kms", provision the secret through KMS and remove the environment variable
- after each fix, restart and re-check until startup completes cleanly
-->

| Error includes | Meaning | Fix |
|---|---|---|
| `security.production must be true` | Public/non-local deployment indicators were detected without production mode | Set `security.production = true` and complete the production checklist |
| `server.transport = "plain_http"` | Production mode rejects plain HTTP direct listener | Use `tls_h2_only` or adjust the deployment boundary before enabling production |
| `server.allowed_hosts` | Host-header validation is not configured | Add expected identity, dashboard, and tenant hostnames |
| `trusted_proxies` | Proxy headers would be ignored or unsafe | Add trusted proxy CIDRs, or explicitly set `direct_client_exposure = true` |
| `database URL is required` | No runtime database URL was configured | Set `DATABASE_URL`, `SIGID_DATABASE__URL`, or plane-specific database URLs |
| `database.require_ssl` | Production database traffic is not marked TLS-protected | Set `database.require_ssl = true` or include `sslmode=` in the URL |
| `kms must be configured` | Production key management is missing | Configure `[kms]` and start `sigid-kms` |
| `openapi.enabled must be false` | OpenAPI docs are enabled in production | Disable `[openapi]` for production |
| `captcha.enabled` or `hibp.enabled` | Auth-plane abuse controls are missing | Configure CAPTCHA and HIBP with fail-closed behavior |
| `moved to sigid-kms` | A removed secret env var is still set | Provision that secret through KMS and remove the env var |

## Next Steps

- [Security And Audit](../business/security-audit.md) for business operational
  controls and incident ownership
- [Verify Tokens](../developers/verify-tokens.md) for backend authorization
  checks
- [Webhooks](../developers/webhooks.md) for webhook receiver operations
- [Billing And Production](../business/billing-production.md) for workspace
  production readiness
