Known limits
What Noite's alpha does not do yet — tenant isolation depth, custom domain ownership, resource containment, and the restart cost of an upgrade.
Noite is alpha and self-hosted: you run it, so you decide who gets an account. These are the gaps that matter for that decision. Each one is on the roadmap; none is a surprise to the maintainers. To report a vulnerability, see the security policy.
Tenant isolation
Under NOITE_TENANCY=multi (Tenancy) tenant code runs as unprivileged users, with a cleared environment and an egress policy. That is a sandbox for semi-trusted tenants, not for adversaries:
- Shared build user. Every build runs as the same uid. Two builds at once can read each other’s worktrees.
- Root bucket keys in fleets. A fleet’s celld process holds the object store’s root keys, so a compromised tenant process can reach every prefix: other apps’ source bundles, databases and telemetry. Scoped per-app keys are the fix.
- Object store reachable from Worker code. The store’s unauthenticated surface (it answers 401/403) is the one private address tenants may reach, because celld needs it.
- No per-app memory cap. Fleets share the container’s memory. One app’s growth pushes every app toward the same shed threshold.
- No container per build. Depth is uid, environment, resource limits and egress rules. Rootless containers or gVisor are not planned unless tenants are expected to be hostile.
If a tenant is not someone you would give shell access to a shared server, do not give them a push key yet.
Custom domains
A hostname is reserved on first claim, and Noite does not check DNS or ownership: a tenant can claim a hostname they do not own, and the first claimant wins. The certificate is issued on demand on the first visit. Treat custom domains as an admin-trusted feature until a DNS/TXT check ships.
Platforms
- Railway does not grant
NET_ADMIN, so the egress policy cannot run there: useNOITE_TENANCY=singleand invite nobody to push code (Platforms). - Coolify passes TLS through to Noite, so per-visitor rate limits are off; per-app ceilings still work.
Operations
- Upgrades restart every app. A new image restarts the runner, and every tenant fleet cold-boots with it (about a minute for many apps). There is no rolling upgrade: batch them and run them off-peak.
- Downgrades across a schema change are refused. The runner and control databases carry a schema version; an older image will not start on newer data. Back up before upgrading (Operations).
- Snapshot loss window. The runner’s SQLite is snapshotted to the bucket about every 70 seconds, so a lost volume loses at most that much recent state.
- One node. The control UI and the runner restart together and nothing fails over; availability is that of the one container and its bucket.
- RustFS is not a qualified store. It is the zero-config default and passes celld’s startup check, but for production use one celld qualifies: S3, R2, GCS, Tigris or Azure Blob (Storage).