# Campaign SMTP/IMAP Test Bed This test bed provides dedicated non-production SMTP/IMAP infrastructure for campaign delivery checks. It uses GreenMail and must never be configured with production recipients or credentials. ## Start ```bash cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed cp .env.example .env docker compose --env-file .env up -d ``` Default endpoints: - SMTP: `127.0.0.1:3025`, plain transport - IMAP: `127.0.0.1:3143`, plain transport - GreenMail API/UI endpoint: `127.0.0.1:38080` ## Smoke Test Run the transport smoke from the core virtual environment after installing the campaign and mail modules: ```bash cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed set -a . ./.env set +a /mnt/DATA/git/govoplan-core/.venv/bin/python run_transport_smoke.py ``` The smoke sends and appends three messages: - no attachment - one normal attachment - one password-protected AES ZIP attachment It verifies SMTP authentication, IMAP authentication, folder listing, delivery to `INBOX`, and append-to-Sent behavior. If the smoke is started immediately after `docker compose up -d`, GreenMail may bind the SMTP/IMAP ports before the services are fully ready. The smoke retries login and folder setup for `GOVOPLAN_MAIL_TEST_READY_TIMEOUT_SECONDS`. ## Campaign Acceptance The transport smoke proves the Mail adapters. The Campaign acceptance runner proves the public composition: it creates an isolated temporary Core database, creates a Mail-owned encrypted profile through the API, materializes the credential-free [`greenmail-delivery`](../../examples/greenmail-delivery/campaign.json) fixture with only that profile reference, validates/builds it, sends the exact generated EML through Campaign once, appends it once, and cross-checks Campaign report/audit state with one unique-subject message in the GreenMail INBOX and Sent folders. Provider mailbox verification is not a byte-for-byte comparison after provider-side header or storage transformations. ```bash cd /mnt/DATA/git/govoplan-campaign/dev/mail-testbed set -a . ./.env set +a /mnt/DATA/git/govoplan/.venv/bin/python run_campaign_acceptance.py \ --evidence /tmp/govoplan-campaign-greenmail-evidence.json ``` The default run also uses controlled loopback protocol endpoints to prove that an SMTP connection loss before transmission is temporary, an explicit SMTP authentication rejection is permanent, and an IMAP authentication rejection after SMTP acceptance leaves the send accepted while the append fails. A second ordinary send must be rejected before another provider effect. The bounded JSON contains no endpoint, account, address, credential, profile, campaign, version, or job identifiers. It records module versions, the fixture hash, normalized classifications/counts, the Mail-profile boundary, required audit actions, provider mailbox increments, and coverage flags. Runtime version declarations identify the exercised composition; they do not claim that the sources are clean, tagged, signed, or release-provenanced. The evidence names them `declared_module_versions` and keeps `source_artifact_provenance` false; exact commit/artifact provenance belongs to the package and release gate. This runner is restricted to literal loopback IP addresses and the synchronous Campaign delivery mode. Hostnames such as `localhost` and every non-loopback address fail before profile creation, avoiding a DNS change between validation and connection. It is local target-like evidence, not approval of an institution's SMTP/IMAP service. A post-DATA connection loss with ambiguous SMTP outcome, an explicit temporary SMTP response, partial recipient refusal, and a real worker restart remain separate drills and are explicitly `false` in the coverage projection. Use `--success-only` only when testing the success journey without the local failure endpoints. Starting the maintained test bed requires a working Docker CLI, Compose plugin, daemon/socket access, and permission to pull `greenmail/standalone:2.1.9`. The Campaign runner needs only the already-running loopback endpoints; it neither starts Docker nor claims that it did. ## Use With A Campaign Use the same settings in a campaign mail profile: - SMTP host `127.0.0.1`, port `3025`, security `plain` - IMAP host `127.0.0.1`, port `3143`, security `plain` - username and password from `.env` - append folder from `GOVOPLAN_MAIL_TEST_SENT_FOLDER` When changing the mailbox, keep `GOVOPLAN_MAIL_TEST_USER` equal to `${GOVOPLAN_MAIL_TEST_LOCAL_PART}@${GOVOPLAN_MAIL_TEST_DOMAIN}` because the compose file creates the GreenMail account from local part, password, and domain while login uses the full email address. Then run the controlled sending checklist from `docs/CAMPAIGN_DELIVERY_RUNBOOK.md` for no attachment, one attachment, and password-protected ZIP campaign variants. ## Stop ```bash docker compose --env-file .env down -v ```