argus picked option (a) on #653: put the upgrade note in each option's `description` so it shows up in `nix flake show` + the rendered options docs, right next to the option itself. cheapest option, no eval-time noise (a `warnings` block would fire on every new deployment that wants false — the normal case now). Appended a `**Breaking change as of #651**` paragraph to each of the three `openFirewall` descriptions, naming the exact option string the operator needs to set to restore the old behaviour. Gateway's note specifically calls out that external reach is the common case (operator's primary entry point), so the upgrade hint is most likely needed there.
198 lines
7.3 KiB
Nix
198 lines
7.3 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.hyperhive.gateway;
|
|
hyperhiveDomain = config.services.hyperhive.domain;
|
|
matrixCfg = config.services.hyperhive.matrix;
|
|
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.
|
|
|
|
options.services.hyperhive.gateway = {
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Run hive-gateway — a single nginx in front of every hyperhive
|
|
surface. On by default: the gateway hosts the matrix GUI static
|
|
dist (when `services.hyperhive.matrix.gui.enable` is true) and
|
|
proxies everything else to hive-c0re's dashboard upstream. Set
|
|
`services.hyperhive.gateway.enable = false` to bypass nginx
|
|
entirely and reach hive-c0re directly on its dashboard port
|
|
(7000 by default).
|
|
|
|
v0 is HTTP-only; TLS / public-domain shape is tracked
|
|
separately.
|
|
'';
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 80;
|
|
example = 8080;
|
|
description = ''
|
|
TCP port the gateway listens on. Default 80 (canonical web
|
|
port). nginx inside the container binds <1024 because the
|
|
container's init runs as root; if 80 is already taken on the
|
|
host (existing nginx, traefik, etc.) override to an unused
|
|
port like 8080 or move the conflicting service.
|
|
'';
|
|
};
|
|
|
|
upstreamHost = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "127.0.0.1";
|
|
description = ''
|
|
Host the gateway proxies non-static requests to. Defaults to
|
|
`127.0.0.1` because the gateway container shares the host
|
|
netns, so loopback resolves directly to hive-c0re.
|
|
'';
|
|
};
|
|
|
|
upstreamPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 7000;
|
|
description = ''
|
|
TCP port the gateway proxies non-static requests to. Defaults
|
|
to `7000` (hive-c0re's out-of-the-box dashboard port). Operators
|
|
who change `services.hyperhive.c0re.dashboardPort` should set
|
|
`upstreamPort` to match — kept as a hardcoded default rather
|
|
than a cross-reference to keep this module's options eval
|
|
independent of c0re's option tree shape.
|
|
'';
|
|
};
|
|
|
|
openFirewall = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Open `port` in the host firewall. Off by default (#651,
|
|
secure-by-default). Flip to `true` to expose the gateway to
|
|
the operator's browser / external clients — required for any
|
|
out-of-host reach, since the agents themselves talk to
|
|
hive-c0re via the per-agent unix sockets and don't need the
|
|
nginx vhost. Leave off when running behind another reverse
|
|
proxy (e.g. caddy / traefik on the host) that handles TLS
|
|
termination + forwards to `port`.
|
|
|
|
**Breaking change as of #651**: this used to default to
|
|
`true`. If you relied on the old default for external reach
|
|
(the common case — the gateway is the operator's primary
|
|
entry point), add `services.hyperhive.gateway.openFirewall = true;`
|
|
to your host config before rebuilding.
|
|
'';
|
|
};
|
|
|
|
localHostsEntry = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Add an `/etc/hosts` entry mapping `services.hyperhive.domain`
|
|
to `127.0.0.1` on the host. Useful for local deployments +
|
|
tests where there's no real DNS for `services.hyperhive.domain`
|
|
but the operator (or browser-based tests) want to hit
|
|
`http://''${services.hyperhive.domain}` to exercise the
|
|
gateway shape. Off by default — operators running with real
|
|
DNS shouldn't have a stale `/etc/hosts` entry sticking
|
|
around. Requires `services.hyperhive.domain` to be set.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = !cfg.localHostsEntry || hyperhiveDomain != null;
|
|
message = ''
|
|
services.hyperhive.gateway.localHostsEntry = true requires
|
|
services.hyperhive.domain to be set. Either pin a hostname
|
|
or leave `localHostsEntry` at its default of false.
|
|
'';
|
|
}
|
|
];
|
|
|
|
containers.hive-gateway = {
|
|
autoStart = true;
|
|
ephemeral = false;
|
|
# Share host netns — nginx then binds host-level ports directly,
|
|
# `localhost` upstream resolution reaches hive-c0re without any
|
|
# port-forward dance, and the firewall config below is the only
|
|
# layer that matters.
|
|
privateNetwork = false;
|
|
config =
|
|
{ pkgs, ... }:
|
|
{
|
|
system.stateVersion = "26.05";
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedOptimisation = true;
|
|
virtualHosts."_" = {
|
|
listen = [
|
|
{
|
|
addr = "0.0.0.0";
|
|
port = cfg.port;
|
|
}
|
|
];
|
|
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/`. fluffychat is a SPA
|
|
# — fall back to its index.html on deep links.
|
|
lib.optionalAttrs (matrixCfg.enable && matrixCfg.gui.enable) {
|
|
"/matrix/" = {
|
|
alias = "${matrixCfg.gui.package}/";
|
|
extraConfig = ''
|
|
try_files $uri $uri/ /matrix/index.html;
|
|
'';
|
|
};
|
|
}
|
|
// {
|
|
# Everything else proxies to hive-c0re. Upgrade
|
|
# headers stay set so SSE (`/dashboard/stream`,
|
|
# `/events/stream`) + websocket (`/screen/ws`)
|
|
# endpoints keep working transparently.
|
|
"/" = {
|
|
proxyPass = "http://${cfg.upstreamHost}:${toString cfg.upstreamPort}";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_buffering off;
|
|
proxy_read_timeout 1d;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
networking.firewall = lib.mkIf cfg.openFirewall {
|
|
allowedTCPPorts = [ cfg.port ];
|
|
};
|
|
|
|
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
|
"127.0.0.1" = [ hyperhiveDomain ];
|
|
};
|
|
};
|
|
}
|