Skip to content

fix(api_server): run the cron-fire token verifier off the event loop (salvage #65730) - #76972

Merged
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:salvage-65730-cronfire-offload
Aug 2, 2026
Merged

kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:salvage-65730-cronfire-offload

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Contributor

Salvages #65730 by @Frowtek — commit cherry-picked to preserve authorship, conflict resolved (test-file-only).

Context — what this fixes, for whom

Anyone using Chronos-scheduled cron jobs: _handle_cron_fire is the inbound webhook that triggers remote job execution, and it verifies the NAS-minted JWT inline on the event loop. The verifier resolves signing keys from a JWKS URL — a synchronous HTTP GET on cache miss (cold process or key rotation). A slow or rate-limited portal stalls every other adapter sharing the loop. Worse, on current main a crashing verifier (network error, parse error) propagates as an unhandled exception → 500, on the one endpoint that can trigger remote code execution.

What the fix does (from #65730, kept verbatim)

  1. Sync verifiers run via asyncio.to_thread (mirrors the platform-event verifier hardening in 14f023c); coroutine verifiers are awaited directly.
  2. Fail-closed: any verifier crash logs via logger.exception and rejects the fire with 401 (instead of today's 500) — the correct posture for an RCE-trigger endpoint.
  3. Three tests: thread-identity proof of offload, crash→401, async-verifier await.

Conflict resolution (ours, content-free)

Cherry-pick anchor drift only: main added test_fire_does_not_require_api_server_key at the same insertion point. Both sides kept; zero production-code conflict. 6/6 tests pass.

Verification

  • tests/gateway/test_cron_fire_webhook.py: 6 passed (2 pre-existing + PR's 3 + main's 1)
  • Premise re-verified on current main @ 9b50a99: api_server.py:5618 still calls the verifier inline, sync, no try/except; chronos/verify.py confirms JWKS HTTP on cache miss
  • Mutation check: revert api_server.py to main → offload + fail-closed tests fail; restore → 6 pass
  • Seam sweep: existing tests patch get_fire_verifier at module level with plain lambdas — iscoroutinefunction routes them to to_thread, behavior preserved (verified by the 2 pre-existing tests passing)
  • ruff clean

Closes #65730 (superseded by this salvage — original author credited via cherry-pick authorship).

_handle_cron_fire verified the NAS-minted fire JWT by calling the
fire-verifier inline on the event loop. That verifier resolves the NAS
signing key from a JWKS URL — a synchronous HTTP GET on a cache miss (a
cold PyJWKClient, or a rotated kid the cached client doesn't know) — so a
slow or rate-limited portal stalls the whole event loop and starves every
other adapter sharing it. NousResearch#64641 already documented this exact symptom
(relay 504s on high-job-count instances) and cut the fetch frequency by
caching the client per URL, but the residual cache-miss fetch still ran
inline on the loop.

Dispatch the verifier the same way the platform HTTP event verifier was
hardened: await a coroutine verifier directly, run a sync one via
asyncio.to_thread so its blocking I/O stays off the loop, and fail closed
(reject with 401, never admit the fire) if the verifier raises — this is
the only inbound that can trigger remote job execution. The verifier's
JWK-client cache is already thread-safe (threading.Lock), so moving the
call to a worker thread is safe.

Adds regression tests: a sync verifier runs on a worker thread rather
than the loop thread, a crashing verifier yields 401 with no fire, and a
coroutine verifier is awaited.
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 2, 2026 17:38
@kshitijk4poor
kshitijk4poor merged commit af077ef into NousResearch:main Aug 2, 2026
35 checks passed
@alt-glitch alt-glitch added type/perf Performance improvement or optimization P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery comp/cron Cron scheduler and job management sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 2, 2026
@kshitijk4poor
kshitijk4poor deleted the salvage-65730-cronfire-offload branch August 5, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants