How a platform with AI inside is built
· 5 min read · piattaforme, supabase, rls, ai
What the lab means by a tailored platform, and the common base it starts from: access, notifications, telemetry, AI spending ceilings, row-level security, deploy. With the failures that changed it.
From 14:53 on 21 August 2026, for five days, the shared backend’s telemetry table, site_events, recorded no events at all. The pages worked. The database held two rs_track functions with the same arguments in a different order. To Postgres those are two separate functions, and a create or replace with swapped parameters adds a second one instead of replacing the first. PostgREST answered HTTP 300, unable to choose.
The client never noticed. When a call fails, supabase.rpc returns an object carrying the error and throws nothing. The empty catch, written so that telemetry would never disturb the page, would have seen nothing anyway. The dashboard showed zero visits, a believable number.
The migration of 26 August drops the old signature. The client now rethrows the error and reports it once in the console. And in the ws-uptime.yml workflow a probe sends rs_track an empty list of events every half hour, which writes nothing, and expects a 204.
What tailored means
Telemetry is one piece of the common base the lab’s platforms start from. For the lab, a tailored platform is cloud software shaped around the work of the people who use it. AI sits inside the product, with spending ceilings, declared fallbacks and a person on the decisions that matter. One installation serves several organisations, with their data separated row by row. And it is built and maintained with the lab’s method, Ratchet Agentic Engineering.
Two places show this today. Seantral, online at seantral.com, is the public demonstrator of the method: an app about the sea, with a backend of its own. And there is a platform for care facilities, in closed trial, with invented data.
The common base
A new platform starts from a base that already exists. In Cockpit by RiftSeed, the tool where the lab applies the method, a new project is created from the templates/riftseed-base template. It holds Vite, React and TypeScript, a Supabase client that reads its address and key from the environment, and sign-in by email link. It also holds an example table with its access rules, a ratchet test with two example ceilings, a Playwright probe at 390 and 1440 pixels wide, and the CI and deploy workflows. The template’s deploy starts only from a person’s approval in Cockpit, never from a push.
The lab’s apps share the rest. Accounts live on Supabase Auth, for anonymous and registered users, and a database function checks the admin role. A notification is first of all a row in the notifications table, which acts as the inbox; a trigger calls the send-notification function, which sends an encrypted push to each of the recipient’s devices that accepts one and prunes dead endpoints.
Telemetry is in-house, with no third-party tools: a random session id for each browser tab, the country read from a Cloudflare header, no IP address stored, ninety days of retention. The frontend goes to Cloudflare Pages from CI, after the gate.
AI inside the product
Each of the site’s AI features has a model and a fallback chain in the ai_tool_config table. The brief generator tries the models in order. If none returns a usable result, it builds a stand-in answer marked _fallback: true, and the interface treats it as one. The fallback is written into the data.
Ceilings are checked before the model is called. Seantral’s assistant counts one person’s messages over the last minute and the last twenty-four hours, and everyone’s over the last twenty-four hours; past a limit, it replies that the limit has been reached and no model is called. Cockpit reserves the spend before the call. Under a per-owner lock it adds up the day’s spend, the open reservations and the new one, and admits the call only if the total stays under the per-brief and per-day ceilings. Afterwards it settles the reservation with the cost OpenRouter reports.
A person stays on the decisions that matter. In the brief generator the AI proposes requirements and the person writing the brief confirms them. The final brief cites only confirmed requirements: every id the model cites is checked against the ids sent in the request, and orphans are dropped. In Cockpit a ceiling only goes up with a person’s decision.
Data separated row by row
Every table created in this repository’s migrations has row-level security switched on. When one installation serves several organisations, every row carries the organisation it belongs to. In the platform for care facilities, every row has its facility, and every policy asks the database what role the signed-in person holds in that facility. Someone working in one facility cannot read another facility’s rows.
The rules are checked without a database. In the template, tests/rls.test.ts reads the migrations and fails if a table lacks RLS, if a policy targets the anonymous role, or if a comparison with auth.uid() sits outside coalesce(..., false).
That last rule dates from 6 July 2026. In a prototype for event invitations, an adversarial run of nineteen checks found that auth.uid() = host_id, with nobody signed in, evaluates to NULL, and inside an authorisation check that NULL let the call through. The same static check has since gone into Cockpit’s access tests, into those of the platform for care facilities, and into the template every new project starts from.
Written the way everything in this lab is built: with AI, then edited and approved by a person.