Managing Herdr Plugins
Manage Herdr plugins without running unreviewed code and keep Adam's local
forks, mr inventory, Stow-managed registry, and Org notes consistent.
Non-negotiable rules
Audit before execution. Read
the security audit procedure completely and
audit the exact source snapshot before running a build, package install,
test, plugin action, herdr plugin install, or herdr plugin link.
Treat a plugin as arbitrary code running as the user. Herdr does not sandbox
plugins, and the marketplace is an automatic, unreviewed GitHub index.
An audit reduces risk; it cannot prove that code is non-malicious. Do not
install when the audit is incomplete, a binary is unverifiable, dependencies
are unpinned, or a critical finding remains.
Present the audit report, exact commit SHA, and residual risks, then obtain
the user's explicit approval before first execution or installation.
Never edit ~/.config/herdr/plugins.json manually. Herdr rewrites it, often
replacing its Stow symlink. After every install, reinstall, link, unlink,
enable, disable, or uninstall, run:
stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config
Use GNU Stow, never ln, for Herdr configuration links.
A fork used for local development belongs under ~/.GIT/3rd-party, must be
registered in ~/.config/mr/groups.d/26-AI, and must be documented in
~/org/notes/herdr.org.
Before editing another repository (~/.config/mr, ~/org, or a plugin),
read that repository's agent instructions and inspect its Git status. Do not
overwrite unrelated changes.
Never stage, commit, or rewrite history in ~/org. Edit the required Org
files in the working tree only, then leave them unstaged for the repository's
auto-commit and auto-sync. Do not run git add, git commit, git commit --amend, rebase, reset, or force-push in ~/org. If an existing unmerged
path cannot be cleared without an index update, stop and obtain explicit user
permission before a single file-scoped git add that marks only the resolved
conflict; leave the merge commit to auto-commit.
Decide which workflow applies
- Evaluate or install upstream: audit a detached inspection checkout, then
install the reviewed SHA with Herdr.
- Develop, patch, or contribute: fork upstream, manage the checkout with
mr, audit it, build it, and use herdr plugin link so Herdr executes the
local working tree.
- Refresh an existing plugin: audit all commits and dependency changes since
the previously approved SHA before reinstalling or rebuilding.
Installing a reviewed upstream plugin
plugin install accepts GitHub shorthand only: owner/repo[/subdir...].
Clone without running repository code and record the immutable commit:
audit_dir=$(mktemp -d)
git -c core.hooksPath=/dev/null clone --no-recurse-submodules \
https://github.com/OWNER/REPO.git "$audit_dir/REPO"
git -C "$audit_dir/REPO" checkout --detach REF
reviewed_sha=$(git -C "$audit_dir/REPO" rev-parse HEAD)
Apply the complete security audit to the whole checkout and the plugin
subdirectory. A subdirectory plugin can still be affected by repository-root
build files, workspaces, dependencies, CI, and shared code, so do not audit
only the subdirectory.
Ask for approval using the audit report and reviewed_sha.
Install the exact reviewed revision. Prefer the interactive trust preview;
use --yes only for an already reviewed noninteractive installation:
source=OWNER/REPO
# For a monorepo plugin instead use: source=OWNER/REPO/SUBDIR
herdr plugin install "$source" --ref "$reviewed_sha"
Adopt the generated registry, then verify the registration:
stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config
herdr plugin list --plugin PLUGIN_ID --json | jq .
herdr plugin action list --plugin PLUGIN_ID
There is no plugin update in plugin v1. To update a GitHub-managed plugin,
audit the new revision and reinstall it with a new exact --ref.
Creating and tracking a local fork
Use repository name REPO as the mr entry name unless local conventions
require otherwise.
Create Adam's GitHub fork without cloning into an unmanaged location:
gh repo fork OWNER/REPO --clone=false
Add this stanza to ~/.config/mr/groups.d/26-AI, following the surrounding
ordering and formatting:
[$HOME/.GIT/3rd-party/REPO]
checkout = github_clone
remotes = auto_remotes
lib =
set_git_origin_user OWNER
Add set_git_origin_name REPO only when the checkout directory name and
upstream repository name differ. Do not add automatic build or link fixups:
future updates must be audited before they execute.
Check out and verify the managed repository:
mrco REPO
git -C ~/.GIT/3rd-party/REPO remote -v
git -C ~/.GIT/3rd-party/REPO config remote.pushDefault
Local mr conventions normally retain upstream as origin, add Adam's fork
as github, and add machine remotes. Verify rather than assuming names.
Audit the exact checkout before installing dependencies or building it.
Work on a topic branch, not the upstream default branch:
git -C ~/.GIT/3rd-party/REPO switch -c TYPE/TOPIC
Making Herdr use the local fork
Set plugin_root to the checkout directory containing herdr-plugin.toml; for
a monorepo it may be ~/.GIT/3rd-party/REPO/SUBDIR.
Determine the plugin id from herdr-plugin.toml and inspect any current
registration:
herdr plugin list --plugin PLUGIN_ID --json | jq .
If the same id is GitHub-managed, uninstall it. If it is already local,
unlink it. These commands unregister the plugin; uninstall additionally
removes Herdr's managed GitHub checkout, while unlink leaves local files:
herdr plugin uninstall PLUGIN_ID # GitHub-managed source
# or
herdr plugin unlink PLUGIN_ID # local source
stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config
Build the audited checkout using the repository's documented locked and
reproducible process. plugin link does not run manifest build commands.
Link the working tree and adopt the registry:
plugin_root=$(realpath ~/.GIT/3rd-party/REPO)
# For a monorepo plugin, append /SUBDIR in the realpath command.
herdr plugin link "$plugin_root"
stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config
Prove that Herdr points to the fork rather than a managed upstream clone:
herdr plugin list --plugin PLUGIN_ID --json |
jq -e --arg root "$plugin_root" \
'.result.plugins | length == 1 and
.[0].source.kind == "local" and .[0].plugin_root == $root'
Do not copy fork source into this configuration repository and do not create a
second manifest here. The registry's absolute local path is the source of truth;
mr ensures the checkout occupies the same path on managed machines.
Local development loop
Source/script changes are read from the linked working tree on the next
invocation. Event hooks may execute them without a manual action.
Disable the plugin before pulling, rebasing, switching to, or introducing
unreviewed executable code. Adopt the registry. Audit and rebuild the new
snapshot before relinking/enabling it, then adopt the registry again.
Rebuild compiled plugins after source changes.
Rerun herdr plugin link "$plugin_root" after changing
herdr-plugin.toml, because actions, events, panes, and link handlers are
registered from the manifest. Adopt plugins.json afterward.
Startup hooks do not run merely because a plugin is linked or enabled. Test
them with a deliberate Herdr server restart/handoff after approval.
Exercise narrow entrypoints and inspect logs:
herdr plugin action list --plugin PLUGIN_ID
herdr plugin action invoke PLUGIN_ID.ACTION
herdr plugin pane open --plugin PLUGIN_ID --entrypoint PANE_ID
herdr plugin log list --plugin PLUGIN_ID --limit 50
Run repository tests and linters only after their commands and dependencies
have been audited.
Before each rebuild after pulling/rebasing, review the incoming diff,
dependency/lockfile changes, generated files, and resulting tree. Repeat the
full audit when trust boundaries or executable paths changed.
Contributing changes upstream
- Keep one focused topic branch and include tests/documentation expected by
upstream.
- Review
git diff origin/DEFAULT...HEAD for secrets, unrelated changes, new
executable behavior, and dependency changes.
- Commit and push to the verified writable fork remote (normally
github).
- Create a PR targeting
OWNER/REPO; include what was tested and any security
implications. Obtain user approval before publishing the PR.
- Keep Herdr linked to
~/.GIT/3rd-party/REPO[/SUBDIR] while the PR is open.
Do not reinstall upstream merely because a PR exists.
- After merge, fetch/rebase the fork, audit the merged revision, rebuild, and
relink. To return to Herdr-managed upstream later, unlink the local plugin
and install an audited exact upstream SHA.
Required Org documentation
Keep ~/org/notes/herdr.org's final two top-level sections named
* Useful keybindings and * Installed plugins; do not append a new top-level
section after them.
- Under
* Useful keybindings, update the appropriate table whenever the
plugin adds or changes a binding. Ensure the documented key, config entry,
qualified action id, and actual behavior agree.
- Put only installed or linked plugins under
* Installed plugins.
- Put evaluated but rejected plugins under a separate
* Rejected plugins
section before the final * Useful keybindings and * Installed plugins
sections. Record why installation was rejected.
- Each plugin subsection should contain upstream and fork links, plugin id,
purpose, local checkout/subdirectory, currently reviewed SHA, audit
date/verdict and residual risks, build command,
herdr plugin link command,
useful actions, and PR/issue links as applicable.
- If no keybinding exists, say so in the appropriate plugin subsection rather
than inventing one.
- Leave Org documentation changes unstaged and uncommitted. The
~/org
auto-commit and auto-sync must create and synchronize the commit; never run
staging, commit, or history-rewriting commands there.
Registry and final verification
After any plugin registry mutation:
stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config
readlink ~/.config/herdr/plugins.json
herdr plugin list --plugin PLUGIN_ID --json | jq .
git -C ~/.GIT/adamspiers.org/herdr-config diff -- .config/herdr/plugins.json
git -C ~/.config/mr diff -- groups.d/26-AI
git -C ~/org diff -- notes/herdr.org
Confirm no unrelated registry entries changed, the linked root is under
~/.GIT/3rd-party, the plugin remains enabled only when intended, and all
three repositories contain only expected changes.
Herdr facts and references
- Remote installs run manifest
[[build]] commands; local links do not.
- Commands run as argv arrays unless the manifest explicitly invokes a shell.
- Runtime commands receive the plugin root, config/state directories, context,
socket, and Herdr binary paths. They can call the full Herdr CLI.
- User-editable configuration belongs in
HERDR_PLUGIN_CONFIG_DIR; durable
state belongs in HERDR_PLUGIN_STATE_DIR, not the managed source checkout.
- Official documentation:
Plugins,
CLI reference, and
Marketplace.
1---2name: managing-herdr-plugins3description: Installs, audits, forks, develops, links, updates, and removes Herdr plugins. Use whenever working with Herdr plugins, herdr-plugin.toml, the Herdr marketplace, local plugin forks, plugin PRs, or switching Herdr from an upstream plugin to a local checkout.4---56# Managing Herdr Plugins78Manage Herdr plugins without running unreviewed code and keep Adam's local9forks, `mr` inventory, Stow-managed registry, and Org notes consistent.1011## Non-negotiable rules12131. **Audit before execution.** Read14 [the security audit procedure](references/security-audit.md) completely and15 audit the exact source snapshot before running a build, package install,16 test, plugin action, `herdr plugin install`, or `herdr plugin link`.172. Treat a plugin as arbitrary code running as the user. Herdr does not sandbox18 plugins, and the marketplace is an automatic, unreviewed GitHub index.193. An audit reduces risk; it cannot prove that code is non-malicious. Do not20 install when the audit is incomplete, a binary is unverifiable, dependencies21 are unpinned, or a critical finding remains.224. Present the audit report, exact commit SHA, and residual risks, then obtain23 the user's explicit approval before first execution or installation.245. Never edit `~/.config/herdr/plugins.json` manually. Herdr rewrites it, often25 replacing its Stow symlink. After every install, reinstall, link, unlink,26 enable, disable, or uninstall, run:2728 ```bash29 stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config30 ```31326. Use GNU Stow, never `ln`, for Herdr configuration links.337. A fork used for local development belongs under `~/.GIT/3rd-party`, must be34 registered in `~/.config/mr/groups.d/26-AI`, and must be documented in35 `~/org/notes/herdr.org`.368. Before editing another repository (`~/.config/mr`, `~/org`, or a plugin),37 read that repository's agent instructions and inspect its Git status. Do not38 overwrite unrelated changes.399. **Never stage, commit, or rewrite history in `~/org`.** Edit the required Org40 files in the working tree only, then leave them unstaged for the repository's41 auto-commit and auto-sync. Do not run `git add`, `git commit`, `git commit42 --amend`, rebase, reset, or force-push in `~/org`. If an existing unmerged43 path cannot be cleared without an index update, stop and obtain explicit user44 permission before a single file-scoped `git add` that marks only the resolved45 conflict; leave the merge commit to auto-commit.4647## Decide which workflow applies4849- **Evaluate or install upstream:** audit a detached inspection checkout, then50 install the reviewed SHA with Herdr.51- **Develop, patch, or contribute:** fork upstream, manage the checkout with52 `mr`, audit it, build it, and use `herdr plugin link` so Herdr executes the53 local working tree.54- **Refresh an existing plugin:** audit all commits and dependency changes since55 the previously approved SHA before reinstalling or rebuilding.5657## Installing a reviewed upstream plugin5859`plugin install` accepts GitHub shorthand only: `owner/repo[/subdir...]`.60611. Clone without running repository code and record the immutable commit:6263 ```bash64 audit_dir=$(mktemp -d)65 git -c core.hooksPath=/dev/null clone --no-recurse-submodules \66 https://github.com/OWNER/REPO.git "$audit_dir/REPO"67 git -C "$audit_dir/REPO" checkout --detach REF68 reviewed_sha=$(git -C "$audit_dir/REPO" rev-parse HEAD)69 ```70712. Apply the complete security audit to the whole checkout and the plugin72 subdirectory. A subdirectory plugin can still be affected by repository-root73 build files, workspaces, dependencies, CI, and shared code, so do not audit74 only the subdirectory.753. Ask for approval using the audit report and `reviewed_sha`.764. Install the exact reviewed revision. Prefer the interactive trust preview;77 use `--yes` only for an already reviewed noninteractive installation:7879 ```bash80 source=OWNER/REPO81 # For a monorepo plugin instead use: source=OWNER/REPO/SUBDIR82 herdr plugin install "$source" --ref "$reviewed_sha"83 ```84855. Adopt the generated registry, then verify the registration:8687 ```bash88 stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config89 herdr plugin list --plugin PLUGIN_ID --json | jq .90 herdr plugin action list --plugin PLUGIN_ID91 ```9293There is no `plugin update` in plugin v1. To update a GitHub-managed plugin,94audit the new revision and reinstall it with a new exact `--ref`.9596## Creating and tracking a local fork9798Use repository name `REPO` as the `mr` entry name unless local conventions99require otherwise.1001011. Create Adam's GitHub fork without cloning into an unmanaged location:102103 ```bash104 gh repo fork OWNER/REPO --clone=false105 ```1061072. Add this stanza to `~/.config/mr/groups.d/26-AI`, following the surrounding108 ordering and formatting:109110 ```ini111 [$HOME/.GIT/3rd-party/REPO]112 checkout = github_clone113 remotes = auto_remotes114 lib =115 set_git_origin_user OWNER116 ```117118 Add `set_git_origin_name REPO` only when the checkout directory name and119 upstream repository name differ. Do not add automatic build or link fixups:120 future updates must be audited before they execute.1211223. Check out and verify the managed repository:123124 ```bash125 mrco REPO126 git -C ~/.GIT/3rd-party/REPO remote -v127 git -C ~/.GIT/3rd-party/REPO config remote.pushDefault128 ```129130 Local `mr` conventions normally retain upstream as `origin`, add Adam's fork131 as `github`, and add machine remotes. Verify rather than assuming names.1324. Audit the exact checkout before installing dependencies or building it.1335. Work on a topic branch, not the upstream default branch:134135 ```bash136 git -C ~/.GIT/3rd-party/REPO switch -c TYPE/TOPIC137 ```138139## Making Herdr use the local fork140141Set `plugin_root` to the checkout directory containing `herdr-plugin.toml`; for142a monorepo it may be `~/.GIT/3rd-party/REPO/SUBDIR`.1431441. Determine the plugin id from `herdr-plugin.toml` and inspect any current145 registration:146147 ```bash148 herdr plugin list --plugin PLUGIN_ID --json | jq .149 ```1501512. If the same id is GitHub-managed, uninstall it. If it is already local,152 unlink it. These commands unregister the plugin; `uninstall` additionally153 removes Herdr's managed GitHub checkout, while `unlink` leaves local files:154155 ```bash156 herdr plugin uninstall PLUGIN_ID # GitHub-managed source157 # or158 herdr plugin unlink PLUGIN_ID # local source159 stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config160 ```1611623. Build the audited checkout using the repository's documented locked and163 reproducible process. `plugin link` does **not** run manifest build commands.1644. Link the working tree and adopt the registry:165166 ```bash167 plugin_root=$(realpath ~/.GIT/3rd-party/REPO)168 # For a monorepo plugin, append /SUBDIR in the realpath command.169 herdr plugin link "$plugin_root"170 stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config171 ```1721735. Prove that Herdr points to the fork rather than a managed upstream clone:174175 ```bash176 herdr plugin list --plugin PLUGIN_ID --json |177 jq -e --arg root "$plugin_root" \178 '.result.plugins | length == 1 and179 .[0].source.kind == "local" and .[0].plugin_root == $root'180 ```181182Do not copy fork source into this configuration repository and do not create a183second manifest here. The registry's absolute local path is the source of truth;184`mr` ensures the checkout occupies the same path on managed machines.185186## Local development loop187188- Source/script changes are read from the linked working tree on the next189 invocation. Event hooks may execute them without a manual action.190- Disable the plugin before pulling, rebasing, switching to, or introducing191 unreviewed executable code. Adopt the registry. Audit and rebuild the new192 snapshot before relinking/enabling it, then adopt the registry again.193- Rebuild compiled plugins after source changes.194- Rerun `herdr plugin link "$plugin_root"` after changing195 `herdr-plugin.toml`, because actions, events, panes, and link handlers are196 registered from the manifest. Adopt `plugins.json` afterward.197- Startup hooks do not run merely because a plugin is linked or enabled. Test198 them with a deliberate Herdr server restart/handoff after approval.199- Exercise narrow entrypoints and inspect logs:200201 ```bash202 herdr plugin action list --plugin PLUGIN_ID203 herdr plugin action invoke PLUGIN_ID.ACTION204 herdr plugin pane open --plugin PLUGIN_ID --entrypoint PANE_ID205 herdr plugin log list --plugin PLUGIN_ID --limit 50206 ```207208- Run repository tests and linters only after their commands and dependencies209 have been audited.210- Before each rebuild after pulling/rebasing, review the incoming diff,211 dependency/lockfile changes, generated files, and resulting tree. Repeat the212 full audit when trust boundaries or executable paths changed.213214## Contributing changes upstream2152161. Keep one focused topic branch and include tests/documentation expected by217 upstream.2182. Review `git diff origin/DEFAULT...HEAD` for secrets, unrelated changes, new219 executable behavior, and dependency changes.2203. Commit and push to the verified writable fork remote (normally `github`).2214. Create a PR targeting `OWNER/REPO`; include what was tested and any security222 implications. Obtain user approval before publishing the PR.2235. Keep Herdr linked to `~/.GIT/3rd-party/REPO[/SUBDIR]` while the PR is open.224 Do not reinstall upstream merely because a PR exists.2256. After merge, fetch/rebase the fork, audit the merged revision, rebuild, and226 relink. To return to Herdr-managed upstream later, unlink the local plugin227 and install an audited exact upstream SHA.228229## Required Org documentation230231Keep `~/org/notes/herdr.org`'s final two top-level sections named232`* Useful keybindings` and `* Installed plugins`; do not append a new top-level233section after them.234235- Under `* Useful keybindings`, update the appropriate table whenever the236 plugin adds or changes a binding. Ensure the documented key, config entry,237 qualified action id, and actual behavior agree.238- Put only installed or linked plugins under `* Installed plugins`.239- Put evaluated but rejected plugins under a separate `* Rejected plugins`240 section before the final `* Useful keybindings` and `* Installed plugins`241 sections. Record why installation was rejected.242- Each plugin subsection should contain upstream and fork links, plugin id,243 purpose, local checkout/subdirectory, currently reviewed SHA, audit244 date/verdict and residual risks, build command, `herdr plugin link` command,245 useful actions, and PR/issue links as applicable.246- If no keybinding exists, say so in the appropriate plugin subsection rather247 than inventing one.248- Leave Org documentation changes unstaged and uncommitted. The `~/org`249 auto-commit and auto-sync must create and synchronize the commit; never run250 staging, commit, or history-rewriting commands there.251252## Registry and final verification253254After any plugin registry mutation:255256```bash257stow --adopt -d ~/.STOW -t ~ --no-folding herdr-config258readlink ~/.config/herdr/plugins.json259herdr plugin list --plugin PLUGIN_ID --json | jq .260git -C ~/.GIT/adamspiers.org/herdr-config diff -- .config/herdr/plugins.json261git -C ~/.config/mr diff -- groups.d/26-AI262git -C ~/org diff -- notes/herdr.org263```264265Confirm no unrelated registry entries changed, the linked root is under266`~/.GIT/3rd-party`, the plugin remains enabled only when intended, and all267three repositories contain only expected changes.268269## Herdr facts and references270271- Remote installs run manifest `[[build]]` commands; local links do not.272- Commands run as argv arrays unless the manifest explicitly invokes a shell.273- Runtime commands receive the plugin root, config/state directories, context,274 socket, and Herdr binary paths. They can call the full Herdr CLI.275- User-editable configuration belongs in `HERDR_PLUGIN_CONFIG_DIR`; durable276 state belongs in `HERDR_PLUGIN_STATE_DIR`, not the managed source checkout.277- Official documentation:278 [Plugins](https://herdr.dev/docs/plugins/),279 [CLI reference](https://herdr.dev/docs/cli-reference/), and280 [Marketplace](https://herdr.dev/docs/marketplace/).