Files
openmail/docs/adr/0006-self-hosted-first.md
Karti TripathiandClaude Opus 5 a42b798a0e Scaffold tier 2 and 3, the schema, the website, and self-hosted-first
Store: the full Postgres schema as an embedded migration. UUIDv7 keys so
`ORDER BY id` is a free chronological index; raw MIME and attachments live
in object storage with only a key in the row; `pods` present from day one
because retrofitting tenancy costs more than an unused column. API keys are
stored as a SHA-256 hash — a database dump must not be a set of live
credentials.

API: the v0 route table, including `ingest`, which closes the
receive→thread→extract loop with zero mail infrastructure and is what makes
the agent layer testable in CI. Scopes are a closed enum rather than
strings, so "can send mail" and "can mint keys" are not one typo apart.
Internal errors are logged in full and reported as a bare string.

MCP: the tool catalogue, six tools. Adding a row here is the only way an
agent gains a capability — a new REST route is invisible until someone opts
it in. Three tests guard the rule that no tool can ever reach key
management; CI fails rather than production.

ADR 0006: enterprise self-hosted first. A hosted offering comes only after
we have run this ourselves long enough to have a deliverability record
worth selling. `pods` stays in the schema as the thing that keeps that path
open — do not remove it as dead code.

Also: multi-stage Dockerfile running as a non-root system user with no
shell in the runtime image, and the openmail.karti.ai static page.

17 tests, zero clippy warnings, fmt clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkyvfNJGTshJNE9FtwPLk7
2026-09-02 13:20:51 -07:00

1.9 KiB

ADR 0006 — Enterprise self-hosted first; SaaS only after we run it ourselves

Status: Accepted, 2026-09-02.

Decision

The product is a self-hostable server for enterprises, small businesses and builders. A hosted offering is not a v1 goal and may never exist. If it does, it comes only after we have run OpenMail ourselves, in production, long enough to have a deliverability track record worth selling.

Why this ordering and not the reverse

  • Deliverability cannot be shortcut. A hosted product's entire value is inbox placement, which is months of IP warmup, feedback-loop enrolment and suppression-list discipline. Selling that before we have it is selling something we do not own.
  • Self-hosted is the differentiator. Every competing agent-mailbox product is hosted-only. Leading with a SaaS puts us on their ground, competing on the thing they have already spent years on.
  • Auditability is the purchase condition. Enterprises will not point MX at a closed box. Public source under Apache-2.0 is why they can say yes, and the self-hosted path is the one that requires no trust in us at all.
  • We become our own first serious user. The bugs that matter in mail — silent DANE downgrades, an MTA-STS policy cached wrong for a year — surface only under real traffic. Running it ourselves before selling it is how we find them on our own mail instead of a customer's.

Consequences

  • pods (tenancy) stays in the schema from v0.1 even though nothing uses it. It is the piece that keeps the SaaS path open without a migration. Do not remove it as dead code.
  • No billing, no Stripe, no hosted control plane in the tree. When someone proposes one, this ADR is the answer.
  • Docs, defaults and error messages are written for an operator running one box, not for a tenant of ours.
  • The website (website/) sells self-hosting and links to the repo. It does not collect signups for a product that does not exist.