Files
jackos1998 d51f2d62b6 docs: Document the deployment
Add a top-level README mapping the boxes and a full docs/ tree: topic
pages (architecture, networking, deployment), per-site box pages for
colony and home with containers nested under their hosts, remote and
mobile boxes, the installer, and the home switch fabric reference
(folded in from home-switches.md, with AGENTS.md and code comments
retargeted to its new home). Box pages carry marked assignment tables
that CI regenerates from nixos.allAssignments.

AGENTS.md points at the new docs and keeps its terse agent version of
the mechanics, referring to the topic pages for depth.
2026-07-26 19:16:43 +01:00

6.8 KiB

middleman

The front-end reverse proxy for the colony's public web services — the single ingress that estuary DNATs HTTP/HTTPS (and Matrix federation on :8448) to. Terminates TLS with wildcard certificates it issues itself, provides nginx-sso for gated vhosts, and runs a librespeed backend.

  • Source: shill/containers/middleman/ (default.nix, vhosts.nix)
  • Host: NixOS container on shill (my.containers ephemeral nspawn on the ctrs bridge; bind-mounts /mnt/media read-only for the static file vhosts)

Role

  • nginx reverse proxy with the vts and fancyindex modules, Brotli, kTLS, a proxy cache, and a dynamic resolver pointed at estuary (proxyResolveWhileRunning) so upstreams re-resolve at runtime. All vhosts are declared in vhosts.nix; upstreams are addressed by their internal container/VM hostnames under ams1.int.nul.ie. nginx is made to wait for colony-psql (systemdAwaitPostgres) before starting — a hack for it getting stuck on DNS early in boot.
  • ACME — issues the wildcard certificates its own vhosts are served with (it is not a shared CA for the other boxes):
    • ams1.int.nul.ie + *.ams1.int.nul.ie via a lego exec DNS challenge that SSHes to pdns-file-records@estuary-vm to add/remove records in the internal PowerDNS zone. This is the default cert (useACMEHost) for vhosts on the internal domain.
    • nul.ie + *.nul.ie + *.s3.nul.ie via the Cloudflare DNS provider. A postRun hook scps the renewed key/cert to the mail (Mailcow) VM and runs mailcow-ssl-reload there.
    • Certs reload nginx on renewal; the acme group owns the secret files and nginx is a member.
  • nginx-sso — single sign-on at sso.nul.ie (one instance, generic), with Google OAuth as the default method and a simple username/password provider. Cookie domain is .nul.ie. SSO-gated vhosts below include server-generic.conf / location-generic.conf from /etc/nginx/includes/sso/.
  • librespeed — speed test, backend + frontend, published as speed.nul.ie and librespeed.ams1.int.nul.ie (both proxy to localhost:8989).

Network assignments

Name Assignment IPv4 IPv6 Domain Notes
middleman-ctr internal 10.100.2.2/24 gw 10.100.2.1 2a0e:97c0:4d2:12::2/64 ams1.int.nul.ie

The firewall allows http, https and 8448 (Matrix federation). A small nftables SNAT rule rewrites outbound IPv6 to the container's own address on host0.

Published vhosts

Everything is under *.nul.ie with the public wildcard cert unless noted; defaults applied to all vhosts are onlySSL, kTLS and HTTP/2. "SSO" = gated behind nginx-sso (generic instance).

Host Upstream Notes
nul.ie (_, default) static landing page (index.html, CV PDF, SSH pubkey); serves Matrix .well-knowns and redirects webfinger/nodeinfo/host-metatoot.nul.ie, atproto-didpds.nul.ie; forceSSL (plain HTTP redirects to HTTPS)
localhost loopback-only VTS status page at /status (scraped by netdata); plain HTTP
sso.nul.ie localhost:8082 nginx-sso endpoint
netdata-colony.nul.ie <host>.ams1.int.nul.ie:19999 netdata fan-out over vm, fw, ctr, oci, http, jackflix-ctr, chatterbox-ctr, colony-psql-ctr; SSO
pass.nul.ie vaultwarden-ctr:8080 vaultwarden; /notifications/hub proxied with websockets
matrix.nul.ie chatterbox-ctr:8008 chatterbox Synapse client + federation; also listens on :8448 as federation default_server; = / redirects to Element; serves Matrix .well-knowns
element.nul.ie static element-web Element configured for the nul.ie homeserver
torrents.nul.ie jackflix-ctr:9091 Transmission (jackflix); SSO
jackett.nul.ie jackflix-ctr:9117 SSO
radarr.nul.ie jackflix-ctr:7878 SSO; websockets
sonarr.nul.ie jackflix-ctr:8989 SSO; websockets
gib.nul.ie jackflix-ctr:5055 Jellyseerr requests
jackflix.nul.ie jackflix-ctr:8096 Jellyfin; /socket websockets; / redirects to /web/
toot.nul.ie toot-ctr:80 Mastodon — upstream currently disabled, see toot
pds.nul.ie toot-ctr:3000 Bluesky PDS (toot); websockets
share.nul.ie object-ctr:9090 Sharry (object); websockets
stuff.nul.ie jackflix-ctr:3923 copyparty
public.nul.ie (+ alias p.nul.ie) static /mnt/media/public fancyindex file listing; addSSL so plain HTTP also works
mc-map.nul.ie simpcraft-oci:8100 Minecraft map (OCI container on whale2)
mc-rail.nul.ie simpcraft-oci:3876 Minecraft railway map
mc-map-kink.nul.ie kinkcraft-oci:8100 Minecraft map
speed.nul.ie localhost:8989 librespeed
librespeed.ams1.int.nul.ie localhost:8989 librespeed on the internal domain (internal wildcard cert)
md.nul.ie object-ctr:3000 HedgeDoc; websockets
pb.nul.ie object-ctr:8088 wastebin
photos.nul.ie jackflix-ctr:2342 PhotoPrism; websockets
pront.nul.ie stream-hi.h.nul.ie:5000 OctoPrint on the home network; /webcam/:5050; SSO
hass.nul.ie hass-ctr.h.nul.ie:8123 Home Assistant (home network); websockets
hass-john.nul.ie john-valorant-tun.ams1.int.nul.ie:8123 remote Home Assistant over the point-to-point tunnel; websockets
minio.nul.ie object-ctr:9001 MinIO console; /ws websockets
s3.nul.ie (+ *.s3.nul.ie) object-ctr:9000 MinIO S3 API (virtual-host style via the *.s3 wildcard cert); /gitea/packages/ has a hack forcing the correct Content-Type for Docker image manifests
nix-cache.nul.ie object-ctr:5000 Harmonia Nix binary cache; .narinfo/nar//serve/ paths get immutable Cache-Control/Expires headers

Notable config files