Repository navigation
fix(gateway): honor explicit api_server enabled:false under env key - #51374
Closed
davidgut1982 wants to merge 1 commit into
Closed
davidgut1982 wants to merge 1 commit into
davidgut1982 wants to merge 1 commit into
Conversation
This was referenced Jun 23, 2026
Closed
davidgut1982
marked this pull request as ready for review
June 23, 2026 14:08
tonydwb
approved these changes
Jun 23, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Targeted fix for the gateway config: an explicit enabled: false for api_server in config.yaml now survives _apply_env_overrides(), even when API_SERVER_KEY is present in the environment.
Looks Good
- Uses
_enabled_explicitmarker (pop convention) to distinguish explicit config from default - The guard
if not api_server_explicit or config.platforms[Platform.API_SERVER].enabledcorrectly handles both cases - Test verifies: explicit disable wins over env-var presence, but the key is still wired through
- 2 files changed, clean and focused
Reviewed by Hermes Agent
This was referenced Jun 25, 2026
10 tasks done
phantom-instruction-set
pushed a commit
to phantom-instruction-set/hermes-agent
that referenced
this pull request
Jul 5, 2026
The email adapter (plugins/platforms/email/adapter.py) read EMAIL_ADDRESS, EMAIL_PASSWORD, EMAIL_IMAP_HOST, EMAIL_SMTP_HOST, EMAIL_ALLOWED_USERS, and EMAIL_ALLOW_ALL_USERS via os.getenv() directly. In a multiplexed gateway, os.environ holds the default profile's .env values, so every secondary profile inherited the default profile's email credentials instead of its own. This was a sibling of the api_server env-leak bug (NousResearch#52307/NousResearch#50051): the same os.getenv→get_secret migration that PR NousResearch#50094 applies to gateway/config.py, but for the email adapter itself, which neither PR NousResearch#50094 nor NousResearch#51374 covers. Changes: - plugins/platforms/email/adapter.py: replace os.getenv with agent.secret_scope.get_secret for all EMAIL_* credential reads (adapter __init__, check_email_requirements, _allowlist_in_effect, _dispatch_message allowlist gate, _send_email SMTP helper). - gateway/config.py: add _getenv/_getenv_str/_getenv_int helpers (from PR NousResearch#50094) and replace os.getenv with _getenv for the email block in _apply_env_overrides, so config.platforms[EMAIL].extra is populated from the scoped value. - tests/gateway/test_email_secret_scope.py: 5 new tests covering scoped credential reads, environ fallback without scope, missing- key-no-leak, allowlist scoping, and check_email_requirements scoping. Related: NousResearch#50051, NousResearch#52307, PR NousResearch#50094, PR NousResearch#51374
Collaborator
|
Thanks for the focused regression coverage. Current Problems
Suggested changes
Automated hermes-sweeper review. |
_apply_env_overrides() force-set ``api_server.enabled = True`` whenever
API_SERVER_KEY (or API_SERVER_ENABLED) was present in the environment.
In multiplex mode, a secondary profile pins
``platforms.api_server.enabled: false`` in its config.yaml so that it
shares the default profile's API-server listener instead of binding its
own port. That profile still inherits the process-level env, including
API_SERVER_KEY, so the unconditional re-enable flipped api_server back on
and tripped the MultiplexConfigError check.
Honor an explicit disable, flagged by ``_enabled_explicit`` in the
platform's extra. Use ``extra.pop("_enabled_explicit", False)``: the
api_server branch is terminal (unlike the migrated plugin platforms, no
later registry pass re-enables api_server), so popping consumes the flag
in a single read and avoids the double-read hazard, while the final
per-platform cleanup remains a no-op.
Adds a regression test asserting that with API_SERVER_KEY set, a config
with api_server explicitly enabled:false + _enabled_explicit:true survives
_apply_env_overrides() as enabled=False (fails without the fix), while the
key is still wired through for the shared listener.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
davidgut1982
force-pushed
the
fix/api-server-env-override-secondary-profile
branch
from
July 15, 2026 11:50
16491e4 to
59195b0
Compare
Author
|
Addressed your review — reworked & rebased onto current
Please re-review. |
Collaborator
teknium1
pushed a commit
that referenced
this pull request
Aug 2, 2026
The email adapter (plugins/platforms/email/adapter.py) read EMAIL_ADDRESS, EMAIL_PASSWORD, EMAIL_IMAP_HOST, EMAIL_SMTP_HOST, EMAIL_ALLOWED_USERS, and EMAIL_ALLOW_ALL_USERS via os.getenv() directly. In a multiplexed gateway, os.environ holds the default profile's .env values, so every secondary profile inherited the default profile's email credentials instead of its own. This was a sibling of the api_server env-leak bug (#52307/#50051): the same os.getenv→get_secret migration that PR #50094 applies to gateway/config.py, but for the email adapter itself, which neither PR #50094 nor #51374 covers. Changes: - plugins/platforms/email/adapter.py: replace os.getenv with agent.secret_scope.get_secret for all EMAIL_* credential reads (adapter __init__, check_email_requirements, _allowlist_in_effect, _dispatch_message allowlist gate, _send_email SMTP helper). - gateway/config.py: add _getenv/_getenv_str/_getenv_int helpers (from PR #50094) and replace os.getenv with _getenv for the email block in _apply_env_overrides, so config.platforms[EMAIL].extra is populated from the scoped value. - tests/gateway/test_email_secret_scope.py: 5 new tests covering scoped credential reads, environ fallback without scope, missing- key-no-leak, allowlist scoping, and check_email_requirements scoping. Related: #50051, #52307, PR #50094, PR #51374
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
The email adapter (plugins/platforms/email/adapter.py) read EMAIL_ADDRESS, EMAIL_PASSWORD, EMAIL_IMAP_HOST, EMAIL_SMTP_HOST, EMAIL_ALLOWED_USERS, and EMAIL_ALLOW_ALL_USERS via os.getenv() directly. In a multiplexed gateway, os.environ holds the default profile's .env values, so every secondary profile inherited the default profile's email credentials instead of its own. This was a sibling of the api_server env-leak bug (NousResearch#52307/NousResearch#50051): the same os.getenv→get_secret migration that PR NousResearch#50094 applies to gateway/config.py, but for the email adapter itself, which neither PR NousResearch#50094 nor NousResearch#51374 covers. Changes: - plugins/platforms/email/adapter.py: replace os.getenv with agent.secret_scope.get_secret for all EMAIL_* credential reads (adapter __init__, check_email_requirements, _allowlist_in_effect, _dispatch_message allowlist gate, _send_email SMTP helper). - gateway/config.py: add _getenv/_getenv_str/_getenv_int helpers (from PR NousResearch#50094) and replace os.getenv with _getenv for the email block in _apply_env_overrides, so config.platforms[EMAIL].extra is populated from the scoped value. - tests/gateway/test_email_secret_scope.py: 5 new tests covering scoped credential reads, environ fallback without scope, missing- key-no-leak, allowlist scoping, and check_email_requirements scoping. Related: NousResearch#50051, NousResearch#52307, PR NousResearch#50094, PR NousResearch#51374
prmartinow
pushed a commit
to prmartinow/hermes-agent
that referenced
this pull request
Aug 26, 2026
The email adapter (plugins/platforms/email/adapter.py) read EMAIL_ADDRESS, EMAIL_PASSWORD, EMAIL_IMAP_HOST, EMAIL_SMTP_HOST, EMAIL_ALLOWED_USERS, and EMAIL_ALLOW_ALL_USERS via os.getenv() directly. In a multiplexed gateway, os.environ holds the default profile's .env values, so every secondary profile inherited the default profile's email credentials instead of its own. This was a sibling of the api_server env-leak bug (NousResearch#52307/NousResearch#50051): the same os.getenv→get_secret migration that PR NousResearch#50094 applies to gateway/config.py, but for the email adapter itself, which neither PR NousResearch#50094 nor NousResearch#51374 covers. Changes: - plugins/platforms/email/adapter.py: replace os.getenv with agent.secret_scope.get_secret for all EMAIL_* credential reads (adapter __init__, check_email_requirements, _allowlist_in_effect, _dispatch_message allowlist gate, _send_email SMTP helper). - gateway/config.py: add _getenv/_getenv_str/_getenv_int helpers (from PR NousResearch#50094) and replace os.getenv with _getenv for the email block in _apply_env_overrides, so config.platforms[EMAIL].extra is populated from the scoped value. - tests/gateway/test_email_secret_scope.py: 5 new tests covering scoped credential reads, environ fallback without scope, missing- key-no-leak, allowlist scoping, and check_email_requirements scoping. Related: NousResearch#50051, NousResearch#52307, PR NousResearch#50094, PR NousResearch#51374
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
The email adapter (plugins/platforms/email/adapter.py) read EMAIL_ADDRESS, EMAIL_PASSWORD, EMAIL_IMAP_HOST, EMAIL_SMTP_HOST, EMAIL_ALLOWED_USERS, and EMAIL_ALLOW_ALL_USERS via os.getenv() directly. In a multiplexed gateway, os.environ holds the default profile's .env values, so every secondary profile inherited the default profile's email credentials instead of its own. This was a sibling of the api_server env-leak bug (NousResearch#52307/NousResearch#50051): the same os.getenv→get_secret migration that PR NousResearch#50094 applies to gateway/config.py, but for the email adapter itself, which neither PR NousResearch#50094 nor NousResearch#51374 covers. Changes: - plugins/platforms/email/adapter.py: replace os.getenv with agent.secret_scope.get_secret for all EMAIL_* credential reads (adapter __init__, check_email_requirements, _allowlist_in_effect, _dispatch_message allowlist gate, _send_email SMTP helper). - gateway/config.py: add _getenv/_getenv_str/_getenv_int helpers (from PR NousResearch#50094) and replace os.getenv with _getenv for the email block in _apply_env_overrides, so config.platforms[EMAIL].extra is populated from the scoped value. - tests/gateway/test_email_secret_scope.py: 5 new tests covering scoped credential reads, environ fallback without scope, missing- key-no-leak, allowlist scoping, and check_email_requirements scoping. Related: NousResearch#50051, NousResearch#52307, PR NousResearch#50094, PR NousResearch#51374
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Honor an explicit
platforms.api_server.enabled: falseunder the presence of an API-server env key.The contract: when
config.yamlexplicitly pinsplatforms.api_server.enabled: false(recorded via the_enabled_explicitmarker inPlatformConfig.extra), that explicit disable takes precedence over the mere presence ofAPI_SERVER_ENABLED/API_SERVER_KEYin the same scope. Today_apply_env_overrides()unconditionally re-enablesapi_serverwhenever either env var is present, silently overriding the explicit disable.This aligns
api_serverwith the already-migrated plugin platforms (telegram / matrix / slack), which already consult_enabled_explicit(via_enable_from_env()) before re-enabling. Theapi_serverblock re-enables inline and was the one platform that did not honor the marker.Related Issue
Fixes #
Type of Change
Root cause
gateway/config.py—_apply_env_overrides(), the# API Serverblock:The presence of
API_SERVER_KEY/API_SERVER_ENABLEDin the environment force-enables the platform even when the same scope'sconfig.yamlexplicitly setenabled: false. The migrated plugin platforms read_enabled_explicitbefore re-enabling;api_serverdid not.Changes Made
gateway/config.py: guard theapi_server.enabled = Trueassignment on the explicit-disable marker. Useextra.pop("_enabled_explicit", False): theapi_serverbranch is terminal (unlike the migrated plugin platforms, no later registry pass re-enablesapi_server), so popping consumes the flag in a single read and avoids a double-read; the per-platform_enabled_explicitcleanup at the end of_apply_env_overridesremains a no-op forapi_server. The normal env-enable path (no explicit disable) is unchanged.tests/gateway/test_config.py: retain the focused regression testTestApiServerEnvOverride::test_env_key_does_not_reenable_explicitly_disabled_api_server.Scope: same-scope precedence, not cross-profile multiplexing
This PR is about the same-scope explicit-disable precedence contract only. It is deliberately orthogonal to the cross-profile (multiplex) contract already covered on
main:mainin0f154e780e....tests/gateway/test_config.pyonmainalready verifies that a default-profile API-server env setting does not enable a secondary profile.That orthogonal contract answers "does a default-profile env var leak into another profile's scope?" — this PR answers a different question: "within one scope, does an explicit
enabled: falseinconfig.yamlsurvive the presence of an env key in that same scope?" It does not, today; this PR makes it so. No cross-profile / multiplexing behavior is added or changed here.How to test
With the fix the test passes; removing the
_enabled_explicitguard makes it fail (theenabledflag flips back toTrueunderAPI_SERVER_KEY). Full config regression:pytest tests/gateway/test_config.py -q(100 passed) andpytest tests/gateway/test_multiplex_phase0.py -q(28 passed).🤖 Generated with Claude Code