hyperhive/nix/host-modules/hive-gateway/vhost-lib.nix
iris 07b62612b0 docs: restructure into topic subdirectories, collapse duplicated index
Per mara's go-ahead on hyperhive#3902 ("getting started is good, but
terminal rendering does not go in there i think"):

Moved 21 top-level docs/*.md files into 7 new topic subdirectories
(existing web-ui/, turn-loop/, swarm/, tools/, crates/ untouched):
  getting-started/  setup.md
  agent-lifecycle/  agent-hierarchy.md, approvals.md, persistence.md
  trust-boundary/   boundary.md, security.md
  integrations/     forge.md, matrix.md, github.md, knowledge.md
  networking/       gateway.md, network.md, snapshot-store.md
  scheduler/        jobq.md, coordinator.md, ci.md, observability.md
  process/          conventions.md, gotchas.md, pr-review-gate.md
  web-ui/           terminal-rendering.md (moved into the EXISTING dir,
                    per mara's correction to the original getting-started
                    guess -- it's UI implementation detail, not onboarding)

The physical layout now matches docs/README.md's own topical headers,
which already amounted to this taxonomy -- see the scoping comment on
the issue for the two findings that motivated this (a genuine
duplication between CLAUDE.md's old "Reading paths" list and
docs/README.md's grouped one, since drifted out of sync with each
other; and the flat layout not matching the grouping we already had).

Fixed every cross-reference this moved across the whole repo (~120
files: docs/ internal links at every depth, Rust doc comments, nix
module option docs, crate READMEs) -- verified two ways: a grep sweep
confirming zero remaining references to any old path, and a script
that resolves every markdown link in docs/**/*.md + CLAUDE.md +
README.md against the filesystem and reports anything that doesn't
exist (zero broken links).

Collapsed CLAUDE.md's "Reading paths" section (the duplicate) down to
a pointer at docs/README.md, now the single index. Rewrote
docs/README.md itself to use the new subdirectory paths and added the
one doc it was missing that CLAUDE.md's old copy had (pr-review-gate.md).

Classified all 22 docs/*.md files first via a haiku subagent (mara's
suggestion) on two axes -- proposed grouping and operator-vs-
implementation focus -- before finalizing the taxonomy; spot-checked
the report and found internal inconsistencies (its classification
table disagreed with its own summary section for a few files), so this
taxonomy is my original proposal + the one correction mara gave
directly, not a blind application of the subagent's table. The
operator-focus data it gathered is still useful for a follow-up
content pass (docs skewing 'mixed' rather than pure operator-facing),
not addressed in this PR -- structure only.

nix fmt clean, both pre-push lints clean.
2026-09-02 01:55:37 +02:00

131 lines
5.2 KiB
Nix

# The gateway's vhost construction kit: the listen set, the per-name TLS
# attrs, and the security headers every vhost in front of this gateway
# needs.
#
# Split out of ./vhosts.nix so it has one home and two consumers. Today
# only ./vhosts.nix reads it; it is published as
# `services.hyperhive.gateway.lib` (see ./options.nix) so a service
# module can declare its OWN vhost without the gateway having to know
# that service by name. `vhosts.nix` reads the published value rather
# than calling this file directly — one source, not a copy that agrees
# by inspection.
#
# Pure function of the gateway's config + resolved cert paths; returns
# an attrset, evaluates no options itself. Everything here is *gateway*
# knowledge — which port pair to bind, which issuer covers a name — and
# stays here even once the service vhosts move out to their own modules.
{
lib,
cfg, # services.hyperhive.gateway
tlsCert,
tlsKey,
svcCert, # swarm-services leaf, for names the hive CA cannot sign
svcKey,
swarmServiceDomains, # which names those are (../swarm.nix derives it)
errorPages, # ./error-pages.nix: { notFound, unreachable, unauthorized, ssoUnavailable }
caBundle, # hive CA trust bundle on the host (root + intermediate)
}:
let
# nixos `services.nginx.virtualHosts.<name>` ssl attrs for a vhost
# covered by the hive's own cert. For ACME mode: `enableACME` +
# `addSSL` — NixOS's ACME integration manages the cert lifecycle and
# sets ssl_certificate automatically. For self-signed / certDir:
# explicit cert paths.
vhostTls =
if cfg.tls.acme.enable then
{
addSSL = true;
enableACME = true;
}
else
{
addSSL = true;
sslCertificate = tlsCert;
sslCertificateKey = tlsKey;
};
hstsDirectives = lib.concatStringsSep "; " (
[ "max-age=${toString cfg.hsts.maxAge}" ]
++ lib.optional cfg.hsts.includeSubDomains "includeSubDomains"
);
in
{
# The gateway always terminates TLS: self-signed is the implicit
# floor when neither `tls.certDir` nor ACME is set, so there is no
# http-only mode. Listen addresses every vhost shares — plain http
# on `cfg.port` plus TLS on `cfg.httpsPort`. See `docs/networking/gateway.md`
# ("TLS modes").
listen = [
{
addr = "0.0.0.0";
port = cfg.port;
}
{
addr = "0.0.0.0";
port = cfg.httpsPort;
ssl = true;
}
];
# TLS attrs for one vhost, by name. A swarm service's name may sit
# outside this hive's domain — and then the hive CA is
# name-constrained out of it, so its vhost must serve the
# swarm-services leaf instead. Everything else keeps the hive leaf.
#
# Only in self-signed mode: with ACME or an operator cert there is a
# single issuer that already covers every name, and a second pair
# would be a cert nobody asked for.
tlsFor =
host:
if !cfg.tls.acme.enable && cfg.tls.certDir == null && builtins.elem host swarmServiceDomains then
{
addSSL = true;
sslCertificate = svcCert;
sslCertificateKey = svcKey;
}
else
vhostTls;
# Dial another service on this hive BY NAME over https, verified.
# nginx verifies nothing by default (`proxy_ssl_verify` is off), so a
# `proxy_pass https://…` without this is encrypted but unauthenticated
# — invisibly, it works and keeps working against any certificate at
# all. Full reasoning for every directive here (plus two real
# footguns — session-cache keying, and a `Host`-header clobber that
# can recurse a subrequest into itself) is in docs/networking/gateway.md's
# "Dialing another vhost by name" section — read it before touching
# this.
verifiedProxyTo = name: ''
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate ${caBundle};
proxy_ssl_name ${name};
proxy_ssl_server_name on;
proxy_set_header Host ${name};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Server $hostname;
'';
# Security headers added at the server scope on every vhost.
# nginx's add_header inheritance rule: a location that defines its
# own add_header does NOT inherit the server-level ones. Any
# location with its own add_header (e.g. CORS on /.well-known or
# /_matrix/) must repeat the security headers explicitly — see those
# locations in ./vhosts.nix. HTML-serving and proxy locations that
# carry no add_header of their own pick these up from the server
# scope automatically.
securityHeaders = ''
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
${lib.optionalString cfg.hsts.enable ''add_header Strict-Transport-Security "${hstsDirectives}" always;''}
'';
# The gateway's styled error pages, re-exported so a service module
# can point an `error_page` at one. Republished rather than imported
# per module for the same reason as everything else in this kit: these
# carry the hive's branding, and a service rendering its own would
# drift from the rest of the gateway the first time the theme changes.
inherit errorPages;
}