Isolation and trust
What it is
Each environment gets its own database, its own service logins and its own keys, so an app's users and tokens cannot reach another environment. Some parts are deliberately shared (the PostgreSQL engine, Storage, the gateway and the host), and anything shared is a place where one environment's failure can affect the others.
Why
Choice: trusted operators, untrusted app visitors. The people who run the server and write SQL for its environments are trusted. The visitors of the apps, with their anonymous or signed-in tokens, are not. This matches the primary user (one developer or agency running apps for their own clients) and keeps the design honest about what it defends against.
Rejected alternative: hostile multi-tenant hosting. Selling space to mutually hostile customers would require isolation from the operator and from arbitrary SQL, which a shared PostgreSQL engine cannot give. Anyone who needs that should run independent stacks.
Choice: separate credentials, not only separate schemas. Separation by schema inside one database would leave every environment behind the same Auth and the same API roles. A database per environment, with its own logins and connection rules that allow exactly one login to reach exactly one database, keeps a leaked service credential inside its environment.
How we built it
Only the TLS proxy faces the network; the engine and Storage are shared failure boundaries.
No data mixing: A's key, tokens and logins open only A's database.
A busy environment uses the idle server, but never the share its neighbours need. The limits are not calibrated under sustained load.
What is separate per environment:
- A database with
CONNECTgranted only to its own logins, and connection rules (the PostgreSQL host-based authentication file, HBA) naming exact login and database pairs. - Three scoped service logins for Auth, REST and Storage, with generated passwords.
- A developer login for migrations, agents and Studio. It is not a superuser, cannot use the platform's schema names, and cannot create extensions that reach the server's network or see every environment, such as
http,pg_netordblink(database access). - Its own Auth signing secret, its own publishable and secret keys (secret keys stored only as hashes), and its own Storage tenant and signing keys.
- Its own Realtime, Edge Functions and Studio containers when they are on. The Functions container reaches only its own Auth, REST and Storage tenant, and each Studio session has a private network.
- A routing record that can put the environment in maintenance without touching the others.
What is shared, and therefore a shared failure boundary:
- The PostgreSQL engine. A crash, a full disk or a long lock affects every environment on it. The canonical API roles (
anon,authenticated,service_role) exist once per engine, because PostgreSQL roles are cluster-wide. - The Storage process. It holds every tenant's configuration; a compromise of that process is a compromise of all tenants' files.
- The gateway and control catalog. If they stop, every environment stops answering.
- Vault's root key. Every environment's Vault secrets are encrypted with one key that belongs to the server. They stay apart because each environment reaches only its own database.
- The host. CPU, memory, disk and IO are shared. Each container runs under a resource tier with CPU weight and per-device IO limits, and new environments are refused when memory, disk, cgroup pressure or connection budgets are short.
Code: lab/resource_policy.py (tiers), lab/resource_admission.py, lab/pressure_admission.py and lab/connection_budget.py (admission), lab/hba_authority.py (connection rules), src/gateway/handler.ts (key checks).
Limits
- Isolation from a hostile server operator or from arbitrary SQL run by a trusted author is not provided.
- Resource tiers are configured, not calibrated under sustained load; a noisy neighbour can still slow the others.
- Reloading connection rules does not end sessions that were already open.
- Multiple adversarial reviews informed the design; they are not a security certification.
Go deeper
- Threat model: assets, actors, trust boundaries, the evidence behind each mitigation and the known gaps.
- Security and operations review.
- Resource policy, resource admission, pressure admission, connection budget, noisy neighbour.
- Shared Storage review and atomic HBA replacement.