Core-only 把 botmux 当作受控的 HTTP 服务嵌入你自己的系统:单进程、固定端口、绑定 127.0.0.1,不需要任何飞书凭证 / bots.json / pm2 / dashboard。编排器(任务 runner、CI、后端服务、沙箱)通过 HTTP 驱动它跑 CLI(codex / claude-code 等)、轮询结果、按需拿到可写 Web 终端。一句话:core-only 是 headless、单租户、纯 loopback 的 botmux——保留完整 daemon IPC 契约,去掉飞书侧。

与普通 fleet 的区别

普通 botmux start(fleet)botmux serve --api-only(core-only)
进程模型pm2 + dashboard + 每 bot 一个 daemon单进程前台运行,进程存活即服务存活
飞书凭证需要 larkAppSecret,缺则启动失败完全不需要,不构造飞书 Client
bot 配置来源~/.botmux/bots.json / BOTS_CONFIG忽略两者,合成唯一 apiOnly bot(仍读全局 .env 与全局配置)
出站通道回复发飞书话题无飞书出站(no-transport),结果只经 HTTP 取回
绑定端口按需探测固定端口、127.0.0.1、bind-or-fail
状态目录~/.botmux/data专用 ~/.botmux/core-only/<botId>/data(隔离)

启动与参数

BOTMUX_API_PORT=8930 botmux serve --api-only                      # 推荐,前台运行
BOTMUX_CORE_ONLY=1 BOTMUX_API_PORT=8930 node <pkg>/dist/index-core-only.js  # 等价,供嵌入式 spawn
参数环境变量默认说明
--portBOTMUX_API_PORT(必填)固定监听端口,bind-or-fail
--botBOTMUX_API_ONLY_BOTlocal_riff合成 bot id,须形如 local_<slug>
--cliBOTMUX_CORE_CLIcodex-app跑哪个 CLI
--working-dirBOTMUX_CORE_WORKING_DIR当前目录CLI 工作目录
--state-dirBOTMUX_CORE_STATE_DIR专用状态根状态目录
(无 flag)BOTMUX_CORE_MODEL未设覆盖合成 bot 默认模型

--flag 优先于环境变量。--cli codex-app 走 app-server runner(结构化返回、无可视终端);--cli codex / claude-code 在 tmux pane 跑可视 TUI,可经 Web 终端围观/操作(见 web-terminal)。

就绪契约

daemon 先 bind 端口、后完成 durable restore,「端口能连」不等于「可以触发」:

  • stdout ready line(锁定契约,正则 ^\[core-only\] listening on ):仅在 restore 完成后打印。
  • GET /healthz(公共免鉴权):未就绪 503 {"ok":false,"status":"starting"},就绪 200 {"ok":true}。
  • 就绪屏障同时作用于公共控制路由(restore 未完成时 trigger/trigger-result/insight 也返 503)。推荐 launcher 先轮询 /healthz 到 200 再发第一条 trigger。
  • 注意 /__health 是 legacy liveness 探针、始终返 200,不能当就绪判断用。

三层鉴权

  1. 第一层 · integrator surface(core-only 专属,免 HMAC)——集成方只用这几条:POST /api/trigger、GET /api/sessions/:id/trigger-result、GET /api/sessions/:id/insight、GET /healthz。这是刻意收窄的 allowlist(/api/asks/answer 刻意不在内,防止同机 turn 劫持待答 ask)。
  2. 第二层 · 内部 capability / 签名路由:session-ready、asks、slash/cd/close/chat-rename、hooks/emit、attention 等,由会话内 rotating per-turn capability(绑定 URL 中 sessionId)或独立强签名在 handler 内验证;合法调用方是会话内 CLI 自身,集成方通常不直接调。
  3. 第三层 · host / operator 路由:其余全部(含可写终端 write-link、会话元信息 GET /api/sessions/:id),需带 bind 的 loopback HMAC。

特例:POST /api/sessions/:id/close 既可用第三层 HMAC,也可用第二层会话内 capability(CLI 自关)。

HMAC 签名(最易踩的坑:bind)

