docs: document the Campaign-to-Mail delivery boundary
This commit is contained in:
@@ -31,7 +31,7 @@ The module has one required runtime dependency:
|
||||
Files and mail are optional module integrations declared in the campaign manifest:
|
||||
|
||||
- `govoplan-files` enables managed attachment selection, frozen file-version evidence, and managed-file usage tracking. Server/API campaigns require this integration for attachments and never resolve caller-supplied local filesystem paths. Legacy file-oriented loading remains available only to explicitly trusted operator/library workflows.
|
||||
- `govoplan-mail` enables reusable mail profiles, delivery policy checks, SMTP sending, and IMAP append behavior. Without it, campaigns can still be authored, validated, built, and reported, but real delivery/profile features are unavailable.
|
||||
- `govoplan-mail` owns reusable profiles, encrypted SMTP/IMAP credentials, delivery policy checks, connection tests, and transport execution. Campaign JSON stores only `server.mail_profile_id`; inline transport settings and credentials are rejected. Without Mail, campaigns can still be authored, but profile validation and real delivery are unavailable.
|
||||
|
||||
Public campaign, version, job, and report responses expose business data and
|
||||
delivery evidence, but never process-local paths, storage-backend keys, or
|
||||
@@ -91,7 +91,10 @@ Platform RBAC and governance rules are documented in `govoplan-core/docs/`.
|
||||
|
||||
## Operations
|
||||
|
||||
- [Campaign handbook](docs/CAMPAIGN_HANDBOOK.md) provides the adaptive user, process, governance, technical, and operations perspectives.
|
||||
- [Campaign delivery runbook](docs/CAMPAIGN_DELIVERY_RUNBOOK.md) covers queueing, local vs Celery operation, retries, reconciliation, reports, and the live SMTP/IMAP test checklist.
|
||||
- Report-email preview uses the selected version's stored v5 Mail-profile evidence. Live report email fails closed until [govoplan-mail#17](https://git.add-ideas.de/add-ideas/govoplan-mail/issues/17) provides a durable, idempotent Mail-owned outbox and transport-attempt ledger; per-job CSV is off by default and requires `campaigns:recipient:export` when requested.
|
||||
- [Campaign/Mail profile boundary](docs/MAIL_PROFILE_BOUNDARY.md) defines profile-only delivery, runtime resolution, execution evidence, and the fail-closed legacy migration path.
|
||||
- [Recipient import guide](docs/RECIPIENT_IMPORT_GUIDE.md) covers user/admin workflows, mapping profiles, validation, and import evidence.
|
||||
- [Recipient and address boundary](docs/RECIPIENT_ADDRESS_BOUNDARY.md) defines the split between campaign-local recipients and future reusable address management.
|
||||
- [Example campaigns and release checklist](docs/EXAMPLE_CAMPAIGNS_AND_RELEASE_CHECKLIST.md) defines the maintained example scenarios and release gates.
|
||||
|
||||
@@ -15,6 +15,8 @@ been validated, built, reviewed, and locked.
|
||||
## Before First Live Use
|
||||
|
||||
- Use dedicated non-production SMTP/IMAP credentials.
|
||||
- Store and test those credentials in a Mail-module profile. Campaign must
|
||||
contain only the selected `server.mail_profile_id` reference.
|
||||
- Start the repository test bed in `dev/mail-testbed/` when a local
|
||||
production-like SMTP/IMAP server is sufficient.
|
||||
- Use a dedicated mailbox/folder for append-to-Sent tests.
|
||||
@@ -64,6 +66,13 @@ Before the first live send for a sender domain or mail-server profile:
|
||||
- `claimed` or `sending` that does not progress: treat as a worker interruption.
|
||||
Re-run worker handling or reconcile if SMTP may already have accepted the
|
||||
message.
|
||||
- IMAP `appending`: A worker owns the durable append claim. Do not start a
|
||||
second append; if the worker cannot finish, reconcile only after checking the
|
||||
mailbox.
|
||||
- IMAP `outcome_unknown`: Never append automatically. An operator with
|
||||
`campaigns:campaign:reconcile` must record an evidence note and resolve it as
|
||||
`imap_appended` or `imap_not_appended`. Only the latter becomes explicitly
|
||||
retryable.
|
||||
|
||||
## Reconciliation
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ schema and are safe to run in non-production environments.
|
||||
- campaign with global attachments and recipient-specific attachment rules
|
||||
- campaign with password-protected ZIP attachments
|
||||
- campaign using a reusable mail profile from the mail module
|
||||
- campaign using inline SMTP/IMAP settings where policy allows campaign-local
|
||||
settings
|
||||
- legacy inline SMTP/IMAP campaign rejected with an actionable profile-migration
|
||||
error and no returned transport data
|
||||
- campaign with validation warnings that may be sent only after explicit review
|
||||
- campaign with blocked recipients or attachment errors that must not be sent
|
||||
- mock delivery campaign that captures SMTP and IMAP append messages in the mail
|
||||
@@ -28,8 +28,8 @@ schema and are safe to run in non-production environments.
|
||||
|
||||
- Examples must not contain production recipient data or production credentials.
|
||||
- Attachment examples should use deterministic small files and checksums.
|
||||
- Secret values must be represented through saved-credential placeholders or
|
||||
secret references.
|
||||
- Mail secrets belong only to encrypted Mail profiles and must never appear in
|
||||
a campaign fixture, placeholder, or campaign-local secret reference.
|
||||
- Examples that require optional modules must declare the required modules and
|
||||
capabilities in their README or fixture metadata.
|
||||
- Examples must stay valid when files or mail modules are physically absent,
|
||||
@@ -49,8 +49,10 @@ Before tagging a campaign release:
|
||||
- Run the GreenMail SMTP/IMAP smoke for a non-production real delivery path.
|
||||
- Confirm reusable mail profile selection is revalidated after campaign owner
|
||||
transfer.
|
||||
- Confirm inline SMTP/IMAP settings are hidden or blocked when policy disables
|
||||
campaign-local mail settings.
|
||||
- Confirm every inline SMTP/IMAP field is rejected on import/write, omitted
|
||||
from responses, and blocked from legacy execution until explicitly migrated.
|
||||
- Confirm execution snapshots store only the Mail profile reference and
|
||||
opaque Mail-owned transport revisions, not resolved transport material.
|
||||
- Confirm delivery reports include SMTP outcome, IMAP append outcome, latest
|
||||
error, generated EML reference, and attachment evidence.
|
||||
- Confirm retries cannot resend messages already accepted by SMTP unless an
|
||||
@@ -71,6 +73,6 @@ Use the Review & Send preflight panel and the delivery runbook together:
|
||||
2. Build exact messages.
|
||||
3. Review warnings, generated recipients, body content, and attachment evidence.
|
||||
4. Run mock delivery if available for the release channel.
|
||||
5. Test SMTP and IMAP settings against non-production infrastructure.
|
||||
5. Test the selected Mail profile against non-production infrastructure.
|
||||
6. Send only after queue, rate limit, and append-to-Sent behavior are understood.
|
||||
7. Reconcile failed, unknown, or pending jobs from the report/audit surfaces.
|
||||
|
||||
90
docs/MAIL_PROFILE_BOUNDARY.md
Normal file
90
docs/MAIL_PROFILE_BOUNDARY.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
1. A Mail administrator creates and tests a reusable profile in Mail.
|
||||
2. A campaign author opens **Mail settings** and selects one profile available
|
||||
for the campaign's tenant, owner, and policy context.
|
||||
3. Campaign stores only the stable profile identifier.
|
||||
4. 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.
|
||||
5. 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.
|
||||
6. 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.
|
||||
7. 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`](https://git.add-ideas.de/add-ideas/govoplan-mail/issues/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.
|
||||
|
||||
## Legacy versions and migration
|
||||
|
||||
Older versions can contain inline SMTP/IMAP settings or credentials. GovOPlaN
|
||||
does not delete or rewrite those stored audit records automatically:
|
||||
|
||||
- API responses omit all legacy transport fields and secrets and expose a
|
||||
`mail_profile_migration_required` marker.
|
||||
- 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 for audit but cannot be used
|
||||
for delivery. Revalidate and rebuild an editable profile-only version. Removing
|
||||
the remaining live and backup secret copies requires the separately approved
|
||||
remediation in
|
||||
[`govoplan-campaign#77`](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/77).
|
||||
|
||||
## 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.
|
||||
@@ -15,7 +15,7 @@ campaign schema and do not require production data.
|
||||
| `recipient-attachment-rules` | core, access, campaigns; optional files | Match recipient-specific attachment rules and verify per-recipient evidence. |
|
||||
| `zip-protected` | core, access, campaigns | Build password-protected AES ZIP output and verify password-source metadata. |
|
||||
| `mail-profile-send` | core, access, campaigns, mail | Select a reusable mail profile and send through the GreenMail test bed. |
|
||||
| `inline-mail-settings` | core, access, campaigns, mail | Use campaign-local SMTP/IMAP settings only when policy allows it. |
|
||||
| `legacy-inline-mail-rejected` | core, access, campaigns, mail | Confirm legacy campaign-local SMTP/IMAP data fails closed and requires explicit profile migration. |
|
||||
| `warnings-review` | core, access, campaigns | Require explicit review before queueing jobs with warnings. |
|
||||
| `blocked-send` | core, access, campaigns | Confirm blocked recipients or missing attachments cannot be queued. |
|
||||
| `mock-delivery` | core, access, campaigns, mail with dev capability | Capture messages in the development mailbox. |
|
||||
@@ -26,8 +26,8 @@ campaign schema and do not require production data.
|
||||
- Do not commit real recipients, mail credentials, or production attachment
|
||||
names.
|
||||
- Keep attachments deterministic and small.
|
||||
- Store secrets as placeholders, saved-credential references, or local `.env`
|
||||
values consumed by the test bed.
|
||||
- Store transport secrets only in encrypted Mail profiles or local `.env`
|
||||
values consumed directly by the test bed, never in campaign JSON.
|
||||
- Declare optional module requirements in fixture metadata.
|
||||
- Fixtures must not import files or mail modules directly; optional behavior is
|
||||
discovered through core module metadata and capabilities.
|
||||
|
||||
Reference in New Issue
Block a user