Skip to content

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.