Defensive against the fresh-boot-before-any-agent-spawn window where the bind-mount source wouldn't exist from c0re's per-agent `set_nspawn_flags` mkdir chain yet. nspawn auto-creates missing sources, so this is intent-explicit rather than functional fix.
576 lines
25 KiB
Nix
576 lines
25 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.hyperhive.gateway;
|
|
hyperhiveDomain = config.services.hyperhive.domain;
|
|
matrixCfg = config.services.hyperhive.matrix;
|
|
forgeCfg = config.services.hyperhive.forge;
|
|
|
|
# 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
|
|
{ }
|
|
else
|
|
builtins.fromJSON (builtins.readFile cfg.agentPortsFile);
|
|
|
|
# Per-agent unix-socket table for `/agent/<name>/` UDS upstream
|
|
# (#784 phase 2 step 3). C0re writes this JSON alongside
|
|
# agent-ports.json; gateway reads at deploy time. Per-agent the
|
|
# entry wins over the TCP port. Missing entry (or missing file)
|
|
# → fall back to the TCP port. See
|
|
# `docs/gateway.md::Per-agent UDS upstream (#784)`.
|
|
agentSocketsTable =
|
|
if cfg.agentSocketsFile == null || !builtins.pathExists cfg.agentSocketsFile then
|
|
{ }
|
|
else
|
|
builtins.fromJSON (builtins.readFile cfg.agentSocketsFile);
|
|
|
|
# Resolve a per-agent upstream URL. Socket entry wins ONLY when the
|
|
# socket file actually exists at eval time — guards against agents
|
|
# that have an `agent-sockets.json` entry from c0re's blanket emit
|
|
# but haven't actually flipped `hyperhive.web.useUnixSocket = true`
|
|
# (their harness still binds TCP only, so a UDS upstream would 502).
|
|
# Falls back to the TCP loopback otherwise. Once c0re ships the
|
|
# `.bound` marker filter (#784 step 2d follow-up), the path-exists
|
|
# check becomes redundant but harmless; step 4 drops it entirely.
|
|
agentUpstreamFor =
|
|
name: port:
|
|
if agentSocketsTable ? ${name} && builtins.pathExists agentSocketsTable.${name} then
|
|
"http://unix:${agentSocketsTable.${name}}:/"
|
|
else
|
|
"http://127.0.0.1:${toString port}/";
|
|
|
|
# Static error pages for `/agent/<name>/` mishaps (#755). Mara's
|
|
# call: useful pages instead of nginx's default 404/502 for routes
|
|
# we've already special-cased. See `docs/gateway.md::Per-agent
|
|
# error pages` for the design rationale + page-vs-status semantics.
|
|
agentErrorPagesDir = pkgs.runCommand "hyperhive-agent-error-pages" { } ''
|
|
mkdir -p $out
|
|
cat > $out/not-found.html <<'EOF'
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>agent not found ◆ hyperhive</title>
|
|
<style>
|
|
body { background: #1e1e2e; color: #cdd6f4; font: 14px/1.5 -apple-system, system-ui, sans-serif; margin: 0; padding: 4rem 1rem; text-align: center; }
|
|
h1 { color: #cba6f7; font-size: 1.5rem; margin: 0 0 0.5rem; }
|
|
p { max-width: 32rem; margin: 0.5rem auto; color: #a6adc8; }
|
|
code { background: #313244; color: #f5c2e7; padding: 0.1rem 0.35rem; border-radius: 0.2rem; }
|
|
a { color: #89b4fa; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>◆ agent not found</h1>
|
|
<p>No agent matches the requested <code>/agent/<name>/</code> path on this hive.</p>
|
|
<p>Operator: check the agent name in <a href="/">the dashboard</a> — the gateway picks up new agents on the next <code>nixos-rebuild switch</code>.</p>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
cat > $out/unreachable.html <<'EOF'
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>agent unreachable ◆ hyperhive</title>
|
|
<style>
|
|
body { background: #1e1e2e; color: #cdd6f4; font: 14px/1.5 -apple-system, system-ui, sans-serif; margin: 0; padding: 4rem 1rem; text-align: center; }
|
|
h1 { color: #f9e2af; font-size: 1.5rem; margin: 0 0 0.5rem; }
|
|
p { max-width: 32rem; margin: 0.5rem auto; color: #a6adc8; }
|
|
code { background: #313244; color: #f5c2e7; padding: 0.1rem 0.35rem; border-radius: 0.2rem; }
|
|
a { color: #89b4fa; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>◆ agent unreachable</h1>
|
|
<p>The agent's harness web server isn't responding. Container restarting, or the agent crashed.</p>
|
|
<p>Operator: <a href="/">dashboard</a> → check the container status / journal; the page will recover on retry once the harness is back up.</p>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
'';
|
|
in
|
|
{
|
|
# 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 {
|
|
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.
|
|
'';
|
|
};
|
|
|
|
agentPortsFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = "/var/lib/hyperhive/agent-ports.json";
|
|
example = "/var/lib/hyperhive/agent-ports.json";
|
|
description = ''
|
|
Path to a JSON file mapping sub-agent names to their web ports
|
|
for `/agent/<name>/` routing through the gateway (#15 v0).
|
|
Shape: `{ "<name>": <port>, ... }`. Written by hive-c0re on
|
|
every topology change (the rust side knows the canonical port
|
|
allocation via `lifecycle::agent_web_port`; the gateway just
|
|
reads what it's told).
|
|
|
|
For each `<name>: <port>` entry, the gateway adds a
|
|
`location /agent/<name>/` block that `proxy_pass`es to
|
|
`http://127.0.0.1:<port>/`. Empty / missing file → no
|
|
per-agent routes generated → gateway falls back to its pre-#15
|
|
shape (just `/` + matrix surfaces).
|
|
|
|
**Purely additive**: the old `http://<host>:<port>/` direct
|
|
reach keeps working in parallel; this just gives the operator
|
|
a single-origin route. Manager isn't included in the map (no
|
|
per-agent prefix needed; manager already gets the `/` route
|
|
via the c0re upstream block).
|
|
|
|
Set to `null` to disable per-agent routing entirely without
|
|
creating the file. Set to a custom path if the operator's c0re
|
|
writes the table elsewhere.
|
|
|
|
**Rebuild trigger**: the gateway container picks up new entries
|
|
on the next `nixos-rebuild switch` (or `hivectl gateway-sync`
|
|
if that helper lands). c0re writes are not auto-applied to a
|
|
running gateway — see the follow-up in #15 for runtime nginx
|
|
include + reload + eventual per-agent unix sockets.
|
|
'';
|
|
};
|
|
|
|
agentSocketsFile = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.path;
|
|
default = "/var/lib/hyperhive/agent-sockets.json";
|
|
example = "/var/lib/hyperhive/agent-sockets.json";
|
|
description = ''
|
|
Path to a JSON file mapping sub-agent names to their
|
|
per-agent unix-socket paths for `/agent/<name>/` UDS upstream
|
|
routing (#784 phase 2 step 3). Shape:
|
|
`{ "<name>": "/run/hive-agent/<name>/web.sock", ... }`.
|
|
Written by hive-c0re alongside `agentPortsFile` on every
|
|
topology change (`hive_c0re::agent_sockets::write`;
|
|
path-shape derives from
|
|
`agent_sockets::socket_path_for(name)`).
|
|
|
|
Per-agent, the socket entry wins over the TCP port: when an
|
|
agent appears in this map, the gateway's `proxy_pass` for
|
|
that agent's `/agent/<name>/` location targets
|
|
`http://unix:<path>:/` instead of `http://127.0.0.1:<port>/`.
|
|
Agents that haven't opted in (no `HIVE_WEB_SOCKET` set,
|
|
no entry in the JSON, or both files unset) fall back to
|
|
TCP via `agentPortsFile`. Coexists with the TCP map during
|
|
the rollout — eventually drops `agentPortsFile` entirely
|
|
when every agent's flipped (#784 step 4).
|
|
|
|
Set to `null` to skip UDS upstreams entirely (gateway uses
|
|
TCP for every agent regardless of what hive-c0re writes).
|
|
|
|
**Bind-mount requirement**: when this is enabled the gateway
|
|
container needs `/run/hive-agent/` bind-mounted from the
|
|
host. Handled automatically by `containers.hive-gateway`
|
|
below when at least one socket entry exists.
|
|
'';
|
|
};
|
|
};
|
|
|
|
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.
|
|
'';
|
|
}
|
|
];
|
|
|
|
# Ensure the per-agent UDS bind-mount source exists at host boot,
|
|
# before the gateway container's first start. nspawn would
|
|
# auto-create an empty dir if missing (argus 🟡 on #829), but a
|
|
# tmpfiles rule makes the intent explicit and dodges the
|
|
# fresh-boot-before-any-agent-spawn window where the dir wouldn't
|
|
# exist yet from c0re's per-agent `set_nspawn_flags` mkdir chain.
|
|
systemd.tmpfiles.rules = [
|
|
"d /run/hive-agent 0755 root root - -"
|
|
];
|
|
|
|
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;
|
|
# Bind-mount the per-agent socket dir so nginx inside the gateway
|
|
# container can `connect(2)` to the UDS upstreams hive-c0re
|
|
# publishes in `agent-sockets.json` (#784 phase 2 step 3).
|
|
# Read-only (we don't bind anything here; just connect). Mount
|
|
# is unconditional but inert when no agents have opted in:
|
|
# agent-sockets.json missing/empty → `agentSocketsTable = {}`
|
|
# → every per-agent location uses the TCP fallback.
|
|
bindMounts."/run/hive-agent" = {
|
|
hostPath = "/run/hive-agent";
|
|
isReadOnly = true;
|
|
};
|
|
config =
|
|
{ pkgs, ... }:
|
|
{
|
|
system.stateVersion = "26.05";
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedOptimisation = true;
|
|
# 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";
|
|
"~*text/html" "/index.html";
|
|
}
|
|
'';
|
|
virtualHosts = {
|
|
"_" = {
|
|
listen = [
|
|
{
|
|
addr = "0.0.0.0";
|
|
port = cfg.port;
|
|
}
|
|
];
|
|
locations =
|
|
# `<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 && matrixCfg.gatewayHost != null) (
|
|
let
|
|
portSuffix = if cfg.port == 80 then "" else ":${toString cfg.port}";
|
|
target = "http://${matrixCfg.gatewayHost}${portSuffix}";
|
|
in
|
|
{
|
|
"/matrix/" = {
|
|
extraConfig = ''
|
|
rewrite ^/matrix/(.*)$ ${target}/$1 permanent;
|
|
'';
|
|
};
|
|
}
|
|
)
|
|
//
|
|
# `.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
|
|
portSuffix = if cfg.port == 80 then "" else ":${toString cfg.port}";
|
|
clientBaseUrl =
|
|
if matrixCfg.gatewayHost != null then
|
|
"http://${matrixCfg.gatewayHost}${portSuffix}"
|
|
else
|
|
"http://${hyperhiveDomain}:${toString matrixCfg.httpPort}";
|
|
serverHostPort =
|
|
if matrixCfg.gatewayHost != null then
|
|
"${matrixCfg.gatewayHost}${portSuffix}"
|
|
else
|
|
"${hyperhiveDomain}:${toString matrixCfg.httpPort}";
|
|
in
|
|
{
|
|
"= /.well-known/matrix/client" = {
|
|
extraConfig = ''
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
return 200 '{"m.homeserver":{"base_url":"${clientBaseUrl}"}}';
|
|
'';
|
|
};
|
|
"= /.well-known/matrix/server" = {
|
|
extraConfig = ''
|
|
default_type application/json;
|
|
return 200 '{"m.server":"${serverHostPort}"}';
|
|
'';
|
|
};
|
|
}
|
|
)
|
|
//
|
|
# Per-agent UIs (#15 v0; UDS upstream #784 step 3).
|
|
# One `/agent/<name>/` block per entry in
|
|
# `agentPortsTable`. `agentUpstreamFor` resolves
|
|
# to `http://unix:<path>:/` when the agent has
|
|
# opted in via `hyperhive.web.useUnixSocket` (and
|
|
# appears in `agentSocketsTable`); otherwise
|
|
# `http://127.0.0.1:<port>/`. Trailing-slash pair
|
|
# strips the prefix; `X-Forwarded-Prefix` lets the
|
|
# harness build absolute URLs when relative isn't
|
|
# enough. `proxy_intercept_errors` + `error_page` rewrite
|
|
# upstream 502/503/504 to `unreachable.html` (#755).
|
|
lib.mapAttrs' (name: port: {
|
|
name = "/agent/${name}/";
|
|
value = {
|
|
proxyPass = agentUpstreamFor name port;
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_set_header X-Forwarded-Prefix /agent/${name};
|
|
proxy_buffering off;
|
|
proxy_read_timeout 1d;
|
|
proxy_intercept_errors on;
|
|
error_page 502 503 504 = /__hive_agent_unreachable;
|
|
'';
|
|
};
|
|
}) agentPortsTable
|
|
//
|
|
# `/agent/` catch-all (#755): hits when an operator
|
|
# requests `/agent/<unknown>/...` — a name not in
|
|
# `agentPortsTable`. Without this it falls through to
|
|
# `/` (c0re dashboard upstream) which returns 404
|
|
# with no useful context. Custom 404 page instead.
|
|
{
|
|
"/agent/" = {
|
|
extraConfig = ''
|
|
error_page 404 = /__hive_agent_not_found;
|
|
return 404;
|
|
'';
|
|
};
|
|
# Internal static-file locations the error_page
|
|
# directives above point at. `internal` keeps
|
|
# operators from hitting the file directly (only
|
|
# nginx's error-handling can reach it); `alias`
|
|
# serves the exact file regardless of request URI.
|
|
"= /__hive_agent_not_found" = {
|
|
extraConfig = ''
|
|
internal;
|
|
alias ${agentErrorPagesDir}/not-found.html;
|
|
default_type text/html;
|
|
'';
|
|
};
|
|
"= /__hive_agent_unreachable" = {
|
|
extraConfig = ''
|
|
internal;
|
|
alias ${agentErrorPagesDir}/unreachable.html;
|
|
default_type text/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;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
}
|
|
//
|
|
# 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 = [
|
|
{
|
|
addr = "0.0.0.0";
|
|
port = cfg.port;
|
|
}
|
|
];
|
|
locations."/" = {
|
|
proxyPass = "http://127.0.0.1:${toString forgeCfg.httpPort}/";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
proxy_buffering off;
|
|
client_max_body_size 1G;
|
|
proxy_read_timeout 1h;
|
|
proxy_send_timeout 1h;
|
|
'';
|
|
};
|
|
};
|
|
}
|
|
//
|
|
# 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 = [
|
|
{
|
|
addr = "0.0.0.0";
|
|
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 *;
|
|
'';
|
|
};
|
|
}
|
|
// lib.optionalAttrs (matrixCfg.gui.enable) (
|
|
{
|
|
# fluffychat at sub-domain root, SPA-fallback via
|
|
# the Accept-header `$matrix_spa_target` map.
|
|
"/" = {
|
|
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";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
networking.firewall = lib.mkIf cfg.openFirewall {
|
|
allowedTCPPorts = [ cfg.port ];
|
|
};
|
|
|
|
# `/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 ]
|
|
++ lib.optional (
|
|
(config.services.hyperhive.forge.enable or false)
|
|
&& (config.services.hyperhive.forge.behindGateway or false)
|
|
) config.services.hyperhive.forge.domain
|
|
++ lib.optional (matrixCfg.enable && matrixCfg.gatewayHost != null) matrixCfg.gatewayHost
|
|
);
|
|
};
|
|
};
|
|
}
|