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.
| Verb | Allows |
|---|---|
view | See the space's pages |
post | Write in chats and streams |
play | Vote, play games and join rounds |
add | Add records, and change their own |
edit | Change or delete anyone's records |
invite | Invite people, answer join requests |
manage | Change 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"] }
]
- Default roles: Owner, Member, Guest, Visitor (Catalog).
ownerandvisitorare core. The owner keeps every permission; a space has one owner. Visitors (people looking at a public, link or friends space they haven't joined) can nevermanage,inviteoredit, and may have no permissions at all.- Only managers can hand out a role that can
manageorinvite. - A member's role changes with
members.setRole; anyone can leave withmembers.removeMember.
Access #
"access": { "visibility": "private", "join": "invite" }
visibility | Who can look |
|---|---|
private | Members only |
link | Anyone with the link |
friends | The owner's friends |
public | Anyone; listed in Explore |
join | How people become members |
|---|---|
invite | Only with an invite link |
request | They ask; someone with invite accepts and picks their role |
open | Anyone 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 }]
}
codes: who gets a personal code (a verb) and the role it gives. Without it, only roles that caninvitemake links. Without/referralsat all, who invited whom is still recorded.qualify: a reward waits until the new person has stayed (24 hours by default) and, if set, done something (event,times), withindays. Checks are scheduled on the server, never polled.rewardsgo through the Ledger, paid once even on a retry, and thereferevent is counted, soledger.leaderboardwithevent: "refer"ranks top inviters.milestonesadd badges or units.- Against farming: a person counts once per space, ever; no self-referrals; the owner and TVs never count; someone who held units here before counts as
void; leaving first lapses it; reward units are locked (spendable here, not sendable) unlesssendable: true; each inviter has a limit (10 rewarded a month by default); a code can't give a role that can manage, invite or edit. In kids spaces only grown-ups (roles that can invite) get codes.
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 #
- Kids spaces where grown-ups also set who kids can talk to and how long they play.
- Gates by password, and pay-by-card memberships (Stripe).
- Sign-in: email and password today; Apple and Google before release; email codes later.