When someone connects a custom domain, they usually care about one outcome: can app.example.com open?
The product surface can be simple: enter a hostname, copy a CNAME, wait for it to become ready. Internally, Appaloft cannot treat that hostname as just another string. Before a custom domain starts receiving traffic, the control plane has to answer a few questions: who owns the hostname, who authorized the DNS change, what route target should receive traffic, whether TLS and the gateway are ready, and where the workflow should stop when something fails.
This post follows What a deploy URL should promise. That post covered URL semantics: immutable deployment URLs, mutable aliases, production aliases, custom domains, and blocked routes carry different commitments. This one focuses on the custom-domain path.
Start With The Hostname
The first step is ordinary: a user enters a hostname on a project, environment, or resource.
For example:
pocketbase.example.com
At that point Appaloft should not immediately create a public route. It can create a pending domain binding and start checking:
- whether the hostname is valid;
- whether the suffix is reserved or platform-owned;
- whether the current organization, project, or resource can request this binding;
- whether another active resource already owns the same hostname or path;
- whether the deployment, resource, or static artifact has a routable target;
- whether the user should go through automatic DNS authorization or manual DNS setup.
Those checks look small, but they decide whether the workflow can fail closed. If two resources compete for the same hostname, the default should be to stop. If the user enters an Appaloft official default-domain suffix, the system should not treat it as an ordinary customer domain.
The routing docs name these URL classes separately: official default subdomain, immutable preview or deployment URL, mutable alias, production alias, and custom domain binding. More names, fewer ambiguous states.
DNS Has Two Setup Paths
The current self-serve Cloudflare DNS path in Appaloft is Domain Connect.
After the user clicks “Connect Cloudflare DNS”, Appaloft generates a deterministic DNS plan and sends the browser through Cloudflare’s Domain Connect authorization flow. The user confirms inside the provider, Cloudflare applies the template, and Appaloft performs DNS readback after the browser returns.
One boundary matters here: Domain Connect is a user-authorized DNS setup flow. Appaloft does not need to store the user’s Cloudflare token long-term for this case. A successful provider redirect is also not enough proof by itself; DNS readback still has to show that the records exist.
The other path is manual DNS.
If Domain Connect is unavailable, canceled, or unsuitable for the hostname, Appaloft should show deterministic DNS records: the record type, name, target, and any TXT verification value. The user changes DNS outside Appaloft, then refreshes readback.
Manual DNS is slower, but it is not a second-class path. Custom domains cannot depend on one provider. Manual setup is a fallback and a diagnostic artifact.
Separate Platform Credentials From User Domains
Appaloft can own its own official domain provider. Cloud default access domains may use Appaloft-controlled wildcard DNS, TLS, and gateway routing.
That does not give the platform authority over example.com.
A user-owned custom domain needs user-side authorization or DNS ownership proof. A Cloudflare token in the Appaloft runtime, a Worker route, or an official suffix configuration only proves that the platform can manage an Appaloft-owned zone. It does not prove that a tenant may point pocketbase.example.com at a resource.
This boundary becomes sharper with AI agents. An agent can help start the intent to bind a resource to a domain. It can call CLI or API surfaces to inspect DNS readiness. It should not receive long-lived provider credentials, and it should not skip the product’s domain binding workflow to mutate DNS directly.
That is why Appaloft models external providers through Connections:
ConnectorDefinitiondescribes a provider capability such as Cloudflare DNS;Connectionrecords an owner-scoped authorized instance;ConnectionCapabilitydescribes what the connection can do, such asdns.records.apply;CredentialGranthandles credential material within its lifecycle;- DNS pages, CLI aliases, and AI-facing tools delegate to the same connection capability.
The user sees a domain workflow. The system sees an owner-scoped capability.
Plan DNS Changes First
Before Appaloft applies DNS changes, it should produce a plan.
A DNS plan should explain:
- which records will be created or verified;
- which route target each record points to;
- whether TXT ownership verification is required;
- whether an existing record would be overwritten;
- whether a conflict was found;
- which connection or manual path will execute the change;
- what readback should prove after apply;
- what diagnostics the user will see if the workflow stops.
The plan should not include tokens, raw provider responses, gateway secrets, or secret-looking values. For the automatic path, it can tell the user that Appaloft will redirect to the provider. For the manual path, it can show copyable DNS records.
If the plan finds a conflict with an existing user-owned DNS record, the default should be to stop. Overwriting DNS is too risky to hide inside a happy-path connect button.
This follows the same discipline as Blueprint install plans: show what the platform intends to do before entering the accepted apply boundary.
Readback Decides What Can Continue
The common mistake after DNS apply is treating “the provider API returned success” as completion.
Appaloft cares about readback.
After Domain Connect, Appaloft checks DNS again. After manual DNS, it checks DNS again. DNS propagation can take time. CAA or certificate issuance may fail independently. The CDN or gateway route may not be ready yet. The product should split those states:
- DNS record missing;
- DNS record present but target mismatch;
- ownership TXT missing;
- conflicting active route;
- TLS pending;
- route target unavailable;
- HTTP reachability pending;
- ready.
That is more precise than “waiting for DNS”, and it is much more useful. Users know whether they are waiting for propagation or correcting a target. Support can see whether DNS, TLS, routing, or HTTP readback is stuck. AI agents are less likely to declare success after a shallow check.
Routing Activation Is Another Stage
Once DNS is ready, the domain binding still has to point at a route target.
A route activation flow looks roughly like this:
create route intent
-> verify ownership or official suffix authority
-> create DNS/CDN/gateway route
-> provision/attach TLS
-> activate route target
-> verify HTTP reachability
-> publish route read model
Cloud production routing still has external-system gaps: real DNS, TLS, CDN, ACME, and readback work need go-live evidence before Appaloft should describe them as fully automated. The current rule is to model the boundary and readiness first; real provider mutation only happens in an approved production path.
That is why a domain binding should not collapse into a boolean. verified: true is not enough. The useful state includes who verified the hostname, which hostname was verified, which target is active, what TLS state is visible, whether route readback passed, whether there is a conflict, and whether the binding is blocked or disabled.
Failure Should Be Safe
Custom-domain workflows fail often. DNS records are still propagating. The user mixed apex and subdomain setup. Existing records conflict. Certificate issuance is pending. The route target was deleted. The provider authorization window expired.
Appaloft should keep three principles when that happens.
First, failure should not create a half-active route. Without ownership proof, do not route traffic.
Second, diagnostics should be specific without leaking old targets, internal hosts, provider account secrets, raw tokens, or request bodies. A user needs to know which DNS record to fix. They do not need gateway internals.
Third, cleanup should be conservative. If a user deletes a binding, Appaloft may clean up resources it created or explicitly marked. It should not delete user-managed DNS data by broad conditions. When automatic cleanup is unavailable, the product should state the manual cleanup gap clearly.
This is the same family of rules as blocked routes. An unavailable domain should be safely unavailable.
CLI And AI Use The Same Path
Custom domains often appear outside the Web Console.
A user might run:
appaloft dns connect example.com --provider cloudflare
appaloft dns plan example.com --hostname pocketbase.example.com --target ...
appaloft domain-binding dns-readiness ...
An AI agent might call the same capability through MCP tools or the operation catalog.
Those entrypoints should not invent separate logic. The Web “Connect Cloudflare DNS” button, appaloft dns connect, domain-binding readiness, and future MCP tools should return to the same connector category, capability, authz, audit, plan, accept, apply, and readback path.
The practical benefit is boring and important: if Web checks conflicts, CLI checks conflicts; if Web redacts secrets, the agent tool never receives them; if DNS apply requires owner or admin permission, automation cannot bypass it.
The agent can help the user move through the workflow. The workflow still belongs to the product.
The User Needs A Clear State
The full path sounds long:
- Enter a hostname.
- Create a pending binding.
- Check conflicts and permissions.
- Generate a DNS plan.
- Choose Domain Connect or manual DNS.
- Apply records or wait for the user to configure them.
- Perform DNS ownership and readback checks.
- Prepare TLS and the gateway route.
- Activate the route target.
- Verify HTTP reachability.
- Publish the ready state and diagnostics.
The product does not have to make every step feel like work. A good interface can show the next action: connect DNS, copy records, refresh checks, wait for the certificate, done.
The control plane still needs to keep the details. Otherwise every issue becomes “domain not ready”, which tells the user almost nothing and gives CLI, agents, workflows, and support no useful handoff point.
The Appaloft custom-domain flow is meant to be understandable by people and tools at the same time: prove ownership, plan DNS changes, read back the result, then activate the route. The user still gets a URL that opens. The difference is that the URL has evidence behind it, respects boundaries, and knows when it should not receive traffic.