Skip to content

gateway status recommends 'gateway start', which silently discards operator customisations in the launchd plist #82046

Description

@Kaizen-Michael

Summary

hermes gateway status detects a stale launchd service definition and advises running
hermes gateway start. Following that advice silently discards every operator
customisation in the installed plist
— no warning, no diff, no backup. The tool
recommends the action that destroys the configuration.

Observed on v0.20.0 (2026.8.3), macOS 26.5.2.

What gets dropped

hermes_cli/gateway.py builds the plist from a hardcoded f-string template. It emits
exactly three environment variables and no resource limits:

<key>EnvironmentVariables</key>
<dict>
    <key>PATH</key>        <string>{sane_path}</string>
    <key>VIRTUAL_ENV</key> <string>{venv_dir}</string>
    <key>HERMES_HOME</key> <string>{hermes_home}</string>
</dict>

There is no SoftResourceLimits / HardResourceLimits emission anywhere in the file, and
the env dict has no merge step. So regeneration drops:

  • any *ResourceLimits — e.g. a raised NumberOfFiles, added because the gateway
    exhausts the default descriptor limit under load
  • any additional EnvironmentVariables — e.g. a daemon idle-timeout tuning

On our host both were deliberate prior fixes with dated backups. Regenerating would have
reverted a limit raise and a memory-daemon setting that had been pinned an hour earlier
— while the tool reported it as routine maintenance.

Why it is worse than a plain overwrite

  1. The tool recommends it. status surfaces the stale-definition warning and names
    start as the remedy, so the destructive path is the advised one.
  2. It is silent. No diff of installed-vs-generated, no "the following keys will be
    dropped", no backup of the existing plist.
  3. The loss is invisible afterwards. A regenerated plist is perfectly well-formed. The
    only way to notice is to already know what used to be there. Resource-limit regressions
    in particular resurface later as unrelated-looking failures under load.
  4. restart is safe and start is not, which is not discoverable from the help text.
    refresh_launchd_plist_if_needed is called only from start and install.

Reproduction

  1. Add any customisation to the installed plist — e.g.
    SoftResourceLimits/NumberOfFiles = 4096, or an extra EnvironmentVariables entry.
  2. Cause the service definition to be considered stale (e.g. upgrade).
  3. hermes gateway status → reports the definition as stale, advises hermes gateway start.
  4. Run it. The plist is regenerated; the customisation is gone, unmentioned.

Suggested fixes, cheapest first

  1. Back up before regenerating. Write the existing plist to a timestamped sibling. One
    line, and it makes the loss recoverable rather than terminal.
  2. Diff and confirm. Before overwriting, compare installed vs generated and print the
    keys about to be dropped; require confirmation (or --force) when the set is non-empty.
  3. Merge rather than replace. Preserve unrecognised top-level keys and unrecognised
    EnvironmentVariables entries from the installed plist. This is the real fix: it makes
    the operation idempotent for operators who have tuned anything.
  4. Correct the recommendation. Where status currently suggests start, suggest
    restart when the gateway is already running — it refreshes the process without
    touching the plist.

(1) and (4) alone would remove the trap. (3) is what makes start safe to recommend at all.

Note

Happy to send a PR for the backup-plus-diff variant if that shape is acceptable — it is
small and does not change the default behaviour beyond adding the safety net.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/configConfig system, migrations, profilescomp/cliCLI entry point, hermes_cli/, setup wizardcomp/gatewayGateway runner, session dispatch, deliverysweeper:risk-compatibilitySweeper risk: may break existing users, config, migrations, defaults, or upgradestype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions