[META] Email / notifications strategy — demo-deployer + hero_onboarding arcs #236

Open
opened 2026-05-21 21:09:28 +00:00 by mik-tf · 2 comments
Owner

[META] Email / notifications strategy — Hero OS demo-deployer + hero_onboarding arcs

Scope: mechanism for transactional emails originated by either the free-demo arc (home#235) or the paid commercial arc (hero_onboarding#1).

Decision (locked at D-20): use SendGrid for all transactional emails originated by either arc. No second email vendor. No self-hosted SMTP for v0. Provider abstraction (EmailSender trait) wraps the SendGrid implementation so future provider swaps are mechanical, but no swap is planned.

Sender domain: TBD — picked at the first session that ships an email-sending code path. Candidates: heroos.com, noreply.heroos.com, or a subdomain of the platform host. Locked + recorded as a follow-up note on D-20 at first-use.

What this covers

Email type Originator Sender
Forge account creation, password reset, Forge notifications Forgejo at forge.ourworld.tf Forgejo's own SMTP. No integration needed on our side.
Stripe / ClickPesa payment receipts Stripe / ClickPesa Provider-side, sent directly to the customer.
Idenfy KYC status notifications (pending / approved / rejected) Idenfy Provider-side, sent directly.
Demo-arc "your demo VM is ready, here's the URL" hero_os_tfgrid_deployer SendGrid (currently deferred — Phase 1 admin shares URL manually via Slack/IM; eligible for automation when Track D ships and a session opts in).
Paid-arc "your VM has been allocated, here's the URL" hero_onboarding SendGrid. v0 scope.
Paid-arc "your credit balance is low" hero_onboarding SendGrid. v0 scope.
Paid-arc "your VM is being released (expires_at reached)" hero_onboarding SendGrid. Likely s2-014 / phase 12 territory.
Paid-arc payment-failure addendum (with product context) hero_onboarding SendGrid. Stripe sends the primary receipt; our wrapper provides product context.
Both arcs feedback acks Forgejo (issue creation) Forgejo handles for free.

Rejected alternatives

  • Resend — costs separately; no infrastructural gain over the existing platform-level provider choice. Reject.
  • Postmark / Mailgun — equivalent quality + reputation to Resend; same rejection rationale. Reject.
  • AWS SES — cheaper at scale, lower DX (raw SMTP-ish, IAM, sandbox-graduation); adds AWS dependency. Reject.
  • Self-hosted SMTP (Postfix on a TFGrid VM) — sovereignty appeal. Rejected for v0 on deliverability grounds: new sender domains face weeks-to-months of IP-reputation ramp + SPF/DKIM/DMARC + bounce/reputation-monitoring ops cost. Outweighs sovereignty win at v0 volume (low single-digit-thousands of emails/month). Revisit if volume grows past a managed provider's reasonable tier OR a hard sovereignty requirement surfaces — neither is on the v0 roadmap.
  • Forgejo SMTP as a general transactional layer — Forgejo only sends for its own built-in events (account / password / notifications). Not a general "POST /api/email" service. Forge stays in its current lane.

What needs to happen at the first email-sending session (acceptance criteria)

The session that first writes email-sending code must, before merging:

  1. Pick the sender domain + edit the table at the top of this issue with the chosen value.
  2. Source SendGrid API key + DNS records (SPF / DKIM / DMARC) via the appropriate operational channels — out of scope for this issue body; handled session-side.
  3. Implement the EmailSender trait (or equivalent) + a SendGrid concrete impl + at least one transactional email type end-to-end.
  4. Smoke-test deliverability (send to a verified mailbox, confirm bounce/error surfaces in logs).
  5. Append the chosen sender domain as a follow-up note on decisions/D-20-email-provider-sendgrid.md (not as a new decision — same decision, populated detail).

Out of scope (this issue)

  • Templating choice (React Email, hand-rolled HTML, etc.) — each arc picks its own; SendGrid accepts plain HTML.
  • Suppression lists / unsubscribe handling — SendGrid built-in; configure when the first list-style email type lands (none on v0 roadmap; all v0 emails are transactional).
  • Email analytics / open-tracking — disabled by default per privacy posture; revisit if a marketing-style use case appears.

Sequencing

  • s140+ (demo arc, Track B start): nothing email-related yet. Standing rule logged in prompt.md §3 so any session that touches email knows the SendGrid + acceptance-criteria constraint.
  • Demo arc — Track D session adding deployer "VM ready" automation (post-s146): acceptance criteria above, then implement.
  • Paid arc — first hero_onboarding email session (s2-010 or later; agent-2 territory): acceptance criteria above, then implement.
  • home#235 — free-demo arc master tracker (Tracks A-F).
  • hero_onboarding#1 — paid-arc master tracker.
  • hero_os_tfgrid_deployer#1 — demo-arc decisions source (meeting minutes).
  • decisions/D-20-email-provider-sendgrid.md — the formal decision lock with full rationale + consequences + alternatives. This issue is the live who-sends-what tracker; D-20 is the durable rationale anchor.
## [META] Email / notifications strategy — Hero OS demo-deployer + hero_onboarding arcs **Scope:** mechanism for transactional emails originated by either the free-demo arc ([home#235](https://forge.ourworld.tf/lhumina_code/home/issues/235)) or the paid commercial arc ([hero_onboarding#1](https://forge.ourworld.tf/lhumina_code/hero_onboarding/issues/1)). **Decision (locked at D-20):** use **SendGrid** for all transactional emails originated by either arc. No second email vendor. No self-hosted SMTP for v0. Provider abstraction (`EmailSender` trait) wraps the SendGrid implementation so future provider swaps are mechanical, but no swap is planned. **Sender domain:** TBD — picked at the first session that ships an email-sending code path. Candidates: `heroos.com`, `noreply.heroos.com`, or a subdomain of the platform host. Locked + recorded as a follow-up note on D-20 at first-use. ## What this covers | Email type | Originator | Sender | |---|---|---| | Forge account creation, password reset, Forge notifications | Forgejo at `forge.ourworld.tf` | Forgejo's own SMTP. No integration needed on our side. | | Stripe / ClickPesa payment receipts | Stripe / ClickPesa | Provider-side, sent directly to the customer. | | Idenfy KYC status notifications (pending / approved / rejected) | Idenfy | Provider-side, sent directly. | | Demo-arc "your demo VM is ready, here's the URL" | hero_os_tfgrid_deployer | **SendGrid** (currently deferred — Phase 1 admin shares URL manually via Slack/IM; eligible for automation when Track D ships and a session opts in). | | Paid-arc "your VM has been allocated, here's the URL" | hero_onboarding | **SendGrid**. v0 scope. | | Paid-arc "your credit balance is low" | hero_onboarding | **SendGrid**. v0 scope. | | Paid-arc "your VM is being released (expires_at reached)" | hero_onboarding | **SendGrid**. Likely s2-014 / phase 12 territory. | | Paid-arc payment-failure addendum (with product context) | hero_onboarding | **SendGrid**. Stripe sends the primary receipt; our wrapper provides product context. | | Both arcs feedback acks | Forgejo (issue creation) | Forgejo handles for free. | ## Rejected alternatives - **Resend** — costs separately; no infrastructural gain over the existing platform-level provider choice. Reject. - **Postmark / Mailgun** — equivalent quality + reputation to Resend; same rejection rationale. Reject. - **AWS SES** — cheaper at scale, lower DX (raw SMTP-ish, IAM, sandbox-graduation); adds AWS dependency. Reject. - **Self-hosted SMTP** (Postfix on a TFGrid VM) — sovereignty appeal. Rejected for v0 on deliverability grounds: new sender domains face weeks-to-months of IP-reputation ramp + SPF/DKIM/DMARC + bounce/reputation-monitoring ops cost. Outweighs sovereignty win at v0 volume (low single-digit-thousands of emails/month). Revisit if volume grows past a managed provider's reasonable tier OR a hard sovereignty requirement surfaces — neither is on the v0 roadmap. - **Forgejo SMTP as a general transactional layer** — Forgejo only sends for its own built-in events (account / password / notifications). Not a general "POST /api/email" service. Forge stays in its current lane. ## What needs to happen at the first email-sending session (acceptance criteria) The session that first writes email-sending code must, before merging: 1. Pick the sender domain + edit the table at the top of this issue with the chosen value. 2. Source SendGrid API key + DNS records (SPF / DKIM / DMARC) via the appropriate operational channels — out of scope for this issue body; handled session-side. 3. Implement the `EmailSender` trait (or equivalent) + a SendGrid concrete impl + at least one transactional email type end-to-end. 4. Smoke-test deliverability (send to a verified mailbox, confirm bounce/error surfaces in logs). 5. Append the chosen sender domain as a follow-up note on `decisions/D-20-email-provider-sendgrid.md` (not as a new decision — same decision, populated detail). ## Out of scope (this issue) - Templating choice (React Email, hand-rolled HTML, etc.) — each arc picks its own; SendGrid accepts plain HTML. - Suppression lists / unsubscribe handling — SendGrid built-in; configure when the first list-style email type lands (none on v0 roadmap; all v0 emails are transactional). - Email analytics / open-tracking — disabled by default per privacy posture; revisit if a marketing-style use case appears. ## Sequencing - **s140+ (demo arc, Track B start)**: nothing email-related yet. Standing rule logged in `prompt.md §3` so any session that touches email knows the SendGrid + acceptance-criteria constraint. - **Demo arc — Track D session adding deployer "VM ready" automation (post-s146)**: acceptance criteria above, then implement. - **Paid arc — first hero_onboarding email session (s2-010 or later; agent-2 territory)**: acceptance criteria above, then implement. ## Cross-links - [home#235](https://forge.ourworld.tf/lhumina_code/home/issues/235) — free-demo arc master tracker (Tracks A-F). - [hero_onboarding#1](https://forge.ourworld.tf/lhumina_code/hero_onboarding/issues/1) — paid-arc master tracker. - [hero_os_tfgrid_deployer#1](https://forge.ourworld.tf/lhumina_code/hero_os_tfgrid_deployer/issues/1) — demo-arc decisions source (meeting minutes). - `decisions/D-20-email-provider-sendgrid.md` — the formal decision lock with full rationale + consequences + alternatives. This issue is the live who-sends-what tracker; D-20 is the durable rationale anchor.
Author
Owner

Update: the email provider choice has changed since this issue was written. We will use Resend instead of SendGrid, behind a small provider abstraction (one send method), by porting the implementation already running in the freezone project (about 100 lines, including a dev-mode fallback that just logs when no API key is set). The first email to ship is the demo deployer's "your machine is ready, here is the URL" message, sent from noreply@hero.ourworld.tf. This is planned as the next step after the current agent and books work.

Signed-by: mik-tf mik-tf@noreply.invalid

Update: the email provider choice has changed since this issue was written. We will use Resend instead of SendGrid, behind a small provider abstraction (one send method), by porting the implementation already running in the freezone project (about 100 lines, including a dev-mode fallback that just logs when no API key is set). The first email to ship is the demo deployer's "your machine is ready, here is the URL" message, sent from noreply@hero.ourworld.tf. This is planned as the next step after the current agent and books work. Signed-by: mik-tf <mik-tf@noreply.invalid>
Author
Owner

Update for the next email step. The demo-arc "your VM is ready, here is the URL" email is moving from deferred to active so testers can be onboarded automatically. The provider decision has changed since this issue was written: use Resend instead of SendGrid, keeping the same provider-abstraction trait so the swap stays mechanical. The freezone app already runs a Resend implementation that can be ported (about 100 lines, with a dev-mode fallback that logs instead of sending). Plan: port it into hero_os_tfgrid_deployer, send from a Hero sender address, store the key in the deployer secret store, and test the send end to end on a provision.

Update for the next email step. The demo-arc "your VM is ready, here is the URL" email is moving from deferred to active so testers can be onboarded automatically. The provider decision has changed since this issue was written: use Resend instead of SendGrid, keeping the same provider-abstraction trait so the swap stays mechanical. The freezone app already runs a Resend implementation that can be ported (about 100 lines, with a dev-mode fallback that logs instead of sending). Plan: port it into hero_os_tfgrid_deployer, send from a Hero sender address, store the key in the deployer secret store, and test the send end to end on a provision.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/home#236
No description provided.