email-connect/workplans/EMAIL-WP-0004-transactional-mail-delivery-service.md
tegwick 04897a149f
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Complete EMAIL-WP-0004 transactional invitation mail delivery.
Ship the railiance01 deploy package (OpenBao/ESO custody, NetworkPolicy,
probes), provider failure classification and suppression, T04 unit and live
proof, and non-secret NK-WP-0024 hand-back evidence. Workplan finished.
2026-08-12 13:32:11 +02:00

151 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: EMAIL-WP-0004
type: workplan
title: "Provide transactional invitation mail delivery"
domain: infotech
repo: email-connect
status: finished
owner: codex
topic_slug: netkingdom
created: "2026-08-08"
updated: "2026-08-12"
depends_on:
- NK-WP-0024
state_hub_workstream_id: "a37e5e4d-090a-4a75-88d2-aacd0c0fd235"
---
# EMAIL-WP-0004 - transactional invitation mail delivery
Extend email-connect beyond mailbox evidence scanning with a narrow,
provider-neutral transactional-send service for user-engine invitations and
verification messages. Email evidence remains non-authoritative.
Deferred (not in this workplan): daily send-quota privilege tiers
(10 / 100 / 1e3 / 1e4 / 1e5 / 1e6 / unlimited) as anti-spam abuse control —
see `DECISIONS.md` “Defer daily send-quota privilege tiers”.
## T01 - Define the send and evidence contract
```task
id: EMAIL-WP-0004-T01
status: done
priority: high
state_hub_task_id: "ef07126a-3c1b-4a8b-9ec1-252c8844ec49"
```
Define an authenticated HTTP send request derived from the user-engine outbox
envelope, approved templates, recipient/address validation, tenant/template
allow-lists, Idempotency-Key, correlation, redaction, and response/error
semantics. Provider acceptance must not be described as inbox delivery,
awareness, identity, or authorization.
Done when the contract supports invitation-created and invitation-resent
events without accepting arbitrary sender, template, or message content.
Done 2026-08-09: authenticated `/v1/send` accepts only the two invitation
event types, validates recipient/event/idempotency fields, and renders a fixed
portal invitation template. Provider acceptance remains explicitly distinct
from delivery or identity evidence.
## T02 - Implement provider-neutral transactional sending
```task
id: EMAIL-WP-0004-T02
status: done
priority: high
state_hub_task_id: "6a6ae927-4393-480f-a83e-93c9dabe18fe"
```
Implement the HTTP receiver, template rendering, idempotency store, suppression
checks, bounded provider calls, and provider adapter. Start with the approved
IONOS SMTP/STARTTLS lane while keeping provider credentials outside requests,
logs, Git, and database evidence.
Done when repeated event IDs send at most one message and failures return
stable retryable/permanent classifications without leaking SMTP details.
Done 2026-08-09: SQLite idempotency, provider-neutral injection, bounded
STARTTLS SMTP calls, duplicate suppression, and redacted provider-unavailable
responses are implemented. The same narrow service now issues public-
registration mailbox challenges: plaintext handles exist only in the message,
SQLite retains a SHA-256 digest, evidence is purpose/binding scoped, expiry is
enforced, and consumption is atomic and single-use. All 23 repository tests
pass.
2026-08-10 cancellation increment: verification messages now include a fixed
portal cancellation link using the same opaque handle. The SQLite store keeps
only its digest and atomically records either consumption or cancellation, so
verification and cancellation invalidate one another and replay fails closed.
Purpose-bound cancellation evidence lets user-engine abandon the matching
registration without accepting browser identity claims. All 24 tests pass.
## T03 - Establish custody and deploy the service
```task
id: EMAIL-WP-0004-T03
status: done
priority: high
state_hub_task_id: "13428364-29b1-4e0d-aaf5-c0b254c829b9"
```
Route SMTP and caller credentials through the credential catalog before
requesting them, store provider material in OpenBao, and deliver only scoped
runtime secrets. Deploy an immutable image on railiance01 with probes,
resource/security controls, default-deny NetworkPolicy, restricted
user-engine ingress, SMTP-only egress, and rollback.
Done when user-engine can call the cluster-local receiver without possessing
SMTP credentials and unrelated workloads cannot send through it.
Done 2026-08-12 after CCR-2026-0010 approval:
* OpenBao path `platform/workloads/email-connect/transactional` provisioned
(ingest token new; SMTP username/password seeded from live forgejo IONOS
lane — sender ConfigMap uses `forgejo@coulomb.social`).
* Policies applied; ESO child token Secret
`external-secrets/openbao-email-connect-eso-token`; ClusterSecretStore
`openbao-email-connect` Ready; ExternalSecret SecretSynced (3 keys).
* Package applied on railiance01: Deployment Ready 1/1, PVC Bound, probes
green, image digest `sha256:bc7a01b4…`.
* Network: email-connect default-deny + user-engine ingress + SMTP-587 egress;
user-engine-runtime egress allow to email-connect:8080 (net-kingdom
`sso-mfa/k8s/user-engine/runtime.yaml`).
* Smoke: user-engine `/healthz`/`/readyz` 200; unauth `/v1/send` 401; bearer
`template_not_allowed` / `invalid_recipient` 400; default-ns probe
Connection refused. user-engine secret has no SMTP keys.
* Catalog `email-connect-transactional` promoted active/resolvable.
* Follow-up (not T03): mount same ingest token into user-engine for NK outbox
(NK-WP-0024); optional dedicated noreply@ mailbox; T04 failure proof.
## T04 - Prove invitation delivery failure behavior
```task
id: EMAIL-WP-0004-T04
status: done
priority: high
state_hub_task_id: "34127b3e-f7c6-4ebe-a86c-743ba1b27640"
```
Test template allow-list denial, invalid recipient, provider timeout, temporary
deferral, permanent rejection, duplicate request, suppression, resend, and
redacted diagnostics. Verify mailbox ownership and provider acceptance never
alter user-engine authorization. Hand non-secret message/event references and
failure evidence back to NK-WP-0024.
Done when deployed invitation and verification flows are observable,
idempotent, retry-safe, and conservative about delivery evidence.
Done 2026-08-12:
* ProviderError classification: timeout/temporary/unavailable → 503
`retryable=true`; permanent_rejection → 422 `retryable=false`; no SMTP text
in responses. SQLite suppressions → `recipient_suppressed`. Accept body
carries `evidence_ceiling=provider_accepted`; verification consume/cancel set
`authorization=false`.
* Unit matrix: 9 tests pass (`tests/test_transactional.py`).
* Live matrix on railiance01 (image
`sha256:a9de1994…`): unauth 401, template/invalid/idempotency 400, accept
202 + duplicate 200, resend 202, bad domain 422 permanent_rejection,
suppression 400; redaction checked. Evidence note for NK-WP-0024:
`docs/EMAIL-WP-0004-T04-failure-evidence.md`.
* Workplan finished when T01T04 all done (this completes EMAIL-WP-0004).