Skip to content

feat(credential-pool): opt-in preemptive key rotation + footer credential field - #57256

Closed
caiolea0 wants to merge 1 commit into
NousResearch:mainfrom
caiolea0:feat/preemptive-credential-rotation
Closed

feat(credential-pool): opt-in preemptive key rotation + footer credential field#57256
caiolea0 wants to merge 1 commit into
NousResearch:mainfrom
caiolea0:feat/preemptive-credential-rotation

Conversation

@caiolea0

@caiolea0 caiolea0 commented Jul 2, 2026

Copy link
Copy Markdown

feat(credential-pool): opt-in preemptive key rotation + footer credential field

Problem

The credential pool rotates reactively onlypool.select() is consulted
inside the 429/401 error-recovery path (recover_with_credential_pool) and on
session restore, never per-request in the mainline loop. With round_robin /
least_used configured, a single long-running session keeps sending every
request on the same key until it is rate-limited, then rotates. Operators with
a pool of N keys expect the load to spread across all N proactively.

Long-standing, still-open request: #22212, #22407, #22916.

Changes

1. Preemptive rotation (HERMES_PREEMPTIVE_KEY_ROTATION, default off)
run_conversation calls a new helper at the top of every loop iteration:
agent_runtime_helpers.preemptive_rotate_credential(agent) advances the pool
cursor via the existing pool.select() and swaps the credential in with the
existing agent._swap_credential(entry) — the same primitives the restore path
already uses. No new rotation logic.

2. Footer credential field
gateway/runtime_footer.py gains an optional credential field; gateway/run.py
resolves the active pooled credential label (from the cached/running agent's
pool) and passes it in. Lets operators see which pooled key served each turn:

display:
  runtime_footer:
    fields: [model, credential, context_pct, cwd]

Safety / scope

  • Inert by default. Flag unset → helper returns immediately; footer field
    only renders when added to fields. Zero behavior change for existing setups.
  • Narrow trigger. Rotation only fires for round_robin / least_used
    api-key pools with an available entry. OAuth, single-key, and fill_first
    providers are never touched.
  • Never breaks a turn. Both the rotation helper and the footer lookup are
    fully wrapped; any exception is swallowed (debug-logged).
  • Reuses pool.select() + agent._swap_credential() — no changes to
    credential_pool.py semantics.

Testing

  • round_robin pool.select() returns a distinct entry per call across a
    20-key pool (20/20 distinct over one cycle, then wraps).
  • Verified end-to-end on a live Telegram gateway: consecutive turns rotated
    across distinct keys, surfaced in the footer (🔑 api-key-9api-key-17
    NIM Key 3).
  • Gateway and headless backend (hermes serve) boot cleanly with the flag on;
    flag off is a verified no-op.
  • py_compile clean on all four touched files.

Notes

Env-flag chosen over a config key to keep the surface minimal and the default
untouched; happy to move rotation behind credential_pool_strategies / a config
option if maintainers prefer that shape.

…tial field

Two related changes for pooled-credential load-spreading and visibility.

1. Preemptive rotation (env HERMES_PREEMPTIVE_KEY_ROTATION, default off):
   run_conversation advances the round_robin/least_used credential-pool
   cursor before each API call, spreading load across all pooled keys
   instead of only rotating reactively after a 429/401. A single long
   session no longer keeps hitting one key until it is rate-limited.
   Inert by default; only fires for api-key pools using round_robin or
   least_used with an available entry. OAuth, single-key, and fill_first
   providers are never touched. Errors are swallowed so a rotation hiccup
   can never break a turn.

2. Runtime footer: a new optional `credential` field renders the active
   pooled credential label, e.g.
     display:
       runtime_footer:
         fields: [model, credential, context_pct, cwd]
   so operators can see which pooled key served each turn. Skipped
   silently when no pool/credential is present, exactly like the other
   footer fields.

Addresses the standing request for proactive load-spreading across a key
pool (NousResearch#22212, NousResearch#22407, NousResearch#22916).
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have labels Jul 2, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused implementation and for reusing the existing pool-selection primitives.

This automated hermes-sweeper review is closing the PR because its user-facing opt-in is a new non-secret HERMES_PREEMPTIVE_KEY_ROTATION flag. Hermes policy requires behavioral settings and feature flags to live in config.yaml, not a new HERMES_* environment variable (AGENTS.md:102-107).

  • The PR body explicitly selects the env flag rather than a config key, and the helper gates all behavior on that flag (agent/agent_runtime_helpers.py, PR commit 3cf27516ee82).
  • Credential-pool strategy settings already have a configuration surface at credential_pool_strategies (website/docs/user-guide/configuration.md:895-905).
  • The linked retry-rotation issue platform retry: rotate auth profiles within a single retry sequence before failing the request #22212 was independently closed as implemented on main; this PR's distinct proactive-rotation idea would need a narrowly scoped config.yaml design if reconsidered.

A focused re-scope using an explicit config.yaml mechanism, with cache-cost documentation and tests for per-API-call behavior, would be the appropriate alternative.


Closed as not-planned per standing maintainer policy (env-var-for-config). This is a design-direction decision, not a code-quality judgment — see the Contribution Rubric in AGENTS.md for what the project is looking for. If you believe this policy was misapplied to your change, comment here and a maintainer will take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants