Plugin processes and operator exemptions
For: cluster operators and plugin authors.
Plugins are trusted code
Plugins run as processes with the node's permissions. Install only plugins you trust, and run nodes as a user without root. The edge records who installed which repository at which commit.
Tool processes get a minimal environment, not the runtime's, so they cannot read the local proxies' tokens.
Strict plugin sandboxes
On a node with sandbox tier 1, each installed plugin's MCP servers (and so its hooks) also run in a sandbox of their own: their plugin's folder read-only, a home of their own, none of the node's other processes in sight, so neither another plugin's environment nor its files, nor the node's data directory and key. By default (oz0-node run --plugin-sandbox strict) their network goes through the node's egress proxy, as a session's does: they reach what the manifest's egress: and the tenant's list allow, and nothing else, whoever wrote the plugin.
The proxy reads the tenant's list as it stands, so a host taken off it stops being reached by a server that is already running.
What a manifest writes in a tool's env stays with that tool: the runner of its sandbox, container, microVM or remote sandbox, which runs as the node's user, starts with the node's variables and hands the tool's on apart from its own, and a sandbox forwards only the node services the runtime gave the tool, never a loopback port a manifest named.
None of a plugin's code runs before its sandbox does: the node builds it without running its build backend or a dependency's build, from wheels, so that an install holds to the same lists. A plugin that needs its build run says so by failing its install, and the node's operator decides whether to use another operator mode.
Host and nested-sandbox exemptions
A tool that cannot run in that sandbox does not start, rather than run half working. That is a tool that must see the host itself, such as ai.oz0/host-health, which says sandbox: host in its manifest, and a tool that starts sandboxes of its own, which says sandbox: own. The plugin's status and the node's findings say which tool and why, and a call of it answers the same.
Only the node's operator lets such a tool out, on that node, by naming its plugin: oz0-node run --plugin-sandbox-exempt host-health (or oz0-node service install with the same flag), or host-health@<commit> for one version, since a name also covers what a tenant admin installs under it next.
Nothing a plugin declares and nothing an edge sends can do this, and a private candidate is never exempt. An exempt tool runs as the node's user, and plugin lint warns about sandbox: host.
Other operator modes
An operator may choose less. --plugin-sandbox auto keeps the host's network, so that only the plugin's own code holds it to its lists, for a plugin that must reach what its manifest does not list yet, or that diagnoses the node's own network; off sandboxes none but private candidates.
On both, a plugin's build runs as the node's user, as does an exempted plugin's on strict.
A node with no tier 1 runs every plugin as its user, its build included, as it always did, shows the tier-0 finding, and reports no plugin sandbox.
On macOS the sandbox is Seatbelt, which strict uses as it does on Linux and auto does not, since Seatbelt has no host's network to share; it holds no memory, process or CPU limits and no user per plugin (Sandbox on macOS).
Per-plugin users on root nodes
On a node that runs as root, each plugin's sandboxes run as a user of its own (1,900,000,000 plus a hash of the plugin's name, under bubblewrap and gVisor alike), so one plugin's server cannot read another's files or signal its processes even outside the namespaces; the node makes its data folder traversable for those users (mode 0711, no listing) and gives each server its own folders and the files placed for its calls.
Whatever the node's umask (the systemd unit it writes has UMask=0077), it sets the modes a plugin's user needs below the data folder before each sandbox starts: the search bit on every folder on the way, and read access to the plugin's own folder, the managed Python and tools, the tenant's egress list and the node binary.
It changes nothing above the data folder, and a server whose folders stay out of its user's reach (a plugin folder linked from a closed folder elsewhere) runs as the node's user, and the node logs why.
Other local accounts of the machine can then read the data folder's files whose names they know and that are not private to the node: the plugins' code and the managed runtimes always, the node's status and health files when its umask leaves them readable; the node's key, its logs, the tenant's settings and every secret stay private.
A data folder under one others cannot traverse (such as /root) keeps plugin users off, and the node logs why: keep a root node's data folder under one others can traverse, such as /var/lib.
Tier-0 nodes run every plugin as the node's user.
Builds and mixed fleets
A strict node installs only builds that run no package code. A plugin using setuptools or hatchling, a dependency with no wheel, or an npm dependency from Git, a URL or outside the plugin folder needs an operator exemption. Its build then runs as the node's user. auto, off and tier 0 also run builds as that user.
Routing does not account for plugin exemptions. In a mixed fleet, a job can land on a node that refuses its tool at startup, even when another node permits it. Apply the exemption consistently to eligible nodes, or use a node label to restrict the job to nodes that have it. Review the plugin before exempting it.