Docs / Concepts / Multi-tenancy

Multi-tenancy

Mailgrid is multi-tenant from the ground up. Every record carries a tenant_id; every query is parameterized.

The model

Isolation guarantees

ResourceIsolation
D1 tablesParameterized WHERE tenant_id = ? on every query. No global tables.
R2 storageKeys prefixed tenants/{tenant_id}/…
Rate limitingOne Durable Object instance per tenant. No tenant can starve another.
SuppressionsPer-tenant. The same email can be suppressed for tenant A but valid for tenant B.
From-addressIAM policy on the SES IAM user restricts From: to verified domains.

Creating tenants

Mailgrid currently provisions tenants via the bootstrap script:

bootstrap-tenant.mjs
node scripts/bootstrap-tenant.mjs \
  --name "Acme Inc." \
  --email "admin@acme.com" \
  --hmac-secret "$API_KEY_HMAC_SECRET" \
  > /tmp/seed.sql

wrangler d1 execute inboxos --remote --file=/tmp/seed.sql

The script creates a tenant row and an admin API key with all scopes. Save the key — only its HMAC hash is stored.

Plans

Each tenant carries a plan field — one of free, pro, scale, business, enterprise. The default rate_limit_per_minute is 600; override per-tenant via the database:

d1 update
wrangler d1 execute inboxos --remote --command \
  "UPDATE tenants SET rate_limit_per_minute = 3000 WHERE id = '...'"