Skip to content

fix(start-goal): reject desktop host in SSH sessions - #6312

Merged
huangruiteng merged 1 commit into
mainfrom
codex/fail-closed-codex-app-ssh-host-mismatch
Oct 12, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/fail-closed-codex-app-ssh-host-mismatch

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Summary

  • fail closed when a Codex thread running over SSH selects the desktop-only codex-app host surface
  • return a typed host_surface_environment_mismatch gate that points callers to codex-app-ssh without exposing environment values
  • prove that the corrected route still emits the thin codex_app_ssh_goal bootstrap instead of desktop heartbeat automation

Root cause

start-goal trusted an explicit host surface even when CODEX_THREAD_ID and SSH transport signals made that selection contradictory. The downstream host profiles were correct, but the CLI admitted the wrong profile and generated a desktop automation activation packet for a remote SSH session.

Scope

This stays in the existing Python start-goal CLI boundary because it validates local process environment before control-plane packet construction. No frontend change is needed: the affected entry point is the CLI host-selection path, and the typed JSON/Markdown failure is the user feedback surface. No adjacent refactor was needed.

Validation

  • 64 start-goal projection tests passed
  • 32 CLI output-budget tests passed
  • ruff checks passed
  • mypy passed
  • semantic vocabulary smoke passed
  • LoopX premerge canary passed with all 5 selected checks
  • public/private boundary scan passed

Signed-off-by: huangruiteng <huangrt01@163.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · xhigh

动机

在 SSH 工作区使用 Codex App 启动 Goal 的操作者。 原流程允许把远端 SSH 工作区选为桌面 App 宿主,生成无法在该宿主执行的 heartbeat 激活步骤,后续工作因此停在错误入口。 已观察到的 Codex SSH 运行时误选桌面 App 时,当前 head 提前拒绝;改用原 SSH profile 后生成 visible Goal 任务包,本地 App 和 CLI 路由保持。 本 PR 不增加远端宿主权限,不负责实际创建自动化,也不把命令预览当成 Goal 已启动。

改动思路

宿主检测应保留运行时环境证据与显式路由参数的区别;在既有 Python CLI 环境 IO 预检中提前拒绝即可,不需要额外身份权威、自动改宿主或 TypeScript 重写。 本 PR 交付已观察到的 Codex SSH 运行时误选桌面宿主的提前拒绝、原 SSH visible Goal 纠正包及本地 App/CLI 行为保留;预览不等于实际激活。

独立依据:docs/reference/protocols/loopx-goal-command-v0.md,冻结修订 323c3053128e41e16df27e6b6498f83c37e81f5e 的 Goal-Start Flow。SSH App 应走 visible Goal,桌面 App 才走 heartbeat。同修订 docs/reference/protocols/codex-app-host-command-registry-v0.md 明确只读预览与当前宿主选择边界。另核对既有 _host_thread.py::ambient_host_thread_id:宿主提供的环境上下文是本地运行时观察,显式 --thread-id 是路由输入,不能单靠它认定当前进程属于 Codex;这也不是身份认证。

具体改动

精确 head 2d964f488a026697c9bfe96985b5f0eabbda359f;全 PR 2 文件 +135/-1(生产 +57/-1,测试 +78),全差异和上下游调用均已读取。

  • start_goal.py::_host_surface_environment_mismatch_payload(124)在 codex-app + 环境线程标识 + SSH_CONNECTION/SSH_CLIENT 时返回 host_surface_environment_mismatch、非零退出、所需 SSH profile 及纠正建议。只列环境变量名,不回显值;安全字段说明不写 registry、不激活宿主。
  • handle_start_goal_command(335)在 packet 构建前执行预检;_render_start_goal_markdown(105)增加 error_code 展示。已有 typed host profile/activation owner 和显式/环境线程解析没有改变。
  • test_start_goal_compact_projection.py 隔离测试宿主 SSH 环境,增加 wrong-host 拒绝与 corrected-host bootstrap 对照。纠正后的包使用 --thin --runtime-profile codex_app_ssh_goal,不请求桌面 automation。
  • 非阻断建议:suggested_command 目前包含字面 ...。recommended_action 已明确要求原命令仅更换 host flag,可正常纠正;后续可由原 CLI 参数 owner 提供保留 project/Goal/Agent/thread/goal-text 的完整 rerun 命令,减少手工重填。该建议不构成额外审批或权限门槛。

对主干的风险

独立冻结 base/head 对同一 8 例实际 CLI 比较:ambient SSH + App(有/无显式 thread 参数)与 SSH_CLIENT-only 在 base 都生成桌面 activation,head 均 exit2 mismatch;保留环境宿主证据时显式参数不能绕过。纠正为 SSH profile 后两者均 set_visible_goal,本地 App 与 CLI 路由不变;普通 SSH shell 缺少环境 Codex 证据时,即使提供显式 thread route 也保留原行为。

审查中曾把“删去环境宿主证据、仅保留显式 ID”的输出当成绕过;核对既有本地运行时来源契约后撤回这一判断。没有把 caller route 提升为宿主证明或把缺证据视为权限。每例都比较可丢弃项目文件字节无变化,不创建实际 heartbeat、不改活动 Goal。纠正路径从拒绝继续到真实 native SSH command pack;不以错误回执声称后续工作已执行。

当前 96 项 start-goal projection/output-budget 验证、native premerge 5 direct +5 selected、Ruff 和 control-plane typecheck 通过。额外 strict mypy 单文件命令会沿 imports 报全树4722错误;冻结 base 与当前 head 的完整错误身份/细节逐项一致,只归一化行号,且本 PR 未修改其因果源。该既有诊断单列,核心 changed invariant 有独立通过证据;不以错误总数相等自证。未查询或等待 CI。

新 gate schema 是局部 CLI 诊断,host literal 重用既有 vocabulary;环境 IO 留在既有 Python CLI,TS 配额/生命周期 owner 不变。没有 default-off 功能声明、新 managed skill、substring denylist 或更广 actor/权限模型。早期拒绝是机器执行义务;纠正建议不执行激活。实际远端 Codex App、真实宿主工具和后续工作未运行,CLI/profile 预览资格不冒称 installed-host 资格。

我的整体评价

APPROVE,无阻断发现。宿主检测应保留运行时环境证据与显式路由参数的区别;在既有 Python CLI 环境 IO 预检中提前拒绝即可,不需要额外身份权威、自动改宿主或 TypeScript 重写。 本 PR 交付已观察到的 Codex SSH 运行时误选桌面宿主的提前拒绝、原 SSH visible Goal 纠正包及本地 App/CLI 行为保留;预览不等于实际激活。 原问题在受支持的运行时观察范围内得到实际 base/head 修复,纠正、本地与未覆盖调用方保持。相邻精简检查了现有 thread helper 与 host profile:显式路由和 ambient runtime fence 语义不同,不能为了“复用”合并为第二种宿主证明;无需新抽象。完整 rerun 的 P2 建议可跟随原 CLI owner 改进。批准只绑定该 head,不授予合并、账户切换或宿主激活。

English verdict: APPROVE - 2d964f4; observed Codex SSH runtime rejects desktop App routing and the corrected native SSH Goal route works. Independent eight-case base/head CLI coverage, 96 focused tests and native premerge pass; explicit-only caller routing is not ambient host evidence. Baseline-identical mypy diagnostics remain separate.

@huangruiteng
huangruiteng merged commit c980bad into main Oct 12, 2026
19 of 27 checks passed
@huangruiteng
huangruiteng deleted the codex/fail-closed-codex-app-ssh-host-mismatch branch October 12, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants