Skip to main content

Overview

fjall domain manages custom domains for your applications. It scaffolds domains, deploys their AWS Route 53 hosted zones, classifies every live record against your declarations, verifies nameserver delegation, and imports or exports DNS records. A domain is declared in fjall/domains/<zone>/infrastructure.ts with the typed Domain construct — see Domain for the construct API.

Usage

Every subcommand supports agent mode: --agent (structured TOON output) with --budget, --fields, and --full.

Subcommands

Create a domain

Scaffold a new domain in the current project:
Adopt an existing Route 53 hosted zone by reference instead of creating a new one:
Both create and import register the domain in fjall-config.json automatically, so the organisation cascade can deploy it. A domain nested under an already-registered domain is registered as delegated with that domain as its parent; everything else is apex.

Deploy a domain

Deploy the domain’s hosted zone and DNS infrastructure to AWS:
The deploy echoes the resolved identity — AWS account id plus target name — before any synth or AWS read, on every route (--plan included): Deploying 'example.com' with AWS account 123456789012 (target 'production'). Resolution fails closed: an unresolvable --target stops before anything is read or mutated with a message listing the available targets, and an unresolvable AWS identity names the cure (fjall connect) rather than falling back to an unnamed account. --region without --target applies the region override to the ambient credential chain. When you deploy an adopted zone (one that already had live records) for the first time, any record that both the live zone and your infrastructure.ts claim must be released through the record-takeover ceremony: the deploy stops with a ticket naming each colliding record, and you consent per row with --remediate '<name>/<TYPE>'=recreate — or --remediate '<name>/<TYPE>@<setIdentifier>'=recreate when the live record set carries a SetIdentifier, since weighted, latency, and failover variants are classified and consented per variant under the three-part key. Consent withheld means exit code 4 — nothing released, nothing deployed. See Destructive Changes.
--allow-replace does not exist on domain surfaces — it is rejected rather than silently dropped. Consent to takeover ticket rows with --remediate '<name>/<TYPE>'=recreate (append @<setIdentifier> exactly when the live record set has one).
fjall deploy domain <name> is the deploy-family spelling of the same operation, with identical flags and exit semantics. Delegated child domains deploy in two phases automatically: the zone and its NS delegation first, then — once the delegation has propagated and the CAA preflight passes — the certificates. This prevents ACM DNS validation hanging against an undelegated zone. Every successful deploy — apex and delegated alike — self-records the entry’s account and region keys in fjall-config.json: the 12-digit AWS account id and the region the domain stack deployed into. Record classification uses a delegated child’s account key to gather the child zone’s cross-account evidence, and the parent-side delegation resolution reads the apex entry’s.

Verify delegation

Check whether the domain’s nameservers point to Route 53:
Print registrar delegation instructions (requires a successful deploy first):
After the DNS check, verify audits the parent-side delegation role: a managed zone whose fjall-config.json declares delegated children needs its stack to publish a delegation-role export, or no child NS delegation can be written in-stack. The audit reports published, missing (warns to redeploy the parent, which publishes the export), or unavailable (credentials could not be resolved — warns to run fjall connect). It is diagnostic only and never gates the DNS verdict.

List live records

List every live record in the zone with its ownership classification:
fjall domain records example.com behaves identically — list is the default action. Each record is classified as one of: Classification is read-only and fail-closed: an evidence gap makes a record unknown, never residue, and unknown records are never offered for remediation. Evidence is drawn from live CloudFormation stack templates — including Custom::CrossAccountZoneDelegation delegation resources — and from ACM certificate ownership (a validation record counts as satellite while its certificate is live). Evidence crosses accounts: a delegated child zone living in another AWS account classifies its parent-side NS row as satellite when the child’s domain stack fully proves ownership. The stack must be live, its template must claim the NS record via a literal DelegatedZoneName, and its nameservers stack output must match the live NS values (order, case, and trailing-dot insensitive). Any missing piece fails closed to unknown with a warning naming the child domain and the cure, never a guess. The classifier finds the child through the delegated domain’s account key in fjall-config.json, set automatically by a successful delegated fjall domain deploy (it can also be added by hand). An unreachable child account affects only that child’s row; the rest of the zone’s evidence, residue detection included, is unaffected.

Sync a zone

Report the classification of every live record (read-only):
Delete consented residue records:
--fix runs the same destruction ceremony as deploy: a ticket names each residue record, you consent per row, and withheld consent exits 4 with nothing deleted. Only residue-classified records ever appear on the ticket — unknown records are never remediable.

Delegate a child domain

Write (or report) the NS delegation for a delegated child domain:
When Fjall manages the parent zone, the child’s NS records are written into it through the parent’s delegation role and the command waits for propagation. When the parent is externally owned, the command prints the exact NS records to create at your registrar instead.

Destroy a domain

Destroy the domain’s stack through the destruction ceremony:
Destroying an apex zone requires consenting to the zone by name — a recreated zone gets a different nameserver set, so DNS stops resolving until the registrar is repointed. Destroying a delegated child deletes its NS record from the parent zone first ('<childZone>/NS'=recreate); when the parent cannot delete it (no delegation role published), the ticket offers =forget instead.

Export DNS records

Export the domain’s DNS records to a BIND zone file:

Import a domain

Import from an existing Route 53 hosted zone (the default when a domain is given):
Import from a BIND zone file:
Import curates an infrastructure.ts from the live zone: records owned by other live stacks (satellites) are deliberately left undeclared, with a header explaining each omission. The first fjall domain deploy of the adopted zone then passes through the record-takeover ceremony described above.

Interactive import

fjall domain import --interactive collects records through prompts. The flow shows a preview of the curated infrastructure.ts — satellite-owned records omitted by design — and writes nothing until the commit step is confirmed.

List domains

Show all domains configured in the current project:

Eject a domain

Remove a domain from Fjall management while keeping its AWS resources in place:

Exit codes

Next Steps

Domain construct

Declare zones, records, certificates, and delegation in infrastructure.ts.

Destructive Changes

How the destruction ceremony and named consents work.

Deploy

Deploy your application to AWS.

Connect AWS

Connect an AWS account to deploy into.