Multica Docs

Переменные окружения

Полный список переменных окружения для self-hosted server Multica.

Self-hosted server Multica читает конфигурацию из переменных окружения при старте — база, вход, email, storage, signup allowlists. На этой странице переменные сгруппированы по назначению: в каждом разделе — что будет, если не задать, и что обязательно в production. Как настроить auth-related переменные — в Sign-in and signup configuration.

Core server variables

Базовые переменные перед deploy — у части есть defaults для старта, но в production задавайте required явно.

VariableDefaultRequired in production?
DATABASE_URLpostgres://multica:multica@localhost:5432/multica?sslmode=disableYes
PORT8080No (unless you change the port)
JWT_SECRETmultica-dev-secret-change-in-productionYes (the default is unsafe)
APP_ENVemptyYes (must be production)
FRONTEND_ORIGINemptyYes (self-host must set its own domain)
MULTICA_DEV_VERIFICATION_CODEemptyNo (must stay empty in production)

Держите MULTICA_DEV_VERIFICATION_CODE пустым в production. Фиксированный локальный test code по умолчанию выключен, но при MULTICA_DEV_VERIFICATION_CODE=888888 любой, кто может запросить code, войдёт с этим значением, пока APP_ENV не production. Shortcut игнорируется при APP_ENV=production.

Database connection pool

VariableDefaultDescription
DATABASE_MAX_CONNS25pgxpool max connections. Daemon опрашивает часто (каждые 3s) и использует connections; крупным deployment может понадобиться больше
DATABASE_MIN_CONNS5Minimum idle connections

Если не заданы, используются значения выше — не встроенные pgx defaults 4/NumCPU, из‑за которых раньше был pool exhaustion в production.

Email configuration

Два backend доставки — Resend для cloud или SMTP relay для internal / on-premise. SMTP_HOST имеет приоритет над RESEND_API_KEY, если заданы оба.

Resend

VariableDefaultDescription
RESEND_API_KEYemptyResend API key
RESEND_FROM_EMAILnoreply@multica.aiSender address (must be a domain verified in your Resend account; also reused as the From: header when SMTP is in use)

SMTP relay

VariableDefaultDescription
SMTP_HOSTemptySMTP relay hostname. Setting this activates SMTP mode and overrides Resend
SMTP_PORT25SMTP port. Use 587 for STARTTLS submission, or 465 for SMTPS (implicit TLS, auto-enabled)
SMTP_USERNAMEemptySMTP username. Leave empty for unauthenticated relay
SMTP_PASSWORDemptySMTP password
SMTP_TLSstarttlsTLS mode. implicit (aliases smtps, ssl) forces an immediate TLS handshake on connect (SMTPS); port 465 auto-enables it. Unset / starttls upgrades via STARTTLS after connect
SMTP_TLS_INSECUREfalseSet true to skip TLS certificate verification (private CA / self-signed only)
SMTP_EHLO_NAMEmachine hostnameEHLO/HELO name announced to the relay. Set a real FQDN when a strict relay (e.g. Google Workspace smtp-relay.gmail.com) rejects the default greeting from a public IP — otherwise the relay drops the connection and it surfaces as an opaque EOF on a later command

STARTTLS включается автоматически, когда server его advertises. Dial timeout 10s, вся SMTP session — deadline 30s, чтобы «чёрная дыра» relay не зависала auth handler.

Если не настроено ни одно: server не падает с ошибкой, но каждое письмо (verification codes, invite links) пишется только в stdout server. Удобно локально — code из логов; в production забытый email config — тихая чёрная дыра, пользователи не получают письма и ошибки нет.

Google OAuth configuration

Optional. Не задавайте — только email + verification code; задайте — кнопка «Sign in with Google» на странице входа.

VariableDefaultDescription
GOOGLE_CLIENT_IDemptyGoogle Cloud OAuth client ID
GOOGLE_CLIENT_SECRETemptyGoogle Cloud OAuth secret
GOOGLE_REDIRECT_URIhttp://localhost:3000/auth/callbackOAuth callback URL (self-host: replace with your frontend domain)

