fix(simplex): scope multiplex secondary-profile config, not shared env - #100241
fix(simplex): scope multiplex secondary-profile config, not shared env#100241nftpoetrist wants to merge 1 commit into
Conversation
The SimpleX adapter's __init__ (auto_accept, group allowlist), check_requirements, validate_config, is_connected, _env_enablement, and _standalone_send all read SIMPLEX_* settings via raw os.getenv unconditionally. Under a multiplexed gateway, a secondary profile's adapter construction and config-load hooks run inside a profile-scoped context where os.environ still holds the DEFAULT profile's YAML-to-env bridge output — so a secondary profile with its own (different or absent) SimpleX config silently inherits the default profile's daemon URL, group allowlist, or auto-accept setting instead of its own. Mirrors the established Buzz adapter fix for the same bug class (NousResearch#98738): add _profile_scoped()/_scoped_platform_setting() so a secondary profile's own PlatformConfig.extra is authoritative and env is not consulted, add _profile_simplex_extra() so check_requirements() (which has no PlatformConfig argument) consults the profile's own config.yaml instead of the process env, and make _env_enablement() return None under a secondary scope so it does not fabricate a SimpleX platform from another profile's env. Adds a TestMultiplexProfileScope test class mirroring the Buzz adapter's existing coverage for this exact scenario.
This PR makes the SimpleX adapter multiplex-profile-aware, mirroring the Buzz fix (#98738): under a secondary-profile secret scope, Test coverage is excellent: the multiplex fixtures keep the suite hermetic, and the standalone-send test correctly manages the Non-blocking: the scoped group-allowlist read assumes Verdict: LGTM |
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in #100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of #100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
|
Thanks @nftpoetrist for this PR — Merged via #101252 (2e25b47) on current main. This one was rewritten onto the shared Closing this PR as superseded by the merged work. |
…plexing SimplexAdapter.__init__ (auto_accept, group_allowed), the registry gates check_requirements/validate_config/is_connected, _env_enablement and _standalone_send all read SIMPLEX_* via raw os.getenv. Under gateway.multiplex_profiles those paths run inside a secondary profile's scope where os.environ holds the DEFAULT profile's YAML-to-env bridge output -- so a secondary profile that never configured SimpleX was auto-enabled on the default's daemon URL and inherited its group allowlist / auto-accept setting. Route every read through the module-local `_get_scoped_secret` wrapper (get_secret; UnscopedSecretError -> os.getenv for the default profile, which constructs unscoped) -- the same helper the IRC/ntfy/Photon/ Mattermost siblings use. Unlike the extra-only `_scoped_platform_setting` shape proposed in NousResearch#100241, this honors BOTH the secondary profile's own .env (the scope) and its config.yaml extra, and needs no config.yaml re-read in check_requirements. Rewrite of NousResearch#100241. Co-authored-by: nftpoetrist <264138787+nftpoetrist@users.noreply.github.com>
What & why
The SimpleX adapter's
__init__(auto_accept, group allowlist),check_requirements,validate_config,is_connected,_env_enablement, and_standalone_sendall readSIMPLEX_*settings via rawos.getenvunconditionally.Under a multiplexed gateway, a secondary profile's adapter construction and config-load hooks run inside a profile-scoped context (
_profile_runtime_scope) whereos.environstill holds the default profile's YAML-to-env bridge output. A secondary profile with its own (different, or absent) SimpleX configuration silently inherits the default profile's:SIMPLEX_WS_URL) — cron/mid-turn delivery via_standalone_sendconnects to the wrong daemonSIMPLEX_GROUP_ALLOWED) — a secondary profile's own, possibly tighter, allowlist gets silently widened to the default profile's (e.g. the default's*wildcard)SIMPLEX_AUTO_ACCEPT)check_requirements/_env_enablementalso gate/seed platform enablement off the default profile's env duringload_gateway_config(), which itself runs inside the secondary profile's scope.Note:
SIMPLEX_ALLOWED_USERS/SIMPLEX_ALLOW_ALL_USERS(the DM allowlist) are unaffected — those already route through the generic, already-scopedgateway/authz_mixin.py::_auth_env()mechanism viaregister()'sallowed_users_env/allow_all_env.Fix
Mirrors the established Buzz adapter fix for the same bug class (#98738):
_profile_scoped()/_scoped_platform_setting()— a secondary profile's ownPlatformConfig.extrabecomes authoritative and env is not consulted; a missing key fails closed to the safe default instead of borrowing the default profile's value. Single-profile gateways and the default profile under multiplexing keep the legacyos.getenvprecedence unchanged._profile_simplex_extra()—check_requirements()has noPlatformConfigargument, so (like Buzz's_profile_buzz_extra()) it reads the profile's ownconfig.yamldirectly via the scoped home override when running inside a secondary profile's scope._env_enablement()returnsNoneunder a secondary profile's scope, so it does not fabricate a SimpleX platform for a profile that never configured one from the default profile's env.Tests
Added
TestMultiplexProfileScopetotests/gateway/test_simplex_plugin.py(7 tests), mirroring the existing Buzz adapter coverage for this exact scenario: secondary profile's extra wins over default's env, missing keys fail closed to safe defaults (not the default's wildcard/disabled values), the default profile stays unscoped and keeps env precedence,check_requirementsconsults the profile's ownconfig.yaml,validate_config/is_connectedrespect scope,_env_enablementreturnsNonewhen scoped, and_standalone_sendconnects to the scoped daemon URL rather than the default's.tests/gateway/test_simplex_plugin.py— 23 passed (16 pre-existing + 7 new)tests/hermes_cli/test_send_cmd.py,tests/gateway/test_channel_directory.py,tests/gateway/test_unauthorized_dm_behavior.py— 45 passedScope note / prior art check
Three open PRs currently touch
plugins/platforms/simplex/adapter.py+tests/gateway/test_simplex_plugin.py— checked each diff directly and confirmed zero function-level overlap with this fix (textual proximity only, same files, different functions/concerns):role_authorizedto the group-message dispatch in_handle_chat_item(gateway authorization signal), unrelated to env/profile scopinglocalDisplayNamein_handle_new_chat_item(allowlist spoofing via display-name), unrelatedconnect()(duplicate-listener prevention), unrelatedMerged PR #65629 (multiplex credential isolation cluster) does not touch this file — confirmed via
gh pr view --json files.