Zvessa docs

People, roles and access

One model fits a family, a team, a class, a business or a fan club: roles the space names, seven fixed permission verbs, and access rules for who can find and join. Code: convex/lib/roles.ts, convex/access.ts, convex/members.ts.

Permissions #

Kits, blocks and server functions check verbs, never role names, so renaming or adding a role never breaks anything.

VerbAllows
viewSee the space's pages
postWrite in chats and streams
playVote, play games and join rounds
addAdd records, and change their own
editChange or delete anyone's records
inviteInvite people, answer join requests
manageChange the space (pages, styles, kits, roles), approve the AI's changes, use the AI chat

Roles #

"roles": [
  { "id": "owner",   "name": "Owner",   "can": ["view","post","play","add","edit","invite","manage"] },
  { "id": "coach",   "name": "Coach",   "can": ["view","post","play","add","edit","invite"] },
  { "id": "player",  "name": "Player",  "can": ["view","post","play","add"] },
  { "id": "visitor", "name": "Visitor", "can": ["view"] }
]

Access #

"access": { "visibility": "private", "join": "invite" }
visibilityWho can look
privateMembers only
linkAnyone with the link
friendsThe owner's friends
publicAnyone; listed in Explore
joinHow people become members
inviteOnly with an invite link
requestThey ask; someone with invite accepts and picks their role
openAnyone who can look can join as a member

A new space is private and invite.

Invites #

members.createInvite({ role, maxUses? }) makes a 10-letter code for a link. members.peek shows the space's name before joining; members.join joins with the invite's role. Codes can be used up (maxUses) or revoked. The invite block puts this on a page.

Invites and referrals #

The Invites and referrals kit (Grow) gives people their own invite code and QR, records who invited whom, and rewards in any Ledger unit (coins; no real money). Code: convex/kits/invites/ (its README.md has the detail; REFERRALS_GUIDE in model.ts is what the AI reads).

"referrals": {
  "codes": { "who": "view", "role": "member" },
  "rewards": [{ "to": "inviter", "unit": "coins", "amount": 50 }, { "to": "invited", "unit": "coins", "amount": 25 }],
  "qualify": { "hours": 24, "event": "post", "times": 1, "days": 30 },
  "limit": { "count": 10, "every": "month" },
  "milestones": [{ "at": 5, "badge": "verified" }, { "at": 10, "unit": "coins", "amount": 200 }]
}

Blocks: invites.code (your code, QR and share), invites.list (who you invited and their status), invites.redeem (enter a code), and a general qr block (any link, text or page). AI and MCP tools in the new people scope: list_invites, create_invite, revoke_invite, list_referrals, recheck_referrals. Making and turning off links through tools needs a person's token; the in-app AI can read and recheck, and changes the rules only by proposal.

Hiding pages and blocks #

{ "id": "staff", "title": "Staff only", "visibleTo": ["coach"], "blocks": [] }
{ "id": "answers", "kind": "card", "visibleTo": ["coach"], "props": { "title": "Answers" } }

visibleTo on a page or block lists the role ids that see it; unset means everyone who can see the space. The server removes hidden parts before sending the blueprint (blueprintFor), so they never reach the person's device. Managers see everything.

The data behind a hidden block is hidden too. If every block that shows a collection, stream, feed or mini-app is hidden from someone, the server also refuses to read or write it for them, whatever client they use. Data no block points to stays reachable to anyone with the verb. See Security.

Several spaces #

A person can own a space and be a member or visitor of others. members.mySpaces lists them, members.open / members.visit switches, members.home goes back to their own. Every function works on the space the person has open, through getAccess, so permissions live in one place.

Friends #

Friends are between accounts, across spaces (friends.*: ask, answer, remove, list, and the spaces your friends let you see). They decide who sees friends spaces. Direct messages (dms.*, the direct block) follow a person across spaces too.

Teams, classes and branches #

The Structure engine (Teams and hierarchy kit) puts people into units of any kind, named by the space ("Class" led by a "Teacher", "Squad" led by a "Captain"), as a tree. Leads place people in their units and set titles; open units can be joined. A stream can be for one unit only, so each team gets its own room. See Engines.

Levels and badges #

With a Ledger kit (tiers, VIP, memberships, verified), visibleTo also takes level:<id>, level:<id>+ (that level or higher by rank) and badge:<id>, so a page can be for VIPs or verified members only. Hidden parts are removed on the server the same way.

Kids spaces #

"audience": "kids" (the Kids space kit) keeps the space private and invite-only, out of Explore, and tells the AI to keep everything kid-safe. The check refuses any other access while it's on. Kids (members whose role there can't invite or manage) only message, friend or ring people who share one of their kids spaces, and only the owner can open the space up again (Security).

Planned #