Skip to content

DNS & Domains

How a hostname finds its service — and why customer apps live on a different domain than the product. (A synthesis page with no legacy § number. Design record: specs/2026-07-11-region-onboarding-dns-design.md.)

The two-domain split

Domain Holds Zone character
starform.io Product only — marketing apex · Stardeck (app.) · API (api.) · status page (status., §39.2 #21) Small, hand-curated, trusted
starform.app Customer workloads only — per-service records (below) + the wildcard 404 catch-all Large, machine-managed by DNSProvider, on the PSL

Three reasons, one line each (the full argument lives in the design record):

  • Dashboard protection. Browsers scope cookies by registrable domain; customer apps sharing the product's domain could set or receive cookies against Stardeck. Separate domains = zero shared surface. (Render/Vercel/Fly/Heroku all split the same way.)
  • Tenant-vs-tenant protection. A Public Suffix List private-section entry for starform.app makes each customer subdomain its own registrable domain — without it, one tenant can set a .starform.app cookie every other tenant receives. Submission is free (template PR + a permanently-maintained _psl TXT record) but volunteer-reviewed and slow, with browser propagation lag — submit during private beta (§39.2 #72).
  • Reputation blast radius. Phishing hosted by a bad customer gets flagged at the domain level — contained to starform.app, never touching the domain the dashboard, API, and outbound email live on.

PSL is pre-launch-blocking (§39.2 #72)

Until the PSL entry merges and propagates into browsers, tenant-to-tenant cookie isolation on starform.app does not exist. Acceptable only for a small closed beta.

Hostname generation

Default hostnames are single-label, always suffixed — matching Render/Railway/Vercel, so names are unique by construction with no "name taken" path and no squatting.

  • Format: production<service-name>-<rand4>; any other environment → <service-name>-<environment>-<rand4> (<rand4> = 4 random base36 chars). Single-label only (myapp-p9vq.starform.app, never a.b.starform.app): Cloudflare Universal SSL covers the apex + the first-level subdomain only, so a deeper label would serve no valid certificate (Universal SSL limits).
  • Uniqueness: the suffix makes practical collisions vanish; service_environments.hostname stays UNIQUE as the backstop (regenerate the suffix on the astronomically rare clash).
  • Rename: the customer may edit the hostname (validate + uniqueness-check → one DNSProvider update + the HTTPRoute); the old name releases immediately (the wildcard 404 takes over). The private internal address (Services › Networking) is separate and does not move on a public rename.
  • Private / worker services get no hostname at all — only public web services do (Services › Networking).

Per-hostname routing — how a hostname finds its region

A wildcard record cannot split traffic by region, so routing is one proxied record per public service in the starform.app zone — the record's name is the service hostname, its content is the placement region's LB IP:

Service DNS record · starform.app zone · created via the DNSProvider port
Type: A · Name: myapp-p9vq · Content: <placement region's DO LB IP> · Proxy: ON

Because the record is proxied, the public internet only ever sees Cloudflare's anycast IPs:

  • The user connects to the nearest Cloudflare PoP, TLS terminates at the edge, WAF runs, and Cloudflare opens the origin connection to the placement region's LB over the Origin Certificate — the existing §5 path, now with a per-hostname origin.
  • This is the Sevalla/Render pattern (Cloudflare-fronted, hostname carries the region), and it delivers the edge-proximity benefit Fly/Railway built their own anycast networks for, through the CDN layer §5 already mandates.

Record lifecycle (all via the DNSProvider port, which already defines exactly these operations):

Event DNS action
First deploy of a web service Create the proxied record → placement region's LB
Region migration (§39.3 #40) Update one record's content to the new LB IP
Service deleted / made private Delete the record — the wildcard 404 takes over
Worker service No record (no ingress)

The wildcard's job: *.starform.app remains as a single catch-all pointing at a branded 404 origin. DNS specificity means explicit records always win — so single-region MVP works unchanged, and region 2 requires no migration event (records are per-service from day one).

Custom domains (post-MVP, §39.3 #27, Cloudflare for SaaS): the customer's domain CNAMEs to myapp.starform.app — the per-service record doubles as the canonical target, and the region answer rides along.

Verify at scale

Cloudflare per-zone DNS record limits vs projected public-service count (§39.3 #73) — fine on paid tiers, needs a number checked before the count grows past a few hundred.


Cross-references

The edge→LB→Envoy path these records feed → §5 · the LB whose IP the record targets → §34 · the DNSProvider port → Starbase › Ports §10 · hostname → pod matching inside the cluster (HTTPRoute) → §20.2 · region onboarding (where a region's LB IP is first recorded) → Starbase › Region Onboarding · PSL + custom-domain items → Open Work. Canonical map: Canonical Sources.