Phase 4 -- Runtime Platform Updates -- Requirements
Numbered requirements that Phase 4 must satisfy. Each requirement is traceable forward to a verification test in verification.md and to a task in specification.md, and backward to one of the eight Capabilities in analysis.md (which in turn maps to a goal-item in ../goal.md).
Requirement-ID conventions
Section titled “Requirement-ID conventions”| Prefix | Scope |
|---|---|
| REQ-PART-NNN | Per-partition deliverables: hosted zones, DNS records, Postmark Sender Signatures, SM secrets, IAM roles, and the CFN exports that surface them. |
| REQ-IAC-NNN | IaC-side hygiene: construct generalization, the partition-email stack, app + instance-config wiring, reserved-words registry extension. |
| REQ-CLI-NNN | Operator entry-points: tools/register-partition-mail-signature.ts, extracted helpers under tools/lib/, the amm.sh partition-mail step. |
| REQ-OPS-NNN | Operator prerequisites and post-deploy verification: 1Password vault state, AWS SSO credentials, mailbox health, Postmark approval reply, sign-off. |
| REQ-CI-NNN | CI workflows: runtime-platform-drift workflow + driver + extracted shared utilities. |
| REQ-DOC-NNN | Documentation deliverables: secret-delivery-pattern.md content, per-partition mail pages, postmark-service updates, encryption-key rotation runbook. |
Every requirement carries a Verifies tag pointing to its verification.md entry (V-PART-NNN, V-IAC-NNN, etc.) and a Source tag pointing to the originating Capability section in analysis.md. Cross-cutting decision references use the DQ-NNN / DQ-R1-NNN form per the ../../decision-log.md.
Per-partition deliverables (REQ-PART)
Section titled “Per-partition deliverables (REQ-PART)”C-DNS-Authority (analysis.md §4)
Section titled “C-DNS-Authority (analysis.md §4)”REQ-PART-001: Per-partition mail sub-zone exists
Section titled “REQ-PART-001: Per-partition mail sub-zone exists”For each active partition (prod, demo in Alpha001; dev, stage in Alpha002), the partition-email stack declares a Route53 PublicHostedZone with zoneName: "{partition}.ardamails.com" via the DnsZone xgress construct. Each zone carries RemovalPolicy.RETAIN to defend against accidental cdk destroy. The kyle partition is excluded (DQ-R1-021; partition suspended).
Source: C-DNS-Authority G-DNS-1. Verifies: V-PART-001.
REQ-PART-002: Partition mail zone exports
Section titled “REQ-PART-002: Partition mail zone exports”For each partition, the partition-email stack publishes two CloudFormation exports, both using the -API- marker per ../../../../../technology/cdk-infrastructure.md § Export naming (the consumer is the operations Helm chart, a non-CDK consumer):
${publishingPrefix}-API-PartitionMailZoneId— the hosted zone ID.${publishingPrefix}-API-PartitionMailZoneName— the canonical zone name ({partition}.ardamails.com), needed for SDKListHostedZonesByName-style lookups and for record-set FQDN composition at runtime.
Source: C-DNS-Authority G-DNS-1. Verifies: V-PART-002.
REQ-PART-003: NS-delegation record per partition in ardamails.com
Section titled “REQ-PART-003: NS-delegation record per partition in ardamails.com”Each partition-email stack instantiates WriteNSRecordsToUpstreamDns with subdomain: "{partition}" and nameServers derived from the partition zone’s hostedZoneNameServers token. The CR Lambda assumes AllowCreatingNSRecordsRole (Phase 2 export arda-allow-create-ns-record-role) and writes the NS record set into the root ardamails.com zone. Cross-account assume-role applies per DQ-R1-006 even when same-account.
Source: C-DNS-Authority G-DNS-2. Verifies: V-PART-003.
REQ-PART-004: SPF record at each partition apex
Section titled “REQ-PART-004: SPF record at each partition apex”A TXT record at {partition}.ardamails.com apex has the value "v=spf1 include:spf.mtasv.net ~all" (Postmark’s SPF include with a soft-fail policy). One record per partition, owned by the partition-email stack.
Source: C-DNS-Authority G-DNS-3. Verifies: V-PART-004.
REQ-PART-005: DMARC record at _dmarc.{partition}.ardamails.com
Section titled “REQ-PART-005: DMARC record at _dmarc.{partition}.ardamails.com”A TXT record at _dmarc.{partition}.ardamails.com has the initial monitoring policy "v=DMARC1; p=quarantine; sp=quarantine; rua=mailto:dmarc-reports@arda.cards". Alignment is relaxed (DMARC default; adkim / aspf tags omitted) — see analysis.md §5 for why relaxed alignment is load-bearing for the per-partition Sender Signature design. The reporting mailbox is reused from DQ-R1-015 (pre-design follow-up C2: no per-partition mailboxes).
Source: C-DNS-Authority G-DNS-4. Verifies: V-PART-005.
REQ-PART-006: Reserved-name registry extended
Section titled “REQ-PART-006: Reserved-name registry extended”The reserved-words registry at the ardamails.com level (Phase 3 introduced arda) is extended with prod, demo, dev, stage, kyle so future tenant slugs cannot collide with partition zone names. kyle is reserved-but-not-provisioned per DQ-R1-021. Per pre-design follow-up B4: extend the same mechanism Phase 3 used for arda; no new registry mechanism.
Source: C-DNS-Authority G-DNS-5. Verifies: V-IAC-007 (covered by the IaC hygiene verification family).
C-Postmark-Sending (analysis.md §5)
Section titled “C-Postmark-Sending (analysis.md §5)”REQ-PART-007: Partition-aware Postmark credential accessor
Section titled “REQ-PART-007: Partition-aware Postmark credential accessor”infrastructure/src/main/cdk/platform/postmark-service.ts exposes postmarkCredentialOpReference(partition: PartitionId): string returning op://Arda-{Env}OAM/Postmark/credential for the partition’s Env vault. Consumed by amm.sh (via op read); CDK has no 1Password dependency. Pure addition; no breaking changes to existing exports.
Source: C-Postmark-Sending G-POSTMARK-1. Verifies: V-PART-007.
REQ-PART-008: Per-partition Postmark Sender Signature
Section titled “REQ-PART-008: Per-partition Postmark Sender Signature”For each active partition, a Postmark Sender Signature (sending-domain entry) exists in the partition’s bound Postmark account (PostmarkProd for prod / demo; PostmarkNonProd for dev / stage) for the domain {partition}.ardamails.com. Created by tools/register-partition-mail-signature.ts (REQ-CLI-001) via the Postmark Account API using the PostmarkSendingDomain thin-wrapper (Phase 3 deliverable). One Signature per partition; leaves inherit DKIM via Postmark sub-domain support + DMARC relaxed alignment (DQ-R1-017).
Source: C-Postmark-Sending G-POSTMARK-2. Verifies: V-PART-008.
REQ-PART-009: Sender Signature verified (DKIM + Return-Path)
Section titled “REQ-PART-009: Sender Signature verified (DKIM + Return-Path)”For each partition’s Sender Signature, the Postmark Account API responses verifyDkim and verifyReturnPath both return true. The verification call is part of the register-partition-mail-signature.ts flow (or a post-deploy step) and uses the existing thin-wrapper’s verification helpers. Records used: <sel>._domainkey.{partition}.ardamails.com (DKIM TXT) and pm-bounces.{partition}.ardamails.com (Return-Path CNAME) — see REQ-PART-010.
Source: C-Postmark-Sending G-POSTMARK-2. Verifies: V-PART-009.
REQ-PART-010: Per-partition DKIM TXT + Return-Path CNAME records
Section titled “REQ-PART-010: Per-partition DKIM TXT + Return-Path CNAME records”For each partition, the partition-email stack composes DnsEmailRecords (Phase 3 xgress construct) with the Postmark-issued DKIM selector / public key / Return-Path target. Inputs come from cdk.context.json (populated by Phase A — register-partition-mail-signature.ts). Two records per partition:
<selector>._domainkey.{partition}.ardamails.com(DKIM TXT)pm-bounces.{partition}.ardamails.com(Return-Path CNAME →pm.mtasv.net)
Source: C-Postmark-Sending G-POSTMARK-3. Verifies: V-PART-010.
REQ-PART-011: Per-partition Postmark account-token SM secret
Section titled “REQ-PART-011: Per-partition Postmark account-token SM secret”For each partition, the partition-email stack declares an aws_secretsmanager.Secret named {fqn}-I-EmailPostmarkAccountToken whose secretStringValue comes from SecretValue.cfnParameter(postmarkAccountTokenParam). The CfnParameter is declared with noEcho: true. The secret carries RemovalPolicy.RETAIN. The pattern is δ.1 per pre-design follow-up B2 — mirrors partitionSecrets.cfn.yaml. Lifecycle: amm.sh re-runs with an unchanged 1Password value are no-ops on the SM secret; rotation happens only when the 1Password item is updated (out-of-band aws secretsmanager put-secret-value is reverted on the next deploy).
Source: C-Postmark-Sending G-POSTMARK-4. Verifies: V-PART-011.
REQ-PART-012: Postmark account-token SM secret ARN exported
Section titled “REQ-PART-012: Postmark account-token SM secret ARN exported”The Postmark account-token SM secret’s ARN is exported as ${publishingPrefix}-API-EmailPostmarkAccountTokenArn for consumption by the operations Helm chart’s ESO ExternalSecret. The -API- marker reflects the non-CDK consumer (see analysis.md §5.3 G-POSTMARK-4 for the resource-name -I- vs export-name -API- distinction).
Source: C-Postmark-Sending G-POSTMARK-4. Verifies: V-PART-012.
REQ-PART-013: arda-nonprod account approval unlocked
Section titled “REQ-PART-013: arda-nonprod account approval unlocked”After the dev partition rollout completes (the first non-prod Sender Signature registration; G-POSTMARK-5 fires), the operator replies to Postmark Compliance ticket #11236089 with the verified-domain evidence (the API response from REQ-PART-009 for dev.ardamails.com on PostmarkNonProd). This is an operator-driven step; the assumption is that Postmark’s policy accepts one verified non-prod Sender Signature as sufficient for account approval (documented assumption — see REQ-OPS-004).
Source: C-Postmark-Sending G-POSTMARK-5. Verifies: V-OPS-004.
C-EmailKey-At-Rest (analysis.md §6)
Section titled “C-EmailKey-At-Rest (analysis.md §6)”REQ-PART-014: Per-partition encryption-key SM secret
Section titled “REQ-PART-014: Per-partition encryption-key SM secret”For each active partition, the partition-email stack declares an aws_secretsmanager.Secret named {fqn}-I-EmailEncryptionKey via CDK generateSecretString with passwordLength: 64, excludeCharacters: '"@/\\', excludePunctuation: false. The secret carries RemovalPolicy.RETAIN. Versioning is delegated to AWS Secrets Manager native (AWSCURRENT / AWSPREVIOUS); no version suffix in the resource name. Full design in email-server-key-encryption.md per DQ-R1-019.
Source: C-EmailKey-At-Rest G-KEY-1. Verifies: V-PART-014.
REQ-PART-015: Encryption-key SM secret ARN exported
Section titled “REQ-PART-015: Encryption-key SM secret ARN exported”The encryption-key SM secret’s ARN is exported as ${publishingPrefix}-API-EmailEncryptionKeyArn. The operations Helm chart’s two ExternalSecret mounts (AWSCURRENT + AWSPREVIOUS — Phase 5b deliverable) both reference this ARN with different versionStage selectors.
Source: C-EmailKey-At-Rest G-KEY-1. Verifies: V-PART-015.
REQ-PART-016: Encryption-key generation config is immutable post-launch
Section titled “REQ-PART-016: Encryption-key generation config is immutable post-launch”The generateSecretString configuration on the encryption-key secret (passwordLength, excludeCharacters, excludePunctuation) must not be modified after first deploy. CDK’s GenerateSecretString has “generate-if-missing” semantics — amm.sh re-runs are no-ops on the secret value, but a config change can silently trigger regeneration, bypassing the operator-driven rotation procedure (DQ-R1-019). The construct’s CDK ID at the call site and the secret’s Name property are likewise immutable (changing either forces a resource replacement, regenerating the value and orphaning every in-flight envelope). Full lifecycle invariants in email-server-key-encryption.md § CDK lifecycle invariants.
Source: C-EmailKey-At-Rest G-KEY-1 (lifecycle invariants). Verifies: V-PART-016 (re-run no-op check) + V-IAC-008 (template snapshot guard).
C-Runtime-DNS-Writes (analysis.md §7)
Section titled “C-Runtime-DNS-Writes (analysis.md §7)”REQ-IAC-001: AllowCreatingNSRecordsRole construct generalized
Section titled “REQ-IAC-001: AllowCreatingNSRecordsRole construct generalized”The existing infrastructure/src/main/cdk/constructs/oam/allow-creating-ns-records-role.ts construct is generalized to accept a configurable trust principal. The construct’s permissions are already generic Route53 record-set CRUD (ChangeResourceRecordSets, ListResourceRecordSets, ListHostedZonesByName) with optional allowedParentHostedZoneIds scope-tightening — only the trust-principal hard-coding to lambda.amazonaws.com + OrgID condition is changed. The construct accepts either (a) the legacy Lambda + OrgID shape (default for backwards compatibility; preserves the Root use case) or (b) an iam.AccountPrincipal(account).withConditions({ ArnLike: {...} }) shape (Phase 4 STS-chain use case). May be renamed (e.g., to AllowCreatingDnsRecordsRole) in the same PR; the rename is mechanical and does not affect the synthesized template (CDK construct ID at the call site is preserved).
Source: C-Runtime-DNS-Writes G-IAM-1. Verifies: V-IAC-001.
REQ-IAC-002: Root-account instantiation byte-identical post-generalization
Section titled “REQ-IAC-002: Root-account instantiation byte-identical post-generalization”The existing Root-account instantiation of AllowCreatingNSRecordsRole in infrastructure/src/main/cdk/stacks/root/root-dns-stack.ts must produce byte-identical CloudFormation before and after REQ-IAC-001. Guarded by a CDK Template.fromStack() snapshot-equality unit test in root-dns-stack.test.ts (or in the construct’s own test file) that pins the Root resource shape. The test fails closed if the generalization regresses Root output, blocking the PR from merging.
Source: C-Runtime-DNS-Writes G-IAM-2. Verifies: V-IAC-002.
REQ-PART-017: Per-partition DNS-records role instantiated
Section titled “REQ-PART-017: Per-partition DNS-records role instantiated”Each partition-email stack (one per active partition) instantiates the generalized AllowCreatingNSRecordsRole construct with the Phase 4 trust principal shape: iam.AccountPrincipal(account).withConditions({ ArnLike: { "aws:PrincipalArn": arn:aws:iam::${account}:role/${fqn}-* } }). Resource scoping via allowedParentHostedZoneIds: [zone.hostedZoneId] is a single-element array — the partition’s own mail sub-zone only. One role per partition; each role’s trust policy and zone scope are partition-specific. This preserves per-partition least-privilege isolation (a dev partition’s pod cannot assume into the stage partition’s role, and the dev role cannot write to stage.ardamails.com). Permissions inherit from the construct (REQ-IAC-001). route53:GetChange is NOT included (account-wide resource scope; Postmark verification is API-driven, no Route53 propagation wait needed).
Source: C-Runtime-DNS-Writes G-IAM-3. Verifies: V-PART-017.
REQ-PART-018: DNS-records role ARN exported
Section titled “REQ-PART-018: DNS-records role ARN exported”The DNS-records role’s ARN is exported as ${publishingPrefix}-API-EmailDnsProvisioningRoleArn per partition. Consumed by the operations Helm chart for STS-AssumeRole credential providers. The role itself may or may not be exercised in Phase 5b v1 — that depends on DQ-R1-023’s resolution. The export exists regardless.
Source: C-Runtime-DNS-Writes G-IAM-3. Verifies: V-PART-018.
C-Fallback-Decryption (analysis.md §8)
Section titled “C-Fallback-Decryption (analysis.md §8)”REQ-PART-019: Per-partition EmailEncryptionKeyFallbackRole
Section titled “REQ-PART-019: Per-partition EmailEncryptionKeyFallbackRole”For each active partition, the partition-email stack declares a fresh IAM role EmailEncryptionKeyFallbackRole. Trust policy = account principal + ArnLike on ${fqn}-* (same shape as REQ-PART-017). Permission: secretsmanager:GetSecretValue on ${encryptionKeySecret.secretArn}* (full SM-secret ARN; the trailing wildcard tolerates the SM-appended random 6-character suffix — SM versions are selected at API call time via VersionId/VersionStage, not encoded in the resource ARN). No additional permissions; the role exists solely to enable the SDK cache-miss fallback path in Phase 5b’s TokenCipher.
Source: C-Fallback-Decryption G-IAM-4. Verifies: V-PART-019.
REQ-PART-020: Fallback role ARN exported
Section titled “REQ-PART-020: Fallback role ARN exported”The fallback role’s ARN is exported as ${publishingPrefix}-API-EmailEncryptionKeyFallbackRoleArn. Consumed by Phase 5b’s operations Helm chart for the STS-AssumeRole credential provider that backs the TokenCipher SDK-fallback SecretsManagerClient.
Source: C-Fallback-Decryption G-IAM-4. Verifies: V-PART-020.
IaC hygiene (REQ-IAC)
Section titled “IaC hygiene (REQ-IAC)”REQ-IAC-003: partition-email stack declared
Section titled “REQ-IAC-003: partition-email stack declared”A new CDK stack file at infrastructure/src/main/cdk/stacks/purpose/partition-email.ts composes all per-partition resources: hosted zone (REQ-PART-001), NS-delegation (REQ-PART-003), SPF + DMARC TXT records (REQ-PART-004, 005), Postmark DKIM + Return-Path records (REQ-PART-010), both SM secrets (REQ-PART-011, 014), both IAM roles (REQ-PART-017, 019), and all six -API- exports (REQ-PART-002, 012, 015, 018, 020 + the zone-name export). The CFN stack name is ${infrastructure}-${partition}-Email per pre-design follow-up C1 — immutable, locked at first deploy.
The stack follows the canonical three-interface pattern per ../../../../../technology/cdk-infrastructure.md § Stacks: exports Configuration (design-time inputs), Props extends Configuration (runtime injections like postmarkAccountTokenParam and podRoleArnPattern), Built (cross-CDK exposures — partition zone, both SM secrets, both IAM roles), ExportKeys (string-union of CFN export key identifiers, every key suffixed API), and an exportDefinition(publishingPrefix) factory function for the newer ExportDefinition form. A static private validateProps(props: Props & cdk.StackProps): Error[] runs purpose.validatePurposeLocator(props.locator) + awsUtil.validateStackProps(props) + domain-specific checks (podRoleArnPattern matches IAM_ROLE_ARN_PATTERN_REGEX, DMARC mailbox shape, etc.); the constructor throws misc.MultiError on accumulated errors before super(scope, id, props). The stack publishes its CFN Outputs only when the App calls stack.publish() after instantiation — never from the stack constructor (per cdk-infrastructure.md § Stack ordering; matches the Phase 3 Corporate precedent where apps/Corporate/index.ts calls mailDns.publish() from the App, not the stack). Closest codebase precedent: ImageStorageStack.
Source: analysis.md §12 G-A and G-B (the stack file itself is part of G-B for the per-partition Phase 4 rollout). Verifies: V-IAC-003.
REQ-IAC-004: apps/Al1x/partition.ts extension
Section titled “REQ-IAC-004: apps/Al1x/partition.ts extension”infrastructure/src/main/cdk/apps/Al1x/partition.ts is extended to instantiate PartitionEmailStack per active partition, wiring it to the partition’s existing pod IRSA role export (${publishingPrefix}-EksPodRoleArn from eks-stack.ts:250 — used only for the trust-policy ArnLike pattern match, not imported as an ARN).
Source: analysis.md G-12 (under Phase 4’s overall stack composition). Verifies: V-IAC-004.
REQ-IAC-005: Per-partition instance configurations
Section titled “REQ-IAC-005: Per-partition instance configurations”Declarative configuration for the partition-email stack lives at infrastructure/src/main/cdk/instances/Alpha001/{prod,demo}.ts and infrastructure/src/main/cdk/instances/Alpha002/{dev,stage}.ts. Each declares the Postmark account reference (from platform/postmark-service.ts), the partition’s sub-zone name ({partition}.ardamails.com), the DMARC reporting mailbox (reused dmarc-reports@arda.cards per pre-design follow-up C2), and any partition-specific overrides. kyle is excluded (DQ-R1-021).
Source: analysis.md G-12, §4 + §5. Verifies: V-IAC-005.
REQ-IAC-006: Stack composition is value-flowing only
Section titled “REQ-IAC-006: Stack composition is value-flowing only”PartitionEmailStack composes its sub-constructs (DnsZone, WriteNSRecordsToUpstreamDns, DnsEmailRecords, PostmarkSendingDomain wrapper, the two SM secrets, the two IAM roles) by passing Built values between them — no shared mutable state, no env-var bridges, no file-artifact channels. Per the typed source-of-truth pattern established in Phase 3 (learnings.md L-1).
Source: cross-cutting hygiene (analysis.md §10.4 reuse + DQ-R1-009 lesson). Verifies: V-IAC-006.
REQ-IAC-007: Reserved-words list extension (covered by REQ-PART-006)
Section titled “REQ-IAC-007: Reserved-words list extension (covered by REQ-PART-006)”(See REQ-PART-006. Recorded as both PART and IAC to surface in both traceability views.) Verifies: V-IAC-007.
REQ-IAC-008: CDK lifecycle invariants encoded in the construct test
Section titled “REQ-IAC-008: CDK lifecycle invariants encoded in the construct test”A unit test in the encryption-key construct’s test file asserts that the generateSecretString configuration is the expected shape and that subsequent cdk synth against the same inputs produces an identical template (re-run-as-no-op invariant per REQ-PART-016). The test fails closed if a future PR changes the generation config in a way that would trigger silent regeneration.
Source: C-EmailKey-At-Rest G-KEY-1 (lifecycle invariants). Verifies: V-IAC-008.
Operator entry-points (REQ-CLI)
Section titled “Operator entry-points (REQ-CLI)”REQ-CLI-001: tools/register-partition-mail-signature.ts entry script
Section titled “REQ-CLI-001: tools/register-partition-mail-signature.ts entry script”A new TypeScript script at infrastructure/tools/register-partition-mail-signature.ts is the Phase A imperative entry point for partition-mail provisioning. Two-positional-argument CLI shape: npx ts-node tools/register-partition-mail-signature.ts <infrastructure> <partition> (mirrors amm.sh’s <infrastructure> <partition> signature; see amm.sh line 140 for the canonical shape). Invoked from amm.sh per partition (amm.sh iterates if all was passed). Usage output: when invoked without arguments (or with --help / -h), the script emits a usage block to stderr listing the valid <infrastructure> values (Alpha001 / Alpha002; SandboxKyle002 rejected per PDEV-438), the partition-to-infrastructure mapping, and a brief description, then exits non-zero. Argparse validation: rejects unknown infrastructures, rejects retired infrastructures with a PDEV-438 reference, and rejects partition-infrastructure mismatches (Alpha001 dev) before any Postmark API call. The script: (a) calls the Postmark Account API to register the partition’s Sender Signature (idempotent — list-then-create); (b) captures the DKIM selector / public key / Return-Path target; (c) writes those values into cdk.context.json (committed in the per-partition PR). Exit codes: 0 on success; non-zero on argparse failure, Postmark API error, 1P resolution failure, or context-write failure. The script is not a standalone operator-facing CLI (per DQ-R1-022’s “no partition-mail-cli” framing) — operators interact via amm.sh.
Source: C-Operator-Surface G-OP-2. Verifies: V-CLI-001.
REQ-CLI-002: Phase A is idempotent
Section titled “REQ-CLI-002: Phase A is idempotent”Re-running register-partition-mail-signature.ts <infrastructure> <partition> against a partition whose Sender Signature already exists, with no DNS / Postmark drift, is a no-op (the list-then-create flow detects the existing Signature and skips creation). Re-runs of the same partition before successful first deploy are safe and converge to the same cdk.context.json state.
Source: C-Operator-Surface G-OP-2 + Phase 3 DQ-R1-013 pattern. Verifies: V-CLI-002.
REQ-CLI-003: tools/lib/ shared helpers
Section titled “REQ-CLI-003: tools/lib/ shared helpers”TypeScript helpers extracted from corporate-cli.ts (per pre-design follow-up B3 — minimal cut) live under a shared location (recommended infrastructure/tools/lib/; final location pinned in specification.md). Helpers consumed by both corporate-cli and register-partition-mail-signature.ts: Postmark Account API client with retry / backoff, idempotent list-then-create helpers for Sender Signatures, 1Password SDK resolution wrapper, output redaction for logs, conflict-check. No bash reimplementation of any of these — amm.sh invokes the TypeScript layer via ts-node.
Source: C-Operator-Surface G-OP-4. Verifies: V-CLI-003.
REQ-CLI-004: amm.sh partition-mail step
Section titled “REQ-CLI-004: amm.sh partition-mail step”infrastructure/amm.sh (or its callees) gains a partition-mail step that, for a given partition argument, executes three direct calls in order: (i) op read 'op://Arda-{Env}OAM/Postmark/credential' plus echo "::add-mask::..." for GHA log redaction; (ii) npx ts-node tools/register-partition-mail-signature.ts <infrastructure> <partition> (Phase A — Postmark API + cdk.context.json write); (iii) cdk deploy ${infrastructure}-${partition}-Email --parameters PostmarkAccountToken=<resolved> (Phase B — declarative deploy). Phase A precedes Phase B because CDK synth reads cdk.context.json.
Source: C-Operator-Surface G-OP-1, G-OP-3. Verifies: V-CLI-004.
REQ-CLI-005: Phase A failure semantics
Section titled “REQ-CLI-005: Phase A failure semantics”If Phase A fails (Postmark API error, 1P resolution failure, context-write failure), register-partition-mail-signature.ts exits non-zero with a redacted summary to stderr. amm.sh aborts the partition-mail step on a non-zero exit code — does NOT proceed to cdk deploy. Re-runs after fixing the underlying issue are idempotent (REQ-CLI-002).
Source: C-Operator-Surface (analogue of Phase 3’s DQ-R1-013). Verifies: V-CLI-005.
Operator prerequisites and verification (REQ-OPS)
Section titled “Operator prerequisites and verification (REQ-OPS)”REQ-OPS-001: 1Password vault state per partition
Section titled “REQ-OPS-001: 1Password vault state per partition”Each partition’s Arda-{Env}OAM vault (DevOAM, StageOAM, DemoOAM, ProdOAM) contains an item titled Postmark with a credential field holding the corresponding account-level API token (PostmarkProd token in DemoOAM and ProdOAM; PostmarkNonProd token in DevOAM and StageOAM). The operator confirms this pre-flight via op read "$(postmarkCredentialOpReference <partition>)" returning a non-empty value.
Source: C-Operator-Surface §10.2 already-in-place. Verifies: V-OPS-001.
REQ-OPS-002: Operator AWS SSO credentials
Section titled “REQ-OPS-002: Operator AWS SSO credentials”The operator running ./amm.sh <infrastructure> <partition> (or equivalent) has AWS SSO credentials for the partition’s hosting Infrastructure account: Admin-Alpha1 profile for prod / demo (Alpha001); Alpha002-Admin profile for dev / stage (Alpha002). Pre-flight: aws sts get-caller-identity --profile <profile> matches the expected account ID.
Source: operational prerequisite. Verifies: V-OPS-002.
REQ-OPS-003: dmarc-reports@arda.cards mailbox healthy
Section titled “REQ-OPS-003: dmarc-reports@arda.cards mailbox healthy”The Corporate-zone DMARC reporting mailbox (DQ-R1-015) is provisioned in Arda’s Google Workspace and receives mail. The operator confirms before any Phase 4 deploy that ingest is functional (a manual test message resolves, or recent traffic is visible in the mailbox). This is the same mailbox the Phase 4 partition DMARC records reference (REQ-PART-005).
Source: C-Operator-Surface inherited from DQ-R1-015. Verifies: V-OPS-003.
REQ-OPS-004: arda-nonprod account approval reply
Section titled “REQ-OPS-004: arda-nonprod account approval reply”After the dev partition’s Sender Signature on PostmarkNonProd is verified (REQ-PART-009 for dev), the operator replies to Postmark Compliance ticket #11236089 with the verified-domain evidence. The reply is operator-initiated (no automation). Documented assumption: Postmark’s policy accepts one verified non-prod Sender Signature as sufficient evidence to grant arda-nonprod account approval — matching the arda-prod precedent (ticket #11236087, approved 2026-05-11). If Postmark requires additional Signatures or other evidence, the operator updates this requirement and the affected rollout sequence.
Source: C-Postmark-Sending G-POSTMARK-5 / DQ-R1-023 unlock path. Verifies: V-OPS-004.
REQ-OPS-005: Operator sign-off
Section titled “REQ-OPS-005: Operator sign-off”The Phase 4 rollout completion is recorded in verification.md’s operator sign-off table — one row per partition (per dev, stage, demo, prod rollout), with operator name, date, deviations, and notes. No automated gate replaces the sign-off; the table is the auditable human record.
Source: project convention (matches Phase 3 REQ-OPS-003). Verifies: V-OPS-005.
CI workflows (REQ-CI)
Section titled “CI workflows (REQ-CI)”REQ-CI-001: runtime-platform-drift workflow
Section titled “REQ-CI-001: runtime-platform-drift workflow”A new GitHub Actions workflow at infrastructure/.github/workflows/runtime-platform-drift.yml runs on a daily cron schedule. Failure-issue labels are drift + runtime-platform per pre-design follow-up C3. The workflow uses the same operational pattern as Phase 1’s external-resources-drift.yml and Phase 3’s corporate-drift.yml — fails closed by opening a labelled GitHub issue. corporate-drift is not renamed (DQ-R1-018).
Source: C-Drift-Detection G-DRIFT-1. Verifies: V-CI-001.
REQ-CI-002: Drift driver script
Section titled “REQ-CI-002: Drift driver script”A TypeScript driver script (recommended infrastructure/tools/runtime-platform-drift.ts; final location pinned in specification.md) enumerates instances/Alpha001/{prod,demo}.ts + instances/Alpha002/{dev,stage}.ts and asserts, for each partition’s Sender Signature: Postmark API returns expected Name / DKIMPendingHost / DKIMHost / ReturnPathDomain; live DNS records resolve to the expected values; AWS Secrets Manager has both partition SM secrets; both IAM roles exist with the expected trust policy + permissions. Report shape matches Phase 1 / Phase 3 drift drivers.
Source: C-Drift-Detection G-DRIFT-2. Verifies: V-CI-002.
REQ-CI-003: Shared utilities extracted from corporate-drift
Section titled “REQ-CI-003: Shared utilities extracted from corporate-drift”Utility code shared between corporate-drift and runtime-platform-drift (Postmark API probe helpers, DNS lookup helpers, report-shape rendering, GitHub-issue creation) is extracted into a shared location (recommended infrastructure/tools/lib/drift/; final location pinned in specification.md). Both workflows source the extracted helpers per DQ-R1-018. The corporate-drift workflow continues to function unchanged after extraction — regression-tested by a workflow-level smoke test or a separate verification.md entry.
Source: C-Drift-Detection G-DRIFT-3. Verifies: V-CI-003.
Documentation deliverables (REQ-DOC)
Section titled “Documentation deliverables (REQ-DOC)”REQ-DOC-001: secret-delivery-pattern.md content
Section titled “REQ-DOC-001: secret-delivery-pattern.md content”current-system/oam/security/secret-delivery-pattern.md (committed as a stub on 2026-05-11) is filled in with full content covering: the op → amm.sh → CFN NoEcho parameter → SM secret → consumer flow; two worked examples (partitionSecrets.cfn.yaml and the Phase 4 Postmark token); rotation flow (1P-source-of-truth model); the “do not use” cases. Frontmatter maturity bumps from draft to review. Cross-link from secrets-vault.md is already in place.
Source: C-Documentation G-DOC-1. Verifies: V-DOC-001.
REQ-DOC-002: Per-partition mail pages in current-system/runtime/
Section titled “REQ-DOC-002: Per-partition mail pages in current-system/runtime/”New pages under current-system/runtime/ (final filenames pinned in specification.md) describe the partition-mail topology: the four sub-zones, the NS-delegation chain from ardamails.com, the relationship between Infrastructures and partition mail zones, the Postmark account-to-partition mapping. The current-system retrofit at project completion reconciles these pages with the broader runtime trees.
Source: C-Documentation G-DOC-2. Verifies: V-DOC-002.
REQ-DOC-003: Postmark-service updates in current-system/oam/postmark-service/
Section titled “REQ-DOC-003: Postmark-service updates in current-system/oam/postmark-service/”Existing pages in current-system/oam/postmark-service/ are extended to cover the multi-partition / multi-Signature operator surface: the Sender Signature inventory post-Phase-4, the per-partition account-to-Signature mapping, the runtime-platform-drift workflow’s place alongside corporate-drift. Includes a section on the per-partition deploy procedure invoking amm.sh.
Source: C-Documentation G-DOC-3. Verifies: V-DOC-003.
REQ-DOC-004: Encryption-key rotation operator runbook
Section titled “REQ-DOC-004: Encryption-key rotation operator runbook”A new operator runbook (location TBD in specification.md — either under current-system/oam/postmark-service/ or a new current-system/runtime/email-encryption-key.md) covers the AWS-SM-native rotation flow per DQ-R1-019: two CLI calls (aws secretsmanager get-random-password → aws secretsmanager put-secret-value) against {fqn}-I-EmailEncryptionKey → ESO refreshes → operations pod’s TokenCipher reloads → lazy + coroutine migration mops up. Includes a migration-completion verification query and the SDK-fallback alarm playbook. Linked from operator-runbook.md.
Source: C-Operator-Surface G-OP-5 + C-Documentation G-DOC-4. Verifies: V-DOC-004.
REQ-DOC-005: Current-system retrofit at project completion
Section titled “REQ-DOC-005: Current-system retrofit at project completion”After Phase 5b lands, the current-system pages added in REQ-DOC-002 + REQ-DOC-003 are reconciled with the broader current-system/runtime/ and current-system/oam/ trees. This is a project-closeout deliverable, not Phase 4’s PR — but it is recorded here so it is not lost.
Source: C-Documentation G-DOC-5. Verifies: project closeout (no V-NNN entry).
Traceability summary
Section titled “Traceability summary”| Capability | Requirements |
|---|---|
| C-DNS-Authority | REQ-PART-001, 002, 003, 004, 005, 006 |
| C-Postmark-Sending | REQ-PART-007, 008, 009, 010, 011, 012, 013 |
| C-EmailKey-At-Rest | REQ-PART-014, 015, 016 |
| C-Runtime-DNS-Writes | REQ-IAC-001, 002; REQ-PART-017, 018 |
| C-Fallback-Decryption | REQ-PART-019, 020 |
| C-Drift-Detection | REQ-CI-001, 002, 003 |
| C-Operator-Surface | REQ-CLI-001, 002, 003, 004, 005; REQ-OPS-001, 002, 003, 004, 005 |
| C-Documentation | REQ-DOC-001, 002, 003, 004, 005 |
| IaC hygiene (cross-cut) | REQ-IAC-003, 004, 005, 006, 007, 008 |
Every Capability is covered. Every requirement maps to exactly one Capability (REQ-IAC-007 is a cross-listing of REQ-PART-006 for traceability discoverability).
References
Section titled “References”analysis.md— Capability decomposition; gap rows; Implementation Groups; readiness criteria.../goal.md— project intent and success criteria.../../decision-log.md— DQ-R1-017..023 + pre-design follow-ups closed.email-server-key-encryption.md— encryption-key envelope + lifecycle invariants.../../cross-cutting-design.md— auth, secrets, drift; § 7a Message stream discipline.../../../../../technology/cdk-infrastructure.md— export-naming convention.- Phase 3 analogue:
3-corporate-updates/requirements.md(de-facto template).
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved