mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
* feat(email): support implicit TLS (SMTPS/465) for SMTP relay The SMTP relay previously only did opportunistic STARTTLS: it dialed plaintext and upgraded if the server advertised STARTTLS. Providers that only offer implicit TLS on port 465 and do not advertise STARTTLS (e.g. Aliyun enterprise mail) could not be used as a relay at all. Add an SMTP_TLS env var: - unset / starttls (default): unchanged STARTTLS-upgrade behavior. - implicit / smtps / ssl: dial with tls.DialWithDialer (SMTPS). Implicit TLS is auto-enabled when SMTP_PORT=465 and SMTP_TLS is unset, so the common case works with no extra config. The startup log line now reports the negotiated mode (starttls / implicit-tls). Co-authored-by: multica-agent <github@multica.ai> * feat(email): plumb SMTP_TLS through selfhost compose, warn on unknown values The backend reads SMTP_TLS but docker-compose.selfhost.yml never forwarded it, so SMTP_TLS=implicit on a non-standard port (or an explicit starttls override on 465) silently did nothing inside the container. Add it to the backend.environment block. Also log a one-line warning when SMTP_TLS is set to an unrecognized value (e.g. "tls"/"true"/"on"), which would otherwise fall through to STARTTLS and fail to dial a 465 SMTPS port with no startup hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> * test(email): cover SMTP_TLS precedence and alias resolution Table-driven test over NewEmailService asserting the implicit-TLS decision: 465 auto-enables implicit; explicit starttls on 465 overrides auto-detect; implicit/smtps/ssl aliases (case-insensitive, whitespace-trimmed) force SMTPS on any port; unknown values fall back to starttls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> * docs: document SMTPS / SMTP_TLS support, drop "465 unsupported" Port 465 implicit TLS is now supported, so the five places that said it was unsupported are wrong. Replace those sentences, add an SMTP_TLS row to the environment-variables tables (EN + ZH), and add a copy-pasteable SMTPS env block to the auth-setup pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: guofengchang <guofengchang@cumulon.com> Co-authored-by: multica-agent <github@multica.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
238 lines
11 KiB
Plaintext
238 lines
11 KiB
Plaintext
# Database
|
|
POSTGRES_DB=multica
|
|
POSTGRES_USER=multica
|
|
POSTGRES_PASSWORD=multica
|
|
POSTGRES_PORT=5432
|
|
DATABASE_URL=postgres://multica:multica@localhost:5432/multica?sslmode=disable
|
|
# Optional pgxpool tuning. Defaults are 25 / 5 per pod and are usually fine.
|
|
# You can also set pool_max_conns / pool_min_conns as query params on
|
|
# DATABASE_URL; env vars below take precedence over URL params.
|
|
# DATABASE_MAX_CONNS=25
|
|
# DATABASE_MIN_CONNS=5
|
|
|
|
# Server
|
|
# APP_ENV gates production safety checks. Docker self-host pins APP_ENV to
|
|
# "production" by default. Local dev can leave it unset.
|
|
# See SELF_HOSTING.md for the full login setup.
|
|
APP_ENV=
|
|
# Optional local/testing shortcut. Empty by default, so there is no fixed
|
|
# verification code. Without RESEND_API_KEY, generated codes print to stdout.
|
|
# If you need deterministic local automation, set a 6-digit value such as
|
|
# 888888 and keep APP_ENV non-production. This is ignored when APP_ENV=production.
|
|
MULTICA_DEV_VERIFICATION_CODE=
|
|
PORT=8080
|
|
# Optional aliases for the local/self-host backend port. If one is set, it
|
|
# takes precedence over PORT in compose, Makefile, and installer helpers.
|
|
# BACKEND_PORT=8080
|
|
# API_PORT=8080
|
|
# SERVER_PORT=8080
|
|
# Prometheus metrics are disabled by default. When enabled, bind to loopback
|
|
# unless you protect the listener with private networking, allowlists, or
|
|
# proxy auth. Do not expose this endpoint through the public app/API ingress.
|
|
# HTTP request metrics start accumulating only when this listener is enabled.
|
|
# METRICS_ADDR=127.0.0.1:9090
|
|
JWT_SECRET=change-me-in-production
|
|
# Derived by Makefile / local scripts from the backend port.
|
|
# Set explicitly only when the daemon reaches the API through a different URL.
|
|
# MULTICA_SERVER_URL=ws://localhost:8080/ws
|
|
# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT.
|
|
# Set explicitly only when the app's public URL differs from local frontend.
|
|
# MULTICA_APP_URL=http://localhost:3000
|
|
# Public URL the API is reachable at from the open internet (no trailing
|
|
# slash). Used to mint absolute webhook URLs for autopilot webhook
|
|
# triggers and to show correct daemon setup commands in the web UI. Leave
|
|
# unset behind a same-origin reverse proxy or for plain localhost dev —
|
|
# the frontend will compose the URL from window.origin + webhook_path in
|
|
# that case. Headers are intentionally not used to derive this value, to
|
|
# avoid Host / X-Forwarded-Host spoofing when a self-hosted reverse proxy
|
|
# is not hardened.
|
|
MULTICA_PUBLIC_URL=
|
|
# Comma-separated CIDR list of reverse proxies whose X-Forwarded-For /
|
|
# X-Real-IP headers the per-IP webhook rate limiter is allowed to trust.
|
|
# Empty (the default) means "trust no headers" — the limiter uses
|
|
# r.RemoteAddr only, which is the safe shape when the backend is
|
|
# exposed directly. Set this when running behind nginx/Caddy/Cloudflare:
|
|
# e.g. "127.0.0.1/32" for a same-host reverse proxy, or the CDN's
|
|
# announced ranges for cloud deployments.
|
|
MULTICA_TRUSTED_PROXIES=
|
|
MULTICA_DAEMON_CONFIG=
|
|
MULTICA_WORKSPACE_ID=
|
|
MULTICA_DAEMON_ID=
|
|
MULTICA_DAEMON_DEVICE_NAME=
|
|
MULTICA_DAEMON_POLL_INTERVAL=3s
|
|
MULTICA_DAEMON_HEARTBEAT_INTERVAL=15s
|
|
MULTICA_CODEX_PATH=codex
|
|
MULTICA_CODEX_MODEL=
|
|
MULTICA_CODEX_WORKDIR=
|
|
MULTICA_CODEX_TIMEOUT=20m
|
|
|
|
# Self-host image channel
|
|
# Default stable release channel. Pin to an exact release like v0.2.4 if you
|
|
# want to stay on a specific version. If the selected tag has not been
|
|
# published to GHCR yet, use make selfhost-build / the build override instead.
|
|
MULTICA_IMAGE_TAG=latest
|
|
MULTICA_BACKEND_IMAGE=ghcr.io/multica-ai/multica-backend
|
|
MULTICA_WEB_IMAGE=ghcr.io/multica-ai/multica-web
|
|
|
|
# Email
|
|
# Two delivery options - only one needs to be configured:
|
|
#
|
|
# Option A: Resend (SaaS, recommended for cloud deployments)
|
|
# Set RESEND_API_KEY to a key from resend.com and verify your sending domain there.
|
|
# For local/dev use, leave RESEND_API_KEY empty - codes print to stdout. To
|
|
# accept a fixed local code, also set MULTICA_DEV_VERIFICATION_CODE above
|
|
# (ignored when APP_ENV=production).
|
|
RESEND_API_KEY=
|
|
RESEND_FROM_EMAIL=noreply@multica.ai
|
|
#
|
|
# Option B: SMTP relay (for self-hosted / on-premise deployments)
|
|
# Takes priority over Resend when SMTP_HOST is set.
|
|
# Supports unauthenticated relay (leave SMTP_USERNAME empty) and authenticated SMTP.
|
|
# Set SMTP_TLS_INSECURE=true only for private CA or self-signed certificates.
|
|
# SMTP_TLS controls the TLS mode:
|
|
# - unset / "starttls" (default): plaintext connect, upgrade via STARTTLS.
|
|
# - "implicit" (aliases: "smtps", "ssl"): TLS handshake on connect (SMTPS).
|
|
# Required by providers that only offer port 465 and do not advertise
|
|
# STARTTLS (e.g. Aliyun enterprise mail). Auto-enabled when SMTP_PORT=465
|
|
# and SMTP_TLS is unset.
|
|
SMTP_HOST=
|
|
SMTP_PORT=25
|
|
SMTP_USERNAME=
|
|
SMTP_PASSWORD=
|
|
SMTP_TLS_INSECURE=false
|
|
SMTP_TLS=
|
|
|
|
# Google OAuth
|
|
# The web login page reads GOOGLE_CLIENT_ID from /api/config at runtime, so
|
|
# changing it only requires restarting the backend / compose stack. No web
|
|
# rebuild is needed.
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT.
|
|
# Set explicitly only when your OAuth callback URL differs from local frontend.
|
|
# GOOGLE_REDIRECT_URI=http://localhost:3000/auth/callback
|
|
|
|
# S3 / CloudFront
|
|
# S3_BUCKET — bucket NAME only (e.g. "my-bucket"). Do NOT include the
|
|
# ".s3.<region>.amazonaws.com" suffix; the server builds the public URL
|
|
# from S3_BUCKET + S3_REGION. S3_REGION must match the bucket's real region.
|
|
S3_BUCKET=
|
|
S3_REGION=us-west-2
|
|
CLOUDFRONT_KEY_PAIR_ID=
|
|
CLOUDFRONT_PRIVATE_KEY_SECRET=multica/cloudfront-signing-key
|
|
CLOUDFRONT_PRIVATE_KEY=
|
|
CLOUDFRONT_DOMAIN=
|
|
# COOKIE_DOMAIN — optional Domain attribute on session + CloudFront cookies.
|
|
# Leave empty for single-host deployments (localhost, LAN IP, or a single
|
|
# hostname) — session cookies become host-only, which is what the browser
|
|
# wants. Only set it when the frontend and backend sit on different
|
|
# subdomains of one registered domain (e.g. ".example.com"). Do NOT set it
|
|
# to an IP address: RFC 6265 forbids IP literals in the cookie Domain
|
|
# attribute and browsers silently drop such cookies.
|
|
COOKIE_DOMAIN=
|
|
|
|
# AUTH_TOKEN_TTL — auth token lifetime. Accepts Go duration strings (e.g.
|
|
# "8760h", "720h30m") or plain integer seconds.
|
|
# Default: 2592000 (30 days). Self-hosted deployments on trusted networks can
|
|
# set a longer value to reduce re-authentication frequency.
|
|
# Note: longer TTL = longer exposure window if a cookie is leaked.
|
|
# AUTH_TOKEN_TTL=2592000
|
|
|
|
# Local file storage (fallback when S3_BUCKET is not set)
|
|
LOCAL_UPLOAD_DIR=./data/uploads
|
|
# Derived by Makefile / local scripts from the backend port.
|
|
# Set explicitly only when uploads are served through a different public URL.
|
|
# LOCAL_UPLOAD_BASE_URL=http://localhost:8080
|
|
|
|
# Security
|
|
# Comma-separated list of allowed origins for CORS and WebSocket connections.
|
|
# Defaults to localhost dev origins when unset.
|
|
# Example: CORS_ALLOWED_ORIGINS=https://app.multica.ai,https://staging.multica.ai
|
|
CORS_ALLOWED_ORIGINS=
|
|
|
|
# ==================== Rate limiting (optional Redis) ====================
|
|
# Per-IP fixed-window rate limiter on the public auth endpoints
|
|
# (/auth/send-code, /auth/verify-code, /auth/google). Backed by Redis.
|
|
# When REDIS_URL is unset the limiter is a no-op (fail-open) and the
|
|
# backend logs "rate limiting disabled: REDIS_URL not configured" at
|
|
# startup. The same REDIS_URL is reused by the realtime fan-out hub,
|
|
# the PAT cache, and the daemon-token cache.
|
|
# REDIS_URL=redis://localhost:6379/0
|
|
# Max requests per IP per minute. Defaults are 5 for send-code/google
|
|
# and 20 for verify-code.
|
|
# RATE_LIMIT_AUTH=5
|
|
# RATE_LIMIT_AUTH_VERIFY=20
|
|
# Comma-separated CIDRs whose X-Forwarded-For the auth limiter is
|
|
# allowed to trust. Empty (default) = never trust XFF, only RemoteAddr.
|
|
# REQUIRED behind a reverse proxy — otherwise every real user shares
|
|
# the proxy IP and the whole deployment lands in one bucket, turning
|
|
# /auth/send-code into 5 req/min site-wide. Use e.g. "127.0.0.1/32,::1/128"
|
|
# for same-host Caddy/Nginx, or the CDN's published ranges for ALB/CF.
|
|
# This is a separate list from MULTICA_TRUSTED_PROXIES above (which
|
|
# governs the autopilot webhook limiter).
|
|
# RATE_LIMIT_TRUSTED_PROXIES=
|
|
|
|
# Realtime metrics endpoint (/health/realtime) access control. See MUL-1342.
|
|
# When unset, the endpoint only serves direct loopback (127.0.0.1 / ::1)
|
|
# callers with no forwarding headers and returns 404 to everything else —
|
|
# safe for local dev. Any deployment behind a reverse proxy (Caddy / Nginx
|
|
# terminating TLS in front of localhost:8080) MUST set this token, since
|
|
# proxied requests look like loopback at the Go layer; with no token, those
|
|
# requests are refused with 404. Pass the token as
|
|
# `Authorization: Bearer <token>`.
|
|
# REALTIME_METRICS_TOKEN=
|
|
|
|
# GitHub App integration (Settings → GitHub "Connect GitHub")
|
|
# Both must be set for the Connect button to enable and for webhooks to be
|
|
# accepted; leave empty to disable the integration. See docs/github-integration.
|
|
# GITHUB_APP_SLUG is the tail of https://github.com/apps/<slug>.
|
|
GITHUB_APP_SLUG=
|
|
GITHUB_WEBHOOK_SECRET=
|
|
|
|
# Frontend
|
|
FRONTEND_PORT=3000
|
|
# Derived by docker-compose.selfhost.yml / local scripts from FRONTEND_PORT.
|
|
# Set explicitly only when serving frontend on a different origin/domain.
|
|
# FRONTEND_ORIGIN=http://localhost:3000
|
|
# Leave empty — auto-derived from page origin in browser, set by Makefile for local dev.
|
|
# NEXT_PUBLIC_API_URL also feeds the Next.js SSR proxy when explicitly set.
|
|
NEXT_PUBLIC_API_URL=
|
|
NEXT_PUBLIC_WS_URL=
|
|
|
|
# Remote API (optional) — set to proxy local frontend to a remote backend
|
|
# Leave empty to use local backend (localhost:8080)
|
|
# REMOTE_API_URL=https://multica-api.copilothub.ai
|
|
|
|
# ==================== Self-hosting: Control Signups (fixes #930) ====================
|
|
# Set to "false" to completely disable new user signups (recommended for private instances)
|
|
ALLOW_SIGNUP=true
|
|
# The web UI reads ALLOW_SIGNUP from /api/config at runtime, so toggling this
|
|
# only requires restarting the backend / compose stack — not rebuilding web.
|
|
# It is not hot-reloaded.
|
|
|
|
# Optional: Only allow emails from these domains (comma-separated)
|
|
ALLOWED_EMAIL_DOMAINS=
|
|
|
|
# Optional: Only allow these exact email addresses (comma-separated)
|
|
ALLOWED_EMAILS=
|
|
|
|
# Set to "true" to disable workspace creation for every caller on this
|
|
# instance (#3433). Operators usually leave this unset, bootstrap the
|
|
# shared workspace, then flip this to "true" and restart so subsequent
|
|
# users join only via invitations and the entire deployment is visible to
|
|
# the platform admin. The web UI reads this from /api/config at runtime,
|
|
# so toggling requires a backend restart but not a frontend rebuild.
|
|
DISABLE_WORKSPACE_CREATION=
|
|
|
|
# ==================== Analytics (PostHog) ====================
|
|
# Product analytics events feed the acquisition → activation → expansion funnel.
|
|
# Leave POSTHOG_API_KEY empty for local dev / self-hosted instances; the server
|
|
# will run a no-op analytics client and ship nothing. See docs/analytics.md.
|
|
POSTHOG_API_KEY=
|
|
POSTHOG_HOST=https://us.i.posthog.com
|
|
# Optional override for the `environment` PostHog event property.
|
|
# Defaults from APP_ENV and normalizes to production / staging / dev.
|
|
ANALYTICS_ENVIRONMENT=
|
|
# Force the no-op client even when POSTHOG_API_KEY is set (CI / opt-out).
|
|
ANALYTICS_DISABLED=
|