# 引擎 HTTP + SSE API(`cst-webui/1`) `hermit --web`(兼容 `cst-agent --web`)在同一进程上暴露 **远程客户端契约**: 任意 HTTP 客户端可订阅 SSE、发起 turn、回答审批。事件与嵌入式 `cst_agent_hooks_t` 一一对应——TUI 与 `--web` 共用引擎,不另开私有通道。 字段级摘要见 [webui-schema.md](webui-schema.md)。设备间 peer 套接字见 [parliament.md](parliament.md)(与本 API 无关)。 默认只嵌入**健康页**;完整浏览器 SPA 参考实现在 `archive/web-spa`。 ## 快速开始 ```bash # 默认只监听 127.0.0.1:8787(健康页 + /api/*) hermit --web # 指定端口;0 = 让系统分配(启动日志里会打印实际 URL) hermit --web --web-port 8787 # 让手机/另一台机器访问(必须给 token,否则拒绝启动在非 loopback 上) hermit --web --web-host 0.0.0.0 --web-token 我自己定的口令 # 开发:读磁盘上的静态目录(健康页或归档 SPA) hermit --web --web-dev ./web hermit --web --web-dev archive/web-spa # 启动后自动打开浏览器 hermit --web --web-open # 启动即跑一轮(不点页面也能用) hermit --web -p "看看这个仓库有什么问题" ``` 启动日志: ``` webui: http://127.0.0.1:8787/ webui: token 3f9a… (append #token=3f9a… to the URL) ``` 带 token 时把 `#token=…` 贴到浏览器地址栏后面即可(`#` 之后不会发给服务器, 页面会用 `?token=` 再请求一次)。 环境变量:`CST_WEB_PORT`、`CST_WEB_HOST`、`CST_WEB_TOKEN`、`CST_WEB_ROOT`。 退出:终端里 Ctrl+C,或 `POST /api/quit`。 ## 安全模型 | 场景 | 行为 | |---|---| | 默认 `127.0.0.1` | 不需要 token;`Host` 必须是 loopback(防 DNS rebinding);带 `Origin` 的请求必须来自本页面自己(`http://`),否则 401 —— 防止任意网页跨站 POST 驱动 agent | | 静态资源 | 页面本身与 `app.js`/`style.css` 不需要 token;`/api/attach/` 图片仍要(页面会把 token 写进 `SameSite=Strict` cookie) | | 请求解析 | 鉴权与大小检查在读请求体**之前**完成;`Content-Length` 必须是纯数字且只有一个值;请求头整体 15 s 内收完 | | `--web-host 0.0.0.0` | 没给 `--web-token` 时自动生成随机 token 并打印;所有 `/api/*` 都要 token | | token 传递 | `Authorization: Bearer `、`Cookie: cst_token=` 或 `?token=`(`EventSource` 不能设 header,所以查询参数是必需的一条路) | | 资源 | 只有白名单路由;路径里的 `..`/编码 NUL 一律 400;静态资源来自二进制内嵌(或 `--web-dev DIR`) | | 输出渲染 | 模型与工具输出都当不可信内容:Markdown 由服务端渲染,并经过标签/属性白名单(`javascript:` 链接、站外图片、`