mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
docs(self-host): document auth rate-limit env keys
Adds REDIS_URL, RATE_LIMIT_AUTH, RATE_LIMIT_AUTH_VERIFY, and RATE_LIMIT_TRUSTED_PROXIES to the environment-variables page (EN + ZH) and to .env.example, with the reverse-proxy caveat that without RATE_LIMIT_TRUSTED_PROXIES every user shares the proxy IP and the whole deployment ends up in one bucket. Follow-up to #2636. MUL-2251. Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
22
.env.example
22
.env.example
@@ -122,6 +122,28 @@ LOCAL_UPLOAD_BASE_URL=http://localhost:8080
|
||||
# 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 —
|
||||
|
||||
@@ -128,6 +128,25 @@ Three allowlist layers combine by priority. **If any layer is set to a non-empty
|
||||
|
||||
**Invite flows themselves do not check the signup allowlist** — but the invitee must still be able to **sign in** before accepting the invite. If they already have a Multica account (for example from another workspace), they can accept directly, unaffected by the allowlist; **if they have never signed up**, the first step of sign-in (requesting a verification code) still passes through the allowlist check, and an email rejected by `ALLOW_SIGNUP=false` or by `ALLOWED_EMAILS` / `ALLOWED_EMAIL_DOMAINS` **cannot finish signup, and therefore cannot accept the invite**.
|
||||
|
||||
## Rate limiting (optional Redis)
|
||||
|
||||
Public auth endpoints — `/auth/send-code`, `/auth/verify-code`, `/auth/google` — have per-IP fixed-window rate limiting in front of them. The limiter is backed by Redis. When `REDIS_URL` is unset the middleware is a **no-op** (fail-open) and the backend logs `rate limiting disabled: REDIS_URL not configured` at startup.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `REDIS_URL` | empty | Redis 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_AUTH` | `5` | Max requests per IP per minute against `/auth/send-code` and `/auth/google` |
|
||||
| `RATE_LIMIT_AUTH_VERIFY` | `20` | Max requests per IP per minute against `/auth/verify-code` |
|
||||
| `RATE_LIMIT_TRUSTED_PROXIES` | empty | Comma-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` |
|
||||
|
||||
When a request is over the limit, the server replies with `429 Too Many Requests`, `Retry-After: 60`, and body `{"error":"too many requests"}`.
|
||||
|
||||
<Callout type="warning">
|
||||
**Behind a reverse proxy you must set `RATE_LIMIT_TRUSTED_PROXIES`.** Otherwise every real user shares the proxy's IP from the backend's point of view, the whole deployment ends up in one bucket, and `/auth/send-code` becomes 5 req/min for the entire site. Typical values: `127.0.0.1/32,::1/128` for a same-host Caddy / Nginx; the CDN's published ranges for Cloudflare / ALB / CloudFront. Only IPs whose `RemoteAddr` falls inside one of these CIDRs may use `X-Forwarded-For` to identify the client.
|
||||
</Callout>
|
||||
|
||||
This separate `RATE_LIMIT_TRUSTED_PROXIES` is **not** the same as `MULTICA_TRUSTED_PROXIES`, which controls the autopilot-webhook limiter (`/api/webhooks/autopilots/{token}`). Each limiter parses its own list, so a deployment behind a proxy should set both.
|
||||
|
||||
## Daemon tuning parameters
|
||||
|
||||
The daemon runs on the user's local machine, and its config is read from local environment variables too. The common ones:
|
||||
|
||||
@@ -128,6 +128,25 @@ Multica 存储用户上传的附件(评论里的图片、文件等)。**优
|
||||
|
||||
**邀请流程本身不检查 signup 白名单**——但被邀请人必须先能**登录**才能接受邀请。如果对方已经有 Multica 账号(比如在其他工作区注册过),可以直接接受,不受白名单影响;**如果对方还没注册过**,他们登录的第一步(发送验证码)仍然会过白名单检查,被 `ALLOW_SIGNUP=false` 或 `ALLOWED_EMAILS` / `ALLOWED_EMAIL_DOMAINS` 拒绝的邮箱**无法完成注册,也就没法接受邀请**。
|
||||
|
||||
## 速率限制(可选 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"}` 响应体。
|
||||
|
||||
<Callout type="warning">
|
||||
**部署在反向代理后面时必须设 `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。
|
||||
</Callout>
|
||||
|
||||
这里的 `RATE_LIMIT_TRUSTED_PROXIES` 和 `MULTICA_TRUSTED_PROXIES` **不是同一个**变量——后者控制的是 autopilot webhook 端点(`/api/webhooks/autopilots/{token}`)的限流器。两个限流器各自读各自的列表,部署在代理后面的实例需要两个都配上。
|
||||
|
||||
## 守护进程的调节参数
|
||||
|
||||
守护进程跑在用户本地机器上,配置也是读本地环境变量。常用的几个:
|
||||
|
||||
Reference in New Issue
Block a user