email-connect/DECISIONS.md
tegwick 89fd13ac2d
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
EMAIL-WP-0005-T02: add GreenMail test harness
Adds tests/harness/docker-compose.yml running GreenMail 2.1.12 (digest-pinned)
with SMTP 3025, IMAP 3143 and the API bound to 127.0.0.1 only, plus a
config/harness-imap.yml scanner profile and harness README. Auth is disabled
and no users are declared, so a mailbox is created on first login and per-test
users need no provisioning.

GreenMail standalone offers no STARTTLS, only plaintext or implicit TLS, while
SMTPProvider hardcoded starttls() -- so no send could reach it. SMTPProvider
now takes a security mode via EMAIL_CONNECT_SMTP_SECURITY, defaulting to
starttls. plaintext is refused for any non-loopback host, and hostnames are
never resolved to decide that, so a misconfigured deployment fails at startup
rather than sending credentials in the clear. Trusting GreenMail's self-signed
cert was rejected as the wider risk; see DECISIONS.md.

Verified end to end against the live harness: SMTPProvider.send -> GreenMail ->
ImapMailboxSource, and the documented scan-mailbox CLI. Suite: 52 passed with
the harness down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:41:38 +02:00

190 lines
6.9 KiB
Markdown

# Decision Log
_Auto-generated by the Custodian State Hub._
## Defer daily send-quota privilege tiers (anti-spam / abuse)
**Date:** 2026-08-12
**Decided by:** human (via agent note)
**Status:** deferred — do not implement in EMAIL-WP-0004
### Idea
Outbound email is high-abuse surface if credentials or a caller is compromised.
The platform should treat **daily send volume** as a privilege, not an
unlimited side effect of holding a send token.
Proposed daily ceilings (per sender / tenant / grant — exact subject TBD):
| Tier | Mails / day |
| --- | ---: |
| minimal | 10 |
| low | 100 |
| standard | 1_000 |
| elevated | 10_000 |
| high | 100_000 |
| bulk-capable | 1_000_000 |
| unlimited | unlimited (explicit break-glass only) |
Default for ordinary transactional use (e.g. invitation/verification) should
land at the low end of the scale until a use case justifies more.
### Why not now (EMAIL-WP-0004)
Current scope is a **single approved caller** (user-engine), **fixed templates**,
NetworkPolicy ingress lock, and one shared ingest bearer. Abuse surface is real
but narrow. Building the full tier model inside T03 custody/deploy or T04
failure proof would delay the invitation path without a multi-tenant grant
story.
Existing interim controls that stay:
- template allow-list (no arbitrary body/sender)
- NetworkPolicy: user-engine only
- bearer ingest token (OpenBao custody, CCR-2026-0010)
- idempotency on event id
### When to revisit
Promote to a dedicated workplan when any of:
- a second caller / tenant may send through email-connect
- the ingest token is treated as a long-lived platform grant
- marketing-adjacent or high-volume notification use appears
- flex-auth or credential-broker can bind a **send quota claim** to the caller
### Design sketch for later (not committed)
- Enforce in **email-connect** at accept time (before provider call), with a
durable counter (not only in-process).
- Classify over-quota as **permanent denial** for the day (or retryable after
window), redacted diagnostics, no SMTP detail leak.
- Bind tier via platform auth/grant (flex-auth / OpenBao policy metadata /
caller registration), not a free-form request field the client can raise.
- Unlimited is operator-issued break-glass, audited, not a default.
- Coordinate with suppression/complaint handling so quota and reputation share
one abuse narrative.
### Home for implementation later
New workplan under `email-connect` (suggested slug theme:
`send-quota-privilege-tiers` / `EMAIL-WP-00xx`), with a hand-off to
flex-auth / railiance-platform if the tier is a platform grant claim rather
than a local ConfigMap.
---
## Operate at build-stage security posture; defer production policy enforcement
**Date:** 2026-07-02
**Decided by:** human
We are in build stage with the infrastructure as a whole. We will tighten security step by step but it is not yet strictly necessary.
---
## SECRETS-WP-0004 — approve warden-sign auth-capability lane prod apply
**Date:** 2026-07-02
**Decided by:** human
I want to move to more automation, this is one step needed to go on.
---
## Defer consumer-side contract-parity drift mode (WHYNOT-WP-0003 T09)
**Date:** 2026-07-02
**Decided by:** human
Sufficient for now
---
## DR-3: External identity path for business clients (lightweight vs NK-WP-0011 Keycloak)
**Date:** 2026-07-09
**Decided by:** human
We will go with A for the lightweigt version and just follow up with B if we meet actual demand later.
---
## DR-1: Tenancy model for vergabe-teilnahme commercial offering
**Date:** 2026-07-09
**Decided by:** human
We will do C and allow for early customers to easy starting serving customere but move on to resource cost optimization early.
---
## DR-2: coulomb.social rebuild architecture (Core Hub extension vs standalone app)
**Date:** 2026-07-09
**Decided by:** human
We will go for B as Coulomb Social is it's own Application. Managed by connecting it with our infrastructure but destinct in its purpose and operations model. It is an Application for the Binky Hedgehog GmbH as another App might be provided to other clients. It should not be relevant the the ecosystem as a whole is provided by Binky Hedgehog as the organization creating coulomb and holding the domain and forgejo repos.
---
## Test mailbox harness: purpose-built test server over s/qmail
**Date:** 2026-08-14
**Decided by:** human
Automated test environments need mail accounts for test users. We will build a
test harness (EMAIL-WP-0005) around a Maildir mailbox source plus a
containerized SMTP/IMAP test server, rather than integrating s/qmail.
Reasons against s/qmail here:
* qmail lineage is an MTA — SMTP transport and Maildir delivery. Test users need
IMAP mailboxes, which would require bolting on Dovecot or Courier anyway, so
s/qmail solves the half we least need.
* Running it means operating a real mail server for test purposes: DNS, TLS,
rDNS, SPF/DKIM/DMARC, queue and spam handling. That is ongoing ops cost
charged against test infrastructure.
* It inverts the repo's scope boundary. `email-connect` is provider-neutral and
deliberately does not own an MTA; absorbing one makes us the provider we are
supposed to abstract over.
* Configuration is idiosyncratic and container/CI recipes are thin compared to
purpose-built test servers, which raises the debugging-budget risk.
If a long-lived, internet-facing test domain with many persistent accounts is
ever needed, revisit with docker-mailserver (Postfix + Dovecot) or Stalwart
before reconsidering s/qmail.
Bounce, complaint, and deferral evidence is explicitly not obtainable from a
local server; those classes stay on crafted fixtures plus an optional provider
simulator tier.
---
## Test harness mail server: GreenMail, and plaintext SMTP for loopback only
**Date:** 2026-08-14
**Decided by:** codex (EMAIL-WP-0005-T02)
GreenMail `2.1.12` (digest-pinned) is the harness mail server. It serves SMTP
and IMAP from one container and, with `greenmail.auth.disabled`, creates a
mailbox on first login — so per-test users need no provisioning step. Mailpit
was the alternative but offers no IMAP, which would leave the scanner's IMAP
source untested.
GreenMail standalone supports only plaintext or implicit-TLS setups; it has no
STARTTLS option (verified against the shipped jar's property builder). The
transactional service previously hardcoded `starttls()`, so no send could reach
it.
`SMTPProvider` therefore takes a `security` mode, `EMAIL_CONNECT_SMTP_SECURITY`,
defaulting to `starttls`. `plaintext` is refused for any non-loopback host, and
hostnames are never resolved to decide this — a name pointing at 127.0.0.1 today
is not a durable guarantee. A misconfigured deployment fails at startup instead
of putting credentials on the wire.
The alternative was trusting GreenMail's self-signed certificate for implicit
TLS, which would have meant shipping certificate-verification bypass code in the
send path. A loopback-guarded plaintext mode is the narrower risk.
---