You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When PostGuard sends a file, cryptify emails each recipient a download link — send_email() in src/email.rs, called from upload_finalize (POST /fileupload/finalize/<uuid>). Today that function returns a single Ok("Email successfully sent") as soon as the SMTP relay accepts each message, and the finalize endpoint reports success. The website (postguard-website) then shows the sender a blanket "sent successfully" confirmation.
This is misleading in two ways:
SMTP acceptance ≠ delivery.mailer.send(&email) returning Ok only means the relay accepted the message for onward delivery (a 250 at submission). A syntactically-valid-but-wrong address — e.g. a mistyped jane@gmial.com — is accepted at submission and bounces asynchronously: the receiving MTA rejects it later and a DSN/bounce is returned to the Return-Path, which cryptify never reads. The sender is told "sent" when in fact nobody received the file. This is exactly the silent failure observed in the July 2026 user test — see Mistyped recipient email fails silently — no error and no email is sent postguard-website#293.
No per-recipient status. The recipient loop in send_email() uses ?, so the first synchronous submission error aborts the whole batch and collapses to one opaque error. There is no structured, per-recipient result the website could use to say "email to x@x.com failed."
Desired behaviour
Report delivery status per recipient so the website can tell the sender exactly which addresses succeeded and which failed, instead of a blanket "sent". There are two timescales:
A. Synchronous submission failures (quick win)
Don't abort the batch on the first failure — attempt every recipient and collect a per-recipient result, e.g. { email, status: sent | failed, error }.
Return that per-recipient status from send_email() and surface it in the upload_finalize response, so the website's send confirmation can render "delivered to A, failed for B" rather than always "sent successfully".
B. Asynchronous bounces (the harder, real part)
True bounces arrive after the HTTP response is already gone, so they can't be waited on synchronously. Detecting them needs one of:
a monitored Return-Path / bounce mailbox + DSN parsing, correlated to a send via VERP or a stored Message-ID; or
moving notification mail to an ESP with a bounce webhook/API (SES / Postmark / SendGrid / Mailgun).
When a bounce is detected, notify the original sender so they can act — this is the backend half of encryption4all/postguard-website#276 ("the original sender should be informed of a bounced/not delivered email"). Optionally, a pre-send MX / mailbox check would catch many bad domains before submission.
Problem
When PostGuard sends a file, cryptify emails each recipient a download link —
send_email()insrc/email.rs, called fromupload_finalize(POST /fileupload/finalize/<uuid>). Today that function returns a singleOk("Email successfully sent")as soon as the SMTP relay accepts each message, and the finalize endpoint reports success. The website (postguard-website) then shows the sender a blanket "sent successfully" confirmation.This is misleading in two ways:
SMTP acceptance ≠ delivery.
mailer.send(&email)returningOkonly means the relay accepted the message for onward delivery (a250at submission). A syntactically-valid-but-wrong address — e.g. a mistypedjane@gmial.com— is accepted at submission and bounces asynchronously: the receiving MTA rejects it later and a DSN/bounce is returned to the Return-Path, which cryptify never reads. The sender is told "sent" when in fact nobody received the file. This is exactly the silent failure observed in the July 2026 user test — see Mistyped recipient email fails silently — no error and no email is sent postguard-website#293.No per-recipient status. The recipient loop in
send_email()uses?, so the first synchronous submission error aborts the whole batch and collapses to one opaque error. There is no structured, per-recipient result the website could use to say "email to x@x.com failed."Desired behaviour
Report delivery status per recipient so the website can tell the sender exactly which addresses succeeded and which failed, instead of a blanket "sent". There are two timescales:
A. Synchronous submission failures (quick win)
{ email, status: sent | failed, error }.send_email()and surface it in theupload_finalizeresponse, so the website's send confirmation can render "delivered to A, failed for B" rather than always "sent successfully".B. Asynchronous bounces (the harder, real part)
True bounces arrive after the HTTP response is already gone, so they can't be waited on synchronously. Detecting them needs one of:
Message-ID; orWhen a bounce is detected, notify the original sender so they can act — this is the backend half of encryption4all/postguard-website#276 ("the original sender should be informed of a bounced/not delivered email"). Optionally, a pre-send MX / mailbox check would catch many bad domains before submission.
Relevant code
src/email.rs—send_email()(recipient loop +Ok("Email successfully sent"))src/main.rs—upload_finalize,POST /fileupload/finalize/<uuid>(awaitssend_email, returns the finalize responder)lettreSMTP crate (raw relay, no bounce handling today).Related