Runtime: frontend читает настройки через /api/configсмена не требует rebuild frontend, restart server достаточно.

Полная настройка (включая Google Cloud Console) — Sign-in and signup configuration.

File storage configuration

Multica хранит вложения (images и files в comments). S3 preferred; без S3 — fallback на local disk.

S3 / S3-compatible storage

VariableDefaultDescription
S3_BUCKETemptyBucket name only (for example my-bucket). Do not include the .s3.<region>.amazonaws.com suffix — the server constructs the public host from S3_BUCKET + S3_REGION. Setting this enables S3 storage
S3_REGIONus-west-2AWS region. Must match the bucket's actual region — it is used both for SDK signing and for building the public URL
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYemptyStatic credentials. When both are unset, the AWS SDK default credential chain is used (IAM role / environment credentials)
AWS_ENDPOINT_URLemptyCustom S3-compatible endpoint (for example MinIO). Setting this switches to path-style URLs
ATTACHMENT_DOWNLOAD_MODEautoAttachment download path: auto, cloudfront, presign, or proxy. In auto, CloudFront is preferred when fully configured; internal/private endpoint hosts use the server proxy; public S3-compatible endpoints use presigned GET URLs when supported
ATTACHMENT_DOWNLOAD_URL_TTL30mTTL for CloudFront signed URLs and S3 presigned download URLs. Accepts Go duration strings

Когда S3_BUCKET не задан: при старте server логирует "S3_BUCKET not set, cloud upload disabled", uploads идут на local disk.

URL stored objects строятся в таком порядке:

  1. https://<CLOUDFRONT_DOMAIN>/<key> если задан CLOUDFRONT_DOMAIN.
  2. <AWS_ENDPOINT_URL>/<S3_BUCKET>/<key> (path-style) если задан AWS_ENDPOINT_URL.
  3. https://<S3_BUCKET>.s3.<S3_REGION>.amazonaws.com/<key> (virtual-hosted-style). Если в S3_BUCKET есть dots, fallback на https://s3.<S3_REGION>.amazonaws.com/<S3_BUCKET>/<key> (path-style) — wildcard TLS AWS не валидирует dotted bucket hosts.

API download_urlGET /api/attachments/{id}/download, если CloudFront signing не настроен. Endpoint редиректит на CloudFront/S3 presigned URLs когда безопасно, или стримит через server для private/internal endpoints вроде http://rustfs:9000. Для Docker/VPC-only object stores задайте ATTACHMENT_DOWNLOAD_MODE=proxy, если auto detection недостаточно консервативен для вашей сети.

Local disk (when S3 is not configured)

VariableDefaultDescription
LOCAL_UPLOAD_DIR./data/uploadsLocal storage directory
LOCAL_UPLOAD_BASE_URLempty (returns relative paths)Public base URL — leave unset and the frontend can't resolve a full URL for attachments

CloudFront (optional)

Если S3 за CloudFront: CLOUDFRONT_DOMAIN, CLOUDFRONT_KEY_PAIR_ID, CLOUDFRONT_PRIVATE_KEY (или CLOUDFRONT_PRIVATE_KEY_SECRET из Secrets Manager). Без CloudFront — пропустите; конфликта с S3 нет.

VariableDefaultDescription
COOKIE_DOMAINemptyScope of the session cookie
  • Empty: cookie только на exact host (single-host deployments)
  • .example.com: cookie shared между subdomains (app.example.com и api.example.com — одна sign-in session)
  • Warning: не может быть IP (бrowsers игнорируют)

Restricting who can sign up

Три слоя allowlist по приоритету. Если любой слой non-empty, email вне списка отклоняется — даже ALLOW_SIGNUP=true не перебивает.

