Skip to content

fix(db): our tables are not in orangecat's schema — tell every client - #138

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/own-db-schema
Aug 27, 2026
Merged

fix(db): our tables are not in orangecat's schema — tell every client#138
github-actions[bot] merged 1 commit into
mainfrom
fix/own-db-schema

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

The 503

/api/health has returned 503 because public.consultations does not exist. It never did.

Botsmann shares one self-hosted Supabase database with orangecat, which owns public and its 128 tables. Ours were nowhere — all eleven migrations were unapplied, and no deploy ever noticed.

Half of the fix already shipped

fleetcrown#398 taught the deploy pipeline that a second app on the shared database needs its own schema. Botsmann now declares supabase:botsmann, and its migrations apply there — exactly as printcraft's do. That has already run: all 11 tags are recorded in botsmann._deploy_schema_history and the 12 tables exist.

This is the other half

A Supabase client that is not told the schema queries public, so it would still get PGRST205. Measured against production:

GET /rest/v1/consultations                             -> 404 PGRST205
GET /rest/v1/consultations  Accept-Profile: botsmann   -> 200 []

db: { schema } is what sends that header — and it was missing from all four client factories: anon, service-role, browser, and route-handler.

The schema name is one constant in lib/constants.ts, not four literals. The second copy is how the first goes stale, and this repo has just spent a day proving what duplicated configuration costs.

Also removed

scripts/run-migration.ts and scripts/migrate-via-api.ts. They applied SQL through the service-role client with no schema set — from the era when migrations were run by hand. Now they would create our tables inside orangecat's public. Schema is the pipeline's job; a second way to do it is a second way to do it wrong. Nothing references them.

The bare SupabaseClient type. It hardcodes 'public', so annotating with it silently asserts we query orangecat's tables — and rejects the client we actually build. The type is now derived from the factory, which keeps the two in step and survives supabase-js reordering its generics.

Test

Pins it per file, by count: every createClient call in a client file must set the schema, from the shared constant, with comments stripped first so the guard cannot be fooled by prose. Adding a fifth unscoped factory fails the test instead of production.

Mutation-checked — unscoping the browser client fails it.

verify green: 237 tests, lint, typecheck, build.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HVwg8DKHQktxJuHeLM3xpG

/api/health has returned 503 because `public.consultations` does not exist. It
never did. Botsmann shares one self-hosted Supabase database with orangecat,
which owns `public` and its 128 tables; ours were nowhere, because all eleven
migrations were unapplied.

The schema half is fixed in the deploy pipeline (fleetcrown#398): botsmann now
declares `supabase:botsmann` and its migrations apply into a schema of its own,
exactly as printcraft's do. That has run — all 11 tags are recorded and the 12
tables exist.

This is the other half. A Supabase client that is not told the schema queries
`public`, so it would still get PGRST205. Measured against production just now:

  GET /rest/v1/consultations                      -> 404 PGRST205
  GET /rest/v1/consultations  Accept-Profile: botsmann -> 200 []

`db: { schema }` is what sends that header, and it was missing from all four
client factories — anon, service-role, browser and route-handler.

The schema name is one constant in lib/constants.ts, not four literals. The
second copy is how the first goes stale, and this repo has just spent a day
proving what duplicated configuration costs.

Two things this also removes:

  * `scripts/run-migration.ts` and `scripts/migrate-via-api.ts` applied SQL
    through the service-role client with no schema set. They are from the era
    when migrations were run by hand; now they would create our tables inside
    orangecat's `public`. Schema is the pipeline's job, and a second way to do
    it is a second way to do it wrong. Nothing references them.
  * The bare `SupabaseClient` type hardcodes `'public'`, so annotating with it
    silently asserts we query orangecat's tables — and rejects the client we
    actually build. The type is now derived from the factory, which keeps the
    two in step and survives supabase-js reordering its generics.

New test pins it per file, by count: every `createClient` call in a client file
must set the schema, from the shared constant, with comments stripped first so
the guard cannot be fooled by prose. Adding a fifth unscoped factory fails the
test instead of production. Mutation-checked: unscoping the browser client
fails it.

verify green: 237 tests, lint, typecheck, build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVwg8DKHQktxJuHeLM3xpG
@github-actions
github-actions Bot merged commit 3c2b229 into main Aug 27, 2026
1 check passed
@github-actions
github-actions Bot deleted the fix/own-db-schema branch August 27, 2026 19:57
github-actions Bot pushed a commit that referenced this pull request Aug 28, 2026
…143)

The schema fix landed in #138, but the repo still told you to operate a
Supabase we do not have. docs/SUPABASE_SETUP.md opened with "Completed Setup"
for a hosted project jkjmhtirxwhljpkcfxqe.supabase.co and said to apply
migrations by pasting SQL into a supabase.com dashboard. Two unreferenced
scripts printed the same instruction, one handing out a psql string against
pooler.supabase.com, and the CLI's .temp cache with the old project ref was
committed — twice, once nested under supabase/migrations/.

That is not stale clutter, it is the outage. A wrong pointer to a real-looking
place is worse than no pointer: no pointer makes someone ask, a plausible one
makes them assume it was handled. Nobody pasted anything, the eleven migrations
were never applied, and /api/health served PGRST205 for months while every
deploy went green.

So the doc now describes what is actually true — self-hosted on bitbaum at
supabase.orangecat.ch, our tables in the `botsmann` schema because one database
backs several apps and `public` is orangecat's, and migrations applied
automatically on deploy by fleetcrown's apply-schema.sh against the
botsmann._deploy_schema_history ledger. Nothing is pasted anywhere. 280 lines
became 100.

Fixing today's copies only clears today's copies, so check:selfhost joins
verify: it fails on supabase.com/dashboard, app.supabase.com, pooler URLs and
real project refs, allows supabase.com/docs because the product documentation
is still correct for us, and exempts docs/archive/ so history can stay honest.
Proven to fail before shipping — it catches an injected dashboard link and a
project ref, and leaves the your-project.supabase.co placeholder alone.


Claude-Session: https://claude.ai/code/session_018waGt1ieA9TjpscqrbrnGb

Co-authored-by: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant