Skip to content

Mandible Configuration

Configure the Mandible REST API server for your deployment.

Mandible resolves its config file in this order: MANDIBLE_CONFIG (an explicit path — required to exist if set), otherwise a file named mandible (e.g. mandible.toml) searched in the current directory, then /etc/mantis/mandible, then <config_dir>/mantis/mandible. 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.

HTTP server settings.

[server]
host = "0.0.0.0"
port = 3000
request_timeout_secs = 30
external_url = "https://api.mantis.example.com"
FieldTypeDefaultRequiredDescription
hoststring"0.0.0.0"NoIP address to bind
portinteger3000NoHTTP port
request_timeout_secsinteger30NoRequest timeout in seconds
external_urlstring-NoExternal URL for OAuth2 discovery endpoints. If not set, derived from host:port

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"
FieldTypeDefaultRequiredDescription
cert_pathstring-Yes (if TLS enabled)Path to TLS certificate PEM file
key_pathstring-Yes (if TLS enabled)Path to TLS private key PEM file

PostgreSQL connection settings.

[database]
url = "postgres://mantis:password@localhost:5432/mantis"
max_connections = 10
min_idle = 2
connection_timeout_secs = 10
FieldTypeDefaultRequiredDescription
urlstringDATABASE_URL env varYesPostgreSQL connection URL. Startup fails with a config error if not set
max_connectionsinteger10NoMaximum connection pool size
min_idleinteger2NoMinimum idle connections
connection_timeout_secsinteger10NoConnection 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 = 3600
refresh_token_expiry_secs = 2592000
issuer = "mandible"
audience = "lens"
key_id = "k1"
FieldTypeDefaultRequiredDescription
algorithmstring"HS256"NoSigning algorithm: HS256 or RS256
secretstring""Yes (HS256)HMAC secret key (min 32 characters). Set via MANDIBLE__JWT__SECRET
private_key_pathstring-Yes (RS256)Path to RSA private key PEM file
public_key_pathstring-Yes (RS256)Path to RSA public key PEM file
key_idstring"k1"NoKey ID for the current signing key (for key rotation)
previous_secretstring-NoPrevious secret for graceful HS256 key rotation
previous_public_key_pathstring-NoPrevious public key path for graceful RS256 key rotation
previous_key_idstring-NoKey ID for the previous key
issuerstring"mandible"NoJWT issuer claim
audiencestring"lens"NoJWT audience claim
access_token_expiry_secsinteger3600NoAccess token lifetime (1 hour)
refresh_token_expiry_secsinteger2592000NoRefresh token lifetime (30 days)

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 = true
max_age_secs = 3600
FieldTypeDefaultRequiredDescription
allowed_originsarray["https://localhost:5173", "http://localhost:5173", "https://localhost:4173", "http://localhost:4173"]NoAllowed origins
allowed_methodsarray["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]NoAllowed HTTP methods
allowed_headersarray["Content-Type", "Authorization", "Accept", "X-Request-ID"]NoAllowed headers
allow_credentialsbooleantrueNoWhether to allow credentials
max_age_secsinteger3600NoPreflight cache duration in seconds

Cookie settings for authentication refresh tokens.

[cookie]
refresh_token_name = "mantis_refresh_token"
secure = true
same_site = "Lax"
path = "/"
domain = ".example.com"
FieldTypeDefaultRequiredDescription
refresh_token_namestring"mantis_refresh_token"NoCookie name for the refresh token
securebooleantrue (release), false (debug)NoHTTPS-only cookies
same_sitestring"Lax"NoSameSite policy: Strict, Lax, or None
pathstring"/"NoCookie path scope
domainstring-NoCookie domain. If not set, defaults to the origin domain

Authentication redirect settings.

[auth]
lens_base_url = "https://lens.example.com/login"
FieldTypeDefaultRequiredDescription
lens_base_urlstring"https://localhost:5173/login"NoLens UI login URL for redirects. Must be a valid HTTP/HTTPS URL

gRPC client settings for connecting to Thorax.

[grpc]
orchestration_url = "https://thorax.example.com:50051"
connect_timeout_secs = 5
request_timeout_secs = 300
tls_ca_cert_path = "/etc/mantis/certs/ca.crt"
tls_cert_path = "/etc/mantis/certs/client.crt"
tls_key_path = "/etc/mantis/certs/client.key"
FieldTypeDefaultRequiredDescription
orchestration_urlstring"https://localhost:50051"NoThorax gRPC URL. Env: MANDIBLE__GRPC__ORCHESTRATION_URL
connect_timeout_secsinteger5NoConnection timeout
request_timeout_secsinteger300NoRequest timeout (5 minutes for long-running operations)
tls_ca_cert_pathstring-NoCA certificate for TLS verification. Env: MANDIBLE__GRPC__TLS_CA_CERT_PATH
tls_cert_pathstring-NoClient certificate for mTLS. Env: MANDIBLE__GRPC__TLS_CERT_PATH
tls_key_pathstring-NoClient private key for mTLS. Env: MANDIBLE__GRPC__TLS_KEY_PATH

Retry configuration for transient gRPC failures. Only read-only operations are retried.

FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable retry for read-only operations
max_retriesinteger3NoMaximum retry attempts
initial_delay_msinteger100NoInitial delay before first retry (ms)
max_delay_msinteger5000NoMaximum delay between retries (ms)

Circuit breaker to prevent cascading failures.

FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable circuit breaker
failure_thresholdinteger5NoConsecutive failures before circuit opens
success_thresholdinteger2NoSuccesses in half-open state before closing
cooldown_secsinteger30NoHow long circuit stays open (seconds)

Internal gRPC server for Thorax-to-Mandible communication. Uses mTLS for authentication.

[internal_grpc]
enabled = true
host = "0.0.0.0"
port = 50052
tls_cert_path = "/etc/mantis/certs/internal.crt"
tls_key_path = "/etc/mantis/certs/internal.key"
tls_ca_cert_path = "/etc/mantis/certs/ca.crt"
FieldTypeDefaultRequiredDescription
enabledbooleanfalseNoEnable the internal gRPC server. Also reads MANDIBLE__INTERNAL_GRPC__ENABLED
hoststring"0.0.0.0"NoBind address. Also reads MANDIBLE__INTERNAL_GRPC__HOST
portinteger50052NoListen port. Also reads MANDIBLE__INTERNAL_GRPC__PORT
tls_cert_pathstring-Yes (if enabled)Server TLS certificate. Also reads MANDIBLE__INTERNAL_GRPC__TLS_CERT_PATH
tls_key_pathstring-Yes (if enabled)Server TLS private key. Also reads MANDIBLE__INTERNAL_GRPC__TLS_KEY_PATH
tls_ca_cert_pathstring-Yes (if enabled)CA certificate for client verification. Also reads MANDIBLE__INTERNAL_GRPC__TLS_CA_CERT_PATH
request_timeout_secsinteger300NoRequest timeout (seconds)
max_connectionsinteger100NoMaximum concurrent connections

API rate limiting settings.

[rate_limit]
enabled = true
requests_per_second = 100
burst_size = 200
FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable rate limiting
requests_per_secondinteger100NoMax requests per second per IP
burst_sizeinteger200NoBurst size (max instant requests)

Authentication-specific rate limiting for brute force protection.

FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable auth rate limiting
login_attempts_per_minuteinteger10NoMax failed login attempts per minute per IP
lockout_duration_secsinteger900NoLockout duration after exceeding limit (15 minutes)

Trusted reverse proxy configuration for X-Forwarded-For header processing.

FieldTypeDefaultRequiredDescription
enabledbooleanfalseNoTrust X-Forwarded-For from configured proxies
addressesarray[]NoTrusted proxy IP addresses or CIDR ranges

Server identity for the /server/info discovery endpoint.

[identity]
server_name = "Mandible Server"
server_cert_path = "/etc/mantis/certs/server.crt"
registration_enabled = true
FieldTypeDefaultRequiredDescription
server_namestring"Mandible Server"NoHuman-readable server name
server_cert_pathstring-NoPath to server TLS certificate for thumbprint calculation
server_thumbprintstring-NoPre-calculated server thumbprint (SHA256 hex)
registration_enabledbooleantrueNoWhether new client registration is enabled

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"
FieldTypeDefaultRequiredDescription
cert_pathstring-Yes, for CA-signed enrollmentPath to the platform root CA certificate
key_pathstring-Yes, for CA-signed enrollmentPath 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.

Tokio-console settings for async runtime debugging. Only active when the tokio-console feature is enabled.

[console]
enabled = true
address = "127.0.0.1"
port = 6670
retention_secs = 60
publish_interval_ms = 1000
FieldTypeDefaultRequiredDescription
enabledbooleanfalseNoEnable the tokio-console server
addressstring"127.0.0.1"NoBind address
portinteger0NoConsole port. The serde default is the sentinel 0 (OS-assigned ephemeral port). Set this explicitly to 6670 (the conventional Mandible port) to use tokio-console; leaving it unset binds to an unpredictable ephemeral port
retention_secsinteger60NoHow long to retain completed task data
publish_interval_msinteger1000NoUpdate interval for connected clients (ms)
recording_pathstring-NoPath to record console data for offline analysis

Audit logging configuration with per-tenant cryptographic hash chains.

[audit]
enabled = true
batch_size = 100
flush_interval_secs = 1
channel_buffer = 10000
max_retries = 3
FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable audit logging
batch_sizeinteger100NoMaximum batch size before forced flush
flush_interval_secsinteger1NoMaximum time between flushes (seconds)
channel_bufferinteger10000NoAsync channel buffer size
max_retriesinteger3NoMaximum total attempts for database writes
dead_letter_pathstring-NoPath for dead-letter queue file (failed entries)

Server-Sent Events session and token configuration.

[sse]
token_ttl_secs = 60
session_ttl_secs = 3600
audience = "mantis-sse"
redis_url = "redis://127.0.0.1:6379"
redis_namespace = "default"
FieldTypeDefaultRequiredDescription
token_ttl_secsinteger60NoSSE token time-to-live (single-use, short-lived)
session_ttl_secsinteger3600NoSSE session time-to-live (1 hour)
audiencestring"mantis-sse"NoSSE token audience claim
redis_urlstring"redis://127.0.0.1:6379"NoRedis URL for SSE state. Also reads SSE_REDIS_URL env var
redis_namespacestring"default"NoRedis key namespace

RabbitMQ queue settings for command dispatch. TLS is mandatory.

[queue]
enabled = true
host = "rabbitmq.internal"
port = 5671
username = "mantis"
password = "secret"
vhost = "/"
ca_cert_path = "/etc/mantis/certs/rabbitmq-ca.crt"
prefetch_count = 10
publisher_confirms = true
fallback_on_failure = true
health_check_interval_secs = 10
FieldTypeDefaultRequiredDescription
enabledbooleanfalseNoEnable RabbitMQ dispatch. Also reads MANDIBLE__QUEUE__ENABLED
hoststring"localhost"NoRabbitMQ host. Also reads MANDIBLE__QUEUE__HOST
portinteger5671NoAMQPS port (TLS only). Also reads MANDIBLE__QUEUE__PORT
usernamestring""NoRabbitMQ username. Also reads MANDIBLE__QUEUE__USERNAME
passwordstring""NoRabbitMQ password. Also reads MANDIBLE__QUEUE__PASSWORD
vhoststring"/"NoVirtual host. Also reads MANDIBLE__QUEUE__VHOST
ca_cert_pathstring-Yes (if enabled)CA certificate for TLS. Also reads MANDIBLE__QUEUE__CA_CERT_PATH
client_cert_pathstring-NoClient certificate for mTLS
client_key_pathstring-NoClient private key for mTLS
prefetch_countinteger10NoConsumer prefetch count
publisher_confirmsbooleantrueNoEnable publisher confirms for guaranteed delivery
fallback_on_failurebooleantrueNoFall back to polling when RabbitMQ is unavailable. Also reads MANDIBLE__QUEUE__FALLBACK_ON_FAILURE
health_check_interval_secsinteger10NoHealth check interval (seconds). Also reads MANDIBLE__QUEUE__HEALTH_CHECK_INTERVAL_SECS

OIDC/SSO settings for redirect URL validation.

[oidc]
allowed_redirect_urls = ["https://app.example.com"]
allowed_redirect_domains = ["*.example.com"]
allow_localhost_redirect = false
allow_any_redirect = false
FieldTypeDefaultRequiredDescription
allowed_redirect_urlsarray[]NoExplicit list of allowed redirect URLs
allowed_redirect_domainsarray[]NoAllowed domains (supports *.example.com wildcards)
allow_localhost_redirectbooleantrue (debug), false (release)NoAllow redirects to localhost
allow_any_redirectbooleantrue (debug), false (release)NoAllow any redirect when no allowlist configured

Encryption settings for credential protection at rest (AES-256-GCM).

[encryption]
key = "base64-encoded-32-byte-key"
FieldTypeDefaultRequiredDescription
keystring-YesBase64-encoded 32-byte encryption key. Also reads MANTIS_ENCRYPTION_KEY or MANDIBLE__ENCRYPTION__KEY env var. Generate with: openssl rand -base64 32

File cache settings for S3/Git storage content caching.

[file_cache]
enabled = true
memory_max_size_bytes = 104857600
FieldTypeDefaultRequiredDescription
enabledbooleantrueNoEnable file caching. Also reads MANDIBLE__FILE_CACHE__ENABLED
memory_max_size_bytesinteger104857600 (100 MB)NoByte 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

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 = 60
check_interval_secs = 5
batch_size = 10
max_input_length = 8192
temperature = 0.1
top_p = 0.9
max_tokens = 768
FieldTypeDefaultRequiredDescription
provider_typestring"ollama"NoLLM provider type: ollama or openai. Env: MANDIBLE__LOG_ANALYZER__PROVIDER_TYPE
api_urlstring"http://ollama:11434"NoLLM provider API URL. Also reads MANDIBLE__LOG_ANALYZER__API_URL
modelstring"granite4"NoModel name. Also reads MANDIBLE__LOG_ANALYZER__MODEL
timeout_secsinteger60NoRequest timeout. Also reads MANDIBLE__LOG_ANALYZER__TIMEOUT_SECS
check_interval_secsinteger5NoInterval between processing batches. Also reads MANDIBLE__LOG_ANALYZER__CHECK_INTERVAL_SECS
batch_sizeinteger10NoMax requests per batch. Also reads MANDIBLE__LOG_ANALYZER__BATCH_SIZE
max_input_lengthinteger8192NoMax log text input length (chars). Also reads MANDIBLE__LOG_ANALYZER__MAX_INPUT_LENGTH
temperaturefloat0.1NoLLM temperature. Also reads MANDIBLE__LOG_ANALYZER__TEMPERATURE
top_pfloat0.9NoNucleus sampling threshold. Also reads MANDIBLE__LOG_ANALYZER__TOP_P
max_tokensintegernone (uncapped)NoMax 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_pathstring-NoPath to custom CA certificate for TLS verification when connecting to LLM provider

Optional WireGuard transport configuration.

[wireguard]
enabled = true
relay_url = "wss://relay.example.com"
turn_shared_secret = "shared-secret"
FieldTypeDefaultRequiredDescription
enabledbooleanfalseNoEnable WireGuard transport
turn_serversarray-NoTURN servers for NAT traversal
turn_shared_secretstring-NoShared secret for TURN credentials
relay_urlstring-NoWebSocket relay URL for fallback transport
udp_endpointsarray-NoDirect UDP endpoints for hole-punching

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=value

Examples:

SettingEnvironment Variable
server.portMANDIBLE__SERVER__PORT
database.urlDATABASE_URL
jwt.secretMANDIBLE__JWT__SECRET
cors.allowed_originsMANDIBLE__CORS__ALLOWED_ORIGINS (comma-separated)
auth.lens_base_urlMANDIBLE__AUTH__LENS_BASE_URL
grpc.orchestration_urlMANDIBLE__GRPC__ORCHESTRATION_URL

See Environment Variables for complete reference.

mandible.toml
[server]
host = "0.0.0.0"
port = 3000
request_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=verify-full"
max_connections = 20
min_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 = 28800
refresh_token_expiry_secs = 604800
issuer = "mandible"
[cors]
allowed_origins = ["https://lens.example.com"]
allow_credentials = true
max_age_secs = 3600
[cookie]
refresh_token_name = "mantis_refresh_token"
secure = true
same_site = "Lax"
domain = ".example.com"
[auth]
lens_base_url = "https://lens.example.com/login"
[grpc]
orchestration_url = "https://thorax.internal:50051"
connect_timeout_secs = 5
request_timeout_secs = 300
tls_ca_cert_path = "/etc/mantis/certs/ca.crt"
[internal_grpc]
enabled = true
host = "0.0.0.0"
port = 50052
tls_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 = true
host = "rabbitmq.internal"
port = 5671
username = "mantis"
password = "secret"
vhost = "/"
ca_cert_path = "/etc/mantis/certs/rabbitmq-ca.crt"
[audit]
enabled = true
[oidc]
allowed_redirect_urls = ["https://lens.example.com"]
allow_localhost_redirect = false
allow_any_redirect = false