Skip to content

Phase 3 -- Corporate Updates -- Exports

The cross-phase contracts Phase 3 produces. Future Corporate consumers (HubSpot, marketing) and the runtime / module phases (Phase 4 / Phase 5b) consume these contracts.

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

Export nameValue typeMeaningStatusConsumers
arda-corporate-mail-zoneHosted Zone IDarda.ardamails.com Corporate-zone Hosted Zone IDNew (Phase 3)Future Corporate consumers (HubSpot, marketing) instantiate DnsEmailRecords against this zone for their own sending sub-domains.
import * as corporateEmail from "arda/stacks/corporate/corporate-mail-dns";
const corpImports = corporateEmail.importValues();
// corpImports.corporateMailZone -> "Z0123456789CORPDEF" (zone ID at runtime)
const newConsumerZone = r53.HostedZone.fromHostedZoneAttributes(this, "CorpZone", {
hostedZoneId: corpImports.corporateMailZone,
zoneName: "arda.ardamails.com",
});
new DnsEmailRecords(this, "MyConsumerRecords", {
hostedZone: newConsumerZone,
subdomain: "<my-consumer>",
dkimSelector: postmarkSendingDomain.built.dkimSelector,
dkimKey: postmarkSendingDomain.built.dkimKey,
returnPathTarget: postmarkSendingDomain.built.returnPathTarget,
});

Important: future Corporate consumers under arda.ardamails.com do not register their own Postmark Sender Signature; they rely on the parent arda.ardamails.com signature created by Phase 3 (per DQ-R1-009). Their DKIM is inherited.

2. TypeScript exports from instances/Corporate/

Section titled “2. TypeScript exports from instances/Corporate/”

Each Corporate consumer’s declarative configuration is a separate file under src/main/cdk/instances/Corporate/. Phase 3 ships one such file (free-kanban-tool.ts); future consumers add siblings.

src/main/cdk/instances/Corporate/free-kanban-tool.ts
export const FREE_KANBAN_CONFIG = {
postmarkAccount: POSTMARK_PROD_ACCOUNT,
postmarkItem: FREE_KANBAN_POSTMARK_ITEM,
sendingSubdomain: "freekanban",
corporateZoneName: "arda.ardamails.com",
// ... plan attributes
} as const;

The shape is intentionally simple — a frozen typed object. The drift driver (Task I8) enumerates instances/Corporate/ to discover assets; new files are picked up automatically.

3. TypeScript exports from platform/constructs/postmark/

Section titled “3. TypeScript exports from platform/constructs/postmark/”

The thin-wrapper constructs Phase 3 introduces are reusable by future Corporate consumers and (eventually) by Phase 4 partition stacks if any of them need to register tenant Postmark servers from CDK code (the per-tenant runtime path stays in operations; this is for Phase 4’s Postmark account-token reference / smoke verification only).

src/main/cdk/platform/constructs/postmark/server.ts
export interface Configuration {
accountToken: string;
serverName: string;
// ... per Postmark API
}
export interface Built {
serverId: number;
// The Server API token is intentionally NOT exposed on Built.
// Phase A persists it to 1Password (op://Arda-CorporateOAM/...);
// any consumer that needs it at runtime resolves through 1Password.
}
export class PostmarkServer {
constructor(scope: Construct, id: string, props: Props) { ... }
built: Built;
}
// src/main/cdk/platform/constructs/postmark/sending-domain.ts
export interface Configuration {
accountToken: string;
domainName: string;
}
export interface Built {
dkimSelector: string;
dkimKey: string;
returnPathTarget: string;
}
export class PostmarkSendingDomain {
constructor(scope: Construct, id: string, props: Props) { ... }
built: Built;
}

The exact field names are finalized at write time; the contract is that the Built interfaces remain stable across the future Custom-Resource migration. Caller code does not change.

4. TypeScript exports from constructs/xgress/

Section titled “4. TypeScript exports from constructs/xgress/”

Two construct exports new or renamed in Phase 3:

// src/main/cdk/constructs/xgress/dns-zone.ts (renamed from route-53-hosted-zone.ts)
export interface Configuration {
zoneName: string; // no default; previously defaulted to arda.cards
// ...
}
export interface Built {
hostedZone: r53.IHostedZone;
domainName: string;
}
export class DnsZone extends Construct {
// class renamed from Route53HostedZone
}
// src/main/cdk/constructs/xgress/dns-email-records.ts (new)
export interface Configuration {
hostedZone: r53.IHostedZone;
subdomain: string;
dkimSelector: string;
dkimKey: string;
returnPathTarget: string;
ttlSeconds?: number;
}
export interface Built {
dkimRecord: r53.TxtRecord;
returnPathRecord: r53.CnameRecord;
}
export class DnsEmailRecords extends Construct { ... }

5. CLI surface from tools/corporate-cli.ts

Section titled “5. CLI surface from tools/corporate-cli.ts”

The Corporate CLI exposes the operator entry point for Phase 3 and is the template for future Corporate-asset onboarding:

corporate-cli prepare <asset> # Phase A: Postmark + 1P + cdk.context.json
corporate-cli deploy <asset> # Phase B: cdk diff + cdk deploy
corporate-cli verify <asset> # API-driven verifyDkim + verifyReturnPath
corporate-cli list # enumerate assets in instances/Corporate/
corporate-cli check <asset> # conflict-check only (no state mutation)

Future onboarding follows: add a sibling instances/Corporate/<asset>.ts, run prepare, run deploy, run verify. No code change to the CLI is required for new assets.

SurfacePathConsumed by
corporate-drift.yml workflowinfrastructure/.github/workflows/corporate-drift.ymlScheduled monthly run; opens issues on drift.
tools/corporate-drift.ts driverinfrastructure/tools/corporate-drift.tsThe workflow above; future Corporate-asset additions are picked up automatically by directory enumeration.
ItemVaultReferenceCreated by
Free-Kanban-Generator-Postmark-ServerArda-CorporateOAMop://Arda-CorporateOAM/Free-Kanban-Generator-Postmark-Server/credentialPhase 3 CLI Phase A

Future Corporate-consumer items live in Arda-CorporateOAM with parallel naming (<Asset-Name>-Postmark-Server). The vault separation guarantee from DQ-R1-007 extends to all of them: OP_SERVICE_ACCOUNT_TOKEN (CI’s auth) does not have read access.

8. External resources Phase 3 references but does NOT create

Section titled “8. External resources Phase 3 references but does NOT create”

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

ResourceOwnerHow Phase 3 references it
ardamails.com hosted zonePhase 2 (RootDnsStack); export arda-ardamails-zonePhase 3’s NS-delegation CR writes into this zone via the Root assume-role; the zone itself is not modified by Phase 3 except for the addition of the arda NS record set.
AllowCreatingNSRecordsRole IAM rolePhase 2 (RootDnsStack); export arda-allow-create-ns-record-rolePhase 3’s CR Lambda assumes this role to write the NS record.
WriteNSRecordsToUpstreamDns constructPre-existing (src/main/cdk/constructs/xgress/write-ns-records-to-upstream-dns.ts)Phase 3 instantiates it once in CorporateMailDns for the arda delegation.
Route53HostedZone xgress constructPre-existing (renamed by Phase 3 to DnsZone)Same construct; Phase 3 renames in place.
POSTMARK_PROD_ACCOUNT typed referencePhase 1 (platform/postmark-service.ts)Phase 3 imports this constant for the Postmark account-token reference.
OAM_VAULT constant + Phase-1 1P itemsPhase 1 (platform/one-password.ts)Phase 3 imports these and adds the new FREE_KANBAN_POSTMARK_ITEM typed reference.
tools/drift-check.tsPhase 1 (tools/drift-check.ts)Phase 3 extends ALL_OP_ITEMS with the new 1P item.
external-resources-drift.yml workflowPhase 1Phase 3 leaves this workflow as-is; the new Corporate items are exercised by the new corporate-drift.yml workflow.
Arda-CorporateOAM 1Password vaultProvisioned 2026-05-05 (operator action; pre-Phase-3 prerequisite per DQ-R1-007)Phase 3 creates the first item inside it.

9. Surfaces Phase 3 does NOT add or remove

Section titled “9. Surfaces Phase 3 does NOT add or remove”

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

  • No change to existing arda-allow-create-ns-record-role — Phase 3 only consumes it via the existing WriteNSRecordsToUpstreamDns construct.
  • No change to tools/drift-check.ts’s shape or report format — only ALL_OP_ITEMS.length changes from 3 to 4.
  • No new GitHub Actions repository secrets — the existing OP_SERVICE_ACCOUNT_TOKEN is the only repo-scoped secret in Arda-cards/infrastructure.
  • No change to the arda.cards-family zones — they remain in RootDnsStack unchanged.
  • No change to existing partition stacks — Phase 3 does not touch apps/Al1x/ or instances/Alpha00*/.

The following downstream surfaces consume the exports Phase 3 produces:

ConsumerConsumesPattern
Future Corporate consumers (HubSpot, marketing)arda-corporate-mail-zone (CFN export)Read via Fn::ImportValue or corporateEmail.importValues(). Instantiate DnsEmailRecords against the zone with their own DKIM / Return-Path values from Postmark. Their DKIM inherits from the parent Sender Signature.
corporate-drift.yml workflowinstances/Corporate/<asset>.ts files (TypeScript exports)The driver enumerates this directory; new asset files are picked up automatically.
corporate-cli operator runsinstances/Corporate/<asset>.ts (TypeScript), platform/postmark-service.ts, platform/one-password.tsThe CLI imports from these to resolve account refs and item refs at runtime.

Phase 4 (Runtime Platform) and Phase 5b (Email Module) consume none of Phase 3’s exports. Phase 4 creates its own per-partition mail sub-zones directly under ardamails.com, not under arda.ardamails.com; Phase 5b runs at runtime in partition pods and is unrelated to Phase 3’s deploy-time IaC for the Corporate instance group.