Mandible Configuration
Mandible Configuration
Section titled “Mandible Configuration”Configure the Mandible REST API server for your deployment.
Configuration File
Section titled “Configuration File”MANDIBLE_CONFIG names an explicit config file (required to exist if set). Otherwise
Mandible layers every file it finds, key by key, with later sources overriding
earlier ones: a file named mandible (e.g. mandible.toml) in the current directory,
then <config_dir>/mantis/mandible, then /etc/mantis/mandible — so /etc/mantis has
the highest file precedence. Settings can be overridden with environment
variables using the MANDIBLE__ prefix and double-underscore separators — with
one exception: log_analyzer.enabled is #[serde(skip)] and comes from the
database (analysis_settings), not from the file or an env var.
Configuration Sections
Section titled “Configuration Sections”[server]
Section titled “[server]”HTTP server settings.
[server]host = "0.0.0.0"port = 3000request_timeout_secs = 30external_url = "https://api.mantis.example.com"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
host | string | "0.0.0.0" | No | IP address to bind |
port | integer | 3000 | No | HTTP port |
request_timeout_secs | integer | 30 | No | Request timeout in seconds |
external_url | string | - | No | External URL for OAuth2 discovery endpoints. If not set, derived from host:port |
max_connections | integer or "unlimited" | half the file descriptor limit | No | API connections held open at once, SSE streams included; one past it is closed as it is accepted |
Each Mantis server raises its file descriptor limit to the hard limit at startup, and an
HTTP listener holds at most half of it unless told otherwise, so its clients can never take
the descriptors its database pool, queues and outbound calls need. The value in force is
logged at startup as max_connections.
Windows sets no descriptor limit to take half of, so there the default is "unlimited".
Set max_connections on a Windows host to the most API connections it should hold at
once, with headroom: every open Lens tab holds one event stream.
[server.tls]
Section titled “[server.tls]”Optional TLS configuration for direct HTTPS termination. Leave unconfigured if using a reverse proxy for TLS.
[server.tls]cert_path = "/etc/mantis/certs/server.crt"key_path = "/etc/mantis/certs/server.key"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
cert_path | string | - | Yes (if TLS enabled) | Path to TLS certificate PEM file |
key_path | string | - | Yes (if TLS enabled) | Path to TLS private key PEM file |
[database]
Section titled “[database]”PostgreSQL connection settings.
[database]url = "postgres://mantis:password@localhost:5432/mantis"max_connections = 10min_idle = 2connection_timeout_secs = 10| Field | Type | Default | Required | Description |
|---|---|---|---|---|
url | string | DATABASE_URL env var | Yes | PostgreSQL connection URL. Startup fails with a config error if not set |
max_connections | integer | 10 | No | Maximum connection pool size |
min_idle | integer | 2 | No | Minimum idle connections |
connection_timeout_secs | integer | 10 | No | Connection timeout in seconds |
JWT authentication token configuration. Supports HS256 (symmetric) and RS256 (asymmetric) algorithms.
[jwt]algorithm = "HS256"secret = "your-256-bit-secret-key-at-least-32-characters-long"access_token_expiry_secs = 3600refresh_token_expiry_secs = 2592000issuer = "https://mantis.example.com"audience = "lens"key_id = "k1"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
algorithm | string | "HS256" | No | Signing algorithm: HS256 or RS256 |
secret | string | "" | Yes (HS256) | JWT signing secret (min 32 characters). Set via MANDIBLE__JWT__SECRET |
private_key_path | string | - | Yes (RS256) | Path to RSA private key PEM file |
public_key_path | string | - | Yes (RS256) | Path to RSA public key PEM file |
key_id | string | "k1" | No | Key ID for the current signing key (for key rotation) |
previous_secret | string | - | No | Previous secret for graceful HS256 key rotation |
previous_public_key_path | string | - | No | Previous public key path for graceful RS256 key rotation |
previous_key_id | string | - | No | Key ID for the previous key |
issuer | string | server.base_url() | No | JWT issuer claim. RFC 8414 requires it to equal the issuer OAuth discovery publishes, so the placeholder "mandible" — including when set explicitly — is replaced with the server’s external URL. Set any other value to keep it verbatim. |
audience | string | "lens" | No | JWT audience claim |
access_token_expiry_secs | integer | 3600 | No | Access token lifetime (1 hour) |
refresh_token_expiry_secs | integer | 2592000 | No | Refresh token lifetime (30 days) |
[security]
Section titled “[security]”[security]# token_lookup_key = "" # Set via MANDIBLE__SECURITY__TOKEN_LOOKUP_KEY| Setting | Type | Default | Required | Description |
|---|---|---|---|---|
token_lookup_key | string | "" | Yes | Keys the HMAC that finds a stored API key or OAuth2 token without storing the token. At least 32 characters. Required whichever JWT algorithm is in use. |
[cors]
Section titled “[cors]”Cross-Origin Resource Sharing settings.
[cors]allowed_origins = ["https://lens.example.com"]allowed_methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]allowed_headers = ["Content-Type", "Authorization", "Accept", "X-Request-ID"]allow_credentials = truemax_age_secs = 3600| Field | Type | Default | Required | Description |
|---|---|---|---|---|
allowed_origins | array | ["https://localhost:5173", "http://localhost:5173", "https://localhost:4173", "http://localhost:4173"] | No | Allowed origins |
allowed_methods | array | ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] | No | Allowed HTTP methods |
allowed_headers | array | ["Content-Type", "Authorization", "Accept", "X-Request-ID"] | No | Allowed headers |
allow_credentials | boolean | true | No | Whether to allow credentials |
max_age_secs | integer | 3600 | No | Preflight cache duration in seconds |
[cookie]
Section titled “[cookie]”Cookie settings for authentication refresh tokens.
[cookie]refresh_token_name = "mantis_refresh_token"secure = truesame_site = "Lax"path = "/"domain = ".example.com"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
refresh_token_name | string | "mantis_refresh_token" | No | Cookie name for the refresh token |
secure | boolean | true (release), false (debug) | No | HTTPS-only cookies |
same_site | string | "Lax" | No | SameSite policy: Strict, Lax, or None |
path | string | "/" | No | Cookie path scope |
domain | string | - | No | Cookie domain. If not set, defaults to the origin domain |
[auth]
Section titled “[auth]”Authentication redirect settings.
[auth]lens_base_url = "https://lens.example.com/login"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
lens_base_url | string | "https://localhost:5173/login" | No | Lens UI login URL for redirects. Must be a valid HTTP/HTTPS URL |
[grpc]
Section titled “[grpc]”gRPC client settings for connecting to Thorax.
[grpc]orchestration_url = "https://thorax.example.com:50051"connect_timeout_secs = 5request_timeout_secs = 300tls_ca_cert_path = "/etc/mantis/certs/ca.crt"tls_cert_path = "/etc/mantis/certs/client.crt"tls_key_path = "/etc/mantis/certs/client.key"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
orchestration_url | string | "https://localhost:50051" | No | Thorax gRPC URL. Env: MANDIBLE__GRPC__ORCHESTRATION_URL |
connect_timeout_secs | integer | 5 | No | Connection timeout; at most 86400 |
request_timeout_secs | integer | 300 | No | Request timeout (5 minutes for long-running operations); at most 86400 |
tls_ca_cert_path | string | - | No | CA certificate for TLS verification. Env: MANDIBLE__GRPC__TLS_CA_CERT_PATH |
tls_cert_path | string | - | No | Client certificate for mTLS. Env: MANDIBLE__GRPC__TLS_CERT_PATH |
tls_key_path | string | - | No | Client private key for mTLS. Env: MANDIBLE__GRPC__TLS_KEY_PATH |
[grpc.retry]
Section titled “[grpc.retry]”Retry configuration for transient gRPC failures. Only read-only operations are retried.
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable retry for read-only operations |
max_retries | integer | 3 | No | Maximum retry attempts |
initial_delay_ms | integer | 100 | No | Initial delay before first retry (ms) |
max_delay_ms | integer | 5000 | No | Maximum delay between retries (ms) |
[grpc.circuit_breaker]
Section titled “[grpc.circuit_breaker]”Circuit breaker to prevent cascading failures.
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable circuit breaker |
failure_threshold | integer | 5 | No | Consecutive failures before circuit opens |
success_threshold | integer | 2 | No | Successes in half-open state before closing |
cooldown_secs | integer | 30 | No | How long circuit stays open (seconds) |
[internal_grpc]
Section titled “[internal_grpc]”Internal gRPC server for Thorax-to-Mandible communication. Uses mTLS for authentication.
[internal_grpc]enabled = truehost = "0.0.0.0"port = 50052tls_cert_path = "/etc/mantis/certs/internal.crt"tls_key_path = "/etc/mantis/certs/internal.key"tls_ca_cert_path = "/etc/mantis/certs/ca.crt"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Enable the internal gRPC server. Also reads MANDIBLE__INTERNAL_GRPC__ENABLED |
host | string | "0.0.0.0" | No | Bind address. Also reads MANDIBLE__INTERNAL_GRPC__HOST |
port | integer | 50052 | No | Listen port. Also reads MANDIBLE__INTERNAL_GRPC__PORT |
tls_cert_path | string | - | Yes (if enabled) | Server TLS certificate. Also reads MANDIBLE__INTERNAL_GRPC__TLS_CERT_PATH |
tls_key_path | string | - | Yes (if enabled) | Server TLS private key. Also reads MANDIBLE__INTERNAL_GRPC__TLS_KEY_PATH |
tls_ca_cert_path | string | - | Yes (if enabled) | CA certificate for client verification. Also reads MANDIBLE__INTERNAL_GRPC__TLS_CA_CERT_PATH |
request_timeout_secs | integer | 300 | No | Request timeout (seconds) |
max_concurrent_requests_per_connection | integer | 100 | No | Per-connection in-flight request limit (tonic’s concurrency limit). The number of connections itself is not bounded here |
[rate_limit]
Section titled “[rate_limit]”API rate limiting settings.
[rate_limit]enabled = truerequests_per_second = 100burst_size = 200| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable rate limiting |
requests_per_second | integer | 100 | No | Max requests per second per IP |
burst_size | integer | 200 | No | Burst size (max instant requests) |
[rate_limit.auth]
Section titled “[rate_limit.auth]”Authentication-specific rate limiting for brute force protection.
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable auth rate limiting |
login_attempts_per_minute | integer | 10 | No | Max failed login attempts per minute per IP |
lockout_duration_secs | integer | 900 | No | Lockout duration after exceeding limit (15 minutes) |
[rate_limit.trusted_proxies]
Section titled “[rate_limit.trusted_proxies]”Trusted reverse proxy configuration for X-Forwarded-For header processing.
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Trust X-Forwarded-For from configured proxies |
addresses | array | [] | No | Trusted proxy IP addresses or CIDR ranges |
[identity]
Section titled “[identity]”Server identity for the /server/info discovery endpoint.
[identity]server_name = "Mandible Server"server_cert_path = "/etc/mantis/certs/server.crt"registration_enabled = true| Field | Type | Default | Required | Description |
|---|---|---|---|---|
server_name | string | "Mandible Server" | No | Human-readable server name |
server_cert_path | string | - | No | Path to server TLS certificate for thumbprint calculation |
server_thumbprint | string | - | No | Pre-calculated server thumbprint (SHA256 hex) |
registration_enabled | boolean | true | No | Whether new client registration is enabled |
[certificate_authority]
Section titled “[certificate_authority]”The platform root CA. Mandible imports it on startup when both paths are set and no root is stored yet.
Mandible signs every agent certificate, so this is required for CSR enrollment. Thorax forwards a CSR here rather than signing it, which is what keeps CA private keys off the Thorax fleet.
[certificate_authority]cert_path = "/etc/mantis/certs/ca-cert.pem"key_path = "/etc/mantis/certs/ca-key.pem"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
cert_path | string | - | Yes, for CA-signed enrollment | Path to the platform root CA certificate |
key_path | string | - | Yes, for CA-signed enrollment | Path to the platform root CA private key |
The private key is encrypted at rest once imported, and the files are read only on the import that stores it.
Use mantisctl cert import-ca instead of these paths to import the root without
the key ever sitting in Mandible’s configuration. Either route is fine — what
matters is that a root exists. Until one does, CSR enrollment fails with
No certificate authority is configured, and tenant certificate authorities
cannot be created.
[console]
Section titled “[console]”Tokio-console settings for async runtime debugging. Only active when the tokio-console feature is enabled.
[console]enabled = trueaddress = "127.0.0.1"port = 6670retention_secs = 60publish_interval_ms = 1000| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Enable the tokio-console server |
address | string | "127.0.0.1" | No | Bind address |
port | integer | 6670 | No | Console port. An absent [console] table yields 6670; the sentinel 0 (OS-assigned ephemeral port) applies only when the table is present and port alone is omitted. Thorax differs — there an absent table yields 0. |
retention_secs | integer | 60 | No | How long to retain completed task data |
publish_interval_ms | integer | 1000 | No | Update interval for connected clients (ms) |
recording_path | string | - | No | Path to record console data for offline analysis |
[metrics]
Section titled “[metrics]”The Prometheus /metrics listener, on its own port so the REST API stays metrics-free.
[metrics]bind_addr = "127.0.0.1:9091"auth_token = "<openssl rand -hex 32>"
[metrics.tls]cert_path = "/etc/mantis/certs/mandible-cert.pem"key_path = "/etc/mantis/certs/mandible-key.pem"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
bind_addr | string | 127.0.0.1:9091 | No | host:port the listener binds to; "" disables it. Env: MANDIBLE__METRICS__BIND_ADDR |
auth_token | string | - | When bind_addr is not loopback | Bearer token a scraper presents in Authorization; at least 32 visible ASCII characters. Env: MANDIBLE__METRICS__AUTH_TOKEN |
tls.cert_path | string | - | With tls | Certificate the listener serves |
tls.key_path | string | - | With tls | Its private key |
tls.client_ca_cert_path | string | - | No | CA scrapers’ client certificates are verified against; set, it requires one |
Once a token is set the listener answers 401 without it, and Mandible refuses to start
with a bind beyond loopback and no token.
[audit]
Section titled “[audit]”Audit logging configuration with per-tenant cryptographic hash chains.
[audit]enabled = truebatch_size = 100flush_interval_secs = 1channel_buffer = 10000max_retries = 3| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable audit logging |
batch_size | integer | 100 | No | Maximum batch size before forced flush |
flush_interval_secs | integer | 1 | No | Maximum time between flushes (seconds) |
channel_buffer | integer | 10000 | No | Async channel buffer size |
max_retries | integer | 3 | No | Maximum total attempts for database writes |
dead_letter_path | string | - | No | Path for dead-letter queue file (failed entries) |
Server-Sent Events session and token configuration.
[sse]token_ttl_secs = 60session_ttl_secs = 3600audience = "mantis-sse"redis_url = "redis://127.0.0.1:6379"redis_namespace = "default"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
token_ttl_secs | integer | 60 | No | SSE token time-to-live (single-use, short-lived) |
session_ttl_secs | integer | 3600 | No | SSE session time-to-live (1 hour) |
audience | string | "mantis-sse" | No | SSE token audience claim |
redis_url | string | "redis://127.0.0.1:6379" | No | Redis URL for SSE state. Also reads SSE_REDIS_URL env var |
redis_namespace | string | "default" | No | Redis key namespace |
[queue]
Section titled “[queue]”RabbitMQ queue settings for command dispatch. TLS is mandatory.
[queue]enabled = truehost = "rabbitmq.internal"port = 5671username = "mantis"password = "secret"vhost = "/mantis"ca_cert_path = "/etc/mantis/certs/rabbitmq-ca.crt"prefetch_count = 10publisher_confirms = truefallback_on_failure = truehealth_check_interval_secs = 10| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Enable RabbitMQ dispatch. Also reads MANDIBLE__QUEUE__ENABLED |
host | string | "localhost" | No | RabbitMQ host. Also reads MANDIBLE__QUEUE__HOST |
port | integer | 5671 | No | AMQPS port (TLS only). Also reads MANDIBLE__QUEUE__PORT |
username | string | "" | No | RabbitMQ username. Also reads MANDIBLE__QUEUE__USERNAME |
password | string | "" | No | RabbitMQ password. Also reads MANDIBLE__QUEUE__PASSWORD |
vhost | string | "/" | No | Virtual host. Also reads MANDIBLE__QUEUE__VHOST |
ca_cert_path | string | - | Yes (if enabled) | CA certificate for TLS. Also reads MANDIBLE__QUEUE__CA_CERT_PATH |
client_cert_path | string | - | No | Client certificate for mTLS |
client_key_path | string | - | No | Client private key for mTLS |
prefetch_count | integer | 10 | No | Consumer prefetch count |
publisher_confirms | boolean | true | No | Enable publisher confirms for guaranteed delivery |
fallback_on_failure | boolean | true | No | Fall back to polling when RabbitMQ is unavailable. Also reads MANDIBLE__QUEUE__FALLBACK_ON_FAILURE |
health_check_interval_secs | integer | 10 | No | Health check interval (seconds). Also reads MANDIBLE__QUEUE__HEALTH_CHECK_INTERVAL_SECS |
[oidc]
Section titled “[oidc]”OIDC/SSO settings for redirect URL validation.
[oidc]allowed_redirect_urls = ["https://app.example.com"]allowed_redirect_domains = ["*.example.com"]allow_localhost_redirect = falseallow_any_redirect = false| Field | Type | Default | Required | Description |
|---|---|---|---|---|
allowed_redirect_urls | array | [] | No | Explicit list of allowed redirect URLs |
allowed_redirect_domains | array | [] | No | Allowed domains (supports *.example.com wildcards) |
allow_localhost_redirect | boolean | true (debug), false (release) | No | Allow redirects to localhost |
allow_any_redirect | boolean | true (debug), false (release) | No | Allow any redirect when no allowlist configured |
[encryption]
Section titled “[encryption]”Encryption settings for credential protection at rest (AES-256-GCM).
[encryption]key = "base64-encoded-32-byte-key"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
key | string | - | Yes | Base64-encoded 32-byte encryption key. Also reads MANTIS_ENCRYPTION_KEY or MANDIBLE__ENCRYPTION__KEY env var. Generate with: openssl rand -base64 32 |
[file_cache]
Section titled “[file_cache]”File cache settings for S3/Git storage content caching.
[file_cache]enabled = truememory_max_size_bytes = 104857600| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | true | No | Enable file caching. Also reads MANDIBLE__FILE_CACHE__ENABLED |
memory_max_size_bytes | integer | 104857600 (100 MB) | No | Byte budget for the in-memory file cache; entries are admitted until their combined size reaches it. Also reads MANDIBLE__FILE_CACHE__MEMORY_MAX_SIZE_BYTES |
[log_analyzer]
Section titled “[log_analyzer]”AI log analyzer for automated deployment failure analysis using LLM providers (Ollama, OpenAI-compatible).
[log_analyzer]provider_type = "ollama"api_url = "http://ollama:11434"model = "granite4"timeout_secs = 60check_interval_secs = 5batch_size = 10max_input_length = 8192temperature = 0.1top_p = 0.9max_tokens = 768| Field | Type | Default | Required | Description |
|---|---|---|---|---|
provider_type | string | "ollama" | No | LLM provider type: ollama or openai. Env: MANDIBLE__LOG_ANALYZER__PROVIDER_TYPE |
api_url | string | "http://ollama:11434" | No | LLM provider API URL. Also reads MANDIBLE__LOG_ANALYZER__API_URL |
model | string | "granite4" | No | Model name. Also reads MANDIBLE__LOG_ANALYZER__MODEL |
timeout_secs | integer | 60 | No | Request timeout. Also reads MANDIBLE__LOG_ANALYZER__TIMEOUT_SECS |
check_interval_secs | integer | 5 | No | Interval between processing batches. Also reads MANDIBLE__LOG_ANALYZER__CHECK_INTERVAL_SECS |
batch_size | integer | 10 | No | Max requests per batch. Also reads MANDIBLE__LOG_ANALYZER__BATCH_SIZE |
max_input_length | integer | 8192 | No | Max log text input length (chars). Also reads MANDIBLE__LOG_ANALYZER__MAX_INPUT_LENGTH |
temperature | float | 0.1 | No | LLM temperature. Also reads MANDIBLE__LOG_ANALYZER__TEMPERATURE |
top_p | float | 0.9 | No | Nucleus sampling threshold. Also reads MANDIBLE__LOG_ANALYZER__TOP_P |
max_tokens | integer | none (uncapped) | No | Max tokens to generate; unset means uncapped (reasoning models need headroom) — 768 is only this example’s value. Also reads MANDIBLE__LOG_ANALYZER__MAX_TOKENS |
tls_ca_cert_path | string | - | No | Path to custom CA certificate for TLS verification when connecting to LLM provider |
[wireguard]
Section titled “[wireguard]”Optional WireGuard transport configuration.
[wireguard]enabled = truerelay_url = "wss://relay.example.com"turn_shared_secret = "..." # at least 32 characters, e.g. openssl rand -hex 32
[[wireguard.turn_servers]]address = "198.51.100.10:3478"protocol = "udp"
[[wireguard.turn_servers]]address = "198.51.100.11:5349"protocol = "tls"hostname = "turn.example.com"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Enable WireGuard transport |
turn_servers | array | - | No | TURN servers for NAT traversal |
stun_servers | array | (the turn_servers addresses) | No | STUN servers (host:port) published to agents for NAT classification |
turn_shared_secret | string | - | No | Shared secret for TURN credentials; at least 32 characters |
relay_url | string | - | No | WebSocket relay URL for fallback transport |
udp_endpoints | array | - | No | Direct UDP endpoints for hole-punching |
overlay_range | string | 10.99.0.0/16 | No | Overlay CIDR that agent overlay IPs come from when a tenant has no ip_range quota |
TURN servers
Section titled “TURN servers”An agent whose direct UDP to its controller does not carry allocates a relay on one of these before falling back to the WebSocket relay, so a symmetric NAT costs it a TURN hop rather than a round trip through Mandible.
| Field | Type | Required | Description |
|---|---|---|---|
address | string | Yes | host:port of the server |
protocol | string | No | udp (default), tcp or tls |
realm | string | No | Server realm, when it does not send one in its challenge |
name | string | No | Identifier used in the agent’s logs |
hostname | string | For TLS | DNS name the server’s certificate is verified against |
A server can also be given as one string — address\|protocol\|realm\|name\|hostname —
so a list fits in a single environment variable:
MANDIBLE__WIREGUARD__TURN_SERVERS=198.51.100.10:3478|udp,198.51.100.11:5349|tls|||turn.example.com.
Both halves are needed: servers with no turn_shared_secret are not advertised to
agents, because an agent cannot authenticate to them and would spend its allocation
timeout before falling back. A tls server with no hostname is not advertised either —
its certificate cannot be verified against an IP address, so the agent refuses it.
Credentials are minted per agent in coturn’s use-auth-secret form (username
<expiry>:<agent id>, password base64(HMAC-SHA1(secret, username))), so the same
turn_shared_secret must be configured as coturn’s static-auth-secret. The TURN REST
API fixes that derivation: the server re-derives each password from the username it
receives, and refuses a credential derived any other way.
Environment Variables
Section titled “Environment Variables”Settings can be overridden with environment variables using the MANDIBLE__ prefix and double-underscore separators (except log_analyzer.enabled, which is database-backed):
MANDIBLE__SECTION__KEY=valueExamples:
| Setting | Environment Variable |
|---|---|
server.port | MANDIBLE__SERVER__PORT |
database.url | DATABASE_URL |
jwt.secret | MANDIBLE__JWT__SECRET |
cors.allowed_origins | MANDIBLE__CORS__ALLOWED_ORIGINS (comma-separated) |
auth.lens_base_url | MANDIBLE__AUTH__LENS_BASE_URL |
grpc.orchestration_url | MANDIBLE__GRPC__ORCHESTRATION_URL |
See Environment Variables for complete reference.
Outbound Proxy
Section titled “Outbound Proxy”Mandible sends its outbound HTTP(S) requests through the proxy named by the standard variables in its own environment:
| Variable | Meaning |
|---|---|
HTTPS_PROXY, https_proxy | Proxy for https:// destinations |
HTTP_PROXY, http_proxy | Proxy for http:// destinations |
ALL_PROXY, all_proxy | Proxy for either scheme when its own variable is unset |
NO_PROXY, no_proxy | Comma-separated hosts, domains and address ranges that are reached direct |
Proxy credentials go in the proxy URL: http://user:password@proxy.internal:3128.
The proxy carries webhook and chat notifications, OIDC, tenant logos, log analysis providers, S3 storage, and Git storage and configuration repositories over HTTPS. It does not carry SMTP or Git over SSH. A Git remote over plain HTTP that the variables send to the proxy is refused; use HTTPS, or list the host in NO_PROXY to reach it directly.
Mandible refuses any destination that resolves to a cloud metadata address, and it checks every name it can resolve itself. A name that only the proxy can resolve goes to the proxy unchecked, so configure the proxy to refuse link-local and metadata destinations.
Leave the variables unset in Mandible’s environment to connect directly.
Complete Example
Section titled “Complete Example”Production Configuration
Section titled “Production Configuration”[server]host = "0.0.0.0"port = 3000request_timeout_secs = 30
[server.tls]cert_path = "/etc/mantis/certs/server.crt"key_path = "/etc/mantis/certs/server.key"
[database]url = "postgres://mantis:secret@db.example.com:5432/mantis?sslmode=require"max_connections = 20min_idle = 5
[jwt]algorithm = "RS256"private_key_path = "/etc/mantis/jwt/private.pem"public_key_path = "/etc/mantis/jwt/public.pem"access_token_expiry_secs = 28800refresh_token_expiry_secs = 604800issuer = "https://mantis.example.com"
[cors]allowed_origins = ["https://lens.example.com"]allow_credentials = truemax_age_secs = 3600
[cookie]refresh_token_name = "mantis_refresh_token"secure = truesame_site = "Lax"domain = ".example.com"
[auth]lens_base_url = "https://lens.example.com/login"
[grpc]orchestration_url = "https://thorax.internal:50051"connect_timeout_secs = 5request_timeout_secs = 300tls_ca_cert_path = "/etc/mantis/certs/ca.crt"
[internal_grpc]enabled = truehost = "0.0.0.0"port = 50052tls_cert_path = "/etc/mantis/certs/internal.crt"tls_key_path = "/etc/mantis/certs/internal.key"tls_ca_cert_path = "/etc/mantis/certs/ca.crt"
[queue]enabled = truehost = "rabbitmq.internal"port = 5671username = "mantis"password = "secret"vhost = "/mantis"ca_cert_path = "/etc/mantis/certs/rabbitmq-ca.crt"
[audit]enabled = true
[oidc]allowed_redirect_urls = ["https://lens.example.com"]allow_localhost_redirect = falseallow_any_redirect = falseNext Steps
Section titled “Next Steps”- Thorax Configuration - Orchestration server
- Environment Variables - Variable reference
- Security Overview - Security settings
