Changelog
All notable changes to sbarbase. The format follows Keep a Changelog and the project uses Semantic Versioning, which for a 0.x version means the interface may still change between releases.
The declared package version is 0.5.13. Historical 0.1.0 and 0.2.0 entries retain their original scope. Later Docker and VM rehearsals are dated in status; no independent public-server or production release acceptance is established.
[Unreleased]
Still open
- Deleting an environment does not work on an ordinary installation. It always answers
409with "Exact ownership inventory unavailable" and changes nothing, so restore and purge cannot be reached either. Provisioning never records the exact list of an environment's resources, and the shared PostgreSQL cluster and Storage process have no check yet that would make such a list honest. The API reference said delete "can answer"409; it now says it always does, and a test pins the current behaviour.
[0.5.13] - 2026-10-19
Fixed
- Signing out of the console no longer ends your access tokens. The console also signs out on its own when a session expires, so an agent's MCP or
/v1access could stop with401while nobody did anything. Tokens now end only as the agents guide says: when revoked or expired, at a password reset or factor recovery, or when the MFA policy changes. A token already stopped this way stays stopped: create a new one.
[0.5.12] - 2026-10-19
Fixed
- A Studio opened before the upgrade to 0.5.11 also starts again by itself after it: on that first restart the activity record does not exist yet, so the time the console last asked for the session is used.
[0.5.11] - 2026-10-19
Fixed
- No sign-in flash. With a stored sign-in, the console waits on a neutral screen while the Auth client checks it, instead of showing the sign-in page for a moment and then the console. With no stored sign-in, the sign-in page shows at once.
- Studio stays open while it is used. The Studio session renews itself while its tab makes requests. It ends after eight hours without any request or seven days after it was opened from the console; signing out, a password change or losing the role still end it at once (Studio).
- Studio comes back after a restart. A restart or an upgrade brings back a Studio used in the last eight hours, after the upgrade is confirmed. An open tab shows "Studio is starting", reloads by itself and continues.
[0.5.10] - 2026-10-19
Fixed
- Docker installs can set a public Studio address.
compose.yaml,deploy/compose.shand the host preflight now forwardSBARBASE_STUDIO_HOST; before, the console container could never receive it (Studio).
[0.5.9] - 2026-10-19
Fixed
- Studio's SQL editor and Edge Functions page. Saved SQL snippets now live in a folder inside the Studio session instead of failing with "SNIPPETS_MANAGEMENT_FOLDER env var is not set", and the Edge Functions page lists each function's deployed code, copied when Studio opens.
- Studio can change what migrations made. Studio works as the environment's developer login, as Supabase Studio works as a project's
postgresuser: it can alter tables, policies and row level security, and sees the developer's own schemas, Vault and migrations. What it creates belongs to the same owner (Studio). - A blocked Studio route is caught. Opening Studio checks its route to Auth and Storage from inside Studio, not from the host, and names the firewall when it is blocked. The server guide lists the two narrow firewall rules it needs (server deployment).
[0.5.8] - 2026-10-19
Added
- One setting for Studio's public address.
lab/studio_address.py plan DOMAINlists what to provide once (a wildcard DNS record, a certificate that covers it and the setting);apply DOMAINwritesSBARBASE_STUDIO_HOSTfor the console and the TLS proxy and restarts them;verify DOMAINsays which step is missing. The TLS proxy now readsSBARBASE_STUDIO_HOSTwhen it has no--studio-hostflag, so one drop-in configures both. After that, every new project opens Studio with no DNS or certificate work (Studio).
Changed
- The console's notice for a server without a public Studio address now names this setup instead of only the variable.
Added
- An environment can create its own schemas and use Vault. The developer login, used by direct database access and by agents, may now create and rename schemas of its own, so migrations that keep code outside
public(for examplecreate schema app) run as on Supabase, and so doessupabase db push. Names the platform uses (auth,storage,extensions,realtime,vault,cron,net,graphql, and any starting withsupabaseorsbarbase) stay refused, by a guard the login can neither drop nor disable. Vault is installed in each environment with the grants Supabase givespostgresandservice_role, without the right to pass them on; all environments of a server share its root key. Thesupabase_realtimepublication now always exists and belongs to the login, so migrations can add tables to it as on Supabase. Extensions that reach the server's network or see every environment, such ashttp,pg_net,dblink,postgres_fdw,pg_stat_statementsandpgroonga(which can write files anywhere on the server), are now refused for the environment's login; the image's supautils used to create them for it. Turn database access off and on once for an environment whose access is already on (database access).
Fixed
- Turning database access off and on keeps a migration's narrower default grants. Each time access was turned on, the API's default grants to
anon,authenticatedandservice_rolewere given again, undoing a migration that had revoked them, for example execute rights on new functions. They are now given once, the first time (database access).
Changed
- The console has a new look. Colours, spacing and components follow Supabase's design tokens, each environment screen has one primary action, and a contrast check guards the palette (pull request 18).
[0.5.7] - 2026-10-19
Fixed
- Studio opens behind the TLS proxy. Studio compresses its pages. The console's Studio proxy and the TLS proxy both let Bun's
fetchdecode the body while keeping thecontent-encodingheader, so the next hop received plain bytes labelled gzip and failed with a decompression error (Cloudflare showed 520). Both now pass Studio's bytes and header through together.
[0.5.6] - 2026-10-19
Fixed
- No more sign-outs after an hour away. The console's access token lasts an hour. A request made with an expired one signed you out and deleted the session on the server, although the session could have been renewed. The console now renews the token and retries once, and signs out only if the renewal is refused.
[0.5.5] - 2026-10-19
Fixed
- No more Studio tab that never loads. On a server whose Studio has no public address, opening the dashboard from the console's public address left a tab reading "Starting Supabase Studio" forever. The console now learns this from the Studio status before opening anything, shows the SSH tunnel steps and opens no tab; a tab already opened for a new project says what happened and closes (Studio).
Changed
- The console looks and reads like Supabase. Every page now uses Supabase's own colour tokens and the look of its buttons, fields, cards, alerts, badges and tables, with stronger edges so sections, fields and buttons no longer blend into one grey. The main action on a screen is the one solid green button, read-only values such as tokens and commands look different from fields you type into, a new key or token appears in a green-edged panel, and focus rings are visible in both themes. Text and control edges meet WCAG AA in light and dark, checked by
tests/ui-contrast.test.ts. Routes, labels and behaviour are unchanged (console design). - The console keeps you signed in for thirty days.
SBARBASE_MANAGEMENT_SESSION_SECONDSnow defaults to 2592000 instead of 43200, so a sign-in no longer ends after twelve hours. Set a lower value to keep the old behaviour (configuration). - Telegram notifications are in plain words. A message says what happened, where (by name), the likely cause and what to do, without ids, field names or reason codes. Email, the webhook and the catalog keep the full record (OPERATOR-NOTIFICATIONS, section 3.6).
[0.5.4] - 2026-10-18
Changed
- Install with Docker is the front door. The Docker guide starts from a bare Ubuntu 26.04 machine:
docker.io,docker-compose-v2anddocker-buildxfrom the distribution, then clone, check, up and bootstrap, with a note to wait for the console before bootstrap (Install with Docker). The website's install section shows these five commands; the systemd quickstart stays linked as "Without Docker".lab/vm-docker-rehearsal.shran it in a local virtual machine, 14 checks (vm-docker-install-checks.json); not yet on a real server.
Security
- Edge Functions leave the shared runtime network. Each environment's Functions container reaches only its own Auth and REST on that environment's network, and Storage through a host listener that pins the environment's tenant, so one environment's functions cannot reach another's services (FUNCTIONS-NETWORK-ISOLATION). The outbound network refuses traffic between its containers.
[0.5.3] - 2026-10-17
Added
- Studio from the public address.
SBARBASE_STUDIO_HOSTsets each environment's Studio address as a pattern, such asstudio-{id}.example.com, and the TLS proxy serves it with--studio-host. With a wildcard DNS record and a certificate that covers it, the console opens Studio on that address instead of<id>.studio.localhost, which only an SSH tunnel reaches (Studio). Until then, the console opened from its public address explains the tunnel instead of opening a tab the browser cannot reach.
Security
- The TLS proxy serves only its public host, loopback names and matching Studio addresses, and answers any other host with 421, so a wildcard DNS record never shows the console under another name.
- On a public address, Studio's session cookie is
__Host-andSecure; a Studio entry ticket works once; a request that changes something must come from the Studio page itself; a request another site starts is refused, except a plain visit, which lands on the project home without its query.
Fixed
- Studio containers no longer show as unhealthy while they serve: the image's own health check asked an address Studio does not listen on.
- Notices on the Functions, Sign-in, Database, Environments and Updates pages kept their original look; a style added in 0.4.0 had changed it.
[0.5.2] - 2026-10-17
Fixed
- A table an agent created through the MCP endpoint or
/v1answered 404 to the app until PostgREST reloaded its schema some other way. After a migration or a query that may change data, the server now tells PostgREST to reload, as Supabase's database does. Found by an agent's live deploy on the real server.
[0.5.1] - 2026-10-17
Fixed
- An Edge Function's answer could not be read by most clients. The gateway passed the caller's
Accept-Encodingto the Edge runtime, which compressed its answer; the gateway's own fetch then unpacked it but kept thecontent-encoding: gzipheader, sofetchand supabase-js failed on the body. Upstream services are now asked for uncompressed answers; the TLS proxy or CDN compresses for the client. Found by an agent's live deploy on the real server. supabase functions deploy --use-apinow lands the same layout as a console deploy: the CLI'ssupabase/functions/<name>/...andsupabase/functions/_shared/...paths become the function's own files and its shared code.- An access token's name may now hold parentheses, apostrophes and
@ : / +.
[0.5.0] - 2026-10-17
Added
- AI agents can deploy to Sbarbase. A person creates a personal access token (
sbp_...) on the console's new Access tokens page: scoped to everything they can reach, one organization or one project, read only or not, expiring within a year. With it, an agent such as Claude Code, Cursor or Codex connects tohttps://<console>/mcp, which runs Supabase's own MCP server (the official@supabase/mcp-server-supabase). It can create projects, run SQL and migrations, deploy Edge Functions, read keys, logs and security advisors (AI agents). - A Supabase-compatible management API under
/v1, for the same tokens: organizations, projects (each environment is a project, with a 20-letter ref), API keys, SQL, migration history, Edge Functions and their secrets, logs and advisors. - A Claude Code skill (
deploy/agent-skill/sbarbase/SKILL.md) that teaches an agent what "deploy to Sbarbase" means. - Publishable keys can be copied again from the API keys page, as on Supabase, since they are public by design. This applies to keys created from this version; secret keys are still kept only as a hash and shown once.
Changed
- The developer login now keeps migration history in a
supabase_migrationsschema made for it. It still cannot create schemas. Turn database access off and on once for an environment whose access was already on.
Fixed
- The database access guide said
supabase db push --db-urlworks with the developer login. It never did: the CLI'sCREATE SCHEMA IF NOT EXISTSneeds a right the login does not have, on purpose. The guide now says so and showspsqlinstead.
[0.4.0] - 2026-10-17
Added
- Secret API keys, as on Supabase. On an environment's API keys page, or
POST /management/v1/environments/{id}/keyswith{"type":"secret","name":"backend"}, create a namedsb_secret_key. Behind the gateway it acts asservice_rolefor REST, Auth admin, Storage, Realtime and Edge Functions; a signed-in user's token still wins. A request that looks like a browser's is refused with it. Like publishable keys, it is kept only as a hash and shown once; creating and revoking keys is audited (API keys).
[0.3.2] - 2026-10-17
Added
sbarbase reset-password <email>sets a new password for a management account from the server's shell, for an operator who lost it. The new password is asked twice without echo; every console and Studio session of that account ends; its authenticator stays; the change is audited (operator setup).
Fixed
- The capability registry still named version 0.2.0 in 0.3.0 and 0.3.1, so its verifier refused them as a version mismatch.
lab/release.py preparenow writes the version there too.
[0.3.1] - 2026-10-17
Fixed
- 0.3.0 could not be installed through the update channel. Its manifest declared the Auth and Storage schema migrations as data migrations, which puts a release outside the channel entirely. Those migrations are already detected from the pin changes and need the operator's confirmation (
attended); 0.3.1 declares none and carries the same code as 0.3.0. The one-time database restart and the way back limit above four environments are in its notes.
[0.3.0] - 2026-10-17
Moving from 0.2.0 migrates the Auth and Storage schemas of every environment, so the way back is a restore of the backup the upgrade takes first. The first start raises the database's connection limit and restarts it once. Nothing here is claimed production ready.
Added
- Up to eight environments per installation (four before). The runtime raises PostgreSQL's
max_connectionsfrom 100 to 200 on start, the database container may run 320 processes, and the gateway admits 64 requests in flight (8 per environment). A synthetic drill of eight environment databases under full load on the 1 GiB, one CPU database tier peaked at 613 MiB (evidence,lab/capacity-drill.sh). On a 4 core server, eight fit only without Realtime or Edge Functions. Deleting an environment still does not free its place. - Telegram notifications connected from the console. An installation owner pastes the bot token on the new Notifications page; it is checked with Telegram, kept 0600 on the server and never shown again, and the chat is bound by a one-time
/startlink. Admins see the state, only owners change it, every change is audited, and a new chat does not receive the old backlog. The notification drain rereads its configuration, so a change applies without a restart. - Alerts for low disk space and stopped services. Under 10 % or 5 GiB free is a warning, under 5 % or 2 GiB critical; the database, Storage, the management sign-in or an environment's Auth, REST or Realtime container stopped for five minutes is critical. Each is reported at most once an hour.
- An outside uptime check (
deploy/uptime-worker/): a Cloudflare Worker that asks/healthevery five minutes and sends one Telegram message after two failures and one on recovery. - The upstream currency work (plan): upstream drift tracking; an attended move of the database onto another PostgreSQL image (
lab/upgrade.py database); the database volume as state; rebuilding the whole cluster from one backup run (lab/cluster_rebuild.py) and taking it into use (lab/cluster_takeover.py); a per-environment write pause; backup and restore of the management database; starting the database alone for a restore after a failed Storage way back. - Updated upstream services: Auth v2.197.0, PostgREST v14.18, Storage v1.83.3, Realtime v2.144.0, Edge Runtime v1.77.4, Studio 2026.10.12 and postgres-meta v0.100.0. PostgreSQL stays 17.6; 17.11 waits for its rehearsal.
Fixed
- Studio and postgres-meta are no longer reachable from other environments. Neither checks its callers, and both sat on the shared runtime network, where another environment's Edge Functions or the database's
pg_netcould run SQL as the Studio login while a session ran. Each session now has its own network, postgres-meta listens on loopback inside Studio's namespace, and every start checks that nothing answers on the shared network. - Restore on a server installed under umask 002. The runtime state directory was left 775 and every restore refused its journal; each creator of it now makes it 0700.
Verified on the real server
- Upgrades from 0.2.0 through the update command with the new images, confirmed by the health checks.
- A restore of an environment from its local backup, and a restore served only from the encrypted off-site copy in Cloudflare R2; the data came back exactly as backed up.
- Studio confinement probed from a container on the shared network before and after the fix.
Still open
- A cluster takeover whose own replacement never becomes ready has no exit (S4); its fourth part is not accepted.
- A restore onto a new server from the off-site copy, an interrupted restore, and a whole installation under real traffic have not been exercised.
- A way back to an older version refuses jobs once more than four environments exist.
[0.2.0] - 2026-09-26, and changes until 0.3.0
These entries were listed as unreleased; they cover 0.2.0 and what was merged after its tag until 0.3.0.
Added
Current product and portability roadmap, Docker-only verification contract, explicit native-placement identity and scoped evidence ledger. Native activation, complete recovery and release acceptance remain open.
Bounded native-effects verification now preserves raw ACL distinctions and enforces phase deadlines. The latest actual fixture still refuses its routine privilege model; this is verification work, not accepted native-effects operation.
Updated current documentation and repository layout, preserving historical results and distinguishing implemented shared-runtime features from experimental native placement.
Rename, move and delete clients, projects and environments. Owners and admins rename; owners move a project to another client they own, which revokes its API keys, and delete what is empty. An environment is deleted only while nothing runs or changes for it; its keys stop working at once (the gateway answers 401), and its data stays on the server. From the console or the management API.
Restore a backup onto a new installation.
sbarbase relink <runtime> <backup>recreates the backup's client, project and environment with their original ids, never matching by name, andsbarbase restorethen fills it. Members, API keys and the signing key do not travel; users sign in again with their old passwords. Rehearsed onto a second VM on 2026-09-25.Rotate an environment's signing key. Owners and admins press Rotate signing key (or
POST .../signing-key/rotate); the supervisor gives the environment a new JWT secret, recreates its Auth and REST, updates its Storage and Realtime tenants and Edge Functions, and ends every session. Publishable keys keep working. A rotation that stops halfway is finished by the next attempt or start. Also: a restart no longer drops an environment's direct database access from the published state.Move a project from Supabase in one command.
lab/import_project.pyreads a Supabase project (Cloud, self-hosted, or another environment) and copies it into a new, empty environment: thepublicschema with its policies and the source's own grants, users with their password hashes, every row, buckets and files with their owners, and triggers onauth.usersandstorage.objects, then compares counts before reporting success.--dry-runonly inspects.Encrypted off-site backup copies.
lab/offsite.py configuretakes S3 settings (Cloudflare R2, Backblaze B2, AWS S3, MinIO) and a passphrase on stdin; from then on each daily backup is encrypted on the server (AES-256-GCM in chunks) and copied to the bucket, the newest 30 per environment are kept, andfetchbrings one back forbackup.py restore.Direct database access per environment. Owners and admins turn on a developer login and get a PostgreSQL connection string, with the password shown once, for
psql,pg_dump,supabase db push --db-url, Prisma or Drizzle over an SSH tunnel. The login works like a project'spostgresuser within its own database, including triggers onauth.usersand Storage policies, but is not a superuser. A loopback listener (port 6543) passes on only developer logins to their own database.Edge Functions per environment. Deploy a Supabase project's
supabase/functionsfolder with one command (lab/functions-deploy.ts),_sharedandverify_jwtinconfig.tomlincluded, or write a function in the console. Each environment that deploys one gets its own pinned upstream edge-runtime; functions are called through the gateway withsupabase.functions.invoke(), getSUPABASE_URLand the anon and service role keys for their own environment, read secrets set in the console, reach the internet fornpm:imports, and can take keyless webhooks whenverify_jwtis off.Uploads up to 50 MiB, or the limit you set. File uploads to Storage are passed through the gateway and the TLS proxy as they arrive instead of being capped at 1 MiB.
SBARBASE_UPLOAD_LIMIT_MB(50 by default, as on Supabase) sets the limit for the gateway, the proxy and Storage together; a change applies at the next start by recreating the shared Storage container. Other API bodies stay at 1 MiB.Logs and metrics per environment. Every member sees the last hour of requests through the gateway on the environment's page: totals, client and server errors, median and 95th percentile response times, requests per minute and per service, and the memory and processor use of its own services. Owners and admins also read the last requests and the Auth, REST, Storage and Realtime logs, with keys, tokens and passwords redacted before a line leaves the server. Counts live in memory and start again after a restart.
Realtime per environment. Owners and admins turn Realtime on for an environment from the console; it runs the pinned upstream Realtime for that environment alone, with its own login, and stops when turned off. supabase-js broadcast, presence and database changes (every
publictable, filtered by row level security) work through the gateway, which checks the key before any socket reaches Realtime. The login holds administrator rights only while Realtime creates its own schema, first start and after an upgrade.Sign-in settings and OAuth providers per environment. Owners and admins set the site URL, allowed redirect addresses, sign-up and anonymous sign-in, and any of 19 providers (Google, GitHub, Apple and others) from the console. Saving recreates only that environment's Auth, keeping the previous one if the new one does not start. Email links and OAuth steps reach Auth without a key, as on Supabase.
SBARBASE_PUBLIC_URLsets the address links and callbacks use.Browser access. A page on any domain can call Auth, REST and Storage through the gateway, as on Supabase: every answer carries
Access-Control-Allow-Origin: *, a preflight gets 204 without a key, and no credentials mode is offered. The operator login stays same-origin.Upgrades with an automatic way back.
lab/upgrade.py startchecks the new version, pulls its images and backs up every environment, then moves the checkout; the restart replaces only the Auth, REST and Storage containers whose pin or configuration changed. If that start fails, the supervisor moves back to the previous version by itself. A version that changes the PostgreSQL image is refused. CI upgrades PostgREST v14.15 to v14.16, then recovers from a broken version, on a clean machine.Fair share admission at the gateway. Each environment is guaranteed 8 requests in flight; a busy one borrows idle slots up to 24 of 32, while the unused share of every environment active in the last minute (at least 8 slots) stays free. Checked over loopback HTTP (
docs/evidence/fair-share-checks.json), not against Supabase or under sustained load.Saturation notice. After 15 minutes in a row of refusals at its limit, or of crowding out a neighbour, an environment raises one
environment.saturatedoperator notification. Borrowing alone never does.Telegram as a third notification channel beside email and webhook.
Invitations. Owners and admins invite someone by email and role; the console shows a one-time link that lasts 7 days. The invitee chooses a password and joins, or signs in first if they already have an account. Unit tested with a fake management Auth, not yet against the live one.
Member roles in the console. An owner changes a member's role or removes them; access ends at the next request. The last owner stays.
Activity. Owners and admins see what happened in their organization, newest first, in the console (
GET /organizations/{id}/audit). A project moved in from another client shows only what happened since it arrived.A gateway share per environment. The console's environment page shows each environment's guaranteed share. Installation operators raise or lower it and see the installation's allocation; a client organization sees only its own share. The gateway applies a change at the next request, and a raise that would exceed the gateway's 32 is refused.
Supabase Studio per environment. Owners and admins start the original, pinned Studio and postgres-meta for one environment from the console, open it on its own address behind the console login, and stop it. Studio signs in with a per-start login that is not a superuser and exists only while it runs. CI uses tables, SQL, users and buckets through it on a clean machine.
Install with Docker.
docker compose up -d --buildon any Linux host with Docker: the image carries Python 3.14, Bun and the Docker CLI, and starts the pinned Supabase services as sibling containers. CI runs build, first operator, first project through supabase-js, restart and a clean stop on a clean machine.Empty-server rehearsal in a local VM.
lab/vm-rehearsal.shboots a disposable Fedora 44 Cloud VM, installs from a clean clone with the one-command acceptance, creates a first project and reboots. Passed on 4 vCPU and 6 GiB (docs/evidence/vm-empty-server-rehearsal.json). Not a real server.First project check.
lab/first-project-check.ts, and--first-projectondeploy/server-acceptance.sh: login, project, environment, key, supabase-js Auth, REST and Storage through the gateway, and a refused revoked key.SBARBASE_CONSOLE_PORTpins the console's loopback port for a TLS proxy.CI for the unit suites, with a manual empty-host acceptance job. The Python suite also runs against an unreachable Docker endpoint, so a unit test that needs the daemon fails in review.
Import inspection (phase 0).
lab/import_inspect.pyreads a Supabase source read-only and reports what an import would refuse, warn about or leave manual (docs/evidence/import-inspect-checks.json). The import itself does not exist yet.Management API: create an organization (owners and admins of the bootstrap organization), list an organization's members, retry a failed or cancelled environment; the console has matching screens.
The control catalog records its schema version and refuses one written by a newer release.
A recovery export records the owning organization, project and environment.
Documentation restructured into explain, guides, reference and decisions, with hand-drawn diagrams, a quickstart, a server guide,
SECURITY.md, a threat model,CONTRIBUTING.mdand a roadmap.Container generation migration.
lab/migrate-generation.pyreplaces a managed database container on the same data volume under an fsynced intent record, one checkpoint per phase and an explicit--reconcile. Five SIGKILL crash points pass on the disposable fixture (docs/evidence/fresh-worker-generation-crash-all.json). It refuses the retained placement: that run is a deliberate attended step and has not happened, solab/durable-check.tsstays disabled and the two blocked load measurements stay blocked.
Fixed
- Found by the milestone runs in the rehearsal VM on 2026-09-25: redeeming an
invitation answered 500, because the account calls went to
/auth/v1/...on the management Auth service itself, which serves them at its root; the first upgrade after an acceptance was refused as local changes, because the acceptance rewrites tracked evidence (it is now copied aside to.lab/upgrades/); the acceptance waited for the console right after installing the unit, before the images existed, so an empty host always timed out (the unit now starts after the installation); and an image pull gave up after one immediate retry (now four attempts, waiting 15, 45 and 90 s). - A backup counted its rows before
pg_dumpran, so a sign-up in between put a row in the dump that the manifest did not count, and the restore refused the backup. The counts and the dump now read one snapshot. - The loopback listener announced
connection: closeafter refusing a request but kept the connection open; a pooled client reused it and a one-second cleanup cut off the next long request. The sustained arrival run now passes. - Found by the empty-server rehearsal: the acceptance checked the console before building it; block IO limits named a partition, which the kernel rejects, so the database never started on a usual VPS disk layout; body-less POST and DELETE requests got a body over the real listener, so key issuance and revocation answered 400; image pulls ran under a 600 second timeout with no progress; a failed first launch was reported as a retained source to adopt.
- An owner or admin of any organization could read every organization's notifications, including their ids and failure reasons. Each caller now sees only their own organizations' events.
- CI was red on a host whose root filesystem is a partition: a test still expected the partition where the runtime correctly names the whole disk.
- Five admission unit tests called Docker for real and failed without a daemon.
- The server acceptance recorded the unit as active in the same second it started it. After every start it now waits, at most 300 seconds, until the console answers over loopback, and fails with the last thing it saw.
Changed
The control catalog schema is now version 3: the delivery table accepts the Telegram channel. The migration is one way; an older release refuses a catalog this release has opened.
The application gateway lends idle slots instead of refusing every request above 8 per environment; the overload probe keeps measuring the fixed share.
The start requirement is derived from the placement a start runs (1792 MiB of limits on an empty server, plus 512 MiB per environment, plus the 2560 MiB reserve), and the preflight, the unit's
ExecStartPreand the runtime use one computation. It was a fixed 5888 MiB in the preflight and 6 GiB in the runtime.Every GitHub Actions step is pinned to a full commit SHA, with the tag it was resolved from as a comment, so a moved tag cannot change what CI runs.
CPU limits are admitted as ceilings: up to twice the cores after one core for the host. The old rule asked for 8 cores.
A new environment is refused when the next restart could not admit it. On an installation that moved an environment, that check and the preflight now count the current recovery target's containers in one computation; the check used to count the source placement only, and a running target's memory is now counted as in use rather than twice. A restored target that was never cut over is counted by neither, because the next start does not run it.
Project names are unique within an organization and environment names within a project; a clash answers 409, not 500. The API refuses an environment past the installation limit with 409 before queueing it.
Moving a project cancels its queued jobs in the move and points its jobs at the destination, so later events name the new owners.
The two load vehicles no longer overwrite their own evidence on failure or let cleanup replace the real error, and the SDK vehicle probes its fixture first.
NOTICEno longer describes Supabase Studio as served; it is planned.
Notes
- A
LICENSEfile (Apache-2.0) is present in the repository. The 0.1.0 entry below says there is none; that entry is kept as it was released.
[0.1.0] - 2026-09-21
The first tagged milestone. It carries the platform's isolation and accounting work, per environment mail, operator notifications, and the measurement evidence that says what the isolation does and does not do.
Added
- Resource tiers and block IO separation. Every container is launched under a
policy tier with a CPU weight, a block IO weight and per device read and write
bandwidth and IOPS limits. The device is resolved from the host rather than
written down, and a launch refuses rather than starting a container with no
block IO separation.
lab/resource_policy.py,docs/engineering/RESOURCE-POLICY.md. - A counted placement derived from the daemon. Admission reads the containers the daemon attributes to the owner label instead of a hand written name list, so a component nobody told the module about is still counted, and a labelled container outside the recorded placement is a refusal while it runs.
- Continuous host pressure sampling and a level 1 response.
lab/pressure_admission.pytakes a bounded series over a window, summarises it, and refuses new admissions while the most recent reading is at or over a threshold, appending every crossing to a durable ledger. Every decision carries what is not implemented next to the action. - Per environment mail. One 0600 configuration file per environment, an Auth
environment built from it, an explicit reconcile command that recreates one
environment's Auth to apply or remove a configuration, and a non secret state
file.
lab/mail_config.py,lab/mail_state.py,docs/engineering/ENVIRONMENT-EMAIL.md. - Operator notifications. A durable outbox and delivery tables whose rows
commit inside the transaction that records the state change they describe, a
drain inside the worker, an email channel and a signed webhook channel, a
fail-closed credential redaction gate, and eleven event kinds wired at their
durable state changes (installation started, stopped and failed start, worker
restart and restart limit, fence applied and released, export completed and
failed, restore verified and failed).
lab/notify.py,lab/notification_producers.py,docs/engineering/OPERATOR-NOTIFICATIONS.md. - Console surfaces for both. A read only route for an environment's mail state and one for the undelivered notification count, with the mail state on the environment surface and the count in the console shell.
- Disposable probes with negative controls. A mail probe that proves delivery, an unconfigured environment, a dead relay and a wrong credential, and a notification probe that proves delivery, deduplication, the redaction gate and that a broken channel records a failure instead of changing the operation it reports.
- Documentation. Three design records, an adversarial review that states the
unfakeable acceptance criteria, an execution plan, a plan for the deferred
container generation migration, and the measurement evidence under
docs/evidence/. - Per environment upstream Studio presented on the administration path, and the console following the operating system theme by default.
Changed
- Placement arithmetic is derived rather than listed, and the block IO rows are the mechanism that binds, because the weight column was measured not to.
- The unwritten
environment_mailcatalog table was removed in favour of the runtime's own summary file, which is the single source the console reads. - The mail configuration comparison runs in both directions, so a key the desired configuration no longer has cannot survive in a reused container.
Fixed
- Mail: the unchanged path recorded a state the vocabulary does not define, so a second run against an unchanged configuration raised instead of reporting. A source level test now walks every record call site and requires a defined state.
- Mail: a configuration an operator deleted could survive a restart and keep a live SMTP credential, rate limits and relay cooldown in the reused container.
- Notifications: the escalation window was the severity window, so a broken channel would have escalated about 72 times per six hours instead of once, and two broken channels could escalate each other.
- Placement: an owned container outside the recorded placement refused installation startup where the earlier revision admitted, and a labelled container under any other owner escaped the count while the module's own documentation claimed it was counted.
- Placement: every recovery target creation site now carries its tier label, and the guard that claimed every launch site passes a tier is a floor rather than an exact count that a new launch site breaks.
- Notification probe: its default run now exercises the fail-closed redaction gate, which it did not, and its summary comparison no longer re-reads a render time sentence that made one check a coin flip.
- Mail: a dangling symlink is refused instead of silently reading as unconfigured.
Measured, with the evidence committed
docs/evidence/resource-policy-cgroup-mapping.json:--cpu-sharesmaps to the cgroup v2cpu.weightsublinearly (2048 asks for 174, not 800),--blkio-weightdid not bind at all on this host, and--device-write-bpsdid, landing a numericwbpsinio.max.docs/evidence/noisy-neighbor-sql-runs.json: five runs of the neighbour experiment, three preserved. No repeatable effect: the median, the p95 and the maximum stay flat between the baseline and the phase where a second environment saturates the shared engine. One earlier run showed a tail spike that four later runs did not reproduce, and it is recorded as an outlier rather than as the neighbour penalty.docs/evidence/pressure-response-checks.json: a real threshold crossing on a disposable container, the level 1 refusal, one durable ledger line, and a return to admitting after recovery.docs/evidence/auth-templates-source-v2.196.0.json: what a template variable of the pinned Auth version does, read from the tag's own source with a digest per file. A template value is an HTTP URL, never a file.- Probes re-run by the maintainer on the landed tree: mail 36 checks, notifications 21 checks, both cleaning up what they created. Gates: 575 Python tests, 87 Bun tests, UI typecheck, console build check.
Not in this release
Stated here rather than left to be discovered:
- No server rehearsal. The deployment readiness matrix is implemented and proven on a workstation, and it is not a rehearsal against real hardware.
- The container generation migration is deferred, so a retained database
container cannot be recreated. The tier contract therefore applies to placements
created after it, and the retained placement on a development host stays
grandfathered.
docs/engineering/CONTAINER-GENERATION-MIGRATION.mdis the plan. - Two measurements are blocked, not merely unrun: the arrival driven pressure
experiment and the mixed SDK load. Their only fixture generator,
lab/durable-check.ts, is disabled pending that migration, and its fixture on a development host names a retired environment. One load vehicle also overwrites its own evidence when it fails, and its cleanup replaces the real error. - The tier tables are uncalibrated. The block IO rows bind and separate the tiers, and no experiment has measured what a tier receives under load.
- Three notification kinds are emitted by nobody: pressure, runtime refused and headroom changed. Nothing calls the pressure response on a schedule.
- Mail is read only in the console and the operator still chooses an SMTP provider. Templates are the upstream defaults, and an override has to be served from inside the runtime network.
- No licence file.
NOTICEstates the copyright and the upstream attributions, including that Supabase Studio and postgres-meta are used as unmodified pinned images and that the console is sbarbase's own work. Choosing a licence is the maintainer's decision and is not made here.