Custom domains first landed in Mechanics 0.1.51, then tightened over the next few releases. By 0.1.56 the model had settled into a safer shape: domain ownership is explicit, DNS proves control, routes only consume verified claims, and removing a domain can keep the app online.
This custom-domain recap covers the releases immediately before 0.1.57. It
follows the path from first-write-wins claims to CDN-safe TLS,
domain attach, read-only checks, and dry-run detach plans.
Where it started
Mechanics 0.1.51 introduced the domain command group:
domain check, domain add, domain list, and
domain remove. A custom domain became a globally unique claim owned by
one Mechanics user. Claims are first-write-wins, and platform-owned names under
the Mechanics base domain stay reserved.
That separation still matters: a domain claim is not a route. The domain command
owns the hostname claim; route create --host only uses a claim that the
current SSH identity already owns and has verified. Route creation never claims
or verifies hostnames implicitly.
The proof moved to one CNAME
The early implementation evolved quickly. 0.1.52 replaced DNS-pointing-as-proof with an explicit DNS challenge, and 0.1.53 moved that proof to the current CNAME model:
domain add www.example.comcreates or resumes a claim- Mechanics prints
_acme-challenge.www.example.com CNAME <token>.acme.<base-domain> - the domain owner publishes that CNAME in their DNS zone
- a later
domain add www.example.comverifies the claim once the record is visible
Exit code 2 means the challenge is still pending; exit code 0
means the claim is verified. Publishing DNS remains a user action. Agents should
show the record and delegate the DNS change to the user, not go looking for DNS
credentials. On Cloudflare DNS, the challenge CNAME should be set to DNS only,
not proxied.
Why the CNAME stays published
The same _acme-challenge CNAME does two jobs. First, it proves control
of the hostname. Second, it permanently delegates ACME DNS-01 challenges to
Mechanics' embedded acme-dns-compatible service. cert-manager can then issue and
renew Let's Encrypt certificates for the origin without depending on where the
traffic DNS points.
That is what made CDN-backed domains viable in 0.1.53. A hostname can point directly at Mechanics, or it can stay behind a CDN such as Cloudflare or Fastly. In the proxied case, the CDN origin should be Mechanics and TLS mode should be Full (strict), because the Mechanics origin presents a real Let's Encrypt certificate for the custom hostname.
Attach is the shortcut
0.1.54 added domain attach for the common workflow. Instead of running
domain verification and route creation as separate steps, users can run
domain attach www.example.com --app myapp.
If the CNAME is missing, domain attach prints the DNS action, exits
2, and performs no route mutation. Once a later run sees the published
CNAME, it verifies the claim and creates or updates the app route to use the
custom host. The same command supports --dry-run, so the route plan can
be reviewed before the GitOps write.
Checks report ownership, DNS, and TLS
domain check is the read-only diagnostic path. It reports whether the
hostname is unclaimed, pending, or verified; whether the expected challenge
CNAME is published; whether traffic DNS points directly at Mechanics or at a
known CDN; and, for the owner of a verified claim, certificate readiness.
The verification lookup also avoids a common DNS trap: Mechanics checks the owning zone's authoritative nameservers for the challenge CNAME, rather than trusting a recursive resolver that may have cached a negative answer before the record existed.
Removal is explicit and app-safe
Releasing a claim never silently deletes an app route. Without
--detach, domain remove www.example.com refuses while any route
still serves that hostname and prints the commands needed to repoint those
routes first.
With --detach, Mechanics repoints each route using the custom domain
back to its default <route>.<user>.<base-domain> hostname, removes the
Gateway listener for the custom host, and then releases the claim. By 0.1.56
this flow had dry-run support and an all-or-nothing preflight, so Mechanics can
show the detach/release plan before changing GitOps files, Gateway listeners, or
claim state.
The safety contract
- one custom hostname belongs to one Mechanics user at a time
- one hostname serves one route; host conflicts are rejected before mutation
- custom domains must be verified before routes can use them
- the challenge CNAME must remain published for TLS renewal
- route cleanup and repo deletion do not release custom domain claims
- admin transfers reset verification, so the new owner must prove DNS control
Custom domains are deliberately separate from routes: claim the name, prove it with DNS, then let routes use the verified host.