Files
govoplan-campaign/docs/CAMPAIGN_HANDBOOK.md
T
zemion 3934e7fedb
Module Package Release / publish-packages (push) Successful in 12s
feat(campaigns): add portable campaign transfers
2026-08-22 04:01:39 +02:00

39 KiB
Raw Blame History

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
Reviewer Review and complete review
Sender or delivery operator Deliver and resolve outcomes
Campaign or tenant administrator Administration and policy
Platform operator Operations and recovery
Integrator or developer Composition and integration contracts
Security, privacy, or audit reviewer Assurance model
Release reviewer Reference-composition acceptance

The shorter task documents remain useful companions:

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, frozen execution evidence, and governed human collaboration entries.

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:

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

Discuss campaign work

Open Collaboration inside a Campaign to keep human coordination beside the work without changing its version history. Discussion access is independent from Campaign editing: parent Campaign read access remains mandatory, while campaigns:discussion:read, campaigns:discussion:post, and campaigns:discussion:moderate separately control reading, posting, and moderation. A read share is sufficient as the parent grant and a comment never turns that share into write access.

Comments are append-only and bounded to 8,000 characters. They can carry one validated reference to an immutable Campaign version, saved recipient import, attachment rule, delivery job, or report. References to version-bound evidence include the exact version ID and never edit that version. Authors can withdraw their own comments; moderators can redact comments and use moderator-only visibility. Both operations remove displayed text but preserve a tombstone, content hash, actor snapshot, timestamp, reference context, and bounded Audit event. There is deliberately no comment-edit API.

Mentions are limited to 20 active users who already have Campaign ownership or share access. When Notifications is installed and healthy, Campaign emits a content-free in-app mention notification. Collaboration remains usable without Notifications. The thread displays only human discussion; approvals, workflow state, delivery events, and durable system evidence remain on their owning surfaces and in Tenant audit.

Assign accountable campaign work

Open Work to assign one bounded purpose to an account, group, or organization function that already has Campaign access. Assignment records responsibility only: it never creates a share, transfers ownership, or grants a permission. Assignees may accept, complete, or reject their work; rejection is distinct from administrative cancellation. Managers may reassign or cancel open work, and every transition retains the expected revision, actor snapshot, typed target, and append-only event history.

Workflow may create or reference a Campaign and open the same assignment through the optional campaigns.workOrchestration capability. Those assignments pin the Campaign version and store the Workflow instance, step, correlation, and idempotency provenance. Campaign emits campaign.work.changed for assignment, acceptance, start, reassignment, completion, rejection, and cancellation. Workflow uses the assignment ID and event revision, rechecks current Campaign access, and then resumes the matching durable external hand-off without browser polling. A missing Tasks or Notifications capability only removes the optional projection or notification. A missing Campaign provider, revoked Campaign access, or stale event revision keeps the Workflow blocked and inspectable.

Campaign also contributes the opt-in Accountable Campaign work hand-off Workflow template. It is deliberately not activated on installation. A configurator must copy or activate it and supply either campaign_id or create_campaign; unused optional input keys must be present with null values. The template prepares the assignment idempotently, opens the exact Campaign work URL, and waits for completion, rejection, cancellation, or the configured timeout. Opening the link never completes the Workflow.

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 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 0500). 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

Portable Campaign transfer

Campaign offers two reuse paths with different boundaries. Copy campaign creates another campaign inside the same installation and can reuse selected local shares, policies, and Mail profile references. Export package creates a versioned JSON hand-off whose selected scopes can cross an installation boundary; Import package always creates a separately owned draft.

The export dialog starts with only metadata and template/configuration. Add recipients, attachment rules, review state, or delivery history only when the handoff requires them and the destination and retention are approved. Recipient and delivery scopes remain protected by recipient/report export permissions. Transport secrets, credential references, password-field values, local storage locators, and attachment bytes are always removed. The manifest records scope counts and redactions, while the envelope carries source Campaign/version provenance and a SHA-256 digest.

Import verifies format, scope, checksum, schema, and destination identity before showing the plan. Editing the destination identity or selected scopes makes the preview stale and requires a new check. The apply step clears source Mail references, creates one editable draft, and stores a bounded source/package and created/skipped receipt. Historical validation/build summaries, review state, approvals, delivery jobs, attempts, and sent outcomes are never replayed. File content is never embedded, so reconnect managed files and local Mail profiles, then validate, build, review, and approve normally.

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.

The Core data-subject-request workflow discovers Campaign through the optional privacy.dsar.campaigns capability. After the request's email, membership, and namespaced Campaign references have been independently authorized and corroborated, the provider searches only the effective tenant and isolates the matching recipient entries and jobs. Its JSON result includes safe Campaign, version, delivery-attempt, schedule, report-projection, share, import-mapping, attachment, generated-artifact, and relevant collaboration metadata. It also finds collaboration entries authored, mentioned, or moderated by the subject. Text authored by the subject is included; somebody else's text is not copied merely because the subject was mentioned. Generated EML bytes and paths, storage locators, delivery target snapshots, worker claims, idempotency material, credentials, secret-like values, and unrelated recipients are never embedded in that result. Authorized Campaign and Files review surfaces remain the source for content that cannot safely be copied into the DSAR case.

Built, locked, published, terminal, delivered, or corrected records are retained with an explicit reason and continue through Campaign's configured retention and redaction process. Draft recipient content and user-owned attachment content require coordinated manual review because copies may span version JSON, jobs, generated messages, and managed files. The provider can idempotently revoke an active share aimed at the subject and delete the subject's personal recipient-import mapping profile. It does not rewrite delivery evidence, delete generated artifacts, or report derived Campaign counts as a separate store. Collaboration withdrawal and redaction retain the tombstone, content hash, context, and Audit evidence; the DSAR workflow does not rewrite these append-only records. Re-running an approved action is safe: already revoked or absent data is reported as unchanged, and tenant, subject, and row ownership are revalidated immediately before mutation.

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);
  • 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.