# CADWorld Sim 网站部署与运维 目标:`https://cadworld-sim.robotquan.com`,主机 `root@47.93.31.109`。复用 1Panel 的 OpenResty,独立容器仅发布 `127.0.0.1:8768`。不安装训练/GPU 服务,不改系统 Python,不需要访问者开启终端。 ## 产品边界 - 网站公开、无需注册。LLM 选择 DeepSeek/OpenRouter 并填写自己的 API_KEY;Jev 单独填写 OpenRouter API_KEY,型号固定 `typesafe/jev-1.13`。 - 保留 ChatGPT 官方设备码登录。当前主机对官方订阅端点的连通性不稳定,入口会诚实显示失败;订阅资格、额度和真实推理需要用户验证,不自动转收费 API。 - 密钥经 HTTPS 到后端,只在会话内存;浏览器输入保存后清空,不进入 URL/存储/日志。空闲 30 分钟或最长 8 小时销毁。关页不保证立即销毁,可点击“清除本次凭据”。同浏览器标签页共享会话,配置版本隔离旧任务;其他浏览器独立。 - ZIP/URDF/地图/物理仿真仍在浏览器。网站隐藏训练、调参和外控,浏览器 Python/ONNX 保留;本地 `npm run dev` 的旧功能不变。 - 容量:128 会话、每会话 1 推理/120 次每小时、全站 8 推理和 2 个订阅账号;按 IP 另有限额。匿名限制不是无限防滥用承诺,需持续观察负载。 - 备案已由站长确认;正式备案/公安备案展示号码由站长提供,不能编造。TLS 证书首次无邮箱注册,自动续期由 systemd timer 负责,建议管理员另行设置证书到期监控。 ## 本地开发与测试 ```bash source .venv/bin/activate python -m decision_server --website-origin http://127.0.0.1:5173 --website-dev # 开发者另一个终端(仅开发时): npm run dev:website -- --host 127.0.0.1 npm run build:website # 输出 website-dist/,不含 tuning.html npm run test:e2e:website # 真实后端 + 假 HTTP 上游,无 API 额度消耗 ``` 生产必须 HTTPS,不能携带 `--website-dev`。前后端 Origin 要一致;本机 Bearer 模式仍使用 `npm run decision-server`,两种模式不自动回退。契约见 [website-api.md](website-api.md)。 ## 构建、传输与首次发布 1. 保留工作树变更基线,运行 TS/Vitest/Python/Playwright 门禁。`build/website-deployment/baseline/` 保存部署前工作树摘要,不包含 `.env`。 2. 使用 [官方 crane v0.20.6](https://github.com/google/go-containerregistry/releases/tag/v0.20.6),校验 release checksums。将 `deploy/cadworld/images.lock.json` 中的 linux/amd64 digest 拉为 docker archive(`crane pull --platform linux/amd64 REF FILE`);不需要本机 Docker daemon,也不让服务器访问不明镜像源。 3. 为 Python 3.12 下载 `requirements.lock.txt` 指定 wheels,验证 hash;从官方 npm `@openai/codex@0.147.0-linux-x64` 包验证 registry 的 integrity 后提取二进制。文件放在 `build/website-deployment/vendor/{wheels/,codex,codex-origin.json}`。不复制任何用户的 Codex HOME/auth.json。 4. `npm run build:website` 后运行 `python3 deploy/cadworld/prepare_bundle.py RELEASE`。脚本只打包显式白名单、生成每文件 SHA-256,并输出归档 SHA-256;不打包 `.env/.git/.venv/build/`、测试代码或个人模型。 5. SSH 传送归档、两份镜像 tar 和 SHA-256 到 `/opt/cadworld-sim/uploads/`。核验 SHA-256 后解压到 `/opt/cadworld-sim/releases/RELEASE/`,运行该版本的 `deploy/import-images.sh IMAGE_DIRECTORY`。先确认 `172.30.88.0/24` 不与现有 Docker 网络冲突。 6. 首次运行 `bash /opt/cadworld-sim/releases/RELEASE/deploy/bootstrap-tls.sh`:备份原 OpenResty conf,创建独立 HTTP challenge vhost,签发域名证书,安装 HTTPS vhost 与续期 timer。不修改默认站点/1Panel账号;已存在同域名配置则拒绝覆盖。 7. `bash /opt/cadworld-sim/releases/RELEASE/deploy/publish.sh RELEASE`:逐文件验 hash、离线构建、启动最小权限容器、健康检查、原子切换静态目录、`nginx -t`、reload。失败恢复上一版本,首次失败撤下新 vhost 恢复默认站点。成功生成 `current.txt/previous.txt/image-id.txt`。 只有 80/443 提供网站流量;8768 绑定回环。不挂载 Docker socket、SSH key、宿主 HOME;应用非 root、只读根、tmpfs、2 GiB/2 CPU/128 PID 限制。证书在已有 OpenResty www 挂载下独立 `ssl/`,不在站点 document root 内。 ## 日常操作(目标主机) ```bash cd /opt/cadworld-sim docker compose --env-file release.env -f compose.yaml ps docker compose --env-file release.env -f compose.yaml logs --tail 100 decision curl -H 'Host: cadworld-sim.robotquan.com' http://127.0.0.1:8768/healthz systemctl status cadworld-certificate.timer bash /opt/cadworld-sim/renew-certificate.sh --dry-run # 后台更新/重启会销毁所有临时凭据,用户需要重新输入/登录: docker compose --env-file release.env -f compose.yaml restart decision # 回滚:取 previous.txt 中的具体版本,调用那个版本的发布脚本,前后端一起恢复。 bash /opt/cadworld-sim/releases/PREVIOUS/deploy/publish.sh PREVIOUS ``` 不要在 1Panel 重复创建同域名覆盖手工 vhost,不要将开发服务器或后台端口直接暴露公网。不要打印完整 Cookie、请求体、API_KEY、设备码或 OAuth 链接来排障。应用和 API 访问日志不记录请求正文;会话异常仅返回稳定错误码。 ## 验证边界 普通测试使用假上游,不证明云端模型可用;mock 抓放使用真实 WASM 物理,不证明真实 LLM/Jev 被调用。真实 API 测试必须由用户明确提供密钥并触发,订阅登录须在官方页面交互。`HEAD` 的 200/401/403/404 只作为连通性证据,不是 POST 推理成功。生产证书、重启/回滚及浏览器验证结果另见实际发布记录。