# ENV Variables

Sentinel is configured entirely through environment variables. Everything below is optional with a sensible default unless noted otherwise — set only what you need to change.

> **Note:**
>
> Any variable also accepts a `_FILE` suffix (e.g. `SECRET_SEED_FILE=/run/secrets/secret_seed`) to read the value from a mounted file instead of the environment directly — useful with Docker/Kubernetes secrets.

## Required secrets

These are auto-generated on first start if unset (and persisted to the data volume) — or derive them deterministically across a cluster with `SECRET_SEED`, see [Clustering](/docs/sentinel/operations/clustering/).

| Variable | Default | Notes |
|---|---|---|
| `ALTCHA_HMAC_SECRET` | auto-generated | Signs ALTCHA challenges. Minimum 24 characters. |
| `CODE_CHALLENGE_SECRET` | auto-generated | Signs code challenges. Minimum 24 characters. |
| `JWT_SECRET` | auto-generated | Signs admin session JWTs. Minimum 24 characters. |
| `EXOTDB_HMAC_SECRET` | *(empty)* | HMAC secret for data signatures (mainly access logs). Minimum 24 characters. |
| `HASHING_SALT` | auto-generated | Salt used for IP and other hashing. |
| `NODE_ID` | auto-generated | Unique identifier for this node — see [Clustering](/docs/sentinel/operations/clustering/). |
| `SECRET_SEED` | *(empty)* | If set, deterministically derives all of the above instead of auto-generating them. |

## Licensing

See [License](/docs/sentinel/security/license/) for how these are used.

| Variable | Default | Purpose |
|---|---|---|
| `LICENSE_KEY` | *(empty)* | License key; verified periodically via call-home to `eu.altcha.org`. |
| `LICENSE_JSON` | *(empty)* | Full license file contents — set instead of `LICENSE_KEY` to disable call-home entirely. |

## Application

| Variable | Default | Purpose |
|---|---|---|
| `PORT` | `8080` | Port the app listens on. |
| `DATA_DIR` | `/data` | Directory for application data. |
| `BASE_URL` | *(empty)* | Base URL for generating absolute links. |
| `APP_BASE_PATH` | *(empty)* | Serve the app under a path prefix. |
| `DEFAULT_ACCOUNT_NAME` | `Default Account` | Name of the account created on first setup. |
| `DEFAULT_ROOT_PASSWORD` | `root` | Initial root password — change immediately after first login. |
| `NODE_NAME` | *(empty)* | Human-readable name for this node. |
| `TZ` | system timezone | Application timezone. |
| `USER_AGENT` | `altcha-sentinel/{version}` | User-Agent used for Sentinel's own outbound requests. |
| `API_DOCS_ENABLED` | `1` | Serve API docs at `/v1/docs`. |
| `INSPECT_ROUTE_ENABLED` | `1` | Enable `GET /v1/inspect`. |

## Security & access

