4.4 KiB
Campaign and Mail Profile Boundary
Product view
A campaign chooses an authorized Mail profile. It does not define a mail server. The Campaign module owns recipients, content, attachment rules, delivery intent, review state, and delivery evidence. The Mail module owns the SMTP/IMAP endpoints, encrypted credentials, connection tests, profile policy, and runtime transport adapters.
The persisted campaign contract is therefore deliberately narrow:
{
"server": {
"mail_profile_id": "stable-mail-profile-id"
}
}
No server.smtp, server.imap, server.credentials, credential-inheritance
override, or password is valid campaign JSON.
User journey
- A Mail administrator creates and tests a reusable profile in Mail.
- A campaign author opens Mail settings and selects one profile available for the campaign's tenant, owner, and policy context.
- Campaign stores only the stable profile identifier.
- Validation asks Mail to authorize the active profile and returns only availability flags plus opaque Mail-owned transport revisions. Reading that summary does not decrypt credentials.
- Build stores the profile identifier, delivery policy, job manifest, and non-secret transport revisions as execution evidence. It stores no resolved host, username, password, or other transport material.
- A delivery worker invokes one Mail-owned effect operation. Mail re-authorizes the profile, resolves credentials, compares the expected transport revision, checks policy, and sends or appends without returning transport material to Campaign. If the selected profile or its non-secret transport settings changed after build, delivery stops until the campaign is revalidated and rebuilt. A password rotation that leaves the transport identity unchanged does not invalidate the build.
- Mail returns only Campaign-owned envelope addresses, counts, sanitized refusal classifications/status codes, or the selected Sent folder. Raw server banners, provider bytes, host details, and credentials never enter Campaign attempts or public evidence.
Non-dry Campaign report email currently fails closed. It must not bypass the
durable job/effect model through a direct SMTP call. Re-enabling it requires the
Mail-owned idempotent outbox, attempt, unknown-outcome, and reconciliation path
tracked in
govoplan-mail#17.
Report generation and dry-run validation remain separate from an external
effect; recipient-level exports require recipient-export authorization.
Campaign authors need mail:profile:use in addition to the relevant Campaign
permission. Profile visibility remains governed by Mail policy and campaign
owner context.
Existing database rows and migration
The current database can contain campaign versions with inline SMTP/IMAP settings or credentials. There is no separate historical Campaign JSON corpus to import or remediate. GovOPlaN treats those inline fields as inert legacy material and does not delete or rewrite the stored audit rows automatically:
- API responses omit all legacy transport fields and secrets and expose a
mail_profile_migration_requiredmarker. - validation, build, queue, retry, and delivery fail closed with an actionable profile-migration error;
- unrelated edits cannot silently scrub the legacy fields;
- an editable version is migrated only through an explicit Mail-settings save with an authorized profile; and
- a locked version remains unchanged. Creating its editable successor records the migration while retaining the locked source as audit evidence.
Legacy execution snapshots are likewise retained but cannot be used for delivery. Revalidate and rebuild an editable profile-only version. Normal database backup, restore, encryption, and access controls apply to the current database as a whole; the product does not define a separate historical-JSON or inline-secret recovery workflow. A restored legacy row remains inert and fail-closed under the same rules.
Operator checks
Before live delivery, confirm that:
- the profile is active and still authorized for the campaign owner;
- SMTP and optional IMAP profile tests pass;
- validation and build occurred after the latest transport-identity change;
- append-to-Sent is enabled only when the selected profile has IMAP; and
- reports show the profile-bound snapshot revisions and delivery outcomes, never credentials or resolved transport configuration.