Moltbook exposed 1.5M agent API keys: what limits damage
Wiz reported on 2 February 2026 that a misconfigured Supabase database at Moltbook exposed about 1.5 million agent API authentication tokens, each a complete login for an agent account. Some agent messages also held plaintext OpenAI keys. What limits that kind of damage is what the platform stores per agent, whether the agent holds the provider key at all, and a cap on that key. None of them fixes a writable database.
Wiz found four kinds of exposure in one database
Wiz's write-up, dated 2 February 2026, describes a security review of Moltbook, a social platform for AI agents, that began by "browsing like normal users". Within minutes the researchers found a Supabase API key in the site's client-side JavaScript. With it, they report, the database answered "exactly as if we were an administrator".
| Data | What Wiz says it held | What it allowed |
|---|---|---|
agents table | An api_key, a claim_token and a verification_code per agent | "fully impersonate any agent on the platform" |
owners table | Email addresses and X handles of 17,000+ users | Exposure of the email addresses, which Wiz says were meant to stay private |
agent_messages table | 4,060 private conversations between agents | Included "plaintext OpenAI API keys shared between agents" |
posts and other public tables | Site content | Write access: Wiz modified an existing post |
The timeline in the post: initial contact on 31 January 2026 at 21:48 UTC, the Row Level Security misconfiguration reported at 22:06 UTC, and the final fix at 01:00 UTC on 1 February 2026. Wiz notes that each round of fixes surfaced more exposed tables, from sensitive tables to write access to GraphQL-discovered resources, so the work took several passes.
The post reports counts of about 1.5 million tokens and 35,000 emails, plus 29,631 more addresses in a separate observers table. It also reports that all data accessed during the research and fix verification was deleted.
The key was public by design; the policy behind it was missing
A Supabase publishable key in a web page is normal. Wiz says so itself: "When properly configured with Row Level Security (RLS), the public API key is safe to expose - it acts like a project identifier." The next sentence is the incident: "However, without RLS policies, this key grants full database access to anyone who has it."
Supabase's documentation states the rule in one line: "A table in an exposed schema without RLS is readable and writable by any role with a grant on it." It also gives the expected behaviour once RLS is on: "Once RLS is enabled, no data is accessible through the API when using a publishable key, until you create policies."
The test Wiz ran is therefore one request. Its shape, with placeholders:
curl "https://<project>.supabase.co/rest/v1/<table>?select=*&limit=3" \
-H "apikey: <publishable key found in the page's JavaScript>"
With RLS working, that request returns an empty array or an authorization error. Wiz describes exactly that expectation before reporting what came back. Writes need their own check. Wiz reports that after the initial fix blocked read access to sensitive tables, "write access to public tables remained open", so read and write policies are separate work.
A stored token turns a table read into a login
The agents table held each agent's api_key, which Wiz describes as a "Full authentication token allowing complete account takeover". The table held the token itself, so reading the row was the same as owning the account. This is the property of a bearer token that the stolen-key entry covers: whoever holds it can use it.
What the verifier keeps per agent decides what a read of that table is worth. RFC 9421 is explicit about the difference between shared secrets and key pairs:
"By their nature, symmetric cryptographic methods require the same key material to be known by both the signer and verifier. This effectively means that a verifier is capable of generating a valid signature, since they have access to the same key material. An attacker that is able to compromise a verifier would be able to then impersonate a signer." (section 7.3.3)
| The verifier stores | A read of that table gives an attacker |
|---|---|
The token itself (Moltbook's api_key, per Wiz) | A working login for every row |
| A shared HMAC secret | The ability to sign as the agent (RFC 9421 section 7.3.3) |
| A public key | The key signatures are checked against; no ability to sign |
Section 7.3.2 of the same RFC adds that "the use of asymmetric signing algorithms exposes key material less than the use of symmetric signing algorithms". How an agent signs each request is the subject of RFC 9421 for AI agents.
The third row has its own limit. If the table of registered public keys is writable the way Wiz found the posts table to be, an attacker replaces a key and signs as the agent. Wiz demonstrated an edit on a post, not on a key table, so this is a conditional, not a finding. It still means the integrity of that table matters as much as its secrecy.
An agent that holds a provider key can leak it in a message
The agent_messages table is the second lesson. Wiz writes that "some contained third-party API credentials, including plaintext OpenAI API keys shared between agents", and, in its lessons: "Users shared OpenAI API keys and other credentials in direct messages under the assumption of privacy, but a configuration issue made those messages publicly accessible."
A leak in one platform became a credential leak for an unrelated service. OpenAI documents three practices for the key itself. Its production best practices, read on 11 October 2026, say:
- "Avoid exposing the API keys in your code or in public repositories; instead, store them in a secure location."
- "We strongly recommend setting an expiration date when you create a project API key and establishing a regular key rotation process."
- "To enforce a monthly cap, set a hard spend limit. Hard spend limits stop affected API traffic when tracked spend reaches the limit".
An expiry bounds how long a copied key works. A hard limit bounds what it can spend inside that window. OpenAI's cap is monthly; a daily window has to come from a layer in front, as that entry sets out per provider.
The Wiz post does not say whether the exposed OpenAI keys had spend limits or were rotated, so what the leak cost those key owners is not in the record.
Write access and fake agents sit outside any key scheme
Wiz ranks write access above data exposure: "Write Access Introduces Far Greater Risk Than Data Exposure Alone". Its reasoning is "content manipulation, narrative control, and prompt injection that can propagate downstream to other AI agents". A signed request, a short token lifetime and a spend cap all concern who is calling and how much they spend. None of them says whether the content an agent reads is the content someone wrote.
The ratio in the post is a separate point. Wiz reports 1.5 million registered agents against about 17,000 human owners, an 88:1 ratio, and writes that "Anyone could register millions of agents with a simple loop and no rate limiting". It also says "The platform had no mechanism to verify whether an 'agent' was actually AI or just a human with a script."
A key proves possession of a key. It does not prove an LLM is behind it. A platform that wants to count agents has to decide what it is counting. Wiz says the number "can be easily inflated without guardrails like rate limits or identity verification".
Where Mandare fits, and what it does not do
Mandare is a local door in front of an agent's LLM calls, not a platform and not a database. As of v0.1.0 (npm view @mandarelabs/cli version on 11 October 2026), checked against commit 327442e of the public repository, it would not have changed Moltbook's RLS misconfiguration, and it does not protect a third party's tables. What it changes is how a reader's own agents hold credentials:
- Provider keys stay out of the agent's reach. The self-host guide says to "put provider keys in the vault, not in env files an agent process can read" and that "nothing agent-reachable holds a raw key".
mandare vault import-envreads keys from.envinto the encrypted vault, the guide says to delete them from.envafterwards, and the door adds them to outbound requests. With that setup the agent's process has no raw provider key to paste into a message. - Passport mode verifies with a public key. The door checks the delegation credential offline against a configured authority, then checks the RFC 9421 signature with the key carried in that credential (
packages/gateway/src/passport-auth.ts). The door needs no per-agent secret to do it, which is the third row of the table above. - Token mode is the symmetric case. Its proof is an HMAC under a per-token secret, and the vault stores each secret sealed under its master key (
packages/vault/src/tokens.ts). A vault file without the master key yields no proofs, and a vault file with it yields proofs for live tokens. This is the section 7.3.3 situation, and the threat model says passport mode is the one to use off loopback. - A mandate caps spend at the door. For the calls it proxies (Anthropic
POST /v1/messages, OpenAI or OpenRouterPOST /v1/chat/completions) the cap holds given a correct price table, and a model without a price is refused.
Limits next to those claims:
- The agent still holds its own Ed25519 signing key, as the concepts page says. If that key leaks, a thief can sign until the credential expires or
mandare killis run, which is local and offline. - A passport binds a key to an owner by delegation credential. Owner KYC is a mock provider in this release, so we make no claim that an owner is verified, and the door cannot tell a model from a script holding the key.
- Content integrity and prompt injection are not addressed by identity or caps.
- Two AI-assisted review passes have been run and an external audit is still pending.
Questions
What did the Moltbook database exposure include?
Wiz's write-up of 2 February 2026 lists about 1.5 million API authentication tokens, 35,000 email addresses and private messages between agents, with read and write access. Some messages held plaintext OpenAI API keys. Wiz reported the issue on 31 January 2026 and the final fix landed at 01:00 UTC on 1 February 2026.
Is a Supabase publishable key safe to put in client-side code?
Yes, when Row Level Security is enabled on every table in an exposed schema and policies grant just what each role should reach. Supabase's documentation says "A table in an exposed schema without RLS is readable and writable by any role with a grant on it." Without RLS the key in the page is a full-access credential, which is the situation Wiz describes.
Does storing a public key instead of an API key stop impersonation after a database leak?
It removes the stolen credential: a public key cannot sign. RFC 9421 section 7.3.3 says that with symmetric methods "a verifier is capable of generating a valid signature". It does not protect the key table from writes, because an attacker who can replace a registered key can register their own.
Would a spend limit have helped with the leaked OpenAI keys?
A hard spend limit caps what a thief can spend on a key, not what they can read. OpenAI documents that hard spend limits "stop affected API traffic when tracked spend reaches the limit". Wiz does not say whether the exposed keys had limits or were rotated.
Sources
- Hacking Moltbook: AI Social Network Reveals 1.5M API Keys · Wiz · 2026-02-02
- Row Level Security · Supabase
- RFC 9421: HTTP Message Signatures · IETF / RFC Editor · 2024-02-01
- Production best practices (OpenAI API documentation) · OpenAI
Run it yourself: 3 commands, no API keys.
github.com/mandarelabs/mandare →