hermes-agent/hermes_cli/dashboard_auth
Ben c34840e22e fix(cron): serve /api/cron/fire on the dashboard app (hosted-agent surface)
Live-test finding: the Chronos fire webhook was only on the APIServerAdapter
(aiohttp), but hosted agents expose `hermes dashboard` (the FastAPI web_server
app on :9119) as their public URL — NOT the api_server adapter. So NAS's relay
callback to {callback_url}/api/cron/fire could never reach the verifier on a
hosted agent (the exact target environment). Two layers were wrong:

1. Wrong server: /api/cron/fire didn't exist on the dashboard app. Added
   cron_fire_webhook there, alongside the existing /api/cron/* dashboard routes.
   It resolves the job's profile (_find_cron_job_profile) and runs fire_due via
   the resolved provider under the cron-profile retarget lock
   (_fire_cron_job_for_profile, mirroring _call_cron_for_profile) so the CAS
   claim + run_one_job operate on the right profile's jobs.json. Runs with no
   live adapters (delivery falls back to the per-platform send path, like the
   desktop cron path). 202 + background so a long turn never trips NAS's
   timeout; the store CAS de-dupes a NAS retry. job-not-found -> 200 "gone".

2. Auth gate: the dashboard auth middleware 401s any non-cookie request before
   the handler runs. Added /api/cron/fire to the shared PUBLIC_API_PATHS so the
   NAS bearer-JWT callback reaches the verifier — the JWT (purpose=cron_fire),
   not the cookie, is the real gate. One shared frozenset feeds both the
   loopback and OAuth middlewares, so no drift.

Kept the APIServerAdapter route too (valid self-host api_server surface).
Contract doc updated to name the dashboard app as the hosted-agent callback
surface.

Tests: test_cron_fire_dashboard (6) — route registered on the dashboard app,
in PUBLIC_API_PATHS, 401 on bad token WITH the cookie gate engaged (proves it's
reachable past the gate + JWT is the gate), 400 missing job_id, 200 gone for
unknown job, 202 + fire_due invoked for the resolved profile on a valid token.
Full hermes_cli + cron + chronos + webhook suites green (7637).

Why the original tests missed it: the api_server webhook test built an
APIServerAdapter client directly and never asserted which server the hosted
public URL exposes — green-but-wrong-integration. The new test pins the route
to the dashboard app.
2026-06-19 12:43:30 +10:00
..
__init__.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
audit.py feat(dashboard-auth): single-use WS tickets + POST /api/auth/ws-ticket 2026-05-27 02:12:27 -07:00
base.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
cookies.py fix(desktop): gate OAuth remote connect on AT-or-RT, not access token alone 2026-06-04 22:18:46 -07:00
login_page.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
middleware.py fix(dashboard-auth): don't abort verify chain on one provider's ProviderError 2026-06-04 03:23:45 -07:00
prefix.py fix(dashboard-auth): warn when public_url override is silently rejected (#43214) 2026-06-10 12:14:57 +10:00
public_paths.py fix(cron): serve /api/cron/fire on the dashboard app (hosted-agent surface) 2026-06-19 12:43:30 +10:00
registry.py feat(dashboard-auth): define DashboardAuthProvider ABC + Session dataclass 2026-05-27 02:12:27 -07:00
routes.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
ws_tickets.py test(dashboard): direct unit coverage for internal WS credential + docstring fix 2026-06-02 23:43:27 -07:00