Capabilities and permissions
Status: Partly implemented. The sandbox, the capability store, the Permissions screen, command permissions, HTTP behind
net.http:<host>and checking or requesting capabilities from code (API 1.20) work. The APIs behind the other capabilities (open URL, clipboard, notifications, synthesized input) and a permission-node API for servers are Planned.
CLight uses two unrelated words that are easy to confuse:
| Term | Who it protects | Where it’s declared |
|---|---|---|
| Capability | The player’s computer, from the mod | permissions / optional_permissions in clight.mod.json |
| Command permission | The server, from its players | On a command node (requires_permission, requires_level) |
Capabilities (the mod sandbox)
Every mod (Lua or WebAssembly) starts with the CLight game API and its own folder, and nothing else:
- read-only:
mods/<id>/(its own package contents; WASI/) - read/write:
mods/<id>/data/(WASI/data; nothing executable is ever run from it) - its config files through the config API (
config/<id>.json,-client.json,-server.json)
Extra reach must be declared, and the player decides:
"permissions": [ "net.http:api.ruby-tools.example" ],
"optional_permissions": [ "os.open_url" ]
| Capability | Grants | API today |
|---|---|---|
game |
The whole game API | always granted |
fs.mod_data |
Read and write mods/<id>/data/ |
always granted (WASI) |
fs.config |
The mod’s config files | always granted (config API) |
net.http:<host>, net.http:*.<domain>, net.http:* |
HTTPS to that host / its subdomains / any host, downloads only into mods/<id>/data/ |
Implemented (API 1.20): Filesystem and HTTP |
os.open_url, os.clipboard, os.notifications |
Open a link, read the clipboard, desktop notifications | Planned (a text click action open_url works without it; the game asks the player) |
input.synthesize |
Send chat, commands or keys the player didn’t type | Planned |
account.profile |
The player’s name, UUID and skin (never tokens) | Planned |
packets.raw |
Raw packet listeners | Planned |
ui.inject |
Change vanilla and other mods’ screens | Planned |
Some mixin targets also name a capability; injecting into them needs the player’s grant.
Things no mod can ever get: other folders (saves, screenshots, other mods), starting programs, loading DLLs, raw sockets, environment variables, Vulkan handles. A manifest that asks for something outside the list is rejected by clight check and the loader. The full list: Capability list.
Enforcement is by construction: Lua has no io or package, and WebAssembly modules can only call CLight imports and a WASI that sees nothing but the mod folder. A denied call returns CL_E_PERMISSION_DENIED; the game never prompts by itself in the middle of play, only when the mod calls cl_cap_request (Lua cl.request_permission, API 1.20) for a capability the player hasn’t decided yet.
The Permissions screen
Mods → select a mod → Permissions lists every capability the mod declared (and its optional ones) with an Allowed / Denied toggle each. Choices are saved in config/clight/permissions.json (per game folder, so per launcher instance). Nothing beyond the sandbox is granted without the player’s choice. A dedicated server reads the same file.
Command permissions (server)
Commands gate who may use them on the node itself (Commands):
| Call | Who passes |
|---|---|
cl_cmd_requires_level(node, 2) |
Sources with at least that permission level (0 players, 2-4 operators, 4 the console) |
cl_cmd_requires_permission(node, CL_STR("my_mod.admin")) |
A named permission. Without a permissions plugin it means operators (level 2). |
cl_cmd_desc.permission_level |
The simple form for cl_cmd_register |
Vanilla clients only see commands they may run, because the command tree sent to each player is filtered. In a command, cl_cmd_ctx_permission_level(ctx) (Lua ctx:permission_level()) returns the source’s level.
Planned: registering permission nodes with defaults, checking them anywhere (has_permission), and a permissions service that a permissions mod can provide (groups, wildcards).
Trust
There is no trust prompt: no mod can run unsandboxed code, so installing a mod can’t give it more than the sandbox plus the capabilities the player allows.