Skip to content

fix(gateway): scope terminal config per routed profile in multiplexed gateway - #68611

Closed
x7peeps wants to merge 1 commit into
NousResearch:mainfrom
x7peeps:fix/issue-68559-multiplexed-gateway-terminal-backend-routing
Closed

x7peeps wants to merge 1 commit into
NousResearch:mainfrom
x7peeps:fix/issue-68559-multiplexed-gateway-terminal-backend-routing

Conversation

@x7peeps

@x7peeps x7peeps commented Jul 21, 2026

Copy link
Copy Markdown

背景

修复 #68559: 多路复用网关忽略路由 profile 的 terminal backend 配置,Docker profile 继承了本地 backend

根因分析

多路复用网关启动时,将默认 profile 的 terminal.* 配置桥接到进程全局 TERMINAL_* 环境变量。_profile_runtime_scope() 在路由到二级 profile 时只切换了 hermes_home 和 secret_scope,但没有切换 terminal 配置。tools.terminal_tool._get_env_config() 从 os.environ 读取,导致被路由的 profile 继承了网关启动 profile 的 terminal backend 设置。

当默认 profile 使用 local backend、被路由的 profile 配置为 docker 时,这构成了安全边界失效:本应被 Docker 沙箱隔离的 profile 实际获得了主机终端访问权限。

修复方式

  1. tools/terminal_tool.py: 引入 ContextVar 类型的终端配置作用域(_terminal_config_scope),并提供 set_terminal_config_scope() / reset_terminal_config_scope() 辅助函数。重构 _get_env_config():作用域激活时从作用域 dict 读取,否则回退到旧版环境变量路径。

  2. gateway/run.py: 在 _profile_runtime_scope() 中,进入路由 profile 作用域时同时安装该 profile 的 terminal 配置(从 profile 自身的 config.yaml 读取),退出时清理。新增 _resolve_profile_terminal_config() 辅助函数。

  3. 不变量保证:

    • 不修改 os.environ(进程全局)
    • ContextVar 随 copy_context() 传播到 agent worker 线程
    • 嵌套作用域正确恢复
    • 无 terminal 段的 profile 不影响现有行为

测试

  • 新增 tests/gateway/test_68559_terminal_config_scope.py,5 个测试全部通过
  • 现有 multiplex 测试(test_multiplex_credential_isolation.py)10 个测试全部通过

验证

  • 代码变更已在本地验证
  • 遵循项目 AGENTS.md 贡献规范
  • 无破坏性变更
  • 向后兼容:无作用域时完全走旧版环境变量路径

Closes #68559

… gateway

Fix NousResearch#68559

**根因分析**

多路复用网关启动时,将默认 profile 的 terminal.* 配置桥接到进程全局
TERMINAL_* 环境变量。_profile_runtime_scope() 在路由到二级 profile 时
只切换了 hermes_home 和 secret scope,但没有切换 terminal 配置。
tools.terminal_tool._get_env_config() 从 os.environ 读取,导致被路由的
profile 继承了网关启动 profile 的 terminal backend 设置。

当默认 profile 使用 local backend、被路由的 profile 配置为 docker 时,
这构成了安全边界失效:本应被 Docker 沙箱隔离的 profile 实际获得了主机
终端访问权限。

**修复方式**

1. 在 tools/terminal_tool.py 中引入 ContextVar 类型的终端配置作用域
   (_terminal_config_scope),并提供 set_terminal_config_scope() 和
   reset_terminal_config_scope() 辅助函数。

2. 重构 _get_env_config():当作用域激活时,从作用域 dict 读取终端配置;
   否则回退到旧版环境变量路径,保持向后兼容。

3. 在 gateway/run.py 的 _profile_runtime_scope() 中,进入路由 profile
   作用域时也安装该 profile 的 terminal 配置(从 profile 自身的 config.yaml
   读取),退出时清理。

4. 新增 _resolve_profile_terminal_config() 辅助函数,从目标 profile 的
   config.yaml 中读取 terminal 段,不修改 os.environ。

5. 添加回归测试验证作用域隔离、嵌套作用域、以及无 terminal 段的 profile
   不会崩溃。
@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery tool/terminal Terminal execution and process management backend/docker Docker container execution area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jul 21, 2026
@x7peeps x7peeps closed this Jul 23, 2026
muhifni added a commit to muhifni/hermes-agent that referenced this pull request Sep 2, 2026
A multiplexed Hermes process (gateway.multiplex_profiles, unified
dashboard/TUI, or cron) can serve several profiles at once. Terminal
settings used to resolve through process-global TERMINAL_* env vars plus
the one-shot _ensure_terminal_env_bridged() guard. The first profile to
touch a terminal tool after startup therefore pinned its backend and
other policy (mounts, SSH target, network, cwd, resources) onto every
later profile until restart.

This is a correctness and sandbox-boundary bug: a local profile can run
inside another profile's docker sandbox, and a docker/ssh profile can be
dropped onto the launch host. Repro lineages: canonical NousResearch#68559, gateway
backend latch NousResearch#94200, dashboard/container symptoms NousResearch#98581/NousResearch#96992.

Fix with an authoritative profile terminal policy seam, analogous to
agent/secret_scope.py:

- tools/terminal_scope.py adds a ContextVar that holds the active
  profile's complete effective terminal policy. Projection order is
  defined defaults + supplemental tool defaults <- profile .env
  TERMINAL_* values <- explicit terminal: config.yaml keys. Once a scope
  is bound, missing values never fall through to ambient os.environ.
- install_profile_terminal_scope() fail-closes: unreadable/malformed
  policy installs a refusal scope; terminal_tool / execute_code refuse
  execution instead of inheriting launch-process policy.
- tools/terminal_tool.py routes TERMINAL_* reads through the scope-aware
  _tenv() helper and suppresses the process-env bridge while scoped.
- gateway/run.py, tui_gateway/server.py and cron/scheduler.py install the
  same profile terminal scope at their in-process profile boundaries.
- Other scoped readers from the first patch (prompt/cwd/media/footer)
  remain routed through the same seam.

Tests now cover the review-requested matrix: polluted launch profile A
with sensitive mounts/SSH/CWD/network/resource policy; profile B with
backend-only, empty terminal config, or .env-only selections cannot
observe A's values. They also cover malformed/unreadable policy refusal,
terminal_tool refusal, gateway cleanup including error paths,
dashboard/TUI session scope, and cron install/reset lifecycles.

Prior art / lineage: x7peeps NousResearch#68611 (original ContextVar direction),
100yenadmin NousResearch#79117/NousResearch#78030, liuhao1024 NousResearch#94206/NousResearch#98589, and complementary
Bergmann89 NousResearch#97014 (env_loader re-bridge containment).

Fixes NousResearch#68559
Refs NousResearch#94200, NousResearch#98581, NousResearch#96992
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping backend/docker Docker container execution comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/terminal Terminal execution and process management type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Multiplexed gateway ignores routed profile terminal backend; Docker profile inherits local backend

2 participants