148 lines
6.9 KiB
Markdown
Executable File
148 lines
6.9 KiB
Markdown
Executable File
# 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.
|