Notes

One domain, many apps

Published 2026-09-14 · 6 min read

Every app in this workshop lives on its own subdomain off one root domain, crafnia.com, named after the product itself — blockclock.crafnia.com, altclock.crafnia.com, fitcalc.crafnia.com, mysteryagents.crafnia.com. The naming convention is simple. Getting each one actually pointed at the right place, and knowing when it is safe to make one public, took a few real mistakes to learn properly.

The setup: CNAME to a Firebase site

Hosting sits on Firebase, with one Firebase project holding a separate Hosting "site" for each app. Pointing a subdomain at one is a standard CNAME record at the DNS provider:

Type:  CNAME
Name:  blockclock
Value: blockclock.web.app

The trap is assuming the value is always just the product name with .web.app appended. It is not, because Firebase site ids are global and first-come, first-served across every Firebase project in the world, not just this one. The id altclock was already taken by someone else's project, so the actual AltClock site had to be created as altclock-crafnia, and the CNAME for altclock.crafnia.com points at altclock-crafnia.web.app, not altclock.web.app. The subdomain name and the Firebase site id are two separate things that happen to match most of the time — checking the real site id in the Firebase console before writing the DNS record avoids a record that looks correct and points at nothing.

The DNS panel has its own sharp edge

Separately from anything Firebase does, the DNS provider's own interface has a habit that is easy to lose time to: on the advanced record editor, changing the record type of a row that is being edited clears the name, TTL and value fields for that row immediately, before anything is saved. The safe order is to pick the record type first, then fill in the rest — filling fields in and then adjusting the type loses the work silently, with nothing that looks like an error to explain what happened. It is a small interface quirk, but it is the kind of thing that costs real time precisely because nothing about it looks like a bug from the outside.

"Records not yet detected" is not always wrong

After adding a CNAME, Firebase's domain verification screen can keep showing "Records not yet detected" for up to an hour even when the record was saved correctly the first time. That delay traces back to the DNS negative-caching TTL: when a resolver asks for a record that is not there yet and gets nothing back, it caches that empty answer for as long as the zone's SOA minimum TTL says to, which on the relevant nameservers here is 3600 seconds — one hour. Every resolver that already cached the "nothing here" answer before the record was added will keep serving that cached miss until the hour is up, no matter how many times the verify button gets pressed in the meantime.

Telling "not propagated yet" from "not saved at all"

Waiting an hour on faith is fine when the record actually was saved. It is a waste of an hour when it was not — a typo in the subdomain field, a record added to the wrong zone, a save that silently did not take. The way to tell the two apart is to skip every resolver that might have a stale cached answer and ask the domain's own authoritative nameserver directly:

nslookup -type=CNAME blockclock.crafnia.com launch1.spaceship.net

If the authoritative server itself has no answer, the record genuinely was not saved and waiting will not fix it — go back and re-check the DNS panel. If the authoritative server does answer correctly, the record is fine and any lingering "not detected" message elsewhere is exactly the negative-cache delay described above, resolving itself once the TTL expires.

Nothing buggy goes on a public subdomain

Because every app shares one root domain, a broken or low-quality subdomain does not stay contained to itself. Search engines and ad reviewers evaluate reputation at the domain level as much as the page level, so one unfinished app sitting on a public crafnia.com subdomain can drag down how the whole domain is perceived, including apps that are in good shape. The practical rule that follows is straightforward: an app does not get a public subdomain until its known bugs are fixed, not the other way around. It is slower than shipping the subdomain first and patching in public, but it protects every other app sharing the same domain.

This is also what makes the earlier note about ad approval worth having: a Google AdSense account reviewed once at the root, crafnia.com, covers every subdomain under it. That saves real time — no separate review queue per app — but it is also exactly why one bad subdomain is expensive. The approval and the reputation both live at the domain level, shared by everything hanging off it.

What this buys, in practice

None of this is glamorous work — a CNAME record, an hour of waiting on a TTL, a habit of checking one more nameserver before assuming a save failed. But it is the layer everything else sits on top of: an app's design, its content and its ad eligibility are all downstream of the domain actually resolving to the right place, cleanly, before any of that gets evaluated by a visitor, a search crawler, or an ad reviewer. Getting the DNS layer boring and predictable once means it stops being a source of surprises for every app added after it.