ThinkWatch 配置参考#
本文档提供 ThinkWatch 所有配置选项的完整参考。配置通过环境变量(用于基础设施和密钥)和数据库存储的动态设置(可通过管理员 Web UI 配置)相结合进行管理。
环境变量#
数据库#
DATABASE_URL#
| 属性 | 值 |
|---|---|
| 必填 | 是 |
| 默认值 | — |
| 示例 | postgres://thinkwatch:password@localhost:5432/thinkwatch |
PostgreSQL 连接字符串。ThinkWatch 需要 PostgreSQL 15 或更高版本。
安全说明:
- 在生产环境中,使用
sslmode=require强制加密连接:postgres://user:pass@host:5432/db?sslmode=require - 避免在提交到版本控制的 URL 中嵌入密码。请使用密钥管理器。
- 数据库用户需要创建表的权限(用于迁移),或者应单独应用迁移。
- 直连数据库,或经由会话模式的连接池,不能使用事务模式:启动时建立数据库结构需要持有会话级 advisory lock。详见外部 PostgreSQL 与 Redis。
REDIS_URL#
| 属性 | 值 |
|---|---|
| 必填 | 是 |
| 默认值 | — |
| 示例 | redis://localhost:6379 |
Redis 连接字符串。用于速率限制、OIDC 状态/nonce 存储和会话管理。
安全说明:
- 在生产环境中,启用 Redis 认证:
redis://:yourpassword@host:6379 - 对于启用了 TLS 的 Redis,使用
rediss://协议:rediss://:password@host:6380 - Redis Cluster 使用
redis-cluster://协议(TLS 为rediss-cluster://)。证书由私有 CA 签发时,把REDIS_CA_CERT设为该 CA 的 PEM 文件路径。详见外部 PostgreSQL 与 Redis。
密码学#
JWT_SECRET#
| 属性 | 值 |
|---|---|
| 必填 | 是 |
| 默认值 | — |
| 示例 | a3f8c1e0b9d74...(64 字符十六进制字符串) |
用于 HS256 JWT 签名和验证的共享密钥。必须至少 32 个字符长,建议至少为 256 位(32 字节 / 64 个十六进制字符)以确保足够的安全性。启动时,ThinkWatch 会对此值执行熵检查,如果不满足最低长度要求将拒绝启动。
安全说明:
- 使用以下命令生成:
openssl rand -hex 32 - 最少需要 32 个字符。 更短的值将导致启动失败。
- 更改此值会使所有活跃的访问令牌和刷新令牌失效,迫使所有用户重新认证。
- 切勿在不同环境(开发/预发布/生产)之间重用此值。
- 存储在密钥管理器中,而非明文文件。
ENCRYPTION_KEY#
| 属性 | 值 |
|---|---|
| 必填 | 是 |
| 默认值 | — |
| 示例 | b7e4d219f0c83...(64 字符十六进制字符串) |
256 位密钥(32 字节,编码为 64 个十六进制字符),用于敏感数据的 AES-256-GCM 静态加密(提供商 API 密钥、MCP 服务器认证密钥)。
安全说明:
- 使用以下命令生成:
openssl rand -hex 32 - 更改此值会使所有先前加密的数据(提供商 API 密钥、MCP 认证密钥)不可读。轮换后必须重新输入这些值。
- 这是系统中最关键的密钥。此密钥泄露将暴露所有存储的提供商凭证。
- 在生产环境中存储在硬件安全模块(HSM)或密钥管理器中。
服务器#
SERVER_HOST#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | 0.0.0.0 |
| 示例 | 127.0.0.1 |
服务器绑定的 IP 地址。使用 0.0.0.0 监听所有接口,或使用 127.0.0.1 仅限本地访问。
GATEWAY_PORT#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | 3000 |
| 示例 | 8080 |
网关服务器的 TCP 端口(OpenAI 兼容 API 和 MCP 传输)。
CONSOLE_PORT#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | 3001 |
| 示例 | 8081 |
控制台服务器的 TCP 端口(管理 API 和管理员端点)。
CORS_ORIGINS#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | http://localhost:5173 |
| 示例 | https://console.example.com,https://admin.example.com |
逗号分隔的允许 CORS 来源列表。每个来源必须包含协议(http:// 或 https://),且不能包含尾部斜杠。
安全说明:
- 在生产环境中,仅限制为托管控制台前端的域名。
- 不要使用
*,这会完全禁用 CORS 保护。
可观测性#
CLICKHOUSE_URL#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | — |
| 示例 | http://clickhouse:8123 |
ClickHouse HTTP 接口的基础 URL,用于审计日志存储和搜索。如果未设置,审计日志写入 ClickHouse 将不可用(条目仍会记录到标准输出并转发至已配置的日志转发器)。
CLICKHOUSE_DB#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | think_watch |
| 示例 | think_watch_prod |
存储审计日志表的 ClickHouse 数据库名称。ThinkWatch 会在启动时自动创建数据库和表(如果不存在)。
CLICKHOUSE_USER#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | default |
| 示例 | thinkwatch |
ClickHouse 认证用户。
CLICKHOUSE_PASSWORD#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | — |
| 示例 | your-clickhouse-password |
ClickHouse 认证密码。
安全说明:
- 在生产环境中,务必为 ClickHouse 设置强密码。
- 存储在密钥管理器中,而非明文文件。
RUST_LOG#
| 属性 | 值 |
|---|---|
| 必填 | 否 |
| 默认值 | info |
| 示例 | thinkwatch=debug,tower_http=debug,sqlx=warn |
使用 tracing-subscriber EnvFilter 语法 控制日志详细程度。接受逗号分隔的 target=level 形式的指令。
常用配置:
| 用例 | 值 |
|---|---|
| 生产环境 | info 或 thinkwatch=info,tower_http=warn |
| 调试 HTTP | thinkwatch=debug,tower_http=debug |
| 调试 SQL | thinkwatch=debug,sqlx=debug |
| 最少输出 | warn |
OIDC / SSO#
要启用 SSO,必须同时设置所有四个 OIDC 变量。如果缺少任何一个,OIDC 将被禁用,GET /api/auth/sso/authorize 将返回 500。
OIDC_ISSUER_URL#
| 属性 | 值 |
|---|---|
| 必填 | 否(如需 SSO 则必填) |
| 默认值 | — |
| 示例 | https://login.microsoftonline.com/tenant-id/v2.0 |
OIDC 发行者 URL。ThinkWatch 在启动时从此 URL 获取 .well-known/openid-configuration 文档以发现端点。
OIDC_CLIENT_ID#
| 属性 | 值 |
|---|---|
| 必填 | 否(如需 SSO 则必填) |
| 默认值 | — |
| 示例 | abcdef12-3456-7890-abcd-ef1234567890 |
在 OIDC 身份提供商注册的客户端 ID。
OIDC_CLIENT_SECRET#
| 属性 | 值 |
|---|---|
| 必填 | 否(如需 SSO 则必填) |
| 默认值 | — |
| 示例 | your-client-secret-value |
OIDC 应用的客户端密钥。
安全说明:
- 这是敏感凭证。请存储在密钥管理器中。
- 根据身份提供商的建议定期轮换。
OIDC_REDIRECT_URL#
| 属性 | 值 |
|---|---|
| 必填 | 否(如需 SSO 则必填) |
| 默认值 | — |
| 示例 | https://console.example.com/api/auth/sso/callback |
OIDC 提供商在认证后重定向到的完整 URL。此 URL 必须与在身份提供商配置中注册的重定向 URI 完全匹配,并且必须可被用户浏览器访问。
提供商配置#
提供商通过管理员 Web UI(管理 > 提供商)或 POST /api/admin/providers API 进行配置。每种提供商类型有特定的配置要求:
OpenAI#
| 字段 | 值 |
|---|---|
provider_type | openai |
base_url | https://api.openai.com |
api_key | sk-...(OpenAI API 密钥) |
前缀为 gpt-、o1-、o3-、o4- 的模型会自动路由到此提供商。
Anthropic#
| 字段 | 值 |
|---|---|
provider_type | anthropic |
base_url | https://api.anthropic.com |
api_key | sk-ant-...(Anthropic API 密钥) |
前缀为 claude- 的模型会自动路由到此提供商。
Google Gemini#
| 字段 | 值 |
|---|---|
provider_type | google |
base_url | https://generativelanguage.googleapis.com |
api_key | Google AI API 密钥 |
前缀为 gemini- 的模型会自动路由到此提供商。
Azure OpenAI#
| 字段 | 值 |
|---|---|
provider_type | azure |
base_url | https://{resource}.openai.azure.com |
api_key | Azure API 密钥 |
config_json | {"api_version": "2024-12-01-preview"} |
Azure OpenAI 使用 api-key 请求头(非 Bearer 令牌)进行认证。api_version 作为查询参数传递。Azure 不支持自动模型路由——必须通过管理 > 模型页面显式注册模型。
AWS Bedrock#
| 字段 | 值 |
|---|---|
provider_type | bedrock |
base_url | AWS 区域(如 us-east-1) |
api_key | ACCESS_KEY_ID:SECRET_ACCESS_KEY |
AWS Bedrock 通过官方 aws-sigv4 Rust crate 使用 SigV4 请求签名。base_url 字段指定 AWS 区域,api_key 字段包含格式为 ACCESS_KEY_ID:SECRET_ACCESS_KEY 的 IAM 凭证。流式传输使用原生 Bedrock 二进制 event-stream 协议(Converse API)。与 Azure 类似,Bedrock 不支持自动模型路由——必须通过管理 > 模型页面显式注册模型。
自定义(OpenAI 兼容)#
| 字段 | 值 |
|---|---|
provider_type | custom |
base_url | 任何 OpenAI 兼容端点 URL |
api_key | 端点的 Bearer 令牌 |
用于自托管模型(vLLM、Ollama、LiteLLM 等)或任何实现了 OpenAI API 格式的第三方服务。
提供商自动加载#
启动时,ThinkWatch 从数据库加载所有活跃提供商并注册到模型路由器。模型路由基于:
- 前缀匹配(OpenAI、Anthropic、Google):匹配提供商默认前缀的模型会自动路由。
- 显式注册(Azure、Bedrock、自定义):模型必须通过管理 > 模型页面注册,并指定提供商分配。
配置模式#
开发环境与生产环境对比#
| 设置 | 开发环境 | 生产环境 |
|---|---|---|
DATABASE_URL | 本地 PostgreSQL,不使用 SSL | 托管 PostgreSQL,使用 sslmode=require |
REDIS_URL | 本地 Redis,无认证 | Redis,使用 requirepass 或托管 Redis |
JWT_SECRET | 任意稳定值,方便开发 | 密码学随机 256 位密钥 |
ENCRYPTION_KEY | 任意稳定的 32 字节十六进制值,方便开发 | 密码学随机,存储在 HSM 中 |
CORS_ORIGINS | http://localhost:5173 | https://console.yourdomain.com |
RUST_LOG | thinkwatch=debug,tower_http=debug | info 或 thinkwatch=info |
| OIDC 变量 | (除非测试 SSO 否则未设置) | 完整配置 |
环境文件#
ThinkWatch 在项目根目录使用单一 .env 文件:
- 开发环境:
cargo run通过dotenvy自动加载,docker compose通过 Makefile 中的--env-file .env加载 - 生产环境:使用项目根目录的
.env.production,通过make deploy加载
deploy/ 目录下没有单独的 .env 文件。复制 .env.example 开始:
cp .env.example .env
# 编辑 .env 填入实际值,或运行:
# deploy/generate-secrets.sh
使用 .env 文件#
本地开发时,在项目根目录创建 .env 文件:
# .env (DO NOT commit this file)
DATABASE_URL=postgres://thinkwatch:devpass@localhost:5432/thinkwatch
REDIS_URL=redis://localhost:6379
JWT_SECRET=dev-only-jwt-secret-do-not-use-in-production-000000
ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
CORS_ORIGINS=http://localhost:5173
RUST_LOG=thinkwatch=debug,tower_http=debug
ThinkWatch 通过 dotenvy crate 自动加载 .env 文件。.env 文件应列在 .gitignore 中。
Docker / Docker Compose#
通过 docker-compose.yml 传递环境变量:
services:
thinkwatch:
image: thinkwatch:latest
environment:
DATABASE_URL: postgres://thinkwatch:${DB_PASSWORD}@postgres:5432/thinkwatch
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
CORS_ORIGINS: https://console.example.com
CLICKHOUSE_URL: http://clickhouse:8123
RUST_LOG: info
ports:
- "3000:3000"
- "3001:3001"
使用 .env 文件或 shell 导出来提供 ${DB_PASSWORD}、${JWT_SECRET} 和 ${ENCRYPTION_KEY},避免在 compose 文件中硬编码。
Kubernetes Secrets#
为敏感值创建 Kubernetes Secret:
kubectl create secret generic thinkwatch-secrets \
--from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
--from-literal=ENCRYPTION_KEY="$(openssl rand -hex 32)" \
--from-literal=DATABASE_URL="postgres://user:pass@pg-host:5432/thinkwatch?sslmode=require" \
--from-literal=REDIS_URL="rediss://:password@redis-host:6380" \
--from-literal=OIDC_CLIENT_SECRET="your-oidc-secret"
在 Deployment 中引用该 Secret:
apiVersion: apps/v1
kind: Deployment
metadata:
name: thinkwatch
spec:
template:
spec:
containers:
- name: thinkwatch
image: thinkwatch:latest
envFrom:
- secretRef:
name: thinkwatch-secrets
env:
- name: CORS_ORIGINS
value: "https://console.example.com"
- name: GATEWAY_PORT
value: "3000"
- name: CONSOLE_PORT
value: "3001"
- name: CLICKHOUSE_URL
value: "http://clickhouse:8123"
- name: RUST_LOG
value: "info"
生成安全随机密钥#
使用 openssl 生成密码学安全的随机值:
# Generate a 256-bit key (for JWT_SECRET or ENCRYPTION_KEY)
openssl rand -hex 32
# Generate a 128-bit key (for less critical uses)
openssl rand -hex 16
# Generate a base64-encoded key (alternative format)
openssl rand -base64 32
JWT_SECRET 和 ENCRYPTION_KEY 都期望 64 字符的十六进制字符串(代表 32 字节 / 256 位)。
动态配置(Web UI)#
许多之前需要通过环境变量配置的设置现在存储在数据库中,可通过管理员 Web UI 在运行时配置。这些设置也可通过 GET/PATCH /api/admin/settings API 端点管理。
动态设置的更改立即生效,无需重启服务器。
认证与 JWT#
| 设置 | 默认值 | 描述 |
|---|---|---|
jwt_access_ttl_seconds | 900 | 访问令牌有效期(秒,默认 15 分钟) |
jwt_refresh_ttl_seconds | 604800 | 刷新令牌有效期(秒,默认 7 天) |
缓存#
| 设置 | 默认值 | 描述 |
|---|---|---|
cache_ttl_seconds | 300 | 各种缓存的默认 TTL |
安全#
| 设置 | 默认值 | 描述 |
|---|---|---|
signature_drift_seconds | 300 | 签名请求允许的最大时钟偏差 |
nonce_ttl_seconds | 300 | 用于重放保护的 nonce 值 TTL |
security.redact | {} | 出站脱敏的策略,见请求防护 |
security.inspect_tools | {} | 工具调用审查的策略,见请求防护 |
security.content | {} | 内容过滤的策略,见请求防护 |
security.rate_limit_fail_closed | false | 限额无法检查时拒绝请求,见无法检查限额时 |
请求防护#
出站脱敏、工具调用审查和内容过滤各以一个 JSON 对象保存完整的策略,分别位于 security.redact、security.inspect_tools 和 security.content。对象的结构与 ThinkWatch Core 的 config.yaml 中 security 一节相同,各字段与全部内置规则见 Core 配置手册;{} 即出厂设置。
mode为off、observe或enforce。出厂为observe:命中的内容记入审计日志,不做任何改动;enforce在控制台中按作用命名,出站脱敏为「替换」,工具调用审查为「切断」,内容过滤为「处置」。enable和disable按 id 启用或停用内置规则,actions改变内置规则在enforce下的处置,custom保存部署自己的规则。- 出站脱敏的自定义规则以
match指定匹配方式:contains(默认)按原样查找pattern,.、*、\d等没有特殊含义;regex把pattern作为正则表达式。case_sensitive为true时区分大小写,否则不区分。控制台中两项分别为「匹配方式」和「区分大小写」。 - 出站脱敏不查找字段名;自定义脱敏规则还会跳过
id、call_id、item_id、tool_use_id、tool_call_id、toolUseId、previous_response_id、response_id、type、role、name、namespace、model、status、object和phase的值。内置规则照常查找这些值,其余的值由所有规则照常查找。按audit.body_redact_pii对存储的请求体脱敏时同样如此。
{
"mode": "enforce",
"enable": ["email"],
"custom": [{ "name": "员工编号", "pattern": "EMP-\\d{6}", "match": "regex", "label": "EMPLOYEE" }]
}
控制台的「内容安全」页编辑这些对象。经 PATCH /api/admin/settings 写入时,一个键整体写入,写入前先校验:字段名拼错、内置规则 id 不存在、正则无法编译或码位写法有误时返回 400。写入 security.redact 需要 pii_redactor:write 权限,另外两个键需要 content_filter:write。GET /api/admin/security 列出每项防护的档位与全部规则,POST /api/admin/security/{guard}/test 用一段样本测试这些规则。ThinkWatch 3.0 至 3.6 保存的自定义脱敏规则不带 match 和 case_sensitive,自 3.7.0 起按原样查找文字、不区分大小写;按正则表达式编写的规则需要加上 "match": "regex",需要区分大小写的再加上 "case_sensitive": true。3.6 读不了使用这两个键的脱敏策略,因此应在所有副本都升级到 3.7.0 之后再修改。从仍使用 security.content_filter_patterns、security.pii_redactor_patterns、security.hidden_text 和 security.tool_inspection 的版本升级时,这些设置在首次启动时转换为上述三个键,原有的脱敏写法转换为区分大小写的 regex 规则。
无法检查限额时#
请求的限额无法检查时,即 Redis 无法访问,或加载请求的限流与预算时数据库出错,由 security.rate_limit_fail_closed 决定如何处理。这一设置覆盖用户、API 密钥与角色的限流和预算,以及路由的 RPM 与 TPM 上限,对 AI 网关和 MCP 网关都有效。
false(默认):请求照常进行,不受无法检查的限额约束;错误作为警告写入日志,并计入*_fail_open_total指标。true:拒绝请求,标签为limits_unavailable、budget_unavailable或rate_limiter_unavailable。AI 网关按客户端的 API 格式返回429和Retry-After: 30;MCP 网关返回429和 JSON-RPC 错误。
漏算限额比拒绝请求更糟时,应打开这一设置。无论如何设置,限额无法读取时 GET /v1/usage 都返回 503。在控制台中,这一设置是「设置 › 安全 › 限速引擎」中的「限额无法检查时拒绝请求」。
预算#
| 设置 | 默认值 | 描述 |
|---|---|---|
budget_warning_threshold | 0.8 | 触发警告的预算使用率 |
budget_critical_threshold | 0.95 | 触发严重告警的预算使用率 |
API 密钥策略#
| 设置 | 默认值 | 描述 |
|---|---|---|
api_key_max_expiry_days | 365 | 新 API 密钥允许的最大过期天数 |
api_key_default_rate_limit_rpm | 60 | 新 API 密钥的默认 RPM 限制 |
通用#
| 设置 | 默认值 | 描述 |
|---|---|---|
data_retention_days | 30 | 软删除记录在清除前保留的天数 |
site_name | ThinkWatch | Web UI 中显示的站点名称 |
启动验证#
ThinkWatch 在启动时验证所有配置和依赖,如果出现以下情况将拒绝启动:
DATABASE_URL缺失或数据库不可达REDIS_URL缺失或 Redis 不可达JWT_SECRET缺失或少于 32 个字符JWT_SECRET未通过熵检查(例如全部相同字符)ENCRYPTION_KEY缺失或不是有效的 64 字符十六进制字符串OIDC_*变量部分配置(必须全部设置或全部不设置)
此外,服务器在启动时执行依赖健康检查:
- PostgreSQL: 验证连接并运行测试查询
- Redis: 通过 PING 命令验证连接
- ClickHouse: 如已配置,验证连接和表是否存在(非阻塞 — 不可用时记录警告)
如果服务器启动失败,请检查应用日志。缺失或无效的配置将以清晰的错误消息报告。