Security and safety
What the system already guards, so a builder doesn't have to. Every rule here is enforced on the server; the app, HQ, the AI, an MCP client and your own code all hit the same checks. Tests: tests/launch-review.test.ts, tests/capabilities.test.ts and each engine's test file.
The ground rules #
| Rule | What it means for you |
|---|---|
| Nothing people make runs as code on the server | Blocks, kits, styles, games and bots are data. Custom logic is a mini-app in a sandbox. |
| The AI proposes, a person approves | Neither the in-app AI nor an MCP client changes a space on its own (unless a person gives a token approve). Every change is a version that can be undone. |
| Every change is checked | checkBlueprint refuses a broken or unsafe blueprint with a readable message (Blueprint). Replacing the whole blueprint in one op is refused. |
| One permission check | Every function goes through getAccess / mustAccess and checks a verb, never a role name (People). |
| Keys stay on the server | No query returns a key; devices, the AI, bots and mini-apps see key names only. |
Hidden means hidden, data too #
visibleTo on a page or block (roles, level:<id>, level:<id>+, badge:<id>) does two things:
- The layout:
blueprintForremoves hidden pages and blocks before the blueprint is sent, so they never reach the device. - The data:
hiddenFrom(convex/lib/blueprint.ts) works out which collections, streams, feeds and mini-apps a person can't reach because every block that shows them is hidden from them. Records, posts and apps behind a staff-only page can't be read or written through the API either. A collection, stream or app that no block points to stays reachable to anyone with the verb, so put private data behind a hidden block, or give the role noview.
HQ blocks (hq.*) are only for people who manage the space. Managers see everything.
Roles and invites #
ownerkeeps every permission;visitorcan nevermanage,inviteoredit.- Only managers hand out a role that can
manageorinvite; someone who can only invite can't accept a join request with a managing role. - Invite codes are 10 letters, can be used up (
maxUses) or revoked. - Changing someone's role changes what their MCP tokens can do at once.
Kids spaces #
audience: "kids" (Kids space kit):
- The space must stay private and invite-only and is out of Explore; only the owner can open it up again.
- A kid is a member of a kids space whose role there can't invite or manage. Kids only message, friend or ring people who share one of their kids spaces; strangers can't reach them, even from another open space the kid is in, and kids can't walk into other open spaces.
- Kids spaces can't have MCP tokens that approve changes. The AI is told to keep every word, page and mini-app kid-safe and to give kids only
view,postandplay.
The AI #
- It only acts through its tools (AI); its changes are proposals. It never approves, never writes records, can't remove the HQ page, and only reads through connections (
GET, read-only MCP tools). - What comes back from the web or a connection is marked as outside data, so a page or a tool result can't make it send anything out.
- Each space runs the AI on its own Claude key (an
aikey in the vault). Per-person, bot, burst (6 a minute) and token limits, and the space's own/ai/limits, are in AI limits. Messages are capped at 8,000 characters.
MCP tokens #
mbk_… tokens (docs/CAPABILITIES.md): one person in one space; can do what both its scopes and the person's current role allow; only a hash is stored and it's shown once; 90 days by default (1 to 365); 120 calls a minute; batches of at most 20; at most 20 live tokens per space. URL tokens (for clients that only take a URL) can only read and propose, because URLs end up in logs. Revoking stops a token at once.
The key vault #
AES-256-GCM under the platform's vault key, tied to the space and the key's name. Each key has uses (ai, connections) and hosts (api.example.com, *.example.com; not shared suffixes such as *.co.uk or *.vercel.app). A key is refused for any other host. Changes can only narrow a key; widening needs it pasted again. Keys are scrubbed from results and errors. Only managers add, change or remove keys.
Calls to the outside #
Every outside call (connections, web fetch, outside feeds) leaves through one Node action, convex/capabilities/egress.ts, using the rules in convex/lib/capabilities/net.ts:
- Public HTTPS on port 443 only. The host is resolved and every address must be public: no loopback, private, link-local or cloud metadata, carrier-grade NAT, NAT64, 6to4, Teredo, IPv4-compatible, documentation or reserved ranges.
- The request is pinned to the checked address, so a name can't switch to an inside address between the check and the call. No redirects; 2 MB and 30 s caps; results trimmed.
- Header values in the blueprint can't look like keys (
Authorization,X-API-Key…); those go in the vault.httpconnections stay under theirurland onlyGETunlessmethodslists more;mcpconnections only call read-only tools unlesstoolslists more. - For more depth, also firewall the backend's outbound traffic away from your private network.
Mini-apps #
A mini-app is HTML in a sandbox (WebView on phones, iframe on the web) with default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; media-src data: blob:; font-src data: (no https: images, so typed text can't leak out in an image address). It can't fetch, open sockets, load scripts or read the app, cookies or keys. It reaches the space only through the bridge: each call must be in the app's needs, allowed by the viewer's role, allowed by the person for anything on the phone, and within the rate limit (bursts of 60, 30 a second), and the server checks again from the saved blueprint. Published apps are reviewed by an admin, use only the adding space's own connections, and kids spaces only get apps rated everyone. It gets the style as CSS variables and is paused when it's off screen (Mini-apps).
Limits against flooding and cheating #
| Where | Limit |
|---|---|
| Chats and streams | 4,000 characters a message; slow mode per stream (moderators exempt); groups capped by talk.groups.max; a later send at least a minute ahead |
| Feeds | 60 posts or reactions per person per 10 minutes; outside sources pulled at most once a minute |
| Records | 40 fields per collection; every field checked and cleaned on add; lists 500 at most |
| Ledger | Whole steps only; balances never go below zero; every move is an entry that's never edited; issuing needs the unit's issue verb |
| Games | Randomness, timers, scores and secrets decided on the server; the big screen never shows secret words, hidden answers or gift names; 200 players; 8 wrong join codes in 15 minutes, then a pause |
| Calls and rooms | 3 calls a minute and 20 an hour per person; 16 people a call; room passes last 10 minutes; a host's remove holds 2 hours |
| Screens | Pairing needs invite; 10 wrong codes a minute; codes last an hour; a TV only gets a role that views and plays |
| Media | Upload links take one exact size and type for 10 minutes; 4 GB a file; 20 GB a space (MEDIA_QUOTA_BYTES); unsaved uploads deleted hourly; private-space links run out after an hour |
| Events | Short lowercase names only; summaries for managers; up to 90 days |
LiveKit's webhook is signature-checked; anything else is refused. Media and room detail: docs/LIVE.md. Feeds: docs/SOCIAL.md.
Reports, blocks and filters #
Reporting and blocking are always on in every space and direct chat. Reports go to the space's moderators; serious reasons, anything in a kids space and anything outside a space also reach Zvessa's admins. A word filter (blueprint.safety) masks, holds or refuses words on the server, and kids spaces never go below its strict settings. Moderators mute and ban; admins suspend accounts and lock spaces. Detail: docs/SAFETY.md.
Accounts and email #
People can delete their account from the Spaces screen (an App Store requirement); email codes are 8 digits, hashed, last 15 minutes and allow 5 tries. Changing email or password is never offered to an AI. Detail: Account.
Planned #
Grown-up controls for who kids talk to and how long they play.