Use a plugin¶
This page walks through installing a plugin, verifying its integrity, binding it to an event, running it, and reading the result. If you are writing a plugin, see Write a plugin; if a required plugin is blocking you, jump to When a plugin blocks you.
Local and Forge plugin execution are implemented
The contracts, CLI, WASM/WASI runtime, local bindings, publisher-signature trust, bounded HTTPS installation, and Forge server-side enforcement run today. The command shapes below describe the implemented local surface. There is no plugin registry.
flowchart LR
I["1. Install<br/>tovio plugin install"] --> V["2. Verify<br/>tovio plugin verify"] --> B["3. Bind<br/>tovio plugin bind<br/>(or policy hook add)"] --> R["4. Run<br/>(event fires, or<br/>tovio plugin run)"] --> O{{"pass / warn /<br/>fail / error"}}
More diagrams for every step, including the blocked-land recovery flow: Workflows.
1. Install a plugin¶
A plugin package is a directory holding manifest.toml plus at most one runtime artifact
(plugin.wasm), or a bare manifest file. TOVIO installs it from a local path or an HTTPS URL into
this repository's store (.tovio/plugins/<id>/ — local, never synced). Every install validates the
manifest before writing anything and checks the integrity metadata: a signed package's artifact hash,
publisher signature, trust, and revocation state. A hash-only or local_dev package is stored as a
development-policy install and is never reported as publisher-verified.
$ tovio plugin install ./license-check
✓ installed `com.example.license-check` v1.2.0 (check) artifact: plugin.wasm
source: local/offline · verification: sha256-only
not a repo/org/Forge policy gate; bind it with `tovio plugin bind`
An HTTPS source ends in / or /manifest.toml and serves a sibling plugin.wasm. The download uses
the system root certificates, is size-bounded, and refuses redirects, credentials, query strings, and
archives. A remote package has to be publisher-signed by an identity you have trusted:
$ tovio plugin trust add did:key:1111111111111111111111111111111111111111111111111111111111111111
$ tovio plugin install https://plugins.example.com/license-check/
Inspect what installed:
$ tovio plugin list
$ tovio plugin show com.example.license-check
$ tovio plugin verify com.example.license-check # re-verify integrity, signature, trust
2. Bind the plugin to an event¶
An installed plugin does nothing until it is bound. Local bindings live in
.tovio/plugins/bindings.toml (scope = "local", inside the unsynced .tovio/ tree); both
tovio plugin bind and tovio policy hook add write there. A local binding is never a repository,
organization, or Forge policy gate (REQ-PLUGIN-025) — Forge-side required gates work differently
and are described under Lifting an enforcing binding.
The binding's grant is deny-by-default: name each capability the plugin may use with --grant, and
each clear-path glob it may read with --grant-read-path. The effective grant is always the
intersection with what the manifest requests, so a binding can never widen a plugin.
$ tovio plugin bind com.example.license-check \
--event pre-land \
--mode enforcing \
--required \
--branches main --branches "release/**" \
--grant emit_audit --grant block_operation \
--grant-read-path Cargo.toml --grant-read-path Cargo.lock
✓ bound `com.example.license-check` → pre-land (enforcing, required), pinned to v1.2.0
grant: emit_audit, block_operation · read-paths: Cargo.toml, Cargo.lock
a LOCAL binding — not a repo/org/Forge policy gate (REQ-PLUGIN-025)
The resulting binding on disk (the [bindings.grant] table lists every capability field):
[[bindings]]
plugin_id = "com.example.license-check"
event = "pre-land"
mode = "enforcing"
scope = "local"
required = true
paths = []
branches = ["main", "release/**"]
actors = []
[bindings.version]
exact = "1.2.0"
[bindings.grant]
read_metadata = true
read_clear_paths = ["Cargo.toml", "Cargo.lock"]
read_protected_metadata = false
read_protected_plaintext = false
write_working_copy = false
write_plugin_cache = false
network_access = false
emit_audit = true
invoke_tovio_command = false
propose_resolution = false
propose_transform = false
block_operation = true
Bindings pin the installed version
tovio plugin bind always pins the binding to the version that is installed
([bindings.version] exact = "1.2.0"), so a later re-install to a different version does not
silently re-scope it. An at_least floor exists only if you hand-edit the file; keep enforcing
bindings pinned so CI, Forge, and every developer laptop run the same code for the same gate.
See Bindings for scope and precedence rules.
For a policy-integrated enforcing binding (the hook surface for required checks):
$ tovio policy hook add pre-land com.example.license-check \
--required \
--paths "**" \
--branches main
3. Run it¶
Bindings run automatically when the event fires:
A pass prints nothing extra. A warning, fail, or error is echoed as
plugin <id> (<mode>) → <status>: <summary> followed by its findings, and a required enforcing
fail refuses the land with TVO-PLUGIN-009 (see below).
You can also invoke a bound plugin explicitly:
run needs a binding for the event — the mode, required flag, and capability grant all come from it —
and it re-verifies the artifact before executing. --dry-run reports what would block without gating
anything, and --json gives you the structured result (status, summary, findings, sandbox runtime, and
whether it blocked or would block).
plugin run sends an empty payload
It builds a schema-valid input envelope with no changed paths and no target — enough to prove the
module loads, speaks the protocol under its real grant, and returns a well-formed result. It is not a
content check. A guest that scans changed paths sees none, and one that fails closed on an
undecodable payload (as the SDK's harness does) reports status = "error". To exercise a plugin over
real content, drive its actual event — tovio commit for pre-snapshot, tovio land for pre-land.
See the CLI reference for every flag.
When a plugin blocks you¶
Enforcing plugins produce a structured, explainable block. It always tells you which plugin, which binding required it, what failed, what to do next, and whether override is possible.
✗ Land blocked by required plugin: com.example.license-check
A REQUIRED enforcing `pre-land` plugin blocked the land (sandboxed-lifecycle-plugins.md §14/§57,
REQ-PLUGIN-054/055/065). The operation was gated BEFORE it took effect — nothing changed.
Plugin: com.example.license-check v1.2.0
Binding: com.example.license-check@pre-land (LOCAL, enforcing, required)
Result: fail (enforcing fail)
Why: One dependency violates repository license policy.
[error] Dependency xyz uses a prohibited license. (Cargo.toml)
This was a LOCAL enforcing binding — not a CI-side or Forge-side gate. Override IS possible for the
repo owner (this is a local binding, not org policy).
Fix what the plugin flagged (see the findings above), then re-run `tovio land`
Or inspect the enforcing binding that required it:
tovio plugin bindings
Owner override: drop the local enforcing binding, or re-bind it advisory:
tovio plugin unbind <plugin-id> --event pre-land
[TVO-PLUGIN-009]
Common resolutions, by cause:
| Cause | What to do |
|---|---|
Plugin returned fail |
Fix what the finding describes. code and remediation guide you. |
Plugin returned error |
The plugin's own summary says why — that is all you get; the sandbox captures guest stderr for bounding and never surfaces it. tovio plugin doctor checks the store. |
Plugin timed out (TVO-PLUGIN-007) |
Retry once; if consistent, raise timeout_ms in the manifest or file a plugin bug. |
Plugin unavailable (TVO-PLUGIN-010) |
Reinstall or update (or rebuild tovio with plugins-wasm if the sandbox is missing); the repo owner can drop or re-bind the local binding advisory meanwhile. |
Integrity check failed (TVO-PLUGIN-003) |
Do not override. Report to the plugin publisher — the artifact does not match its hash or signature. |
Capability denied (TVO-PLUGIN-005) |
The manifest asks for more than the binding grants. Update the binding or the manifest. |
See the full error catalog.
Lifting an enforcing binding¶
Local advisory bindings are trivially overridden — you already have the warning, and the operation already ran.
Enforcing bindings have no override flag on tovio land or tovio commit. A local enforcing
binding belongs to the repository owner, who lifts it by dropping the binding or re-binding it
advisory:
$ tovio plugin unbind com.example.license-check --event pre-land
$ tovio plugin bind com.example.license-check --event pre-land --mode advisory
The block itself is already in the audit trail as a signed PluginBlocked entry, so the sequence
"blocked, then unbound, then landed" is visible to anyone who verifies the chain.
On the Forge, a required plugin is a plugin:<id> check on the proposal. The land gate accepts
only a server-recorded, revision-stamped success posted by a policy-approved attester — a local
pass is never trusted (TVO-PLUGIN-015), and the org plugin policy decides which plugins may back an
enforcing binding at all (TVO-PLUGIN-014). Lifting such a gate is a forge-admin's change to the
proposal's required set or to the served plugin policy, not a client-side flag.
Audit — what gets recorded¶
An enforcing execution that blocks writes a signed PluginBlocked entry to the acting identity's
audit chain, carrying:
- the plugin id, version, and the binding id that required it (
<plugin-id>@<event>), - the acting identity, the binding mode, and the target change or ref,
- the blocking status, the finding count, the input and output hashes, the timestamp, and the sandbox runtime label,
- the effective capability name set granted (names only — never globs, content, or plaintext),
- whether the gated operation touched protected paths (metadata only; plaintext is never marshalled).
A pass writes nothing — the entry exists to record the block, not the run. A non-blocking warning writes
nothing either, so it is printed to stderr even under --json and --quiet, which is the only record it
gets. A Simple repo has no signing identity and therefore no chain: the block still gates the operation,
but nothing is written.
Verify the chain with tovio audit verify.
Where to go next¶
- Bindings — scopes, path/branch filters, precedence.
- Capabilities & security — what the plugin can and cannot see.
- Errors — recovery for every
TVO-PLUGIN-*code. - Write a plugin — build your own.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure