Review: Stalwart: Create domains + SMTP accounts via API

review-1736-2026-07-04 Review

review needs-refinement

Verdict: NEEDS_REFINEMENT

Template Completeness

  • [x] Type — Feature
  • [x] Lineage — "Depends on: pal-e-platform#498 (Stalwart install)"
  • [x] Repo — present (but incorrect, see below)
  • [x] User Story — present
  • [x] Context — good detail on both domains and SMTP-only accounts
  • [x] File Targets — present (but wrong targets, see below)
  • [x] Feature Flag — "none"
  • [x] Acceptance Criteria — 5 criteria
  • [x] Test Expectations — swaks test command
  • [x] Constraints — present
  • [x] Checklist — present
  • [x] Related — present

Traceability

  • [x] story:self-hosted label — "As platform admin, I want zero third-party email dependencies so I control deliverability"
  • [x] story note verified — found in project-stalwart-email user-stories section
  • [x] arch:stalwart label — references Stalwart Mail Server component
  • [x] arch note verified — arch-stalwart note exists in pal-e-docs with full architecture diagram
  • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/pal-e-services/issues/164, open

File Targets

  • [ ] terraform/keycloak.tf — ISSUE: This file exists in pal-e-services and contains the dynamic smtp_server block (lines 47-60), but it is for DOWNSTREAM work (configuring Keycloak to use Stalwart). This ticket's scope is creating domains/accounts IN Stalwart, not configuring Keycloak to connect.
  • [ ] "Document SMTP account credentials in Salt GPG pillar or secrets pipeline" — ISSUE: Vague. Per arch-stalwart note, the actual target should be salt/pillar/stalwart.sls in pal-e-platform (domains, accounts, DKIM keys). This file does not exist yet (created by #498).

Repo Placement

MISMATCH. Issue filed on ldraney/pal-e-services but:
  • Title says "via API" — implies Stalwart REST API calls (admin action on edge VPS, not code in pal-e-services)
  • Per arch-stalwart: "SaltStack managed: Declarative state in salt/states/stalwart/; pillar-driven config" — Salt files live in pal-e-platform
  • If automated via Salt pillar, repo should be ldraney/pal-e-platform
  • If manual API calls, this is more of a Task type (no code files to modify)
  • keycloak.tf in pal-e-services is the consumer of the credentials, not where they are created

Dependencies

  • Blocked by #1733 (pal-e-platform#498): "Salt: Install Stalwart binary + systemd service on edge VPS" — currently in backlog, state OPEN. Stalwart must exist before domains can be created on it.
  • Blocks #1737 (pal-e-services#165): DNS SPF/DKIM/DMARC for landscaping-assistant.app — needs DKIM public key output from this ticket
  • Blocks #1738 (pal-e-services#166): DNS SPF/DKIM/DMARC for westsidekingsandqueens.com — needs DKIM public key output from this ticket
  • Dependencies are partially documented (Lineage mentions #498; Related mentions "Blocked by: pal-e-platform#498"). Downstream blockers (#1737, #1738) are not documented in this issue but are captured on the board.

Acceptance Criteria

5 criteria — at the boundary but acceptable. All are verifiable:
  • Domain existence: verifiable via Stalwart admin API GET
  • Account auth: verifiable via swaks (test command provided)
  • DKIM export: verifiable by checking key file exists
Missing criterion: "SMTP credentials stored in secrets pipeline / Salt pillar for downstream Keycloak config." This bridges to the downstream work.

Blast Radius

Low risk. This is a configuration action on a new service (Stalwart). No existing services are modified. The only downstream consumers are:
  • DNS tickets (#1737, #1738) need the DKIM public keys as output
  • Keycloak realm SMTP config (separate ticket needed) needs the account credentials

Decomposition Assessment

5 acceptance criteria, 1 repo (actual work is API calls or Salt pillar), estimated agent time <5 minutes once Stalwart is running. No decomposition needed.

Recommendations

  • [BODY] Fix File Targets — remove terraform/keycloak.tf reference (downstream Keycloak config, not this ticket's scope). Replace with: (a) If Salt-managed: salt/pillar/stalwart.sls in pal-e-platform — add domain/account entries; or (b) If API-driven: document that this is a manual/scripted Stalwart admin API action with credential output stored in secrets pipeline.
  • [BODY] Fix Repo — if Salt-based automation, change to ldraney/pal-e-platform. If manual API action, change Type to Task and replace File Targets with a Scope section describing the admin workflow.
  • [BODY] Add output artifact — acceptance criteria should include "DKIM public keys written to a known location (e.g., ~/secrets/stalwart/dkim/) for downstream DNS tickets to consume."
  • [LABEL] Consider adding story:custom-domain as secondary label — this ticket directly enables per-domain sending (the custom-domain story), in addition to the self-hosted story.