carlos / platform Public

edge: answer the steering health check — cert coverage and a real 200

The health-probe gate the T5/T6 reviews routed here: without both halves
of this, steering's rung-2 withdrawal is decorative.

TLS. The Route53 checks handshake with SNI edge.<apps-domain>, one label
SHALLOWER than the per-account wildcard parents this edge derives, so
nothing covered the name and every check failed at the handshake —
withdrawing exactly the records it was meant to protect. With steering
armed, the apps domain itself joins the wildcard parent set, which is what
a static parent already IS (go.tito.io is there for the same reason).
DNS-01 is not a preference here but the only path that can work: the sole
client that ever sends that SNI is the health checker, and a per-host
autocert order is triggered lazily BY a handshake and cancelled with it,
so no checker-driven order could outlive the checker's own timeout. It
also needs no A record to exist first, which matters because the record
only appears once the converger runs.

HTTP. edge.<apps-domain> is not a registry route, so it fell through to
unknownHost — a 404, or on a -fallback edge (every flagship box) a 302,
which Route53 scores as HEALTHY. A false-healthy check never withdraws
anything and says nothing in the journal. The edge now answers GET/HEAD /
on that host, and only that host and only that path, with a tiny 200,
placed ahead of every other serving path on both listeners.

Gated on CARLOS_STEERING_ZONE_ID (validated by the converger's own
SteeringZoneID), never on PLATFORM_APPS_DOMAIN alone: both production
deployments set an apps domain and neither steers, so that gate would
answer for a hostname no zone steers and mint an unasked-for
*.<apps-domain> on live boxes. No new environment variable either way.

Tests: a real handshake-and-GET for edge.<apps-domain> against a served
wildcard (200), the fallback edge NOT 302ing the probe, non-/ and non-GET
falling through to ordinary routing, the parent set arming only when
steered, and byte-identical behaviour with the zone unset. Both new
behaviours were verified to FAIL with the wiring disabled. Plus the
dead-box rollout note: nothing retires an edges file, so a terminated
box's EIP is contributed forever and the health check is the only
withdrawal.
Paul Campbell pushed by paul@keymail.dev 003f2a37e28132af18d79c0bdd10dee8b66f1ed7 parent e83dc6f
5 files changed, +414 −1
  • docs/superpowers/plans/2026-08-23-multi-region-steering.md +28 −0
  • internal/edge/edge.go +25 −1
  • internal/edge/steeringprobe.go +113 −0
  • internal/edge/steeringprobe_test.go +217 −0
  • internal/edge/wildcard.go +31 −0
