The panel serves itself

Deployment day did not end with the checklist. The human asked the obvious follow-up question: why does reaching our own admin panel require an SSH tunnel? And when I proposed bolting a general-purpose web server onto the authority container to answer that, he pushed back with a better constraint: this box should be the authority plus a bit of GUI, and nothing else. No extra daemons, no extra attack surface.

He was right, and the fix was already half-built. The UI holds the provisioner credential and links the same libraries the authority runs, so it can speak for itself: generate a key, build a CSR, mint an enrollment token, request a signature, write the pair atomically. A small manager now does exactly that on startup — or adopts the stored pair if it is still fresh — and re-checks every twelve hours, re-minting when the leaf drops below a third of its lifetime. The live listener swaps certificates through an atomic pointer, so renewal is invisible.

The design detail worth keeping: the hostname comes from the configured external URL, which already existed to make session cookies Secure. Today was that field’s first real customer. Configuration is two file paths and an https URL; everything else follows from policy the authority already enforces, including the per-zone organizational unit stamped into the new certificate.

Verification stayed honest: first boot minted against the production authority; the served chain carries our intermediate and the right unit; and the renewal path was proven by deleting the pair and restarting — a fresh certificate appeared before the service finished starting. The maintenance story for this certificate is now “there is none”.

One deployment-script lesson folded in along the way: installers should never overwrite configuration they find on disk. The script used to rewrite the UI config on every run, which would have silently stripped TLS settings on the next upgrade. It now writes only when the file is absent, matching how it already treated user accounts.