From 2309c04668f71001431e229294b731c8be424290 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Sun, 13 Sep 2026 23:46:27 -0400 Subject: [PATCH] amail: a partial dedupe index, and a greeting the relay will take MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sparse still indexes a row that has `to` but no messageId — every inside letter — so the compound unique index collided on the first two and took a real letter down with it. Partial, on rows that carry a messageId, instead. Google's SMTP relay closes at EHLO unless the greeting names a real host and TLS comes first; give it both, and fall back to plain Gmail SMTP (which keeps the name and Reply-To, rewriting only the From address) if it still refuses. Co-Authored-By: Claude Fable 5.1 --- system/backend/mail.mjs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/system/backend/mail.mjs b/system/backend/mail.mjs index be344a9823..34b38dee3f 100644 --- a/system/backend/mail.mjs +++ b/system/backend/mail.mjs @@ -146,7 +146,13 @@ export async function deliverFromOutside( ) { const tells = await mailbox(database); if (!dedupeIndexed) { - await tells.createIndex({ to: 1, messageId: 1 }, { unique: true, sparse: true }); + // Partial, not sparse: sparse still indexes a row that has `to` but no + // messageId (as null), and every inside letter is one of those — the + // build collided on the first two and took a real letter down with it. + await tells.createIndex( + { to: 1, messageId: 1 }, + { unique: true, partialFilterExpression: { messageId: { $type: "string" } } }, + ); await tells.dropIndex("messageId_1").catch(() => {}); // the first cut's global one dedupeIndexed = true; } @@ -224,19 +230,30 @@ export async function sendOutside({ from, toEmail, subject, text }, database) { const fromHandle = handle ? "@" + handle : code; const home = `${handle || code}@${ROOT_DOMAIN}`; - const transporter = nodemailer.createTransport({ - host: process.env.AMAIL_SMTP_SERVER || "smtp-relay.gmail.com", - port: 587, - secure: false, - auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }, - }); - const info = await transporter.sendMail({ + // The relay greets with a 421 unless EHLO names a real host and TLS comes + // first. If the relay still won't have us, fall back to plain Gmail SMTP, + // which rewrites From to the post office's own address — the name and + // Reply-To survive, so a reply still finds its way home. + const auth = { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }; + const common = { port: 587, secure: false, requireTLS: true, name: "inbound.aesthetic.computer", auth }; + const letter = { from: { name: `${fromHandle} via Amail`, address: `${POST_OFFICE}+${code || handle}@${ROOT_DOMAIN}` }, replyTo: home, to: toEmail, subject: subject || `a letter from ${fromHandle}`, text: `${text}\n\n— ${fromHandle}, via Amail · reply to ${home}`, - }); + }; + let info; + try { + info = await nodemailer + .createTransport({ ...common, host: process.env.AMAIL_SMTP_SERVER || "smtp-relay.gmail.com" }) + .sendMail(letter); + } catch (err) { + shell.log("🟡 relay refused the letter, trying plain SMTP:", err?.message?.split("\n")[0]); + info = await nodemailer + .createTransport({ ...common, host: process.env.SMTP_SERVER || "smtp.gmail.com" }) + .sendMail(letter); + } const tells = await mailbox(database); const when = new Date(); -- 2.51.2