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.
1. CloudFormation stack exports
Section titled “1. CloudFormation stack exports”| Export name | Value type | Meaning | Status | Consumers |
|---|---|---|---|---|
arda-corporate-mail-zone | Hosted Zone ID | arda.ardamails.com Corporate-zone Hosted Zone ID | New (Phase 3) | Future Corporate consumers (HubSpot, marketing) instantiate DnsEmailRecords against this zone for their own sending sub-domains. |
How downstream consumers use the export
Section titled “How downstream consumers use the export”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.
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).
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.tsexport 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.jsoncorporate-cli deploy <asset> # Phase B: cdk diff + cdk deploycorporate-cli verify <asset> # API-driven verifyDkim + verifyReturnPathcorporate-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.
6. CI surfaces
Section titled “6. CI surfaces”| Surface | Path | Consumed by |
|---|---|---|
corporate-drift.yml workflow | infrastructure/.github/workflows/corporate-drift.yml | Scheduled monthly run; opens issues on drift. |
tools/corporate-drift.ts driver | infrastructure/tools/corporate-drift.ts | The workflow above; future Corporate-asset additions are picked up automatically by directory enumeration. |
7. 1Password vault surface
Section titled “7. 1Password vault surface”| Item | Vault | Reference | Created by |
|---|---|---|---|
Free-Kanban-Generator-Postmark-Server | Arda-CorporateOAM | op://Arda-CorporateOAM/Free-Kanban-Generator-Postmark-Server/credential | Phase 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:
| Resource | Owner | How Phase 3 references it |
|---|---|---|
ardamails.com hosted zone | Phase 2 (RootDnsStack); export arda-ardamails-zone | Phase 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 role | Phase 2 (RootDnsStack); export arda-allow-create-ns-record-role | Phase 3’s CR Lambda assumes this role to write the NS record. |
WriteNSRecordsToUpstreamDns construct | Pre-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 construct | Pre-existing (renamed by Phase 3 to DnsZone) | Same construct; Phase 3 renames in place. |
POSTMARK_PROD_ACCOUNT typed reference | Phase 1 (platform/postmark-service.ts) | Phase 3 imports this constant for the Postmark account-token reference. |
OAM_VAULT constant + Phase-1 1P items | Phase 1 (platform/one-password.ts) | Phase 3 imports these and adds the new FREE_KANBAN_POSTMARK_ITEM typed reference. |
tools/drift-check.ts | Phase 1 (tools/drift-check.ts) | Phase 3 extends ALL_OP_ITEMS with the new 1P item. |
external-resources-drift.yml workflow | Phase 1 | Phase 3 leaves this workflow as-is; the new Corporate items are exercised by the new corporate-drift.yml workflow. |
Arda-CorporateOAM 1Password vault | Provisioned 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 existingWriteNSRecordsToUpstreamDnsconstruct. - No change to
tools/drift-check.ts’s shape or report format — onlyALL_OP_ITEMS.lengthchanges from 3 to 4. - No new GitHub Actions repository secrets — the existing
OP_SERVICE_ACCOUNT_TOKENis the only repo-scoped secret inArda-cards/infrastructure. - No change to the
arda.cards-family zones — they remain inRootDnsStackunchanged. - No change to existing partition stacks — Phase 3 does not touch
apps/Al1x/orinstances/Alpha00*/.
10. Downstream consumption catalogue
Section titled “10. Downstream consumption catalogue”The following downstream surfaces consume the exports Phase 3 produces:
| Consumer | Consumes | Pattern |
|---|---|---|
| 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 workflow | instances/Corporate/<asset>.ts files (TypeScript exports) | The driver enumerates this directory; new asset files are picked up automatically. |
corporate-cli operator runs | instances/Corporate/<asset>.ts (TypeScript), platform/postmark-service.ts, platform/one-password.ts | The 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.
11. References
Section titled “11. References”analysis.md— gap analysis.requirements.md— numbered requirements.specification.md— task contract.verification.md— test catalogue.plan/task-plan.md— execution plan.../2-root-updates/exports.md— upstream contracts Phase 3 consumes.../phases.md— phase plan; Phase 3 / future-Corporate consumption documented here.../decision-log.md— decisions including newDQ-R1-009..016.
Copyright: © Arda Systems 2025-2026, All rights reserved