# Fixed public deployment The public demo is hosted directly on the server at `https://511.kendeji.fun`. Caddy terminates HTTPS and proxies only to the container port bound on `127.0.0.1:8511`; the application port is not exposed to the public network. The server layout is: ```text /opt/511-project/ ├── .env # OpenAI connection settings, mode 0600 ├── .env.auth # Web authentication settings, mode 0600 ├── deploy/server/ ├── artifacts/runs/ # Persistent request artifacts ├── artifacts/user-presets/ ├── artifacts/server/ # GSCU infrared catalog and server-side state ├── artifacts/tmp/ # Runtime gateway and calibration state ├── artifacts/self-repair/ └── artifacts/mode-profiles/ # User-created modes for all vehicles ``` User-created function modes are persisted from the ignored host path `artifacts/mode-profiles/` into the service's separate writable user-mode registry at `/app/artifacts/mode-profiles/`, including common and future vehicle-specific modes. Preset files under `/app/config/modes` remain read-only and are never modified. Build and start the service: ```bash cd /opt/511-project mkdir -p \ artifacts/runs \ artifacts/user-presets \ artifacts/server \ artifacts/tmp \ artifacts/self-repair \ artifacts/mode-profiles chown -R 10001:10001 \ artifacts/runs \ artifacts/user-presets \ artifacts/server \ artifacts/tmp \ artifacts/self-repair \ artifacts/mode-profiles chmod 0700 \ artifacts/runs \ artifacts/user-presets \ artifacts/server \ artifacts/tmp \ artifacts/self-repair \ artifacts/mode-profiles docker compose -f deploy/server/compose.yaml up -d --build docker compose -f deploy/server/compose.yaml ps ``` 镜像固定安装 `@openai/codex@0.150.0-alpha.8`。AI 助手使用命名卷 `codex_home` 保存 Codex 运行状态。N7Web 会在 App Server 启动握手期间验证模型认证, 认证缺失或初始化失败时不会把 `/api/health` 误报为 ready。 若 `.env` 同时设置 `OPENAI_BASE_URL` 和 `OPENAI_API_KEY`,且地址不是 OpenAI 官方 默认地址,N7Web 会为每个助手线程注入一个完整、固定的 OpenAI-compatible provider: `base_url` 使用该地址,密钥只通过 provider 的 `env_key=OPENAI_API_KEY` 从子进程环境 读取,不会出现在命令行、状态或日志中。这一步是必须的;Codex 内置 `openai` provider 不会把应用的 `OPENAI_BASE_URL` 自动当作模型端点。 若未设置自定义 `OPENAI_BASE_URL`,则使用 Codex 内置 OpenAI provider。已有 `codex_home/auth.json` 时复用现有登录;没有缓存登录但设置了 `OPENAI_API_KEY` 时, N7Web 通过 App Server 的 API-key 登录接口完成初始化,Codex 会把认证状态写入 `codex_home`。因此该命名卷属于含凭据的持久数据,应限制管理员访问并纳入密钥轮换 流程。不要把 `auth.json` 复制进镜像、日志或仓库。仅设置 `CODEX_API_KEY` 不会让固定 版本的 App Server 自动登录。 若使用 ChatGPT 账号认证,推荐在无浏览器的服务器上执行 device-code 登录;普通网页 OAuth 依赖容器内 localhost 回调,容易在 headless 环境失败。登录完成后重启服务: ```bash docker compose -f deploy/server/compose.yaml run --rm web codex login --device-auth docker compose -f deploy/server/compose.yaml restart web docker compose -f deploy/server/compose.yaml exec web codex login status curl -fsS http://127.0.0.1:8511/api/health ``` App Server 线程采用 `read-only` sandbox 和线程级 `approvalPolicy=never`,并关闭 内置 shell、统一执行器、浏览器和应用工具;因此命令执行或文件修改不能通过交互式 审批获得额外权限。Codex 子进程只继承启动及模型认证所需的环境变量。容器使用无特权 UID `10001` 和只读根文件系统。 唯一启用的 `n7_vehicle` MCP 服务将工具级 `default_tools_approval_mode=approve` 固定为 `approve`。这与 `approvalPolicy=never` 不矛盾:前者仅避免 App Server 对已经进入 N7Web 安全边界的 MCP 调用再发起一次当前嵌入式网页无法响应的审批,后者仍让 App Server 自身的命令和 文件权限失败关闭。MCP 只开放运行时能力目录查询和回环地址上的座舱执行桥,其中只有 执行桥有副作用。工具令牌绑定原始用户请求,结构化候选计划还会经过原文证据、能力 注册表、参数 Schema、执行模式、车辆硬件审批与安全策略校验;模型不能提交替代指令 或原始 CAN,`approve` 也不会绕过这些应用层门禁。审批字段语义见 [Codex App Server 官方文档](https://learn.chatgpt.com/docs/app-server) 与 [Codex 配置参考](https://learn.chatgpt.com/zh-Hans/docs/config-file/config-reference)。 Add `deploy/server/Caddyfile.example` to the host Caddyfile, validate, and reload Caddy: ```bash caddy validate --config /etc/caddy/Caddyfile systemctl restart caddy ``` This host disables Caddy's admin endpoint, so `caddy reload` and `systemctl reload caddy` cannot apply configuration. Validate first, then use a service restart. Operational checks: ```bash curl -I https://511.kendeji.fun/ docker compose -f /opt/511-project/deploy/server/compose.yaml logs --tail=100 systemctl status caddy ``` Never place `.env`, `.env.auth`, API keys, login passwords, or SSH credentials inside the image or source control. Updating the application uses the same source synchronization followed by `docker compose ... up -d --build`. ## Public physical-bench gateway When the physical T2G bench is attached to a separate trusted workstation, Caddy may prefer a restricted reverse-SSH upstream on `127.0.0.1:18512` and retain the container on `127.0.0.1:8511` as the unavailable-bench fallback. The workstation service must use the same `.env.auth` as the public container; the SSH key must be restricted to that single reverse-listen port and must not be allowed to run remote commands. Public physical execution uses a local-approval lease. A browser first submits an access request, and the bench operator chooses one terminal at `http://127.0.0.1:8000/hardware-control`. The decision API is restricted to unproxied loopback traffic, so it cannot be used through the public domain or the SSH tunnel. The approved terminal may run multiple tests; its lease is revoked after 180 seconds without a physical test, on voluntary release, or by the local operator. A process restart clears all leases and pending requests. Inside the approved lease, the physical bench remains a process-wide single-flight resource. A hardware action owns the bench from intent planning through final physical feedback. Concurrent actions and passive physical-state reads receive HTTP `409` (`hardware_busy`) with `Retry-After`; they never enter the CAN path or wait to run later against stale position state. Dry-run and simulated-hardware requests are not serialized by this action-level gate.