主题
环境变量、凭据与降级开关
本章目标:
- 知道凭据/配置
.env分布在哪几处、各管什么、为什么不入库- 拿到一张完整的"紧急降级开关"速查表,生产出事时知道关哪个
- 理解 env 变更的部署铁律(为什么
restart不行、必须--force-recreate)
TL;DR
a-cdm 的敏感凭据走 .env,被 .gitignore 排除不入库,分三处:acdm-backend/.env(DB/Nextcloud/Keycloak/SECRET_KEY/网关 key)、deer-flow/.env(引擎 key + RUNTIME_BACKEND)、deer-flow/frontend/.env.local(可选)。控制面配置 schema 在 acdm-backend/app/config.py 的 Settings(pydantic BaseSettings)。一组以 ACDM_*_ENABLED / COOKIE_SECURE / RUNTIME_BACKEND 命名的开关构成"紧急降级层":出事时改 .env 关掉对应能力即可回退,但改 .env 后必须 up -d --force-recreate,restart 不重读 .env。
Overview(为什么要有一层"降级开关")
a-cdm 把鉴权、授权、可逆操作、速率限制、Aegra 运行时等高风险能力逐个做成可一键关闭的开关,核心理由是生产事故响应:这些能力任何一个出 bug 都可能让全站不可用,而完整回滚代码 + 重新 CI + 部署要十几分钟。降级开关让运维能在 1 分钟内把出问题的能力降级到"上一个已知好状态",争取排查时间 —— 这是"安全要能快速关掉"的工程化。
每个开关都遵循同一约定:默认开启(生产行为),设为 false/0/no 降级到旧行为,且 dev 与 prod 同一套开关无后门。
Architecture:.env 分布与不入库约束
.gitignore:2-5 排除 .env / .env.* / *.env.local,只放行 .env.example。三处 .env(CLAUDE.md:16):
| 文件 | 管什么 | 加载方式 | Source |
|---|---|---|---|
acdm-backend/.env | DATABASE_URL、NEXTCLOUD_、KEYCLOAK_、SECRET_KEY、JOINTPILOT/AICC/VOLCENGINE key、GITLAB_WIKI_PAT | pydantic Settings,env_file | acdm-backend/app/config.py:240-242 |
deer-flow/.env | 引擎 LLM key、RUNTIME_BACKEND、Aegra DB URL | docker compose env_file / 进程环境 | deploy/prod/deer-flow-backend/compose.yml:114 |
deer-flow/frontend/.env.local | 前端可选环境变量(NEXT_PUBLIC_*) | Next.js 编译/运行期 | CLAUDE.md:16 |
生产 /data/apps/*/.env 手工 scp,不进库(CLAUDE.md:16)。已废字段:API_SECRET_TOKEN / NEXT_PUBLIC_ACDM_TOKEN(CLAUDE.md:16)。
控制面配置由 acdm-backend/app/config.py:17 的 Settings(BaseSettings) 集中声明,model_config = SettingsConfigDict(env_file=...)(config.py:240-242)指向 acdm-backend/.env。DATABASE_URL 是唯一必填(Field(...),config.py:19),其余多有默认值;敏感字段留空时不崩,而是相关端点返 503(如 KEYCLOAK_CLIENT_SECRET 空 → /auth/login 503,config.py:42-44;GITLAB_WIKI_PAT 空 → /wiki/* 503,config.py:113-115)——与 ai_registry 的 graceful degradation 同一哲学。
关键凭据清单
| env | 用途 | 空时行为 | 红线 | Source |
|---|---|---|---|---|
DATABASE_URL | asyncpg 连接串(必填) | 启动失败 | — | config.py:19 |
SECRET_KEY | HS256 JWT 签发密钥 | — | ❌ 禁入库,openssl rand -hex 32 | config.py:54-56 |
KEYCLOAK_CLIENT_SECRET | Keycloak OAuth client secret | /auth/login 503 | ❌ 禁入库 | config.py:42-44 |
JOINTPILOT_API_KEY | 主网关 LLM 凭据 | 相关 role 503(graceful) | 先改 .env 再 merge | config.py:122-124 |
GITLAB_WIKI_PAT | llm-wiki GitLab PAT | /wiki/* 503 | ❌ 禁入库 | config.py:113-115 |
ACDM_MCP_SERVICE_TOKEN | service-to-service token | /mcp/* 拒所有请求 | — | config.py:177-179 |
NEXTCLOUD_PASSWORD | WebDAV 凭据 | 存储操作失败 | ❌ 禁入库 | config.py:25 |
红线 CLAUDE.md(红线段)三条相关:不把 KEYCLOAK_CLIENT_SECRET/SECRET_KEY/BETTER_AUTH_SECRET 写进库;新增/rotate xxx_API_KEY 必须先 ssh prod 加 .env → 再 merge → 再 force-recreate(L94 教训)。
紧急降级开关速查表
这是本章最该收藏的一张表 —— 生产出事时按现象关对应开关:
| 开关 env | 默认 | 降级到 | 关掉用于 | 读取点 | Source |
|---|---|---|---|---|---|
ACDM_PROJECT_ACCESS_ENABLED | true | Phase 1(所有成员校验放行) | 三级授权出 bug 锁死用户 | acdm-backend/app/auth/dependencies.py:38 | acdm-backend/app/auth/dependencies.py:13 |
ACDM_THREAD_AUTH_ENABLED | true | 所有 thread auth handler 立即放行 | owner_sub 隔离误伤 | deer-flow/backend/.../auth/acdm_auth.py:40 + app/gateway/acdm_thread_auth.py:42 | acdm_auth.py:20 |
RUNTIME_BACKEND | langgraph(compose 默认) | langgraph ← 从 aegra 回退 | Aegra 0.9.4 出问题 | deploy/prod/deer-flow-backend/compose.yml:84 | compose.yml:70-73 |
COOKIE_SECURE | True(生产) | False(允许 HTTP cookie) | HTTPS 故障需走 HTTP IP 兜底 | config.py:61-63 | config.py:61-63 |
ACDM_RATE_LIMIT_ENABLED | true | 关 /auth/login 限速 | 限速误伤正常登录 | config.py:81-83 | config.py:81-88 |
ACDM_PUBLIC_HOSTS | 三 host 白名单 | 空 = 不做 host 白名单 | 公网边缘加固误拦 | config.py:69-71 | config.py:69-79 |
ACDM_AI_NC_WRITES_ENABLED | true | via_agent 写 NC 被拒(40310) | AI 误写文件需紧急止血 | config.py:184-186 | config.py:184-190 |
ACDM_REVERSIBLE_STORAGE_BYPASS | false | ReversibleStorageClient 退化为直代理 | 可逆框架本身出 bug | config.py:217-219 | config.py:217-219 |
说明:
RUNTIME_BACKEND在compose.yml:114的默认是langgraph,但生产.env实际设为aegra(2026-04-25 起,见CLAUDE.md:14);"降级"指把.env改回langgraph或删该行让 cron 自动重启回退(compose.yml:22)。
配额与保留期配置(可逆操作相关)
| env | 默认 | 含义 | Source |
|---|---|---|---|
ACDM_AI_NC_DAILY_BYTES_CAP | 10 GB | via_agent 每日写入上限,超出返 40312 | config.py:192-194 |
ACDM_AI_NC_TRASH_RETENTION_DAYS | 90 | .acdm-trash 保留天数,超期 cron 清理 | config.py:197-199 |
ACDM_DB_PRUNE_RETENTION_DAYS | 90 | 软删行保留天数,0=关闭 auto prune | config.py:204-206 |
ACDM_AI_NC_BACKUP_THRESHOLD_MB | (见字段) | 小文件备份到 .acdm-versions 的大小阈值 | config.py:209-211 |
MAX_UPLOAD_BYTES | 200 MB | 单文件上传上限 | config.py:226 |
这些在"可逆 DB 操作与存储层"章详解。
Common Pitfalls / 实战 Tips
restart不重读 .env(L88):改完.env用docker compose up -d --force-recreate <svc>,不是restart。这是最高频踩坑(CLAUDE.md:14)。- 新增/rotate API key 顺序:先 ssh prod 加 .env → 再 merge → 再 force-recreate。反过来会触发 fail-fast/503(L94,
CLAUDE.md红线)。 COOKIE_SECURE=True的副作用是预期:HTTP IP 路径业务不可用(cookie 不发)是有意设计,不是 bug —— IP 路径仅作 HTTPS 故障时静态资源兜底(CLAUDE.md:15)。- 降级开关不是常开配置:它们是"出事临时关、修好再开"的应急阀门,长期 false 等于带病运行。
- dev 与 prod 同一套开关:本地不起 Caddy 不代表开关失效,前端 proxy 仍按同一逻辑工作。
References
acdm-backend/app/config.py:17-242— 控制面 Settings 全字段(本章主源).gitignore:2-5—.env不入库规则acdm-backend/app/auth/dependencies.py:13-42—ACDM_PROJECT_ACCESS_ENABLED降级读取deer-flow/backend/packages/harness/deerflow/auth/acdm_auth.py:20-40—ACDM_THREAD_AUTH_ENABLED降级读取deploy/prod/deer-flow-backend/compose.yml:14-114—RUNTIME_BACKEND切换/回退机制CLAUDE.md:14-16— 凭据分布、降级开关清单、env 部署铁律(线索,已回源核实)
Related Pages
| Page | Relationship |
|---|---|
| AI 角色注册表与模型矩阵 | 本章网关凭据为该章 graceful degradation 提供输入 |
| 鉴权与授权双层体系 | 本章 ACDM_PROJECT_ACCESS_ENABLED/ACDM_THREAD_AUTH_ENABLED 是该章两层的降级阀 |
| Aegra 运行时与 LangGraph | 本章 RUNTIME_BACKEND 切换在该章详解 |
| 可逆 DB 操作与存储层 | 本章配额/保留期/bypass 开关在该章对应到机制 |
| CI/CD 与生产部署运维 | 本章 --force-recreate 铁律是该章部署流程的一部分 |