Simmetric Chat 有两个配置层级:在进程启动时用 Zod 校验的环境变量,以及存储在数据库中的运行时配置层,管理员可以从界面修改而无需重启任何服务。
- 环境变量(进程启动)- 在
packages/<pkg>/src/config/env.ts中用 Zod 校验;无效时process.exit(1)。 - 运行时配置(DB `SystemConfig`) - 可从管理界面修改,无需重启。
解析顺序:
ALWAYS_READONLY键(JWT_SECRET、DATABASE_URL、SERVER_PORT、COLLECTOR_PORT、SERVER_URL、COLLECTOR_URL、LICENSE_SECRET):ENV > Default,不可从界面修改。- 其他所有键:
DB > ENV > Default,可通过PUT /api/system/settings修改。
三个包通过 path.resolve(__dirname, "../../.env") 读取 .env(与工作目录无关)。参考文档是仓库根目录的 .env.example。
环境变量 - server
Schema:packages/server/src/config/env.ts。显示默认值;必填 = 必需。
Core / 基础设施
| 变量 | 默认值 | 必填 | 描述 |
|---|---|---|---|
NODE_ENV | development | opt | development/production/test |
SERVER_PORT | 3000 | opt | 服务器 API 端口 |
COLLECTOR_PORT | 3210 | opt | Collector 端口(用于默认 URL) |
SERVER_URL | http://localhost:3000 | opt | 服务器公开 URL |
COLLECTOR_URL | http://localhost:3210 | opt | Collector URL |
WIDGET_SERVICE_URL | http://localhost:3211 | opt | Widget 服务 URL(推送 cache-bust) |
WIDGET_API_KEY | — | opt | Widget 推送共享密钥;缺失时 cache-bust 为 no-op(TTL 5 分钟) |
DATABASE_URL | postgresql://simmetricchat:[email protected]:5432/simmetricchat | opt | PostgreSQL URL |
JWT_SECRET | — | 必填 | JWT 密钥 |
ENCRYPTION_KEY | — | opt | AES-256-GCM(静态数据)的 32 字节 Base64。未设置 → 回退 scryptSync(JWT_SECRET, salt) |
SESSION_EXPIRY | 86400000(24h ms) | opt | JWT 过期时间 |
ALLOWED_ORIGINS | http://localhost:5173,127.0.0.1:5173,localhost:3000 | opt | 全局 CORS 白名单(CSV)。/api/internal/widget 除外 |
LOG_LEVEL | info | opt | debug/info/warn/error |
ALLOW_REGISTRATION | false | opt | 自助注册开关 |
DISABLE_TELEMETRY | true | opt | 禁用遥测 |
引导管理员(seed)
| 变量 | 默认值 | 描述 |
|---|---|---|
SEED_BOOTSTRAP_ADMIN | true | 首次启动时自动 seed 管理员(自定义转换:false/0/no/off/"" = 禁用) |
SEED_ADMIN_USERNAME | admin | 引导管理员用户名 |
SEED_ADMIN_PASSWORD | admin123 | 密码(最少 8 位);账户 mustChangePassword=true |
SEED_ADMIN_EMAIL | [email protected] | 引导管理员邮箱 |
LLM
| 变量 | 默认值 | 描述 |
|---|---|---|
LLM_PROVIDER | ollama | openai/anthropic/ollama/openrouter |
LLM_MODEL | gemma4:latest | 默认模型 |
LLM_TEMPERATURE | 0.7 | 0–2 |
LLM_MAX_TOKENS | 4096 | 补全 token 上限 |
LLM_TIMEOUT | 0 | Axios 超时毫秒数(0 = 无,用于较慢的本地 LLM) |
LLM_API_KEY / LLM_API_BASE_URL | — | 通用覆盖 |
OPENAI_API_KEY / OPENAI_MODEL | — | OpenAI |
ANTHROPIC_API_KEY / ANTHROPIC_MODEL | — | Anthropic |
OLLAMA_BASE_URL | http://ollama:11434 | Ollama 端点 |
OLLAMA_MODEL / OLLAMA_API_KEY | — | Ollama 覆盖 |
OPENROUTER_API_KEY / OPENROUTER_BASE_URL / OPENROUTER_MODEL | https://openrouter.ai/api / — | OpenRouter |
Agent 看门狗(防止循环/停滞)
| 变量 | 默认值 | 描述 |
|---|---|---|
AGENT_WALLCLOCK_TIMEOUT_MS | 600000(10 分钟) | ReAct 请求的绝对上限 |
AGENT_MAX_TOTAL_TOKENS | 200000 | 每个请求的 prompt+completion 总量 |
AGENT_MAX_CONTEXT_BYTES | 500000(500 KB) | 上下文数组大小上限 |
AGENT_MAX_TOOL_OUTPUT_LENGTH | 5000 | 每个 skill 的输出截断 |
AGENT_MAX_SKILL_EXECUTION_MS | 60000 | 每个 skill 的超时 |
AGENT_LOOP_DETECTION_WINDOW | 3 | N 个连续相同 tool+input 后中止 |
CHAT_MAX_CONCURRENT_PER_USER | 5 | 每个用户的聊天并发限制 |
Embedding / Vector DB / OCR / Synthesis
| 变量 | 默认值 | 描述 |
|---|---|---|
EMBEDDING_PROVIDER | local | local/openai/ollama |
EMBEDDING_MODEL | Xenova/all-MiniLM-L6-v2 | Embedding 模型 |
EMBEDDING_API_KEY | — | Embedding API 密钥 |
VECTOR_DB_PROVIDER | lancedb | lancedb/qdrant |
VECTOR_DB_URL / VECTOR_DB_API_KEY | — | 用于远程 Qdrant |
OCR_MODEL | glm-ocr:latest | 视觉 OCR 模型(model:version 格式) |
OCR_TIMEOUT | 600000 | 视觉 OCR 超时 |
SYNTHESIS_LLM_MODEL | gemma4:latest | 合成流水线模型 |
PUPPETEER_EXECUTABLE_PATH | — | 用于存档 PDF 导出的 Chromium |
许可证、推送、邮件、collector
| 变量 | 默认值 | 描述 |
|---|---|---|
LICENSE_KEY | — | Enterprise 许可证 JWT(HS256)。缺失 → Community |
LICENSE_SECRET | — | JWT 验证密钥(推荐);回退到 LICENSE_KEY |
COLLECTOR_SECRET | — | 必填 — server↔collector 共享密钥 |
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT | — | Web Push;未设置 → 在开发环境生成临时密钥 |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM | — | SMTP,用于密码重置 + 备份失败通知 |
LEGACY_PREVIOUS_ENCRYPTION_KEYS | — | 用于密钥轮换的 CSV base64 32 字节密钥 |
.env.example中但作为 DB settings 管理的变量(不在envSchema中):OCR_ENABLED、OCR_PRECHECK_CHARS、STORAGE_PATH(collector)、RESET_PASSWORD_STRATEGY。
环境变量 - collector
Schema:packages/collector/src/config/env.ts。
| 变量 | 默认值 | 必填 | 描述 |
|---|---|---|---|
COLLECTOR_PORT | 3210 | opt | HTTP 端口 |
COLLECTOR_URL | http://localhost:3210 | opt | 自身 URL |
SERVER_URL | http://localhost:3000 | opt | 服务器 URL(回调) |
EMBEDDING_PROVIDER | local | opt | local/openai/ollama |
EMBEDDING_MODEL | — | opt | Embedding 模型 |
EMBEDDING_API_KEY | — | opt | API 密钥 |
VECTOR_DB_PROVIDER | lancedb | opt | lancedb/qdrant |
VECTOR_DB_URL / VECTOR_DB_API_KEY | — | opt | Qdrant |
OLLAMA_BASE_URL | http://ollama:11434 | opt | Ollama |
STORAGE_PATH | ./storage | opt | 文件系统存储 |
COLLECTOR_SECRET | — | 必填 | 必须与服务器一致 |
环境变量 - widget
Schema:packages/widget/src/config/env.ts。
| 变量 | 默认值 | 必填 | 描述 |
|---|---|---|---|
NODE_ENV | development | opt | 环境 |
WIDGET_PORT | 3211 | opt | HTTP 端口 |
SERVER_URL | http://localhost:3000 | opt | 服务器 URL |
WIDGET_API_KEY | — | 必填 | widget↔server 共享密钥(必须一致) |
LOG_LEVEL | info | opt | 日志级别 |
前端:没有VITE_*变量,也没有.env文件。前端从服务器读取品牌/配置(GET /api/system/settings、GET /api/license/info)。
功能开关与许可证
定义于 packages/shared/src/constants/license.ts(FEATURE_FLAGS + 默认值)。在 packages/server/src/services/licenseService.ts 中验证。
如何从 Community 升级到 Enterprise
- 设置
LICENSE_KEY(HS256 JWT),建议同时设置LICENSE_SECRET。 jwt.verify→ payload 对照licensePayloadSchema验证(tier、iss、sub、iat、exp、features)。tier=enterprise→ 从ENTERPRISE_FEATURE_DEFAULTS启动 + JWT 的features覆盖。- 过期/无效 → 回退 Community 并警告。
优雅降级:getLicenseInfo() 在运行时检查过期时间;如果 Enterprise 在使用期间过期,无需重启即可降级到 Community。requireFeature(flag) → 禁用时返回 HTTP 402;requireFeatureLimit(flag, model) → 当 count >= limit 时返回 402(Enterprise = Infinity)。
始终开启的通用开关:自 EPA-02 移除后,8 个通用开关不再存在于 FEATURE_FLAGS 中 - webhooks、push_notifications、web search、memory、lead_export、widget_analytics、auto_title、synthesis_rate_limit 在 Community 中始终开启(通用路由无门控运行)。priority_support 不是开关:它是商业/SLA 合同(EPA-08)。剩余 11 个 enterprise 开关,包括 widget_credits_editing(widget 积分编辑,Community false / Enterprise true)和作为数字限制的 custom_agents(Community 3,Enterprise Infinity)。
开关表(11)
| 开关 | 类型 | Community | Enterprise | 控制内容 |
|---|---|---|---|---|
sso_enabled | bool | false | true | Enterprise SSO |
audit_log_immutable | bool | false | true | 审计日志不可篡改 |
white_label | bool | false | true | 品牌 BRANDING_* |
max_workspaces | num | 3 | Infinity | 创建工作区 |
max_projects | num | 3 | Infinity | 创建项目 |
custom_agents | num | 3 | Infinity | 自定义代理(限制) |
widget_enabled | bool | false | true | 可嵌入 widget |
max_widgets | num | 1 | Infinity | 创建 widget |
backup_enabled | bool | false | true | 备份系统 |
max_backup_destinations | num | 1 | Infinity | 备份目标 |
widget_credits_editing | bool | false | true | 编辑 widget 积分 |
运行时配置(DB SystemConfig)
可通过 GET/PUT /api/system/settings(批量)和 PUT /api/system/chat-retention(特定,已审计)修改。默认值位于 CONFIG_DEFAULTS(packages/shared/src/constants/permissions.ts)。
键分组:LLM(LLM_PROVIDER、LLM_MODEL、LLM_TEMPERATURE、...)、Embedding/Vector、服务器端口/URL、Auth(SESSION_EXPIRY)、品牌(BRANDING_APP_NAME、BRANDING_PRIMARY_COLOR、BRANDING_APP_SUBTITLE、BRANDING_APP_ICON_URL - 由 white_label 门控)、功能(DISABLE_TELEMETRY、ALLOW_REGISTRATION、DLP_ENABLED)、OCR(OCR_DEFAULT_MODEL、OCR_DEFAULT_MODE、OCR_DEFAULT_CUSTOM_INSTRUCTIONS、OCR_ENABLED、OCR_PRECHECK_CHARS)、合成(SYNTHESIS_LLM_PROVIDER_ID、SYNTHESIS_LLM_MODEL)、保留期(upload_draft_retention_days、ALLOW_NON_ADMIN_UPLOAD、chat_message_retention_days)。
PUT /api/system/settings 始终返回 200,带有 { updated: [...], rejected: [...] }(只读键被拒绝,其他键被应用)。
聊天消息保留(reaper)
服务:packages/server/src/services/chatMessageReaperJob.ts。DB 开关:chat_message_retention_days。
- 第 1 步(软删除):对超过
retentionDays的活跃聊天消息打墓碑。值为null/""/<=0时为 no-op。 - 第 2 步(硬清除):删除超过 7 天宽限期(硬编码)的墓碑行。无论保留配置如何始终运行(防 PII 泄露)。
- 节奏:24 小时
setInterval,首次运行于当地时间 03:00。isRunning互斥锁。 - 仅
PUT /api/system/chat-retention(已审计,confirmDataLoss: true,retentionDays: integer|null,最小 1);批量设置端点拒绝此键。
备份保留
按任务(BackupJob 上的 retentionDays 字段,默认 30)。backupRetentionService.ts 清理 cutoffDate 之后的日志/文件。它不是全局的。
加密密钥轮换
packages/server/src/services/encryptionService.ts(AES-256-GCM,32 字节密钥,16 字节 IV,盐 simmetric-chat-encryption-salt)。
- 当前密钥:设置时的
ENCRYPTION_KEY(base64 32 字节);否则scryptSync(JWT_SECRET, salt, 32)。 LEGACY_PREVIOUS_ENCRYPTION_KEYS(CSV base64 32 字节)→ 解密密钥链[current, ...previous, scryptLegacy?]。encrypt()使用chain[0]。没有自动重新加密任务:流程是手动用旧密钥解密 → 用新密钥重新加密。
交叉链接
- 设置端点:04 - API Reference
- 内部功能:05 - Feature Guide
- 部署环境变量:09 - Deployment