diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index c1bb2158..05b19f62 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -51,17 +51,6 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { ); } tracing::info!(static_dir = %static_dir.display(), "dashboard static dir resolved"); - // Optional matrix-GUI static dist (#607). When `HIVE_MATRIX_GUI_DIR` - // is set and the dir exists, mount it at `/matrix/` as a sibling - // ServeDir before the SPA fallback. Pre-#15 (nginx front) this - // is the simplest way to put fluffychat-web on the same origin as - // the dashboard so the operator has matrix chat one tab away. - let matrix_gui_dir: Option = std::env::var_os("HIVE_MATRIX_GUI_DIR") - .map(PathBuf::from) - .filter(|p| p.is_dir()); - if let Some(dir) = &matrix_gui_dir { - tracing::info!(matrix_gui_dir = %dir.display(), "matrix GUI mount enabled at /matrix"); - } let app = Router::new() .route("/api/state", get(api_state)) .route("/approve/{id}", post(post_approve)) @@ -103,14 +92,6 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { // /static/dashboard.css → dist/static/dashboard.css, etc.). .fallback_service(ServeDir::new(&static_dir)) .with_state(AppState { coord }); - // Sibling static mount for the optional matrix GUI dist. Layered - // before the SPA fallback so `/matrix/...` resolves there instead - // of falling through to the dashboard. - let app = if let Some(dir) = matrix_gui_dir { - app.nest_service("/matrix", ServeDir::new(dir)) - } else { - app - }; let addr = SocketAddr::from(([0, 0, 0, 0], port)); let listener = bind_with_retry(addr).await?; tracing::info!(%port, "dashboard listening"); @@ -242,10 +223,13 @@ struct StateSnapshot { /// links each container's config + each approval's commit into the /// forge's `agent-configs` repos. forge_present: bool, - /// Whether the matrix GUI static dist is mounted at `/matrix/` - /// (`HIVE_MATRIX_GUI_DIR` set + dir exists). Drives the dashboard's - /// `M4TR1X →` tab-strip entry so it doesn't flash a dead link when - /// the operator hasn't opted in (#607). + /// Whether the matrix GUI is reachable at `/matrix/`. Sourced from + /// `HIVE_MATRIX_GUI_ENABLED` env var (set by the c0re NixOS module + /// when `services.hyperhive.matrix.gui.enable` is on). The gateway + /// (hive-gateway.nix) does the actual `/matrix/` static serving; + /// this flag is just an availability signal for iris's dashboard + /// chrome so the `M4TR1X →` tab doesn't flash when the GUI is off + /// (#607, #634). matrix_gui_enabled: bool, } @@ -439,9 +423,13 @@ async fn api_state(headers: HeaderMap, State(state): State) -> axum::J port_conflicts, rebuild_queue: state.coord.rebuild_queue.snapshot(), forge_present: crate::forge::is_present().await, - matrix_gui_enabled: std::env::var_os("HIVE_MATRIX_GUI_DIR") - .map(PathBuf::from) - .is_some_and(|p| p.is_dir()), + matrix_gui_enabled: std::env::var_os("HIVE_MATRIX_GUI_ENABLED").is_some_and(|v| { + // Accept any truthy string ("1", "true", "yes") since the + // env var is set by NixOS module wiring with the literal + // "1"; defensive parse so manual overrides also work. + let s = v.to_string_lossy().to_ascii_lowercase(); + matches!(s.as_str(), "1" | "true" | "yes") + }), }) } diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index c5aeacce..9492f015 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -241,6 +241,15 @@ in # Agents poll this URL for Forgejo notifications. Derived from # services.hyperhive.forge.{domain,httpPort} so it tracks forge config changes. HIVE_FORGE_URL = "http://${config.services.hyperhive.forge.domain}:${toString config.services.hyperhive.forge.httpPort}"; + } + // lib.optionalAttrs config.services.hyperhive.matrix.gui.enable { + # Availability flag for `/api/state.matrix_gui_enabled`. The + # gateway (hive-gateway.nix) does the actual static serving of + # fluffychat-web at `/matrix/`; c0re doesn't host the dist + # itself (#634, mara on PR #620). This env var just tells the + # dashboard chrome whether the GUI is reachable so iris's + # `M4TR1X →` tab doesn't show when the GUI is off. + HIVE_MATRIX_GUI_ENABLED = "1"; }; # Matrix GUI static serving lives entirely on the hive-gateway # nginx since #609 — when gateway is off the operator opts out diff --git a/nix/modules/hive-matrix.nix b/nix/modules/hive-matrix.nix index 3b0a820d..fc9087b5 100644 --- a/nix/modules/hive-matrix.nix +++ b/nix/modules/hive-matrix.nix @@ -183,34 +183,30 @@ in type = lib.types.package; default = pkgs.fluffychat-web.overrideAttrs (old: { # fluffychat-web ships with `` baked into its - # index.html — that's the placeholder flutter would replace - # via `--base-href` at build time. Since hyperhive serves the - # dist at `/matrix/` (not the document root), the literal - # `` would resolve every relative asset path - # (`Imaging.js`, `flutter.js`, `splash/*`, etc.) against the - # ROOT of the origin, leading to 404s on every load (#634). - # Patch the placeholder to `` at install - # time so the SPA boots correctly under the sub-path mount. - # If/when we move to subdomain routing (#609 − - # `matrix.${hyperhiveDomain}`), drop this override and revert - # to upstream `pkgs.fluffychat-web` — it'll be at the root - # again. - postInstall = (old.postInstall or "") + '' - ${pkgs.gnused}/bin/sed -i \ - 's|||' \ - $out/index.html - ''; + # index.html — flutter's `--base-href` build flag replaces + # that placeholder. Default upstream build is `--base-href "/"` + # which is wrong for hyperhive's `/matrix/` sub-path mount: + # the browser resolves relative asset paths (`Imaging.js`, + # `flutter.js`, `splash/*`) against the document ROOT, + # producing 404s for every asset (#634). Inject the right + # base-href into the build via nixpkgs's `buildFlutterApplication` + # targetFlags. When subdomain routing lands (#609 — + # `matrix.${hyperhiveDomain}`), drop this override; upstream's + # `/` base-href is correct at the root of a dedicated subdomain. + targetFlags = (old.targetFlags or [ ]) ++ [ + "--base-href" + "/matrix/" + ]; }); - defaultText = lib.literalExpression "pkgs.fluffychat-web (patched for /matrix/ base href)"; + defaultText = lib.literalExpression "pkgs.fluffychat-web with --base-href /matrix/"; description = '' Static web client dist to serve at `/matrix/`. Defaults to - `pkgs.fluffychat-web` (with the `` patched to - `/matrix/` so relative asset paths resolve under the - sub-path mount, #634). 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. + `pkgs.fluffychat-web` rebuilt with `--base-href "/matrix/"` + so relative asset paths resolve under the sub-path mount + (#634). 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. ''; }; };