VariableDefaultDescription
ALLOWED_EMAILSemptyExplicit email allowlist (comma-separated). When non-empty, only listed emails can sign up
ALLOWED_EMAIL_DOMAINSemptyDomain allowlist (comma-separated). When non-empty, only listed domains can sign up
ALLOW_SIGNUPtrueSignup master switch. Set false to disable signup entirely

Контринтуитивно: ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true не значит «company.io или все» — только company.io. Allowlist layers — AND semantics; дерево решений — Sign-in and signup configuration → Signup allowlists.

Invite flows не проверяют signup allowlist — но invitee должен войти перед accept. Если аккаунт Multica уже есть (другой workspace), accept без allowlist; если never signed up, первый шаг sign-in (verification code) проходит allowlist — email, отклонённый ALLOW_SIGNUP=false или ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS, не завершит signup и не примет invite.

Locking down workspace creation

ALLOW_SIGNUP=false блокирует новые аккаунты, но не блокирует signed-in user создать workspace через POST /api/workspaces. На self-hosted, где admin видит каждый issue, repo и agent, задайте DISABLE_WORKSPACE_CREATION=true.

VariableDefaultDescription
DISABLE_WORKSPACE_CREATIONfalseWhen true, every call to POST /api/workspaces returns 403 workspace creation is disabled for this instance. The web UI hides every "Create workspace" affordance via /api/config. There is no role/owner exception — the gate is global per instance

Рекомендуемая bootstrap sequence:

  1. Старт с DISABLE_WORKSPACE_CREATION unset (default).
  2. Admin создаёт shared workspace.
  3. DISABLE_WORKSPACE_CREATION=true, restart backend. Дальше — только invitation.

Если нужен ALLOW_SIGNUP=true для invited users с первым verification code, сочетайте DISABLE_WORKSPACE_CREATION=true с ALLOWED_EMAIL_DOMAINS / ALLOWED_EMAILS. ALLOW_SIGNUP=false дополнительно блокирует pending invitees без аккаунта — только если все members уже имеют Multica account.

Rate limiting (optional Redis)

Публичные auth endpoints — /auth/send-code, /auth/verify-code, /auth/google — fixed-window rate limit per IP. Limiter на Redis. Без REDIS_URL middleware no-op (fail-open), в логах rate limiting disabled: REDIS_URL not configured.

