Skip to content

Phase 2 -- Root Updates -- Exports

The cross-phase contracts Phase 2 produces. Downstream phases (Phase 3 Corporate, Phase 4 Runtime Platform) consume these contracts to compose their own stacks.

This document complements specification.md — the spec describes what Phase 2 does; this document describes what other phases see once Phase 2 has landed.

RootDnsStack (CFN stack name RootConfiguration) publishes the following exports. Names that exist today are preserved; names new in Phase 2 are flagged as such.

Export nameValue typeMeaningStatusConsumers
arda-app-zoneHosted Zone IDapp.arda.cardsPreservedExisting partition / ingress stacks
arda-io-zoneHosted Zone IDio.arda.cardsPreservedExisting partition / ingress stacks
arda-auth-zoneHosted Zone IDauth.arda.cardsPreservedExisting partition / ingress stacks
arda-assets-zoneHosted Zone IDassets.arda.cardsPreservedExisting partition / ingress stacks
arda-allow-create-ns-record-roleIAM Role ARNAllowCreatingNSRecordsRole ARN — assume-role target for cross-account NS-delegation writesPreservedPhase 3 (Corporate stack), Phase 4 (per-partition stacks), existing partition IngressStacks
arda-ardamails-zoneHosted Zone IDardamails.com mail-root zoneNew (Phase 2)Phase 3 (Corporate stack composing WriteNSRecordsToUpstreamDns against the parent zone), Phase 4 (per-partition stacks doing the same for partition sub-zones)

How downstream phases consume these exports

Section titled “How downstream phases consume these exports”

Phase 3 / Phase 4 stacks read these via the existing stackTypes.readImports() helper used by every consuming stack today, e.g.:

import * as rootDns from "arda/stacks/root/root-dns-stack";
const rootImports = rootDns.importValues();
// rootImports.ardamailsZone -> "Z0123456789ABCDEFG" (zone ID at runtime)
// rootImports.allowCreateNsRecordRole -> arn:aws:iam::<root-acct>:role/AllowWriteNsRecords

Both values resolve to CloudFormation Fn::ImportValue references at synth time, so a stack consuming them must be deployed after the Root stack and within the same CloudFormation account boundary or via cross-account stack imports as appropriate.

2. TypeScript exports from instances/Root/dns.ts

Section titled “2. TypeScript exports from instances/Root/dns.ts”

The new declarative configuration file in Phase 2:

src/main/cdk/instances/Root/dns.ts
/** All zone names this instance group declares. */
export const ROOT_ZONE_NAMES = {
app: "app.arda.cards",
io: "io.arda.cards",
auth: "auth.arda.cards",
assets: "assets.arda.cards",
ardamails: "ardamails.com",
} as const;
/** CloudFormation export keys this instance group publishes. */
export const ROOT_EXPORT_KEYS = [
"appZone",
"ioZone",
"authZone",
"assetsZone",
"ardamailsZone",
"allowCreateNsRecordRole",
] as const;
export type RootExportKey = (typeof ROOT_EXPORT_KEYS)[number];

(The exact symbol shape is finalised at write time; the contract is that instances/Root/dns.ts declares the zones and export keys as the single source of truth used by apps/Root/r53-zones.ts and consumed by tests in verification.md.)

3. TypeScript exports from stacks/root/root-dns-stack.ts

Section titled “3. TypeScript exports from stacks/root/root-dns-stack.ts”

After the rename, the stack module’s public surface is:

src/main/cdk/stacks/root/root-dns-stack.ts
export class RootDnsStack extends cdk.Stack { ... }
export interface Built {
readonly appZone: string;
readonly ioZone: string;
readonly authZone: string;
readonly assetsZone: string;
readonly ardamailsZone: string; // NEW in Phase 2
readonly allowCreateNsRecordRole: iam.IRole;
}
export type ExportKeys =
| "appZone"
| "ioZone"
| "authZone"
| "assetsZone"
| "ardamailsZone" // NEW in Phase 2
| "allowCreateNsRecordRole";
export interface ExportDefinition extends Record<ExportKeys, stackTypes.StackIODefinition> {}
export interface ExportValues extends Record<ExportKeys, stackTypes.StackIOValue> {}
/** Read the cross-stack imports for the Root stack's exports. */
export function importValues(): ExportValues { ... }

The class name change (RootConfigurationStackRootDnsStack) is the only breaking TypeScript change; in-repo callers are updated as part of Task 2 in specification.md.

4. External resources Phase 2 references but does NOT create

Section titled “4. External resources Phase 2 references but does NOT create”

For traceability, the resources Phase 2 reads / depends on but does not own:

ResourceOwnerHow Phase 2 references it
ardamails.com registrar record (Route53 domain registration in platformRoot account)Already-registered domain in AWS Route53The PublicHostedZone Phase 2 creates becomes the authoritative zone for the registered domain. Registrar-side NS configuration (the four AWS nameservers) is handled outside CDK — the operator confirms registrar NS values match the Route53-assigned NS values once after first deploy. (Manual; not a Phase 2 task because the four NS values are stable across CDK redeploys.)
AllowCreatingNSRecordsRole (IAM role)Already exists in RootConfigurationStack today; carries over to RootDnsStackPreserved verbatim through the rename.
WriteNSRecordsToUpstreamDns constructAlready exists at src/main/cdk/constructs/xgress/write-ns-records-to-upstream-dns.tsNot instantiated by Phase 2. Phase 3 / Phase 4 instantiate it with targetAccountId = Root account ID.
inline-lambdas/write-platform-root-ns-record.ts (Lambda body)Already existsPhase 2 leaves this file untouched.

5. Surfaces Phase 2 does NOT add or remove

Section titled “5. Surfaces Phase 2 does NOT add or remove”

For clarity, the following surfaces are not changed by Phase 2:

  • No new IAM roles. AllowCreatingNSRecordsRole is preserved; no new role is created.
  • No new stack class beyond the rename. RootDnsStack is the renamed RootConfigurationStack; no second stack is added in this phase.
  • No new GitHub Actions workflows. Phase 2 introduces no new CI surface.
  • No CloudFormation export removed. Every export present today is preserved.

The following phases consume the exports Phase 2 produces. Each row describes the consumption pattern for forward traceability (so a future agent reading the Phase 3 spec can find its upstream contract here).

Consumer phaseConsumesPattern
Phase 3 (Corporate)arda-ardamails-zone, arda-allow-create-ns-record-roleCorporate stack instantiates r53.PublicHostedZone(this, "ArdaCorpZone", { zoneName: "arda.ardamails.com" }), then new WriteNSRecordsToUpstreamDns(this, "WriteArdaDelegationNs", { targetAccountId: ROOT_ACCOUNT_ID, hostingZoneName: "ardamails.com", subdomain: "arda", nameServers: corpZone.hostedZoneNameServers }). The WriteNSRecordsToUpstreamDns construct internally builds the assume-role ARN from the well-known role name (ALLOW_WRITE_NS_RECORDS_ROLE.name) so explicit consumption of the arda-allow-create-ns-record-role export is optional.
Phase 4 (Runtime Platform)arda-ardamails-zone, arda-allow-create-ns-record-roleEach partition stack instantiates r53.PublicHostedZone({ zoneName: "<partition>.ardamails.com" }), then new WriteNSRecordsToUpstreamDns(this, "Write<Partition>DelegationNs", { targetAccountId: ROOT_ACCOUNT_ID, hostingZoneName: "ardamails.com", subdomain: "<partition>", nameServers: partitionZone.hostedZoneNameServers }). Same pattern as Phase 3, one instantiation per partition.