Permissions and slots¶
For plugin authors: what a manifest may declare, what the installer checks, and the rules that apply to a plugin at run time. The catalog below is the list to pick from; how the server contains a plugin, and what that containment cannot stop, is described in Plugin sandbox and threat model.
What is checked at install¶
A plugin is refused at install when:
kindis missing, or is neitherpluginnortheme;- a
kind: pluginlackslanguageorentrypoint, or akind: themedeclares them, or declarespermissionsorcapabilities; - a permission or a capability is unknown;
- a frontend module targets a slot outside the slot list;
- an
external_fetchentry breaks the fetch rules, ornetworkis declared without any entry; - the ZIP breaks the extraction limits;
- the static analysis finds a critical pattern, or a warning the installer
treats as one (unsafe deserialization, obfuscation). Compiled binaries,
embedded archives and native code are refused unless the manifest declares
native-binary.
Comments are stripped before the analysis, so a commented-out line is not reported. The categories are listed in Plugin sandbox and threat model.
Before installing, the administrator sees the declared permissions and capabilities, and what the analysis found; slots, hosts and the signature are shown at update and on the Security Dashboard. Nothing a plugin declares is granted silently.
What the plugin runs under¶
Whatever the manifest declares:
- the plugin sees its own directory, not the server's data;
- it has no direct network access: a plugin with the
networkpermission reaches the hosts of itsexternal_fetchlist through the host'sFetchcall, and nothing else; - without
subprocess, it cannot start child processes (threads work); - it runs under resource limits the administrator can adjust: 256 MiB of memory, 64 processes and threads, 100 MiB of disk for its directory, and a lower CPU priority by default.
Signing is optional during development. How to sign a plugin, and the signature policy a server can enforce: Writing a plugin.
Permissions catalog¶
A permission is declared under permissions; any other value refuses the
install. Receiving webhooks (webhook.receiver) and providing metadata
(metadata.provider) are capabilities, declared under capabilities: see
Plugin API reference.
| Permission | What it grants |
|---|---|
network |
The backend may call Fetch. Must be combined with a non-empty external_fetch list; refused at install without it. The process itself still has no socket. |
subprocess |
The backend may spawn child processes. Without it, the launcher's kernel filter refuses fork, vfork and clone without CLONE_THREAD, and the seccomp shim refuses execve for interpreted plugins. Threads keep working either way. The administrator sees a dedicated warning. |
native-binary |
The package may ship .so, .dll, .exe, .wasm, .jar, embedded archives or native code the analysis cannot read. The administrator sees and must accept opaque code. Required for a go plugin. |
filesystem |
Declared intent to touch the filesystem beyond the plugin's directory. Recorded and shown to the administrator; the sandbox grants no file access outside the plugin's directory whatever is declared. |
media.read |
GetMediaItem and ListMediaItems on the host: the full media record. |
media.write |
Reserved for the metadata write calls, not served today. |
api.library |
The same media read calls as media.read. |
api.user |
Reserved for GetUserPreferences, not served today. |
api.metadata |
Reserved for UpdateMetadata, not served today. |
notification.sender |
Reserved for SendNotification, not served today. |
public |
The interface may read a safe projection of media items (id, title, type, year, overview, poster), never paths or streams. See evey.media.*. |
prefs |
The interface may read and write the current user's preferences in the plugin's own namespace. See evey.prefs.*. |
hardware.gpu |
The process gets the host's DRM render node (the real renderD1xx, never a card node). A kernel-driver attack surface and a GPU-memory boundary shared with the transcoder: a hung GPU can stall transcoding. Without it, GPU nodes are excluded from the Landlock allowlist entirely; with it, the launcher adds an IOCTL_DEV rule scoped to that one file (Landlock ABI 5 or later; on older kernels, read and write access alone permits command submission). The Security Dashboard shows a critical warning once the plugin is installed. Grant it only to plugins you would run outside a sandbox. |
External fetch rules¶
Declared in external_fetch: [...] and checked at install
(validateExternalFetch). All-or-nothing: one invalid entry refuses the
whole install.
An entry is a hostname. A scheme prefix and a trailing path are dropped at
parse; only the hostname is kept. Hosts match exactly, a *.example.com
wildcard is refused at install and a port suffix is
ignored.
| Entry | Valid | Reason |
|---|---|---|
api.tmdb.org |
Yes | Exact hostname, the normal form |
https://api.tmdb.org |
Yes | Scheme and path dropped at parse, hostname kept |
*.tmdb.org |
No | Wildcards are refused at install; list each host |
* |
No | A bare wildcard is refused |
192.168.1.1, ::1 |
No | IP literals are refused, both families |
At run time the host broker adds its own limits, which the manifest cannot
widen: http and https only, GET and HEAD only, the target host matched
case-insensitively against your list (a :port suffix in an entry is
ignored), private or reserved addresses refused after DNS resolution, a body
capped at 8 MiB and a request at 20 seconds. Redirects are re-checked against
both the list and the address rule.
The same host list is the connect-src of the plugin's frame, prefixed with
https://: a widget may call those hosts directly, over https only.
Slot whitelist¶
Plugins may inject UI only in explicitly allowed slots. Critical paths are intentionally absent.
| Slot | Auth required | Notes |
|---|---|---|
home.section |
Yes | Main page content area |
home.hero |
Yes | Hero banner area |
library.sidebar |
Yes | Library view sidebar |
media.detail.sidebar |
Yes | Media detail sidebar |
player.overlay |
Yes | Player overlay UI |
settings.user.appearance |
Yes | Appearance section in user settings |
sidebar.bottom |
Yes | Bottom area of the navigation tray |
admin.nav |
Yes | One nav entry in the admin panel opening the plugin's own sandboxed page; it does not inject into existing admin screens |
Never allowed: admin.* (except admin.nav), login.*, setup.*,
profile.security.*. These paths are authentication- or privilege-critical,
and injection there is a privilege-escalation or phishing vector.
admin.nav content is sandboxed like any other slot: it renders in an
<iframe sandbox="allow-scripts"> without allow-same-origin, in a page
whose policy is default-src 'none', so it is third-party content displayed
in the admin area, not an admin capability granted to the plugin.
The bridge limits what a frame can ask of the page: evey.toast at most 3
messages per second, 200 characters, no HTML; evey.navigate only /home,
/library, /media, /player, /settings and /search; evey.resize
clamped to 50-800 px. The frame holds no token: the host mints a five-minute
token scoped to (plugin, user) for the public and prefs calls, and the
server refuses by default every method that token does not name.
Update workflow¶
- The administrator uploads a new ZIP.
PreviewPluginUpdateruns the static analysis and the signature check and returns the difference in permissions, capabilities, slots andexternal_fetchagainst the installed version. - If the difference is not empty, the administrator must acknowledge it
before the update is applied (
requires_ack = true; through the upload route,accept_permission_changes=true). - The hash of the package is captured at preview and verified at apply: the ZIP that was analysed is the ZIP that gets installed.
- The signing key is checked for continuity: an update signed by another
key than the installed version, or no longer signed, is refused until the
administrator accepts the new publisher. Under
EVEY_PLUGINS_COSIGN_POLICY=enforce, an untrusted or invalid signature at the apply step blocks the update even if the preview passed.