Decision Log: Invitation Mail Server
Decision Log: Invitation Mail Server
Section titled “Decision Log: Invitation Mail Server”Purpose
Section titled “Purpose”Tracks design decisions for the invitation-mail-server project (goal, PDEV-1235): provisioning a partition-level system email server and delivering the common-module email-sending library. Decisions inherited from the email-integration project (DQ-009, DQ-R1-009, DQ-R1-017, DQ-R1-022, secret-delivery pattern) are constraints here, not re-opened.
Decision Table
Section titled “Decision Table”| # | Question | Status | Decision | Round |
|---|---|---|---|---|
| DQ-001 | Sender Signature for system.{partition}? | Decided | A — inherit the partition Signature | R1 |
| DQ-002 | System server token custody between deploys | Decided | B — partition-vault 1Password item | R1 |
| DQ-003 | CDK placement of new resources | Decided | A — extend PartitionEmailStack | R1 |
| DQ-004 | Source of the library sending classes | Decided | A — lift & generalize from operations; it adopts | R1 |
| DQ-005 | How components obtain a configured sender | Decided | A′ — component email block; server-agnostic library | R2 |
| DQ-006 | operations wiring scope in this project | Decided | A — secret projection + internal system sender | R2 |
| DQ-007 | Webhook posture for the system server (v1) | Decided | A — none (fire-and-forget); console follow-up filed | R2 |
Round 1: Infrastructure and Sourcing
Section titled “Round 1: Infrastructure and Sourcing”DQ-001: Sender Signature for system.{partition}?
Section titled “DQ-001: Sender Signature for system.{partition}?”Context: The system server sends from system.{partition}.{base}. Postmark verification per DQ-R1-009 (email-integration) anchors Sender Signatures at a parent domain, with leaf sub-domains inheriting DKIM. The partition Signature at {partition}.ardamails.com already exists (Phase 4); tenant leaves ride it (DQ-R1-017).
| Option | Description | Trade-offs |
|---|---|---|
| A | Inherit the partition Signature: send from system.{partition}.{base} with DKIM/Return-Path resolved at the partition parent. | Zero new DNS records, zero new signature registrations, no placeholder-context extension, minimal drift surface. System mail shares receiver-side reputation with tenant mail on the same partition. |
| B | Dedicated Signature at system.{partition} with its own DKIM TXT + Return-Path CNAME at the leaf. | Reputation isolation between administrative and tenant mail; costs a second Pre-Deploy registration, new DNS records in the partition zone, placeholder-context machinery extension, and added drift checks. |
Recommendation: Option A — consistent with how every tenant leaf already sends; reputation isolation has no evidence of need yet.
Decision: Option A. Reopen (→ Option B) if system-mail deliverability measurably suffers from tenant traffic on a shared partition Signature, or when the System Administration console (PDEV-1250) adds per-stream reputation monitoring that shows divergence.
Applied to:
- Design Document § Structural Design, § Behavioral Design (Provisioning)
- Goal § Constraints (domain fixed as
system.{partition}.{base})
DQ-002: System server token custody between deploys
Section titled “DQ-002: System server token custody between deploys”Context: Unlike the Postmark account token (operator-provisioned in 1Password, flowing 1P → NoEcho → SM), a Postmark server token is generated by Postmark at server creation. Something must custody it so subsequent deploys can (re)deliver it to the SM secret. Corporate precedent (corporate-cli, DQ-R1-007): persist to a 1Password vault item.
| Option | Description | Trade-offs |
|---|---|---|
| A | SM-only: Pre-Deploy re-reads ApiTokens[0] via GET /servers (using the account token it already holds) on every run and feeds the NoEcho parameter directly. | No new 1Password items; Secrets Manager is the single custody point. But the token is invisible to operators outside AWS, rotation is implicit, and every deploy depends on a live Postmark API read. |
| B | Partition-vault 1Password item: Pre-Deploy writes the token to the partition’s Arda-{Env}OAM vault at creation; deploys read 1P → NoEcho → SM like the account token. | Operator-visible custody and explicit rotation surface; symmetric with every other partition credential (vault convention: service-name-only item title, one item per env vault). Costs a new item in all four vaults and a second custody point to keep consistent. |
Recommendation: Option A — fewer moving parts, single custody point.
Decision: Option B (user override of the recommendation): the corporate precedent and the partition-vault convention win — operator visibility and an explicit rotation surface are worth the second custody point. New item title SystemEmailServer (field credential) in each Arda-{Env}OAM vault, added to all four per the vault convention; canonical op:// mapping lives in platforms.ts (PartitionMail), never in bash.
Applied to:
- Design Document § Structural Design (
PartitionMail, Pre-Deploy tool), § Behavioral Design (Provisioning), § Operations Impact - Goal § Success Criteria 1 (custody path)
DQ-003: CDK placement of new resources
Section titled “DQ-003: CDK placement of new resources”Context: The new SM secret ({fqn}-I-EmailSystemServerToken), its NoEcho parameter, and its -API- export need a stack. PartitionEmailStack ({fqn}-Email) already owns all partition mail resources; CFN stack names are immutable (workspace rule), and amm.sh already runs the --force/NoEcho parameter dance for this stack.
| Option | Description | Trade-offs |
|---|---|---|
| A | Extend PartitionEmailStack with the additional parameter, secret, and export. | One email stack per partition; rides the existing deploy path; cdk diff clean-or-additive. Couples system-email lifecycle to the existing stack (acceptable: same domain, same operator flow). |
| B | New sibling stack {fqn}-SystemEmail. | Isolated lifecycle/blast radius; but duplicates parameter plumbing, adds amm.sh ordering, and fragments the email provisioning story for no current benefit. |
Recommendation: Option A.
Decision: Option A. Reopen if system-email infra grows resources with a materially different lifecycle (e.g., its own zone or per-stream infrastructure).
Applied to:
- Design Document § Structural Design (
PartitionEmailStack), § Implementation Artifacts
DQ-004: Source of the library sending classes
Section titled “DQ-004: Source of the library sending classes”Context: operations already contains a generic, stateless Postmark sending client (PostmarkServerProxy.sendEmail, sealed SendEmailOutcome, wire types) and a retry core (EmailSender) entangled with its tenant ConfigurationService. The common-module library needs exactly this behavior.
| Option | Description | Trade-offs |
|---|---|---|
| A | Lift & generalize: move the wire types, send proxy, sealed outcome, retry core (parameterized on a token supplier + optional lifecycle hooks), and fromHeader into common-module; operations re-imports them for the per-tenant path. | Single implementation; no divergence between the system and per-tenant senders; the retry core’s tenant coupling stays behind an adapter in operations. Medium operations diff, well covered by existing unit/integration tests. |
| B | Fresh library classes; operations keeps its private copy. | Zero operations regression risk in this project, but two Postmark send clients drift apart; the accounts follow-up inherits the dedup debt. |
Recommendation: Option A.
Decision: Option A. The lift preserves public behavior; operations’ existing email tests are the regression net.
Applied to:
- Design Document § Structural Design (library elements), § Implementation Artifacts (
common-module,operations)
Round 2: Library API and Runtime Wiring
Section titled “Round 2: Library API and Runtime Wiring”DQ-005: How components obtain a configured sender
Section titled “DQ-005: How components obtain a configured sender”Context: The library must serve two construction paths: the system sender (config/secret-driven, this project) and operations’ per-tenant senders (DB-driven EmailConfiguration). ConfigurationProvider already demonstrates the component-level binding pattern (globalDsConfiguration: optional top-level HOCON block, lazily bound, smart-constructed with Result).
| Option | Description | Trade-offs |
|---|---|---|
| A | Component-level email block: ConfigurationProvider binds an optional top-level email {} HOCON block via EmailConfig.fromConfig(): Result<EmailConfig>; token value reaches HOCON through the existing arda.config.location secret-file layering. | System email becomes a first-class component capability; accounts adoption ≈ config + one call. Adds a binding to ConfigurationProvider (small, optional-block precedented). |
| B | Consumer-wired factory: library ships pure classes; each component reads its secret mount and wires the sender in its composition root (like TokenCipher material today). | No ConfigurationProvider change, maximal flexibility; every adopter re-implements boot wiring. |
Recommendation: Option A.
Decision: Option A with a clarification (A′): the library is email-server-identity-agnostic. It provides the classes to send email given a configured email server — a server-connection value type (API base, server token, from-domain parameters), a factory that builds a sender from that value, and the config binding that produces the value from HOCON. The same server value type is constructible from operations’ DB-based EmailConfiguration, so the config-bound path (system) and the DB-bound path (per-tenant) are two producers of one input type. No server identity, domain literal, or account knowledge is baked into common-module.
Applied to:
- Design Document § Structural Design (library API), § Behavioral Design (Construction paths), § Module-Interaction Mechanics
DQ-006: operations wiring scope in this project
Section titled “DQ-006: operations wiring scope in this project”Context: The accounts invitation flow adopts the capability in a follow-up project. This project must decide how much runtime consumption operations ships now, given success criterion 2 (integration-tested sending path) and the risk of provisioning a secret nothing consumes.
| Option | Description | Trade-offs |
|---|---|---|
| A | Secret + internal sender: project the system-token secret (ExternalSecret entry + mount), bind the email block, construct the system EmailSender at boot, expose it internally (no REST). | Proves infra → secret → config → send end-to-end in a deployed component; dev-partition smoke can send real mail via PostmarkNonProd. Modest, additive diff. |
| B | None: library proven by common-module tests only. | Smallest diff; but the provisioned secret ships unconsumed and the seam is first exercised by the follow-up. |
| C | A + a guarded diagnostic REST endpoint for test sends. | Best operability; adds API surface and auth review this project doesn’t need. |
Recommendation: Option A.
Decision: Option A. The diagnostic surface is deferred to the System Administration console (PDEV-1250).
Applied to:
- Design Document § Structural Design (
operationswiring), § Behavior Verification (E2E), § Operations Impact
DQ-007: Webhook posture for the system server (v1)
Section titled “DQ-007: Webhook posture for the system server (v1)”Context: The existing postmark-events webhook route is tenant-scoped by construction (opaque routing token → tenantId → tenant-owned rows). A system server would need a non-tenant scope to reuse it.
| Option | Description | Trade-offs |
|---|---|---|
| A | No webhook — fire-and-forget: system sends carry no bounce/complaint ingestion; delivery issues observed via Postmark console + logs/metrics. Per-message status remains interrogable on demand via the server-token Messages API (GET /messages/outbound/{messageid}/details). | No changes to tenant-scoped webhook machinery; monitoring debt explicitly parked on the console follow-up. |
| B | Install a webhook to a new system scope now: mint a system routing token, extend the resolver and record* paths beyond tenant scope. | Bounce/suppression tracking from day one, but touches the tenant-scoped machinery this project otherwise avoids, before a real consumer exists. |
Recommendation: Option A.
Decision: Option A, plus a filed follow-up: PDEV-1250 — System Administration console for system email capability (low priority) covers capability management, sent-mail monitoring, and the webhook-vs-polling ingestion decision.
Applied to:
- Design Document § Behavioral Design (Robustness), § Operations Impact, § Out of Scope
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved