Mandible Configuration
Mandible Configuration
Section titled “Mandible Configuration”Configure the Mandible REST API server for your deployment.
Configuration File
Section titled “Configuration File”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.
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 |
[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 = "mandible"audience = "lens"key_id = "k1"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
algorithm | string | "HS256" | No | Signing algorithm: HS256 or RS256 |
secret | string | "" | Yes (HS256) | HMAC secret key (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 | "mandible" | No | JWT issuer claim |
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) |
[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 |
request_timeout_secs | integer | 300 | No | Request timeout (5 minutes for long-running operations) |
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_connections | integer | 100 | No | Maximum concurrent connections |
[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 | 0 | No | Console 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_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 |
[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 = "/"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 = "shared-secret"| Field | Type | Default | Required | Description |
|---|---|---|---|---|
enabled | boolean | false | No | Enable WireGuard transport |
turn_servers | array | - | No | TURN servers for NAT traversal |
turn_shared_secret | string | - | No | Shared secret for TURN credentials |
relay_url | string | - | No | WebSocket relay URL for fallback transport |
udp_endpoints | array | - | No | Direct UDP endpoints for hole-punching |
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.
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=verify-full"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 = "mandible"
[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 = "/"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
