Files
fire-safety-ymd/docs/project/operations/docker-test-deployment.md
2026-09-06 01:40:15 +08:00

582 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 测试服务器 Docker 部署手册
| 项 | 约定 |
| --- | --- |
| 目标服务器目录 | /home/firee-safety-ymd |
| 运行方式 | 宿主机 Nginx 终止 HTTPS,Docker Compose 运行单个 Go 服务 |
| 应用监听边界 | 容器内监听 8080,只发布到宿主机 127.0.0.1:16587 |
| 数据库 | 使用现有 PostgreSQL/PostGIS;本手册不创建数据库容器、不导入 SQL |
| 状态 | 测试环境部署基线;目标机镜像构建和 Nginx 语法检查已由现场截图证明通过,但容器稳定运行、页面资源、公网 TLS 和 SuperAgent MCP 完整工具链仍待重测 |
本手册不包含真实 Token、API Key、数据库密码或旧 Nginx 配置内容。命令中的尖括号是服务器上需要替换的占位符;不要把 Secret 写进命令行、Nginx 文件、镜像构建参数或 Git。
## 1. 部署前提
本仓库已有可由服务器获取的 `main` 分支。后续部署仍必须由项目维护者先审阅本地变更、手动 commit 并 push 到远程仓库;本项目不会自动提交、推送或迁移服务器数据。以下命令以发布 `main` 分支为例。
在开发机提交前先检查暂存区,不要把 `.env` 或被忽略的原始 SQL 强制加入 Git:
~~~bash
cd /Users/andy/IdeaProjects/fire-safety-ymd
git status --short
git diff --cached --stat
# 审阅后通过 IDE 或逐项执行 git add,只暂存确定要发布的路径。
git commit
git push -u origin main
~~~
服务器需要具备:
- Git、Docker Engine 和 Docker Compose v2(命令为 docker compose)。
- 宿主机 Nginx、可由 Nginx 读取的 agent.nianxx.com 证书和私钥。
- 到 PostgreSQL/PostGIS 的网络访问;数据库账号应为运行时只读账号。
- 如由 SuperAgent 访问 MCP,防火墙、安全组和 TLS 必须允许按约定访问 443。
先确认工具版本,不要把配置文件内容打印到日志:
~~~bash
git --version
docker --version
docker compose version
nginx -v
~~~
如果 Nginx 尚未安装,可按发行版选择一种方式;用户已安装时跳过:
~~~bash
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y nginx
~~~
~~~bash
# RHEL/CentOS/Fedora 系
sudo dnf install -y nginx
~~~
安装后确认服务:
~~~bash
sudo systemctl enable --now nginx
sudo systemctl status nginx --no-pager
~~~
## 2. 获取代码到 /home/firee-safety-ymd
### 首次部署
维护者完成 commit/push 后,在服务器上执行。私有仓库使用服务器已有的 Git 凭证管理方式,不要把 Git 密码或 Token 写入命令:
~~~bash
sudo mkdir -p /home/firee-safety-ymd
sudo chown "$(id -un):$(id -gn)" /home/firee-safety-ymd
git clone https://git.nianxx.cn/huangting/fire-safety-ymd.git /home/firee-safety-ymd
cd /home/firee-safety-ymd
git switch main
~~~
如果目录已经是本仓库的工作副本,先确认远程、分支和工作树,再只做快进更新:
~~~bash
cd /home/firee-safety-ymd
git remote -v
git status --short
git fetch --prune origin
git switch main
git pull --ff-only origin main
~~~
若 git status --short 有服务器本地改动,先确认归属;不要用 git reset --hard 或覆盖本地文件排障。
## 3. 创建服务器 .env
.env.example 只是字段说明,不能作为运行时 Secret 文件。首次部署时复制并收紧权限:
~~~bash
cd /home/firee-safety-ymd
cp .env.example .env
chmod 600 .env
~~~
用服务器上的受保护编辑器填写 .env,至少确认以下值。三个凭证必须分别生成,不能相互复用:
~~~text
FIRE_SAFETY_HTTP_ADDR=:8080
# Build-time only. Keep the official default unless the target server cannot
# reach it; this value is not passed to the running application container.
FIRE_SAFETY_BUILD_GOPROXY=https://proxy.golang.org,direct
FIRE_SAFETY_SUPERAGENT_ENABLED=true
FIRE_SAFETY_SUPERAGENT_BASE_URL=https://<superagent-api-host>
FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY=<superagent-open-api-key>
# 当前外部应用策略关闭 Trace 时保持 false;只有策略显式允许 Trace 才设置 true
FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE=false
FIRE_SAFETY_CHAT_ENABLED=true
FIRE_SAFETY_CHAT_AUTH_TOKEN=<独立的高熵xtoken>
# 新环境必须保持 false;仅当已交付旧客户端的短凭证无法立即更换时,
# 才能在受控测试/迁移窗口临时设置 true,轮换后恢复 false。
FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=false
FIRE_SAFETY_CHAT_SUBJECT_ID=fire-safety-ymd-chat-test-subject
FIRE_SAFETY_CHAT_COMPAT_APP_ID=<与Nginx路径完全一致的公开app-id>
# 可选的受控测试页面默认关闭;开启时必须同时启用 Chat/兼容入口
FIRE_SAFETY_CHAT_PAGE_ENABLED=false
# 页面从 agent.nianxx.com 同源加载时必须包含该精确 Origin;可按需追加其他测试 Origin
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS=https://agent.nianxx.com
FIRE_SAFETY_MCP_ENABLED=true
FIRE_SAFETY_MCP_AUTH_TOKEN=<独立的高熵MCP-bearer>
FIRE_SAFETY_MCP_SCOPE_MODE=all
FIRE_SAFETY_MCP_ALLOWED_TOWNS=
FIRE_SAFETY_POSTGIS_ENABLED=true
FIRE_SAFETY_POSTGIS_DSN=<只读PostgreSQL连接串>
FIRE_SAFETY_POSTGIS_MIGRATION_DSN=
FIRE_SAFETY_POSTGIS_EXPECTED_SRID=4326
~~~
注意:
- FIRE_SAFETY_CHAT_AUTH_TOKEN 只用于兼容对话入口的 xtoken;FIRE_SAFETY_MCP_AUTH_TOKEN 只用于 SuperAgent 调用 /mcp;FIRE_SAFETY_SUPERAGENT_OPEN_API_KEY 只用于 Go 服务访问 SuperAgent。三者必须不同。
- Chat Token 默认至少 32 个可打印 ASCII 字符。如果已交付的旧客户端凭证较短且无法立即更换,只能临时设置 `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=true`;此时仍要求 Token 非空、不超过 4096 字节,并且每个字符都在 ASCII `0x21-0x7e` 范围内(无空格、控制字符或 Unicode)。该开关只影响 Chat 原生 Bearer 和兼容 `xtoken`,不放宽 MCP Token 至少 32 个可打印 ASCII 字符的要求。新环境不得开启,凭证轮换完成后必须恢复 `false`;开关启用时启动日志会记录不含 Secret 的安全 warning。
- FIRE_SAFETY_CHAT_COMPAT_APP_ID 是公开路径标识,不是 Secret;它必须与 Nginx 的精确 location = /api/v1/apps/<app-id>/completion 完全一致。
- FIRE_SAFETY_CHAT_PAGE_ENABLED 默认必须为 false。开启后 Go 提供 `/chat`、`/chat/` 以及同源的
`/chat/app.css`、`/chat/app.js`;`/chat` 会 308 到 `/chat/`,页面和资源的最终可用性仍由 Go
开关决定。页面只用于受控测试,不嵌入或持久化 Token。
- FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE 默认必须为 false。无 Trace 的严格成功条件是最终 AI 消息
`finish_reason=stop`、非空顶层 `event: message.final` 加顶层 `event: end`;设置为 true 时还要求
`run.completed(status=success)`,且外部应用策略必须开启 `trace_policy.enabled=true`。无 Trace
只是不返回公开工具/步骤轨迹,不等于禁止 SuperAgent 调用 MCP。
- 页面开启时 FIRE_SAFETY_CHAT_ALLOWED_ORIGINS 必须包含精确的 `https://agent.nianxx.com`,并且
Chat 兼容 App ID、Chat Token 和 SuperAgent 配置已经可用;该静态页面不是最终用户认证。
- 使用 FIRE_SAFETY_MCP_SCOPE_MODE=all 时,FIRE_SAFETY_MCP_ALLOWED_TOWNS 必须保持为空。all 是服务账号级的固定查询表范围,不是最终用户级授权。
- FIRE_SAFETY_POSTGIS_MIGRATION_DSN 不应配置给运行服务;迁移凭证只供一次性迁移命令使用,完成后应移除。
- FIRE_SAFETY_HTTP_ADDR 在容器内应为 :8080,安全边界由 Compose 的 127.0.0.1:16587:8080 和宿主机 Nginx 提供;不要在 Compose 场景设为容器内的 127.0.0.1:8080。
- 不要用没有 --quiet 的 docker compose config 或 docker inspect 把完整环境渲染到终端、CI 日志或工单;检查 .env 权限仍为 600。
- 修改 `.env` 后必须重新创建容器(例如 `docker compose up -d --force-recreate --no-build`)才能加载新的 Chat 兼容开关;单独 `docker compose restart` 不会重新读取容器环境。旧短凭证仅用于受控测试/迁移,完成轮换后把开关改回 `false` 并再次 recreate。
- 修改 `FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE` 或其他 SuperAgent 代码/配置后,至少执行
`docker compose config --quiet`、`docker compose build --pull` 和
`docker compose up -d --force-recreate --remove-orphans`;仅 `docker compose restart` 不会
重新读取 `.env`,也不会把新代码构建进镜像。
## 4. 容器访问现有 PostgreSQL/PostGIS
本项目不在 Compose 中创建或初始化数据库。数据库继续由现有 PostgreSQL/PostGIS 运维,Go 服务只使用已完成 SRID 确认的只读数据源。
如果 PostgreSQL 在另一台机器:
- DSN 使用数据库私网 DNS 或私网地址,不使用容器内的 127.0.0.1。
- 数据库防火墙只允许测试服务器访问 5432;如启用 TLS,按数据库证书要求配置 DSN 的 sslmode,不要为了省事关闭证书校验。
如果 PostgreSQL 与 Docker 在同一台宿主机:
- 容器内的 127.0.0.1 指向容器自身;DSN 使用 Compose 声明的 host.docker.internal(或运维明确提供的 Docker 网关地址)。
- Linux Compose 需要有 host.docker.internal:host-gateway 映射;本仓库的 Compose 配置包含该映射时才能使用这个名称。
- PostgreSQL 的 listen_addresses、pg_hba.conf 和主机防火墙只允许 Docker 网段中的只读账号访问,不要发布 5432 到公网。
- 不要把迁移账号放入 .env;数据库配置变更先由管理员验证最小权限。
服务启动时进行 MCP PostGIS readiness 校验。SRID、几何类型或 WGS84 范围硬门禁失败会阻止服务装配;空/无效几何和缺 GiST 索引是当前已知 warning。GET /health 只是进程存活检查,不代表数据库、SuperAgent 或 MCP 已可用,必须同时检查 Compose 日志和实际接口。
## 5. 构建、启动和本机检查
以下命令在服务器项目目录执行。Compose 构建上下文不复制 `.env`;非 Secret 的 `FIRE_SAFETY_BUILD_GOPROXY` 只会被映射为构建参数,并在运行容器环境中强制清空。
先检查 Go 模块代理是否从服务器/Docker 所在网络可达:
~~~bash
curl --head --connect-timeout 10 https://proxy.golang.org/
~~~
如果连接官方代理超时,可以测试经运维认可的替代代理。例如 [Goproxy.cn](https://goproxy.cn/) 官方说明支持标准 Go module proxy 协议:
~~~bash
curl --head --connect-timeout 10 https://goproxy.cn/
~~~
只有确认替代代理可达且组织允许使用时,才在服务器 `.env` 中改为:
~~~text
FIRE_SAFETY_BUILD_GOPROXY=https://goproxy.cn,direct
~~~
该值必须是不含用户名、密码或 Token 的代理地址;Docker build argument 不是 Secret 注入机制。若组织代理要求认证,需要另做 BuildKit Secret 设计,不能把凭证嵌进 URL 或 `.env` 中的构建参数。
主机侧 curl 成功只能初步说明连通性;最终仍以 `docker compose build` 是否能下载并校验模块为准。如果两个地址都超时,应修复 Docker/宿主机的 DNS、HTTPS 出口、透明代理或防火墙,或者使用组织自建的可信 Go module proxy。不要设置 `GOSUMDB=off`、跳过 `go.sum` 校验或反复执行 `docker compose up` 掩盖依赖下载失败。
代理连通后再构建并启动:
~~~bash
cd /home/firee-safety-ymd
docker compose config --quiet
docker compose build --pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 api
curl --fail http://127.0.0.1:16587/health
~~~
预期结果:容器为 Up(健康状态由 Compose 显示),health 返回 HTTP 200;有限日志不应出现 DSN、密码、Token 或 API Key。docker compose config --quiet 只验证配置,不输出展开后的 Secret。若 Compose 不支持 --quiet,升级 Compose 或使用不会回显结果的等价校验方式。
若构建在 `RUN go mod download` 处失败,则镜像和容器尚未生成,后续访问宿主机端口会得到 connection refused。先解决模块代理连通性并重新执行 `docker compose build --pull`,成功后才能执行 `up -d`。
失败时只读取有限日志:
~~~bash
docker compose ps
docker compose logs --tail=200 api
~~~
不要把完整日志粘贴到公共工单;先删去 URL 凭证、连接串、Header、联系人和精确位置。readiness 报错时核对数据库地址、只读权限和 FIRE_SAFETY_POSTGIS_EXPECTED_SRID=4326,不要关闭门禁或修改原始几何。
## 6. 安装/替换宿主机 Nginx 配置
仓库模板为 deploy/nginx/fire-safety-ymd.conf.example,不含真实 Secret。它把精确 Chat 兼容路径、/mcp 和可选 /health 反代到 127.0.0.1:16587,其他路径返回 404;详细 SSE 边界见 nginx-public-entry.md。
先只查找拥有 agent.nianxx.com 的配置文件名,避免使用 nginx -T 把旧文件中的 DashScope Key 打到终端:
~~~bash
sudo grep -RIl \
'server_name[[:space:]]\+agent\.nianxx\.com' \
/etc/nginx/conf.d /etc/nginx/sites-enabled 2>/dev/null
~~~
旧配置迁移前:
1. 记录旧配置文件路径和归属;不要把旧配置内容复制到仓库或新模板。
2. 如需回滚,将旧文件以 root 权限备份到不被 Nginx include 的目录并限制为 600;若含真实 DashScope Key,应由凭证所有者安排轮换。
3. 把旧文件移出 *.conf include(例如改为 .disabled),避免同一域名出现两个冲突的 server 块。移动前确认目标是上一步查到的精确路径,不要批量操作整个 /etc/nginx。
部署模板的示例(目录因发行版而异;使用 sites-enabled 时按实际 include 位置调整):
~~~bash
sudo install -o root -g root -m 0644 \
deploy/nginx/fire-safety-ymd.conf.example \
/etc/nginx/conf.d/fire-safety-ymd.conf
sudo vi /etc/nginx/conf.d/fire-safety-ymd.conf
~~~
编辑时完成以下非 Secret 替换:
- 将 replace-with-fire-safety-app-id 替换为 .env 中的 FIRE_SAFETY_CHAT_COMPAT_APP_ID。
- 确认证书路径、域名和上游 127.0.0.1:16587 符合测试服务器。
- 保留精确 location、SSE 的 proxy_buffering off/超时设置和其他路径的 404;不要恢复旧配置的 location /、DashScope proxy_pass、Authorization Key 或 Nginx if Token 比较。
- limit_req_zone 必须位于 Nginx http 上下文;模板假定由 conf.d/*.conf 在 http {} 中 include。若 include 层级不同,把指令移到合法的 http 上下文。
确认旧配置已禁用且新文件不含 Secret 后,再验证并 reload:
~~~bash
sudo nginx -t
sudo systemctl reload nginx
~~~
如果 Nginx 不由 systemd 管理,使用发行版的 reload 方式;reload 前必须先通过 nginx -t。
## 7. 公网 Chat SSE 冒烟
先验证宿主机本地 /health 和 HTTPS /health,再用不含敏感信息的问题验证 SSE。将 Token 安全注入当前 shell(例如受保护的 Secret 管理器或交互式读取),不要把字面量写入 shell 历史:
~~~bash
read -rsp 'Chat xtoken: ' CHAT_XTOKEN
printf '\n'
~~~
首轮请求:
~~~bash
curl -N --fail \
-H "xtoken: $CHAT_XTOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
--data '{"input":{"prompt":"观水镇附近有哪些水源候选?"},"parameters":{}}' \
'https://agent.nianxx.com/api/v1/apps/<same-safe-app-id>/completion'
~~~
确认能读取 event: result,最后一个成功事件的 output.finish_reason 为 stop。只有 stop 事件的 output.text 可当作完成;中途断流、HTTP 错误或只有 finish_reason=null 不得当作可信答案。output.session_id 是本项目本地会话 ID,不是 SuperAgent 内部 Session ID。
后续轮次使用上一轮成功返回的本地会话 ID:
~~~bash
curl -N --fail \
-H "xtoken: $CHAT_XTOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
--data '{"input":{"prompt":"再说明候选的限制","session_id":"<session-id-from-previous-response>"},"parameters":{}}' \
'https://agent.nianxx.com/api/v1/apps/<same-safe-app-id>/completion'
~~~
至少验证:错误 xtoken 为 401;不存在的 app ID 和未列出的路径为 404。不要使用真实联系人、电话或精确敏感位置作为冒烟问题。
完成 Chat 测试后清理当前 shell 中的临时变量:
~~~bash
unset CHAT_XTOKEN
~~~
## 8. 公网 Chat 测试页面
测试页面由 Go 可选提供,不需要单独的前端容器或 Nginx 静态目录。页面及两个同源资源的
Nginx 精确路径已经包含在仓库模板中:`/chat`、`/chat/`、`/chat/app.css` 和 `/chat/app.js`。
Nginx 始终可以反代这些路径,但是否返回内容由 Go 的
`FIRE_SAFETY_CHAT_PAGE_ENABLED` 控制。
### 开启页面
在服务器受保护的 `.env` 中确认或修改以下非 Secret 配置:
~~~text
FIRE_SAFETY_CHAT_ENABLED=true
FIRE_SAFETY_CHAT_COMPAT_APP_ID=<与Nginx completion路径一致的公开app-id>
FIRE_SAFETY_CHAT_PAGE_ENABLED=true
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS=https://agent.nianxx.com
~~~
页面开启要求 Chat、兼容 App ID、Chat 测试凭证和 SuperAgent 配置已经有效;
`https://agent.nianxx.com` 必须是精确 Origin,不能使用 `*`。页面是受控测试工具,不是最终
用户认证,用户要在页面中手动输入 xtoken,不能把 Token 写入页面、Nginx 或镜像。
修改 `.env` 后必须重新创建容器,单独 restart 不会重新读取环境变量:
~~~bash
cd /home/firee-safety-ymd
docker compose config --quiet
docker compose up -d --force-recreate --no-build
docker compose ps
curl --fail http://127.0.0.1:16587/health
~~~
如果 Nginx 模板刚刚更新,再执行 `sudo nginx -t`,确认通过后再 reload;只改 Go 开关时不需要
改 Nginx 文件。
### 页面验收
先不带 Token 检查页面和资源路由:
~~~bash
curl -i https://agent.nianxx.com/chat
curl -i https://agent.nianxx.com/chat/
curl -i https://agent.nianxx.com/chat/app.css
curl -i https://agent.nianxx.com/chat/app.js
~~~
开启时预期 `/chat` 返回 HTTP 308 并指向 `/chat/`,`/chat/` 返回 HTTP 200 HTML,CSS 和
JavaScript 返回 HTTP 200。需要自动跟随入口重定向时使用:
~~~bash
curl -iL https://agent.nianxx.com/chat
~~~
关闭时,Go 不注册页面 Handler,`/chat`、`/chat/`、`/chat/app.css` 和 `/chat/app.js` 均
直接返回 HTTP 404;这与 Nginx 是否保留四个精确 location 无关。
浏览器打开 `https://agent.nianxx.com/chat/`,手动输入受控测试 xtoken(页面不会预填或保存),
发送不含敏感信息的问题。Network 面板应看到同源 POST 到
`/api/v1/apps/<same-safe-app-id>/completion`,请求体为第三方兼容格式,Header 含 `xtoken`,
响应为 `event: result` SSE。只有 `output.finish_reason=stop` 才是成功;第二轮请求应把第一轮
成功返回的 `output.session_id` 放入 `input.session_id`。刷新或关闭页面后,Token 和 session_id
均应清除,不得自动恢复。
### 关闭与回滚
紧急关闭页面时,将 `.env` 的 `FIRE_SAFETY_CHAT_PAGE_ENABLED` 改回 `false`,执行:
~~~bash
cd /home/firee-safety-ymd
docker compose up -d --force-recreate --no-build
curl -i https://agent.nianxx.com/chat
curl -i https://agent.nianxx.com/chat/
~~~
确认两个路径直接 404(无 308),并按需检查 CSS/JavaScript 资源也为 404。该操作不会自动
关闭兼容 completion;如需一并关闭 Chat,再按 Chat 总开关执行配置回滚。若页面版本或 Nginx
版本导致故障,回到维护者指定的已验证 Git revision 或 root-only Nginx 备份,先 `nginx -t`
再 reload;不要恢复旧的全路径 DashScope 代理、关闭 Go 鉴权或把 Secret 写入 Nginx。
## 9. SuperAgent Open API 无 Trace 模式
当前测试环境优先使用无 Trace 模式,避免外部应用策略未开放公开 Trace 导致消息流返回
`HTTP 403` / `open_agent_trace_disabled`。在服务器 `.env` 中确认:
~~~text
FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE=false
~~~
无 Trace 模式仍严格要求上游最终 AI 消息的 `finish_reason=stop`、非空顶层
`event: message.final` 和顶层 `event: end`;最终回答只取 `message.final.text`。它只是不向调用方
返回公开工具/步骤轨迹,不等于禁止 SuperAgent 在执行中调用已配置的 MCP 工具。确认 MCP 是否
实际被调用,必须检查 `/mcp` 日志和返回结果。只有外部应用策略显式开启
`trace_policy.enabled=true` 时,才把该值改为 `true`;Trace 模式会额外要求
`run.completed(status=success)`。
修改 `.env` 后必须重新构建并重新创建服务,使配置在运行容器中生效:
~~~bash
cd /home/firee-safety-ymd
docker compose config --quiet
docker compose build --pull
docker compose up -d --force-recreate --remove-orphans
docker compose ps
docker compose logs --tail=100 api
curl --fail http://127.0.0.1:16587/health
~~~
不要在日志或工单中粘贴 Open API Key。若仍收到 403,只记录 HTTP 状态、脱敏后的 Provider code、
时间和请求关联 ID,交由 SuperAgent 平台核对外部应用策略;不要反复轮换无关的 Chat/MCP 凭证。
受控诊断已经确认:同一 Key 的 `include_trace=true` 因应用策略返回 403
`open_agent_trace_disabled`;同一 Key 的 `include_trace=false` 已由真实探针完成严格成功判定,
本地兼容 Chat SSE 也完成 `finish_reason=null -> stop`。公网完整消防对话、MCP 实际工具调用和
多轮会话仍需单独验收。
## 10. SuperAgent MCP 回调配置与冒烟
在 SuperAgent 的工具/MCP 配置中新增远程 MCP 服务:
~~~text
URL: https://agent.nianxx.com/mcp
Method: POST
Authorization: Bearer <FIRE_SAFETY_MCP_AUTH_TOKEN>
~~~
该 Bearer 必须是 .env 中独立的 FIRE_SAFETY_MCP_AUTH_TOKEN,不要填 Chat xtoken 或 SuperAgent Open API Key。SuperAgent 当前不能在配置中指定 `protocolVersion`;不要把它或 `MCP-Protocol-Version` 作为客户端配置项加入。已稳定接通的 `th-hotel-simple-superagent` 不读取或校验这些版本元数据,而是固定返回 `2025-06-18`;消防 MCP 按这一已验证兼容档案接入,目标是打通工具调用,不是追求最新协议。SuperAgent 还可能需要公网来源 IP 白名单、TLS CA 或其他平台级 Header,按实际平台和网络策略确认。
服务端兼容规则如下:对可解析的 JSON-RPC `initialize`,`params.protocolVersion` 的缺失、空值、非字符串或其他值都不作为版本拒绝条件,响应固定返回 `result.protocolVersion: "2025-06-18"`;`MCP-Protocol-Version` Header 的缺失或值也不作为版本拒绝条件。固定返回该版本不表示服务实现或声明支持任意客户端版本,`2025-03-26`、`2025-11-25` 等值也不改变服务端实现目标。兼容仅针对版本元数据,Bearer、`Content-Type`、非空 `Origin`、JSON-RPC 结构、只读工具、服务端数据范围和敏感字段边界仍严格执行。
现场日志已证明公网 `/mcp` 完成 initialize、notifications/initialized、tools/list,并成功调用一次 `fire_safety_search_place_candidates`;该证据只覆盖地点搜索,不能替代其余消防工具和完整多工具链验收。同一 SuperAgent 中 `th-hotel-simple-superagent` 已稳定调用是兼容档案参照。部署后继续通过 `direct_success`(客户端值为 `2025-06-18`)或 `compatibility_success`(版本字段缺失或其他值被兼容处理)日志分类确认,服务端不得记录原始版本值。
如果修改了 MCP Handler 或握手协商逻辑,目标机必须拉取包含该修改的明确 revision 后重建镜像;仅 `docker compose restart` 不会更新镜像中的代码:
~~~bash
cd /home/firee-safety-ymd
git fetch --prune origin
git switch main
git pull --ff-only origin main
docker compose config --quiet
docker compose build --pull
docker compose up -d --force-recreate --remove-orphans
docker compose ps
docker compose logs --tail=100 api
curl --fail http://127.0.0.1:16587/health
~~~
在服务器或受控终端用独立变量测试初始化。下面的版本字段只是普通 JSON-RPC 参数示例,SuperAgent 不需要配置它;也可以省略或使用客户端实际发送的值:
~~~bash
read -rsp 'MCP bearer: ' MCP_BEARER
printf '\n'
curl --fail \
-H "Authorization: Bearer $MCP_BEARER" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"manual-probe","version":"1"}}}' \
'https://agent.nianxx.com/mcp'
curl --fail --output /dev/null --write-out 'initialized HTTP %{http_code}\n' \
-H "Authorization: Bearer $MCP_BEARER" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'https://agent.nianxx.com/mcp'
curl --fail \
-H "Authorization: Bearer $MCP_BEARER" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'https://agent.nianxx.com/mcp'
curl --fail \
-H "Authorization: Bearer $MCP_BEARER" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"fire_safety_search_place_candidates","arguments":{"place_name":"观水镇","limit":3}}}' \
'https://agent.nianxx.com/mcp'
~~~
确认第一步固定返回 `result.protocolVersion: 2025-06-18`,第二步返回 HTTP 202,第三步列出 7 个固定工具,第四步至少完成一个受控只读 `tools/call`。这四步 `initialize` → `notifications/initialized` → `tools/list` → `tools/call` 才是 SuperAgent 能调用消防 MCP 的验收链路;只看到 initialize 成功不能宣称接通。版本 Header 不需要配置或继续携带。再用无敏感信息的已知地名/点位做少量只读调用,核对正确 Bearer 成功、错误/缺失 Bearer 为 401、warnings 保留、非法参数不会变成任意 SQL。查询到资源记录不代表实时可用、路线已规划或正式集结点。
完成 MCP 测试后清理当前 shell 中的临时变量:
~~~bash
unset MCP_BEARER
~~~
## 11. 更新、重启与回滚
### 发布新版本
维护者先将目标版本 commit/push 后,服务器快进更新并重建:
~~~bash
cd /home/firee-safety-ymd
git fetch --prune origin
git switch main
git pull --ff-only origin main
docker compose config --quiet
docker compose build --pull
docker compose up -d --remove-orphans
docker compose ps
docker compose logs --tail=100 api
~~~
更新会重启单个 Go 进程,现有内存会话丢失;客户端按会话失效处理并重新开始对话。当前部署不支持多实例共享会话,也没有持久化历史或审计。
### 回滚
回滚到上一个已验证的 Git commit 或镜像版本。先保留现状信息并确认目标版本;不要用破坏性 reset 覆盖未知的服务器本地改动:
~~~bash
cd /home/firee-safety-ymd
git status --short
git log --oneline -5
~~~
由维护者确认目标 commit(例如 `<verified-commit>`)后,先以 detached HEAD 检出该已验证版本,再重新构建启动;这是显式回滚,不会自动覆盖服务器本地改动:
~~~bash
git fetch --prune origin
git switch --detach <verified-commit>
docker compose build --pull
docker compose up -d --remove-orphans
docker compose ps
~~~
回滚结束并准备恢复正常发布轨道时,再切回维护者指定的分支:
~~~bash
git switch main
git pull --ff-only origin main
~~~
如果是 Nginx 配置导致故障,先恢复 root-only 备份,确认旧域名配置没有重复 include,再执行:
~~~bash
sudo nginx -t
sudo systemctl reload nginx
~~~
回滚后重新检查本机和公网 /health、Chat 401/404 边界以及 MCP 初始化。docker compose down 只停止容器,不删除代码或数据库;明确需要停服时再执行:
~~~bash
cd /home/firee-safety-ymd
docker compose down
~~~
## 12. 完成判定与当前限制
本手册完成不代表公网部署已完成。现场交付至少应记录:
- 目标机 Compose 配置校验、build、up、健康检查和无 Secret 的有限日志结果。
- 目标机 nginx -t 和 reload 成功;443 证书、DNS、安全组及旧 location / 已确认不再生效。
- 页面开关关闭时 `/chat`、`/chat/`、`/chat/app.css`、`/chat/app.js` 直接返回 404;开启时 `/chat` 返回 308、`/chat/` 返回 200 HTML,两个资源返回 200;`FIRE_SAFETY_CHAT_ALLOWED_ORIGINS` 包含 `https://agent.nianxx.com`。
- Chat 首轮/多轮 SSE、错误凭证、未知 app/path 的实际 HTTPS 响应。
- 页面浏览器验收证明用户手动输入 xtoken、Token 不被页面持久化、兼容 completion SSE 及同页面 `session_id` 复用;这只是静态测试页面,不是生产认证。
- 若为已交付旧客户端临时开启 Chat legacy 短凭证兼容,应记录受控迁移窗口,确认启动 warning 不含 Secret,并在凭证轮换后恢复 `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=false`。
- SuperAgent Open API 使用 `FIRE_SAFETY_SUPERAGENT_INCLUDE_TRACE=false` 时,验证最终 AI 消息
`finish_reason=stop`、非空顶层 `message.final` 加顶层 `end`;若切换为 true,验证外部应用
Trace 策略已开启且 `run.completed(status=success)` 仍为必要条件。无 Trace 不等同于 MCP
禁用;完整消防对话和 MCP 工具调用仍须单独记录。
- SuperAgent -> `/mcp` 的独立 Bearer、TLS/网络白名单、无版本配置 initialize 兼容响应、`notifications/initialized`、`tools/list` 和全部 7 个受控只读 `tools/call`。现场已有地点搜索一次成功证据,但其余工具未验收;版本字段/Header 不作为配置或拒绝门禁;公网请求到达但没有完成这条调用链,不算完成。
- 数据库未公开 5432,运行账号保持只读,PostGIS readiness warning 和 35 条无效面几何缺口已记录。
若目标服务器无法使用 host.docker.internal、Compose 不支持 --quiet、Nginx include 目录不同、证书路径不同或 SuperAgent 对 MCP 的认证格式不同,应先记录实际环境并调整部署 Spec;不要通过放开端口、写入 Token、关闭 readiness 或恢复全路径反代规避问题。