三个 header:X-Botmux-Cli-Ts(Unix 秒,窗口 ±30s)、X-Botmux-Cli-Nonce(随机 hex,60s 防重放)、X-Botmux-Cli-Auth(HMAC_SHA256(secret, msg) 的 base64url)。密钥是 ~/.botmux/.dashboard-secret 里的原始 43 字符 base64url 字符串,直接当 key 用,不要再 base64 解码。

bind = `${METHOD} ${pathname} ${port}`    // 例: "GET /api/sessions/<sid>/write-link 8930"
msg  = `${ts}:${nonce}:${bind}`

三个易错点:port 是服务实际 bound 的端口(不是 Host 头的值);pathname 不含 query string;secret 用原始字符串。只签 bare ts:nonce(漏 bind)会得到 401 {reason:"sig_mismatch"},且往往第一次在 write-link 上才暴露。权威实现:src/core/daemon-ipc-auth.ts 的 daemonIpcAuthHeaders()。

触发与轮询

四态契约与普通 bot 一致(见 external-trigger),core-only 差异:

  • 必须带 HTTP 应答模式:trigger 须带 options.waitForFinalOutput(同步)或 options.asyncReturnSessionId(异步),否则 400 bad_request(没有飞书群可回)。
  • 异步返回合成 http_async_* chatId + 真实 sessionId;用该 sessionId 轮询。
  • 用户指令放顶层 instruction 字段(渲染成 trusted <botmux_task>),不是 prompt。
  • options.model / options.reasoningEffort 对 codex 家族 fresh trigger 生效。
  • 轮询用完整 sessionId(UUID),不要用日志里的 [worker:xxxxxxxx] 短 tag(会 session_not_found)。
  • completion 依赖从 CLI transcript 抽取 final_output;claude-code 首轮 ready-gate 回落导致永久 running 的 bug 已在 v3.9.0 修复,用 v3.9.0+。

可写 Web 终端

  • GET /api/sessions/:id/write-link(需 §HMAC 签名)返回带写 token 的 URL,打开即得可输入 xterm;只读版用 viewToken。
  • backend 为 zmx 时不支持(409 terminal_unsupported);tmux/pty 支持(见 tmux、zmx)。
  • core-only 专属:只要有存活 worker 终端,公共 trigger-result 响应会附带 readOnlyUrl + viewToken(普通 fleet 不发射;closed/无 live worker 不发射)。写 token 永远只经 HMAC write-link 获取。
  • token 本身稳定(host secret + sessionId 的 domain-separated HMAC,跨 restart 不变),但建议打开时现取 URL:代理端口/广告 host/worker 可用性会变。只持久化 sessionId。
  • 单个 core-only 会话不会被空闲回收(无 idle 超时),worker 常驻、终端持续可达。

安全边界(no-transport)加固

  • 不构造 Lark.Client,larkAppSecret 对 worker withheld;入口删除 process.env.BOTS_CONFIG,防止 worker 内 agent 读到真实 fleet 凭证。
  • 冻结 SESSION_DATA_DIR 到专用目录、BOTMUX_WORKER_HTTP_HOST 与 WEB_EXTERNAL_HOST 冻结为 127.0.0.1。
  • 不跑 fleet 级 auto-restart / botmux restart / 共享 HOME breadcrumb,绝不触碰同机全局 botmux 安装。
  • loopback 只是连通性不是身份(同机进程含 bwrap 沙箱都能拨 127.0.0.1),鉴权仍是硬门:除第一层三条控制路由 + /healthz、/__health 外全部要鉴权,没有任何一层「裸 loopback 就放行」。

端点速查

端点方法鉴权用途
/healthzGET公共就绪探针(503 starting / 200 ok)
/api/triggerPOST公共发起一轮任务(须带 HTTP 应答模式)
/api/sessions/:id/trigger-resultGET公共轮询最终结果(四态);live worker 附 readOnlyUrl+viewToken
/api/sessions/:id/insightGET公共轮询对话/进度
/api/sessions/:id/write-linkGETHMAC + bind取可写终端 URL(建议打开时现取)
/api/sessions/:idGETHMAC + bind会话元信息
/api/sessions/:id/closePOSTHMAC + bind 或会话内 capability取消/关闭会话

相关页面