Files
backend_fiesta/docs/MAIL_SETUP.md
2026-09-30 14:58:30 +05:30

7.4 KiB

Mail setup — Google Workspace SMTP, sending as care@nearledaily.com

What this is for: the first-password invitation. Every back-office account on Fiesta is created with an empty password, and the link in this email is the only way to set one — the sign-in screen no longer offers a form, because a public one meant that knowing a merchant's email address was enough to claim their account.

So this is not newsletter plumbing. If the mail lands in spam, a business that was just onboarded cannot sign in, and the first anyone hears of it is a phone call. Step 3 is the one that decides that, and it is the one people skip.


The decisions

why
Relay Google Workspace SMTP no server to run, no IP to warm, no port 25 exception to beg for. At a few dozen invitations a month that is the whole argument
Sender care@nearledaily.com the link points at app.nearledaily.com; a password mail whose sender and destination are different domains is the shape of a phishing mail
care@ not no-reply@ somebody replying "I never got this" is the most useful reply this system can receive, and it should reach a person
Auth an App Password, never the login password it can be revoked on its own if it leaks

This replaces an earlier plan to self-host Postal. Postal is the better answer at volume; it is the wrong answer for tens of emails a month, because the work is not the software — it is IP reputation, rDNS and blocklists.


Step 1 — Google Workspace on nearledaily.com

  1. Sign up at workspace.google.com with nearledaily.com. Business Starter is enough.
  2. Verify the domain with the TXT record Google gives you.
  3. Create care@nearledaily.com. It is a real mailbox and somebody has to read it — merchant replies and bounce notices both land there, and a bounce is how you learn an invitation never arrived.

Step 2 — DNS on nearledaily.com

Record Name Value
MX nearledaily.com smtp.google.com (priority 1)
TXT (SPF) nearledaily.com v=spf1 include:_spf.google.com ~all
TXT (DKIM) google._domainkey the key from Step 3
TXT (DMARC) _dmarc v=DMARC1; p=none; rua=mailto:care@nearledaily.com
  • One SPF record only. If the domain already has one, merge include:_spf.google.com into it. Two SPF records is a permerror and fails every check.
  • Remove old MX records if the domain receives mail somewhere else today, or that mail keeps going to the old place.
  • Keep DMARC at p=none for a couple of weeks, read the reports, then move to p=quarantine. Going straight to p=reject is how you find a misaligned sender by losing its mail.

Step 3 — DKIM (the step people skip)

Admin console → Apps → Google Workspace → Gmail → Authenticate email.

  1. Generate new record (2048-bit), add the TXT record it prints to DNS.
  2. Wait for DNS to propagate — minutes to hours.
  3. Come back and click Start authentication.

Until you click that last button the mail is unsigned, and unsigned mail carrying a password link goes to spam.

Step 4 — An App Password for Fiesta

  1. Sign in as care@nearledaily.com → Google Account → Security → turn on 2-Step Verification.
  2. Security → App passwords → create one named Fiesta. You get 16 characters.
  3. That is what Fiesta uses. Never the account's real password.

No App passwords option? An admin has to allow it, or set up Admin console → Gmail → Routing → SMTP relay service with "require SMTP authentication" and "require TLS". In that case MAIL_HOST becomes smtp-relay.gmail.com.

Step 5 — Point Fiesta at it

Credentials go in .env.secrets, which is read first and is the only env file git ignores. Never in .env — that one is tracked and shared.

MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=care@nearledaily.com
MAIL_PASSWORD=<16-character app password>

Already set in .env:

MAIL_PORT=587
MAIL_FROM=care@nearledaily.com
MAIL_FROM_NAME=Nearle
MAIL_CONSOLE_URL=https://app.nearledaily.com

Google shows the App Password as four groups — abcd efgh ijkl mnop. Paste it with or without the spaces; Fiesta strips them for Google SMTP hosts only, and only when what remains is the sixteen alphanumerics an App Password actually is. Another relay's password is never edited.

MAIL_CONSOLE_URL is the merchant console and never the platform one — a merchant sets their password at app.nearledaily.com/set-password and nowhere else.

Restart. The log says which state it is in:

mail: sending as care@nearledaily.com via smtp.gmail.com:587
mail: OFF — <reason naming the missing variable>

On a hosted deployment these belong in the platform's own environment (Dokploy), not in a file in the repo. A .env committed to the repository is overwritten at build time — that is how the nutrition service shipped switched off.

What Fiesta does with them

utils/mail.go upgrades to TLS with STARTTLS before authenticating, and refuses to send at all if a relay offers no encryption while credentials are configured. Go's own smtp.PlainAuth would decline to hand over the password anyway, so nothing leaks either way — but it reports that as the server refusing the credentials, which sends somebody to check the password when the problem is the connection. It also closes a downgrade, where an attacker strips STARTTLS from the greeting.

Step 6 — Check the DNS

dig TXT nearledaily.com +short                    # SPF, with _spf.google.com
dig TXT google._domainkey.nearledaily.com +short  # DKIM key
dig TXT _dmarc.nearledaily.com +short             # DMARC
dig MX  nearledaily.com +short                    # smtp.google.com

Google's Check MX tool at toolbox.googleapps.com does the same job.

Step 7 — Prove it end to end

Not "the config looks right" — watch one arrive.

  1. Send a test to a mail-tester.com address. Aim for 9/10 or better before a real merchant sees one.
  2. Onboard a test merchant with an address you can read.
  3. Confirm it is in the inbox, not spam. In Gmail, "Show original" should show SPF, DKIM and DMARC all PASS.
  4. Follow the link, set a password, sign in at app.nearledaily.com.
  5. Press Resend invite. It must refuse, naming the business: "… has already set a password — send them to the sign-in page instead." That refusal is what stops this becoming a password reset.

Worth knowing

  • Limit: about 2,000 messages a day per user. Onboarding runs at a few dozen a month, so this is not a constraint.
  • Bounces arrive as "Delivery failed" in the care@ inbox. Nothing in Fiesta watches for them, so somebody has to read that mailbox after onboarding.
  • Not for bulk. Google does not permit marketing sends through Workspace. If newsletters are ever wanted, that is a separate provider — not this mailbox.
  • If the App Password leaks: revoke it in Google Account → Security, issue a new one, update .env.secrets. Nothing else has to change.

What the merchant receives

Plain text, deliberately. A password link arriving as an image-heavy HTML template is the shape of a phishing mail, and plain text renders identically everywhere. The body names the business, puts the link on its own line, and says it expires in seven days — because an invitation found three weeks later needs to explain itself rather than look broken.

The wording is inviteMessage in services/inviteService.go.