Connect domains and inspect DNS¶
A registered domain, a provisioned hosted zone and a working hostname are separate things. Start in Domains and open the exact zone you want to inspect. The detail page separates its configuration, provider records, recorded app routes and runnable diagnostics. Checks include their observation time and location; refreshing a check does not change DNS or registrar settings.
Use the CLI with the selected server and organization, or the control API. Domain reads require the current domain permission. Provider account inventory requires platform-operator authority. Routing results contain only apps the current caller can read in the selected organization. Missing access is never evidence that no records or routes exist.
Connect Cloudflare in five steps¶
On an installation with domains.cloudflare_connections, open Domains →
Connect DNS provider (/domains/connect). From an existing domain's detail,
use its connection action; the wizard carries the exact domain GUID through
/domains/connect?domainId=DOMAIN_GUID. This is read-only provider discovery,
not a registrar connection or a DNS writer.
Setup requires an active installation Super-admin plus fresh selected-org
provider_plugin.read / provider_plugin.configure authority and credential
ceilings. Organization administration or staff status alone is insufficient.
Support flags describe current availability; every read and write rechecks the
actual actor, session or bearer and exact connection/domain versions.
- Provider: choose Cloudflare. Without a reviewed domain, the Route53 option opens registered Route53 domain inventory using the installed provider identity; it has no AWS credential form. Started from a domain's detail, it lists hosted zones with a cluster's AWS credential instead; see Route 53 zones and zone certificates.
- Connection: give the connection a name. Use configured Connect with Cloudflare OAuth in the same selected-org browser session, or paste a zone-restricted API token with Zone Read and DNS Read. The token is write-only and encrypted at rest; it is cleared from the input before dispatch. If OAuth is unconfigured, the reason is shown and the API-token path remains available. Select the saved active connection after returning from OAuth.
- Zone: select an existing Cloudflare zone from the complete inventory for that connection GUID/version. A forbidden, changed, oversized or incomplete inventory is not an empty successful account. Discovery is bounded to 400 entries; a larger inventory is explicitly unavailable.
- Review: inspect the exact zone, nameservers and normalized DNS records,
including bounded TXT content. Review delegation separately and acknowledge
the read-only limits. For a new domain, Register creates only a protected
local
cloudflare_read_onlyregistration with defaultNONE, excluded from app routing and DNS-writer selection. For an existing same-zone registration, Attach adds the protected binding while preserving its existing DNS writer, TXT proof, configuration and app routes. It does not migrate them. - Verify: copy the returned TXT challenge and publish it yourself through
the authoritative provider. Use Verify ownership when the current
canVerifyhint permits it, then open the domain's diagnostics. TXT observation does not provision routing, certificates or a provider zone.canVerifyis separate from Revalidate, which uses its own current cluster action.
The wizard never changes registrar delegation, DNS records or certificates. Its new read-only registration does not start a provisioning workflow. Auxiliary attachment does not replace an existing writer's ordinary lifecycle. To manage Cloudflare writes automatically, a connection-backed writer and reviewed provisioning/certificate integration are still required.
Configure OAuth as an operator¶
In Cloudflare Manage Account → OAuth clients, register a server-side
Authorization Code client with response type code, grant authorization_code
and token authentication client_secret_post. Register the exact HTTPS callback
URL below. Astrolift adds PKCE S256. Follow
Cloudflare's client-registration instructions.
Obtain the two actual OAuth scope IDs corresponding to Zone Read and DNS Read
from Cloudflare's OAuth scope catalogue
(GET /client/v4/oauth/scopes) and select them as required client scopes. These
are the returned id labels, not API-token permission UUIDs or guessed display
names. The public reference does not enumerate universal values; do not copy
illustrative test strings. Configure exactly two distinct read IDs.
| Installation setting | Required value |
|---|---|
ASTROLIFT_CLOUDFLARE_OAUTH_CLIENT_ID |
The registered client ID |
ASTROLIFT_CLOUDFLARE_OAUTH_CLIENT_SECRET |
Its secret from the installation's secret environment; never frontend configuration |
ASTROLIFT_CLOUDFLARE_OAUTH_READ_SCOPES |
The two verified scope IDs, separated by a space; both must end in .read |
APP_BASE_URL |
Exact public HTTPS app origin, with no path/query/fragment |
| Registered redirect URL | https://YOUR_ASTROLIFT_ORIGIN/api/clusters/dns/cloudflare/callback/, matching APP_BASE_URL |
Before accepting a token, configure a strong retained DJANGO_SECRET_KEY and
protect its backups. The currently registered secret-at-rest backend is
local_fernet; its encryption key derives from Django SECRET_KEY. Keep that
key available for retained ciphertext and outstanding OAuth attempts. Other cloud
secret backends are not implemented by this connection flow.
Before enabling OAuth, redact or omit this callback's query string in load balancer, reverse-proxy and access logs. Never collect code, state, PKCE verifier, client secret, bearer headers or provider error bodies in custom telemetry. The application's URL-attribute scrubbing cannot redact upstream logs or browser history. Tokens exchange only at the fixed Cloudflare endpoints described in Cloudflare's integration contract.
OAuth attempts bind the original browser actor/org/session and client, expire after ten minutes and consume the encrypted verifier before one token exchange. A replay, expiry or changed session/client refuses. An unconfirmed exchange is not retried automatically. Refresh/offline access is not implemented; reconnect when the bounded access token expires. Client registration and real OAuth acceptance must be verified for the actual installation.
Recover or disconnect without repeating an unknown write¶
If registration or attachment was accepted but its follow-up read failed, keep the accepted receipt and refresh the domain/binding. If a reply is lost or inconsistent, the wizard stops another write; read current Domains and the exact binding before deciding what happened. Retesting a connection advances its version, so explicitly rebind a domain after reviewing fresh versions.
Disconnect removes local access and wipes its ciphertext first. API-token
connections report LOCAL_ONLY; revoke the token at Cloudflare separately when
needed. OAuth attempts one remote revocation: OAUTH_REVOKED is confirmed, while
OAUTH_UNCONFIRMED still means local disconnection was accepted. Do not retry an
uncertain revocation or assume earlier reads were cancelled. Retained connection
and OAuth-attempt history prevents migration rollback; preserve it.
Use the existing GraphQL CLI for metadata recovery; never put a token in a query
example or shared shell history. Save this as dns-support.graphql:
query DnsConnectionSupport {
dnsProviderConnectionSupport {
allowed reason apiTokenSupported oauthConfigured oauthSetupReason dnsWritesSupported
}
dnsProviderConnectionsPage(page: 1, pageSize: 20) {
items { id version name state authMethod revocationState expiresAt dnsWritesSupported }
totalCount page pageSize
}
}
For a known domain, save dns-binding.graphql:
query DnsBindingRecovery($domainId: GUID!) {
dnsProviderDomainBinding(domainId: $domainId) {
domainId domainVersion state connectionId connectionVersion
currentConnectionVersion zoneId zoneName canVerify dnsWritesSupported
}
}
astro --server staging --org ORGANIZATION_GUID api graphql \
--file dns-binding.graphql --var domainId=DOMAIN_GUID
An unavailable/changed binding cannot become a valid empty tuple. These are
read-only recovery operations; they neither grant write authority nor repair
DNS. The domains / dns offline topic includes the same setup instructions.
Understand the stages¶
| Stage | What it proves | What to check next |
|---|---|---|
| Registered | Astrolift has a configuration row for the domain | Ownership verification and provider access |
| Ownership verified | The required TXT proof was observed, or verification was not required | Exact provider zone binding |
| Provisioned | The provisioning workflow reached its final configuration step | Public delegation and records |
| Delegation matches | The observed public nameservers match the configured provider nameservers | The intended hostname's DNS answers |
| Answer observed | A resolver returned the requested record; the expected target may be unknown | Review the intended target, route and HTTPS |
| Route recorded | Astrolift has an authorized app/environment URL under the zone | The actual ingress and hostname response |
| HTTPS response observed | A public address accepted verified TLS for the hostname and returned an HTTP status | Application health and expected status |
The zone apex does not need an A or AAAA record if you only serve subdomains. Check the hostname you intend to use. A wildcard also does not prove that every name resolves: explicit records, delegation boundaries and TLS wildcard scope can affect the result.
Read an observation at its actual scope¶
DNS_ANSWER is shown as Answer observed. It means the requested DNS answer
was returned, not that it matches an intended app or load balancer. Expected:
Unknown means that this check has no configured expected answer to compare;
it is neither a failed lookup nor a verified routing target. The typed OK
state alone does not establish hostname or application health.
A public Delegation matches check compares nameservers only. It does not check apex A/AAAA records, wildcard answers, app ingress or TLS. Run a lookup for the exact intended hostname and inspect its route and HTTPS separately.
Internal DNS probe not configured means there is no configured in-cluster observer for this diagnostic. It cannot establish private-zone, pod or cluster DNS visibility. Public resolver answers do not substitute for that observation.
Compare public delegation with the provider zone¶
The Overview separates the expected nameservers from the observed values. A public Route 53 hosted zone can exist while your registrar still points the domain at Cloudflare. In that case, records written to Route 53 do not become the domain's public answers merely because the zone exists.
Choose the authoritative provider deliberately. If you keep the current provider, connect and inspect that provider's zone. If you migrate, preserve the existing records and review the registrar nameserver change before making it. Changing nameservers can affect mail, verification records and other services under the domain. The diagnostic tools perform no migration.
For Route 53, compare the four nameservers assigned to the exact public
hosted zone with the registrar delegation. See
AWS's domain routing guidance.
An INSYNC change means Route 53 propagated a change to its own servers; it
does not prove that the registrar delegates to those servers or that every
resolver has refreshed its cache.
GetChange
describes that distinction.
A private hosted zone is resolved from its associated network. Public delegation is not a health requirement for that zone. A public check cannot verify private network visibility, and the page reports that limitation explicitly.
Resume an authorized legacy provisioning run¶
Certificate registration preserves the domain's existing configuration and its actual provisioning cluster. The recorded cluster link keeps authorized revalidation available when a delegation check pauses a run. This does not backfill older rows, associate an arbitrary cluster or change an organization's effective app/preview domain. Existing app hostnames and fleet routing are not migrated automatically.
For a supported managed writer, use Revalidate when its current action hint allows it. Revalidation signals the exact provisioning run to check again; if the old delegation gate closed that run, it can start a new run for the reviewed cluster and zone. This is an intentional provisioning action, separate from a read-only diagnostic refresh. It may resume zone or certificate work and does not prove that app ingress records or a redeployment are complete.
A legacy row without a cluster association needs an authorized operator to
review the original intended cluster. Use its exact public GUID and the exact
registered zone; do not guess a cluster from a name or pass an internal integer
ID as a GUID. The mutation requires current cluster.manage for that cluster,
selected-organization domain access and platform-operator authority for shared
resources. DNS-read permission and a navigation hint do not grant these rights.
Cloudflare read-only registration is not a managed writer.
Save the existing mutation as revalidate-domain.graphql:
mutation RevalidateDomain($clusterId: GUID!, $zone: String!) {
revalidateManagedDomain(clusterId: $clusterId, zone: $zone) {
ok errors { code message }
data { zone signaled message }
}
}
With umask 077, put the reviewed existing public cluster GUID and registered
zone in a private revalidate-domain.json:
astro --server staging --org ORGANIZATION_GUID api graphql \
--file revalidate-domain.graphql --vars-file revalidate-domain.json
This existing mutation accepts cluster GUID and zone, not an expected-version
argument. Review the current domain/cluster tuple before submitting. Inspect
GraphQL errors, ok, signaled and message: a restart may be reported with
signaled=false, and a disabled/unavailable workflow is not success. An
accepted request is not a completed check. After a lost reply or failed refresh,
read the current domain, diagnostics and run status before considering another
write; do not automatically replay it.
Inspect records and routes¶
The Records view shows the provider inventory returned for the bound zone, including ordinary record values and Route 53 alias targets. An alias may have no ordinary TTL/value tuple. A provider permission error is shown as an error, and an incomplete inventory is marked truncated. Neither is an empty successful zone.
Use Routing to find the recorded app/environment URL, its cluster and ingress class. Open the app to inspect its environment, ingress and certificate status. A stored URL is a configuration association, not evidence that the load balancer currently serves it. Shared zones do not expose another organization's app routes. The routing list states when its visible result is incomplete.
Run diagnostics¶
Select an exact hostname within the registered domain. Diagnostics do not accept arbitrary URLs, resolver addresses or shell commands.
| Tool | Observation | Limits |
|---|---|---|
| Lookup / Dig | A typed DNS query through the fixed public recursive resolver | NXDOMAIN, no answer and resolver failure are distinct; the Dig control uses the same bounded DNS engine |
| HTTPS | Verified TLS with the hostname as SNI and one HTTP status line | Public address only, fixed HTTPS port, no redirect following or response-body collection |
| Ping | Bounded ICMP observation from the control-plane host | The utility or permission may be unavailable; a blocked ICMP response does not prove HTTPS failure |
| Traceroute | A bounded network path observation from the control-plane host | Routers may omit responses; unavailable utilities are reported explicitly |
DNS and network results represent the server's observation location, not your browser's network or every internet resolver. The tools never report an unavailable check as successful. Checks refuse unsafe network addresses, out-of-zone targets, stale domain versions and unavailable permission.
Use the CLI¶
First select the server and organization and list the zones:
astro operator domains list --org acme --json
astro operator domains show DOMAIN_GUID --org acme
astro operator domains check DOMAIN_GUID --org acme
astro operator domains check DOMAIN_GUID --hostname api.apps.example.com --record-type A --json
astro operator domains lookup DOMAIN_GUID --hostname apps.example.com --record-type NS --json
astro operator domains dig DOMAIN_GUID --hostname _dmarc.apps.example.com --record-type TXT --json
astro operator domains probe DOMAIN_GUID --hostname api.apps.example.com --tool https --json
astro operator domains probe DOMAIN_GUID --hostname api.apps.example.com --tool ping --json
astro operator domains probe DOMAIN_GUID --hostname api.apps.example.com --tool traceroute --json
Replace DOMAIN_GUID and the example hostnames with values from your selected
installation. show reads the exact domain without relying on the recent
200-row catalog. Each check, lookup, dig or probe reads the current domain
version and sends it with the observation request. A domain change during that
request refuses the result; rerun after reviewing the changed configuration.
Permission or schema errors do not trigger a local-network or alternate-server
fallback.
A completed report may contain a mismatch or an unsupported tool. Inspect its typed state when using JSON in automation; process success means a report was returned, not that every domain check passed.
Use the API¶
Read the domain and its version:
query DomainForCheck($domainId: GUID!) {
astroliftManagedDomain(domainId: $domainId) {
id version zone dnsDriver verificationState provisionState
provisionNameservers provisionClusterId delegationCheck
}
}
Carry that exact version into a fresh report:
query CheckDomain($domainId: GUID!, $version: Int!) {
astroliftManagedDomainDiagnostics(
domainId: $domainId, expectedVersion: $version, recordType: NS
) {
id version zone checkedAt
checks { key state perspective checkedAt reason expected observed }
providerZone {
state reason checkedAt zoneId zoneName privateZone nameservers truncated
records { name type ttl values aliasTarget aliasZoneId evaluateTargetHealth }
}
routes {
appId appName appSlug environmentId environmentName recordedUrl hostname
clusterId clusterName ingressClass observedState
}
routesTruncated
}
}
Run a single lookup or network probe separately:
query ProbeDomain($domainId: GUID!, $version: Int!, $hostname: String!) {
astroliftManagedDomainProbe(
domainId: $domainId, expectedVersion: $version,
hostname: $hostname, tool: HTTPS, recordType: A
) {
state perspective checkedAt reason hostname tool recordType values
publicAddress httpStatus tlsVerified latencyMs
}
}
States are OK, MISMATCH, UNKNOWN, ERROR and UNSUPPORTED. Preserve them
in your own display. A missing result or permission error cannot be converted
into a healthy empty inventory.
Check email delivery separately¶
DNS lookup can inspect MX, SPF TXT, DKIM selectors and DMARC TXT records. Expected values come from the configured sender or mailbox provider; do not invent DKIM selectors or replace existing MX records to perform a test.
Existing app email-service panels expose the supported sender verification, DNS authentication and delivery observations. The app overview also has an explicit test-send action. Transport acceptance is separate from recipient delivery: confirm which transport was used, then look for a matching supported delivery or bounce event. A generic install-wide test does not certify an app's different managed email binding.
See exact email-service tests for reviewed SES sends, stable nonce recovery and signed observations.
Mail provider setup and a receive-capable inbox are separate capabilities. Configuring an outbound sender does not allocate an inbox for an app or agent.