Capabilities & security¶
The plugin security model is: deny by default, grant explicitly, intersect strictly, and never expose secrets or keys. This page unpacks each layer.
The capability set¶
Every plugin execution has an effective capability set — the exact permissions it holds at runtime. It is the intersection of four sources:
effective = manifest_request ∩ binding_grant ∩ actor_authorization ∩ agent_token
^ (only when the actor is an agent)
If any layer denies a capability, the plugin does not get it. A plugin cannot grant itself
capabilities (REQ-PLUGIN-032).
Default (deny-by-default) set¶
read_metadata = true
read_clear_paths = [] # no cleartext file contents
read_protected_metadata = false
read_protected_plaintext = false # never provided by default
write_working_copy = false
write_plugin_cache = false
network_access = false
emit_audit = false
invoke_tovio_command = false
propose_resolution = false
propose_transform = false
block_operation = false
Field-by-field¶
| Capability | What it enables | Guardrails |
|---|---|---|
read_metadata |
Path/change-kind/target/actor metadata in the event payload. | Always safe; on by default. |
read_clear_paths |
Cleartext file contents for these glob patterns. | Globs must match non-protected paths; protected paths are still redacted. |
read_protected_metadata |
Redacted metadata for protected paths (path, change kind, policy id, hash where allowed). | Never plaintext. |
read_protected_plaintext |
Plaintext contents of protected paths. | Requires all five conditions in §10 (manifest, binding, actor, sandbox trust, audit). Even then, MVP defaults to metadata-only, and it is the one capability --grant refuses outright (TVO-PLUGIN-013). |
write_working_copy |
Propose changes to the working copy. | Only applied if the binding grants transform-apply. |
write_plugin_cache |
Write to the plugin's own sandbox-scoped cache directory. | Isolated per plugin; not shared. |
network_access |
Reserved for a future sandbox egress channel. | Denied by default — and today it cannot be turned on. The sandbox exposes no network host functions at all, so a plugin whose effective grant asks for the network is refused before it runs (TVO-PLUGIN-012). |
emit_audit |
Attach structured audit_metadata to the result. |
Required for policy-adapter/enforcing use. |
invoke_tovio_command |
Call back into tovio from inside the sandbox. |
Heavily gated; never grants extra authority. |
propose_resolution |
Emit a conflict Resolution (required for resolver type). |
Applied through the normal conflict path. |
propose_transform |
Emit a transform proposal (required for transform type). |
Applied only if the binding grants transform-apply. |
block_operation |
Cause an operation to fail-close when bound in enforcing mode. | Without this, fail cannot block. |
The empty set, on purpose¶
One event goes the other way and demands nothing: a plugin declaring
transcript-parse must request an empty capability set, is refused
at install if it requests even one, and is executed with a completely empty grant no matter what its
binding approves.
That is not tidiness — it is the reason the event can exist at all. AI agent transcripts live outside the
repository, next to ~/.aws, ~/.ssh, and ~/.config/gh, while every capability in the table above is
repository-bounded. Honoring those files through the sandbox would need a host-filesystem read capability,
which would escape the boundary that makes this whole set reasonable to reason about and, composed with
network_access, would be a general exfiltration primitive. So TOVIO reads the file itself, first-party and
in-process under your own privileges, and sandboxes only the parsing — a pure bytes-to-turns function that
needs no reach whatsoever.
Protected content — the strictest rule¶
Protected files (encrypted per TOVIO's cryptographic permission model) are the sharpest edge of the capability model.
- Protected plaintext MUST NOT be provided to plugins by default (
REQ-PLUGIN-033). - Protected plaintext, if ever provided, requires all of (
REQ-PLUGIN-034): - manifest declaration,
- binding approval,
- actor read authorization,
- sandbox trust approval,
- audit metadata recording.
- Key material MUST NEVER be provided (
REQ-PLUGIN-035). No manifest field, no policy override, no binding grant makes this possible. - Plugin output MUST NOT contain protected plaintext or key material in
summary,findings,audit_metadata, stdout, or stderr (REQ-PLUGIN-036). - Protected paths default to redacted metadata (
REQ-PLUGIN-037):{path, change_kind, policy, content_hash?}. Never plaintext.
The sandbox¶
The preferred runtime is WASM/WASI (REQ-PLUGIN-038); the shipped one is Wasmer with the
Cranelift compiler, in the tovio-plugin-host crate. The runner:
- enforces the manifest
timeout_ms(REQ-PLUGIN-039) with two independent bounds — a deterministic instruction (fuel) budget, so aloop {}that never calls a host function still traps, and a wall-clock deadline, - bounds every plane a guest artifact can size: linear memory, table element counts per table and in
aggregate, and the host-side compiled-module cache (
REQ-PLUGIN-040). "The runtime does not support that limit" is not a licence to run a plane unbounded — a runtime that cannot enforce a guest-side bound is refused unless the runner enforces an equivalent host-side one, before instantiation, - denies network (
REQ-PLUGIN-041) — by exposing no network host functions at all, so a plugin whose effective grant asks fornetwork_accessis refused rather than run, - denies arbitrary filesystem access (
REQ-PLUGIN-042), - never exposes
.tovio/internals directly (REQ-PLUGIN-043), - runs plugins with a minimal environment — no inherited ambient secrets (
REQ-PLUGIN-044), - deems native executable plugins not part of MVP default trust; if allowed later, only through
explicit opt-in with clear trust warnings (
REQ-PLUGIN-045).
Intersection with agent tokens¶
When an agent runs a plugin, the agent's capability token further
constrains what the plugin sees (REQ-PLUGIN-031, REQ-PLUGIN-077). For example:
- An agent with
path_scope = ["src/**"]cannot cause a plugin invocation to see file contents outsidesrc/**, even if the plugin's manifest requests broaderread_clear_paths. - An agent without
secret_clearancecannot cause a plugin to receive protected plaintext. - An agent cannot install, update, bind, or grant capabilities to plugins unless policy
explicitly authorizes it (
REQ-PLUGIN-075).
The rule is simple: a plugin never sees more than the invoking actor could see directly.
What is computed today
The two contract-level operands — manifest_request ∩ binding_grant — are computed in pure
tovio-core and enforced on every execution. The last two operands narrow further and can only
ever shrink the set, but the CLI edge does not yet resolve a real actor DID or thread an agent
capability token into plugin input; a local run carries a fixed local actor marker. Because
plugin input is metadata-only for protected paths regardless, the missing narrowing cannot widen
what a plugin sees beyond the bound set.
Audit — what's recorded, what's not¶
Every enforcing plugin execution writes signed audit metadata (REQ-PLUGIN-058..060):
- plugin id and version, event, mode, binding id,
- actor, repository, target change/ref,
- status, finding count, input hash, output hash, timestamp,
- sandbox runtime,
- the effective capability name set (
plugin_granted_capabilities) — names only, never globs or plaintext, - a
plugin_touches_protectedboolean — whether the gated operation touched protected paths.
Audit records MUST NOT include protected plaintext or key material. Verifying with
tovio audit verify covers the plugin fields as they ride the signed AuditEntry body.
Threat model — what plugins can and cannot do¶
| Threat | Mitigated by |
|---|---|
Plugin reads a .env outside the repo |
Sandbox denies arbitrary filesystem access (REQ-PLUGIN-042). |
| Plugin exfiltrates data over the network | The sandbox exposes no network host functions at all, so there is nothing to call (REQ-PLUGIN-041); a plugin needing egress is refused, not run. |
| Plugin reads protected file plaintext | read_protected_plaintext requires the full §10 approval chain and is metadata-only in MVP. |
Plugin leaks a secret in stderr/findings |
Output is treated as untrusted; protected/key content must not appear (REQ-PLUGIN-036). |
| Plugin escalates its own authority | A plugin cannot grant itself capabilities (REQ-PLUGIN-032); the effective set is intersected. |
| Local malicious plugin gates Forge merge | Forge does not trust local results without policy-approved attestation (REQ-PLUGIN-070). |
| A package drops a native binary | MVP allows only wasm-wasi; native is opt-in with warnings (REQ-PLUGIN-045). |
| Malformed output silently passes | Malformed output is treated as error and fails closed on protected operations. |
Related¶
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure