Herdr OpenShell provisions one OpenShell sandbox for each eligible Herdr
worktree, checks out the matching Git branch in /sandbox/project, and starts
OpenCode directly in the worktree's focused pane.
Prototype: Automation is intentionally restricted to
nemo-platform. MERGE BLOCKER: Replace the hard-coded repository allowlist with an explicit user-configurable policy before merging or distributing this plugin.
OpenShell needs a gateway to create and manage sandboxes. Read the OpenShell installation guide for the supported Docker, Podman, MicroVM, and Kubernetes configurations.
For the quickest local Docker-backed setup, start Docker Desktop or Docker Engine, then install OpenShell and verify the gateway with one command:
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | sh && openshell statusThe installer installs the CLI and local gateway service. The gateway auto-detects the running Docker engine. To run the gateway itself in a container, follow the Docker Compose gateway guide.
The plugin uses a GitHub provider to clone the repository inside each sandbox.
Create a token with access to the repository, then create the provider named in
config.example.json:
GITHUB_TOKEN=<your-token> openshell provider create \
--name nemo-platform-github \
--type github \
--from-existingVerify that the gateway and provider are available:
openshell status
openshell provider listSee Manage Providers for credential-provider details.
Install Herdr 0.7.0 or newer if it is not already installed:
curl -fsSL https://herdr.dev/install.sh | shSee the Herdr installation guide for other installation methods.
From this repository, build and link the plugin, then locate its configuration directory:
cargo install --path . --root .herdr --locked --force
herdr plugin link "$PWD"
herdr plugin config-dir local.herdr-openshellHerdr runs .herdr/bin/herdr-openshell for hooks and actions. GitHub plugin
installation runs the same Cargo release build through the manifest's build
step; local plugin link intentionally does not run build steps.
Place a reviewed copy of config.example.json at config.json in the printed
directory. Update these machine-specific fields:
Configurations from plugin versions before the SDK migration may retain an
openshell_bin field. The plugin ignores that deprecated field; remove it when
updating the configuration because the plugin no longer invokes the OpenShell
executable.
| Field | Value |
|---|---|
gateway |
Gateway name from openshell gateway list. |
github_provider |
GitHub provider name created above. |
allowed_repo_key |
Exact repository key, normally the absolute Git common directory from git rev-parse --path-format=absolute --git-common-dir. |
remote_url |
Canonical Git remote to clone. HTTPS and SSH forms of the same remote compare as equal. |
base_branch |
Remote branch cloned before the worktree branch is created. |
Confirm that Herdr registered the plugin:
herdr plugin list --plugin local.herdr-openshell --json
herdr plugin action list --plugin local.herdr-openshellCreate a Herdr worktree from a committed, remote-reachable base. The
worktree.created hook automatically creates the sandbox, clones the exact host
commit, and starts OpenCode:
herdr worktree create \
--cwd /path/to/nemo-platform \
--branch your-name/your-branch \
--base origin/mainHerdr opens a new workspace for the worktree. When provisioning finishes, its root pane contains OpenCode running inside the corresponding OpenShell sandbox.
Check the hook and sandbox state from another pane:
herdr plugin log list --plugin local.herdr-openshell
openshell sandbox list --selector "herdr.integration=prototype,herdr.repo=nemo-platform"The worktree.created hook is the normal entry point. The plugin also exposes
three actions in an eligible worktree workspace:
herdr plugin action invoke local.herdr-openshell.provision
herdr plugin action invoke local.herdr-openshell.start
herdr plugin action invoke local.herdr-openshell.status| Action | Behavior |
|---|---|
provision |
Create or adopt the worktree's sandbox, verify its checkout, and start OpenCode. |
start |
Verify and use an existing sandbox, then start OpenCode. It never creates a sandbox. |
status |
Report the sandbox selected by the worktree identity labels. |
Actions use the current Herdr workspace context. Run them from the worktree workspace they should target.
Removing a worktree with herdr worktree remove emits worktree.removed. The
plugin deletes the sandbox recorded for that exact repository, branch, and
workspace, then removes its state mapping. Cleanup is idempotent when the
sandbox or mapping is already absent. It never deletes the host worktree;
Herdr owns that lifecycle.
The sandbox identity derives from the Herdr repository key and Git branch. The plugin labels each sandbox with the integration, repository, branch hash, and workspace. Repeated provisioning adopts the one matching sandbox; multiple matches stop automation instead of guessing.
The sandbox checkout must satisfy all of these conditions:
- The configured repository key matches the Herdr worktree.
- The canonical host and configured Git remotes match.
- The sandbox origin and branch match the configuration and Herdr worktree.
- The host commit is available from the configured remote.
- The host commit remains an ancestor of the sandbox branch, allowing commits made by the sandboxed agent while rejecting a divergent checkout.
Only committed, remote-reachable Git state is reproduced. Dirty files and unpushed commits are not transferred. Provisioning fails closed when the host commit cannot be obtained from the configured remote.
- Automation is hard-coded to the
nemo-platformrepository name and also requires the configured repository key and remote to match. - A per-worktree process lock serializes event and action invocations.
- Runtime mappings are written atomically under
HERDR_PLUGIN_STATE_DIR. - Duplicate matching sandboxes stop automation instead of guessing.
- Cleanup requires both an exact persisted mapping and matching live sandbox identity labels, including the original Herdr workspace.
- Failed or partial sandboxes without a complete ownership mapping remain available for inspection.
- A failed OpenShell deletion preserves the mapping and appears in Herdr's plugin logs instead of being treated as success.
- The plugin refuses to replace a pane running an agent or another foreground process.
- Event replay adopts the existing sandbox and does not relaunch an already running OpenCode process.
The plugin manages sandboxes through the OpenShell Rust SDK. It does not invoke
the openshell executable for list, create, wait, exec, attach, or delete
operations. Host subprocesses are limited to Git inspection and Herdr pane
commands; git, gh, and opencode commands that operate on the sandbox run
through SDK exec RPCs.
openshell-sdk and openshell-bootstrap are pinned to OpenShell revision
8cf2673c0ba26852102b340c79e94af9a51b25b6. Building with --locked therefore
requires network access to fetch that Git revision unless it is already in the
Cargo cache. The plugin resolves the configured gateway name from OpenShell's
stored gateway metadata.
Supported gateway authentication modes are plaintext HTTP, stored Cloudflare edge JWT, and stored static OIDC access tokens. The SDK does not support mTLS at the pinned revision, so the plugin fails closed for mTLS, missing tokens, and unknown authentication modes. OIDC refresh is not performed; refresh the stored token with OpenShell before it expires.
- Herdr 0.7.0 or newer
- Rust 1.90 or newer to build the plugin
- A reachable OpenShell gateway with a Docker, Podman, MicroVM, or Kubernetes compute driver
- A GitHub provider whose credential is available as
GITHUB_TOKEN git,gh, andopencodein the sandbox image
Inspect recent event and action output:
herdr plugin log list --plugin local.herdr-openshell --limit 20Check gateway connectivity and sandbox state:
openshell status
openshell sandbox list --selector "herdr.integration=prototype,herdr.repo=nemo-platform"These troubleshooting commands use the optional OpenShell CLI. The plugin runtime itself only requires compatible stored gateway metadata and the pinned Rust SDK.
Event hooks run asynchronously and Herdr does not retry a failed cleanup. If a
worktree.removed hook fails, the sandbox and mapping remain for inspection.
Review the plugin log, then delete the named sandbox manually after resolving
the failure:
openshell sandbox delete <sandbox-name>Run formatting, linting, and tests:
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo install --path . --root .herdr --locked --force