Zvessa docs

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 #

RuleWhat it means for you
Nothing people make runs as code on the serverBlocks, kits, styles, games and bots are data. Custom logic is a mini-app in a sandbox.
The AI proposes, a person approvesNeither 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 checkedcheckBlueprint refuses a broken or unsafe blueprint with a readable message (Blueprint). Replacing the whole blueprint in one op is refused.
One permission checkEvery function goes through getAccess / mustAccess and checks a verb, never a role name (People).
Keys stay on the serverNo 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:

  1. The layout: blueprintFor removes hidden pages and blocks before the blueprint is sent, so they never reach the device.
  2. 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 no view.

HQ blocks (hq.*) are only for people who manage the space. Managers see everything.

Roles and invites #

Kids spaces #

audience: "kids" (Kids space kit):

The AI #

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:

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 #

WhereLimit
Chats and streams4,000 characters a message; slow mode per stream (moderators exempt); groups capped by talk.groups.max; a later send at least a minute ahead
Feeds60 posts or reactions per person per 10 minutes; outside sources pulled at most once a minute
Records40 fields per collection; every field checked and cleaned on add; lists 500 at most
LedgerWhole steps only; balances never go below zero; every move is an entry that's never edited; issuing needs the unit's issue verb
GamesRandomness, 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 rooms3 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
ScreensPairing needs invite; 10 wrong codes a minute; codes last an hour; a TV only gets a role that views and plays
MediaUpload 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
EventsShort 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.