Skip to main content
There is one production: a hosted Supabase project. Every other copy of the engine is a local stack a developer runs on their own machine. No key is ever written in a page, a repo, a seed or a migration; this page says where each group lives, never what it is.

Environments

The backend repo holds the migrations, the functions and the local stack’s configuration.

Where the secrets live

The secrets vault is one Infisical project. The backend’s secrets sit in three of its folders, one per consumer. A secret is set in the vault first, then copied to the place its consumer reads from. The three folders’ exact names are on Names. The vault link file in the backend repo points at the production environment and the functions folder, so a developer’s export lands the right group in the local functions’ environment.

Rotating a secret

A secret is one value set in two places, never typed or pasted. The backend README holds the one command that generates the value, sets it in the vault and sets it in the production project in the same line, then deletes the temporary file. A value that exists in only one of the two places is a broken rotation: the function reads the old value until the second set lands. The QuickBooks connection is the exception to “one value”: it is an app the owner authorises once, and its refresh token rotates itself and is kept in the engine’s own token table; see QuickBooks.

Who may call a function

Every function has the platform’s own token check switched off and checks authorization in its own code. A webhook receiver checks the vendor’s signature over the raw body; a route the app calls checks the signed-in user; a route a scheduled job calls checks the job’s shared secret. This is a rule of the repo, held by review, and the local stack’s configuration says so beside every function.

How code and schema reach production

Nothing reaches production from a laptop. A merge to the main branch runs the gate; a green gate deploys. The deploy workflow has two jobs, and their order is the safety. The order guarantees nothing the other way. Between a migration landing and the function deploy finishing, the previously deployed code runs against the new schema. A migration that drops or renames something the live code still reads takes production down for that window. The gate refuses such a migration; the fix is two steps in two merges: stop reading the column first, drop it next. A manual run of the deploy workflow exists for a re-deploy, or for one half on its own. Direct SQL against production is allowed for operational work and is never a schema change: a schema change is always a migration.