Tenancy
NOITE_TENANCY decides whether other people may push code — multi runs tenant code sandboxed, single is just you.
NOITE_TENANCY decides whether other people may push code to the install. Unset, it is multi off localhost and single on localhost.
multi: tenant builds and release commands run as the unprivilegedbuilduser with a cleared environment (no platform secret reaches them), tenant fleets as thefleetuser, and an nftables policy on those users closes new connections to loopback, private ranges and cloud metadata. The one private address allowed is the object store the fleet’s own celld needs; Worker code can reach it but holds no keys./datais private to the runner. Release commands are skipped until scoped per-app bucket keys exist (what that means). All of this needsNET_ADMIN,SETUID,SETGIDandCHOWN, whichdocker/compose.yamlgrants; without them the runner keeps/readyat 503 with the reason and refuses builds.single: only you push code. No egress policy, and release commands run with the root bucket keys.
What multi does and does not promise (alpha)
multi is for semi-trusted tenants: people you would invite to your server, running code you do not review, who are not trying to break out. It stops accidents and casual snooping. It is not a boundary you can hand to strangers.
- Fleets hold the object store’s root keys (scoped per-app keys are not built yet). A compromised tenant process can read and write every app’s data in the bucket, including other tenants’ git bundles and databases.
- Builds share one
builduser, so two builds running at once can read each other’s worktrees. - Worker code can reach the object store’s unauthenticated surface.
- Fleets share the container’s memory: one app’s growth pushes every app toward the same shed threshold.
- Release commands (for example D1 migrations) are skipped in
multi, because they would need those root keys. Apps that need a migration step before they start needsinglefor now.
Until scoped credentials land, run multi only for people you trust the way you would trust a colleague with shell access to a shared box, and use single when you are the only one pushing. The full list is in Known limits.
The repository’s isolation lane (make e2e-isolation, also runnable from the CI workflow) proves the multi claims: a hostile tenant app tries to read platform secrets and files and to reach every internal API from its build, its release command and its Worker, and the lane asserts every attempt fails while internet egress still works.
NOITE_TENANCY and related knobs are documented in Environment variables.