Your Shared Gateway Shouldn't Serve Every Profile: multiplex_profile_allowlist in Hermes


You run one Discord gateway on a shared server, and the machine has five Hermes profiles installed: a general-purpose default, a restricted profile for the kids, two developer profiles. Start the gateway and it boots every profile’s adapters and cron jobs at once. Worse: a message that was supposed to route to the restricted profile arrives while that profile is unavailable — and the gateway quietly runs it under default instead. For a restricted profile that is not graceful degradation; that is crossing the capability boundary. PR #83400, merged on August 11, 2026, closes exactly this gap: gateway.multiplex_profile_allowlist lets you declare which profiles your shared gateway serves, and routing that misses the served set now fails closed — the message is rejected, never silently downgraded.

One config key to define the served set

Add the allowlist to config.yaml:

gateway:
  multiplex_profiles: true
  multiplex_profile_allowlist:
    - worker
    - guest

With those four lines, this gateway only starts and serves default, worker, and guest — every other profile installed on the host is neither started nor exposed through this gateway.

The exact semantics of the allowlist

The rules (official multi-profile-gateways.md docs) are worth going through one by one, because the edge cases are all handled:

  • default is always served — you never need to list it;
  • Unset = historical behavior: serve every valid named profile on the host;
  • Empty list [] = serve only default;
  • Names are normalized and deduplicated; invalid entries or profiles that aren’t installed are skipped with a warning; a malformed non-list value fails safe to default-only, not to a crash;
  • The served set also controls /p/<profile>/ API and webhook prefixes, runtime status, profile_routes eligibility, and which profiles the in-process cron scheduler ticks;
  • A named profile outside the allowlist is not started by this gateway — but it can still run its own standalone gateway. “Not served by the shared gateway” does not mean “unusable”.

Fail closed: reject, don’t silently downgrade

This is the security-relevant part of the change. Before, the risk was: profile_routes explicitly routed a channel to a restricted profile, but if that profile was unavailable (not installed, broken, outside the served set), traffic would silently fall back to default. The rule is now inverted:

  • An explicit route matches but its target profile is not installed or outside the allowlist → the gateway rejects the message and logs the route and target profile — it never runs default for it;
  • Traffic that matches no route keeps the historical behavior (goes to default);
  • profile_routes only applies when multiplex_profiles: true.

Real-world scenario: a restricted profile on a shared gateway

The classic use case is a family-shared bot: one Discord bot where regular channels run on default and the kids’ channel runs on a restricted profile. Set it up in two layers:

  1. Gateway layer: multiplex_profile_allowlist lists only default + kids-safe, and profile_routes sends the kids’ channel to kids-safe;
  2. Profile layer: hardening lives in the restricted profile’s own config.yaml — enabling only the toolset it needs, not inheriting default’s credentials, and so on. Profiles are isolated config homes, so that isolation is done at this layer anyway.

One boundary to state plainly: the official docs remind us that profile routing plus an allowlist is not a complete security sandbox. When you need an operating-system-level hard boundary (say, code that must run in an isolated environment), use separate processes or container isolation — don’t expect config-level routing to backstop it.

The takeaway

multiplex_profile_allowlist is a small change that solves a real shared-gateway pain: you have profiles installed for different purposes, but the gateway used to serve them all indiscriminately — and degrade silently when a route missed. Now you can declare exactly who this gateway serves, and turn that degradation into a rejection. For the full profile multi-instance concept, see our multi-profile guide; profile routing in a Feishu setup is covered in that article; the command surfaces live in the hermes-gateway and hermes-profile reference pages.