From df549ed2a531764697fbd04bb4675043f3bcc58b Mon Sep 17 00:00:00 2001 From: atlas Date: Sun, 31 May 2026 14:17:35 +0200 Subject: [PATCH] nix/hive-{matrix,gateway}: host fluffychat at matrix./, 301 from /matrix/ (#772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mara on #764:9897: "host the fluffy chat app at / as follow up?" Moves fluffychat-web from the bare-domain sub-path (`/matrix/`) to the matrix sub-domain root (`matrix./`). Follow-up to #764 (matrix vhost itself), per mara's gateway-architecture verdict (sub-domain for external standard apps, sub-path for hyperhive-internal). Stacked on `atlas/747-matrix-behind-gateway` — depends on #764 landing first. ## Mechanics **hive-matrix.nix:** - Drop `flutterBuildFlags = [ "--base-href" "/matrix/" ]` from `fluffychat-web-fixed`. Upstream default `--base-href "/"` is correct at sub-domain root. - Update option docs to reflect new mount point. **hive-gateway.nix:** - `$matrix_spa_target` map target flips from `/matrix/index.html` → `/index.html` (sub-domain root now). - New `/matrix/*` location: `rewrite ^/matrix/(.*)$ matrix./$1 permanent;` — 301 redirect preserves bookmark + deep-link compatibility for `/matrix/#/rooms/...` URLs during the transition. - `/matrix/config.json` location removed (moved to `/config.json` on the matrix vhost). - Matrix vhost (#764) gains `/` location: serves fluffychat dist as static files with the Accept-header SPA fallback (`/_matrix/` proxying to tuwunel keeps working via nginx longer-prefix-wins precedence). When `gui.enable = false`, `/` returns 404 cleanly. - Matrix vhost gains `= /config.json` for the FluffyChat boot-config pre-fill (#736). ## Verified ``` vhosts: ["_", "forge.test.local", "matrix.test.local"] bare locations: ["/", "/matrix/", "= /.well-known/matrix/client", "= /.well-known/matrix/server"] matrix vhost locations: ["/", "/_matrix/", "= /config.json"] /matrix/ extraConfig: "rewrite ^/matrix/(.*)$ http://matrix.test.local/$1 permanent;" matrix vhost / alias: /nix/store/...fluffychat-web-2.6.0/ ``` Full container toplevel builds clean. ## Risk Medium. Two breaking changes for operators: 1. **Bookmark migration**: `http:///matrix/#/rooms/...` 301s to `http://matrix./#/rooms/...`. Browser bookmarks + shared links keep working via the redirect; can be cleaned up once it's been in the wild long enough. 2. **fluffychat-web dist hash changes**: dropping the `--base-href "/matrix/"` flag changes the derivation hash, so `gui.package` rebuilds even though the source is the same. Operators on substitute caches will fetch the new dist; building from source takes the same time as before. The `.well-known/matrix/{client,server}` delegation (already advertising `matrix.` per #764) means matrix clients auto-discover the new location — no client config change needed. ## Sequencing **Depends on #764** — needs the matrix vhost to host the new `/` location. Merge after #764 lands + soaks. If #764 changes shape during review I'll rebase + force-push. Closes #772. --- nix/modules/hive-gateway.nix | 174 +++++++++++++++++++---------------- nix/modules/hive-matrix.nix | 75 ++++++++------- 2 files changed, 133 insertions(+), 116 deletions(-) diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index aeaf6e72..fe6634f3 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -231,11 +231,14 @@ in # takes over. # # Only emitted when the matrix GUI is on (saves a no-op - # `map` directive otherwise). + # `map` directive otherwise). Target now points at the + # sub-domain-root `/index.html` (fluffychat moved off + # `/matrix/` sub-path to `matrix./` root in + # #772; `--base-href` reverts to upstream-default `/`). appendHttpConfig = lib.optionalString (matrixCfg.enable && matrixCfg.gui.enable) '' map $http_accept $matrix_spa_target { default "/__matrix_spa_no_html_fallback"; - "~*text/html" "/matrix/index.html"; + "~*text/html" "/index.html"; } ''; virtualHosts = { @@ -247,67 +250,41 @@ in } ]; locations = - # Matrix GUI: when the operator has flipped both - # `services.hyperhive.matrix.enable` and `matrix.gui.enable` - # on, nginx serves fluffychat-web (or whatever override) - # as a static dist at `/matrix/`. + # Bare-domain `/matrix/*` → 301 redirect to the + # matrix sub-domain root (#772). fluffychat-web used to + # live at this sub-path; #772 moved it to `matrix./` + # so it gets full sub-domain origin isolation + sub-spec + # matches the matrix-spec deploy shape. The redirect + # preserves bookmark + deep-link compatibility for + # `/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. # - # SPA fallback (iris/#643, rewritten in #686 per mara - # + damocles on PR #729): the original - # `try_files $uri $uri/ /matrix/index.html;` shape - # silently masked missing assets — flutter's bootstrap - # requesting e.g. `/matrix/native_executor.js` got - # `index.html` (Content-Type: text/html, status 200) - # when the file was absent from the dist, so the JS - # runtime never loaded and `/matrix/` rendered blank - # without any visible error. - # - # The followup #729 narrowed it with an extension - # allowlist; this version uses `$matrix_spa_target` - # (defined in the `appendHttpConfig` above, keyed on - # the `Accept` header) so the decision lives in HTTP - # semantics rather than a maintained extension list. - # Navigations (Accept: text/html) fall to index.html; - # asset fetches (Accept: */*, image/*, etc.) get a - # clean 404 via the trailing `=404`. - lib.optionalAttrs (matrixCfg.enable && matrixCfg.gui.enable) { - "/matrix/" = { - alias = "${matrixCfg.gui.package}/"; - extraConfig = '' - try_files $uri $uri/ $matrix_spa_target =404; - ''; - }; - # FluffyChat fetches `/matrix/config.json` directly on - # boot for its own branding + default-homeserver - # bootstrap, BEFORE asking the user to pick a server - # (#736). The upstream `pkgs.fluffychat-web` dist - # ships without one, so the fetch 404s and the user - # sees the empty "enter homeserver" prompt. Serve a - # minimal config that pre-fills `defaultHomeserver` - # with the operator's hive domain — the matrix - # client then runs `.well-known/matrix/client` against - # that domain (already served by the - # `= /.well-known/matrix/client` block below) and - # discovers the actual tuwunel endpoint. - # - # Only the `defaultHomeserver` field is overridden — - # everything else (branding, audio defaults, etc.) - # falls back to fluffychat's hardcoded defaults so - # we don't pin against upstream config-schema drift. - # No-op when `services.hyperhive.domain` is unset - # (the location block is omitted entirely; the SPA - # then falls back to its empty form, same as before - # #736). - } // lib.optionalAttrs ( - matrixCfg.enable && matrixCfg.gui.enable && hyperhiveDomain != null - ) { - "= /matrix/config.json" = { - extraConfig = '' - default_type application/json; - return 200 '{"defaultHomeserver":"${hyperhiveDomain}"}'; - ''; - }; - } + # Only emitted when both the matrix GUI is on AND + # `matrixCfg.gatewayHost` is set (else there's no + # canonical sub-domain to redirect to). + lib.optionalAttrs ( + matrixCfg.enable + && matrixCfg.gui.enable + && matrixCfg.gatewayHost != null + ) ( + let + portSuffix = if cfg.port == 80 then "" else ":${toString cfg.port}"; + target = "http://${matrixCfg.gatewayHost}${portSuffix}"; + in + { + # `rewrite ^/matrix/(.*)$ → matrix./$1` — + # strips the `/matrix/` prefix on the way out so + # `/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; + ''; + }; + } + ) // # `.well-known/matrix/*` auto-discovery (#660): when # `services.hyperhive.matrix.enable` is on and the @@ -507,22 +484,63 @@ in port = cfg.port; } ]; - locations = { - "/_matrix/" = { - proxyPass = "http://127.0.0.1:${toString matrixCfg.httpPort}"; - proxyWebsockets = true; - extraConfig = '' - proxy_buffering off; - client_max_body_size 50M; - proxy_read_timeout 1h; - proxy_send_timeout 1h; - add_header Access-Control-Allow-Origin *; - ''; - }; - "/" = { - return = "404"; - }; - }; + locations = + { + "/_matrix/" = { + proxyPass = "http://127.0.0.1:${toString matrixCfg.httpPort}"; + proxyWebsockets = true; + extraConfig = '' + proxy_buffering off; + client_max_body_size 50M; + proxy_read_timeout 1h; + proxy_send_timeout 1h; + add_header Access-Control-Allow-Origin *; + ''; + }; + } + // + # 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) { + "/" = { + return = "404"; + }; + }; }; }; }; diff --git a/nix/modules/hive-matrix.nix b/nix/modules/hive-matrix.nix index 05b97925..3bc629d2 100644 --- a/nix/modules/hive-matrix.nix +++ b/nix/modules/hive-matrix.nix @@ -103,17 +103,14 @@ let }; # `pkgs.fluffychat-web` with #685's three missing files patched - # in via postInstall, plus the existing `--base-href "/matrix/"` - # override (#634) for the sub-path mount. + # in via postInstall. No `--base-href` override anymore (#772 moves + # fluffychat from the bare-domain `/matrix/` sub-path to the + # `matrix./` 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. fluffychat-web-fixed = pkgs.fluffychat-web.overrideAttrs (old: { - # `--base-href "/matrix/"` so relative asset paths resolve - # under the sub-path mount (#634). Upstream default is `/`, - # wrong for hyperhive's `/matrix/` location. - flutterBuildFlags = (old.flutterBuildFlags or [ ]) ++ [ - "--base-href" - "/matrix/" - ]; - # `dart` from the flutter341 closure (already pulled, no # incremental closure cost) so we can compile the web-worker # entry point that buildFlutterApplication skips. @@ -385,21 +382,22 @@ in defaultText = lib.literalExpression "config.services.hyperhive.matrix.enable"; description = '' Serve a matrix web client (default `pkgs.fluffychat-web`) as - a static dist at `/matrix/` via the hive-gateway nginx - (#607 / #634). Defaults to whatever + a static dist at the root of the matrix sub-domain vhost + (`matrix.''${services.hyperhive.domain}/`, via the hive-gateway + nginx — #607 / #634 / #772). Defaults to whatever `services.hyperhive.matrix.enable` is — turning on the homeserver gives you the web client by default; set to - `false` explicitly to opt out of the GUI while keeping - the homeserver running for agents. Requires - `services.hyperhive.gateway.enable` (default on); when - gateway is off no one hosts the GUI and the - `M4TR1X →` dashboard tab is hidden. + `false` explicitly to opt out of the GUI while keeping the + homeserver running for agents. Requires + `services.hyperhive.gateway.enable` (default on) + + `services.hyperhive.matrix.gatewayHost != null` (default + `matrix.`); when either is off no one hosts the GUI + and the `M4TR1X →` dashboard tab is hidden. - fluffychat-web supports per-login server pick — point it at - the in-host tuwunel URL (`http://localhost:8008` by - default) the first time. The post-#15 nginx-front re-root - (`https://matrix.''${services.hyperhive.domain}`) is tracked - separately in #609. + The `.well-known/matrix/{client,server}` delegation (served + on the bare hive-domain) advertises this sub-domain, so + clients opened at `http:///` auto-discover the + fluffychat root automatically. ''; }; @@ -407,24 +405,25 @@ in type = lib.types.package; default = fluffychat-web-fixed; defaultText = lib.literalMD '' - `pkgs.fluffychat-web` rebuilt with `--base-href /matrix/` (#634) - and patched via `postInstall` to add the three files - `flutter341.buildFlutterApplication` skips: `native_executor.js` - (compiled via `dart compile js` from `web/native_executor.dart`), - plus `Imaging.js` + `Imaging.wasm` (built from the - `native_imaging` dart package's C source via `pkgs.emscripten`). - See the `let` block in `nix/modules/hive-matrix.nix` for the - full rationale (#685). + `pkgs.fluffychat-web` patched via `postInstall` to add the + three files `flutter341.buildFlutterApplication` skips: + `native_executor.js` (compiled via `dart compile js` from + `web/native_executor.dart`), plus `Imaging.js` + + `Imaging.wasm` (built from the `native_imaging` dart + package's C source via `pkgs.emscripten`). See the `let` + block in `nix/modules/hive-matrix.nix` for the full + rationale (#685, #772). ''; description = '' - Static web client dist to serve at `/matrix/`. Defaults to - `pkgs.fluffychat-web` rebuilt with `--base-href "/matrix/"` - so relative asset paths resolve under the sub-path mount - (#634), plus a `postInstall` patch for #685's three missing - files. Override to swap for `hydrogen-web` (lightest), - `cinny` (no threads), `element-web` (heaviest, full - features), or an out-of-tree client dist — any replacement - also needs its `` aligned with the mount path. + Static web client dist to serve at the matrix sub-domain + root (`matrix.''${services.hyperhive.domain}/`). Defaults + to `pkgs.fluffychat-web` with the #685 `postInstall` patch + for the three missing files. Override to swap for + `hydrogen-web` (lightest), `cinny` (no threads), + `element-web` (heaviest, full features), or an out-of-tree + client dist — any replacement gets served at the sub-domain + root with the upstream-default ``, no + sub-path gymnastics needed. ''; }; };