Appearance
Configuration
v1 preview
The complete repository and supported production Compose release are not public during beta. This reference describes the intended v1 configuration surface and may change before release. It is not a current deployment guide.
The planned self-hosted release uses environment variables for application, database, cache, search, AI, mail, and administration settings. Final required values, defaults, secret handling, and service versions will be published with v1.
Compose File
The planned compose.prod.yml file defines the service topology and reads its configuration from environment variables or a Docker secrets workflow. The public v1 release will identify which values are required and provide the supported startup procedure.
Environment Variables
Application
| Variable | Description | Required | Default |
|---|---|---|---|
APP_URL | Public URL of the application | Yes | — |
WS_URL | Public WebSocket URL. Set this when the WebSocket server runs on a different host or port than the app | No | Same as APP_URL |
PORT | Application port | No | 3000 |
SESSION_SECRET | Express session secret | Yes | — |
JWT_SECRET | JWT signing secret | Yes | — |
NODE_ENV | Environment mode | No | production |
SERVER_MODE | Run mode: omit for full app, ws for WebSocket-only, scheduler for background jobs | No | — |
SOCKET_IO_ADAPTER | Socket.IO cluster transport: mongodb or memory | No | mongodb |
SOCKET_IO_MONGO_URL | Optional MongoDB connection override for the MongoDB adapter | No | MONGO_URI |
The MongoDB Socket.IO adapter stores events in the socketio collection and uses change streams, so it requires a MongoDB replica set or sharded cluster.
Database
| Variable | Description | Required | Default |
|---|---|---|---|
MONGO_URI | MongoDB connection string | No | mongodb://mongo:27017/managani |
Managani requires a MongoDB replica set. Connections use the driver-default read preference with majority read and write concerns. Each model sets its read preference with the Mongoose schema read option, and most models use secondaryPreferred. Transactions read from the primary.
Cache
| Variable | Description | Required | Default |
|---|---|---|---|
MEMCACHED_SERVERS | Comma-separated Memcached endpoints | No | memcached:11211 in Docker Compose |
Search (Typesense)
| Variable | Description | Required | Default |
|---|---|---|---|
TYPESENSE_HOST | Typesense server host | No | typesense |
TYPESENSE_PORT | Typesense server port | No | 8108 |
TYPESENSE_PROTOCOL | Protocol (http or https) | No | http |
TYPESENSE_API_KEY | Typesense API key | Yes | — |
TYPESENSE_NODES | Multi-node cluster config as JSON. Example: [{"host":"ts1","port":8108,"protocol":"http"},{"host":"ts2","port":8108,"protocol":"http"}] | No | — |
TYPESENSE_COLLECTION_PREFIX | Prefix added to all collection names (mg_urls_<id>), so multiple products can share one Typesense cluster. Set to an empty string to keep unprefixed names on a dedicated cluster. | No | mg |
When TYPESENSE_NODES is set, the individual host/port/protocol variables are ignored and the cluster config is used instead.
AI / LLM
Managani uses plan-aware model tiers for AI tasks:
| Variable | Description | Required | Default |
|---|---|---|---|
LLM_BASIC_MODEL | Lower-cost model for basic plans, parsing, classification, and internal analysis | No | gpt-6-luna |
LLM_BASIC_MODEL_PROVIDER | Provider for the basic model: google or openai | No | openai |
LLM_TOP_MODEL | Top model for Pro/customer-facing replies and generated content | No | gpt-6-sol |
LLM_TOP_MODEL_PROVIDER | Provider for the top model: google or openai | No | openai |
CHAT_AI_MODEL | Legacy alias for LLM_TOP_MODEL when LLM_TOP_MODEL is unset | No | gpt-6-sol |
CHAT_AI_MODEL_PROVIDER | Legacy alias for LLM_TOP_MODEL_PROVIDER when LLM_TOP_MODEL_PROVIDER is unset | No | openai |
SOCIAL_REPLY_LLM_MODEL | Model used for review-only Reddit and Quora reply drafts | No | gpt-6-sol |
SOCIAL_REPLY_LLM_PROVIDER | Provider used for review-only Reddit and Quora reply drafts | No | openai |
NL_SEARCH_MODEL | Optional override for intent classification | No | gpt-6-luna |
NL_SEARCH_MODEL_PROVIDER | Optional provider override for intent classification | No | openai |
TS_CONVERSATION_MODEL | Optional override for Typesense conversation search | No | gpt-6-luna |
TS_CONVERSATION_MODEL_PROVIDER | Optional provider override for Typesense conversation search | No | openai |
GOOGLE_API_KEY | Google AI API key (required when using Google models) | Conditional | — |
OPENAI_API_KEY | OpenAI API key (required when using OpenAI models) | Conditional | — |
WIDGET_CHAT_EMAIL_FALLBACK_ENABLED | Global emergency switch for unread chat fallback email | No | true |
WIDGET_CHAT_EMAIL_FALLBACK_CRON | Scheduler cadence for due fallback email | No | * * * * * |
WIDGET_CHAT_EMAIL_FALLBACK_DELAY_MINUTES | Unread delay reset after each team or AI reply | No | 3 |
WIDGET_CHAT_EMAIL_FALLBACK_MAX_PER_RUN | Maximum fallback deliveries claimed per scheduler run | No | 50 |
At least one API key is required for AI chat to function. Hosted Free and Starter tenants are downgraded to the basic model for top-tier requests; hosted Pro tenants use the top model.
Inbound Email Webhooks
Set EMAIL_FORWARD_DOMAIN to the domain used for incoming replies.
/import/email accepts forwarded replies without a webhook signing key. It checks the conversation and sender before storing a reply. Valid handled deliveries return HTTP 200, including duplicate and unmatched messages. Responses contain acknowledgment fields, without stored email or chat content. Replies cannot write to accounts being deleted. Message creation and conversation updates commit together, so a failed database write can be retried without leaving a partial reply. The same email can reach separate conversations; repeated delivery to the same conversation is stored once.
Email (SMTP)
| Variable | Description | Required | Default |
|---|---|---|---|
SMTP_HOST | SMTP server host | Yes | — |
SMTP_PORT | SMTP server port | No | 587 |
SMTP_USER | SMTP username | Yes | — |
SMTP_PASS | SMTP password | Yes | — |
SMTP_FROM | Sender email address | Yes | — |
Admin
| Variable | Description | Required | Default |
|---|---|---|---|
SYSADMIN_EMAIL | System admin email. If set, a sysadmin account is provisioned on startup | No | — |
SYSADMIN_PASSWORD | System admin password | No | — |
Analytics (OpenPanel)
| Variable | Description | Required | Default |
|---|---|---|---|
ENABLE_OPENPANEL | Enable OpenPanel analytics | No | false |
OPENPANEL_CLIENT_ID | OpenPanel client ID | No | — |
OPENPANEL_CLIENT_SECRET | OpenPanel client secret | No | — |
OPENPANEL_API_URL | OpenPanel API URL | No | — |
Reverse Proxy
The planned production deployment will sit behind a reverse proxy such as nginx or Caddy. The final v1 guide will require operators to:
- Set
APP_URLto your public domain - Proxy WebSocket connections to port 3001
- Proxy MCP connections to port 3002
- Set
X-Forwarded-ForandX-Forwarded-Protoheaders
The app and MCP server share config.trustedProxies. Express uses this list to accept the forwarded protocol from a trusted socket peer and to find the first untrusted client address from the right of X-Forwarded-For. A browser session mutation must still send an Origin that matches the request's protocol and host. An untrusted socket cannot select HTTPS by sending X-Forwarded-Proto.
The hosted Swarm configuration uses the external hmnet16 network without defining its subnet. The trust list therefore includes only the production peers 30.0.9.70/32 and 30.0.9.71/32 observed in ClickStack on October 27, 2026, alongside the existing local and Cloudflare ranges. IPv4-mapped IPv6 forms of these peers are also covered. Reverify these addresses after proxy rescheduling; a new peer can cause same-origin HTTPS mutations to return 403 untrusted_origin. Do not expand trust to all of 30.0.0.0/8 without verified network boundaries.
Example nginx configuration:
nginx
server {
listen 443 ssl;
server_name your-instance.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /mcp {
proxy_pass http://localhost:3002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}