Tenants & stores
Forge has two levels of grouping, and they answer different questions.
A tenant is who owns the data. A store is a front the data is sold through.
The tenant is the isolation boundary
Section titled “The tenant is the isolation boundary”Each tenant’s data lives in its own Postgres schema. Not a tenant_id column that every query must
remember to filter on — a schema. The kernel sets the search path for the transaction, and a query that
forgets a filter simply cannot see another tenant’s rows, because they are not on the path.
There is also a system schema, forge_control, which holds what belongs to the platform rather than to any
tenant. Commands marked System: yes run there.
Two things follow, and both matter to you:
- A tenant is a unit you can move. Its data is a schema; a backup is a schema dump. There is no disentangling.
- The isolation does not depend on application code being careful. It depends on where the tables are.
The store is the commercial boundary
Section titled “The store is the commercial boundary”A tenant can run several stores: different domains, currencies, catalogues, price lists, shipping rules, payment methods. A product can be published to one store and not another; a price can differ per store.
A store is resolved from the request — a public read carries store, and a storefront resolves it from its
own hostname. It is not a credential and it is not authorisation: knowing a store id lets you read the
public face of that store, which is by definition what anyone with a browser can do.
Which one do I pass?
Section titled “Which one do I pass?”- Public reads take
store. They have no credential to resolve a tenant from. - Internal reads and commands resolve the tenant from the credential. There is no tenant parameter to get wrong, and passing one would be a way to ask for someone else’s data.
- Where a command needs to act on a specific store, it takes a store id in its input, and the kernel checks that the store belongs to the caller’s tenant.
What this is not
Section titled “What this is not”It is not multi-tenancy as a discount. Every tenant gets the whole kernel; there are no features gated by plan, because there are no plans. The instance you run is the instance you run.
Where a customer wants a genuinely separate box — their own database, their own upgrade cadence, their own infrastructure — that is not a bigger tenant. That is their own instance, and the model supports it directly.