VariableDefaultDescription
REDIS_URLemptyRedis connection URL (for example redis://localhost:6379/0). When unset, rate limiting on auth endpoints is disabled. The same Redis is also used by the realtime hub fan-out, the PAT cache, and the daemon-token cache — they all fall back to in-memory / direct-DB mode when unset
RATE_LIMIT_AUTH5Max requests per IP per minute against /auth/send-code and /auth/google
RATE_LIMIT_AUTH_VERIFY20Max requests per IP per minute against /auth/verify-code
RATE_LIMIT_TRUSTED_PROXIESemptyComma-separated CIDRs whose X-Forwarded-For header the limiter is allowed to trust. Empty (the default) means never trust XFF — the limiter only uses the direct connection's RemoteAddr

При превышении limit: 429 Too Many Requests, Retry-After: 60, body {"error":"too many requests"}.

За reverse proxy задайте RATE_LIMIT_TRUSTED_PROXIES. Иначе все пользователи делят IP proxy с точки зрения backend, весь deployment в одном bucket, /auth/send-code — 5 req/min на весь site. Типично: 127.0.0.1/32,::1/128 для Caddy / Nginx на том же host; published ranges CDN для Cloudflare / ALB / CloudFront. Только IP, чей RemoteAddr внутри CIDR, могут использовать X-Forwarded-For для client id.

Отдельный RATE_LIMIT_TRUSTED_PROXIES не то же, что MULTICA_TRUSTED_PROXIES (autopilot-webhook limiter /api/webhooks/autopilots/{token}). Каждый limiter парсит свой список — за proxy задайте оба.

Daemon tuning parameters

Daemon на локальной машине пользователя; config тоже из local env. Частые переменные:

VariableDefaultDescription
MULTICA_SERVER_URLws://localhost:8080/wsServer address (self-host: replace with your domain)
MULTICA_DAEMON_HEARTBEAT_INTERVAL15sHeartbeat interval
MULTICA_DAEMON_POLL_INTERVAL3sTask polling interval
MULTICA_DAEMON_MAX_CONCURRENT_TASKS20Max concurrent tasks
MULTICA_<PROVIDER>_PATHmatches the CLI namePath to each AI coding tool's executable (for example MULTICA_CLAUDE_PATH)
MULTICA_<PROVIDER>_MODELemptyDefault model for each AI coding tool

Подробнее — Daemon and runtimes.

Frontend access control

VariableDefaultDescription
FRONTEND_ORIGINemptyFrontend address. Invite email links, the CORS allowlist, and the cookie domain are all derived from this. When unset, invite email links fall back to the hosted domain https://app.multica.ai — self-host must set this explicitly
MULTICA_APP_URLemptyFrontend URL for CLI login flow. Also used by the web UI to show self-host daemon setup commands with your app domain; for same-origin deployments this is also used as daemon server_url when MULTICA_PUBLIC_URL is unset
MULTICA_PUBLIC_URLemptyPublic API URL, without trailing slash. Used for autopilot webhook URLs and by the web UI as the daemon server_url
CORS_ALLOWED_ORIGINSemptyAdditional allowed CORS origins (comma-separated)
ALLOWED_ORIGINSemptyWebSocket-specific origin allowlist (comma-separated); when unset, fallback order is CORS_ALLOWED_ORIGINSFRONTEND_ORIGINlocalhost:3000/5173/5174

Пустой FRONTEND_ORIGIN — две тихие поломки: (1) invite links ведут на https://app.multica.ai, клик не возвращает на self-hosted instance; (2) WebSocket Origin checks fallback на localhost:3000 / 5173 / 5174, production WebSocket отклоняются — frontend «теряет realtime updates».

GitHub integration

GitHub PR ↔ issue integration требует две переменные. Обе — Connect GitHub в Settings и приём webhooks. Ещё две optional — имя connected account при install.

VariableDefaultDescription
GITHUB_APP_SLUGemptyThe slug of your GitHub App (the tail of https://github.com/apps/<slug>). Drives the Settings → GitHub install button URL
GITHUB_WEBHOOK_SECRETemptyThe Webhook secret you set on the GitHub App. Used for HMAC-SHA256 verification of every pull_request / installation delivery, and as the HMAC key for the setup-callback state token
GITHUB_APP_IDemptyOptional. Numeric App ID from the App's settings page. Combined with GITHUB_APP_PRIVATE_KEY, lets the setup callback fetch the connected account name from GitHub immediately on install
GITHUB_APP_PRIVATE_KEYemptyOptional. Full PEM block of the App's RSA private key (including -----BEGIN/END----- lines, newlines preserved). Used to mint the short-lived JWT GitHub requires for App-authenticated REST calls

Если одна из required не задана:

  • Connect GitHub в Settings → GitHub disabled и hint «not configured» для admins.
  • /api/webhooks/github503 github webhooks not configured — Multica не обрабатывает без secret.

Если optional GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY не заданы:

  • Карточка кратко показывает Connected to unknown после install; обновление до real org/user name после webhook installation.created (обычно секунды) и realtime update на открытой вкладке Settings → GitHub.

Note: GITHUB_WEBHOOK_SECRET же подписывает state token install flow — один secret для оператора. Это не GitHub App Client secret. См. GitHub integration → Self-host setup.

Usage analytics

По умолчанию server шлёт analytics в официальный PostHog Multica. Opt-out: ANALYTICS_DISABLED=true.

VariableDefaultDescription
ANALYTICS_DISABLEDfalseSet true to disable backend analytics entirely
POSTHOG_API_KEYbuilt-in default keySet when pointing at your own PostHog instance
POSTHOG_HOSThttps://us.i.posthog.comChange to your own host if you self-host PostHog

Далее