fix(gateway): use get_secret for platform tokens in multiplexer scope - #51115
fix(gateway): use get_secret for platform tokens in multiplexer scope#51115manus-use wants to merge 2 commits into
Conversation
When tool_preview_length is set to 0 (the documented "no limit" value), the gateway progress preview incorrectly fell back to 40 characters due to a falsy check: `_cap = _pl if _pl > 0 else 40`. Replace the pattern with an explicit guard that only truncates when _pl is a positive integer, making 0 truly mean "no limit" as documented in the config reference. Fixes NousResearch#51067
When gateway.multiplex_profiles is enabled, _apply_env_overrides was
resolving platform tokens (TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, etc.)
via raw os.getenv(), which reads the process-global environment. In a
multiplexed setup this means a secondary profile's adapter gets the
DEFAULT profile's bot token instead of its own.
Replace all os.getenv() calls in _apply_env_overrides with get_secret()
from agent.secret_scope. This function:
- Reads from the active set_secret_scope mapping during a multiplexed
turn (profile-scoped, authoritative)
- Falls back transparently to os.environ when no scope is installed
(single-profile / non-multiplex deployments — no behavior change)
- Raises UnscopedSecretError (fail-closed) when multiplex mode is
active but no scope is set, surfacing any remaining unscoped reads
loudly instead of silently leaking another profile's credentials
Fixes: NousResearch#51029
|
Duplicate of #50094 — same fix: replaces os.getenv with get_secret (agent.secret_scope) for platform tokens in gateway/config.py::_apply_env_overrides so multiplexed secondary profiles read their own profile-scoped tokens. #50094 is the earlier canonical PR (and a superset, adding the nested multiplex config layer). #51044 was already flagged duplicate of #50094; #49474/#49484 are the load_gateway_config sibling. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the multiplex credential-isolation work. The principal fix is already present on current main: gateway/config.py:174-189 resolves active profile-scoped values, _apply_env_overrides() uses that helper at gateway/config.py:1450, and secondary startup loads config within _profile_runtime_scope at gateway/run.py:8550-8551 (0f154e780e71c74f8a1cdccb25c97a6abd8e5a57, released in v2026.7.7).
Problems
- The bundled preview change reverses current intentional all/new gateway behavior.
gateway/run.py:17409-17427retains a 40-character default cap whentool_preview_lengthis0;f1c084714restored that cap because full persistent messages were undesirable, andtests/gateway/test_run_progress_topics.py:570-580verifies it. tests/gateway/test_tool_preview_length_zero.pysimulates the proposed branch locally instead of exercising the gateway progress path.
Suggested changes
- Remove the already-landed scope fix.
- Split or drop the preview change; changing the deliberate gateway-message cap needs a separate maintainer decision and an integration test.
This is an automated hermes-sweeper review.
| """When _pl == 0, no truncation occurs regardless of string length.""" | ||
| set_tool_preview_max_len(0) | ||
| _pl = get_tool_preview_max_len() | ||
| long_preview = "x" * 200 |
There was a problem hiding this comment.
This test only reimplements the proposed conditional locally; it never invokes the gateway progress callback. Current main intentionally asserts that all/new mode with tool_preview_length: 0 truncates at 40 characters in tests/gateway/test_run_progress_topics.py:570-580, following f1c084714.
|
Closing as redundant on current
Nothing left to salvage, but the diagnosis was right — thanks for the thorough sweep, and sorry it collided with a parallel fix. |
Summary
What: Replace
os.getenvwithget_secretfor all platform token reads in_apply_env_overridesWhy: Platform tokens in
_apply_env_overridesused rawos.getenv()which bypasses the per-profile secret scope, causing secondary profiles to inherit the default profile's credentials. Whengateway.multiplex_profilesis enabled, each profile has its own.envwith its own bot tokens. Theset_secret_scope()/get_secret()machinery already exists inagent.secret_scopefor exactly this purpose, but_apply_env_overrideswas never migrated to use it.How: Replace all
os.getenv()calls inside_apply_env_overrideswithget_secret()fromagent.secret_scope. This function:os.environtransparently — no behavior change for single-profile setupsUnscopedSecretError(fail-closed) — surfaces any remaining unscoped reads loudly instead of silently leakingAlso adds
from agent.secret_scope import get_secretimport at the top ofgateway/config.py.Testing:
New test file covers:
get_secretreads fromos.environwhen no scope is active (7 tests across Telegram, Discord, Slack, Matrix, WhatsApp Cloud, Mattermost, Weixin)os.environtoken; two profiles get different tokens (6 tests)UnscopedSecretErrorraised in multiplex mode with no scope installed (3 tests)get_secretis patched atgateway.config.get_secretto verify the function is actually called, notos.getenv(3 tests including exhaustive check of 9 primary credentials)All 19 tests pass.
Security impact: Fixes credential leakage between multiplexed profiles. Before this fix, a secondary profile's Telegram/Discord/Slack/WhatsApp/etc. adapter would silently connect using the default profile's bot token.
Closes #51029