Experiment 017
Live Supabase Auth, Postgres, Edge Functions, RLS, 10 verified capturesFour Apps, One Door, Zero Funnel — Tracing Outages and Moving Auth Plus Data to Supabase
Can a home-based multi-app stack survive a broken public access path, and what does moving auth and data to a managed platform actually fix?
mimo v2.6 flash via opencode · Supabase Auth, Postgres, Edge Functions · 10 verified captures · 0 VPS bill · 29 Sep 2026
My three apps now log in through Supabase Auth, not the shared backend of EXP 011 anymore. I did not copy auth, I moved it. Old token keys jobTracker.token, crm.token, and content.token are retired, so you log in one more time and then you are done. The old shared auth detail still lives in EXP 011. The :7002 backend stays alive for cv screening only.
Source Code
The source code is public. Prove it yourself.
The backend API and the database moved to Supabase. The repo that got the real update is the jobtracker backend. The other two backend repos stay as they were, almost untouched, and the frontend lives inside my porto repo.
github.com/robbyaliasaakbar/backend-api-services-jobtracker (updated)
github.com/robbyaliasaakbar/contentos-backend-api-service (as is)
github.com/robbyaliasaakbar/mini-leads-service-api--backend (as is)
No secrets inside any README. The updated backend README carries the receipts.
System Requirements
Live view
Modern browser + internet
Live login
Supabase Auth, email code
Live apps
/jobtracker/, /contentOS/, /miniLeads/
Cloud home
Supabase free tier, 500MB, 50k MAU
Data moved
2050 leads, content rows, lamaran rows
Still at home
n8n, evolution, postgres, auth :7002
Test gate
RLS negative 0, E2E green, 0 funnel hits
Model
mimo v2.6 flash via opencode
Four apps live on GitHub Pages, three with cloud login. My home PC now only carries the side paths, n8n, WhatsApp, postgres, and the old auth for cv screening.
Screenshots
Blocked First Then Terminal Green At The End
I captured ten shots in strict order: the broken door first, the locks and the moved data in the middle, green receipts at the end. Terminals, Supabase dashboards, and one live login, all 800 by 500, all checked before upload.
Blocked - Four Countries, Zero Bytes
public-blocked.webp
DNS Back - Door Still Shut
dns-health.webp
Locked - Anonymous Query Denied
rls-deny.webp
Moved - Two Thousand Fifty Rows Clean
seed-2050.webp
Cross User - Other Account Sees Zero
cross-user.webp
Closed - Edge Function Wants JWT
edge401.webp
Code Only - No Link For Scanners
otp-code.webp
Open - Login From The Real Internet
login-page-live.webp
Detached - Zero Funnel Hits
nol-funnel.webp
Retired - Old Servers Stay Down
old-backend-die.webp
Live Demo
Four apps live, three on cloud auth, no VPS bill
All four apps run on GitHub Pages, and three of them log in through Supabase now. Try any app, log in once, and check that the rows you see belong to you alone. My laptop can be off, your login still works.
Caution - the one app that has not moved
cv screening is the only app still on the old path. Its webhook runs through n8n on my home PC behind the Tailscale port, so uploads need my laptop awake, and its login still uses the shared auth on :7002. Everything else on this card works with the laptop off.
Old token keys are retired, so your first login after the move asks for email and code one more time. Free tier limits apply. Side paths like n8n and the old auth for cv screening still wake my home PC, which is expected and written in the status below.
For those of you unable to read this data from technical standpoint, here is the conclusion:
1. My four apps shared one front door, when the door broke nobody from outside could get in
2. I checked the pipes one by one, thirteen problems written down with receipts
3. Then I moved login and data to Supabase, the cloud does the guarding with row locks
4. Old data came along, two thousand fifty leads arrived clean, counts match the old box
5. The three apps now work without my laptop being open, still zero VPS bill
6. Everything that broke is written here, no hidden magic
Experiment Details
Tracing The Broken Door, Then Moving The House
This experiment pairs two stories, the tracing of a broken public path and the move to a managed platform. Here are the five decisions I made to get four apps through both without changing how they behave.
K01 to K08, three way test
I traced eight findings before I changed anything. DNS was dead first, then the funnel door, then I found my own test lying to me because it ran from inside the network. I fixed the method, DoH for names, forced public IP with curl resolve, remote nodes on check host. Every claim on this page passed that three way gate.
I developed this page with the hosted model mimo v2.6 flash via opencode. I led research, architecture, review, and test, while Udin typed code and suggestions.
Supabase auth, database
I weighed three options, keep the home backend, move only auth, or move it all. I chose the full move. Frontend on GitHub Pages now talks straight to Supabase, so the broken funnel door and the laptop dependency leave the critical path. The rate bucket problem retires with the old backend too.
Supabase is a service, yes, but it is open source and self hostable later, free tier covers 500MB database, 50k monthly users, and 2GB egress. No monthly bill, same as my home setup.
RLS, own rows or admin
The tables got row locks before any data landed. The rule translates my old backend exactly, a user reads their own rows, an admin reads all, the stranger reads nothing. I checked the negative path twice, anonymous role came back empty, and a normal account cannot promote itself to admin.
My proof is the SQL editor shots and the policy list. Row locks live in the database, not in my frontend, so even a stolen anon key opens nothing.
RPC and Edge Functions keep the shape
Not everything is a plain table read. CSV export, dashboard counts, ingest with dedup, and two parsing helpers needed real logic. I moved them to database functions and two Edge Functions, and I kept the response shape identical to my old Express routes, so the frontend only swapped the auth layer and the base URL, no screen rewrite.
One behavior did change on purpose. The extract helper used to be open, now it demands a signed token and answers 401 without it. I logged that as a fix, not as an accident.
cutover, verify, rollback path open
No big bang. Each app moved only after its checks passed live, seed counts, filters, export, and row locks. The old backend stayed available as my rollback the whole time, and only after three apps passed I stopped the old containers with restart turned off, so they never wake up on reboot.
Old token keys died with the old backend, so every user logs in one more time. That cost was small and I wrote it down instead of hiding it.
Evidence Log
Standalone Evidence Log: Locks, Moved Data, Zero Funnel
I back every claim on this page with verifiable command line outputs from 29 Sep 2026. I never show passwords or full tokens, raw status and counts remain exact, and the positive control proves the greps really ran.
E1. Names Resolve, Door Still Shut
NOERROR Then 404DNS over HTTPS answers from two providers, then a forced public request still misses the app.
dig @ns1.dnsimple.com aispec.tail06293c.ts.net A
status: NOERROR, answers: 103.84.155.217, 103.84.155.153
curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=aispec.tail06293c.ts.net&type=A'
Status: 0
curl --resolve aispec.tail06293c.ts.net:443:103.84.155.153 https://aispec.tail06293c.ts.net/
HTTP 404 {"error":"Not found"}
Result: names answer again, the app behind the door still does not open.
E2. Row Locks Deny The Stranger
Anon 0 RowsAnonymous role reads nothing, and a normal account cannot raise its own role.
select tablename, policyname from pg_policies where schemaname = 'public' leads own rows or admin profiles own rows or admin set local role anon select count(*) from leads 0 rows, denied update profiles set role = 'admin' where id = auth.uid() 0 rows affected
Result: stranger sees zero, self promotion blocked, same rule as the old backend.
E3. Seed Arrived Clean
2050 / 0Total rows and rows without an owner, then the status spread for comparison with the old box.
select count(*) as total,
count(*) filter (where user_id is null) as tanpa_user
from leads
2050 / 0
select lead_status, count(*) from leads group by 1 order by 2 desc
7 status values, counts match the old box
Result: two thousand fifty rows moved, none without owner, counts match.
E4. Edge Function Wants A Token
401 No TokenThe extract helper used to be open, now it rejects callers without a signed token.
curl -s -X POST 'https://yvnrzfgxsjzjsxkgjcfn.supabase.co/functions/v1/leads-extract' \
-H 'Content-Type: application/json' -d '{"text":"..."}'
HTTP:401
{"code":"UNAUTHORIZED_NO_AUTH_HEADER","message":"Missing authorization header"}
Result: no token, no parse, closed door with a clear reason.
E5. Old Servers Down, Bundle Clean
3 Stopped, 0 HitsThree old backends stopped with restart turned off, and the live bundle greps clean with a positive control.
docker ps -a | grep -E 'infra-backend-api|contentos-backend|minileads-backend' infra-backend-api-1 Exited (0) restart=no backend-server-contentos-backend-1 Exited (137) restart=no backend-server-minileads-backend-1 Exited (137) restart=no grep -oE 'localhost|tail06293c|:7002|:7012' bundle.js | wc -l 0 grep -oE 'supabase|yvnrzfg' bundle.js | wc -l 4
Result: old backends stay down, zero old addresses, positive hits for the new home.
Failure Log
Thirteen Failures Written Honestly
I logged thirteen findings from the outage and the move. Five deep dives below, the rest as an honest list after. Three were found before any user hit them, and every fix carries a receipt.
1. The Public Door Answers Nothing
K02 Infra OUTAGESymptom: Names resolve fine, HTTPS from the internet dies with a broken pipe, every public URL answers nothing.
Diagnosis: The funnel ingress cut the TLS stream, tracked upstream in the vendor issue I opened with support.
Fix: I did not wait for the door. I moved auth and data to Supabase so the apps no longer need that path to work.
Lesson: A working name is not a working door, always test the TLS path itself.
2. The Test That Lied To Me
K03 False Positive HIGHSymptom: My first verdict said public access worked.
Diagnosis: The check ran from inside the same network, and MagicDNS answered like home, so the broken path never got tested.
Fix: Three way method from then on, DoH for names, forced public IP with curl resolve, remote nodes on check host.
Lesson: Never trust a check that lives on the same network as the service, and treat every green result as a hypothesis.
3. One Rate Bucket For The Whole World
K04 Auth GLOBALSymptom: Five logins per minute, then everyone on earth gets locked out together.
Diagnosis: The backend counted the proxy address, so all visitors shared one bucket.
Fix: For the three moved apps the problem retired with the old backend, the local fix got cancelled as no longer needed.
Lesson: Rate limits must count real clients, not the pipe they arrive through.
4. The Reset Link That Was Ignored
K09 Auth PIVOTSymptom: The new auth service ignored my redirect after password reset, the flow landed on its own page.
Diagnosis: The platform default does not honor my old redirect the way my PHP did.
Fix: I pivoted to plain OTP codes, the user copies the code instead of clicking through.
Lesson: Study the defaults of the platform you move into before you promise the old behavior.
5. The Link And The Code Together
K13 Email PREFETCHSymptom: The code died before the user touched it, first use said already used.
Diagnosis: Email scanners clicked the link inside my message, the single use code burned itself on the scan.
Fix: The template body now carries the bare code only, no link at all.
Lesson: Assume every email gets scanned by a machine before a human reads it.
K01 DNS never provisioned at first, now resolved, names answer again from two providers.
K05 Three index files sat in dev mode, restored to the built version before push.
K06 The cv screening webhook still depends on my laptop, decision still open, written here not hidden.
K07 The funnel accepted odd ports without a warning, moved to standard caps so silence cannot lie again.
K08 Old Node backend answered any origin with a wildcard, logged as low risk, not touched yet.
K10 A cached bundle made a live test fail for fake reasons, fixed by reading the body length instead of trusting the label.
K11 A reset file in the temp folder went stale after the user reset, temp path rethink needed.
K12 The custom mail service locked my email template, unlocked after an app password was set.
Recap: thirteen on record, three found before any user hit them, every entry here has a receipt or a reason.
FAQ
Frequently Asked Questions
Is this a new login? ▼
Yes, one more time. The three apps now use Supabase Auth, old token keys are retired, you log in once and you are done.
Did my old data move? ▼
Yes. Two thousand fifty leads and the app rows came across, counts match, old boxes kept until checks passed.
Why Supabase instead of a VPS? ▼
Free tier covers it, row locks come built in, and the stack is open source so I can self host later. No monthly bill.
Why row locks from minute one? ▼
The stranger sees nothing, a user sees their own rows, an admin sees all. Same rule as my old backend.
Why cut over one app at a time? ▼
Each app had to pass live checks before the next one moved. My rollback path stayed open the whole time.
Why write down every failure? ▼
Thirteen on record so far. The receipts are the proof, and the lessons only count if they are written.
Live Status
Live Status - Cloud Auth, Home PC On The Side
What works now
- Three apps live on GitHub Pages with Supabase login
- Row locks on, anonymous reads nothing, other accounts read nothing
- Seed verified, 2050 rows, zero without owner
- Edge functions answer 401 without a token
- Live bundles show zero old funnel addresses
What is not production yet
- cv screening still on the old auth :7002, that decision is open
- Funnel leftovers still answer on the side, the outage is not closed upstream
- Home PC still runs n8n, WhatsApp, postgres, and the old auth
- Free tier limits, hardening is a separate project
Three apps live with cloud auth and row locks. My home PC now only carries the side paths, n8n, WhatsApp, postgres, and the old auth for cv screening. The outage shots are stamped 29 Sep 2026 as it happened, a later check the same day already answered 200 on the jobtracker path, so every blocked claim on this page carries its date. No passwords, tokens, or OTP codes ever shown.
Disclaimer
Disclaimer - Cloud Auth, Home Side Paths, Not Production-Hardened
Built with mimo v2.6 flash via opencode. Snapshots taken 29 Sep 2026. Passwords, tokens, and OTP codes are never shown. Row locks and free tier limits stand as written.
Shared auth old story: EXP 011 (:7002) · v1 jobtracker: EXP 010 (vanilla) · Previous: EXP 016 (v2 enterprise) · This: EXP 017 (Supabase move) · Machine: EXP 001