Anatomy of the Moltbook hack — 1.5 million API keys in 72 hours
Moltbook leaked 1.5 million API keys, 35,000 emails, and 4,060 private messages in 72 hours. Wiz's disclosure showed the root cause: a single Supabase table without row-level security. Here is the timeline, the exact bug, and the ten-minute hardening walkthrough for your own app.
Wiz disclosed the Moltbook breach on January 28, 2026. 1.5 million API keys, 35,000 email addresses, and 4,060 private messages reachable via an unauthenticated HTTP request. In this post I will walk through the exact bug Wiz described, reproduce the bypass at a code level, and give you the ten-minute Supabase hardening drill we run internally.
The one-line bug
Moltbook shipped a Supabase table named roughly agent_credentials with RLS disabled. That single change is enough to make every row reachable by anyone who knows the project URL and the public anon key — both of which ship in the client bundle by design.
Why it shipped
Two compounding mistakes:
1. RLS was never enabled on that table. In new Supabase projects, RLS is opt-in per table — you must explicitly run enable row level security on each one. An AI coding assistant will not reliably prompt you to do this.
2. The table stored cross-agent shared credentials. Even with RLS, a policy like USING (auth.uid() = owner) only scopes by user. If the application was designed to let agents share credentials via a shared field, the scoping was never strict enough.
Ten-minute Supabase hardening drill
Run these four steps, in order, right now on your own app:
Step 1 — Enable RLS on every table.
select 'alter table ' || quote_ident(schemaname) || '.' || quote_ident(tablename)
|| ' enable row level security;' as command
from pg_tables
where schemaname = 'public'
and not rowsecurity;That returns the exact alter table commands to run for every table missing RLS.
Step 2 — Add a default-deny policy to every enabled table.
create policy deny_all on public.<table>
for all using (false) with check (false);Then layer explicit allow policies on top. Default-deny is the single biggest leap in Supabase security posture; almost no vibe-coded app does this.
Step 3 — Scope by both user and tenant in every policy.
create policy users_read_own_tenant
on public.orders for select
using (auth.uid() = user_id
and tenant_id = (auth.jwt() ->> 'tenant')::uuid);Step 4 — Rotate anything that looks like a service-role key. If your git history has ever contained a string starting with eyJhbG in a .env file, rotate it now. Use the free leak scanner to check.
What Moltbook did not have that would have helped
A pre-deploy gate running on every commit. The agent_credentials table existed in the migration history — any automated scanner that understood RLS would have flagged it before it shipped. That is exactly what Securie does: install the GitHub App and the RLS specialist runs on every pull request.
Related reading
- Supabase RLS misconfiguration guide — detect, exploit, fix
- Free Supabase RLS scanner — paste your project URL, get a report
- Wiz's original technical write-up: wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys