…self
The gateway config in this repo describes the box that routes without
decrypting. Nothing described the box that actually terminates the session,
which is where every live Moshpit name currently goes wrong.
Diagnosed on a real one. `chovy.hacker` is registered with an IPv6 target
pointing at a droplet, and direct-to-origin resolution works — no gateway
involved. But the droplet answers :443 for that name with a Let's Encrypt
certificate for `dev.profullstack.com`, because there is no server block for
`chovy.hacker` and it falls through to the default vhost. Its :80 block then
301s to https://, so a browser is redirected straight into the error. The
registry also has `pins: []`, so no client would accept the name even once
the certificate is right.
That is three separate mistakes and none of them is exotic. Hence a template
and a script rather than prose.
setup-origin.sh generates the key and self-signed certificate, writes the
server block, reloads, and then connects back to 127.0.0.1:443 with the name
as SNI to check what is actually presented. A clean `nginx -t` does not mean
the name reaches the intended block, and the default-vhost fallthrough is
precisely the bug this exists to prevent — so it is verified, not assumed.
Reuses an existing key when one is present, because the pin is over the key:
certificates can be regenerated freely without invalidating what is published.
The template pins TLS 1.3 and asks for X25519MLKEM768, which needs nginx built
against OpenSSL 3.5+. The script reads `nginx -V`, warns, and comments the line
out rather than handing someone a failed reload that takes their live site down
with it.
Verified the pin the script prints is byte-identical to what lib/spki.ts
computes — the value moshpit-proxy and TronBrowser actually check against.
Shell syntax checked, dry-run exercised. 35 tests pass, tsc clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>