What a plugin is allowed to touch
The capability model, the intersection rule, and the sandbox ceilings.
Written By Dustin
Last updated About 3 hours ago
Every plugin runs with an explicit capability set. The default is deny: exactly one capability is on, and it is the one that reveals nothing.
The capabilities
The intersection rule
The effective grant is the manifest request intersected with the binding grant. Both halves must say yes, and neither can widen the other. For read_clear_paths, the intersection is the narrower glob set — a plugin can never read a path that only one side named.
An agent adds a third narrowing: an agent running a plugin is further constrained by its own capability token, so a plugin can never be used as a way around an agent's scope.
A plugin cannot grant itself anything. There is no API for it, and asking for something outside the intersection is TVO-PLUGIN-005.
Protected content
Protected plaintext is never provided by default, and it is the one capability a flag cannot enable. --grant read_protected_plaintext is refused at bind and hook authoring with TVO-PLUGIN-013, and nothing is written.
Enabling it at all would require all five of: a manifest declaration, binding approval, the actor's own read authorization, sandbox trust approval, and audit recording. One token on a command line satisfies none of them, and silently dropping the request would be worse than refusing — it would look granted.
Two absolutes sit underneath that:
- Key material is never provided to a plugin. Not by any grant, not on any tier.
- Plugin output, logs, findings, and audit records must not contain protected plaintext or key material. By default a protected path appears in plugin input as redacted metadata — path, change kind, policy name, and content hashes where policy permits.
The sandbox ceilings
These are always on, and they apply to every plugin including the built-in scanner.
- No network host functions at all. A grant that requires network cannot be honoured —
TVO-PLUGIN-012. - No arbitrary filesystem access, and
.toviointernals are never exposed directly. - A minimal environment. No ambient secrets are inherited.
- A fuel budget — a deterministic instruction bound — plus a wall-clock deadline from the manifest timeout, so a runaway guest is killed rather than left running.
- A linear-memory ceiling, and a cap on captured stdout and stderr.
- A fresh instance per execution.
A repository owner may tighten the time, fuel, and memory ceilings through plugins.max-timeout-ms, plugins.max-fuel, and plugins.max-memory-mb, or their TOVIO_PLUGIN_* environment equivalents. Most restrictive wins, and a cap can only ever tighten — an owner cannot raise a ceiling above the built-in one. A plugin that exhausts a cap fails closed with TVO-PLUGIN-007; a required plugin that times out blocks, never silently skips.
What gets audited
An enforcing execution produces a signed audit entry recording the plugin id and version, the event, the mode, the actor, the repository, the target, the status, the finding count, the binding, input and output hashes, the timestamp, the sandbox runtime, and the granted capability names.
Names only — never globs, never plaintext — which is what makes recording the full set safe. The entries ride the signed audit chain, so tovio audit verify covers them like anything else.