37dcc0a6e8
Under gateway.multiplex_profiles the default gateway serves every profile, yet four startup/status paths still reasoned from the wrong source: * A secondary profile's API_SERVER_KEY (which the docs REQUIRE for /p/<profile>/ auth) auto-enabled api_server in that profile's config, so _load_secondary_profile_config raised SecondaryPortBindingConfigError and the whole profile was skipped. gateway/config_env.py::_enable_from_env now leaves `enabled` alone for port-binding platforms while a multiplexer loads a NON-default profile (home override + multiplex flag, the same signal gateway.config uses for scoped reads); the credential still lands in extra so the shared listener can authenticate the prefix. Default profile unchanged. * "Is this profile served?" was re-derived from the default config.yaml plus GATEWAY_MULTIPLEX_PROFILES as seen by the CLI process. `hermes -p coder ...` loads coder's .env, so an env-only opt-in on the default profile was invisible (guard never fired, status said stopped) and an allowlist edit flipped the answer before the restart. named_profile_served_by_running_multiplexer now reads the pid-verified default gateway_state.json served_profiles (written by _record_served_profiles) first and falls back to config derivation only when the key is absent. The record helpers live in hermes_cli/gateway_multiplex_served.py. * The served-profile guard ran only inside `gateway run`. `hermes -p X gateway start|install|restart` reached the service manager, whose unit then exited 78 forever (systemd parks it while the CLI prints "started"; launchd KeepAlive respawns every 30 s). The service verbs now run the same guard up front (exit 78, same message) and accept --force; the Desktop /api/gateway/start route returns 409 for a served profile instead of spawning a doomed child. * Status surfaces disagreed: `hermes -p coder status` said stopped, `hermes -p coder cron status` said "cron jobs will NOT fire" while `cron list` said fine, and the default `hermes status` never listed served profiles. Both now route through the probe / the recorded served set. The -p/--profile matcher in _scan_gateway_pids and gateway.status._command_line_belongs_to_profile compares the flag token for equality (`-p ops` no longer claims -- or lets `gateway stop` SIGTERM -- an `-p ops-2` gateway). Docs: multi-profile-gateways.md now describes the start/install refusal, the --force flags, the API_SERVER_KEY behaviour and the single default-home gateway_state.json (the per-profile runtime_status.json claim was wrong). Fixes #100397 Addresses #89726 #97360 #71344 (cherry picked from commit d002c1864a7b6a22c53758b16b7b0cc79aea2edf)