From 19c9f0c81570321b7d1ea1bddcaea9eff3ccf3e2 Mon Sep 17 00:00:00 2001 From: atlas Date: Tue, 18 Aug 2026 12:26:22 +0200 Subject: [PATCH] docs(#3255): record why the public URL is gated on the swarm UI mara asked whether SWARM_CONTROLLER_PUBLIC_URL should also be set when the controller does not run on the same host, and then said the two read as one service to her. Both are worth answering in the file rather than only in the thread: that split is not representable today, and the reason is two hops away from this line. The UI's /api/ location proxies http://unix: -- a path that resolves nowhere else -- and this daemon binds no TCP address at all. So the vhost and the daemon are co-located by construction, and ui.enable is the flag that declares the vhost rather than a guess about some host serving it. Also names the tripwire: the day the daemon grows a TCP listener, this line silently stops being right (env unset, registration quietly skipped, no error). That is when an explicit publicUrl option becomes correct -- not before, while there is exactly one derivable answer. --- nix/host-modules/swarm-controller.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index c249d3ad..fcc5fbfd 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -95,6 +95,20 @@ let # when this is set, and a hook whose `target_url` nothing answers is worse # than no hook at all — forgejo keeps the registration, marks every # delivery failed, and the hook still reads as configured. + # + # ⚠️ Correct ONLY while the vhost and this daemon share a host, and they do + # by construction: the UI's `/api/` location proxies + # `http://unix:`, a path that resolves nowhere else, and this + # daemon binds no TCP address at all (see the header). `swarm.ui.enable` + # therefore is not a guess about *some* host publishing the endpoint — it is + # the flag that declares that vhost, on the box holding the socket. + # + # ⇒ The day this daemon grows a TCP listener so another host can front it, + # THIS LINE SILENTLY STOPS BEING RIGHT: the env goes unset on the controller + # host, registration quietly does not happen, and nothing errors. That is + # the moment to add an explicit `publicUrl` option — not before, because + # until then there is exactly one derivable answer and an option would only + # be a second place to get it wrong. webhookEnv = lib.optionalAttrs uiCfg.enable { SWARM_CONTROLLER_PUBLIC_URL = "https://${uiCfg.domain}"; };