Phase 4 -- Runtime Platform Updates -- Exports
Cross-phase contracts that Phase 4 produces and that downstream consumers (Phase 5b, future runtime-platform additions, the operations Helm chart, drift workflows) read. Each export is named, typed, and pinned to a producer.
The principal consumer of Phase 4’s CFN exports is the operations Helm chart (non-CDK), which is why every CFN export uses the -API- marker per cdk-infrastructure.md § Export naming. Phase 4’s resource names continue to use the -I- marker (intra-partition AWS scope; per the partitionSecrets.cfn.yaml convention) — that marker is independent of the CFN export-name marker.
1. CloudFormation stack exports
Section titled “1. CloudFormation stack exports”Per partition (4 active partitions × 6 exports = 24 CFN exports total; kyle excluded). Each export is published by the ${infrastructure}-${partition}-Email CloudFormation stack.
| Export name | Value type / shape | Producer | Primary consumer |
|---|---|---|---|
${publishingPrefix}-API-PartitionMailZoneId | Route53 hosted zone ID (Z0123…) | PartitionEmailStack per partition | Phase 5b operations Helm chart (for route53:ChangeResourceRecordSets HostedZoneId parameter) + drift workflow |
${publishingPrefix}-API-PartitionMailZoneName | Canonical zone name ({partition}.ardamails.com) | PartitionEmailStack per partition | Phase 5b for SDK ListHostedZonesByName and record-set FQDN composition; drift workflow |
${publishingPrefix}-API-EmailPostmarkAccountTokenArn | SM secret ARN (arn:aws:secretsmanager:…:secret:${fqn}-I-EmailPostmarkAccountToken-XXXXXX) | PartitionEmailStack per partition (Postmark account-token SM secret) | Phase 5b operations Helm chart ESO ExternalSecret (mounts the token at runtime for the per-tenant server provisioning) |
${publishingPrefix}-API-EmailEncryptionKeyArn | SM secret ARN (arn:aws:secretsmanager:…:secret:${fqn}-I-EmailEncryptionKey-XXXXXX) | PartitionEmailStack per partition (encryption-key SM secret) | Phase 5b operations Helm chart two ESO ExternalSecret mounts (AWSCURRENT + AWSPREVIOUS) — both reference this single ARN with different versionStage selectors |
${publishingPrefix}-API-EmailDnsProvisioningRoleArn | IAM role ARN (arn:aws:iam::…:role/${roleName}) | PartitionEmailStack per partition (DNS-records role via generalized AllowCreatingNSRecordsRole) | Phase 5b operations component’s STS-AssumeRole credential provider for runtime DNS writes (conditional on DQ-R1-023’s resolution) |
${publishingPrefix}-API-EmailEncryptionKeyFallbackRoleArn | IAM role ARN | PartitionEmailStack per partition (fallback role) | Phase 5b operations component’s SecretsManagerClient STS-AssumeRole for the SDK cache-miss fallback path |
How downstream consumers use the exports
Section titled “How downstream consumers use the exports”The operations Helm chart’s values overlay (or amm.sh-driven generation) reads these CFN exports via aws cloudformation list-exports (or describe-stacks --query 'Stacks[0].Outputs') and threads the values into the Helm chart’s values.yaml per release. Concrete wiring is Phase 5b’s specification.md to pin; Phase 4 ships the exports.
The runtime-platform-drift workflow reads the exports the same way to discover what to probe per partition.
2. TypeScript exports from infrastructure/src/main/cdk/stacks/purpose/partition-email.ts
Section titled “2. TypeScript exports from infrastructure/src/main/cdk/stacks/purpose/partition-email.ts”Follows the canonical three-interface stack pattern documented in ../../../../../technology/cdk-infrastructure.md § Stacks (closest precedent: ImageStorageStack in stacks/purpose/image-storage.ts).
| Name | Kind | Purpose |
|---|---|---|
Configuration | interface | Design-time parameters the consumer (instance config) decides: locator: purpose.Locator (composes infrastructure.id + partition.id), postmarkAccount: postmarkService.AccountReference (the POSTMARK_PROD_ACCOUNT or POSTMARK_NONPROD_ACCOUNT typed ref the partition binds to), dmarcReportingMailbox: string ("dmarc-reports@arda.cards" per pre-design follow-up C2). |
Props extends Configuration | interface | Adds runtime injections from the App: postmarkAccountTokenParam: cdk.CfnParameter (the NoEcho parameter declared in apps/Al1x/partition.ts), podRoleArnPattern: string (the ${fqn}-* pattern for the IAM roles’ ArnLike trust condition). |
Built | interface | Cross-CDK exposures (consumed by sibling stacks if any). Phase 4 has no sibling CDK consumer (Phase 5b consumes via CFN -API- exports, not CDK Built), but Built is populated for codebase consistency: partitionMailZone: r53.IHostedZone, emailEncryptionKeySecret: secretsmanager.ISecret, emailPostmarkAccountTokenSecret: secretsmanager.ISecret, emailDnsProvisioningRole: iam.IRole, emailEncryptionKeyFallbackRole: iam.IRole. |
ExportKeys | type (string union) | CFN export key identifiers: "partitionMailZoneIdAPI" | "partitionMailZoneNameAPI" | "emailPostmarkAccountTokenArnAPI" | "emailEncryptionKeyArnAPI" | "emailDnsProvisioningRoleArnAPI" | "emailEncryptionKeyFallbackRoleArnAPI". Every key suffixed API (the publish() regex enforces alignment with the -API- export-name marker). |
ExportDefinition | class extends stackTypes.ExportDefinitions<ExportKeys> | The newer ExportDefinition form (per cdk-infrastructure.md § Export-key pattern; same shape as ImageStorageStack’s). |
exportDefinition(publishingPrefix: string) | factory function | Returns an ExportDefinition instance keyed to a partition’s publishing prefix. |
ExportValues | interface | The resolved (post-readImports) shape of the exports — typed values consumers of the stack import; matches ExportKeys 1:1. |
PartitionEmailStack | class extends cdk.Stack | Constructor signature: constructor(scope: Construct, id: string, props: Props & cdk.StackProps). Static private validateProps(props: Props & cdk.StackProps): Error[] runs purpose.validatePurposeLocator(props.locator) + awsUtil.validateStackProps(props) + domain-specific checks; the constructor throws misc.MultiError on accumulated errors before super(). Composes hosted zone, NS-delegation CR, SPF/DMARC records, DKIM/Return-Path records, both SM secrets, both IAM roles; populates this.built; publishes all six -API- CFN exports. CFN stack name is the literal ${infrastructure}-${partition}-Email passed as the id argument (immutable per § 1.4 of specification.md). |
3. TypeScript exports from infrastructure/src/main/cdk/platform/postmark-service.ts
Section titled “3. TypeScript exports from infrastructure/src/main/cdk/platform/postmark-service.ts”| Name | Kind | Purpose |
|---|---|---|
postmarkCredentialOpReference(partition) | function(partition: PartitionId): string | Returns op://Arda-{Env}OAM/Postmark/credential for the partition’s bound Env vault. Consumed by amm.sh (via op read); CDK has no 1P dependency. |
POSTMARK_PROD_ACCOUNT / POSTMARK_NONPROD_ACCOUNT | typed account references (unchanged from Phase 1) | Inherited from Phase 1; consumed by Phase 4’s instance configs to declare each partition’s Postmark account binding. |
4. TypeScript exports from infrastructure/src/main/cdk/constructs/oam/allow-creating-{ns,dns}-records-role.ts
Section titled “4. TypeScript exports from infrastructure/src/main/cdk/constructs/oam/allow-creating-{ns,dns}-records-role.ts”(Phase 4 generalizes the existing construct; the file may also be renamed.)
| Name | Kind | Purpose |
|---|---|---|
AllowCreatingNSRecordsRole (or AllowCreatingDnsRecordsRole if renamed) | class extends Construct | The generalized construct. Trust principal is parameterizable: legacy Lambda + OrgID (Phase 2 Root use case; default for backwards compatibility) or account principal + ArnLike (Phase 4 STS-chain use case). |
Configuration | interface | Gains a new field trustPrincipal: TrustPrincipalConfig — a discriminated union: { kind: "lambdaOrgID"; orgId: string } (legacy default; the Phase 2 Root call site passes this shape) OR { kind: "stsAssumeRole"; account: string; arnLikePattern: string } (Phase 4 form; arnLikePattern is the aws:PrincipalArn ArnLike value, typically arn:aws:iam::<account>:role/<fqn>-*). The existing allowedParentHostedZoneIds?: string[] scope-tightening field is unchanged. |
Props extends Configuration / Built | interface | Shape unchanged across the generalization. |
validateProps | static method | Now validates the trustPrincipal discriminated union: each variant’s required fields must be present and shaped correctly. Returns Error[]; the constructor throws misc.MultiError on accumulated errors. |
Hard invariant: the Root-account instantiation in root-dns-stack.ts produces byte-identical CloudFormation before and after the generalization (V-IAC-002 enforces this both pre-merge as a CDK Template.fromStack() snapshot test and post-merge as an operator-driven cdk diff against the deployed Root template).
5. TypeScript exports from infrastructure/tools/
Section titled “5. TypeScript exports from infrastructure/tools/”| Name | Kind | Purpose |
|---|---|---|
tools/register-partition-mail-signature.ts | TS script with a two-positional-argument CLI shape (<infrastructure> <partition>) and a no-arg usage output | Phase A entry script. Invoked by amm.sh via npx ts-node tools/register-partition-mail-signature.ts <infrastructure> <partition> (per-partition; amm.sh iterates if all was passed). When invoked without arguments it emits a usage block (per § 4 of cdk-infrastructure.md operator-friendly convention; mirrors amm.sh’s usage output). Not a standalone operator-facing CLI (DQ-R1-022). |
tools/runtime-platform-drift.ts | TS script | The drift driver invoked by runtime-platform-drift.yml. Enumerates instances/Alpha001/{prod,demo}.ts + instances/Alpha002/{dev,stage}.ts; probes Postmark + DNS + AWS SM + IAM state; reports drift. |
tools/lib/postmark-client.ts (or equivalent) | TS module (helpers) | Postmark Account API client with retry / backoff and idempotent list-then-create helpers. Consumed by corporate-cli.ts, register-partition-mail-signature.ts, and the drift driver. Final file layout pinned at implementation. |
tools/lib/op-resolver.ts (or equivalent) | TS module | 1Password SDK resolution wrapper. Single function resolveOpReference(ref: string): Promise<string>. Consumed by corporate-cli and register-partition-mail-signature.ts. |
tools/lib/redact.ts (or equivalent) | TS module | Output redaction helper for logs and cdk.context.json writes. Consumed by the same set. |
tools/lib/drift/*.ts (or equivalent) | TS modules | Shared drift utilities extracted from corporate-drift.ts per DQ-R1-018: Postmark probe helpers, DNS lookup helpers, report-shape rendering, GitHub-issue creation, cross-seam assertion machinery. |
6. Operator surface from amm.sh
Section titled “6. Operator surface from amm.sh”The Phase 4 partition-mail step in amm.sh (invoked per partition) is the operator-facing entry point for partition-mail provisioning. The step is not a standalone CLI — operators invoke amm.sh per its existing partition-selection interface (the implementer aligns with the existing partitionSecrets step’s shape).
Inputs (operator invocation):
./amm.sh [--profile <aws_profile>] [--region <aws_region>] <infrastructure> (<partition>...)- Infrastructure name (required positional):
Alpha001(hostsprod+demo) orAlpha002(hostsdev+stage).SandboxKyle002is retired (PDEV-438) —amm.shexits 0 with a notice if invoked with it. - Partition name(s) (one or more positional, OR the special token
allwhichamm.shexpands per-infrastructure:Alpha001→demo prod,Alpha002→dev stage). - Optional
--profile/--regionflags (per the AWS-CLI shape; the script exports them viaAWS_PROFILE/AWS_REGIONenv vars for downstream CDK invocations — note the env-var export happens insideamm.sh, not from the operator’s shell prefix, per workspace memoryfeedback_aws_profile_flag). - AWS SSO session for the partition’s Infrastructure account (
Admin-Alpha1forAlpha001;Alpha002-AdminforAlpha002). - 1Password access (operator’s local
opCLI signed in to the partition’sArda-{Env}OAMvault).
Outputs:
- Partition mail sub-zone live and delegated.
- Postmark Sender Signature for
{partition}.ardamails.comregistered and verified. - Both partition SM secrets (Postmark token, encryption key) populated.
- Both partition IAM roles declared.
- Updated
cdk.context.json(committed in the per-partition PR). - Operator sign-off row populated in
verification.md.
7. CI surfaces from infrastructure/.github/workflows/runtime-platform-drift.yml
Section titled “7. CI surfaces from infrastructure/.github/workflows/runtime-platform-drift.yml”| Surface | Description |
|---|---|
| Daily scheduled run | Probes per-partition Postmark + DNS + AWS-SM + IAM state. Fails closed by opening a labelled GitHub issue (drift + runtime-platform). |
workflow_dispatch manual trigger | Operator-initiated run for verification or post-rollout smoke-test. |
Shared utility surface (tools/lib/drift/) | Reusable helpers (Postmark probe, DNS lookup, report rendering, GitHub-issue creation) consumed by both runtime-platform-drift and corporate-drift per DQ-R1-018. corporate-drift workflow YAML is not renamed. |
8. 1Password vault surface (consumed; not created)
Section titled “8. 1Password vault surface (consumed; not created)”Phase 4 reads but does not write to the 1Password vaults listed below. The vaults already exist (provisioned during Phase 1 and the Phase-4 prerequisite work).
Per-partition Arda-{Env}OAM vaults — read by the deploy path
Section titled “Per-partition Arda-{Env}OAM vaults — read by the deploy path”Read by amm.sh (operator-driven) and tools/register-partition-mail-signature.ts (invoked by amm.sh). Resolution goes through postmarkCredentialOpReference(partition) (§ 3).
| Vault | Item | Field | Consumer |
|---|---|---|---|
Arda-DevOAM | Postmark | credential | amm.sh for dev partition deploy; register-partition-mail-signature.ts for the dev Sender Signature registration on PostmarkNonProd |
Arda-StageOAM | Postmark | credential | same for stage partition (PostmarkNonProd) |
Arda-DemoOAM | Postmark | credential | same for demo partition (PostmarkProd) |
Arda-ProdOAM | Postmark | credential | same for prod partition (PostmarkProd) |
Pre-flight: op read "$(postmarkCredentialOpReference <partition>)" returns the token.
Arda-SystemsOAM — read by the CI drift workflow only
Section titled “Arda-SystemsOAM — read by the CI drift workflow only”Read by runtime-platform-drift.yml (the parallel workflow Phase 4 introduces) via OP_SERVICE_ACCOUNT_TOKEN. The GHA secret’s 1P scope is Arda-SystemsOAM (set in Phase 1; no change in Phase 4 per pre-design follow-up B5 / G-16 option (a) — the drift workflow does not require per-partition vault access). The workflow needs the Postmark account-level tokens to authenticate Postmark Account API probes for each partition’s Sender Signature.
| Vault | Item | Field | Consumer |
|---|---|---|---|
Arda-SystemsOAM | Postmark-Prod | credential | runtime-platform-drift.yml for Postmark Account API probes against PostmarkProd (prod + demo Sender Signatures) |
Arda-SystemsOAM | Postmark-NonProd | credential | runtime-platform-drift.yml for Postmark Account API probes against PostmarkNonProd (dev + stage Sender Signatures) |
Phase 4 does not introduce these items — they exist from Phase 1. The drift workflow inherits the same authentication pattern corporate-drift already uses. Per-partition SM-secret state is verified via AWS SDK (describe-secret) with the workflow’s existing AWS OIDC role; no additional 1P scope expansion is required.
Arda-CorporateOAM — NOT read by Phase 4
Section titled “Arda-CorporateOAM — NOT read by Phase 4”Phase 3’s Free-Kanban-Generator-Postmark-Server item lives in Arda-CorporateOAM (DQ-R1-007). Phase 4 does not read it. Listed here only to forestall confusion — the Corporate vault is exclusively a Phase 3 / future-Corporate-consumer concern.
9. External resources Phase 4 references but does NOT create
Section titled “9. External resources Phase 4 references but does NOT create”| External resource | Source | How Phase 4 uses it |
|---|---|---|
ardamails.com root zone | Phase 2 (RootDnsStack); CFN export arda-ardamails-zone | Phase 4’s WriteNSRecordsToUpstreamDns writes NS-delegation records into this zone per partition (one record per partition). |
AllowCreatingNSRecordsRole (Root account) | Phase 2; CFN export arda-allow-create-ns-record-role | Phase 4’s NS-delegation Custom Resource Lambda assumes this role for the cross-account write into ardamails.com. Unchanged from Phase 3 ‘s same usage. |
WriteNSRecordsToUpstreamDns construct | Phase 2 (constructs/xgress/write-ns-records-to-upstream-dns.ts) | Phase 4 instantiates per partition. |
DnsZone, DnsEmailRecords xgress constructs | Phase 3 (constructs/xgress/) | Phase 4 instantiates per partition. |
PostmarkSendingDomain thin-wrapper | Phase 3 (platform/constructs/postmark/sending-domain.ts) | Phase 4’s tools/register-partition-mail-signature.ts uses the thin-wrapper to register the partition’s Sender Signature. PostmarkServer thin-wrapper is not used (per-tenant servers are Phase 5b). |
| Per-partition pod IRSA role | Existing infrastructure stack; export ${publishingPrefix}-EksPodRoleArn (eks-stack.ts:250) | Phase 4’s two IAM roles (DNS-records role, fallback role) reference this role’s name pattern via their trust policy ArnLike condition. The export is not imported as an ARN. |
corporate-drift workflow + driver | Phase 3 | Phase 4 extracts shared utilities into tools/lib/drift/ (DQ-R1-018); corporate-drift continues to function unchanged. |
Existing partitionSecrets.cfn.yaml + amm.sh NoEcho-parameter pattern | Pre-Phase-4 (predates this project) | Phase 4’s δ.1 Postmark account-token delivery mirrors this pattern via CDK SecretValue.cfnParameter(). |
10. Surfaces Phase 4 does NOT add or remove
Section titled “10. Surfaces Phase 4 does NOT add or remove”| Surface | Phase 4 stance |
|---|---|
PostmarkServer thin-wrapper | Used by Phase 3 (Free Kanban Tool server); not used by Phase 4 (per-tenant servers are Phase 5b). The construct remains available for Phase 5b. |
tools/corporate-cli.ts operator-facing entry-point | Continues to exist and function. Phase 4 extracts shared helpers into tools/lib/ (B3 minimal cut); corporate-cli’s Phase-3 Corporate flow is unaffected. |
corporate-drift.yml workflow | Not renamed per DQ-R1-018; continues to run on its existing schedule. Phase 4 extracts shared utilities (DQ-R1-018); the workflow consumes the extracted helpers. |
kyle.ardamails.com sub-zone | Not provisioned per DQ-R1-021 (partition suspended). The name remains reserved at the ardamails.com level (REQ-PART-006 / REQ-IAC-007) so it cannot be appropriated as a tenant slug. |
Root account’s AllowCreatingNSRecordsRole instantiation | Unchanged at the CloudFormation level post-Phase-4 (REQ-IAC-002 enforces this byte-identically). Phase 4 generalizes the construct but the existing call-site continues to produce identical synth output. |
Apex SPF / DMARC on ardamails.com | Out of project; not added by Phase 4. |
AllowCreatingNSRecordsRole.allowedParentHostedZoneIds scope-tightening (Root use case) | Out of project; not tightened by Phase 4. Future security-hardening project owns this. |
11. Downstream consumption catalogue
Section titled “11. Downstream consumption catalogue”| Consumer | Reads what | Used for |
|---|---|---|
| Phase 5b operations Helm chart | All six per-partition -API- CFN exports (§ 1) | ESO ExternalSecret definitions; STS-AssumeRole credential providers for the two IAM roles; tenant DNS write paths |
Phase 5b EmailConfigurationService (L3) | Encryption-key SM secret value (via ESO-projected K8s Secret); EmailEncryptionKeyFallbackRoleArn (via Helm values) | Initializes TokenCipher at startup; assumes the fallback role for SDK cache-miss path |
| Phase 5b runtime DNS writes | EmailDnsProvisioningRoleArn (via Helm values; conditional on DQ-R1-023’s resolution) | Per-tenant DKIM TXT + Return-Path CNAME record writes into the partition’s mail sub-zone (if per-tenant Signatures introduced) |
runtime-platform-drift workflow | The CFN exports + Postmark Account API + DNS | Per-partition cross-seam state assertion |
corporate-drift workflow | The shared utilities (tools/lib/drift/) | Existing Corporate Sender Signature drift assertion; unchanged behaviour post-extraction |
12. References
Section titled “12. References”requirements.md— requirements traced from these exports.specification.md— task contract producing these exports.verification.md— tests verifying each export’s existence and shape.analysis.md— Capability decomposition;-API-vs-I-naming explanation.../../decision-log.md— DQ-R1-017..023 + pre-design follow-ups.../../../../../technology/cdk-infrastructure.md— export-naming convention.- Phase 3 analogue:
3-corporate-updates/exports.md.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved