Permissions overview
This page is the starting point for setting up permissions in your game. It shows the layers that decide what a player can do, the permission keys you work with, and a checklist for configuring them.
All permission configuration is done by app admins (members with the
manage_apps permission on the app's organization). manage_apps is a per-user
permission checked on the normal bearer token, so admins can configure
permissions from any trusted context — including a browser admin tool such as the
management UI, not only a backend service. The standing advice is simply to keep a
privileged admin token out of the untrusted client you ship to end users.
The three layers
A player's effective abilities come from three layers that stack:
| Layer | Where you set it | Scope | Answers |
|---|---|---|---|
| App access + tier | Management API | Whole app | Can this player play, and what does their tier allow everywhere? |
| Grid permissions | Game API — Grids and permissions | A 3D region | What may this player do in this specific area? |
| Team roles | Game API — Teams | A team | Who may manage this team, and what does the team grant in the world? |
| Channel roles | Game API — Channels | A channel | Who may manage this channel, and who may publish to it (send_messages)? |
Tiers set the baseline for the whole app. Grids add spatial rules on top — either expanding control in an area (e.g. a player owns their plot) or restricting it (e.g. a safe zone). Teams let you grant and delegate at the group level instead of player-by-player.
Open by default
Permissions are always enforced on the realtime path — every spatial action a player takes is checked against their app access, tier, and grid permissions. There is no "permissions off" mode.
To keep the common case effortless, a new app is open by default:
- Creating an app provisions a default tier that grants the explicit legacy
gameplay allowlist (
access,teleport,update_voxel_data,use_voice_chat) and the two communication keys (use_text_chat,use_direct_messages). - The app gets a default grid that spans the whole world, and any player you grant app access is automatically granted those same legacy keys on that grid.
The four code keys (write_server_code, run_server_code,
write_client_code, run_client_code) are deliberately excluded. Adding new
runtime keys does not widen the default tier/grid automatically; server/client
code remains opt-in.
The communication keys are the one exception: any tier that grants access also
grants use_text_chat and use_direct_messages, whether or not its
permissionKeys name them, unless you create or update it with
applyCommunicationDefaults: false. Pass false to take text chat or direct
messages away from a tier. Both keys are app-level only: grids never carry them.
The automatic grid grant above happens once, when the player is first given app access. It is a snapshot of the tier at that moment, not a live link to it.
So adding a key to a tier later — run_server_code, say, so players can switch their
mods on —
reaches every new player and none of the existing ones. They keep the keys they
were granted on the way in. Nothing errors: enforcement needs the key at both the app
and grid layers, so the tier now allows the action and the grid still refuses it.
To bring existing players up, grant the key on the grid as well:
mutation {
grantGridPermissions(input: {
appId: "APP_ID", gridId: "GRID_ID", userId: "USER_ID",
permissionKeys: ["run_server_code"]
}) { __typename }
}
Check who has what with gridUserPermissions before and after — a tier that reads
correctly while a grid row lags behind is the shape this failure takes.
So a fresh app behaves like an open sandbox: any entitled player can move, build, and use voice anywhere — no manual grid or grant setup required. You only do the work below when you want to restrict something (a non-building safe zone, plot ownership, members-only areas, etc.). Restriction is opt-in; openness is the default.
Permission keys
Grid and tier permissions use these runtime keys:
| Key | Allows |
|---|---|
access | Entering / moving / sending events in an area |
teleport | Teleporting within the app (wire enforcement may depend on the client path) |
update_voxel_data | Editing voxels (building) |
use_voice_chat | Voice audio. From replication server v0.43.0, hearing it too. |
use_text_chat | Nearby text (sendTextPacket, opcode 136) and channel messages (sendChannelMessage, sendRangedChannelMessage; opcodes 17 and 32). In every tier that grants access unless you pass applyCommunicationDefaults: false. Bit index 10; app-level only (appliesToGrid false). |
use_direct_messages | Messages to one player (sendSingleActorMessage, opcode 142). In every tier that grants access unless you pass applyCommunicationDefaults: false. Bit index 11; app-level only. From replication server v0.43.0 a player under 18 never holds it, whatever the tier. |
use_video_chat | Webcam video (sendVideoPacket / wire opcode 143). Opt-in: not in a new app's default tier, because every receiver in range pays the egress for every frame — grant it on a tier deliberately. Bit index 9; appliesToApp and appliesToGrid both true. |
write_server_code | Authoring/deploying server code in owned grids |
run_server_code | Running admitted server code in owned grids |
write_client_code | Authoring browser-target code |
run_client_code | Running admitted browser-target code |
use_studio_agent | Entering the Agentic Crowdy Studio protocol. Separately grantable and app-only; it does not imply project, runtime, grid, Play-lease, trust, or commerce authority. |
Query the Management API runtimePermissions for the live catalog when
building a permission picker in your studio tools.
use_video_chat follows the tier onto the grids the platform creates for a player: the default world grid grant and a self-claimed chunk (claimGridChunk) carry it only when the player's tier does. Grids you create yourself list it explicitly in permissionKeys like any other key.
use_studio_agent currently occupies replica permission bit index 8, but
clients and tier configuration should use the stable key instead of hard-coding
the bit. It has appliesToApp=true and appliesToGrid=false; do not require or
infer a grid ACL row for this key. Agentic Build still checks the four
target-specific code keys above, and Play still checks a human-granted lease
plus the ordinary game permissions. See
Agentic Crowdy Studio.
Teams use a separate set of team-management keys (manage_group,
manage_members, manage_roles, invite_members) — see Teams.
Set up permissions for your game
-
Define tiers and what each grants. In the Management API, create your access tiers and choose which permission keys each tier grants app-wide (for example, a free tier with
accessonly, and a builder tier that addsupdate_voxel_data). This is the baseline every player gets. -
Decide which regions need finer control. Create grids for those regions. A grid can be a whole zone or a single chunk, so you can hand one player control of exactly one chunk.
-
Cap what a region allows (optional). Use
setGridPermissionLimitsto make safe zones — e.g. allowaccessanduse_voice_chatbut never building, regardless of other grants. To stop building on land nobody has claimed, close the wilderness (updateAppwithwildernessWritesOpen: false). -
Grant permissions in the region. Either:
- Per player with
grantGridPermissions(e.g. give a plot ownerupdate_voxel_dataon their chunk), or - Per team/role with
assignGroupToGrid— grant a whole team (or just members holding a specific role) permissions across a region, or - To every player with
setGridOpenPermissions— a public build area or an arena (open grids).
- Per player with
-
Let players run their own teams (optional). Use
setTeamPolicyto choose who can create teams (admin/member/anyone) and the default join policy. Team leaders can then manage their own members and roles.
How it's enforced
The realtime layer (the Buddy replication servers) checks permissions on every
spatial message — this is always on. A message is accepted only if the sender has
active app access whose tier holds the needed key and the target chunk is
inside a grid where the sender holds that key. A player without access to a
region can't act there, building requires update_voxel_data, voice requires
use_voice_chat, text and channel messages require use_text_chat on the app, and
messages to one player require use_direct_messages on the app; anything else is
rejected with UNAUTHORIZED. From replication server v0.43.0 the receiving
side is checked too: voice, video, text and direct messages reach only players who hold the
matching key, and each player's voice and video settings, contacts and blocks
decide the rest. Building is
checked against the chunk's most specific covering grid only (the smallest
box; of equal boxes, the lowest gridId), and against the app's
wilderness setting when that grid is the
world grid; the other keys are granted by any covering grid. See
which grid decides a voxel write.
With the open-by-default setup above this is transparent — the default grid
grants every entitled player every key everywhere — until you add narrower
grids: a nested grid decides building in its chunks, so a zone everyone should
build in must be opened. A player's
effective keys on a grid are:
(their tier baseline) plus (direct grants ∪ team/role grants ∪ the grid's open keys) on that grid, within the grid's limits.
Use gridUserPermissions (one grid) or nearbyGridPermissions (all grids around
a chunk) to see exactly what a player has where.
Related guides
- Grids and permissions — grids, limits, direct and group grants.
- Teams — teams, roles, delegation, and assigning teams to grids.
- Channels — app-wide message channels, the
send_messagesrole, and publishing/receiving channel messages over the realtime UDP path. - Voice, video, contacts and blocks — each player's settings, contacts and blocks, and the defaults for players under 18.
- Mods — players' server code on grids they own, the code keys it needs, and the app's code admission.
- Avatar state and Actor state — owner-exclusive write / public read for character data.