Load a plugin
rev0 can load declarative commands and sandboxed HTML from independent sources. A source is either a local folder (a Git checkout, a mounted volume, or a directory an agent generated) or a repo URL the board clones for you. What a plugin may declare is in Plugin contract.
The board never writes inside a folder you point it at. It does not clone, pull,
install, copy, commit, or generate anything there. Everything the board writes lives under
~/.rev0/plugins/, a directory it owns and created for this purpose, and it writes there
only when you press Fetch or Update on a repo source. A repo source is pinned to
one commit: it never moves on its own.
Add a source
Section titled “Add a source”Add a source under External plugins in Settings > Connections > Integrations. The
path field autocompletes as you type, drilling into folders and offering the repos
discovered under repos_root, so a local source is picked rather than pasted. An invalid
plugin publishes nothing and shows its failure beside the configured source.
Each configured source carries Load, Unload and Reload:
- Unload adds the path to
plugin_disabled_paths. The source stays configured and visible, but the board stops reading it, so its commands and UI surfaces disappear. - Load removes it from that list again. Because the path never left
plugin_paths, switching a source back on never means finding it a second time. - Reload re-reads the source from disk. The catalogue is rebuilt per request with no cache, so this only refreshes the view (use it after editing a manifest).
Removing the path removes every contribution on the next refresh; that is the permanent
action, and unload is the reversible one. When board authentication is enabled, only a
logged-in browser session can change any of these through PUT /config: unloading
decides whether a source publishes commands and UI, and a pin decides WHICH code it
publishes, so an agent token must not reach them.
Add a repo source
Section titled “Add a repo source”A source may be a repository URL instead of a path:
https://github.com/owner/repo the repo root, at the default branchhttps://github.com/owner/repo@v1.2 pinned to a tag (or branch, or commit)https://github.com/owner/repo@main#bundles/whale one bundle inside a monorepogit@github.com:owner/repo#whale-pet scp form works too#<subdir> is what makes a monorepo usable: a collection of bundles usually has no
plugin.toml at its root, so each bundle is configured as its own source pointing at its
own subdirectory. The subdirectory is relative to the repository root and cannot leave it.
The board clones into ~/.rev0/plugins/<host>/<owner>/<repo>@<ref>. The ref is part of
the directory name, so two refs of one repository never share a checkout. Clones are
shallow (--depth 1), with no tags, no submodules and no LFS. Private repositories use
the credentials this machine already has (your ssh agent, or whatever gh auth
configured), and the board stores no tokens of its own.
Fetch and update
Section titled “Fetch and update”Fetching is never implicit. GET /plugins only ever reads the filesystem, so an
ordinary poll can never hang on a slow remote. Two buttons touch the network, both on the
source’s own row:
- Fetch clones a source that has none yet and records the commit it resolved to.
- Update moves the pin: it fetches the ref again, checks out whatever it now points at, and reports which contributions were added or removed by that move.
Until you press one of those, the source keeps serving the exact commit it is pinned to. That is the point: “why did the board behave differently today?” always has an answer.
A source that has never been fetched, or whose fetch failed, reads failed with the reason git gave: being offline looks like being offline, not like a corrupt bundle. A failed update changes nothing: the working checkout and its pin stay exactly as they were, and the error goes to a toast rather than knocking a loaded plugin out.
Removing a repo source drops its pin but leaves the cached checkout on disk, so re-adding it is instant and works offline.
A repo source is never trusted to run code by default, and moving a pin asks again
(see Plugin trust). A pin is an approval of one specific
commit; if Update silently inherited it, “update the plugin” would quietly mean “run new
code under the old approval”. The changed list Update reports names hooks arriving or
leaving, so that question can be asked with evidence.
Let an agent load a plugin it built
Section titled “Let an agent load a plugin it built”An agent that has just written a plugin can activate it itself with the
set_plugin_source MCP tool, naming the bundle’s absolute folder (or a repo URL) and one
state:
loadedpublishes it,unloadedkeeps it configured but stops running it, andremovedforgets the source. They are the same two lists the Settings buttons write, so an agent and a human are never operating two different lifecycles.- The tool asks every time. The first call parks the ticket with an approve or decline panel naming the path, the plugin id and every command, UI surface and action the bundle would publish; approving re-invokes the agent, which calls again to apply it.
- The approval is of the ARGUMENTS, not of the tool: it covers that one path in that one direction. Loading a second plugin, or later removing the same one, asks again. An “Always allow this tool” click carries no path and so authorizes nothing here.
- A bundle that does not parse is refused before anyone is asked, so a broken plugin never becomes something a human has to judge, and never half-registers itself.
- A repo URL is the one case with nothing to parse yet, because fetching it before the grant would be doing the very thing being approved. The panel names the repository, ref and subdirectory instead, and approving it clones once and pins the commit. What that clone turns out to contain shows up on the source’s row afterwards.
The write goes through POST /plugins/sources, which authorizes against that permission
grant rather than the caller’s token. PUT /config is unchanged and stays browser-only.
An agent can also request trust for a bundle’s code by passing trust_hooks; that is a
separate approval, described in Plugin trust.