Production Checklist
Production Checklist
Section titled “Production Checklist”Use this checklist to verify your Mantis deployment is ready for production.
Security
Section titled “Security”Authentication
Section titled “Authentication”- Changed default admin password
- JWT secret is cryptographically random (32+ characters)
- JWT private key is secured (file permissions 400)
- Session tokens have appropriate expiry
TLS/mTLS
Section titled “TLS/mTLS”- Valid TLS certificates installed (not self-signed)
- mTLS enabled for client authentication
- Registration tokens created with
auto_approvedisabled (new clients reviewed before approval) - Certificate expiration monitoring configured
- CA private key stored securely (HSM or secrets manager)
Encryption
Section titled “Encryption”-
MANTIS_ENCRYPTION_KEYis a secure 32-byte base64 value - Encryption key stored in secrets manager (not in .env)
- Key rotation procedure documented
Access Control
Section titled “Access Control”- RBAC roles configured appropriately
- Principle of least privilege applied
- Admin accounts limited and audited
- SSO/OIDC configured (if applicable)
Configuration
Section titled “Configuration”Environment
Section titled “Environment”- Log level set to
infofor all services (not debug/trace):RUST_LOG=infofor Mandible,MANTIS_LOG_LEVEL=infofor Thorax,TARSUS__LOG_LEVEL=infofor Tarsus - Lens production build served (no dev server, source maps disabled)
- CORS origins restricted to actual domains
- All default passwords changed
Database
Section titled “Database”- External/managed PostgreSQL 16
- Migration role can
CREATE ROLEand owns the audit tables — migrations create themantis_audit_*roles and applyFORCE ROW LEVEL SECURITY, and fail outright on a managed instance that grants neither - Connection pooling configured appropriately —
database.max_connections(default 10) sizes each Mandible replica’s connection pool; a replica also holds two dedicated connections outside it, one per change listener (entity_changes,notification_delivery_queued) - SSL/TLS enabled for database connections
- Database credentials in secrets manager
Message Queue
Section titled “Message Queue”RabbitMQ is off by default — Mantis falls back to database polling. Skip this
section unless you set queue.enabled = true.
- External/managed RabbitMQ (not containerized)
- TLS enabled for all connections (port 5671)
- Queue credentials secured
- External/managed Redis (not containerized)
- AUTH password configured
- TLS enabled (if supported)
Infrastructure
Section titled “Infrastructure”Network
Section titled “Network”- Only Lens, Mandible, and Thorax exposed externally
- Database/RabbitMQ/Redis on internal network only
- Firewall rules configured
- Metrics ports closed to the internet. Both default to loopback (Mandible
127.0.0.1:9091, Thorax127.0.0.1:9090); compose widens both to0.0.0.0inside the containers, behindMANTIS_METRICS_TOKEN - Rate limiting enabled
Reverse Proxy
Section titled “Reverse Proxy”- TLS termination configured
- X-Forwarded headers passed correctly
- DDoS protection enabled
- Request size limits configured
Scaling
Section titled “Scaling”- Multiple Thorax instances for HA (if needed)
- Load balancer configured
- Health checks configured
Operations
Section titled “Operations”Monitoring
Section titled “Monitoring”- Health endpoints monitored
- Prometheus metrics collected from both Mandible and Thorax. Mandible serves
GET /metricsonMANDIBLE__METRICS__BIND_ADDR(default127.0.0.1:9091; set it to the empty string to disable the listener entirely) and Thorax onTHORAX__METRICS__BIND_ADDR(default127.0.0.1:9090). Tarsus exports none - A random
metrics.auth_token(openssl rand -hex 32) on both listeners, which a bind beyond loopback requires; Prometheus presents it from acredentials_file. Thorax’s listener also serves/health, which discloses cluster topology - Alerting configured for critical issues
- Certificate expiration alerts
Logging
Section titled “Logging”- Centralized logging configured
- Deployment-log retention configured at
PUT /api/v1/admin/log-retention— off by default, so logs accumulate indefinitely until it is set - If archiving deployment-log output, a destination is configured and
POST /api/v1/admin/log-retention/verify-archivesreturns every archive intact - Archive lifetime decided: the destination’s own lifecycle policy (the default), or
archive_retention_days, after which Mantis deletes each archive under the configured destination, and its record - Operational history windows set on the same endpoint, if the database should not
grow without bound:
operational_history_days(settled notifications, mail, digest entries, and commands with their results, aged from their last result) andspent_record_days(revoked or expired registration tokens that issued no certificate, deregistered or long-offline Thorax instances nothing refers to, expired notification suppression). Both are off by default. Environment state history is on neither window and is kept for the life of the install: rollback and promotion resolve against it, and it adds at most one row per successful deployment to an environment. Deleting its environment clears the reference and keeps the row - Audit logs enabled and monitored
- No sensitive data in logs
Backup
Section titled “Backup”- Database backup schedule configured
- Backup restoration tested
- CA certificate/key backed up securely
- Disaster recovery plan documented
Compliance
Section titled “Compliance”- Audit logging enabled
- Audit retention is never automatic — there is no schedule or purge job. Entries are immutable and the triggers block row deletion; ageing data out means archiving a completed quarterly partition and dropping it, which an administrator invokes deliberately. The chain is verified and the archive read back first, and the operation refuses if either fails
- Audit chain verification run periodically, and its recorded history reviewed at
GET /api/v1/audit/integrity/{tenant_id}/history
Documentation
Section titled “Documentation”- Runbooks for common operations
- Incident response procedures
- Contact information documented
Final Verification
Section titled “Final Verification”./docker.sh health is a local docker-compose helper: it runs pg_isready and
rabbitmq-diagnostics inside compose containers, so it cannot reach the
external PostgreSQL and RabbitMQ this checklist mandates, and it checks neither
Thorax, Tarsus nor Redis. Verify the services individually instead:
# Mandible: database connectivity only -- this endpoint performs no# security checks, despite the name suggesting otherwisecurl -s https://your-domain/api/v1/health | jq
# Readiness and the gRPC dependencycurl -s https://your-domain/api/v1/health/ready | jqcurl -s https://your-domain/api/v1/health/grpc | jq
# Mandible metrics (separate listener from the REST API port)curl -s -H "Authorization: Bearer $METRICS_TOKEN" http://mandible-host:9091/metrics | head
# Thoraxcurl -s -H "Authorization: Bearer $METRICS_TOKEN" http://thorax-host:9090/metrics | head