# Master Server 运维 本文记录 Master Server 的架构与判定规则。已安装的 Codex wrapper 负责命令语法, 本文负责 profile 边界、安全规则和验收标准。通用 server 生命周期与 GC 命令仍由 `unidesk-ops` skill 负责。禁止在本文记录 Secret、一次性故障流水或带日期的 changelog。 ## 执行边界 - Master Server 是生产入口和控制面。 - 本机默认只执行轻量源码修改、Git 操作、状态检查、健康检查、日志、诊断、 JSON CLI 控制动作和本地 profile wrapper 维护。 - 重型构建和仓库级检查必须放到 G14、D601、CI/CD 或目标运行面。 - 本机 `/root/unidesk` checkout 可用于轻量 UniDesk CLI、`trans`、`tran` 和 helper 修改。 ## Codex Provider Profile - `mycx` 与 `oncx` 是 `/usr/local/bin/` 下的稳定系统 Codex wrapper。 - 两者复用同一个系统 `codex` binary。 - 两者的认证 home 已隔离,长期约束见“系统 Wrapper 与共享状态”。 - `dscx`、`mxcx` 和 `acx` 是 `/root/.local/bin/` 下的隔离 profile wrapper。 - 它们是 host 级工具,不属于 UniDesk service 代码。 - `acx` 是统一多模型入口。 - `gocx` 保留为 OpenCode Zen Go 兼容入口。 - 旧的 `dscx-go`、`dfcx-go` 和 `glcx-go` 只保留兼容入口。 - `dscx`: - 使用 `CODEX_HOME=/root/.codex-deepseek-v4-pro`; - 使用模型 `deepseek-v4-pro` 和 Codex 自定义 provider `deepseek`; - 使用本机 Moon Bridge `http://127.0.0.1:38440/v1`。 - `mxcx`: - 使用 `CODEX_HOME=/root/.codex-minimax-m3`; - 使用模型 `MiniMax-M3` 和 Codex 自定义 provider `minimax`; - 使用本机 Moon Bridge `http://127.0.0.1:38441/v1`。 - `acx`: - 使用 `CODEX_HOME=/root/.codex-acx`; - 默认模型是 `gpt-5.5-only`; - GPT alias 通过 Codex 自定义 provider 直连 OpenAI-compatible Responses 上游; - OpenCode Zen Go alias 仍通过本机 ACX router 到达 `gocx` / Moon Bridge。 - `gocx`: - 使用 `CODEX_HOME=/root/.codex-opencode-go-all`; - 默认模型是 `glm-5.1`; - 使用 Codex 自定义 provider `opencode`; - 使用本机 Moon Bridge `http://127.0.0.1:38447/v1`。 - `dscx-go`: - 使用 `CODEX_HOME=/root/.codex-opencode-go`; - 使用模型 `deepseek-v4-pro`; - 使用本机 Moon Bridge `http://127.0.0.1:38443/v1`。 - `dfcx-go`: - 使用 `CODEX_HOME=/root/.codex-opencode-flash`; - 使用模型 `deepseek-v4-flash`; - 使用本机 Moon Bridge `http://127.0.0.1:38444/v1`。 - `glcx-go`: - 使用 `CODEX_HOME=/root/.codex-opencode-glm`; - 使用模型 `glm-5.1`; - 使用本机 Moon Bridge `http://127.0.0.1:38446/v1`。 - `acx` 的 `model-catalog.json`: - 包含全部 OpenCode Zen Go upstream slug; - 包含 `gpt-5.5-only` 和 `gpt-5.5-sub2api`; - 允许在同一 profile 中通过 `/model` 或 `-m ` 切换。 - `acx` 的 GPT 路由: - `gpt-5.5-only` 直连 `only` OpenAI-compatible Responses endpoint; - `gpt-5.5-sub2api` 直连 Sub2API pool endpoint; - 两者的 upstream model 都是 `gpt-5.5`; - GPT 请求禁止经过 Moon Bridge 或 ACX router。 - GPT direct provider: - 必须通过 `ACX_GPT_DIRECT_API_KEY` 一类环境键接收 API key; - wrapper 从对应的 `/root/.codex/auth.json.` 读取; - 禁止回退到 `/root/.codex-acx/auth.json`; - `/root/.codex-acx/auth.json` 只允许保存本地 router dummy key。 - 隔离的 Moon Bridge 与 router wrapper: - 从 profile auth 文件读取 upstream API key; - 仅在兼容路径确有需要时由 wrapper 注入环境变量; - 生成配置放在 profile 的 `.tmp/`,权限必须是 `0600`; - 此兼容规则不适用于 `mycx` 的 Pika command-backed auth。 - 每个 profile: - 必须在 `config.toml` 中设置 `model_catalog_json`; - 必须指向包含当前模型的 profile-local `model-catalog.json`; - 缺失时 Codex 会回退到默认 metadata,并输出 `Model metadata ... not found`。 - Profile context metadata: - 必须与真实 upstream limit 对齐; - 必须让 Codex auto-compact 在 provider 拒绝请求前触发。 - 当前 Master Server profile baseline: - `acx` 暴露的 GPT profile 使用 `model_context_window = 272000` 和 `model_auto_compact_token_limit = 240000`; - `deepseek-v4-pro` 与 `deepseek-v4-flash` 使用 `model_context_window = 1000000` 和 `model_auto_compact_token_limit = 900000`; - `glm-5.1`、`MiniMax-M3` 及其他本地 Moon Bridge profile 使用 `model_context_window = 200000` 和 `model_auto_compact_token_limit = 180000`。 - Wrapper 生成的 Moon Bridge / router metadata: - 必须与 profile `config.toml` 和 `model-catalog.json` 对齐; - 不一致时 Codex 与本地 admission layer 会对 compact 和请求大小产生分歧。 - Codex API 通道的 `NO_PROXY` / `no_proxy`: - 必须保留 `hyueapi.com` 和 `.hyueapi.com`。 ### 系统 Wrapper 与共享状态(`mycx` / `oncx`) - `mycx` 与 `oncx`: - 复用同一个系统 `codex` binary; - 使用不同的认证 `CODEX_HOME`; - 共享 SQLite session index 和 durable transcript。 - `/usr/local/bin/mycx`: - 使用 `CODEX_HOME=/root/.codex`; - 选择 profile `pika`; - 可写 profile 文件是 `/root/.codex/pika.config.toml`。 - `/usr/local/bin/oncx`: - 使用 `CODEX_HOME=/root/.codex-only`; - 使用 `CODEX_SQLITE_HOME=/root/.codex`; - 选择 profile `only`; - 可写 profile 文件是 `/root/.codex-only/only.config.toml`。 - `oncx` 的账号认证: - 只使用 `/root/.codex-only/auth.json`; - `cli_auth_credentials_store` 必须是 `file`; - `forced_login_method` 必须是 `chatgpt`; - `auth.json` 权限必须是 `0600`; - wrapper 必须清理 `OPENAI_API_KEY`、`CODEX_API_KEY`、 `CODEX_ACCESS_TOKEN` 和 `CRS_OAI_KEY`; - `oncx login`、`oncx login status` 和 `oncx logout` 必须直接操作 隔离 home,禁止附加仅适用于 runtime command 的 `--profile`。 - `oncx` 的共享状态: - `/root/.codex-only/sessions` 链接到 `/root/.codex/sessions`; - `/root/.codex-only/archived_sessions` 链接到 `/root/.codex/archived_sessions`; - `history.jsonl`、`skills` 和 `plugins` 链接到共享 home; - shell snapshot、认证文件和 profile 配置保持隔离。 - `/root/.codex/config.toml` 与 `/root/.codex/auth.json`: - 由 `codex-pool configure-local` 管理; - 只代表 Sub2API consumer; - 禁止作为 `oncx` 的 ChatGPT 登录凭据。 - Webterm: - `config/platform-infra/webterm.yaml` 启动 `mycx`; - YAML 是 target、镜像、端口、启动命令、fallback shell、挂载和公网暴露的唯一真相; - 关闭公网暴露: - `publicExposure.enabled: false` 时,DNS、FRP 和 Caddy 字段不参与解析与验证; - `plan`、`status` 和 `validate` 必须明确返回 `skipped`。 - target 校验: - 按 YAML 中的实际端口和 `TERMINAL_SHELL` 只读挂载判断; - 禁止把 `7683`、固定挂载数量或其他实例值写入代码门禁。 - PTY 所有权: - `nsenter` 或 `chroot` 后必须由唯一的长期交互 Bash 持有 PTY; - 禁止让外层非交互 Bash 等待内层交互 Bash,再在内层退出后创建 fallback shell。 - 启动命令中断: - 持有 PTY 的 Bash 必须在启动命令运行期间显式处理 `SIGINT`; - 前台子命令退出后继续执行 fallback,再恢复默认 handler; - fallback shell 必须由原进程 `exec`,禁止创建新的等待层。 - Webterm launch check: - wrapper path 或启动方式变更必须保留原入口; - 从真实 Web/xterm 入口启动 `mycx`、发送 `Ctrl+C` 并执行 marker; - 确认会话仍为 `running`; - 确认没有 `initialize_job_control`、`no job control` 或非主动 `logout`。 - foreground process group 验证: - 使用 Bash 内建 `read < /proc/$$/stat` 同时读取 `pgrp` 和 `tpgid`; - 禁止用外部 `ps` 读取后直接比较; - `ps` 执行期间会暂时成为 foreground process group,导致错误的不相等结果。 - Shell startup 文件: - 禁止定义同名 function 覆盖这些 executable; - 禁止在启动时重新生成 profile 文件; - wrapper 变更后,旧 Codex 进程必须退出; - 旧 shell 必须清除残留 function 或启动新 shell后再验收。 - Profile-v2 持久化: - `/model` 把 `model` 与 reasoning effort 写入当前 profile 文件; - `mycx` 与 `oncx` 必须独立记住最后一次选择; - `-m`、`-c model...` 和 `-c model_reasoning_effort...` 只允许临时覆盖; - 新启动、profile 切换和 resume 必须分别诊断。 - Codex 官方认证边界: - `--profile` 只覆盖 profile TOML,不隔离 `auth.json`; - file-based credential storage 的 `auth.json` 由 `CODEX_HOME` 决定; - 账号登录与 API-key consumer 必须使用不同的 `CODEX_HOME`; - 依据见 [Codex Manual](https://developers.openai.com/codex/codex-manual.md#configuration-auth-and-models)。 - Session discovery: - 共享 SQLite 和 transcript 不保证 `/resume` 结果完全相同; - picker 还会按 provider ID、cwd、source 和 archive state 过滤; - provider ID 是持久化且大小写敏感的 session bucket key; - `--all` 只移除 cwd 限制,不移除 provider 限制; - Codex 升级后必须重新验证此契约; - resume 使用当前 profile 的有效 model、provider 和 reasoning override; - resume 不保证恢复 session 最后记录的模型。 - Rollout JSONL: - 是 durable history; - 可以重新填充 SQLite index; - 禁止只修改 `state_5.sqlite` 合并 provider bucket; - history migration 必须停止活动 Codex 进程; - 必须用结构化 JSON parser 修改 rollout metadata; - 必须事务化更新 SQLite index; - 必须保留 provenance 并在 reindex 后验证相同 session ID 集合。 - Provider identity: - 是兼容性契约; - 修改 `CODEX_HOME`、profile name、provider ID 或大小写前必须预检历史; - 会产生新隐藏 bucket 时必须告警或阻断; - 禁止让不同真实 backend 仅为合并 picker list 而冒充同一 provider。 - Pika 认证: - 必须使用 `[model_providers.Pika.auth]` 的 command-backed provider auth; - credential source 是 `/root/.codex/auth.json.pika`; - 文件权限必须是 `0600`; - 禁止把 Pika API key 导出到 wrapper 或 Codex 环境; - shell snapshot 权限必须是 `0600`; - shell snapshot 禁止包含可复用 credential; - 发现 plaintext snapshot 时必须删除副本并轮换 credential。 - Secret-safe 诊断只允许报告: - 有效 `CODEX_HOME`; - wrapper path; - 当前 profile 文件; - provider ID; - model 与 reasoning; - 按 provider 统计的 session 数量; - credential presence 或 fingerprint; - 相关文件权限。 - Profile 持久化验收: - 选择一个非 baseline `/model`; - 退出后重新启动相同 wrapper; - 确认选择保持; - 确认另一 profile 文件未变化; - 确认临时 CLI override 不写回 profile。 - `oncx` 认证验收: - `oncx login status` 必须显示 `Logged in using ChatGPT`; - 注入无效 API-key 环境变量后仍必须显示 ChatGPT 登录; - `oncx exec --ephemeral ''` 必须完成真实 Responses turn; - 共享 `codex login status` 必须继续显示 Sub2API API-key 登录; - 全程禁止输出 token 或完整 API key。 - `mycx` 认证验收: - 对应 API-key 环境变量缺失时,Pika command-backed auth 仍必须可用。 - `/resume` 差异诊断: - 先比较 `CODEX_HOME`、provider ID、cwd、source 和 archive filter; - 禁止在这些边界未核对前归因为 storage loss 或 cache。 相关调查证据与源码级依据保留在 [UniDesk issue #1640](https://github.com/pikasTech/unidesk/issues/1640),本文是当前长期规则的 唯一权威入口。 ## Moon Bridge Moon Bridge 安装在 `/root/.local/bin/moonbridge`。该 binary 暴露 OpenAI Responses endpoint,并把 Codex 请求桥接到 provider-specific upstream API。已安装的 profile wrapper 负责 bridge 命令语法,本文只记录运行边界。 - 已安装 patch build 的本地源码: - 路径是 `/root/src/moon-bridge-sanitize`; - 修改时必须保持范围收敛; - 必须运行 package-level test; - 必须构建 replacement binary; - 必须备份旧 binary; - 必须重启受影响的 profile bridge; - 仍受 Master Server 重型构建限制。 ### Profile 架构 - `dscx bridge-start`: - 渲染 profile 配置; - 在 `127.0.0.1:38440` 启动 Moon Bridge。 - `mxcx bridge-start`: - 渲染 profile 配置; - 在 `127.0.0.1:38441` 启动 Moon Bridge。 - `acx route-start`: - 渲染 ACX router 配置; - 在 `127.0.0.1:38448` 启动本地 routing service; - 只服务仍需 OpenCode Zen Go bridge path 的非 GPT alias。 - `gocx bridge-start`: - 渲染多模型 OpenCode Zen Go profile 配置; - 在 `127.0.0.1:38447` 启动 Moon Bridge。 - `dscx-go bridge-start`: - 在 `127.0.0.1:38443` 启动 Moon Bridge。 - `dfcx-go bridge-start`: - 在 `127.0.0.1:38444` 启动 Moon Bridge。 - `glcx-go bridge-start`: - 在 `127.0.0.1:38446` 启动 Moon Bridge。 - Wrapper 启动 bridge: - 使用 `setsid`; - 使用 profile-local PID 文件; - 日志写入 `/logs/moonbridge/`。 - `dscx` 路由: - 通过 Moon Bridge 使用 Anthropic-compatible upstream; - 使用 `deepseek_v4` extension。 - `mxcx` 路由: - 通过 Moon Bridge 使用 `openai-response` upstream passthrough。 - OpenCode Zen Go 路由: - `acx` 把模型路由到 `gocx` / Moon Bridge; - `gocx`、`dscx-go`、`dfcx-go` 和 `glcx-go` 使用 `openai-chat` upstream; - upstream 是 `https://opencode.ai/zen/go/v1/chat/completions`; - Codex 侧保持 `wire_api = "responses"`。 - `acx` GPT 路由: - 必须直连 OpenAI-compatible Responses endpoint; - 禁止经过 Moon Bridge 或本地 ACX router; - GPT mode 下 `acx status` 必须报告 `mode=gpt-direct`; - 必须报告 `routerRequired=false`; - `127.0.0.1:38448` 不应存在 listener。 - OpenCode Zen Go model ID: - 必须使用 upstream slug,例如 `glm-5.1`; - `GLM-5.1` 一类 display name 不是 profile model identifier。 - OpenCode Zen Go bridge: - 禁止保留手写 bridge script; - 禁止保留静态替代 `moonbridge.config.yml`; - 唯一支持路径是 wrapper 生成的 `.tmp/moonbridge.generated.yml` 和 `/root/.local/bin/moonbridge`; - 生成的 provider 配置必须显式设置 `user_agent`。 - Moon Bridge context-window admission: - oversized request 必须在本地 Responses path 被拒绝; - 预期状态是 HTTP `400`; - 错误类型是 `invalid_request_error`; - 错误 code 是 `context_length_exceeded`; - 禁止表现为 upstream-translated `502 Bad Gateway`。 - MiniMax: - 未经 tool-enabled smoke 证明可用,禁止把 Codex CLI 切到 `openai-chat`。 ### Profile 验收 - `*-go raw-smoke`: - 直接验证 upstream OpenCode Zen Go Chat Completions API。 - `*-go bridge-smoke`: - 验证本地 Moon Bridge 的 `/v1/responses` translation path。 - `*-go exec '在吗'`: - 验证真实 Codex profile; - 输出禁止包含 `Model metadata ... not found`; - 最新 session 必须使用 profile catalog 提供的 `model_context_window`。 - ACX GPT 验收: - 使用 `acx status`、`acx models`、 `acx gpt-only exec '在吗'`、`acx gpt-sub2api exec '在吗'` 和 默认 `acx exec '在吗'`; - 必须完成真实 Codex Responses turn; - repeated 或 resume traffic 必须出现非零 `cached_input_tokens`; - 禁止依赖 `127.0.0.1:38448` listener。 - ACX OpenCode Zen Go alias 验收: - 使用 `acx route-start`、`acx route-status`、`acx models` 和 `acx -m exec '在吗'`; - 验证 router 到 `gocx` 的路径。 - `gocx` 模型验收: - 使用 `gocx raw-smoke [model]`; - 使用 `gocx bridge-smoke [model]`; - 使用 `gocx -m exec '在吗'`; - 省略 `[model]` 时默认使用 `glm-5.1`。 - `ReasoningSummaryDelta without active item`: - 是 reasoning summary event 的独立 adapter noise; - 不等同于 model metadata 缺失; - 不能单独证明 profile 不可用。 ### OpenAI Responses Tool Argument Sanitizer - Moon Bridge: - 负责 MiniMax 风格错误 tool argument JSON 的在线 sanitizer; - 作用于 `openai-response` passthrough path; - 是第一道防线; - wrapper-level session cleanup 只处理 sanitizer 安装前已损坏的 JSONL。 - Sanitizer: - 递归扫描 `ResponsesRequest.input`; - upstream 前修复 tool-call `arguments`; - 在 completed event 中修复 response-side `arguments`; - 禁止修改 delta chunk; - 无法恢复时回退到 `{}`; - response rewrite 必须删除 upstream `Content-Length`。 ## MiniMax Session 恢复 - `mxcx`: - 为损坏的 MiniMax-backed session JSONL 提供 cleanup 与 guard layer; - wrapper 负责命令语法; - 本节只记录行为规则。 - `mxcx resume`: - 调用 Codex 前自动执行 `session-clean` 和 `session-guard`; - 使用 `CODEX_HOME=/root/.codex-minimax-m3`。 - `session-clean`: - 只处理无效 tool-call `arguments`; - 包括 malformed JSON、MiniMax sentinel text 和 schema-invalid case; - 必须保留行顺序、非 tool message、reasoning、output、token record 和 session metadata; - 禁止 compact、summarize、truncate 或 reorder transcript。 - 新的无效 `arguments`: - 必须由 Moon Bridge sanitizer 在线阻止; - `session-clean` 只用于恢复既有损坏 session。 - `session-guard`: - 注入 `mxcx-minimax-tool-and-apply-patch-guard-v2`; - 禁止重复注入。 ## MiniMax Apply-Patch 操作 - MiniMax-backed session: - 必须使用与其他 agent 相同的 UniDesk 远端文本 patch 契约; - route 在前,operation 在后; - 默认使用 `apply-patch` v2。 - 稳定写入形式: - `trans :/absolute/workspace apply-patch < patch.diff`。 - 只读检查: - 使用 `trans :/absolute/workspace nl -ba file` 或同等有界命令。 - `apply-patch` 报告 `failed to find expected lines` 时: - 先读取目标当前精确内容; - 再用更小的 `Update File` hunk 重试; - 或使用 `@@ `; - 或拆成多个小 hunk; - 这是正常 stale-context 恢复,禁止因此切换工具。 - 文本 patch 失败时禁止使用: - `download` / `upload`; - 远端 Python、Perl 或 sed heredoc; - `cat >` 或 `tee` 整文件覆盖; - `apply-patch-v1`。 - 仅在以下情况允许例外: - `apply-patch` 本身不可用; - 目标不是文本; - 目标是批量机械生成内容。