Environment Variables
Environment Variables
Section titled “Environment Variables”All Mantis components support environment variable configuration. A component’s own
prefixed keys (MANDIBLE__*, THORAX__*, TARSUS__*) override file-based settings.
The shared MANTIS_* keys do not: they are applied only where the file left the field
at its default, so a value set in the config file wins over them.
Naming Conventions
Section titled “Naming Conventions”Each component uses a different convention:
| Component | Convention | Example |
|---|---|---|
| Mandible | MANDIBLE__SECTION__KEY | MANDIBLE__JWT__SECRET |
| Thorax | THORAX__SECTION__KEY | THORAX__GRPC__PORT |
| Tarsus | TARSUS__SECTION__KEY | TARSUS__LISTEN__PORT |
| Shared | MANTIS_ or DATABASE_URL | MANTIS_TLS_CERT_PATH |
All components use double underscore (__) as the separator for nested configuration sections. Shared variables (MANTIS_*, DATABASE_URL) use single underscores.
Shared Variables
Section titled “Shared Variables”These environment variables are used by multiple components.
| Variable | Components | Description |
|---|---|---|
MANTIS_TLS_CERT_PATH | Thorax, Tarsus | TLS certificate PEM file |
MANTIS_TLS_KEY_PATH | Thorax, Tarsus | TLS private key PEM file |
MANTIS_TLS_CA_CERT_PATH | Thorax, Tarsus | CA certificate PEM file |
MANTIS_TLS_CRL_PATH | Thorax | Certificate revocation list PEM file. Revocation checking is off while unset |
MANTIS_TLS_CRL_URL | Thorax | Published CRL bundle URL. Revocation checking is off while both CRL keys are unset |
MANTIS_TLS_AUTH_MODE | Thorax | TLS auth mode: thumbprint or ca_signed |
MANTIS_ENCRYPTION_KEY | Mandible | Base64-encoded 32-byte encryption key. Thorax does not read it — it holds no credentials at rest |
MANTIS_LOG_LEVEL | Thorax, Tarsus | Log level override |
RABBITMQ_ENABLED | Thorax | Thorax-only: auto-enables cluster coordination when RabbitMQ is on (Mandible uses MANDIBLE__QUEUE__ENABLED) |
MANTIS_MAX_SYNC_DOCUMENTS | Mandible | Config-as-code: most documents one repository sync may evaluate. Default 500; a sync above the ceiling is rejected whole. Read once per process, so a change needs a restart. Not part of the MANDIBLE__ layering — it is a plain variable |
Mandible Variables
Section titled “Mandible Variables”Mandible uses the config crate with MANDIBLE__ prefix and double underscore separators.
General Pattern
Section titled “General Pattern”MANDIBLE__<SECTION>__<KEY>=valueThe database connection URL is conventionally set with MANDIBLE__DATABASE__URL. The unprefixed DATABASE_URL is also honored (and is what the Docker examples below use).
Common variables (not exhaustive)
Section titled “Common variables (not exhaustive)”This lists the most-used variables, not every
MANDIBLE__*key. Any field of the config structs can be set via itsMANDIBLE__<SECTION>__<FIELD>form — e.g. theSSE,AUDIT,OIDC,IDENTITY,CONSOLE,CLIENT_SESSION,INSTANCE_CLEANUP,DISPATCH,SERVER__TLS,GRPC__RETRY/CIRCUIT_BREAKER,RATE_LIMIT__AUTH/TRUSTED_PROXIES,COOKIE, andJWT__PREVIOUS_*sections, plus the top-levelENABLE_SWAGGER_UI.
| Variable | Default | Description |
|---|---|---|
MANDIBLE__SERVER__HOST | "0.0.0.0" | HTTP bind address |
MANDIBLE__SERVER__PORT | 3000 | HTTP port |
MANDIBLE__SERVER__REQUEST_TIMEOUT_SECS | 30 | Request timeout |
MANDIBLE__SERVER__EXTERNAL_URL | - | External URL for OAuth2 discovery |
MANDIBLE__SERVER__MAX_CONNECTIONS | half the file descriptor limit | API connections held open at once: a number or unlimited |
MANDIBLE__DATABASE__URL | - | PostgreSQL connection URL |
MANDIBLE__DATABASE__MAX_CONNECTIONS | 10 | Connection pool max size |
MANDIBLE__JWT__SECRET | - | HS256 signing secret |
MANDIBLE__SECURITY__TOKEN_LOOKUP_KEY | - | Keys the lookup that finds a stored API key or OAuth2 token. Required for RS256 and HS256 alike |
MANDIBLE__JWT__ALGORITHM | "HS256" | JWT algorithm |
MANDIBLE__JWT__PRIVATE_KEY_PATH | - | RS256 private key |
MANDIBLE__JWT__PUBLIC_KEY_PATH | - | RS256 public key |
MANDIBLE__JWT__KEY_ID | "k1" | Current key ID |
MANDIBLE__JWT__PREVIOUS_SECRET | - | Previous secret for rotation |
MANDIBLE__JWT__ACCESS_TOKEN_EXPIRY_SECS | 3600 | Access token lifetime |
MANDIBLE__JWT__REFRESH_TOKEN_EXPIRY_SECS | 2592000 | Refresh token lifetime |
MANDIBLE__CORS__ALLOWED_ORIGINS | localhost dev origins | Comma-separated origins |
MANDIBLE__CORS__ALLOW_CREDENTIALS | true | Allow credentials |
MANDIBLE__CORS__MAX_AGE_SECS | 3600 | Preflight cache duration |
MANDIBLE__COOKIE__SECURE | true (release) | HTTPS-only cookies |
MANDIBLE__COOKIE__SAME_SITE | "Lax" | SameSite policy |
MANDIBLE__AUTH__LENS_BASE_URL | "https://localhost:5173/login" | Lens UI login URL |
MANDIBLE__GRPC__ORCHESTRATION_URL | "https://localhost:50051" | Thorax gRPC URL |
MANDIBLE__GRPC__TLS_CA_CERT_PATH | - | gRPC CA cert path (HTTPS URLs) |
MANDIBLE__GRPC__TLS_CERT_PATH | - | gRPC client cert path (mTLS) |
MANDIBLE__GRPC__TLS_KEY_PATH | - | gRPC client key path (mTLS) |
MANDIBLE__INTERNAL_GRPC__ENABLED | false | Enable internal gRPC server |
MANDIBLE__INTERNAL_GRPC__HOST | "0.0.0.0" | Internal gRPC bind address |
MANDIBLE__INTERNAL_GRPC__PORT | 50052 | Internal gRPC port |
MANDIBLE__INTERNAL_GRPC__TLS_CERT_PATH | - | Internal gRPC server cert |
MANDIBLE__INTERNAL_GRPC__TLS_KEY_PATH | - | Internal gRPC server key |
MANDIBLE__INTERNAL_GRPC__TLS_CA_CERT_PATH | - | Internal gRPC CA cert |
MANDIBLE__RATE_LIMIT__ENABLED | true | Enable rate limiting |
MANDIBLE__RATE_LIMIT__REQUESTS_PER_SECOND | 100 | Max requests per second |
MANDIBLE__RATE_LIMIT__BURST_SIZE | 200 | Burst size |
MANDIBLE__ENCRYPTION__KEY | - | Base64-encoded 32-byte encryption key |
SSE_REDIS_URL | "redis://127.0.0.1:6379" | SSE Redis URL |
MANDIBLE__QUEUE__ENABLED | false | Enable RabbitMQ |
MANDIBLE__QUEUE__HOST | "localhost" | RabbitMQ host |
MANDIBLE__QUEUE__PORT | 5671 | RabbitMQ AMQPS port |
MANDIBLE__QUEUE__USERNAME | "" | RabbitMQ username |
MANDIBLE__QUEUE__PASSWORD | "" | RabbitMQ password |
MANDIBLE__QUEUE__VHOST | "/" | RabbitMQ vhost |
MANDIBLE__QUEUE__CA_CERT_PATH | - | RabbitMQ CA cert |
MANDIBLE__QUEUE__FALLBACK_ON_FAILURE | true | Fall back to polling |
MANDIBLE__QUEUE__HEALTH_CHECK_INTERVAL_SECS | 10 | Health check interval |
MANDIBLE__FILE_CACHE__ENABLED | true | Enable file cache |
MANDIBLE__FILE_CACHE__MEMORY_MAX_SIZE_BYTES | 104857600 | Cache max size (100 MB) |
MANDIBLE__DISPATCH__COMMAND_PICKUP_TIMEOUT_SECS | 600 | A pending command expires after this long when its agent has made no contact for as long. A busy agent’s queued commands keep waiting |
MANDIBLE__DISPATCH__BUSY_TARGET_MAX_WAIT_SECS | 86400 | How long a deployment may be queued before a target another deployment holds fails it |
MANDIBLE__METRICS__BIND_ADDR | "127.0.0.1:9091" | Prometheus /metrics listener; "" disables it. A bind beyond loopback requires MANDIBLE__METRICS__AUTH_TOKEN |
MANDIBLE__METRICS__AUTH_TOKEN | - | Bearer token /metrics requires (at least 32 visible ASCII characters) |
MANDIBLE__LOG_ANALYZER__PROVIDER_TYPE | "ollama" | LLM provider: ollama or openai |
MANDIBLE__LOG_ANALYZER__API_URL | "http://ollama:11434" | LLM provider API URL |
MANDIBLE__LOG_ANALYZER__MODEL | "granite4" | Model name |
Thorax Variables
Section titled “Thorax Variables”As with Mandible, this lists the most-used keys, not every
THORAX__*key. Any field of the config structs can be set via itsTHORAX__<SECTION>__<FIELD>form.
Thorax loads its configuration in layers: the config file first, then a THORAX__ environment layer that uses double underscores between nested sections, then the unprefixed MANTIS_* keys shared with Tarsus. A THORAX__* value wins over the MANTIS_* equivalent.
| Variable | Default | Description |
|---|---|---|
THORAX_CONFIG | - | Path to config file |
THORAX__NAME | hostname-derived | Server name |
MANTIS_LOG_LEVEL | "info" | Log level |
THORAX__GRPC__ADDRESS | "0.0.0.0" | gRPC bind address |
THORAX__GRPC__PORT | 50051 | gRPC port |
THORAX__GRPC__ENROLLMENT_PORT | - | Enrollment port (ca_signed) |
THORAX__METRICS__BIND_ADDR | "127.0.0.1:9090" | Bind address of the HTTP listener (plaintext unless THORAX__METRICS__TLS__* is set) serving /metrics, /health and the WireGuard relay WebSocket. A bind beyond loopback requires THORAX__METRICS__AUTH_TOKEN; put a proxy or a management interface in front |
THORAX__METRICS__AUTH_TOKEN | - | Bearer token /metrics and /health require (at least 32 visible ASCII characters); the relay does not take it |
THORAX__METRICS__TLS__CERT_PATH | - | Certificate that listener serves over TLS; set with the key |
THORAX__METRICS__TLS__KEY_PATH | - | Its private key |
MANTIS_TLS_CERT_PATH | - | Server TLS certificate |
MANTIS_TLS_KEY_PATH | - | Server TLS private key |
MANTIS_TLS_CA_CERT_PATH | - | CA certificate |
MANTIS_TLS_CRL_URL | - | Published CRL bundle URL, fetched at startup and refreshed periodically (ca_signed only) |
MANTIS_TLS_CRL_PATH | - | Certificate revocation list. Revocation checking is off while unset |
MANTIS_TLS_AUTH_MODE | "thumbprint" | TLS auth mode |
THORAX__CONSOLE__ENABLED | false | Enable tokio-console |
THORAX__CONSOLE__ADDRESS | "127.0.0.1" | Console bind address |
THORAX__CONSOLE__PORT | 0 (rejected at startup) | Console port. Thorax has no compiled-in default and refuses to start on 0 once ENABLED is set, so 6669 (the convention) must be given explicitly here or in thorax.toml. |
THORAX__CONSOLE__RETENTION_SECS | 60 | Console data retention |
THORAX__CONSOLE__PUBLISH_INTERVAL_MS | 1000 | Console publish interval |
THORAX__CONSOLE__RECORDING_PATH | - | Console recording path |
THORAX__MANDIBLE__ENDPOINT | "https://localhost:50052" | Mandible gRPC endpoint (required) |
THORAX__MANDIBLE__CONNECT_TIMEOUT_SECS | 10 | Mandible connect timeout |
THORAX__MANDIBLE__REQUEST_TIMEOUT_SECS | 30 | Mandible request timeout |
THORAX__MANDIBLE__TLS_CERT_PATH | - | Mandible client cert |
THORAX__MANDIBLE__TLS_KEY_PATH | - | Mandible client key |
THORAX__MANDIBLE__TLS_CA_CERT_PATH | - | Mandible CA cert |
THORAX__MANDIBLE__INSTANCE_NAME | thorax-<hostname> | Name this instance registers with Mandible under. Follows THORAX__CLUSTER__INSTANCE_ID when that is set, so two instances on one host do not collide |
RABBITMQ_ENABLED | false | Thorax-only: auto-enables cluster coordination unless THORAX__CLUSTER__ENABLED is set. The guard is the env var, not the file — an enabled = false under [cluster] in thorax.toml does not suppress it. Does not enable the queue — use THORAX__QUEUE__ENABLED for that. |
THORAX__QUEUE__ENABLED | false | Queue enabled (explicit) |
THORAX__QUEUE__HOST | "localhost" | RabbitMQ host |
THORAX__QUEUE__PORT | 5671 | RabbitMQ port |
THORAX__QUEUE__USERNAME | "" | RabbitMQ username |
THORAX__QUEUE__PASSWORD | "" | RabbitMQ password |
THORAX__QUEUE__VHOST | "/" | RabbitMQ vhost |
THORAX__QUEUE__CA_CERT_PATH | - | RabbitMQ CA cert |
THORAX__QUEUE__CLIENT_CERT_PATH | - | RabbitMQ client cert |
THORAX__QUEUE__CLIENT_KEY_PATH | - | RabbitMQ client key |
THORAX__QUEUE__FALLBACK_ON_FAILURE | true | Fall back to polling |
THORAX__QUEUE__RECONNECT_DELAY_SECS | 5 | Reconnect delay |
THORAX__CLUSTER__ENABLED | false | Cluster enabled (explicit) |
THORAX__CLUSTER__REDIS_URL | "redis://localhost:6379" | Redis URL |
THORAX__CLUSTER__HEARTBEAT_INTERVAL_SECS | 10 | Cluster heartbeat |
THORAX__CLUSTER__LIVENESS_TIMEOUT_SECS | 30 | Liveness timeout |
THORAX__CLUSTER__NAMESPACE | "thorax" | Redis key namespace |
THORAX__WIREGUARD__ENABLED | false | Enable the WireGuard overlay controller (guide) |
THORAX__WIREGUARD__OVERLAY_IP | "10.99.0.1" | Controller’s overlay address |
THORAX__WIREGUARD__OVERLAY_PREFIX_LEN | 16 | Overlay subnet prefix length |
THORAX__WIREGUARD__UDP_BIND_ADDR | "0.0.0.0:51820" | WireGuard data-plane UDP bind |
THORAX__WIREGUARD__UDP_ADVERTISE_ADDR | (bind addr) | Publicly reachable UDP endpoint advertised to agents |
THORAX__WIREGUARD__PRIVATE_KEY_PATH | /var/lib/mantis/thorax-wireguard.key | Persisted controller keypair path |
Tarsus Variables
Section titled “Tarsus Variables”Tarsus loads its configuration the same way Thorax does: the config file first, then a TARSUS__ environment layer that uses double underscores between nested sections, then the unprefixed MANTIS_* keys. A TARSUS__* value wins over the MANTIS_* equivalent.
An unrecognised TARSUS__* key is a startup error rather than a silently ignored default, and so is a single-underscore spelling of one — a typo stops the agent instead of leaving it on the default you meant to change.
| Variable | Default | Description |
|---|---|---|
TARSUS_CONFIG | - | Path to config file |
TARSUS__NAME | hostname | Client name |
TARSUS__MODE | "listen" | Operating mode: listen or poll |
TARSUS__LOG_LEVEL | "info" | Log level |
TARSUS__DATA_DIR | platform data dir | Data directory |
TARSUS__MANDIBLE__ENDPOINT | "localhost:50052" | Mandible internal gRPC endpoint for registration (host:port) — typically port 50052, not the REST API port 3000 |
TARSUS__LISTEN__ADDRESS | "0.0.0.0" | Listen bind address |
TARSUS__LISTEN__PORT | 9342 | Listen port |
TARSUS__POLL__INTERVAL_SECS | 30 | Poll interval (seconds) |
TARSUS__REGISTRATION__HEARTBEAT_INTERVAL_SECS | 30 | Heartbeat interval (seconds) |
TARSUS__REGISTRATION__TOKEN | - | Registration token |
MANTIS_TLS_CERT_PATH | - | Client TLS certificate |
MANTIS_TLS_KEY_PATH | - | Client TLS private key |
MANTIS_TLS_CA_CERT_PATH | - | CA certificate |
TARSUS__TLS__ENROLLMENT_ADDRESS | - | CSR enrollment address (host:port) |
TARSUS__CONSOLE__ENABLED | false | Enable tokio-console |
TARSUS__CONSOLE__ADDRESS | "127.0.0.1" | Console bind address |
TARSUS__CONSOLE__PORT | 6671 | Console port. 6671 applies only while no console key is set at all; setting ENABLED (or any other CONSOLE key) without PORT leaves the port at its own default 0, an ephemeral bind. Set both together. |
TARSUS__CONSOLE__RETENTION_SECS | 60 | Console data retention |
TARSUS__CONSOLE__PUBLISH_INTERVAL_MS | 1000 | Console publish interval |
TARSUS__CONSOLE__RECORDING_PATH | - | Console recording path |
TARSUS__TRANSPORT__ENABLED | false | Enable WireGuard transport |
TARSUS__TRANSPORT__PRIVATE_KEY_PATH | - | WireGuard private key path |
TARSUS__TRANSPORT__KEY_ROTATION_INTERVAL_SECS | 0 | WireGuard key rotation interval (seconds; 0 disables) |
Lens Variables
Section titled “Lens Variables”The Lens UI server reads plain environment variables, with no prefix and no config file.
| Variable | Default | Description |
|---|---|---|
MANTIS_API_URL | required in the released image; https://localhost:3000 in a development build | Mandible’s REST API origin, which the UI calls and its Content-Security-Policy allows |
HOST | "0.0.0.0" | Bind address |
PORT | 4173 | Bind port |
TLS_CERT_PATH | /etc/mantis/certs/lens-cert.pem | TLS certificate |
TLS_KEY_PATH | /etc/mantis/certs/lens-key.pem | TLS private key |
LENS_ALLOW_PLAINTEXT | unset | 1, true, yes or on serves plaintext when neither TLS file exists or is named; 0, false, no, off, empty or unset does not, and any other value stops startup |
LENS_UI_DIR | lens/ui/build in the source tree | Directory a development build serves the UI from; the released image embeds the UI and ignores it |
Docker / Compose Examples
Section titled “Docker / Compose Examples”Mandible
Section titled “Mandible”environment: DATABASE_URL: 'postgres://mantis:secret@db:5432/mantis' MANDIBLE__JWT__SECRET: '${JWT_SECRET}' MANDIBLE__SECURITY__TOKEN_LOOKUP_KEY: '${TOKEN_LOOKUP_KEY}' MANDIBLE__CORS__ALLOWED_ORIGINS: 'https://lens.example.com' MANDIBLE__GRPC__ORCHESTRATION_URL: 'https://thorax:50051' MANDIBLE__INTERNAL_GRPC__ENABLED: 'true' MANDIBLE__QUEUE__ENABLED: 'true' MANDIBLE__QUEUE__HOST: 'rabbitmq' MANDIBLE__QUEUE__USERNAME: 'mantis' MANDIBLE__QUEUE__PASSWORD: '${RABBITMQ_PASSWORD}' MANDIBLE__QUEUE__CA_CERT_PATH: '/certs/ca.crt' MANDIBLE__QUEUE__CLIENT_CERT_PATH: '/certs/rabbitmq-client-cert.pem' MANDIBLE__QUEUE__CLIENT_KEY_PATH: '/certs/rabbitmq-client-key.pem' MANDIBLE__ENCRYPTION__KEY: '${ENCRYPTION_KEY}'Thorax
Section titled “Thorax”environment: MANTIS_TLS_CERT_PATH: '/certs/server.crt' MANTIS_TLS_KEY_PATH: '/certs/server.key' MANTIS_TLS_CA_CERT_PATH: '/certs/ca.crt' THORAX__MANDIBLE__ENDPOINT: 'https://mandible:50052' RABBITMQ_ENABLED: 'true' # cluster coordination only THORAX__QUEUE__ENABLED: 'true' THORAX__QUEUE__HOST: 'rabbitmq' THORAX__QUEUE__USERNAME: 'mantis' THORAX__QUEUE__PASSWORD: '${RABBITMQ_PASSWORD}' THORAX__QUEUE__CA_CERT_PATH: '/certs/ca.crt' THORAX__QUEUE__CLIENT_CERT_PATH: '/certs/thorax-1-cert.pem' THORAX__QUEUE__CLIENT_KEY_PATH: '/certs/thorax-1-key.pem' THORAX__CLUSTER__REDIS_URL: 'redis://redis:6379'Tarsus
Section titled “Tarsus”environment: TARSUS__MODE: 'listen' TARSUS__MANDIBLE__ENDPOINT: 'mandible:50052' MANTIS_TLS_CA_CERT_PATH: '/certs/ca.crt' TARSUS__REGISTRATION__TOKEN: '${REGISTRATION_TOKEN}'Next Steps
Section titled “Next Steps”- Mandible Configuration - Full Mandible config reference
- Thorax Configuration - Full Thorax config reference
- Tarsus Configuration - Full Tarsus config reference
