623 lines
32 KiB
Markdown
623 lines
32 KiB
Markdown
# Campaign Handbook
|
||
|
||
## Purpose and status
|
||
|
||
This is the canonical, multi-perspective handbook for the Campaign module. It
|
||
describes the current implementation, the operational contract it relies on,
|
||
and the remaining work required before Campaign can be presented as GovOPlaN's
|
||
maintained reference composition.
|
||
|
||
Use the section that matches the task at hand:
|
||
|
||
| Perspective | Start here |
|
||
| --- | --- |
|
||
| Campaign author | [Prepare a campaign](#prepare-a-campaign) |
|
||
| Reviewer | [Review and complete review](#review-and-complete-review) |
|
||
| Sender or delivery operator | [Deliver and resolve outcomes](#deliver-and-resolve-outcomes) |
|
||
| Campaign or tenant administrator | [Administration and policy](#administration-and-policy) |
|
||
| Platform operator | [Operations and recovery](#operations-and-recovery) |
|
||
| Integrator or developer | [Composition and integration contracts](#composition-and-integration-contracts) |
|
||
| Security, privacy, or audit reviewer | [Assurance model](#assurance-model) |
|
||
| Release reviewer | [Reference-composition acceptance](#reference-composition-acceptance) |
|
||
|
||
The shorter task documents remain useful companions:
|
||
|
||
- [Campaign delivery runbook](CAMPAIGN_DELIVERY_RUNBOOK.md)
|
||
- [Mail profile boundary](MAIL_PROFILE_BOUNDARY.md)
|
||
- [Recipient import guide](RECIPIENT_IMPORT_GUIDE.md)
|
||
- [Recipient and Addresses boundary](RECIPIENT_ADDRESS_BOUNDARY.md)
|
||
- [Examples and release checklist](EXAMPLE_CAMPAIGNS_AND_RELEASE_CHECKLIST.md)
|
||
|
||
## What Campaign is for
|
||
|
||
Campaign turns governed source data into individually built messages or
|
||
printable output and then controls their review, delivery, and evidence. It is
|
||
intentionally a composition module: it demonstrates how one user journey can
|
||
use optional Mail, Files, Addresses, Distribution Lists, Templates, Postbox,
|
||
and Notifications capabilities alongside Core access/audit infrastructure
|
||
without copying ownership from those modules. Policy may consume Campaign
|
||
context through a narrow capability; Campaign does not import Policy.
|
||
|
||
Campaign owns:
|
||
|
||
- the communication purpose, content, campaign-local fields, and templates;
|
||
- campaign-local recipient snapshots, exclusions, and personalization;
|
||
- message and attachment rules for a version;
|
||
- validation, review, build, queue, and delivery-control state;
|
||
- the durable jobs and attempts needed to explain delivery outcomes; and
|
||
- campaign-specific reports, shares, and frozen execution evidence.
|
||
|
||
Campaign does not own:
|
||
|
||
- SMTP/IMAP profiles, credentials, protocol adapters, or mailbox policy;
|
||
- long-lived address-book master data, consent lifecycle, or deduplication;
|
||
- managed file bytes, connector credentials, or external-file provenance;
|
||
- general-purpose workflow definitions; or
|
||
- global identity, organization, permission, or retention policy.
|
||
|
||
Those boundaries matter in both persistence and UI. A campaign references an
|
||
authorized Mail profile and managed file versions; it must never become a
|
||
second secret store, file store, or address directory.
|
||
|
||
## Process view
|
||
|
||
The supported process is a controlled progression, not a single "send" call:
|
||
|
||
```text
|
||
create/edit
|
||
-> validate and resolve policy/integrations
|
||
-> review warnings and blockers
|
||
-> build exact recipient messages and/or printable artifacts
|
||
-> complete review and queue
|
||
-> selected Mail, Postbox, or print effect per job
|
||
-> optional IMAP append per accepted job
|
||
-> report, retry, reconcile, or correct
|
||
-> archive when no active/uncertain delivery remains
|
||
```
|
||
|
||
Campaign status summarizes the whole campaign. Version workflow state describes
|
||
the selected immutable/editable version. Each recipient job separately records
|
||
build, validation, queue, SMTP, and IMAP state. Operators must use the job-level
|
||
states when deciding whether another external effect is safe.
|
||
|
||
Important distinctions:
|
||
|
||
- **Validation lock** is the reversible lock created by a successful
|
||
validation/build path. Editing requires unlocking and invalidates derived
|
||
evidence as appropriate.
|
||
- **User lock** is an explicit audit-safe lock. A permanently locked or
|
||
delivery-final version is not edited in place; create an editable successor.
|
||
- **SMTP accepted** means the provider accepted the message. It does not prove
|
||
inbox delivery, reading, or business acknowledgement.
|
||
- **Outcome unknown** means an attempt may have had an external effect. It must
|
||
be investigated and reconciled before retry.
|
||
- **IMAP append** is a separate effect after SMTP acceptance. An append failure
|
||
is not evidence that sending failed.
|
||
- **Archive** preserves evidence. Draft-only campaigns without built, locked,
|
||
or delivery evidence may be deleted where policy allows; evidence-bearing
|
||
campaigns are archived instead.
|
||
- **Copy campaign** creates a new campaign and one fresh editable version from
|
||
the selected source version. It copies configuration, but never delivery
|
||
jobs, outcomes, shares, locks, or audit evidence.
|
||
- **Archive historical version** hides only a non-current version from the
|
||
default history. It does not change the version's workflow state or remove
|
||
configuration, reports, delivery results, or audit evidence.
|
||
|
||
Campaign lifecycle confirmations are bound to the state shown in the UI. If a
|
||
job, version, share, or campaign state changes before confirmation, the server
|
||
rejects the stale action and requires a reload. The lifecycle-policy response
|
||
states the applicable built-in rule and the reason for every unavailable
|
||
action.
|
||
|
||
## User tasks
|
||
|
||
### Prepare a campaign
|
||
|
||
1. Create a campaign and confirm its owner or owning group.
|
||
2. Define global settings, fields, templates, and recipient data.
|
||
3. Import one-off recipient data or select a reusable Addresses source when the
|
||
optional capability is installed. Review source provenance and stale-source
|
||
warnings; Campaign freezes the selected rows rather than following later
|
||
directory changes silently.
|
||
4. Select managed attachments through Files. Server/API campaigns do not accept
|
||
arbitrary local paths. Preview rules and unmatched files before building.
|
||
5. Open **Mail settings** and select an available Mail profile. The campaign
|
||
stores only `server.mail_profile_id`; it never accepts SMTP/IMAP settings,
|
||
usernames, passwords, or credential references.
|
||
If the selected profile permits a campaign-scoped Mail credential, that
|
||
credential remains Mail-owned even though it is created from this surface.
|
||
For that credential and for password-valued campaign fields, the shared
|
||
password generator keeps its candidate separate from the form until **Use
|
||
password** is explicitly confirmed. Copying a candidate does not save or
|
||
submit it.
|
||
6. Save the editable version, validate the relevant sections, and resolve every
|
||
blocking issue. Warnings remain explicit review decisions.
|
||
7. Build the exact messages and inspect recipient, addressing, template,
|
||
attachment, and generated-message evidence.
|
||
|
||
If a selected optional module is absent, Campaign remains loadable and explains
|
||
which function is unavailable. It must not fail startup because Mail, Files, or
|
||
Addresses is not installed.
|
||
|
||
### Review and complete review
|
||
|
||
The reviewer should verify the immutable candidate that will be delivered, not
|
||
just the authoring form:
|
||
|
||
1. Confirm purpose, owner, selected version, and recipient count.
|
||
2. Inspect blocking errors, warnings, exclusions, and recipients requiring
|
||
review.
|
||
3. Inspect representative and exceptional rendered messages, including From,
|
||
To/CC/BCC, Reply-To, subject, body, and attachment evidence.
|
||
4. Confirm the selected Mail profile is authorized for the campaign's current
|
||
tenant and owner context.
|
||
5. Confirm attachment behavior when a rule matches no files, ZIP/password
|
||
behavior, and any recipient-specific files.
|
||
6. Record review completion and the inspected message keys through the review
|
||
surface. Validation, build, review, and exception evidence records the actor,
|
||
timestamp, and immutable build token/message digest where applicable. If
|
||
content, recipients, attachment inputs, owner context, or non-secret
|
||
transport identity changes, revalidate and rebuild.
|
||
|
||
The Review & Send surface separates three kinds of attention. Critical
|
||
blockers must be corrected before delivery, individual review items require a
|
||
recorded message decision, and non-critical group items may be acknowledged
|
||
together after individual review is complete. Each warning or blocker names
|
||
the required action, the responsible role, and the workspace to open. The
|
||
review summary keeps reviewed and remaining counts visible; a completed review
|
||
acknowledges the group items and remains bound to the current build token.
|
||
|
||
This evidence is the Campaign input to separation-of-duties policy. Generic
|
||
approve/reject chains, delegation, substitutions, escalation, and signatures
|
||
belong to the optional Approvals capability. Campaign must not claim an
|
||
approval merely because validation, building, or message review completed.
|
||
|
||
When a campaign has an Approval request reference, mock and real delivery
|
||
resolve `approvals.requests` and require an approved request for the exact
|
||
`campaign_version` subject and current version id. A missing Approvals module,
|
||
unknown request, pending/rejected chain, or approval for an older version blocks
|
||
delivery with an explicit requirement. Campaign stores the approval reference,
|
||
not Approval tables; changing the campaign version requires a new exact-subject
|
||
approval.
|
||
|
||
Normal readers and reviewers see business state and safe evidence. Process-local
|
||
paths, storage keys, worker claim tokens, and raw provider diagnostics require
|
||
the dedicated diagnostic permission and must not leak through ordinary campaign,
|
||
version, job, or report responses.
|
||
|
||
Campaign now provides a separate aggregate **Reports** surface for readers with
|
||
`campaigns:report:read` and access to the campaign. It loads only the safe
|
||
aggregate projections, applies small-cell suppression, and offers no recipient
|
||
rows, drill-down, filtering, export, or delivery actions. The recipient-aware
|
||
**Campaign Report** still requires recipient-read access and does not yet hide
|
||
every action control that the actor lacks. The server authorizes each action,
|
||
but permission-aware action visibility on that detailed surface remains open
|
||
work; do not confuse it with the aggregate reader experience.
|
||
|
||
The module-local aggregate surface remains at `/campaigns/reports`. When the
|
||
optional Reporting module is enabled, Campaign also contributes the same
|
||
recipient-free projection as the `campaigns/delivery-outcomes` report provider.
|
||
Reporting owns `/reports`, records the run purpose, effective audience, source
|
||
campaign/version revision, privacy transformations, retention, actor/time,
|
||
output hash, and export history, and applies Policy before returning the
|
||
result. Campaign does not register a fallback `/reports` route when Reporting
|
||
is absent.
|
||
|
||
The detailed Campaign Report includes the selected campaign's effective
|
||
retention policy, its system/tenant/owner/campaign provenance, and the current
|
||
evidence state. It distinguishes retained, redacted, expired, partially
|
||
minimized, unavailable, and not-applicable source JSON, stored report detail,
|
||
generated EML, and Postbox-copy evidence. When Policy is absent, the report
|
||
shows the platform defaults and explicitly warns that automated retention
|
||
enforcement is unavailable. Retention removes or minimizes detail; aggregate
|
||
counters and audit references may remain so outcomes can still be explained.
|
||
|
||
### Deliver and resolve outcomes
|
||
|
||
Use the [delivery runbook](CAMPAIGN_DELIVERY_RUNBOOK.md) for the detailed
|
||
operator sequence.
|
||
|
||
At a minimum:
|
||
|
||
1. Queue only a validated, locked, built version. Use **Queue for workers** for
|
||
ordinary batches; the durable progress remains visible after leaving and
|
||
returning to Review and send.
|
||
2. Use **Send now** only when the exact persisted eligible build is within the
|
||
effective synchronous limit shown by the UI. The default deployment limit
|
||
is 25 recipient jobs. The backend repeats the count and preflights every
|
||
message and the Mail profile revision before contacting SMTP.
|
||
3. Treat `smtp_accepted` as protected from ordinary retry.
|
||
4. Retry `failed_temporary` explicitly after inspecting the cause.
|
||
5. Include `failed_permanent` only after correcting the cause and making a
|
||
conscious override.
|
||
6. Never retry `outcome_unknown` blindly. Inspect SMTP/provider evidence and
|
||
reconcile it as **accepted** or **not sent**, with a note identifying the
|
||
evidence.
|
||
7. Process append-to-Sent only for SMTP-accepted jobs and investigate append
|
||
failure independently. Never retry an `outcome_unknown` IMAP append blindly:
|
||
reconcile mailbox evidence as **appended** or **not appended** with a note.
|
||
Only the latter becomes explicitly retryable, and neither decision resends
|
||
the already SMTP-accepted message.
|
||
8. Archive only after active and uncertain effects are resolved.
|
||
9. Use **Delete draft** only for an untouched draft. If retained evidence or an
|
||
active share exists, revoke the share where appropriate or archive instead.
|
||
|
||
Pause stops new eligible work but cannot undo a provider effect already in
|
||
progress. Cancel marks work that has not yet produced a protected SMTP outcome;
|
||
it cannot recall accepted mail.
|
||
|
||
The deployment ceiling is configured with
|
||
`GOVOPLAN_CAMPAIGN_SYNCHRONOUS_SEND_MAX_RECIPIENTS` (0 disables Send now; the
|
||
accepted range is 0–500). A tenant may only narrow that ceiling with
|
||
`tenant.settings.campaign_delivery_policy.synchronous_send_max_recipients`.
|
||
The effective value and source are returned by the protected delivery-options
|
||
API, recorded for successful/rejected synchronous commands, and stated in the
|
||
configured handbook topic.
|
||
|
||
### Test and one-message actions
|
||
|
||
The current baseline includes mock send, queue dry-run, synchronous immediate
|
||
delivery, sending one selected job, retry selection, and unattempted-job
|
||
selection. Their audit and state behavior is not yet the final vocabulary
|
||
accepted for issue `govoplan-campaign#69`.
|
||
|
||
The intended distinction is:
|
||
|
||
- **Test:** deliver once to the configured test path, audit it, and leave the
|
||
production job unsent.
|
||
- **Single send:** send one unsent job, audit it, and mark its production effect
|
||
complete.
|
||
- **Single resend:** intentionally send one job again regardless of an earlier
|
||
failure or acceptance, with distinct authority, warning, reason, and audit.
|
||
|
||
Until that slice is implemented and target-tested, do not present the existing
|
||
buttons as a complete resend policy. Ordinary retry protection remains the safe
|
||
default.
|
||
|
||
## Data and evidence model
|
||
|
||
### Versions and snapshots
|
||
|
||
Editable campaign JSON is versioned. Build creates recipient jobs and an
|
||
execution snapshot. A new profile-only snapshot contains the stable Mail
|
||
profile id, delivery policy/evidence, and opaque Mail-issued transport
|
||
revisions. It contains no resolved SMTP/IMAP configuration or credentials.
|
||
|
||
At delivery time Mail re-authorizes the profile, compares the expected opaque
|
||
revisions, resolves credentials inside the same Mail-owned operation, and
|
||
performs the transport effect. Campaign receives only the sanitized result it
|
||
needs for job state and evidence. This same-call check prevents a
|
||
validate-then-use race across the module boundary.
|
||
|
||
Credential rotation that does not change the non-secret transport identity may
|
||
continue to satisfy the snapshot. A host, account identity, protocol, sender,
|
||
or other revisioned transport change stops delivery until the campaign is
|
||
revalidated and rebuilt.
|
||
|
||
### Existing legacy database records
|
||
|
||
The current Campaign database may contain versions with inline SMTP/IMAP
|
||
material. No separate historical Campaign JSON corpus exists. Inline transport
|
||
material in those rows is treated as inert legacy data and is never interpreted
|
||
as an executable Mail configuration:
|
||
|
||
- public responses remove legacy transport fields and secrets;
|
||
- validation, build, queue, retry, and delivery fail closed;
|
||
- computed previews that require a current Campaign configuration return an
|
||
actionable validation problem instead of a server error;
|
||
- an editable version changes to profile-only form only through an explicit
|
||
Mail-settings save; and
|
||
- a locked version is preserved and must be forked to an editable successor.
|
||
|
||
Normal database backup/restore and access controls cover those rows together
|
||
with the rest of the current database. There is no separate historical-JSON,
|
||
backup-scanning, or inline-secret migration program. Restoring an existing
|
||
legacy row preserves it as inert evidence and does not make it deliverable.
|
||
|
||
### Recipient and attachment evidence
|
||
|
||
The delivery record should be able to identify:
|
||
|
||
- source/import context and the frozen recipient row;
|
||
- effective addressing and message id;
|
||
- template inputs and unresolved-placeholder decisions;
|
||
- managed file/version ids, source provenance, checksums, and ZIP evidence;
|
||
- generated EML checksum and size;
|
||
- Mail profile reference and opaque transport revisions;
|
||
- each SMTP and IMAP attempt, its classification, safe provider response, and
|
||
reconciliation note; and
|
||
- actor/system trigger, timestamps, policy context, and corrections.
|
||
|
||
An excluded recipient/message is a completed validation decision, not a
|
||
pending delivery. Its SMTP and IMAP states are both `skipped`; it is counted and
|
||
filterable separately from unattempted, failed, accepted, and append outcomes.
|
||
No SMTP or IMAP attempt exists for such a row. If historical data contains
|
||
actual transport evidence despite an exclusion marker, that evidence is
|
||
preserved for audit and reconciliation rather than relabelled.
|
||
|
||
## Administration and policy
|
||
|
||
### Roles and permissions
|
||
|
||
The supplied role templates deliberately separate preparation, review, and
|
||
delivery:
|
||
|
||
- **Campaign manager** prepares, validates, and builds campaigns and recipients.
|
||
- **Campaign reviewer** inspects prepared material and records review
|
||
completion for the exact messages checked.
|
||
- **Campaign sender** queues, sends, pauses/resumes/cancels, retries, reconciles,
|
||
reads reports, and has diagnostic access.
|
||
|
||
Administrators may compose narrower roles from the declared permissions. Keep
|
||
these separations where institutional policy requires four-eyes approval. Mail
|
||
profile use additionally requires `mail:profile:use`; profile and credential
|
||
administration remains a Mail permission.
|
||
|
||
Campaign access is also constrained by tenant, owner/group context, and explicit
|
||
shares. A share grants only its declared campaign permission; it does not grant
|
||
Mail credentials, Files administration, or tenant-wide recipient access.
|
||
|
||
### Configuration checklist
|
||
|
||
- Install compatible Core and Campaign versions. Access and base audit are Core
|
||
infrastructure; install optional Mail, Files, Addresses, Notifications, and
|
||
Policy modules only where the configured journey requires them.
|
||
- Configure Mail profiles and policy in Mail, not in campaign fixtures or JSON.
|
||
- Configure Files storage/connectors and attachment permissions in Files.
|
||
- Define role assignments and separation-of-duty policy.
|
||
- Configure Redis/Celery for durable batch workers where production volume
|
||
requires it. Local execution is a development/small-run mode, not horizontal
|
||
worker coordination.
|
||
- Set rate limits for the target provider. Mail may use Redis for shared
|
||
throttling when present and a process-local fallback in development.
|
||
- Establish retention, deletion, archive, report-export, and diagnostic-access
|
||
policy before production recipient data is loaded.
|
||
- Use non-production SMTP/IMAP identities and the maintained examples before
|
||
allowing a production profile.
|
||
|
||
### Owner transfer
|
||
|
||
Mail profile visibility can depend on campaign owner or group. Transferring an
|
||
editable campaign with a selected profile clears/requires reselection and
|
||
revalidation. A locked or delivery-final version is never silently rewritten;
|
||
create an editable successor.
|
||
|
||
## Operations and recovery
|
||
|
||
### Health to observe
|
||
|
||
- database and migration health;
|
||
- compatible module interface versions;
|
||
- queue publication, worker heartbeat, claim age, and backlog;
|
||
- SMTP/IMAP profile test result and deployment egress policy;
|
||
- Mail profile authorization/transport-revision mismatch;
|
||
- Files resolution and frozen attachment availability;
|
||
- counts by queue, SMTP, IMAP, and reconciliation state; and
|
||
- audit and report generation failures.
|
||
|
||
### Worker interruption
|
||
|
||
A crashed worker can leave a job claimed or sending. Do not infer non-delivery
|
||
from worker loss. If the SMTP boundary may have been crossed, classify the
|
||
outcome as unknown and reconcile from external evidence. Only work that is
|
||
provably unattempted may be returned to an ordinary queue.
|
||
|
||
### Retry and reconciliation
|
||
|
||
Retries create new attempt evidence; they do not overwrite the previous
|
||
attempt. Reconciliation is a privileged factual correction supported by an
|
||
operator note. Repeated automated requests should be idempotent for the same
|
||
eligible job state; a deliberate resend is a different, not-yet-finalized
|
||
business action and must never be disguised as a retry.
|
||
|
||
### Backups and restoration
|
||
|
||
Back up Campaign, Mail, Files, Core/Audit, and shared storage consistently for
|
||
the composition. A database restore without generated EML/file storage, or file
|
||
storage without matching metadata, does not reconstruct the evidence chain.
|
||
After restore, keep outbound delivery paused until queue/attempt state and
|
||
provider evidence have been reconciled; never let restored accepted jobs send
|
||
again merely because a queue message was lost.
|
||
|
||
For unreferenced generated objects after process loss, platform operators first
|
||
run the bounded Campaign artifact inventory in dry-run mode. Apply only an
|
||
inspected page with a unique incident idempotency key. The cleanup keeps exact
|
||
keys out of ordinary Campaign responses, does not clear database references,
|
||
and leaves storage failures in the Core recovery ledger for explicit retry.
|
||
|
||
### Incident handling
|
||
|
||
1. Pause new delivery when duplicate or unknown effects are possible.
|
||
2. Preserve database, queue, provider, worker, and audit evidence.
|
||
3. Scope affected campaigns/jobs without exposing recipient content broadly.
|
||
4. Reconcile uncertain jobs individually or through an approved bounded tool.
|
||
5. Correct configuration in its owning module, then revalidate/rebuild where
|
||
revisioned transport inputs changed.
|
||
6. Record the incident reference and recovery rationale in audit evidence.
|
||
|
||
## Composition and integration contracts
|
||
|
||
Campaign has one required platform dependency: Core. Optional module behavior
|
||
is discovered through versioned, Core-mediated capabilities; Campaign must not
|
||
import optional sibling ORM, services, or WebUI implementation.
|
||
|
||
Current principal contracts include:
|
||
|
||
| Contract | Direction | Purpose |
|
||
| --- | --- | --- |
|
||
| `mail.campaign_delivery` 0.2.x | Mail -> Campaign | Summarize a known reference; authorize and revision-gate it; send/append using Mail-owned configuration and credentials; return sanitized results |
|
||
| `files.campaign_attachments` 0.1.x | Files -> Campaign | Select/materialize governed file versions and preserve campaign usage/evidence |
|
||
| `addresses.lookup` 0.1.x | Addresses -> Campaign | Optional address suggestions |
|
||
| `addresses.recipient_source` 0.1.x | Addresses -> Campaign | Optional versioned recipient-source snapshots |
|
||
| `dist_lists.source` / `dist_lists.expand` 0.1.x | Distribution Lists -> Campaign | Discover, preview, and freeze reusable audiences without importing module internals |
|
||
| `templates.catalog` / `templates.renderer` 0.1.x | Templates -> Campaign | Select compatible published printable templates and produce deterministic, evidence-bearing artifacts |
|
||
| `campaigns.access` 0.1.x | Campaign -> platform | Explain campaign access/existence without exporting ORM objects |
|
||
| `campaigns.mail_policy_context` 0.1.x | Campaign -> Mail | Resolve campaign tenant/owner context for Mail policy |
|
||
| `campaigns.delivery_tasks` 0.1.x | Campaign -> workers | Execute narrow queued send/append tasks |
|
||
| `campaigns.retention` 0.1.x | Campaign -> retention | Apply Campaign-owned retention behavior |
|
||
|
||
Breaking payload or ownership changes require an interface-version bump and a
|
||
release-composition alignment gate. Optional absence must be tested physically,
|
||
not only hidden in navigation.
|
||
|
||
### Bulk recipient activation
|
||
|
||
Recipient data can activate all currently inactive rows or deactivate all
|
||
currently active rows. The action displays the exact affected count and
|
||
requires confirmation. It changes only the local Campaign draft until the
|
||
operator saves. Saving follows the ordinary versioned Campaign update path, so
|
||
the resulting recipient state is retained in version and protocol evidence and
|
||
any stale validation, build, or review evidence is invalidated.
|
||
|
||
### Reusable Distribution Lists
|
||
|
||
When Distribution Lists is available, Recipient data offers a separate import
|
||
dialog. The author selects a visible list revision, supplies declared
|
||
parameters, requests candidate channels, and previews included, excluded,
|
||
stale, ambiguous, suppressed, policy-blocked, and provider-unavailable results.
|
||
The final action freezes an idempotent Distribution Lists snapshot and copies
|
||
the resulting rows into the editable Campaign version.
|
||
|
||
Each copied row retains the list and revision IDs, definition and expansion
|
||
hashes, snapshot ID, source entry IDs, provider references, channel candidates,
|
||
the explicitly selected primary route, optional fallback, and the decision
|
||
explanation. Campaign-only fields, attachment rules, review state, and outcomes
|
||
remain local to Campaign and never mutate the reusable list.
|
||
|
||
A later list revision only raises a drift warning. Refresh is deliberate and
|
||
uses append or replace; saving that changed Campaign version clears prior
|
||
validation, build, review, and execution state through the normal content
|
||
invalidation path. Preferred or single usable candidates are preselected
|
||
visibly; ambiguous rows must be decided before freezing. Postal and
|
||
internal-mail routes remain active when a compatible published Templates output
|
||
is selected.
|
||
|
||
### Governed hybrid and printable delivery
|
||
|
||
Campaign supports Mail, Postbox, printable output, and bounded ordered
|
||
fallbacks without making any of those provider modules mandatory. Opt-in and
|
||
channel-preference data are inputs to the visible routing decision; they never
|
||
silently cause duplicate delivery.
|
||
|
||
For a printable route, select a published label, envelope, serial-letter,
|
||
form-letter, list-layout, or generic template on the Template page. Validation
|
||
checks the selected revision, output format, and required fields. Build sends
|
||
one deterministic item collection to `templates.renderer`, records template,
|
||
input, output, actor, route, and artifact hashes, and stores the resulting
|
||
artifact through Files when configured. The review stage exposes that exact
|
||
artifact and its hashes before execution.
|
||
|
||
Each recipient job records an idempotent print acceptance attempt for its item
|
||
in the frozen artifact. `mail_then_print` and `postbox_then_print` invoke print
|
||
only after a confirmed rejection before acceptance. An accepted or
|
||
outcome-unknown digital effect never falls through to print because that could
|
||
produce duplicate delivery. Reports and CSV exports include route provenance,
|
||
print state, attempts, artifact reference, and hashes.
|
||
|
||
Without Templates, Campaign still loads and Mail/Postbox authoring remains
|
||
available; validation explains why a configured print route cannot proceed.
|
||
Without Files, Templates may return a bounded artifact instead of a managed
|
||
file. Campaign copies that payload into shared object storage and exposes it
|
||
through the Campaign ACL plus `campaigns:recipient:read`; it never redistributes
|
||
the broader Templates URL. A print-only Campaign does not require Mail or Postbox.
|
||
|
||
### External API expectations
|
||
|
||
- Tenant and campaign access are evaluated for every operation.
|
||
- Writes require CSRF/auth behavior supplied by Core and the specific declared
|
||
scope.
|
||
- Queue/retry/reconcile endpoints operate on persisted state and return safe
|
||
summaries; initiating HTTP success is not proof of external delivery.
|
||
- Delta endpoints are optimization surfaces, not a separate source of truth.
|
||
- Report exports contain permitted business/evidence fields but no credentials,
|
||
local paths, storage keys, or worker claims.
|
||
|
||
## Assurance model
|
||
|
||
### Security invariants
|
||
|
||
- No new campaign payload, fixture, response, or execution snapshot contains
|
||
SMTP/IMAP settings or credentials.
|
||
- Mail resolves credentials and performs transports inside Mail-owned calls.
|
||
- Every real connector peer is validated and pinned at connection time under
|
||
deployment-wide private-network policy.
|
||
- API/server attachment paths use managed Files references; arbitrary and
|
||
traversal-capable local paths are rejected.
|
||
- Public responses recursively remove infrastructure locators and secret-like
|
||
legacy fields.
|
||
- Accepted and outcome-unknown jobs are protected from ordinary retry.
|
||
- Diagnostic permission is separate from campaign read/report access.
|
||
|
||
### Privacy
|
||
|
||
Recipient fields and rendered messages may contain personal or sensitive data.
|
||
Grant recipient read/export, report export, and diagnostic access separately.
|
||
Prefer aggregate status for readers who do not need recipient detail. Define
|
||
purpose, lawful basis, minimization, export control, and retention before the
|
||
campaign starts; do not use Campaign as a substitute consent or address-master
|
||
system.
|
||
|
||
### Audit and destructive actions
|
||
|
||
Material authoring, validation, locking, review, queueing, send, retry,
|
||
reconciliation, sharing, owner transfer, archive, and permitted deletion actions
|
||
emit attributable evidence. Audit details must be non-secret and should refer to
|
||
stable ids rather than repeat message bodies or credentials.
|
||
|
||
Draft deletion is allowed only while no audit-relevant build, delivery job, or
|
||
lock exists. Evidence-bearing campaigns are archived. Destructive module
|
||
retirement remains a separately confirmed installer operation with backup and
|
||
retirement evidence.
|
||
|
||
The Campaign **Audit** page is currently an explained handoff, not a second
|
||
audit store: Campaign emits bounded platform audit records and authorized
|
||
readers inspect them in Administration > Tenant audit. A future object-scoped
|
||
projection may improve that navigation without duplicating Audit ownership.
|
||
Evidence-bundle export and offline verification remain owned by Audit #3.
|
||
|
||
The advanced **JSON** page displays and downloads the complete campaign
|
||
configuration available to the current campaign reader. It contains no inline
|
||
transport secrets, but recipient, message, and attachment fields may contain
|
||
personal data. The UI therefore identifies it as sensitive expert output;
|
||
campaign access and export purpose remain the governing controls.
|
||
|
||
## Reference-composition acceptance
|
||
|
||
Campaign is ready to serve as the demonstration module only when all of the
|
||
following are repeatable in a pinned clean installation:
|
||
|
||
1. The maintained examples validate and build with Mail/Files present, and
|
||
Campaign still starts with each optional module absent.
|
||
2. A user can import recipients, select managed files, choose an authorized Mail
|
||
profile, validate, review, build, and queue without entering transport ids or
|
||
secrets manually.
|
||
3. Campaign JSON and all ordinary APIs reject/omit inline transport material;
|
||
legacy records are visible as migration-required and cannot execute.
|
||
4. SMTP success, temporary/permanent failure, connection loss, worker loss,
|
||
outcome unknown, retry, reconciliation, IMAP success, and IMAP failure have
|
||
tested, non-duplicating outcomes against the target environment.
|
||
5. Author, reviewer, sender/operator, reader, and administrator views use the
|
||
central component system and expose only task-relevant actions.
|
||
6. Reports and audit can reconstruct recipient/message/file/profile/attempt
|
||
evidence without exposing secrets or ordinary-reader infrastructure details.
|
||
7. Clean install, upgrade, backup/restore, module permutations, version
|
||
alignment, security audit, and target SMTP/IMAP tests pass.
|
||
8. This handbook and its adaptive Docs topics match the shipped UI wording and
|
||
distinguish implemented behavior from planned work.
|
||
|
||
## Explicitly planned, not yet claimed
|
||
|
||
The following are part of the selected reference journey but are not implied by
|
||
the current baseline:
|
||
|
||
- the final audited **test / single send / single resend** semantics;
|
||
- durable, idempotent Campaign report delivery through a Mail-owned outbox
|
||
([`govoplan-mail#17`](https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/17));
|
||
- a fully packaged one-command Campaign reference composition with production
|
||
policy presets and target-provider certification;
|
||
- function-bound Postbox delivery (stage 2 of the reference program);
|
||
- generic workflow-driven campaign transitions.
|
||
|
||
Each item needs an owning issue, implementation, failure tests, documentation,
|
||
and release evidence before the wording above can move from planned to current.
|