docs/gateway.md: extract big-picture gateway docs, trim in-code comments (mara #775:9988)
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.
This commit is contained in:
parent
df549ed2a5
commit
922057e81b
4 changed files with 171 additions and 314 deletions
|
|
@ -42,6 +42,7 @@ Depth lives in [`docs/`](docs/) — pick the one matching your task:
|
|||
| config-edit + approval state machine | [`docs/approvals.md`](docs/approvals.md) |
|
||||
| what survives destroy / purge / restart | [`docs/persistence.md`](docs/persistence.md) |
|
||||
| naming, wire protocol, commit style | [`docs/conventions.md`](docs/conventions.md) |
|
||||
| nginx vhost map + sub-domain routing | [`docs/gateway.md`](docs/gateway.md) |
|
||||
| NixOS / nspawn gotchas | [`docs/gotchas.md`](docs/gotchas.md) |
|
||||
|
||||
## Host config
|
||||
|
|
|
|||
78
docs/gateway.md
Normal file
78
docs/gateway.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# 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:
|
||||
|
||||
1. Client fetches `http://<hive>/.well-known/matrix/client` → `{"m.homeserver":{"base_url":"http://matrix.<hive>"}}` (no port suffix when gateway listens on 80).
|
||||
2. Client connects to `matrix.<hive>/_matrix/client/...`.
|
||||
3. Gateway routes `/_matrix/*` → tuwunel at `127.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.1`
|
||||
- `forge.<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 any `X-Forwarded-Prefix` gymnastics — 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-Prefix` set 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-known` delegation.
|
||||
- #772 / #775 — fluffychat hops from `<hive>/matrix/` to `matrix.<hive>/`.
|
||||
|
||||
Next-up tracked separately: #14 (container netns isolation), TLS (#594).
|
||||
|
|
@ -10,25 +10,10 @@ let
|
|||
matrixCfg = config.services.hyperhive.matrix;
|
||||
forgeCfg = config.services.hyperhive.forge;
|
||||
|
||||
# Per-agent port table for `/agent/<name>/` routing (#15 v0). Single-
|
||||
# sourced from `cfg.agentPortsFile` (default
|
||||
# `/var/lib/hyperhive/agent-ports.json`), written by hive-c0re on every
|
||||
# topology change in shape `{ "<name>": <port>, ... }`.
|
||||
#
|
||||
# Read at deploy time via `builtins.fromJSON (builtins.readFile ...)`
|
||||
# — pure eval (the file lives outside the nix store; nix copies the
|
||||
# content into the store as a fixed-output dep). When the file is
|
||||
# missing (fresh install before c0re has had a chance to write it),
|
||||
# default to an empty map → no per-agent routes generated → gateway
|
||||
# falls back to its pre-#15 shape. The container rebuilds on every
|
||||
# `hivectl gateway-sync` (operator-initiated) or on the next
|
||||
# `nixos-rebuild switch`, picking up whatever c0re has written
|
||||
# since the last build.
|
||||
#
|
||||
# mara on #740 (comment 9295) + #15 (comment 9270): the gateway
|
||||
# nginx container lives in system config (not meta), so it can't
|
||||
# auto-rebuild from meta-flake events — the JSON file is what
|
||||
# bridges the host's nix eval to the agent-lifecycle data c0re owns.
|
||||
# Per-agent port table for `/agent/<name>/` routing. C0re writes
|
||||
# this JSON on every topology change; gateway reads at deploy time.
|
||||
# Missing file → empty map → no per-agent routes (graceful default).
|
||||
# See `docs/gateway.md` for the discovery + rebuild flow.
|
||||
agentPortsTable =
|
||||
if cfg.agentPortsFile == null || !builtins.pathExists cfg.agentPortsFile then
|
||||
{ }
|
||||
|
|
@ -36,21 +21,11 @@ let
|
|||
builtins.fromJSON (builtins.readFile cfg.agentPortsFile);
|
||||
in
|
||||
{
|
||||
# Single nginx in front of every hyperhive surface (#609 / #15 v0).
|
||||
# Lives in its own nixos-container (like hive-forge / hive-matrix) so
|
||||
# the operator can opt out without touching the host's own nginx, and
|
||||
# so the static-serve responsibility for the matrix GUI moves off
|
||||
# hive-c0re's axum router. Shares host netns so `localhost`
|
||||
# upstream resolution works without any port-forward dance.
|
||||
#
|
||||
# Routes (v0):
|
||||
# `location /matrix/` → static-serve fluffychat-web dist (when
|
||||
# `services.hyperhive.matrix.gui.enable` is true)
|
||||
# `location /` → proxy_pass to hive-c0re's dashboard upstream
|
||||
#
|
||||
# Container name `hive-gateway` keeps hive-c0re's lifecycle scanner
|
||||
# (which only sees `h-*`) out of the picture. State-free — nginx
|
||||
# config lives in the nix store, no runtime persistence to manage.
|
||||
# Single nginx in front of every hyperhive web surface — dashboard,
|
||||
# per-agent UIs (sub-path), forge + matrix (sub-domain), .well-known
|
||||
# delegations. Container `hive-gateway`, shared host netns,
|
||||
# state-free. Full vhost map + discovery flow + design rationale in
|
||||
# `docs/gateway.md`.
|
||||
|
||||
options.services.hyperhive.gateway = {
|
||||
enable = lib.mkOption {
|
||||
|
|
@ -209,32 +184,11 @@ in
|
|||
enable = true;
|
||||
recommendedProxySettings = true;
|
||||
recommendedOptimisation = true;
|
||||
# SPA-fallback target keyed on the `Accept` request header
|
||||
# (#686, mara + damocles on PR #729). This decides whether a
|
||||
# `/matrix/...` miss falls through to `index.html` (route
|
||||
# navigation) or returns a clean 404 (asset miss) — see the
|
||||
# `/matrix/` location comment below for the full rationale.
|
||||
#
|
||||
# Top-frame browser navigations always send
|
||||
# `Accept: text/html,...` (chrome/firefox/safari are
|
||||
# consistent on this). Asset fetches from script tags / img
|
||||
# / fetch() / XHR send asset-typed Accepts (`image/*`,
|
||||
# `application/javascript`, `*/*`) without `text/html`.
|
||||
# Mapping is purely on the header → no extension allowlist
|
||||
# to keep in sync with whatever the SPA ships, no regex
|
||||
# heuristic to false-positive on dot-segment routes.
|
||||
#
|
||||
# `$matrix_spa_target` defaults to a sentinel nonexistent
|
||||
# path so `try_files` falls through to the trailing `=404`
|
||||
# for asset misses. Browser navigations route to
|
||||
# `/matrix/index.html` where the SPA's client-side router
|
||||
# takes over.
|
||||
#
|
||||
# Only emitted when the matrix GUI is on (saves a no-op
|
||||
# `map` directive otherwise). Target now points at the
|
||||
# sub-domain-root `/index.html` (fluffychat moved off
|
||||
# `<hive>/matrix/` sub-path to `matrix.<hive>/` root in
|
||||
# #772; `--base-href` reverts to upstream-default `/`).
|
||||
# Accept-header SPA fallback (#686 / #729): navigations
|
||||
# (`Accept: text/html,...`) fall to index.html, asset
|
||||
# fetches (Accept *anything else*) fall to a sentinel
|
||||
# nonexistent path → `try_files` returns 404. Pattern
|
||||
# detailed in `docs/gateway.md` ("SPA fallback").
|
||||
appendHttpConfig = lib.optionalString (matrixCfg.enable && matrixCfg.gui.enable) ''
|
||||
map $http_accept $matrix_spa_target {
|
||||
default "/__matrix_spa_no_html_fallback";
|
||||
|
|
@ -250,19 +204,11 @@ in
|
|||
}
|
||||
];
|
||||
locations =
|
||||
# Bare-domain `<hive>/matrix/*` → 301 redirect to the
|
||||
# matrix sub-domain root (#772). fluffychat-web used to
|
||||
# live at this sub-path; #772 moved it to `matrix.<hive>/`
|
||||
# so it gets full sub-domain origin isolation + sub-spec
|
||||
# matches the matrix-spec deploy shape. The redirect
|
||||
# preserves bookmark + deep-link compatibility for
|
||||
# `<hive>/matrix/#/...` URLs during the transition;
|
||||
# operators can drop the redirect block once it's been
|
||||
# in the wild long enough that no stale bookmarks remain.
|
||||
#
|
||||
# Only emitted when both the matrix GUI is on AND
|
||||
# `matrixCfg.gatewayHost` is set (else there's no
|
||||
# canonical sub-domain to redirect to).
|
||||
# `<hive>/matrix/*` → 301 → `matrix.<hive>/$1`
|
||||
# (fluffychat moved to sub-domain root in #772; this
|
||||
# keeps bookmarks + deep-links working during the
|
||||
# transition). See `docs/gateway.md` for the vhost
|
||||
# map.
|
||||
lib.optionalAttrs (
|
||||
matrixCfg.enable
|
||||
&& matrixCfg.gui.enable
|
||||
|
|
@ -273,11 +219,6 @@ in
|
|||
target = "http://${matrixCfg.gatewayHost}${portSuffix}";
|
||||
in
|
||||
{
|
||||
# `rewrite ^/matrix/(.*)$ → matrix.<hive>/$1` —
|
||||
# strips the `/matrix/` prefix on the way out so
|
||||
# `<hive>/matrix/#/rooms/...` lands at the right
|
||||
# SPA route on the sub-domain side. `permanent`
|
||||
# emits 301 + sets the canonical Location header.
|
||||
"/matrix/" = {
|
||||
extraConfig = ''
|
||||
rewrite ^/matrix/(.*)$ ${target}/$1 permanent;
|
||||
|
|
@ -286,37 +227,14 @@ in
|
|||
}
|
||||
)
|
||||
//
|
||||
# `.well-known/matrix/*` auto-discovery (#660): when
|
||||
# `services.hyperhive.matrix.enable` is on and the
|
||||
# operator's set a hive domain, the gateway serves
|
||||
# the matrix-spec discovery JSON at the canonical
|
||||
# location so clients pointed at `${hyperhive.domain}`
|
||||
# resolve through to the actual tuwunel endpoint
|
||||
# without needing a `matrix.` subdomain.
|
||||
#
|
||||
# `m.homeserver.base_url` advertises the client-server
|
||||
# API. `m.server` advertises the federation
|
||||
# `host:port` (tuwunel serves both client + federation
|
||||
# on the same `httpPort` — see hive-matrix.nix).
|
||||
#
|
||||
# CORS `*` on the client endpoint per the matrix spec
|
||||
# (https://spec.matrix.org/v1.15/client-server-api/#getwell-knownmatrixclient).
|
||||
# No-op until the operator turns matrix on; until then
|
||||
# there's no homeserver to advertise.
|
||||
# `.well-known/matrix/{client,server}` discovery JSON.
|
||||
# Points clients at `matrixCfg.gatewayHost` (sub-domain
|
||||
# vhost) when set; falls back to direct `<hive>:<httpPort>`
|
||||
# when no gateway target. CORS `*` per matrix spec.
|
||||
# See `docs/gateway.md` "Discovery flow" for the full
|
||||
# client-bootstrap sequence.
|
||||
lib.optionalAttrs (matrixCfg.enable && hyperhiveDomain != null) (
|
||||
let
|
||||
# `.well-known/matrix/{client,server}` advertise where
|
||||
# the actual matrix API lives. When `matrixCfg.gatewayHost`
|
||||
# is set (default `matrix.<hive-domain>`, #747), point
|
||||
# at the sub-domain — no port suffix when the gateway
|
||||
# is on the canonical port 80, transparent to clients
|
||||
# (mara on #749:9609 sub-domain verdict, "not user-
|
||||
# visible because the .well-known redirect routes
|
||||
# clients through automatically"). When `gatewayHost`
|
||||
# is unset (no hive-domain, or operator nulled it),
|
||||
# fall back to the direct `host:port` shape — clients
|
||||
# reach tuwunel without going through the gateway,
|
||||
# no sub-domain delegation.
|
||||
portSuffix = if cfg.port == 80 then "" else ":${toString cfg.port}";
|
||||
clientBaseUrl =
|
||||
if matrixCfg.gatewayHost != null then
|
||||
|
|
@ -346,33 +264,12 @@ in
|
|||
}
|
||||
)
|
||||
//
|
||||
# Per-agent UIs (#15 v0). One `/agent/<name>/`
|
||||
# block per `<name>: <port>` entry in
|
||||
# `agentPortsTable` (loaded from `cfg.agentPortsFile`
|
||||
# — `/var/lib/hyperhive/agent-ports.json` by default,
|
||||
# written by hive-c0re on every topology change).
|
||||
#
|
||||
# Trailing-slash pair (`/agent/<name>/` + `proxy_pass
|
||||
# http://...:<port>/`) strips the `/agent/<name>`
|
||||
# prefix on the upstream side, so the agent server
|
||||
# receives `GET /` for the SPA root, `GET /api/state`
|
||||
# for the API, `GET /screen/ws` for the websocket, etc.
|
||||
# The agent's emitted asset URLs are document-relative
|
||||
# (iris's #731) so they round-trip back through the
|
||||
# gateway under the same prefix without the harness
|
||||
# needing prefix-awareness.
|
||||
#
|
||||
# `X-Forwarded-Prefix` set so the harness can build
|
||||
# correct absolute URLs for any case where relative
|
||||
# isn't enough (server-emitted redirects, OG meta
|
||||
# tags, etc.).
|
||||
#
|
||||
# SSE / websocket support via `proxyWebsockets = true`
|
||||
# (same as the c0re `/` block below).
|
||||
#
|
||||
# Empty / missing `cfg.agentPortsFile` → empty table
|
||||
# → no per-agent blocks; old `<host>:<port>/` direct
|
||||
# reach still works.
|
||||
# Per-agent UIs (#15 v0). One `/agent/<name>/` block
|
||||
# per entry in `agentPortsTable`. Trailing-slash pair
|
||||
# strips the prefix; `X-Forwarded-Prefix` lets the
|
||||
# harness build absolute URLs when relative isn't
|
||||
# enough. See `docs/gateway.md` for the vhost map
|
||||
# + tuning rationale.
|
||||
lib.mapAttrs' (name: port: {
|
||||
name = "/agent/${name}/";
|
||||
value = {
|
||||
|
|
@ -402,31 +299,11 @@ in
|
|||
};
|
||||
}
|
||||
//
|
||||
# Forge vhost (#749, mara verdict at issue:9609 —
|
||||
# sub-domain over sub-path). When forgejo runs behind the
|
||||
# gateway (`forge.behindGateway = true`), it gets its own
|
||||
# `server { server_name = forge.domain; }` block. The
|
||||
# block proxies all `/` → `http://127.0.0.1:<forge.httpPort>/`
|
||||
# so forgejo handles requests at root (default deploy shape
|
||||
# — no `ROOT_URL`-prefix translation needed).
|
||||
#
|
||||
# `forge.domain` is the full hostname (e.g.
|
||||
# `forge.darkest.space`, `git.example.com`) — single source
|
||||
# of truth for both the forgejo `DOMAIN` setting and the
|
||||
# gateway vhost name (mara on #754:9684 — "specify full
|
||||
# forge domain in options instead").
|
||||
#
|
||||
# `client_max_body_size 1G` — git pushes + LFS uploads can
|
||||
# be large; nginx's default 1M would 413 most real commits.
|
||||
#
|
||||
# Long timeouts for big repo operations: a fresh clone of a
|
||||
# multi-GB repo can take minutes; the default 60s
|
||||
# `proxy_read_timeout` would abort mid-stream.
|
||||
#
|
||||
# `proxyWebsockets = true` keeps forgejo's live-update
|
||||
# endpoints (`/api/v1/events`) + any future websocket
|
||||
# endpoints working transparently. SSH stays direct on
|
||||
# `forge.sshPort` (separate listener protocol, not HTTP).
|
||||
# Forge sub-domain vhost (#749 / #754). `server_name =
|
||||
# forge.domain`, proxies all `/` → forgejo. Tuned for
|
||||
# git: `client_max_body_size 1G`, `proxy_read_timeout 1h`
|
||||
# (multi-GB clones). SSH stays direct on `forge.sshPort`.
|
||||
# See `docs/gateway.md`.
|
||||
lib.optionalAttrs (forgeCfg.enable or false && forgeCfg.behindGateway or false) {
|
||||
"${forgeCfg.domain}" = {
|
||||
listen = [
|
||||
|
|
@ -448,34 +325,12 @@ in
|
|||
};
|
||||
}
|
||||
//
|
||||
# Matrix homeserver vhost (#747, mara verdict on #749:9609 —
|
||||
# sub-domain over sub-path for matrix; "not user-visible"
|
||||
# because clients discover the sub-domain via the
|
||||
# `.well-known/matrix/{client,server}` delegation served
|
||||
# above on the bare hive-domain).
|
||||
#
|
||||
# `server { server_name = matrixCfg.gatewayHost; }` proxies
|
||||
# `/_matrix/...` → `http://127.0.0.1:''${matrixCfg.httpPort}/_matrix/...`.
|
||||
# Tuwunel listens on `:''${httpPort}` (default 8008); the
|
||||
# gateway terminates on `:''${cfg.port}` (80) so external
|
||||
# clients speak matrix over the canonical web port without
|
||||
# operators having to open the tuwunel port through firewalls.
|
||||
#
|
||||
# `/` returns 404 — nothing else lives at the matrix vhost;
|
||||
# the matrix client-server API is entirely under `/_matrix/`,
|
||||
# and federation under `/_matrix/federation/...`.
|
||||
#
|
||||
# CORS `*` on the matrix vhost per the matrix spec —
|
||||
# federation + client requests come from any origin.
|
||||
#
|
||||
# `client_max_body_size 50M` covers typical media uploads
|
||||
# (matrix-spec media size cap default); operators with bigger
|
||||
# uploads override via the matrix module's own cap when that
|
||||
# lands.
|
||||
#
|
||||
# `proxy_read_timeout 1h` for long-poll `/sync`; the default
|
||||
# 60s would abort `/sync?timeout=30000` legitimately when
|
||||
# tuwunel's keepalive exceeds that.
|
||||
# Matrix sub-domain vhost (#747 / #764). `server_name =
|
||||
# matrixCfg.gatewayHost`. `/_matrix/*` → tuwunel (CORS *,
|
||||
# 50M body cap, 1h long-poll timeout). `/` serves
|
||||
# fluffychat (#772) or 404 if GUI off. nginx
|
||||
# longer-prefix-wins puts `/_matrix/` ahead of `/`.
|
||||
# See `docs/gateway.md`.
|
||||
lib.optionalAttrs (matrixCfg.enable && matrixCfg.gatewayHost != null) {
|
||||
"${matrixCfg.gatewayHost}" = {
|
||||
listen = [
|
||||
|
|
@ -498,49 +353,34 @@ in
|
|||
'';
|
||||
};
|
||||
}
|
||||
//
|
||||
# Fluffychat-web (or override) served at sub-domain
|
||||
# root (#772 — moved from bare-domain `/matrix/`).
|
||||
# nginx location-precedence: longer prefix wins,
|
||||
# so `/_matrix/` (above) handles the matrix API
|
||||
# and `/` falls through to fluffychat for
|
||||
# everything else.
|
||||
#
|
||||
# SPA fallback uses the same Accept-header `$matrix_spa_target`
|
||||
# map from `appendHttpConfig` above — navigations
|
||||
# fall to `/index.html` (now sub-domain-root path),
|
||||
# asset misses return clean 404.
|
||||
#
|
||||
# `= /config.json` serves the FluffyChat boot-config
|
||||
# pre-fill with the operator's hive-domain so the
|
||||
# client's `.well-known/matrix/client` lookup hits
|
||||
# the right delegation endpoint (#736).
|
||||
lib.optionalAttrs (matrixCfg.gui.enable) (
|
||||
{
|
||||
"/" = {
|
||||
alias = "${matrixCfg.gui.package}/";
|
||||
extraConfig = ''
|
||||
try_files $uri $uri/ $matrix_spa_target =404;
|
||||
'';
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs (hyperhiveDomain != null) {
|
||||
"= /config.json" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}';
|
||||
'';
|
||||
};
|
||||
}
|
||||
)
|
||||
//
|
||||
# Fall-through `/` when GUI is off: nothing else
|
||||
# lives at the matrix vhost, return 404 cleanly.
|
||||
lib.optionalAttrs (!matrixCfg.gui.enable) {
|
||||
// lib.optionalAttrs (matrixCfg.gui.enable) (
|
||||
{
|
||||
# fluffychat at sub-domain root, SPA-fallback via
|
||||
# the Accept-header `$matrix_spa_target` map.
|
||||
"/" = {
|
||||
return = "404";
|
||||
alias = "${matrixCfg.gui.package}/";
|
||||
extraConfig = ''
|
||||
try_files $uri $uri/ $matrix_spa_target =404;
|
||||
'';
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs (hyperhiveDomain != null) {
|
||||
# FluffyChat boot-config pre-fill so the client's
|
||||
# `.well-known/matrix/client` lookup hits the
|
||||
# right delegation endpoint (#736).
|
||||
"= /config.json" = {
|
||||
extraConfig = ''
|
||||
default_type application/json;
|
||||
return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}';
|
||||
'';
|
||||
};
|
||||
}
|
||||
)
|
||||
// lib.optionalAttrs (!matrixCfg.gui.enable) {
|
||||
"/" = {
|
||||
return = "404";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -551,17 +391,10 @@ in
|
|||
allowedTCPPorts = [ cfg.port ];
|
||||
};
|
||||
|
||||
# `/etc/hosts` entries for local dev: the bare hive domain plus
|
||||
# any sub-domain modules (forge via #749/#754, matrix via #747)
|
||||
# that are on. All map to `127.0.0.1` since the gateway shares
|
||||
# host netns. Operators with real DNS leave `localHostsEntry =
|
||||
# false`; this is the dev-loop shortcut for `http://<hive-domain>/`
|
||||
# + `http://forge.<hive-domain>/` + `http://matrix.<hive-domain>/`
|
||||
# resolving locally.
|
||||
#
|
||||
# `lib.unique` collapses any duplicate (e.g. if forge.domain
|
||||
# happens to equal hyperhiveDomain or matrixCfg.gatewayHost) so
|
||||
# `/etc/hosts` doesn't carry the same entry twice.
|
||||
# `/etc/hosts` entries for local dev — bare hive domain + any
|
||||
# sub-domain modules that are on. `lib.unique` dedupes if any
|
||||
# sub-domain happens to equal another. See `docs/gateway.md`
|
||||
# ("Local dev").
|
||||
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
||||
"127.0.0.1" = lib.unique (
|
||||
[ hyperhiveDomain ]
|
||||
|
|
|
|||
|
|
@ -9,50 +9,20 @@ let
|
|||
hyperhiveDomain = config.services.hyperhive.domain;
|
||||
effectiveServerName = if cfg.serverName != null then cfg.serverName else hyperhiveDomain;
|
||||
|
||||
# Three files are missing from nixpkgs's `pkgs.fluffychat-web` dist
|
||||
# because `flutter341.buildFlutterApplication` doesn't run the dart
|
||||
# web-worker compile pass + doesn't run the native_imaging package's
|
||||
# emscripten build (#685):
|
||||
#
|
||||
# - native_executor.js ← flutter web worker entry, compiled
|
||||
# from web/native_executor.dart via
|
||||
# `dart compile js` (handled inline
|
||||
# in `fluffychat-web-fixed.postInstall`
|
||||
# below — dart SDK is already in the
|
||||
# flutter341 closure)
|
||||
#
|
||||
# - Imaging.js / Imaging.wasm ← emscripten-compiled C library from
|
||||
# the native_imaging dart package
|
||||
# (vendored by Famedly). The package
|
||||
# ships C source + a Makefile that
|
||||
# builds them via emcc; nixpkgs's
|
||||
# flutter builder doesn't run that
|
||||
# pipeline. Built from source via
|
||||
# `fluffychat-web-imaging` below
|
||||
# (per mara's #685 call: "fix the
|
||||
# compile … dont use the prebuilt
|
||||
# binary").
|
||||
#
|
||||
# When `flutter341.buildFlutterApplication` grows worker + emcc
|
||||
# support upstream, drop both this derivation and the postInstall.
|
||||
# Three files are missing from `pkgs.fluffychat-web` because
|
||||
# `flutter341.buildFlutterApplication` doesn't run the dart
|
||||
# web-worker compile pass + doesn't run the native_imaging emscripten
|
||||
# build (#685). `fluffychat-web-imaging` below builds the latter from
|
||||
# source via `pkgs.emscripten`; the worker compile is inline in
|
||||
# `fluffychat-web-fixed.postInstall`. Drop both when nixpkgs's
|
||||
# flutter builder grows worker + emcc support upstream.
|
||||
|
||||
# Imaging.{js,wasm} built from source: native_imaging's `js/Makefile`
|
||||
# runs `emcmake cmake` → `make -C build` → `emcc` to produce the
|
||||
# emscripten-wrapped C library that fluffychat's main.dart.js
|
||||
# references at runtime.
|
||||
#
|
||||
# Source: the exact native_imaging derivation that `pkgs.fluffychat-web`
|
||||
# already pulls in via its `pubspecLock` (resolved by nixpkgs's flutter
|
||||
# pub-cache machinery), reached via `passthru.pubspecLock.dependencySources`.
|
||||
# This means **no parallel hash pin** — when nixpkgs bumps
|
||||
# `pkgs.fluffychat-web` (and with it the pubspec.lock-resolved
|
||||
# native_imaging version), our build automatically picks up the
|
||||
# matching source. Version is also pulled from passthru for the
|
||||
# derivation's `version` attr so it stays in lockstep.
|
||||
#
|
||||
# Closure cost: `pkgs.emscripten` is ~3.6 GiB build-time (LLVM +
|
||||
# toolchain). Runtime closure is only the two produced files —
|
||||
# nothing emscripten-shaped survives into the deployed dist.
|
||||
# `Imaging.{js,wasm}` built from `native_imaging`'s C source via
|
||||
# emscripten. Source comes from
|
||||
# `pkgs.fluffychat-web.passthru.pubspecLock.dependencySources` so
|
||||
# there's no parallel hash pin — version auto-syncs with nixpkgs
|
||||
# bumps. Build closure +~3.6 GiB (emscripten LLVM); runtime closure
|
||||
# is just the two output files.
|
||||
fluffychat-web-imaging = pkgs.stdenv.mkDerivation {
|
||||
pname = "fluffychat-web-imaging";
|
||||
version = pkgs.fluffychat-web.passthru.pubspecLock.dependencyVersions.native_imaging;
|
||||
|
|
@ -102,14 +72,9 @@ let
|
|||
};
|
||||
};
|
||||
|
||||
# `pkgs.fluffychat-web` with #685's three missing files patched
|
||||
# in via postInstall. No `--base-href` override anymore (#772 moves
|
||||
# fluffychat from the bare-domain `<hive>/matrix/` sub-path to the
|
||||
# `matrix.<hive>/` sub-domain root; upstream's default `--base-href
|
||||
# "/"` is correct at sub-domain root). The previous `/matrix/`
|
||||
# override from #634 + #754 (forge sub-domain pattern) lived here
|
||||
# until the matrix sub-domain vhost in #764 made the sub-path
|
||||
# redundant.
|
||||
# `pkgs.fluffychat-web` with #685's three missing files patched in
|
||||
# via postInstall. Mount point is `matrix.<hive>/` (#772); upstream
|
||||
# `--base-href "/"` is correct at sub-domain root, no override.
|
||||
fluffychat-web-fixed = pkgs.fluffychat-web.overrideAttrs (old: {
|
||||
# `dart` from the flutter341 closure (already pulled, no
|
||||
# incremental closure cost) so we can compile the web-worker
|
||||
|
|
@ -119,34 +84,14 @@ let
|
|||
postInstall =
|
||||
(old.postInstall or "")
|
||||
+ ''
|
||||
# #685: compile web/native_executor.dart → native_executor.js.
|
||||
# The flutter web bootstrap loads this from /matrix/native_executor.js
|
||||
# at startup; without it, main.dart.js logs a network-error and
|
||||
# the SPA renders blank (see #643 for the symptom).
|
||||
#
|
||||
# `dart compile js` needs `.dart_tool/package_config.json` to
|
||||
# resolve `package:matrix/...` and the rest of fluffychat's
|
||||
# `pubspec.lock` deps. buildFlutterApplication's pub-get step
|
||||
# writes that file to the build CWD (the unpacked source dir),
|
||||
# not to `$src` (the read-only nix store path). So we must use
|
||||
# a relative path that walks up from `web/` to the build CWD
|
||||
# where pub-get's package_config lives — pointing at
|
||||
# `$src/web/native_executor.dart` walks up to `$src/`, finds
|
||||
# no `.dart_tool/`, and fails with `Couldn't resolve the
|
||||
# package 'matrix'` (mara's first build attempt on #685).
|
||||
#
|
||||
# nixpkgs's buildFlutterApplication leaves CWD at the source
|
||||
# root for postInstall (see `pkgs/development/compilers/flutter/
|
||||
# build-support/build-flutter-application.nix` — installPhase
|
||||
# is `cp -r build/web "$out"` with no `cd` first). So `web/...`
|
||||
# resolves correctly here.
|
||||
# `web/...` is relative to build CWD so dart's package_config
|
||||
# walk-up hits buildFlutterApplication's pub-get output (#685
|
||||
# / #733 fixup — `$src/web/...` would walk up to a read-only
|
||||
# store path with no `.dart_tool/`).
|
||||
${pkgs.flutter341.dart}/bin/dart compile js \
|
||||
-o $out/native_executor.js \
|
||||
web/native_executor.dart
|
||||
|
||||
# #685: install Imaging.{js,wasm} built from the native_imaging
|
||||
# dart package's C source via emscripten (see
|
||||
# `fluffychat-web-imaging` above for the build-time rationale).
|
||||
install -m 644 ${fluffychat-web-imaging}/Imaging.js $out/Imaging.js
|
||||
install -m 644 ${fluffychat-web-imaging}/Imaging.wasm $out/Imaging.wasm
|
||||
'';
|
||||
|
|
|
|||
Loading…
Reference in a new issue