4 · Email (SMTP)
Objective — wire transactional email and prove it actually sends — the hard part is finding where SMTP resolves at runtime, because the most common vendor family loads it from the DB and the panel’s warning banner lies about it.
Steps at a glance:
- Find the real source of truth — Before touching any field, decide where SMTP config actually resolves at
runtime. The brand survey (page 2) already flagged whether the vendor ships a
CustomConfigProvider; confirm it now. - Configure SMTP in the admin panel —
Admin → Config → SMTP(orSettings → Email). The human enters the password/secret field; the agent fills non-secret fields, clicks the panel test, and checks logs when browser automation is available. Inbox confirmation stays human-owned unless the operator explicitly shares that mailbox session. - Set a sensible
.envfallback (defence in depth) — Even when the DB path is truth, leave realMAIL_*values inshared/.env. If theemail_settingsrow is corrupted or the provider query fails, Laravel falls back to.env. - Review email templates & hygiene — audit the app’s email templates and sender hygiene, so customer-facing messages are branded, deliverable, and not leaking vendor defaults.
Background
Section titled “Background”Transactional email is a MUST — password resets, invoices, and notifications all depend on it, and section 1 of page 3 warned you not to change the admin email until mail works. The hard part isn’t the SMTP fields; it’s knowing where the app actually reads them from — and on the most common CodeCanyon vendor family, the admin UI actively lies about that.
Before editing mail settings, read Zaj-PROJECT.md for the selected environment, mail provider, from-address, vault item pointer, and whether runtime mail config is DB-owned or .env-owned. Update those non-secret facts after verification; never store SMTP passwords there.
1. Find the real source of truth
Section titled “1. Find the real source of truth”Before touching any field, decide where SMTP config actually resolves at runtime. The brand survey (page 2) already flagged whether the vendor ships a CustomConfigProvider; confirm it now.
flowchart TD Q{"Does a CustomConfigProvider /<br/>SettingsProvider rewrite mail config<br/>at boot from a DB table?"} Q -->|Yes — Froiden family| DB["DB table is truth<br/>(email_settings).<br/>Configure the ADMIN PANEL.<br/>.env MAIL_* = fallback only"] Q -->|No admin Email tab at all| ENV["Configure shared/.env MAIL_*<br/>(see Phase 8 email infra)"] Q -->|Panel present, no provider| PANEL["Admin panel writes config<br/>directly — use it"]-
Grep for a provider that rewrites mail config at runtime.
Terminal window grep -rn "mail\.mailers\.smtp\|Config::set.*mail" app/Providers/ app/Http/Middleware/ 2>/dev/null | headfind app/ -name "*ConfigProvider*" -o -name "*SettingsProvider*" 2>/dev/null# Expected: a provider that calls Config::set('mail...'), or no match- ✅ If a provider calls
Config::set('mail.mailers.smtp.*', DB::table(...)), the database overrides.envand the admin panel is the source of truth; otherwise.envis.
- ✅ If a provider calls
The lesson generalizes beyond this one banner:
2. Configure SMTP in the admin panel
Section titled “2. Configure SMTP in the admin panel”Admin → Config → SMTP (or Settings → Email). This is a mixed browser flow: human-owned secret fields, agent-owned non-secret fields, save/test clicks, banner checks, and log verification when browser automation is available.
-
Fill the SMTP fields from your Phase 4 SMTP credentials + brand profile.
Field Value Mail From Name App name (brand profile) Mail From Email admin@[DOMAIN]SMTP Host e.g. smtp.hostinger.comSMTP Port 587(TLS) or465(SSL)Username / Password mailbox creds — the human enters the password from the vault; the agent never types or reads it Encryption TLS(587) /SSL(465)- ✅ All fields are filled from real credentials; the password was human-entered from the vault and never exposed to the agent.
-
Save, then click the panel’s own “Test SMTP” button — not
artisan tinker. Tinker sends through Laravel’s default mailer, which a CLI context may not have reconfigured via the provider yet.- ✅ “Test SMTP” succeeds and the dashboard “SMTP details are not correct” banner disappears — that’s your real success signal.
-
Trigger a password-reset on a test account and confirm the email arrives.
- ✅ A real password-reset email lands end-to-end in the inbox.
3. Set a sensible .env fallback (defence in depth)
Section titled “3. Set a sensible .env fallback (defence in depth)”Even when the DB path is truth, leave real MAIL_* values in shared/.env. If the email_settings row is corrupted or the provider query fails, Laravel falls back to .env. Real values = mail still works; garbage values = mail breaks silently.
-
Append the fallback idempotently (Python-free here, but
grep -qguards against duplicates; special-char passwords stay out of this block).Terminal window grep -q "^MAIL_HOST=" shared/.env || cat >> shared/.env <<'EOF'# SMTP fallback (.env defaults; runtime config overridden by CustomConfigProvider from DB)MAIL_MAILER=smtpMAIL_HOST=smtp.hostinger.comMAIL_PORT=465MAIL_ENCRYPTION=sslMAIL_FROM_ADDRESS=admin@[DOMAIN]MAIL_FROM_NAME="[APP_NAME]"EOF# Expected: the MAIL_* block is appended once (no duplicate on re-run)- ✅
shared/.envcarries realMAIL_*fallback values, appended only once.
- ✅
4. Review email templates & hygiene
Section titled “4. Review email templates & hygiene”-
Review the email templates (if the vendor exposes
Admin → Email Templates) — welcome, password-reset, invoice, and notification — setting the sender name, logo, and footer to match your brand (the email header logo you uploaded on page 3 lives here on some vendors).- ✅ Sender name, logo, and footer match the brand on every reviewed template.
-
Confirm no SMTP credentials are committed to git — they belong in
shared/.envon the server and your password manager only.- ✅ No SMTP secrets are tracked in git.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🤖 Source confirmed — where SMTP resolves at runtime (DB-via-provider vs
.env) is confirmed; the Froiden banner was verified against source, not trusted. - 🔀 Panel SMTP saved — human entered secrets; agent saved/tested the panel when browser automation was available; “Test SMTP” passes and the dashboard error banner is gone.
- 👤 Reset email received — a real password-reset email arrived end-to-end, or the mailbox-only check is tracked with owner/trigger.
- 🤖
.envfallback set —shared/.envcarries realMAIL_*fallback values; no SMTP secrets in git. - 👤 Templates reviewed — sender, logo, footer reviewed if the vendor exposes them.
- 🤖 Project state updated —
Zaj-PROJECT.mdrecords the mail provider, from-address/name, runtime source of truth (admin DB vs.env), vault pointer, and last verified date without secrets.