carlos / platform Public

An app cannot tell when a new host is servable on every edge; a lagging edge answers a dead-end unknown host Feature Request open

Claimed by claude~paul@keymail.dev 1 month ago

What happened (tito, 2026-09-23)

A titogo instance router creates a new host (sb-andyet.go.tito.io, an account's sandbox) and has to decide when it can send a browser there. The browser is going to hit whichever edge edge.platform.tito.io steers it to.

The router asks the host the way a visitor would: GET https://<host>/api/version, and a 200 means ready. On the ie box, though, /etc/hosts pins edge.platform.tito.io to that box's own edge, 54.228.234.85. So the probe only ever asks the ie edge.

Timeline (UTC):

  • 19:47:55: host created. The router shows its "almost ready" page and probes every 3s.
  • 19:48:46: the ie edge learns the route. The next probe gets 200, and the router redirects the browser to https://sb-andyet.go.tito.io/....
  • The browser resolves edge.platform.tito.io to 35.177.77.51 (the uk edge), which has not learned the route yet. It answers the edge's own unknown host page, which is a dead end with no retry and no way on.
  • About a minute later the same click works.

dig edge.platform.tito.io from outside the box answers 35.177.77.51. From the box, the pinned 54.228.234.85.

The ask

An app has no way to know when a host it just created is servable on every edge a visitor can be steered to. It can only ask the edge nearest itself. Any one of these would close the gap (in order of preference):

  1. Edges never answer "unknown host" for a host the platform knows but hasn't propagated yet. An edge that doesn't have a route for a host under a platform-managed domain asks the origin or the home region (or forwards to it) before answering unknown host. Then propagation lag is invisible to the visitor.
  2. A readiness signal. A console/API read (or a control-socket verb on the box) that answers "host X is routable on all armed edges" (or lists which edges have it), so the app waits for the right thing.
  3. At minimum, the edge's unknown-host page for a platform-domain host should be a 503 with Retry-After and a self-refresh, not a terminal page. A just-created host then recovers on its own instead of stranding the person.

We deliberately haven't built (2) ourselves by probing each IP in the deployment's edge_ips. That list exists for custom-domain DNS, has been wrong before (#20: 3 of 6 armed edges), and "which edges serve this host" is the platform's fact to state, not something an app should reconstruct.

Why it matters

The first press of Sandbox for every existing account creates a host, so every account meets this once. It's also the same shape for signup's first redirect to a new account's host, which has a probe with the same blind spot (defaultProbeReady in titogo's internal/carlos/signup_http.go).

Asks 1 and 3 are built, landed as 055ed3b6, and shipped in v0.21.0, which is rolling to flagship now. Tito's fleet takes it with carlos system update --version v0.21.0.

Why the uk edge said unknown host. Another edge learns that a box homes a host from that box's control/edges/<box>.json, and then backhauls the request there. That is ask 1, and it already existed. But the file was published only every 5 minutes plus jitter, and other edges poll it every 30s. So for up to about 6.5 minutes after the ie box started serving sb-andyet, other edges had nowhere to send it.

What changed

  • A box republishes its edges file within one 2s poll of its registry changing. That leaves about 30s, which is the readers' poll interval.
  • An unknown host strictly under a platform domain (CARLOS_WILDCARD_DOMAINS and PLATFORM_APPS_DOMAIN, so *.go.tito.io) gets 503 + Retry-After: 5 + no-store, with a page that retries on its own for 90s. After that it shows not-found text, or goes to the -fallback URL. Customer domains, and any host not in lowercase canonical form, keep the old 404 or 302.

So the sandbox redirect now lands on "still being set up" for a few seconds and then the page, with no dead end. A probe that follows Retry-After gets the same behaviour.

Ask 2, a readiness signal, is not built. You're right that "which edges serve this host" is the platform's fact to state. The shape I'd propose: the console already sees every box's edges file, so it could answer GET /api/.../hosts/<host>/routable with the edges that have it. It would lag by the same ~30s poll. With the two changes above it may not be needed, so say if the 503 isn't enough and I'll open it as its own thread.