diff --git a/docs/superpowers/plans/2026-08-23-multi-region-steering.md b/docs/superpowers/plans/2026-08-23-multi-region-steering.md
index 7390757f7..e252a5931 100644
--- a/docs/superpowers/plans/2026-08-23-multi-region-steering.md
+++ b/docs/superpowers/plans/2026-08-23-multi-region-steering.md
@@ -349,3 +349,31 @@ func desiredState(routes []registry.Route, edges []edgesFileView, appsDomain str
## Rollout notes (filled by Task 8 at execution)
- (filled at execution)
+
+Pre-filled by Task 7 (fold into the list above at Task 8):
+
+- **A dead box is never retired by anything but the health check.** A box
+ writes `control/edges/<box>.json` and nothing ever deletes it: an
+ instance that is terminated, or simply stops running the agent, keeps
+ contributing its EIP to `desiredState` forever, so the converger keeps
+ writing a latency record for an address nobody answers on. The Route53
+ health check is the ONLY withdrawal — which is why the probe has to
+ actually pass on a live box (Task 7's edge half) — and it withdraws the
+ ANSWER, not the record. Retiring a region is therefore a two-step
+ operator action: stop the box, then delete its edges file from the
+ control bucket. Until that second step, the zone still names it.
+- **The steering env has THREE consumers on a box, not one.**
+ `CARLOS_STEERING_ZONE_ID` gates (a) the converger duty in the agent,
+ (b) the edge's steering-probe answer AND its `*.<apps-domain>` wildcard
+ parent — the cert the health check handshake needs, which no per-host
+ order can ever produce — and (c) the console's custom-domain guidance,
+ which only offers the CNAME to `edge.<apps-domain>` where the value is
+ set. All three read the same variable and validate it identically; a box
+ whose edge/console did not get it stays on the old behaviour silently
+ (dormant, not broken). The edge additionally needs a DNS provider
+ configured (`CARLOS_DNS_PROVIDER`) — without one the apps-domain
+ wildcard is never issued and every health check fails at the handshake.
+- **`PLATFORM_EDGE_IPS` must list every armed edge's EIP once steering is
+ armed**, not just the local box's: the DNS check that stamps a custom
+ domain `pointing` compares the resolved address against that list, and a
+ steered name resolves to whichever edge the resolver is nearest.
diff --git a/internal/edge/edge.go b/internal/edge/edge.go
index 31d8e8b14..19a565b38 100644
--- a/internal/edge/edge.go
+++ b/internal/edge/edge.go
@@ -96,6 +96,12 @@ type Edge struct {
// because HostPolicy refuses to mint certs for unknown hosts (anything else
// would let strangers burn the ACME rate limit). The :80 path always works.
fallbackURL string
+ // steeringHost is this deployment's DNS-steering hostname
+ // (edge.<apps-domain>), or "" when the deployment does not steer — which
+ // is every deployment today. Set once at construction from the
+ // environment; see steeringprobe.go for why it is a serving concern at
+ // all.
+ steeringHost string
// Static serving config (EnableStatic): the deployment store the site
// tars are fetched from, the release pubkey channel pointers must verify
// against, and the on-box cache the trees are extracted into. staticStore
@@ -229,7 +235,7 @@ type upstream struct {
// New builds an Edge over reg and performs the startup reload. act may be nil
// (no hibernation).
func New(reg *registry.Registry, act Activator) (*Edge, error) {
- e := &Edge{reg: reg, act: act, probers: newProberSet(), gzipOn: gzipEnabled()}
+ e := &Edge{reg: reg, act: act, probers: newProberSet(), gzipOn: gzipEnabled(), steeringHost: steeringProbeHostFromEnv()}
if err := e.Reload(); err != nil {
return nil, err
}
@@ -786,6 +792,16 @@ func (e *Edge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
}
+ // The deployment's steering health check (steeringprobe.go), answered
+ // FIRST — ahead of the tunnel hub, the hop, routing and, decisively, the
+ // unknown-host fallback. A -fallback edge redirects an unknown host with
+ // a 302, which Route53 scores as healthy: reached in that order, the one
+ // mechanism that withdraws a dead edge would report every edge alive
+ // forever. Inert (one comparison against "") unless this deployment
+ // steers.
+ if e.steeringProbe(w, r, host) {
+ return
+ }
// The reserved tunnel hostname is answered by the hub itself — before the
// route lookup, so registration cannot be intercepted by a route, and
// after the host:port split, so a dialer sending "tunnel.host:443" still
@@ -1321,6 +1337,14 @@ func (e *Edge) httpFallbackHandler() http.Handler {
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
}
+ // The steering hostname answers here too (ServeHTTP's own first
+ // branch, and the same reasoning): the health check is HTTPS today,
+ // but a :80 probe — or an operator's curl — must not be handed this
+ // edge's fallback redirect for a name that is deployment
+ // infrastructure.
+ if e.steeringProbe(w, r, host) {
+ return
+ }
// The reserved tunnel hostname is answered by the hub itself on 443
// (ServeHTTP, and Allowed's same check for issuance) but is not a
// registry route, so lookupUpstream alone never sees it — without this,
diff --git a/internal/edge/steeringprobe.go b/internal/edge/steeringprobe.go
new file mode 100644
index 000000000..b740824d0
--- /dev/null
+++ b/internal/edge/steeringprobe.go
@@ -0,0 +1,113 @@
+package edge
+
+// The steering probe: the small 200 that decides whether DNS steering's
+// rung-2 withdrawal is real or decorative (spec:
+// docs/superpowers/specs/2026-08-20-multi-region-ingress-design.md, Part 4).
+//
+// The converger (internal/steering/route53.go) binds every latency record it
+// writes to a Route53 health check that probes the edge's EIP over HTTPS
+// with SNI edge.<apps-domain> and GET /. That check is the ONLY thing that
+// withdraws a dead edge's answers, so it has to pass on a live box — and on
+// an unmodified edge it could not, twice over:
+//
+// - TLS: edge.<apps-domain> is one label under the apps domain, and every
+// wildcard this edge holds is a per-ACCOUNT parent one label DEEPER
+// (*.<acct>.<apps-domain>). No cert covered the name, so the probe died
+// at the handshake and every record looked dead. The fix is in
+// wildcard.go: with steering armed, the apps domain itself joins the
+// wildcard parent set, so *.<apps-domain> covers exactly this name —
+// issued over DNS-01, which is the only issuance path that can work
+// here. A per-host order cannot: the sole TLS client that ever sends
+// this SNI is the health checker, and autocert orders LAZILY out of the
+// handshake, so every order would be cancelled by the checker's
+// few-second timeout before it could finish, forever.
+//
+// - HTTP: the hostname is not a registry route, so it fell through to
+// unknownHost — a 404, or on a -fallback edge (every flagship box) a
+// 302, which Route53 counts as HEALTHY. A false-healthy check is worse
+// than no check: it never withdraws anything, and nothing in the journal
+// says so. Hence the answer below, placed AHEAD of every other serving
+// path.
+//
+// Inert unless the deployment steers: steeringProbeHostFromEnv returns ""
+// unless CARLOS_STEERING_ZONE_ID is set AND usable — the same read, with the
+// same validation, that the converger performs — so on every box today the
+// probe is one comparison against an empty string and nothing else changes.
+// PLATFORM_APPS_DOMAIN alone is NOT the gate: both production deployments
+// set it, and gating there would answer for a hostname no zone steers and
+// order a wildcard nobody asked for.
+
+import (
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+
+ "github.com/carlosframework/platform/internal/steering"
+)
+
+// steeringProbeBody is what the health check reads. Deliberately tiny and
+// deliberately not a status page: it says the edge answered, nothing about
+// what it serves or who it serves it for.
+const steeringProbeBody = "carlos steering probe ok\n"
+
+// steeringProbeHostFromEnv derives this deployment's steering hostname, or ""
+// when the deployment does not steer. Read once, at Edge construction (the
+// gzipEnabled precedent), because a hostname that changes under a running
+// edge is a redeploy, not a reload.
+func steeringProbeHostFromEnv() string {
+ if steering.SteeringZoneID() == "" {
+ return ""
+ }
+ domain := strings.ToLower(strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(os.Getenv("PLATFORM_APPS_DOMAIN")), ".")))
+ if domain == "" {
+ // Armed but with nothing to build the name from. The converger says
+ // the same thing on its own pass (desiredState mints no steering
+ // hostname without an apps domain); saying it here too is what tells
+ // an operator reading the EDGE's journal why no probe is answered.
+ 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")
+ return ""
+ }
+ host := "edge." + domain
+ log.Printf("edge: steering probe armed for %s (GET / answers 200; every other path on that host routes normally)", host)
+ return host
+}
+
+// steeringProbe answers the deployment's Route53 health check, reporting
+// whether it did.
+//
+// Narrow on purpose, in all three axes:
+//
+// - the HOST must be the steering hostname exactly (case-insensitively,
+// trailing dot tolerated — DNS names are case-insensitive and a client
+// may send either form);
+// - the PATH must be exactly "/" — the health check's own path
+// (healthCheckPath, internal/steering/route53.go). Anything else on that
+// host carries on to the ordinary routing path, so this is a reserved
+// ANSWER, not a reserved hostname;
+// - the METHOD must be GET or HEAD. Nothing here mutates, so anything else
+// falls through rather than being handled.
+//
+// Callers place it before every other branch — including the fallback
+// redirect, which would otherwise 302 the check into false health.
+func (e *Edge) steeringProbe(w http.ResponseWriter, r *http.Request, host string) bool {
+ if e.steeringHost == "" {
+ return false
+ }
+ if !strings.EqualFold(strings.TrimSuffix(host, "."), e.steeringHost) {
+ return false
+ }
+ if r.URL.Path != "/" || (r.Method != http.MethodGet && r.Method != http.MethodHead) {
+ return false
+ }
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ // A health check must never be answered from anyone's cache: the whole
+ // point of the probe is that it reflects THIS box, right now.
+ w.Header().Set("Cache-Control", "no-store")
+ w.WriteHeader(http.StatusOK)
+ if r.Method == http.MethodGet {
+ _, _ = io.WriteString(w, steeringProbeBody)
+ }
+ return true
+}
diff --git a/internal/edge/steeringprobe_test.go b/internal/edge/steeringprobe_test.go
new file mode 100644
index 000000000..2dad7c5f5
--- /dev/null
+++ b/internal/edge/steeringprobe_test.go
@@ -0,0 +1,217 @@
+package edge
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "io"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/crypto/acme/autocert"
+)
+
+// steeringEdge is an Edge with the steering environment armed, built through
+// New so the derivation under test is the production one.
+func steeringEdge(t *testing.T, zone, appsDomain, fallback string) *Edge {
+ t.Helper()
+ t.Setenv("CARLOS_STEERING_ZONE_ID", zone)
+ t.Setenv("PLATFORM_APPS_DOMAIN", appsDomain)
+ e, err := New(openReg(t), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ e.fallbackURL = fallback
+ return e
+}
+
+// TestSteeringProbeHostFromEnv: the hostname is derived only where the
+// deployment actually steers. A missing or malformed zone id — every box
+// today — leaves it empty, which is the whole inertness claim.
+func TestSteeringProbeHostFromEnv(t *testing.T) {
+ cases := []struct{ name, zone, domain, want string }{
+ {"nothing set (every deployment today)", "", "", ""},
+ {"apps domain alone is NOT the gate", "", "oncarlos.com", ""},
+ {"malformed zone id stays dormant", "/hostedzone/Z123", "oncarlos.com", ""},
+ {"armed", "Z0ABCDEF1234", "oncarlos.com", "edge.oncarlos.com"},
+ {"armed, case and dot normalized", "Z0ABCDEF1234", " OnCarlos.com. ", "edge.oncarlos.com"},
+ {"armed with no apps domain: nothing to build from", "Z0ABCDEF1234", "", ""},
+ }
+ for _, c := range cases {
+ t.Run(c.name, func(t *testing.T) {
+ t.Setenv("CARLOS_STEERING_ZONE_ID", c.zone)
+ t.Setenv("PLATFORM_APPS_DOMAIN", c.domain)
+ if got := steeringProbeHostFromEnv(); got != c.want {
+ t.Errorf("steeringProbeHostFromEnv() = %q, want %q", got, c.want)
+ }
+ })
+ }
+}
+
+// TestSteeringProbeAnswersAheadOfTheFallback is the health-probe gate itself:
+// on a steering deployment the check's exact request (GET / with the steering
+// hostname) is answered 200 even though a fallback URL is configured — the
+// 302 that fallback would otherwise return is what Route53 scores as healthy,
+// which would make the withdrawal decorative.
+func TestSteeringProbeAnswersAheadOfTheFallback(t *testing.T) {
+ e := steeringEdge(t, "Z0ABCDEF1234", "example.com", "https://marketing.example.net")
+
+ get := func(method, host, path string) *httptest.ResponseRecorder {
+ t.Helper()
+ r := httptest.NewRequest(method, "http://"+host+path, nil)
+ r.Host = host
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, r)
+ return w
+ }
+
+ w := get("GET", "edge.example.com", "/")
+ if w.Code != http.StatusOK {
+ t.Fatalf("GET / on the steering host = %d, want 200 (a -fallback edge must not 302 the health check)", w.Code)
+ }
+ if w.Body.String() != steeringProbeBody {
+ t.Errorf("body = %q, want %q", w.Body.String(), steeringProbeBody)
+ }
+ if ct := w.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/plain") {
+ t.Errorf("Content-Type = %q, want text/plain", ct)
+ }
+ if cc := w.Header().Get("Cache-Control"); cc != "no-store" {
+ t.Errorf("Cache-Control = %q, want no-store", cc)
+ }
+
+ // Port and trailing dot are both legal ways for a client to name it.
+ if w := get("GET", "EDGE.example.com.:443", "/"); w.Code != http.StatusOK {
+ t.Errorf("case/dot/port-qualified host = %d, want 200", w.Code)
+ }
+
+ // HEAD answers, with no body (the health check may use either).
+ if w := get("HEAD", "edge.example.com", "/"); w.Code != http.StatusOK || w.Body.Len() != 0 {
+ t.Errorf("HEAD / = %d body %q, want 200 with no body", w.Code, w.Body.String())
+ }
+
+ // The probe is an ANSWER, not a reserved hostname: everything else on
+ // that host takes the ordinary path (here: the fallback redirect).
+ for _, c := range []struct{ method, path string }{
+ {"GET", "/other"},
+ {"POST", "/"},
+ } {
+ if w := get(c.method, "edge.example.com", c.path); w.Code != http.StatusFound {
+ t.Errorf("%s %s on the steering host = %d, want the ordinary 302 fallback", c.method, c.path, w.Code)
+ }
+ }
+
+ // :80 answers the same way, for the same reason.
+ r := httptest.NewRequest("GET", "http://edge.example.com/", nil)
+ r.Host = "edge.example.com"
+ w80 := httptest.NewRecorder()
+ e.httpFallbackHandler().ServeHTTP(w80, r)
+ if w80.Code != http.StatusOK || w80.Body.String() != steeringProbeBody {
+ t.Errorf(":80 GET / = %d %q, want 200 %q", w80.Code, w80.Body.String(), steeringProbeBody)
+ }
+}
+
+// TestSteeringProbeInertWithoutSteering: with no steering zone the edge is
+// byte-identical to what it was — the steering hostname is just another
+// unknown host, redirected to the fallback on both listeners.
+func TestSteeringProbeInertWithoutSteering(t *testing.T) {
+ e := steeringEdge(t, "", "example.com", "https://marketing.example.net")
+ if e.steeringHost != "" {
+ t.Fatalf("steeringHost = %q on an unsteered deployment, want empty", e.steeringHost)
+ }
+ for _, h := range []http.Handler{e, e.httpFallbackHandler()} {
+ r := httptest.NewRequest("GET", "http://edge.example.com/", nil)
+ r.Host = "edge.example.com"
+ w := httptest.NewRecorder()
+ h.ServeHTTP(w, r)
+ if w.Code != http.StatusFound || w.Header().Get("Location") != "https://marketing.example.net" {
+ t.Errorf("unsteered edge: %d %q, want the unchanged 302 fallback", w.Code, w.Header().Get("Location"))
+ }
+ }
+}
+
+// TestSteeringArmsTheAppsDomainWildcardParent: the cert half. The health
+// check handshakes with SNI edge.<apps-domain>, one label SHALLOWER than the
+// per-account parents, so the apps domain itself must join the parent set —
+// and only when the deployment steers.
+func TestSteeringArmsTheAppsDomainWildcardParent(t *testing.T) {
+ cache := autocert.DirCache(t.TempDir())
+ t.Setenv("CARLOS_WILDCARD_DOMAINS", "")
+ t.Setenv("PLATFORM_APPS_DOMAIN", "oncarlos.com")
+ t.Setenv("CARLOS_DNS_PROVIDER", "")
+ t.Setenv("CARLOS_DNSIMPLE_TOKEN", "tok")
+
+ e := &Edge{}
+ e.upstreams.Store(&map[string]upstream{})
+ w := newWildcardFromEnv(e, cache)
+ if w == nil {
+ t.Fatal("want a manager")
+ }
+ if len(w.staticDomains) != 0 {
+ t.Fatalf("unsteered staticDomains = %v, want none (the apps domain must not become a parent)", w.staticDomains)
+ }
+ if got := parentsFor(w.staticDomains, w.appsDomain, nil); len(got) != 0 {
+ t.Fatalf("unsteered parents = %v, want none", got)
+ }
+
+ e.steeringHost = "edge.oncarlos.com"
+ w = newWildcardFromEnv(e, cache)
+ if !reflect.DeepEqual(w.staticDomains, []string{"oncarlos.com"}) {
+ t.Fatalf("steered staticDomains = %v, want [oncarlos.com]", w.staticDomains)
+ }
+ // The parent has to cover the probe name exactly.
+ w.parents = parentsFor(w.staticDomains, w.appsDomain, nil)
+ cert, _ := selfSignedWildcard(t, "oncarlos.com", time.Now().Add(time.Hour))
+ w.certs["oncarlos.com"] = cert
+ if w.certFor("edge.oncarlos.com") == nil {
+ t.Error("the steering hostname must be covered by the apps-domain wildcard")
+ }
+}
+
+// TestSteeringProbeOverTLS is the end-to-end shape the Route53 health check
+// performs: a real handshake for edge.<apps-domain>, verified by name against
+// the issuing certificate, followed by GET / — the pair that has to answer
+// 200 for a withdrawal to mean anything.
+func TestSteeringProbeOverTLS(t *testing.T) {
+ e := steeringEdge(t, "Z0ABCDEF1234", "example.com", "https://marketing.example.net")
+
+ cert, _ := selfSignedWildcard(t, "example.com", time.Now().Add(time.Hour))
+ w := &wildcardManager{
+ parents: []string{"example.com"},
+ certs: map[string]*tls.Certificate{"example.com": cert},
+ }
+ srv := httptest.NewUnstartedServer(e)
+ srv.TLS = &tls.Config{GetCertificate: wrapGetCertificate(w, func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
+ // No per-host path in this test: if the wildcard does not cover the
+ // name, the handshake fails exactly as it would on a live box.
+ return nil, errors.New("no per-host certificate")
+ })}
+ srv.StartTLS()
+ t.Cleanup(srv.Close)
+
+ pool := x509.NewCertPool()
+ pool.AddCert(cert.Leaf)
+ client := &http.Client{
+ Timeout: 10 * time.Second,
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{RootCAs: pool},
+ DialContext: func(_ context.Context, network, _ string) (net.Conn, error) {
+ return net.Dial(network, srv.Listener.Addr().String())
+ },
+ },
+ }
+ resp, err := client.Get("https://edge.example.com/")
+ if err != nil {
+ t.Fatalf("handshake + GET against the steering hostname: %v", err)
+ }
+ defer resp.Body.Close()
+ body, _ := io.ReadAll(resp.Body)
+ if resp.StatusCode != http.StatusOK || string(body) != steeringProbeBody {
+ t.Fatalf("GET https://edge.example.com/ = %d %q, want 200 %q", resp.StatusCode, body, steeringProbeBody)
+ }
+}
diff --git a/internal/edge/wildcard.go b/internal/edge/wildcard.go
index b8c21463c..aa3fdda91 100644
--- a/internal/edge/wildcard.go
+++ b/internal/edge/wildcard.go
@@ -683,6 +683,37 @@ func newWildcardFromEnv(e *Edge, cache autocert.Cache) *wildcardManager {
if len(static) == 0 && appsDomain == "" {
return nil
}
+ // Steering (spec Part 4, steeringprobe.go): the deployment's Route53
+ // health checks handshake with SNI edge.<apps-domain>, which is one label
+ // under the apps domain — one label SHALLOWER than the per-account
+ // parents this manager derives, so nothing here covered it and every
+ // check failed at the handshake, withdrawing every record it was meant to
+ // protect. Adding the apps domain itself as a parent is the whole fix,
+ // and it is exactly what a static parent IS (CARLOS_WILDCARD_DOMAINS
+ // carries go.tito.io for the same reason: a domain whose one-label-deep
+ // hosts need certs).
+ //
+ // DNS-01 is not merely convenient here, it is the only path that works:
+ // the sole client that ever sends that SNI is the health checker, and a
+ // per-host order is triggered lazily BY a handshake and cancelled with
+ // it, so no checker-driven order could ever outlive the checker's own
+ // few-second timeout. This manager's loop orders on its own schedule,
+ // answers the challenge in our own zone, and needs no A record to exist
+ // first — which matters, because the record only appears once the
+ // converger runs.
+ //
+ // Gated on the steering zone, not on the apps domain: both production
+ // deployments set an apps domain today and neither steers, and minting an
+ // unasked-for *.<apps-domain> on them (a real ACME order, a real CT log
+ // entry, and a change to which cert their existing one-label hosts are
+ // served from) is not something a dormant feature gets to do.
+ //
+ // Read off the Edge rather than the environment a second time: New()
+ // derived it once (edge.go), so the name this box ANSWERS on and the
+ // parent it gets a certificate for cannot come from two different reads.
+ if e.steeringHost != "" {
+ static = append(static, appsDomain)
+ }
dns, reason := dnsProviderFromEnv()
if dns == nil {
log.Printf("edge: wildcard certs off (%s) — per-host issuance continues", reason)