feat(email): native SMTP-listener ingress (A3)
An opt-in receive-only SMTP listener (PICLOUD_SMTP_BIND) so an MX can point straight at PiCloud. It speaks minimal SMTP (HELO/EHLO/MAIL/RCPT/DATA/RSET/ NOOP/QUIT), resolves each RCPT TO mailbox to the app-owned email trigger that claims it, and inserts an Email outbox row the dispatcher fires — the same tail as the HMAC webhook, unchanged. - migration 0076: email_trigger_details.inbound_address (case-insensitively unique among app triggers) + TriggerRepo::email_inbound_target_by_address / SmtpInboundTarget; the interactive create-email API accepts inbound_address. - crates/picloud/src/smtp.rs: a small tokio accept loop + session state machine + a testable deliver() core + a minimal RFC-5322 header/body split. DATA is size-capped with dot-unstuffing; no AUTH/STARTTLS (TLS terminates upstream — the recipient address + per-app isolation are the boundary). - spawned in run_server alongside axum::serve, sharing the pool, on the same shutdown signal. Pinned by a picloud integration test (deliver → one Email outbox row for a known mailbox, none for an unknown one) + smtp.rs unit tests (address parse, header/body split). Multipart/MIME decoding is a documented follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
-- A3 raw SMTP ingress — an inbound email ADDRESS an email trigger listens on.
|
||||
--
|
||||
-- The HMAC-webhook path (v1.1.7) addresses a trigger by its UUID in the URL. A
|
||||
-- native SMTP listener instead receives `RCPT TO:<address>` and must resolve
|
||||
-- that address to the bound trigger. `inbound_address` is the mailbox the
|
||||
-- trigger claims; NULL for a webhook-only trigger. Case-insensitively unique
|
||||
-- among app-owned triggers so an address resolves to exactly one script.
|
||||
ALTER TABLE email_trigger_details
|
||||
ADD COLUMN inbound_address TEXT;
|
||||
|
||||
CREATE UNIQUE INDEX email_trigger_details_inbound_address_uidx
|
||||
ON email_trigger_details (lower(inbound_address))
|
||||
WHERE inbound_address IS NOT NULL;
|
||||
Reference in New Issue
Block a user