环境变量
self-host Multica 服务器需要配置的环境变量清单。
Multica 的 自部署 服务器启动时从环境变量读取配置——数据库、登录、邮件、存储、注册白名单都在这里配。这一页按用途分组给完整清单:每组说清楚不设会怎样、生产必须设哪几个。Auth 相关那几个怎么真正配见 登录与注册配置。
核心 server 环境变量
这些是你部署前必须考虑的核心变量——有些有默认值能让 server 启动,但生产环境里你应该显式配置必填项。
| 环境变量 | 默认值 | 生产必须设? |
|---|---|---|
DATABASE_URL | postgres://multica:multica@localhost:5432/multica?sslmode=disable | 是 |
PORT | 8080 | 否(除非换端口) |
JWT_SECRET | multica-dev-secret-change-in-production | 是(默认值不安全) |
APP_ENV | 空 | 是(必须 production) |
FRONTEND_ORIGIN | 空 | 是(self-host 要填你自己的域名) |
MULTICA_DEV_VERIFICATION_CODE | 空 | 否(生产必须保持为空) |
生产环境保持 MULTICA_DEV_VERIFICATION_CODE 为空。 固定本地测试验证码默认关闭;如果你设置 MULTICA_DEV_VERIFICATION_CODE=888888,在 APP_ENV 非 production 时,任何能请求验证码的人都能用这个固定值登录。APP_ENV=production 时该快捷码会被忽略。
数据库连接池
| 环境变量 | 默认值 | 说明 |
|---|---|---|
DATABASE_MAX_CONNS | 25 | pgxpool 最大连接数。守护进程高频轮询(每 3 秒)会占用连接;大规模部署可能需要调高 |
DATABASE_MIN_CONNS | 5 | 最小常驻连接 |
不设时使用上表默认值,不是 pgx 内置的 4/NumCPU——后者在生产曾引发连接池耗尽。
怎么配邮件
Multica 支持两种邮件发送通道——Resend 适合公网部署,SMTP relay 适合内网/自部署。同时设置时 SMTP_HOST 优先级高于 RESEND_API_KEY。
Resend
| 环境变量 | 默认值 | 说明 |
|---|---|---|
RESEND_API_KEY | 空 | Resend API key |
RESEND_FROM_EMAIL | noreply@multica.ai | 发件地址(必须是 Resend 账号已验证的域名;走 SMTP 时同时作为 From: 头) |
SMTP relay
| 环境变量 | 默认值 | 说明 |
|---|---|---|
SMTP_HOST | 空 | SMTP relay 主机名。设置后即启用 SMTP 模式并覆盖 Resend |
SMTP_PORT | 25 | SMTP 端口。STARTTLS 提交端口用 587;SMTPS(隐式 TLS,自动启用)用 465 |
SMTP_USERNAME | 空 | SMTP 用户名。留空表示未认证 relay |
SMTP_PASSWORD | 空 | SMTP 密码 |
SMTP_TLS | starttls | TLS 模式。implicit(别名 smtps、ssl)在连接时立即进行 TLS 握手(SMTPS);465 端口会自动启用。未设置 / starttls 则在连接后通过 STARTTLS 升级 |
SMTP_TLS_INSECURE | false | 设为 true 跳过 TLS 证书校验(仅限私有 CA / 自签证书) |
SMTP_EHLO_NAME | 机器主机名 | 向 relay 通告的 EHLO/HELO 名称。当严格的 relay(例如 Google Workspace smtp-relay.gmail.com)拒绝来自公网 IP 的默认问候时,填一个真实的 FQDN——否则 relay 会直接断开连接,并在后续某条命令上表现为一个不知所云的 EOF |
服务端 advertise STARTTLS 时会自动升级。dial 超时 10s,整个 SMTP 会话有 30s deadline,避免 relay 黑洞把 auth handler 挂死。
两种都不设的行为:server 不会报错,但所有本该发出去的邮件(验证码、邀请链接)只打到 server 的 stdout。本地开发方便(你从 server 日志里抄验证码);生产环境忘记设就是黑洞,用户收不到邮件也没任何错误提示。
怎么配 Google OAuth
可选。不设则只有邮箱 + 验证码登录;设了就在登录页出现「用 Google 登录」。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
GOOGLE_CLIENT_ID | 空 | Google Cloud OAuth client ID |
GOOGLE_CLIENT_SECRET | 空 | Google Cloud OAuth secret |
GOOGLE_REDIRECT_URI | http://localhost:3000/auth/callback | OAuth 回调地址(self-host 换成你的前端域名) |
热生效:前端在运行时通过 /api/config 拿这些配置,改了不用重启前端也不用重建镜像——改完重启 server 即可。
完整配置步骤(含 Google Cloud Console 操作)详见 登录与注册配置。
怎么配文件存储
Multica 存储用户上传的附件(评论里的图片、文件等)。优先走 S3;不配 S3 就回落本地磁盘。
S3 / S3 兼容存储
| 环境变量 | 默认值 | 说明 |
|---|---|---|
S3_BUCKET | 空 | 只填 bucket 名(例如 my-bucket),不要带 .s3.<region>.amazonaws.com 后缀——server 会用 S3_BUCKET + S3_REGION 自己拼公开 host。设了就启用 S3 存储 |
S3_REGION | us-west-2 | AWS 区域。必须和 bucket 所在区域一致——SDK 签名和公开 URL 都用它 |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY | 空 | 静态凭证。全未设时用 AWS SDK 默认凭证链(IAM role / 环境凭证) |
AWS_ENDPOINT_URL | 空 | 自定义 S3 兼容端点(例如 MinIO)。设了会切到 path-style URL |
ATTACHMENT_DOWNLOAD_MODE | auto | 附件下载路径:auto、cloudfront、presign 或 proxy。auto 下 CloudFront 配完整时优先 CloudFront;内网/私有 endpoint host 走 server proxy;公网 S3 兼容 endpoint 在支持时走 presigned GET |
ATTACHMENT_DOWNLOAD_URL_TTL | 30m | CloudFront signed URL 和 S3 presigned download URL 的有效期。使用 Go duration 格式 |
S3_BUCKET 未设时:server 启动时打 info 日志 "S3_BUCKET not set, cloud upload disabled",所有上传回落到本地磁盘。
对象存储 URL 按优先级拼装:
- 设了
CLOUDFRONT_DOMAIN→https://<CLOUDFRONT_DOMAIN>/<key> - 设了
AWS_ENDPOINT_URL→<AWS_ENDPOINT_URL>/<S3_BUCKET>/<key>(path-style) - 默认走 AWS S3 →
https://<S3_BUCKET>.s3.<S3_REGION>.amazonaws.com/<key>(virtual-hosted-style)。bucket 名含点时会回落到https://s3.<S3_REGION>.amazonaws.com/<S3_BUCKET>/<key>(path-style),因为 AWS 通配证书无法覆盖含点 host。
API 返回的 download_url 在未配置 CloudFront 签名时会指向 GET /api/attachments/{id}/download。这个端点会在安全时跳转到 CloudFront/S3 presigned URL;遇到 http://rustfs:9000 这类私有或内网 endpoint 时则由 server 流式转发。Docker/VPC 内部对象存储建议显式设置 ATTACHMENT_DOWNLOAD_MODE=proxy。
本地磁盘(S3 未配时)
| 环境变量 | 默认值 | 说明 |
|---|---|---|
LOCAL_UPLOAD_DIR | ./data/uploads | 本地存储目录 |
LOCAL_UPLOAD_BASE_URL | 空(返回相对路径) | 公开访问的 base URL——不设前端就拿不到附件的完整 URL |
CloudFront(可选)
如果你用 CloudFront 给 S3 做 CDN,三个相关的环境变量:CLOUDFRONT_DOMAIN、CLOUDFRONT_KEY_PAIR_ID、CLOUDFRONT_PRIVATE_KEY(或从 Secrets Manager 读的 CLOUDFRONT_PRIVATE_KEY_SECRET)。不用 CloudFront 就不用管——和配置 S3 不冲突。
Cookie 域
| 环境变量 | 默认值 | 说明 |
|---|---|---|
COOKIE_DOMAIN | 空 | session cookie 的作用域 |
- 空:cookie 只对访问的那个 host 生效(单主机部署正确)
- 设成
.example.com:cookie 在所有子域共享(让app.example.com和api.example.com共用登录态) - ⚠️ 不能是 IP 地址(浏览器会忽略)
怎么限制谁能注册
三层白名单按优先级组合。任何一层白名单一旦设置非空,不匹配的邮箱就会被拒——即使 ALLOW_SIGNUP=true 也挡不住。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
ALLOWED_EMAILS | 空 | 显式邮箱白名单(逗号分隔)。非空时只有列表里的邮箱能注册 |
ALLOWED_EMAIL_DOMAINS | 空 | 域名白名单(逗号分隔)。非空时只有列表里的域名能注册 |
ALLOW_SIGNUP | true | 注册总开关。设 false 完全关闭注册 |
不直观的点:ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true 的组合不是「允许 company.io 或所有人」,而是只允许 company.io。白名单的 AND 语义——决策树详见 登录与注册配置 → Signup 白名单。
邀请流程本身不检查 signup 白名单——但被邀请人必须先能登录才能接受邀请。如果对方已经有 Multica 账号(比如在其他工作区注册过),可以直接接受,不受白名单影响;如果对方还没注册过,他们登录的第一步(发送验证码)仍然会过白名单检查,被 ALLOW_SIGNUP=false 或 ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS 拒绝的邮箱无法完成注册,也就没法接受邀请。
锁死工作区创建
ALLOW_SIGNUP=false 能挡住新注册,但挡不住已经登录的用户继续 POST /api/workspaces 自助开新工作区。在希望平台管理员能看到全部 issue / 仓库 / 智能体的自部署实例里,把 DISABLE_WORKSPACE_CREATION=true 打开以堵上这个口子。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
DISABLE_WORKSPACE_CREATION | false | 设为 true 时,对 POST /api/workspaces 的任何调用都返回 403 workspace creation is disabled for this instance。Web UI 通过 /api/config 隐藏所有「创建工作区」入口。该开关是实例级的,没有 owner / admin 例外 |
推荐的开服流程:
- 启动实例时保持
DISABLE_WORKSPACE_CREATION未设置(默认)。 - 管理员登录并创建共享工作区。
- 把
DISABLE_WORKSPACE_CREATION=true设上并重启 backend。之后新用户只能通过邀请加入。
如果还想让被邀请的新用户能完成首次注册,保留 ALLOW_SIGNUP=true(必要时用 ALLOWED_EMAIL_DOMAINS / ALLOWED_EMAILS 限定可注册邮箱),只把 DISABLE_WORKSPACE_CREATION=true 打开即可;如果同时设 ALLOW_SIGNUP=false,连有 pending invite 的邮箱都无法完成首次注册,仅适合所有成员都已有 Multica 账号的实例。
速率限制(可选 Redis)
公开认证端点——/auth/send-code、/auth/verify-code、/auth/google——前面挂了按 IP 的固定窗口限流。限流器后端是 Redis。REDIS_URL 不设时中间件直通(fail-open),后端启动会打日志 rate limiting disabled: REDIS_URL not configured。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
REDIS_URL | 空 | Redis 连接 URL(例如 redis://localhost:6379/0)。不设时认证端点的限流功能直接关闭。同一个 Redis 也被实时事件 fan-out、PAT 缓存、守护进程 token 缓存复用;不设时这些组件分别回落到内存模式 / 直查 DB |
RATE_LIMIT_AUTH | 5 | 单 IP 每分钟对 /auth/send-code 和 /auth/google 的最大请求数 |
RATE_LIMIT_AUTH_VERIFY | 20 | 单 IP 每分钟对 /auth/verify-code 的最大请求数 |
RATE_LIMIT_TRUSTED_PROXIES | 空 | 逗号分隔的 CIDR 列表,列在内的来源 IP 才允许通过 X-Forwarded-For 标识客户端。默认空 = 永不信任 XFF,限流器只看直连的 RemoteAddr |
被限流的请求会返回 429 Too Many Requests,带 Retry-After: 60 头和 {"error":"too many requests"} 响应体。
部署在反向代理后面时必须设 RATE_LIMIT_TRUSTED_PROXIES。 否则在后端看来所有真实用户都共用代理那个 IP,整个部署落到同一个桶里,/auth/send-code 会变成全站每分钟只能发 5 次。常见值:本机 Caddy / Nginx 用 127.0.0.1/32,::1/128;Cloudflare / ALB / CloudFront 用各家公开的 CDN IP 段。只有 RemoteAddr 落在这些 CIDR 内的请求才被允许通过 X-Forwarded-For 改写客户端 IP。
这里的 RATE_LIMIT_TRUSTED_PROXIES 和 MULTICA_TRUSTED_PROXIES 不是同一个变量——后者控制的是 autopilot webhook 端点(/api/webhooks/autopilots/{token})的限流器。两个限流器各自读各自的列表,部署在代理后面的实例需要两个都配上。
守护进程的调节参数
守护进程跑在用户本地机器上,配置也是读本地环境变量。常用的几个:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
MULTICA_SERVER_URL | ws://localhost:8080/ws | server 地址(self-host 换成你的域名) |
MULTICA_DAEMON_HEARTBEAT_INTERVAL | 15s | 心跳频率 |
MULTICA_DAEMON_POLL_INTERVAL | 3s | 任务轮询频率 |
MULTICA_DAEMON_MAX_CONCURRENT_TASKS | 20 | 并发任务上限 |
MULTICA_AGENT_TIMEOUT | 0 | 单次任务的绝对墙钟上限;0 = 不设上限,任务只受看门狗约束(活跃任务不会因为跑得久被杀)。想要硬性成本/资源天花板时再设一个正值 |
MULTICA_AGENT_IDLE_WATCHDOG | 30m | 空闲看门狗:backend 持续静默(无消息、消息队列为空、且没有工具在途)这么久就 force-stop。0 = 关闭整套看门狗 |
MULTICA_AGENT_TOOL_WATCHDOG | 2h | 工具在途时的静默上限:某个工具调用发出后长时间无任何输出(疑似卡死的子进程)这么久就 force-stop。0 = 关闭该兜底(在途工具永不被停) |
MULTICA_<PROVIDER>_PATH | 对应 CLI 名 | 各 AI 编程工具的可执行文件路径(如 MULTICA_CLAUDE_PATH) |
MULTICA_<PROVIDER>_MODEL | 空 | 各 AI 编程工具的默认模型 |
完整解释每个参数对守护进程行为的影响,见 守护进程与运行时。
前端访问控制
| 环境变量 | 默认值 | 说明 |
|---|---|---|
FRONTEND_ORIGIN | 空 | 前端地址。邀请邮件里的链接、CORS 白名单、cookie domain 都从这里推导。邮件链接在不设时会 fallback 到托管版域名 https://app.multica.ai——self-host 必须显式填 |
MULTICA_APP_URL | 空 | CLI 登录流程使用的前端 URL。Web UI 也会用它显示带你自己 app domain 的 self-host 守护进程 setup 命令;同源部署中,如果 MULTICA_PUBLIC_URL 未设置,它也会作为守护进程的 server_url |
MULTICA_PUBLIC_URL | 空 | 公开 API URL,不带结尾 slash。用于 autopilot webhook URL,也会被 Web UI 用作守护进程的 server_url |
CORS_ALLOWED_ORIGINS | 空 | 额外的 CORS 允许来源(逗号分隔) |
ALLOWED_ORIGINS | 空 | WebSocket 专用的 origin 白名单(逗号分隔);不设就按 CORS_ALLOWED_ORIGINS → FRONTEND_ORIGIN → localhost:3000/5173/5174 顺序回落 |
FRONTEND_ORIGIN 不设就有两个静默失败:(1)邀请邮件里的链接指向 https://app.multica.ai(托管版的域名),用户点了跳不回你的 self-host 实例;(2)WebSocket 连接的 Origin 校验回落到 localhost:3000 / 5173 / 5174,生产部署的 WebSocket 全部被拒,前端看起来「实时更新不工作」。
GitHub 集成
GitHub PR ↔ issue 集成 依赖两个必填环境变量。两个都配上才会启用 Settings 里的 Connect GitHub 并接受 webhook。另外两个可选变量用于在安装时直接拿到关联账号名。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
GITHUB_APP_SLUG | 空 | 你的 GitHub App slug(https://github.com/apps/<slug> 的尾部)。Settings → GitHub 里安装按钮的跳转 URL 用它拼 |
GITHUB_WEBHOOK_SECRET | 空 | 你在 GitHub App 上设置的 Webhook secret。每条 pull_request / installation delivery 都用它做 HMAC-SHA256 校验;同一个值也用作 setup 回调里 state token 的签名密钥 |
GITHUB_APP_ID | 空 | 可选。App 设置页上的数字 App ID。配合 GITHUB_APP_PRIVATE_KEY 使用,让 setup 回调在安装那一刻直接从 GitHub 取到关联账号名 |
GITHUB_APP_PRIVATE_KEY | 空 | 可选。App RSA 私钥的完整 PEM 块(包含 -----BEGIN/END----- 两行,保留换行)。用于签发 GitHub App 鉴权 REST 调用所需的短效 JWT |
任一必填变量未设时:
- Settings → GitHub 里
Connect GitHub按钮 disable,对 admin 显示「not configured」提示 /api/webhooks/github直接返回503 github webhooks not configured——secret 没配置时 Multica 拒绝处理任何 webhook 事件,而不是把所有签名当 valid
可选 GITHUB_APP_ID / GITHUB_APP_PRIVATE_KEY 未设时:
- 安装完成后,连接卡片会先短暂显示
已连接到 unknown。等 GitHub 的installation.createdwebhook 到达(通常几秒内),Multica 会把 row 刷成真实的组织/用户名,并通过 realtime 推送让正在打开的 Settings → GitHub 页面无需手动刷新即可更新。
注意: GITHUB_WEBHOOK_SECRET 同时被复用为 install 流程里 state token 的签名密钥,所以运维只需要维护一个 secret。它不是 GitHub App 的 Client secret——Client secret 是 OAuth 用的,和本集成无关。完整配置流程见 GitHub 集成 → Self-Host 配置。
用量统计
默认上报到 Multica 官方 PostHog 实例。不想上报就把 ANALYTICS_DISABLED=true。
| 环境变量 | 默认值 | 说明 |
|---|---|---|
ANALYTICS_DISABLED | false | 设 true 完全关闭后端上报 |
POSTHOG_API_KEY | 内置默认 key | 换成你自己的 PostHog 实例时填 |
POSTHOG_HOST | https://us.i.posthog.com | 自建 PostHog 的话改成你自己的地址 |