mara on PR #775: "this is too much docs in code - move bigger picture stuff to md files and put refs in code" New `docs/gateway.md` consolidates the gateway architecture story that was spreading across long inline comments in `hive-gateway.nix`, `hive-matrix.nix`, and `hive-forge.nix`: - vhost map (which URL serves what, which upstream, which option) - matrix discovery flow (.well-known → sub-domain delegation sequence) - Accept-header SPA fallback pattern (#686 / #729 design history) - local-dev `localHostsEntry` story - sub-domain rationale (mara verdict tracking) + when sub-path is right (hyperhive-internal apps) - per-vhost tuning knobs (forge LFS, matrix long-poll, agent SSE) - sequencing history (which PR added which routing piece) In-code comments in the two nix modules get trimmed to short refs into the doc — keeps the *why* in the markdown while the *what* stays alongside the code: - hive-gateway.nix: top-of-file comment, `agentPortsTable`, `appendHttpConfig`, every location block + vhost - hive-matrix.nix: `fluffychat-web-fixed`, `fluffychat-web-imaging`, the dart compile postInstall README.md gets a new row in the docs table pointing at gateway.md. Verified `nix eval` still resolves the same vhost + location layout after the comment trim — no behavioral change, just less in-code prose.
5.5 KiB
hive-gateway
Single nginx in front of every hyperhive web surface. Container hive-gateway, shared host netns, system-config (not meta-flake managed). Configured via services.hyperhive.gateway.* + per-subsystem opt-in flags in services.hyperhive.{forge,matrix,...}.
Vhost map
| URL | vhost | upstream | source |
|---|---|---|---|
<hive>/ |
_ (catch-all) |
hive-c0re dashboard (7000) |
always |
<hive>/agent/<name>/ |
_ |
per-agent harness on agent_web_port(name) |
agentPortsFile JSON, #15 |
<hive>/.well-known/matrix/{client,server} |
_ |
inline JSON (no upstream) | matrix.enable && domain != null, #660 / #747 |
<hive>/matrix/ (deprecated) |
_ |
301 → matrix.<hive>/ |
matrix.gui.enable, #772 |
forge.<hive>/ |
forge.<hive> |
forgejo (3000) |
forge.behindGateway, #754 |
matrix.<hive>/_matrix/* |
matrix.<hive> |
tuwunel (8008) |
matrix.gatewayHost != null, #764 |
matrix.<hive>/ |
matrix.<hive> |
fluffychat-web static | matrix.gui.enable, #772 |
matrix.<hive>/config.json |
matrix.<hive> |
inline JSON (FluffyChat boot config) | matrix.gui.enable && domain != null, #736 |
Per-agent UIs stay sub-path because they're hyperhive-internal and base-path-aware (iris #731). External standard apps (forge / matrix) get sub-domains because their defaults work cleanly at sub-domain root + per-origin cookies / storage isolation matters.
Discovery flow (matrix)
Operator points client at <hive>. Sequence:
- Client fetches
http://<hive>/.well-known/matrix/client→{"m.homeserver":{"base_url":"http://matrix.<hive>"}}(no port suffix when gateway listens on 80). - Client connects to
matrix.<hive>/_matrix/client/.... - Gateway routes
/_matrix/*→ tuwunel at127.0.0.1:8008.
Federation peers fetch .well-known/matrix/server → {"m.server":"matrix.<hive>"} and connect to matrix.<hive>:8448 per spec default. Gateway only listens on configured port; cross-hive federation needs either an SRV record (_matrix._tcp.matrix.<hive> → port 80) OR matrix.openFirewall = true so peers reach tuwunel's federation port directly. Hyperhive is mostly closed/internal, so this rarely bites.
SPA fallback (Accept-header pattern)
The <hive> catch-all and the matrix.<hive> vhost both serve a flutter SPA (per-agent UI, fluffychat). Two requirements collide:
- hard-refresh on a sub-route must serve
index.html(SPA's client-side router takes over after JS bootstrap) - missing assets must surface as 404, not as HTML with wrong content-type (the original #643 bug)
Solution: an nginx http-context map $http_accept $matrix_spa_target { ... } keyed on the request's Accept header. Browser navigations (Accept: text/html,...) get index.html; asset fetches (Accept: image/*, */*, etc.) get a sentinel nonexistent path → try_files falls through to =404. No extension allowlist, no if block, no regex heuristics. #686 + #729 thread for the design history.
Local dev (localHostsEntry)
services.hyperhive.gateway.localHostsEntry = true adds entries to the host's /etc/hosts:
<hive-domain>→127.0.0.1forge.<hive>→127.0.0.1(when forge.behindGateway)matrix.<hive>→127.0.0.1(when matrix.gatewayHost set)
lib.unique de-dupes if any sub-domain happens to equal another entry. Operators with real DNS leave it off.
Sub-domain shape (rationale)
mara verdict at #749:9609 + #747:9722: sub-domain over sub-path for forge + matrix, sub-path for per-agent UIs.
- forgejo's default
ROOT_URL = http://<host>/works without anyX-Forwarded-Prefixgymnastics — sub-domain hosting is the canonical Forgejo deploy shape. - matrix-spec deployments universally use
matrix.<server_name>for the actual API listener — federation already expects this. - per-agent UIs are hyperhive-internal; iris's #731 made them base-path-aware specifically for
/agent/<name>/. Sub-domain per agent would multiply DNS + TLS-per-subdomain cost without per-app config wins. - cookie / storage isolation: a future forge XSS can't reach the dashboard session because they're different origins.
services.hyperhive.{forge.domain,matrix.gatewayHost} take the full hostname (forge.darkest.space, git.example.com) rather than a label that gets concatenated with hive-domain — mara on #754:9684 wanted operator control over the full shape, not a forced <label>.<hive-domain> pattern.
Tuning knobs
Per-vhost timeouts + body-size limits live in the location blocks:
- forge
/(forgejo):client_max_body_size 1G(LFS),proxy_read_timeout 1h(multi-GB clones),proxyWebsockets = true(live-update endpoints). - matrix
/_matrix/(tuwunel):client_max_body_size 50M(media uploads),proxy_read_timeout 1h(long-poll/sync), CORS*(federation + cross-origin clients),proxyWebsockets = true. - per-agent
/agent/<name>/:proxy_read_timeout 1d(long-lived SSE / WebSocket dashboards),proxyWebsockets = true,X-Forwarded-Prefixset so the harness can build absolute URLs when relative isn't enough.
SSH for forge stays direct on cfg.sshPort — separate listener protocol, not HTTP-over-nginx.
Sequencing history
- #15 v0 (per-agent routing, #740) — first sub-app behind the gateway, JSON port table from c0re.
- #686 / #729 — Accept-header SPA fallback pattern.
- #749 / #754 — forge to sub-domain (mara: sub-domain over sub-path).
- #747 / #764 — matrix sub-domain vhost +
.well-knowndelegation. - #772 / #775 — fluffychat hops from
<hive>/matrix/tomatrix.<hive>/.
Next-up tracked separately: #14 (container netns isolation), TLS (#594).