"평범한 HTTP랑 사설 TLS는 깔끔한 listener 둘로 서빙해. 둘 다인 척하는 socket 하나 말고. 그리고 엔진이 먼저 입을 열 땐 '지금은 말고'가 성공이어야 해."
영리한 socket 하나보다 listener 둘
Bellows엔 닿는 길이 둘 필요해. canonical 포트의 평범한 HTTP로 loopback이랑 LAN이랑 tailnet에서 직접 붙는 길, 그리고 tailnet에서 TLS를 끊어주는 사설 HTTPS 엔드포인트. 게으른 설계는 이 둘을 socket 하나에 몰아넣으려 해. 첫 바이트를 킁킁대서 TLS냐 평문이냐로 갈라내는 식으로. Bellows는 안 그래. 자기 포트엔 평범한 HTTP를 서빙하고, 영속 Tailscale Serve 엔드포인트가 HTTPS를 따로 끊어서 같은 프로세스로 넘겨줘. 프로토콜 둘, listener 둘, 바이트 킁킁대기는 없이. 공개 노출인 Funnel은 꺼둔 채로 두고. 자세부터가 사설이야.
엔진이 가끔 먼저 말해
요청에 답하는 것 말고도, Bellows는 ambient announcement를 낼 수 있어. 엔진이 자기 판단으로 입을 여는 거지. 시키지도 않았는데 말하는 건 뭐든 고삐가 필요해. 그래서 announcement는 철저히 opt-out이야. enable 토글이랑 quiet hours가 둘 다 입을 막아. 미묘하고 중요한 건 그 '막힘'을 어떻게 다루냐야.
막힌 건 에러가 아니라 성공이야
quiet hours가 announcement를 침묵시켰다고 요청이 실패한 게 아니야. 오히려 존중받은 거지. 호출자는 '말해도 될까?'를 물었고, 정책을 지킨 정답이 '지금은 말고'였던 거야. 그래서 억제는 에러 코드가 아니라 성공을 돌려줘. 진짜 들려야만 하는 드문 경우를 위해 명시적인 force만 그 가드를 넘고. '안 하기로 골랐다'를 성공으로 다루면, 호출자가 존중받은 quiet hour를 재시도로 뚫어야 할 결함으로 오해하지 않아.