"Serve plain HTTP and private TLS as two clean listeners, not one socket pretending to be both. And when the engine speaks on its own, let 'not now' be a success."
Two Listeners, Not One Clever Socket
Bellows needs to be reachable two ways: plain HTTP for loopback, LAN, and direct tailnet access on its canonical port, and a private HTTPS endpoint for tailnet-terminated TLS. The lazy design tries to multiplex both on a single socket — sniff the first bytes, branch to TLS or plain. Bellows doesn't. It serves plain HTTP on its port, and a persistent Tailscale-served TLS endpoint terminates HTTPS separately and proxies to that same process. Two protocols, two listeners, no clever byte-sniffing. Public exposure (Tailscale Funnel) stays off — this is private by posture.
The Engine Sometimes Speaks First
Beyond answering requests, Bellows can produce ambient announcements — the engine speaking on its own initiative. Anything that talks unprompted needs a governor, so announcements are strictly opt-out: an enable toggle and quiet hours both suppress them. The subtle, important part is how suppression is modeled.
Suppression Is a Success, Not an Error
When quiet hours silence an announcement, the request didn't fail — it was honored. The caller asked to maybe say something, and the correct, policy-respecting answer was "not now." So suppression returns success, not an error code. Only an explicit force overrides the guard, for the rare case that genuinely must be heard. Modeling "I chose not to" as success keeps callers from treating a respected quiet hour as a fault to retry around.