feat(auth): HERMES_AUTH_FILE env var to share auth.json across profiles - #20405
Closed
AShahver wants to merge 1 commit into
Closed
feat(auth): HERMES_AUTH_FILE env var to share auth.json across profiles#20405AShahver wants to merge 1 commit into
AShahver wants to merge 1 commit into
Conversation
Allows multiple Hermes profiles (or processes on different hosts sharing a network mount) to point at one auth.json by setting HERMES_AUTH_FILE. This is required when several profiles share a single OAuth account: refresh tokens are single-use and rotated on every successful refresh, so without a shared file the first profile to refresh invalidates the others' tokens. With a shared path the existing fcntl flock in `_auth_store_lock` (keyed off `<auth_file>.lock`) automatically serializes refreshes across all consumers — no new locking primitive needed. Use case: an 8-profile Hermes deployment on one host, all using the same ChatGPT account. Pre-fix, 7 of 8 profiles would silently get stuck on the dead refresh token and start failing with "Codex refresh token was already consumed by another client" once their access tokens expired. `tests/conftest.py` adds HERMES_AUTH_FILE to the behavioral-vars blanking list so production env doesn't leak into tests. 7 new unit tests in `tests/hermes_cli/test_auth_file_path.py` cover: - default fall-through to HERMES_HOME/auth.json - override honors path verbatim - ~ expansion in the override - whitespace-only override falls through (treats blank as unset) - _auth_lock_path follows the override (the property that makes flock work) - two distinct HERMES_HOME values + one HERMES_AUTH_FILE share state - two profiles compute the same lock path
This was referenced May 20, 2026
Contributor
|
Thanks for the focused reproduction and for tracing the existing lock behavior. This is an automated hermes-sweeper review.
Closing this PR under that standing configuration policy. Closed as not-planned per standing maintainer policy ( |
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When several Hermes profiles share a single OAuth account (very common with ChatGPT-account-backed Codex auth), the per-profile
~/.hermes/profiles/<name>/auth.jsonfiles all start out with the same refresh token. Refresh tokens are single-use and rotated on every successful refresh, so the first profile to refresh invalidates the others' tokens. The other 7-of-8 profiles then silently keep the dead token and start failing with:…once their access tokens expire ~10 days later. There's no recovery short of running the interactive
hermes authdevice-code flow on each broken profile — which then immediately re-races on the next refresh.Fix
Add a
HERMES_AUTH_FILEenv var that overrides_auth_file_path(). Set it on every profile's gateway plist + login shell, point all profiles at one shared path (e.g.~/.hermes/shared/auth.json), and the existing_auth_store_lock— which is keyed off<auth_file>.lock— automatically serializes refreshes across all consumers because they all now compute the same lock path.No new locking primitive, no fan-out daemon, no token-copy cron. The flock that's already there does the right thing once the paths align.
Diff shape
hermes_cli/auth.py— 9-line addition at the top of_auth_file_path(). Falls through to existing per-HERMES_HOMEbehavior if the env var is unset/blank.tests/conftest.py— addsHERMES_AUTH_FILEto_HERMES_BEHAVIORAL_VARSso production env doesn't leak into the test suite.tests/hermes_cli/test_auth_file_path.py— 7 new unit tests covering: default fall-through, env override,~expansion, blank override, lock-path follows override, cross-profile state sharing, and identical lock paths from two profiles.Verification
Verified end-to-end on an 8-profile production fleet: applied locally, all profiles now share
~/.hermes/shared/auth.json, force-refresh from one profile rotated the token, all other profiles immediately observed the rotation, and a Codex cron that had been failing with the consumed-token error for 5 days completed successfully on the next tick.Compatibility
Pure addition. Default behavior (env var unset) is unchanged —
get_hermes_home() / "auth.json"still resolves exactly the same way. No migration needed.