Ghar Email
Sending and receiving mail are two completely different problems, and almost nothing about either one works the way you'd guess. A tour of MX records, MIME, SPF/DKIM/DMARC, and why email has no such thing as a conversation
Tech Stack
About This Project
hello@ and support@ are addresses, not people, but every mail provider prices them per seat. Ghar runs them as real shared mailboxes on a domain you already own, for nothing a month. You bring the domain and the API keys, so there is no per-user bill to grow into.
I wrote up the parts that surprised me about the protocol itself in I Built My Own Email. This is the architecture.
Shape
inbound sender → Cloudflare Email Routing → Email Worker → POST /v1/ingest/email
(raw RFC 5322 + per-domain secret)
outbound POST /v1/messages → Resend, using the tenant's own API key
Two halves, two vendors, because receiving is an uptime problem and sending is a deliverability problem. Cloudflare is the building that answers on port 25. Resend is the reputation I do not have to build myself.
Decisions worth defending
Tenants deploy their own inbound worker. The worker is include_str!'d into the binary and served from GET /v1/domains/:id/worker with the API URL already substituted, so there is exactly one value left to paste. It ships with its own INGEST_SECRET. There is deliberately no platform-wide ingest key: a shared secret would let any tenant inject mail into anyone else's mailbox, so the secret is per domain and checked against the resolved alias's own domain row.
One predicate does all the authorization. Mail belongs to an alias, not a user. Every alias-scoped query embeds the same generated WHERE clause:
($2 -- superadmin
or {col} in (select alias_id from alias_members where user_id = $1)
or {col} in (select a.id from aliases a
join domain_members dm on dm.domain_id = a.domain_id
where dm.user_id = $1))
One place to audit, and it composes: you see a mailbox if you were assigned it, or if you run the domain it belongs to. Postgres RLS mirrors the same three rules, so a mistake in the app layer is not the only thing standing between two tenants.
404, never 403. Out-of-reach resources are indistinguishable from nonexistent ones, so tenants cannot probe each other for what exists.
Secrets are sealed, not stored. Resend keys and ingest secrets are AES-256-GCM encrypted at rest and only ever returned masked, except the ingest secret once at creation.
No sending until you opt in. A domain with no Resend key is receive-only and says so explicitly, rather than accepting a send and quietly dropping it.
A signature beats BIMI. I built the full BIMI flow, including an SVG Tiny Portable/Secure validator, then found that Gmail also wants a $1,100/year certificate backed by a registered trademark. The UI says this outright instead of letting people discover it after setup. The feature that actually earns its keep is an HTML signature appended on send, which works everywhere and costs nothing.
Cost
Cloudflare Email Routing: free, unlimited addresses. Resend: 3,000 messages a month. Supabase, Render and Vercel free tiers. The domain was already paid for.
The per-seat pricing was never covering the hard part. Receiving is free, sending is nearly free, and the rest was an assumption that a shared address should be billed like a person.