| Variable | Default | Purpose |
|---|---|---|
| `APP_IP_WHITELIST` | *(empty, unrestricted)* | Restrict admin/app access to given IPs/CIDRs. |
| `ALLOWED_HOSTS` | *(empty)* | Comma-separated, wildcard-supported allowed hostnames. |
| `CORS_ORIGINS` | *(empty, unrestricted)* | Comma-separated allowed CORS origins. |
| `X_FORWARDED_FOR_TRUSTED` | *(empty)* | Trusted proxy IPs allowed to set `X-Forwarded-For` — see [Reverse Proxy](/docs/sentinel/operations/reverse-proxy/). |
| `PASSWORD_LOGIN_ENABLED` | `1` | Enable/disable password-based login. |
| `PASSWORD_MIN_LENGTH` | `8` | Minimum admin password length. |
| `JWT_ISSUER` | `ALTCHA_SENTINEL` | Issuer identifier used when generating admin session JWTs. |
| `JWT_TTL` | `24h` | Time-to-live for generated JWTs. |
| `SECURITY_TXT` | *(empty)* | Overrides or disables `/.well-known/security.txt`. |
| `TLS_EXTRA_CA_CERTS` | *(empty)* | Extra trusted CA bundle (PEM). |
| `HTTP2_CERT` / `HTTP2_KEY` | *(empty)* | TLS cert/key to terminate HTTPS directly on the container. |
| `ANONYMIZE_IP_ADDRESS` | `1` | Anonymize IPs generally. |
| `ACCESS_LOG_ANONYMIZE_IP_ADDRESS` | `1` | Anonymize IPs specifically in access logs. |
| `IP_HEADERS_SECRET` | *(empty)* | Secret to validate incoming IP-related headers. |
| `HEADER_ENTROPY_MAX_ENTRIES` | `100` | Max header-combination hashes stored per IP — see [Detection Signals](/docs/sentinel/features/detection-signals/#header-entropy). |

## Networking

| Variable | Default | Purpose |
|---|---|---|
| `HTTP_PROXY` / `HTTPS_PROXY` | *(empty)* | Outbound proxy for Sentinel's own HTTP/HTTPS requests. |
| `NO_PROXY` | `localhost,127.0.0.1,::1,0.0.0.0` | Hosts that bypass the outbound proxy. |

## Database

Set one connection-string variable matching your engine — see [Databases](/docs/sentinel/databases/#enabling-an-external-main-database) for the full list (`POSTGRES_URL`, `MYSQL_URL`, `MARIADB_URL`, `MSSQL_URL`, `ORACLE_URL`, `LIBSQL_URL`, `TURBOLITE_URL`) and examples. Each engine also accepts connection-tuning variables following the same prefix, most commonly:

| Suffix | Applies to | Purpose |
|---|---|---|
| `_CONNECT_TIMEOUT` | MariaDB, MSSQL, MySQL, PostgreSQL | Connection timeout, in ms (default `10000`). |
| `_MAX_CONNECTIONS` | MariaDB, MSSQL, MySQL, PostgreSQL, LibSQL | Max pool size (default `10`). |
| `_MIN_CONNECTIONS` | PostgreSQL | Min pool size (default `0`). |
| `_IDLE_TIMEOUT` | PostgreSQL | Idle connection timeout, in ms (default `10000`; `0` disables). |
| `_TLS_CA` / `_TLS_CERT` / `_TLS_KEY` | MariaDB, MSSQL, MySQL, PostgreSQL | Custom TLS certificate material. |
| `_TLS_REJECT_UNAUTHORIZED` | MariaDB, MSSQL, MySQL | Reject untrusted TLS connections (default `1`). |
| `LIBSQL_AUTH_TOKEN` | LibSQL | Auth token for the LibSQL/Turso database. |

`POSTGRES_SSL_CA` / `POSTGRES_SSL_CERT` / `POSTGRES_SSL_KEY` are deprecated aliases for the `POSTGRES_TLS_*` variables above.

The embedded default database is configured via `EXOTDB_DATABASE_ADDR` (default `:::4080`), `EXOTDB_DATABASE_LOCATION` (default `./db/altcha-sentinel.db`), `EXOTDB_ROOT_PASSWORD` (default `root`), and `EXOTDB_ENCRYPTION_KEY` (optional, not generally recommended). `DATABASE_URL` (default `http://root:root@localhost:4080?bootstrap=1`) is the connection URL Sentinel uses internally to reach this embedded server — you don't need to set it yourself unless you're customizing the embedded setup directly.

## KV store (Redis)

See [Databases](/docs/sentinel/databases/#enabling-an-external-redisvalkey) for enabling an external instance. Additional tuning:

| Variable | Default | Purpose |
|---|---|---|
| `REDIS_COMMAND_TIMEOUT` | `5000` | Command timeout, ms. |
| `REDIS_CONNECT_TIMEOUT` | `10000` | Connection timeout, ms. |
| `REDIS_MAX_RETRIES` | `2` | Max retry attempts. |
| `REDIS_KEY_PREFIX` | *(empty)* | Prefix applied to all Redis keys. |
| `REDIS_SENTINEL_HOSTS` | *(empty)* | Comma-separated `host:port` list for Redis Sentinel mode. |
| `REDIS_SENTINEL_MASTER_NAME` | *(empty)* | Monitored master name, for Sentinel mode. |
| `REDIS_SENTINEL_AUTH` / `REDIS_SENTINEL_REDIS_AUTH` | *(empty)* | Auth for the Sentinel endpoints / the Redis nodes behind them. |
| `REDIS_SENTINEL_TLS` / `REDIS_SENTINEL_REDIS_TLS` | `0` | Enable TLS to Sentinel endpoints / Redis nodes. |

The embedded default store is configured via `EXOTDB_REDIS_ADDR` (default `:::6389`) and `EXOTDB_REDIS_LOCATION` (default `./db/redis.db`).

## Snapshots

See [Backups & Recovery](/docs/sentinel/operations/backups-recovery/) for how these fit together.

| Variable | Default | Purpose |
|---|---|---|
| `SNAPSHOTS_MAIN_SCHEDULE` | *(empty, disabled)* | Cron schedule for automatic main-database snapshots. |
| `SNAPSHOTS_REDIS_SCHEDULE` | *(empty, disabled)* | Cron schedule for automatic Redis snapshots. |
| `SNAPSHOTS_STORAGE_PROVIDER` | `local` | `local`, `s3`, or `azure`. |
| `SNAPSHOTS_DB_PREFIX` | `altcha-sentinel-backups/db:${iso_date()}` | Path prefix for stored snapshots. |
| `SNAPSHOTS_ENCRYPTION_KEY` | *(empty)* | Optional encryption key for snapshots. |
| `SNAPSHOTS_STORAGE_LOCAL_DIR` | `backups` | Local directory, if using the `local` provider. |
| `SNAPSHOTS_STORAGE_S3_URL` | *(empty)* | S3 URL, e.g. `https://s3.eu-west-1.amazonaws.com/?bucket=my-bucket&prefix=storage`. |
| `SNAPSHOTS_STORAGE_AZURE_CONTAINER` / `SNAPSHOTS_STORAGE_AZURE_CONNECTION_STRING` | *(empty)* | Azure container + connection string. |

`SNAPSHOTS_STORAGE_S3_BUCKET` / `_ACCESS_KEY_ID` / `_SECRET_ACCESS_KEY` / `_REGION` / `_ENDPOINT`, and `SNAPSHOTS_CRON_SCHEDULE`, are deprecated — use `SNAPSHOTS_STORAGE_S3_URL` and `SNAPSHOTS_MAIN_SCHEDULE`/`SNAPSHOTS_REDIS_SCHEDULE` instead.

## File storage

See [Storage Providers](/docs/sentinel/operations/storage-providers/) for details.

| Variable | Default | Purpose |
|---|---|---|
| `STORAGE_PROVIDER` | `local` | `local`, `s3`, or `azure`. |
| `STORAGE_LOCAL_DIR` | `uploads` | Local directory, if using the `local` provider. |
| `STORAGE_S3_BUCKET` / `STORAGE_S3_ACCESS_KEY_ID` / `STORAGE_S3_SECRET_ACCESS_KEY` / `STORAGE_S3_REGION` / `STORAGE_S3_ENDPOINT` | *(empty)* | S3 (or S3-compatible, e.g. MinIO) credentials and location. |
| `STORAGE_AZURE_CONTAINER` / `STORAGE_AZURE_CONNECTION_STRING` | *(empty)* | Azure Blob Storage container + connection string. |

## Caching

| Variable | Default | Purpose |
|---|---|---|
| `CACHE_DURATION_API_KEYS` | `10s` | How long API keys are cached before refresh. |
| `CACHE_DURATION_TRAINING_DATA` | `1h` | How long training data is cached before refresh. |
| `CACHE_DURATION_USED_CHALLENGES` | `4h` | How long used-challenge records are cached, to prevent reuse. |

## Logging

| Variable | Default | Purpose |
|---|---|---|
| `LOG_LEVEL` | `info` | `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `silent`. |
| `LOG_FORMAT` | `json` | `json` or `plain`. |
| `REQUEST_LOGS_TTL` | `72h` | How long request logs are retained. |
| `ACCESS_LOG_ENABLED` | `1` | Enable/disable access logging. |

## Monitoring

See [Monitoring & Logging](/docs/sentinel/operations/monitoring-logging/).

| Variable | Default | Purpose |
|---|---|---|
| `MONITORING_IP_WHITELIST` | *(private ranges, see below)* | IPs/CIDRs allowed to reach `/.health`, `/.metrics`, etc. |
| `MONITORING_HTTP_CREDENTIALS` | *(empty)* | `user:password` for Basic Auth on monitoring endpoints. |

`MONITORING_IP_WHITELIST` defaults to `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128,fd00::/8,100.64.0.0/10` — the standard private/loopback/link-local ranges.

## OpenTelemetry (Enterprise)

| Variable | Default | Purpose |
|---|---|---|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | *(empty)* | OTLP collector URL. |
| `OTEL_SERVICE_NAME` | `altcha-sentinel` | Service name in traces/logs. |
| `OTEL_EXPORTER_OTLP_HEADERS` | *(empty)* | Extra headers for OTLP requests (e.g. auth). |
| `OTEL_EXPORTER_OTLP_TIMEOUT` | `10000` | Timeout, ms. |

## ClickHouse (Enterprise)

See [ClickHouse](/docs/sentinel/operations/clickhouse/) for the table schema and full setup.

| Variable | Default | Purpose |
|---|---|---|
| `CLICKHOUSE_URL` | *(empty)* | e.g. `http://user:password@localhost:8123/db_name`. |
| `CLICKHOUSE_BATCH_INTERVAL` | `1000` | Max ms before flushing the batch buffer. |
| `CLICKHOUSE_BATCH_MAX` | `100` | Max entries before flushing. |
| `CLICKHOUSE_TLS_CA` / `_TLS_CERT` / `_TLS_KEY` | *(empty)* | TLS certificate material. |

## SSO (Enterprise)

See [SSO](/docs/sentinel/security/sso/).

| Variable | Purpose |
|---|---|
| `SSO_AZURE` / `SSO_GOOGLE` / `SSO_KEYCLOAK` / `SSO_OKTA` | OIDC configuration per provider. |
| `SSO_LDAP` | LDAP configuration. |
| `SSO_LDAP_TLS_CA` / `_TLS_CERT` / `_TLS_KEY` | LDAP TLS certificate material (`SSO_LDAP_SSL_CA` / `_SSL_CERT` / `_SSL_KEY` are deprecated aliases). |

## Email / SMTP

| Variable | Default | Purpose |
|---|---|---|
| `SMTP_URL` | *(empty)* | SMTP connection URL for outgoing email — see [Forms](/docs/sentinel/configure/forms/#form-settings). |
| `EML_BODY_LIMIT` | `5MB` | Max body size for `POST /v1/eml`. |

## AI providers

See [AI Providers](/docs/sentinel/integrations/ai-providers/).

| Variable | Purpose |
|---|---|
| `AI_PROVIDER` | `anthropic`, `azure`, `google`, `mistral`, `ollama`, or `openai`. |
| `AI_PROVIDER_MODEL` | Model name. |
| `AI_PROVIDER_OPTIONS` / `AI_PROVIDER_REQUEST_OPTIONS` | Extra JSON-encoded provider/request options. |
| `CONTEXT_DATA_KEY` | Shared AES-GCM key for encrypting/decrypting [Context Override](/docs/sentinel/integrations/custom-parameters/#context-override) data passed to AI Security Rules. |

## Threat intelligence

See [Threat Intelligence](/docs/sentinel/features/threat-intelligence/).

| Variable | Default | Purpose |
|---|---|---|
| `THREATS_ENABLED` | `1` | Enable/disable threat intelligence. |
| `THREATS_INDEX_STORE` | `auto` | `auto`, `memory`, or `sqlite`. |
| `THREATS_MALICIOUS_LIMIT` | `10/5m(expire=48h)` | Rate limit for `malicious`-kind threats. |
| `THREATS_PROBE_LIMIT` | `2/5m(expire=48h)` | Rate limit for `probe`-kind threats. |
| `THREATS_BOT_LIMIT` / `THREATS_PROXY_LIMIT` / `THREATS_TOR_LIMIT` | *(empty)* | Same rate-limit syntax, for the `bot`/`proxy`/`tor` kinds. |

## Geolocation & risk detection

See [Detection Signals](/docs/sentinel/features/detection-signals/) and [IP Resolvers](/docs/sentinel/integrations/ip-resolvers/).

| Variable | Default | Purpose |
|---|---|---|
| `CLOUDFLARE_IP_COUNTRY_ENABLED` | *(empty)* | Use Cloudflare's `CF-IPCountry` header for geolocation. |
| `HIGH_RISK_COUNTRIES` | *(19 country codes, see below)* | Country codes flagged high-risk. |
| `IP_API_COM_TOKEN` / `IPINFO_IO_TOKEN` / `IPSTACK_COM_TOKEN` | *(empty)* | API tokens for third-party geolocation providers. |
| `IPINFO_IO_LITE_ENABLED` | *(empty)* | Use ipinfo.io's Lite API endpoint instead of the standard one. |
| `IPINFO_IO_MMDB_DOWNLOAD_URL` | *(empty)* | URL to download a local ipinfo.io MMDB database (local mode, instead of per-request API calls). |
| `IPINFO_IO_MMDB_DOWNLOAD_SCHEDULE` | `0 0 * * *` | Update schedule (cron) for the local ipinfo.io MMDB database. |
| `IPINFO_IO_MMDB_DOWNLOAD_HEADERS` | *(empty)* | Extra HTTP headers to send with the MMDB download request. |
| `MAXMIND_ACCOUNT_ID` / `MAXMIND_LICENSE_KEY` | *(empty)* | MaxMind GeoIP credentials. |
| `MAXMIND_DOWNLOAD_URL` | GeoLite2-City download URL | Download URL for the MaxMind database. |
| `MAXMIND_DOWNLOAD_SCHEDULE` | `0 0 * * *` | Update schedule (cron) for the MaxMind database. |
| `MAXMIND_DOWNLOAD_HEADERS` | *(empty)* | Extra HTTP headers to send with the MaxMind download request. |
| `PENALTY_TTL` | `30m` | How long an accumulated `penalty` score persists before resetting. |

`HIGH_RISK_COUNTRIES` defaults to `by,cd,cf,cu,il,iq,ir,kp,lb,ly,mm,ng,ru,sd,so,sy,ua,ve,zw`.

`HIGH_RISK_COUNTRIES_EXCLUDE` is deprecated — use `HIGH_RISK_COUNTRIES` directly instead.

## Lists (phishing, disposable email, user agents)

| Variable | Default | Purpose |
|---|---|---|
| `PHISHING_LIST_URL` | PhishTank's public feed | Source list for [Phishing Detection](/docs/sentinel/features/phishing-detection/) — see that page for the exact default URL. |
| `PHISHING_LIST_SCHEDULE` | `0 */12 * * *` | Update schedule (cron). |
| `PHISHING_LIST_SIZE_LIMIT` | `20MB` | Max file size for the downloaded list. |
| `PHISHING_LIST_MAX_ENTRIES` | `100000` | Max entries read from the list. |
| `EMAIL_LIST_DISPOSABLE` | GitHub `disposable-email-domains` list | Source list of disposable email domains. |
| `EMAIL_LIST_DISPOSABLE_SCHEDULE` | `0 0 * * *` | Update schedule (cron). |
| `EMAIL_LIST_DISPOSABLE_SIZE_LIMIT` | `5MB` | Max file size for the downloaded list. |
| `EMAIL_LIST_DISPOSABLE_MAX_ENTRIES` | `100000` | Max entries read from the list. |
| `USER_AGENT_LIST_URL` | `intoli/user-agents` dataset | Source list of known user agents. |
| `USER_AGENT_LIST_SCHEDULE` | `0 0 * * *` | Update schedule (cron). |
| `USER_AGENT_LIST_SIZE_LIMIT` | `20MB` | Max file size for the downloaded list. |
| `USER_AGENT_LIST_MAX_ENTRIES` | `10000` | Max entries read from the list. |

## Rate limiting & proof-of-work

See [Rate Limiters](/docs/sentinel/features/rate-limiters/).

| Variable | Default | Purpose |
|---|---|---|
| `FLOOD_RATE_LIMIT` | `100/1m` | Global flood-protection rate limit. Set empty to disable — useful when [benchmarking](/docs/sentinel/operations/performance-tuning/#benchmarks). |
| `FLOOD_RATE_LIMIT_KEY` | `ip` | Key used for flood-protection rate limiting. |
| `AUDIO_CHALLENGE_RATE_LIMIT` | `10/5m(key=ip&block=10m)` | Rate limit for the audio challenge endpoint. |
| `AUDIO_CHALLENGE_DELAY` | `3s` | Delay before generating/returning an audio challenge. |
| `POW_MAX_MEMORY` | `128MB` | Max memory PoW algorithms may request. |
| `POW_MAX_THREADS` | ~half of hardware concurrency | Max threads for challenge creation/validation. |

## Spam handling

| Variable | Default | Purpose |
|---|---|---|
| `DELETE_SPAM_SUBMISSIONS_IN` | `14d` | Auto-delete spam-flagged submissions after this duration. |

## Container resource limits

Covered in [Install with Docker](/docs/sentinel/install/docker/) and [Performance Tuning](/docs/sentinel/operations/performance-tuning/).

| Variable | Purpose |
|---|---|
| `CONTAINER_CPUS` | Manual override for available CPU cores (e.g. `2`). Auto-detected if unset. |
| `CONTAINER_MEMORY_LIMIT_MB` | Manual override for the memory limit (e.g. `4096`). Auto-detected if unset. |
| `HEAP_LIMIT_MB` | Manual override for heap memory — keep lower than `CONTAINER_MEMORY_LIMIT_MB`. |
