Skip to content

fix(dashboard): isolate the environment of named-profile actions - #107434

Closed
BowmanStephen wants to merge 1 commit into
NousResearch:mainfrom
BowmanStephen:pr/dashboard-profile-action-env
Closed

BowmanStephen wants to merge 1 commit into
NousResearch:mainfrom
BowmanStephen:pr/dashboard-profile-action-env

Conversation

@BowmanStephen

Copy link
Copy Markdown

Summary

_spawn_hermes_action copies the dashboard's os.environ verbatim into every detached hermes ... action. The dashboard runs inside the gateway and has loaded its own profile's .env into the process environment, so hermes -p <other> gateway restart (and every other dashboard-driven profile action) starts with the default profile's platform credentials and ports already present. load_hermes_dotenv does not override keys that are already set, so the named profile's own .env cannot displace them. Observed result: an A2A-only profile claimed the default Discord bot token and bound the default API server / BlueBubbles ports.

Change

For actions carrying a leading profile selector (-p X, --profile X, --profile=X):

  • start from build_subprocess_env(base=os.environ, scrub_secrets=True);
  • drop _PROFILE_MANAGED_ENV_KEYS plus every key defined by the dashboard / default profile .env files and their hydrated secret sources (get_secret_source_values);
  • pin HERMES_HOME to the target profile (_resolve_profile_dir, so the same validation applies) and run apply_subprocess_home_env; the child's normal startup then loads that profile's .env.

Only the leading selector is inspected: argv after the subcommand may legitimately contain -p for a nested process (mcp add --args ...). Actions without a selector keep the historical environment byte-for-byte. _HERMES_GATEWAY is still dropped (#52470).

Tests

test_named_profile_action_isolates_parent_env_and_loads_target_env asserts the leaked keys are gone, benign keys survive, HERMES_HOME is pinned, and (by running the real dotenv loader in a fresh interpreter with the captured env) that the target profile's values load without reviving any default-profile value. The existing loop-guard test additionally checks a default-profile action still inherits the process env.

tests/hermes_cli/test_dashboard_admin_endpoints.py: 43 passed.

🤖 Generated with Claude Code

_spawn_hermes_action copied the dashboard's os.environ verbatim into every
detached `hermes ...` action. The dashboard runs inside the gateway and has
loaded its own profile's .env into the process environment, so
`hermes -p <other> gateway restart` (and every other dashboard-driven
profile action) started with the DEFAULT profile's platform credentials
and ports already present. load_hermes_dotenv does not override keys that
are already set, so the named profile's own .env could not displace them:
an A2A-only profile ended up claiming the default Discord bot token and
binding the default API server / BlueBubbles ports.

For actions carrying a profile selector (`-p X`, `--profile X`,
`--profile=X`), build the child env from the standard scrubbed subprocess
environment, drop _PROFILE_MANAGED_ENV_KEYS plus every key defined by the
dashboard/default profile's .env and its hydrated secret sources, and pin
HERMES_HOME to the target profile so the child's normal startup loads that
profile's .env. Only the leading selector is inspected: argv after the
subcommand may legitimately contain -p for a nested process. Actions
without a selector keep the historical environment byte-for-byte.

Test: the new case asserts the leaked keys are gone, benign keys survive,
and (by running the real dotenv loader in a fresh interpreter with the
captured env) that the target profile's values load without reviving any
default-profile value.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/cli CLI entry point, hermes_cli/, setup wizard area/profiles Multi-profile isolation, HERMES_HOME scoping area/config Config system, migrations, profiles labels Sep 10, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Status note: #108440 (53e32d0) landed the in-process side of this bug class (YAML→env bridges, TERMINAL_* latch, write-guard memo, env_passthrough allowlist) but deliberately did not fold this PR in — the child-process env for dashboard hermes -p <profile> actions in hermes_cli/web_server_gateway.py is a different sink. This stays open for its own review.

@teknium1

Copy link
Copy Markdown
Collaborator

Landed on main in #108676. Cherry-picked with authorship preserved as 4d47891 — named-profile dashboard actions (-p X / --profile X) now get the scrubbed subprocess env with _PROFILE_MANAGED_ENV_KEYS and every key the dashboard profile's .env defines dropped, and HERMES_HOME pinned to the target, so the child loads only its own .env. Live repro on the merged tree: DISCORD_BOT_TOKEN absent from the -p alpha child env and HERMES_HOME pointing at profiles/alpha. Thanks, @BowmanStephen.

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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants