carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package edge
2
3// The steering probe: the small 200 that decides whether DNS steering's
4// rung-2 withdrawal is real or decorative (spec:
5// docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md, Part 4).
6//
7// The converger (internal/steering/route53.go) binds every latency record it
8// writes to a Route53 health check that probes the edge's EIP over HTTPS
9// with SNI edge.<apps-domain> and GET /. That check is the ONLY thing that
10// withdraws a dead edge's answers, so it has to pass on a live box — and on
11// an unmodified edge it could not, twice over:
12//
13// - TLS: edge.<apps-domain> is one label under the apps domain, and every
14// wildcard this edge holds is a per-ACCOUNT parent one label DEEPER
15// (*.<acct>.<apps-domain>). No cert covered the name, so the probe died
16// at the handshake and every record looked dead. The fix is in
17// wildcard.go: with steering armed, the apps domain itself joins the
18// wildcard parent set, so *.<apps-domain> covers exactly this name,
19// issued over DNS-01. The narrow alternative — a per-host exception for
20// the single name — was rejected on three counts (the full argument is
21// at the arming site in wildcard.go): it would have to breach the
22// exact-registry Allowed rule (PR #180) in TWO places, issuerHostPolicy
23// AND the mirror's m.allowed bound on the live challenge read-through;
24// with the name resolving to every armed edge, the CA reaches an
25// arbitrary one, so issuance would hang on the cross-box challenge
26// mirror; and every attempt is paid for in failed health checks and in
27// the shared ACME account's failed-validation budget. NOT because an
28// order could not complete — autocert orders under context.Background()
29// with a 5-minute budget (autocert.go:274), so a checker that hangs up
30// on the handshake does not cancel the order it triggered.
31//
32// - HTTP: the hostname is not a registry route, so it fell through to
33// unknownHost — a 404, or on a -fallback edge (every flagship box) a
34// 302, which Route53 counts as HEALTHY. A false-healthy check is worse
35// than no check: it never withdraws anything, and nothing in the journal
36// says so. Hence the answer below, placed AHEAD of every other serving
37// path. (Since #174 the name, one label under the apps domain, would get
38// the pending page's 503 instead, which Route53 scores unhealthy on
39// every edge: wrong the other way. The probe still answers first.)
40//
41// Inert unless the deployment steers: steeringProbeHostFromEnv returns ""
42// unless CARLOS_STEERING_ZONE_ID is set AND usable — the same read, with the
43// same validation, that the converger performs — so on every box today the
44// probe is one comparison against an empty string and nothing else changes.
45// PLATFORM_APPS_DOMAIN alone is NOT the gate: both production deployments
46// set it, and gating there would answer for a hostname no zone steers and
47// order a wildcard nobody asked for.
48
49import (
50 "io"
51 "log"
52 "net/http"
53 "os"
54 "strings"
55
56 "github.com/carlosframework/platform/internal/steering"
57)
58
59// steeringProbeBody is what the health check reads. Deliberately tiny and
60// deliberately not a status page: it says the edge answered, nothing about
61// what it serves or who it serves it for.
62const steeringProbeBody = "carlos steering probe ok\n"
63
64// steeringProbeHostFromEnv derives this deployment's steering hostname, or ""
65// when the deployment does not steer. Read once, at Edge construction (the
66// gzipEnabled precedent), because a hostname that changes under a running
67// edge is a redeploy, not a reload.
68func steeringProbeHostFromEnv() string {
69 if steering.SteeringZoneID() == "" {
70 return ""
71 }
72 domain := strings.ToLower(strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(os.Getenv("PLATFORM_APPS_DOMAIN")), ".")))
73 if domain == "" {
74 // Armed but with nothing to build the name from. The converger says
75 // the same thing on its own pass (desiredState mints no steering
76 // hostname without an apps domain); saying it here too is what tells
77 // an operator reading the EDGE's journal why no probe is answered.
78 log.Printf("edge: steering zone is set but PLATFORM_APPS_DOMAIN is not — no steering hostname to answer; health checks on this box will fail until it is set")
79 return ""
80 }
81 host := "edge." + domain
82 log.Printf("edge: steering probe armed for %s (GET / answers 200; every other path on that host routes normally)", host)
83 return host
84}
85
86// steeringProbe answers the deployment's Route53 health check, reporting
87// whether it did.
88//
89// Narrow on purpose, in all three axes:
90//
91// - the HOST must be the steering hostname exactly (case-insensitively,
92// trailing dot tolerated — DNS names are case-insensitive and a client
93// may send either form);
94// - the PATH must be exactly "/" — the health check's own path
95// (healthCheckPath, internal/steering/route53.go). Anything else on that
96// host carries on to the ordinary routing path, so this is a reserved
97// ANSWER, not a reserved hostname;
98// - the METHOD must be GET or HEAD. Nothing here mutates, so anything else
99// falls through rather than being handled.
100//
101// Callers place it before every other branch — including the fallback
102// redirect, which would otherwise 302 the check into false health.
103func (e *Edge) steeringProbe(w http.ResponseWriter, r *http.Request, host string) bool {
104 if e.steeringHost == "" {
105 return false
106 }
107 if !strings.EqualFold(strings.TrimSuffix(host, "."), e.steeringHost) {
108 return false
109 }
110 if r.URL.Path != "/" || (r.Method != http.MethodGet && r.Method != http.MethodHead) {
111 return false
112 }
113 w.Header().Set("Content-Type", "text/plain; charset=utf-8")
114 // A health check must never be answered from anyone's cache: the whole
115 // point of the probe is that it reflects THIS box, right now.
116 w.Header().Set("Cache-Control", "no-store")
117 w.WriteHeader(http.StatusOK)
118 if r.Method == http.MethodGet {
119 _, _ = io.WriteString(w, steeringProbeBody)
120 }
121 return true
122}
123