Compare commits
2 changed files with 10 additions and 212 deletions
|
|
@ -6,28 +6,6 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
cfg = config.services.hyperhive.forge;
|
cfg = config.services.hyperhive.forge;
|
||||||
gatewayCfg = config.services.hyperhive.gateway;
|
|
||||||
hyperhiveDomain = config.services.hyperhive.domain;
|
|
||||||
|
|
||||||
# ROOT_URL forgejo advertises in clone links + outbound URLs. When
|
|
||||||
# served behind the gateway (#749 — mara verdict at issue:9609,
|
|
||||||
# sub-domain over sub-path), `cfg.domain` doubles as both the
|
|
||||||
# forgejo `DOMAIN` setting AND the gateway vhost server-name, so
|
|
||||||
# ROOT_URL just uses it directly (drops the port suffix when the
|
|
||||||
# gateway is on the canonical port 80). When direct (gateway off
|
|
||||||
# or `behindGateway = false`), keep the host:port shape so direct
|
|
||||||
# browser access on `:httpPort` still produces correct links.
|
|
||||||
# Operators can override via `cfg.rootUrl` for TLS / non-default
|
|
||||||
# gateway ports / bespoke shapes.
|
|
||||||
defaultRootUrl =
|
|
||||||
if cfg.behindGateway then
|
|
||||||
let
|
|
||||||
portSuffix = if gatewayCfg.port == 80 then "" else ":${toString gatewayCfg.port}";
|
|
||||||
in
|
|
||||||
"http://${cfg.domain}${portSuffix}/"
|
|
||||||
else
|
|
||||||
"http://${cfg.domain}:${toString cfg.httpPort}/";
|
|
||||||
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Private Forgejo for hyperhive agents, wrapped in a nixos-container
|
# Private Forgejo for hyperhive agents, wrapped in a nixos-container
|
||||||
|
|
@ -82,30 +60,13 @@ in
|
||||||
|
|
||||||
domain = lib.mkOption {
|
domain = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = if hyperhiveDomain != null then "forge.${hyperhiveDomain}" else "localhost";
|
default = "localhost";
|
||||||
defaultText = lib.literalExpression ''
|
example = "forge.internal";
|
||||||
if services.hyperhive.domain != null then
|
|
||||||
"forge.''${services.hyperhive.domain}"
|
|
||||||
else
|
|
||||||
"localhost"
|
|
||||||
'';
|
|
||||||
example = "git.example.com";
|
|
||||||
description = ''
|
description = ''
|
||||||
Public hostname for the forge. Doubles as both the forgejo
|
Hostname used in repo clone URLs the forge advertises. The
|
||||||
`DOMAIN` setting (clone URLs forgejo advertises) AND the
|
container shares host netns so `localhost` works for any
|
||||||
gateway vhost server-name when `behindGateway = true`
|
agent on the same host; set a real hostname when you want
|
||||||
(#749, mara verdict at issue:9609 — sub-domain over sub-path).
|
clones from outside the host to look canonical.
|
||||||
|
|
||||||
Defaults to `forge.''${services.hyperhive.domain}` when the
|
|
||||||
hive-domain is set (idiomatic sub-domain shape — `forge`
|
|
||||||
labelled under the hive's bare domain), falling back to
|
|
||||||
`localhost` otherwise (pre-#749 direct-on-port behaviour).
|
|
||||||
|
|
||||||
Set to a full hostname (`git.example.com`,
|
|
||||||
`forge.internal.lan`, etc.) for a bespoke vhost shape — the
|
|
||||||
full domain goes here, no separate sub-domain-label option
|
|
||||||
(mara on #754:9684 — "specify full forge domain in options
|
|
||||||
instead").
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -124,58 +85,6 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
behindGateway = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = gatewayCfg.enable or false;
|
|
||||||
defaultText = lib.literalExpression "config.services.hyperhive.gateway.enable";
|
|
||||||
description = ''
|
|
||||||
Serve forgejo through the hive-gateway nginx as a sub-domain
|
|
||||||
vhost (`server_name = cfg.domain`) instead of directly on
|
|
||||||
`httpPort` (#749, mara verdict at issue:9609 — sub-domain
|
|
||||||
over sub-path).
|
|
||||||
|
|
||||||
When `true`:
|
|
||||||
- The gateway adds a `server { server_name = ''${cfg.domain}; }`
|
|
||||||
block that proxies all `/` → `http://127.0.0.1:''${httpPort}/`.
|
|
||||||
- Forgejo's `ROOT_URL` flips to `http(s)://''${cfg.domain}/`
|
|
||||||
(sub-domain root, no port suffix when gateway is on 80).
|
|
||||||
- `gateway.localHostsEntry = true` extends `/etc/hosts` to
|
|
||||||
include `cfg.domain → 127.0.0.1` for local dev.
|
|
||||||
|
|
||||||
Defaults to `services.hyperhive.gateway.enable` — flipping
|
|
||||||
the gateway on/off auto-routes forge through it. Set `false`
|
|
||||||
explicitly to keep forge on the direct port even when the
|
|
||||||
gateway is running (e.g. an external git client that doesn't
|
|
||||||
traverse the gateway).
|
|
||||||
|
|
||||||
The mara-call on #749:9609 picks sub-domain over sub-path for
|
|
||||||
forge + matrix (both are external standard apps with sub-domain-
|
|
||||||
native config defaults). Per-agent UIs stay on sub-path
|
|
||||||
(`/agent/<name>/`) because they're hyperhive-internal +
|
|
||||||
already base-path-aware via #731.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
rootUrl = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.str;
|
|
||||||
default = null;
|
|
||||||
example = "https://forge.example.com/";
|
|
||||||
description = ''
|
|
||||||
Override the auto-derived forgejo `ROOT_URL`. When `null`
|
|
||||||
(default), `ROOT_URL` is derived from `cfg.domain` + gateway
|
|
||||||
state:
|
|
||||||
|
|
||||||
- `behindGateway = true` → `http://''${cfg.domain}/` (uses
|
|
||||||
`services.hyperhive.gateway.port` when non-80)
|
|
||||||
- `behindGateway = false` → `http://''${cfg.domain}:''${cfg.httpPort}/`
|
|
||||||
|
|
||||||
Set this to a fully-qualified URL when running behind TLS
|
|
||||||
termination (`https://...`), a non-default gateway port, or
|
|
||||||
a bespoke shape. Must end with `/` per forgejo's `ROOT_URL`
|
|
||||||
contract.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
openFirewall = lib.mkOption {
|
openFirewall = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
|
|
@ -199,51 +108,6 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = cfg.rootUrl == null || lib.hasSuffix "/" cfg.rootUrl;
|
|
||||||
message = ''
|
|
||||||
services.hyperhive.forge.rootUrl must end with "/". forgejo's
|
|
||||||
ROOT_URL contract requires a trailing slash for correct
|
|
||||||
relative-link generation; without it forgejo emits URLs like
|
|
||||||
`https://forge.example.com.user.id` instead of
|
|
||||||
`https://forge.example.com/user.id`. Got: ${toString cfg.rootUrl}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
|
||||||
# `cfg.domain` can't be empty — would render `.<hive>` shaped
|
|
||||||
# garbage as both server_name (nginx wildcard catch-all) and
|
|
||||||
# /etc/hosts entry (invalid). Default rejects this case (lands
|
|
||||||
# `"localhost"` when hive-domain is unset), but operator-set
|
|
||||||
# empty strings should fail loud.
|
|
||||||
assertion = cfg.domain != "";
|
|
||||||
message = ''
|
|
||||||
services.hyperhive.forge.domain = "" is rejected. The
|
|
||||||
rendered URLs would be invalid (nginx wildcard catch-all
|
|
||||||
for an empty server_name, /etc/hosts rejects empty entries).
|
|
||||||
Either leave at default (auto-derives to
|
|
||||||
"forge.<services.hyperhive.domain>" when set, else
|
|
||||||
"localhost"), or set a non-empty hostname like "forge.example.com"
|
|
||||||
or "git.internal".
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
|
||||||
# behindGateway requires the gateway module to actually be on.
|
|
||||||
# Otherwise the configured `ROOT_URL` flips to a sub-domain
|
|
||||||
# shape that has no nginx vhost backing it → broken on the
|
|
||||||
# rebuild.
|
|
||||||
assertion = !cfg.behindGateway || (gatewayCfg.enable or false);
|
|
||||||
message = ''
|
|
||||||
services.hyperhive.forge.behindGateway = true requires
|
|
||||||
services.hyperhive.gateway.enable = true (the gateway vhost
|
|
||||||
serving forge needs the gateway container to actually be
|
|
||||||
running). Either turn the gateway on, or set
|
|
||||||
services.hyperhive.forge.behindGateway = false to keep forge
|
|
||||||
on its direct port.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
containers.hive-forge = {
|
containers.hive-forge = {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ephemeral = false;
|
ephemeral = false;
|
||||||
|
|
@ -286,7 +150,7 @@ in
|
||||||
DEFAULT.APP_NAME = "HyperHive";
|
DEFAULT.APP_NAME = "HyperHive";
|
||||||
server = {
|
server = {
|
||||||
DOMAIN = cfg.domain;
|
DOMAIN = cfg.domain;
|
||||||
ROOT_URL = effectiveRootUrl;
|
ROOT_URL = "http://${cfg.domain}:${toString cfg.httpPort}/";
|
||||||
HTTP_PORT = cfg.httpPort;
|
HTTP_PORT = cfg.httpPort;
|
||||||
START_SSH_SERVER = true;
|
START_SSH_SERVER = true;
|
||||||
SSH_PORT = cfg.sshPort;
|
SSH_PORT = cfg.sshPort;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ let
|
||||||
cfg = config.services.hyperhive.gateway;
|
cfg = config.services.hyperhive.gateway;
|
||||||
hyperhiveDomain = config.services.hyperhive.domain;
|
hyperhiveDomain = config.services.hyperhive.domain;
|
||||||
matrixCfg = config.services.hyperhive.matrix;
|
matrixCfg = config.services.hyperhive.matrix;
|
||||||
forgeCfg = config.services.hyperhive.forge;
|
|
||||||
|
|
||||||
# Per-agent port table for `/agent/<name>/` routing (#15 v0). Single-
|
# Per-agent port table for `/agent/<name>/` routing (#15 v0). Single-
|
||||||
# sourced from `cfg.agentPortsFile` (default
|
# sourced from `cfg.agentPortsFile` (default
|
||||||
|
|
@ -238,8 +237,7 @@ in
|
||||||
"~*text/html" "/matrix/index.html";
|
"~*text/html" "/matrix/index.html";
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
virtualHosts = {
|
virtualHosts."_" = {
|
||||||
"_" = {
|
|
||||||
listen = [
|
listen = [
|
||||||
{
|
{
|
||||||
addr = "0.0.0.0";
|
addr = "0.0.0.0";
|
||||||
|
|
@ -395,54 +393,7 @@ 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).
|
|
||||||
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;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -451,25 +402,8 @@ in
|
||||||
allowedTCPPorts = [ cfg.port ];
|
allowedTCPPorts = [ cfg.port ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# `/etc/hosts` entries for local dev: the bare hive domain plus
|
|
||||||
# any sub-domain modules (forge, matrix-via-#751) 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>/` resolving locally.
|
|
||||||
#
|
|
||||||
# Forge's `cfg.domain` may equal `hyperhiveDomain` (e.g. operator
|
|
||||||
# set `forge.domain = "darkest.space"` matching the hive domain)
|
|
||||||
# — `lib.unique` collapses the duplicate so `/etc/hosts` doesn't
|
|
||||||
# carry the same entry twice.
|
|
||||||
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) {
|
||||||
"127.0.0.1" = lib.unique (
|
"127.0.0.1" = [ hyperhiveDomain ];
|
||||||
[ hyperhiveDomain ]
|
|
||||||
++ lib.optional (
|
|
||||||
(config.services.hyperhive.forge.enable or false)
|
|
||||||
&& (config.services.hyperhive.forge.behindGateway or false)
|
|
||||||
) config.services.hyperhive.forge.domain
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue