nix/hive-gateway: static not-found + unreachable pages for /agent/<name>/ (#755)

mara on #755: "e.g. /agent/name should show an error page stating
that the agent could not be found if missing in json or that it is
not reachable if we get a connection error. we dont want a fully
generic fallback, only for routes already special cased in the
nginx config."

Adds two static HTML pages built at deploy time via
`pkgs.runCommand "hyperhive-agent-error-pages"`:

- **not-found.html** — served when `/agent/<unknown>/...` hits the
  bare `/agent/` catch-all. The catch-all `return 404`s, and
  `error_page 404 = /__hive_agent_not_found` rewrites to the static
  page.
- **unreachable.html** — served when `/agent/<known>/...` proxy_pass
  to the harness returns 502 / 503 / 504. `proxy_intercept_errors
  on` + `error_page 502 503 504 = /__hive_agent_unreachable` on each
  per-agent location block rewrites to the static page.

Mechanics:

- `agentErrorPagesDir` (in the `let` block) is a `runCommand` that
  emits two HTML files using a `<<EOF` heredoc — no template engine
  needed.
- Two `internal` nginx locations (`= /__hive_agent_not_found`,
  `= /__hive_agent_unreachable`) `alias` the exact files. `internal`
  keeps the URIs unreachable from direct operator request — only
  nginx's own error-handling can hit them.
- Per-agent location blocks pick up the `error_page` directive
  through the existing `lib.mapAttrs'` over `agentPortsTable`. No
  per-agent generated content; same static page for all.
- `/agent/` catch-all generates from a tiny optionalAttrs alongside
  the per-agent block — both are no-op when the agent table is
  empty (matches the pre-#15 shape).

Pages: minimal inline CSS, catppuccin palette matching the
dashboard (`#1e1e2e` bg, `#cdd6f4` text, `#cba6f7` not-found heading,
`#f9e2af` unreachable heading). No frontend-dist dependency — render
even when hive-c0re is down. Both link back to `/`.

Per mara's "only for routes already special cased" — scope stays
narrow. Forge / matrix / fluffychat keep nginx defaults; extending
the custom-error pattern to other vhosts is a separate follow-up
if/when needed.

Verified:
- nginx location attrset has `["/", "/agent/", "= /__hive_agent_not_found", "= /__hive_agent_unreachable"]`
- container toplevel builds clean (`nixos-system-hive-gateway-26.05pre-git`)
- `docs/gateway.md::Per-agent error pages` section captures the
  design + rationale + intentional narrowness

Closes #755.
This commit is contained in:
atlas 2026-05-31 15:01:37 +02:00
commit 24775845a3
2 changed files with 123 additions and 2 deletions

View file

@ -132,3 +132,36 @@ is here for state + systemd-unit isolation, not network isolation.
State lives at `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/`
and survives container restart / host reboot. To wipe, destroy the
container.
## Per-agent error pages
`/agent/<name>/` requests hit two failure modes; both get static
HTML pages instead of nginx's default error chrome (#755):
- **Agent not found** (`/agent/<unknown>/...`) — name isn't in
`agentPortsTable`. nginx's prefix match falls back to the bare
`/agent/` catch-all, which `return 404`s and `error_page 404` rewrites
to `/__hive_agent_not_found` → serves `not-found.html` with a link
back to the dashboard.
- **Agent unreachable** (`502 / 503 / 504` from `proxy_pass`) — the
per-agent harness isn't responding (container restarting, crash
recovery, etc.). `proxy_intercept_errors on` + `error_page 502 503
504 = /__hive_agent_unreachable` rewrites to `unreachable.html`.
Both pages are built at deploy time via `pkgs.runCommand` (one nix
derivation `hyperhive-agent-error-pages` with `not-found.html` +
`unreachable.html` inside) and served via two `internal` nginx
locations with `alias` to the exact file. `internal` keeps the
files from being directly request-able by operators — only nginx's
own error-handling can reach them.
Page styling: minimal inline CSS matching the dashboard's catppuccin
palette (`#1e1e2e` bg, `#cdd6f4` text, `#cba6f7` heading). No
dependencies on the frontend dist — these pages render even when
hive-c0re itself is down.
Scope is intentionally narrow per mara on #755: "only for routes
already special cased in the nginx config". Other gateway routes
(forge / matrix / fluffychat) get nginx defaults — extending the
custom-error pattern there is a separate follow-up.