One app serves the operator and every customer. There is no per-tenant deployment and no per-tenant codebase — tenancy is a data + identity concern, not a routing one.
A signed-in user carries a Supabase JWT whose tenant_id (mirrored on
profile.tenant_id) determines what they can see:
tenant_id = the operator org) → the whole fleet: every
business, every agent, every run.The same code paths and the same SPA serve both. The dashboard subdomain
(design.miqromanage.app, <tenant>.miqromanage.app) is a vanity entry point;
it does not by itself grant access — the JWT does.
Each tenant has two distinct host fields, and conflating them is the classic bug:
| Field | Meaning | Example |
|---|---|---|
subdomain |
the private dashboard host | acme.miqromanage.app |
custom_domain |
the public website host | acme.com |
Published customer sites are served path-based at miqrodesign.com/site/{slug}
or on the customer's own custom_domain — never on a *.miqromanage.app
dashboard subdomain.
Tenant data lives in shared Postgres tables (Supabase) and is isolated by
row-level security. Queries are constrained to the caller's tenant_id, so a
customer cannot read another customer's rows even though the rows share a table.
Server-to-server and agent code uses a privileged path and must pass the
tenant_id explicitly — there is no ambient "current tenant."
A new tenant is created with a slug. Before the slug becomes
<slug>.miqromanage.app, it is checked against the reserved-subdomain set so
it can't collide with an infrastructure or product host. The provisioner agent
folds the same set into its slug-uniqueness check so auto-naming never round-trips
a 409.
Today the apex (miqromanage.app) redirects management-view paths to
design.miqromanage.app because miqrodesign is currently the only business —
a single-tenant shortcut (a 302, deliberately not cached). The correct
end-state, for when a second business onboards, is: the apex serves the SPA and
the SPA redirects post-auth to the signed-in user's own
<tenant>.miqromanage.app. This is a routing detail, not a data-model change.