A herdr plugin that makes VS Code devcontainers visible to herdr, and lets an agent (Claude Code, OpenCode, etc.) running inside one show up as a normal, tracked herdr agent, the same as one running on the host.
herdr plugin install scott-the-programmer/vscode-devcontainers-herdrThat's it for installation. Nothing to configure. Now open a herdr pane in any project
that has a .devcontainer/. herdr detects the container's state (running / stopped /
none) automatically and reports it back, ready to show in the sidebar once you add the
$devcontainer token to a row:
# ~/.config/herdr/config.toml
ui.sidebar.agents.rows = [["state_icon", "workspace", "tab"], ["agent", "$devcontainer"]]To run an agent inside that container while herdr still tracks the pane, use exec from
that same pane:
herdr-devcontainer-status exec claudeThis starts claude inside the devcontainer, wired up so herdr sees it exactly like an
agent on the host, with the same idle/working/done status.
For the full walkthrough (finding the binary's install path, aliases worth adding,
troubleshooting), see USAGE.md.
herdr shows panes and Docker shows containers, but nothing connects the two by default. An agent running inside a devcontainer is invisible to herdr. This plugin fixes that: it reports the container's status and tracks agent sessions running inside it.
One host binary, three jobs:
| subcommand | runs on | what it does |
|---|---|---|
refresh / hook |
host | detects the pane project's devcontainer state and reports it to herdr |
exec [--agent <name>] <cmd> |
host | runs <cmd> inside the container, carrying the pane's herdr identity in so herdr tracks it |
bridge / relay |
host / container | the plumbing that lets the container side talk back to herdr's control socket on the host |
exec works against any project's devcontainer, not just checkouts of this repo. It
pushes a small statically-linked (musl) copy of itself into the target container the first
time it's needed, so nothing needs to be installed there ahead of time, and the target
project doesn't need a Rust toolchain or any relation to this crate at all.
See .devcontainer/README.md if you want the internals of how
the two ends of the relay fit together.
For this project, bin/devco records each worktree's workspace manager, id, and provider
under ~/.local/state/revaesor/devco/. The plugin reads these records and shows Docker and
SSH workspace states. Add $devsy to a Herdr sidebar row to see the provider summary next to
$devcontainer:
ui.sidebar.agents.rows = [["state_icon", "workspace", "tab"], ["agent", "$devcontainer", "$devsy"]]The plugin also keeps the $devpod token for older sidebar configurations. Press Ctrl+B, then
Shift+B to open the current worktree's URL in a new Chrome tab.
To run an agent in a Devsy workspace, set HERDR_DEVSY_PROVIDER before calling exec:
HERDR_PANE_CWD="$PWD" HERDR_DEVSY_PROVIDER=ssh herdr-devcontainer-status exec piUse docker or ssh as the provider value. Devsy adds an SSH host alias for each workspace.
The plugin uses that host alias to start Pi with a terminal. It also opens a reverse SSH tunnel
for Herdr's agent-state relay. The tunnel only lasts for the agent session. It does not expose
Herdr's control socket on the network. Configure the Devsy providers before you use them.
- Docker, running
- The
devcontainerCLI:npm i -g @devcontainers/cli - Rust, only needed if there's no prebuilt binary for your platform (macOS/Linux, x86_64/arm64), or you're building from a checkout
herdr plugin install clones this repo and runs ./build.sh, which downloads the release
binary matching that checkout's version for your platform, verifies it against the
release's SHA256SUMS, and installs it. If no prebuilt binary covers your platform, or the
download fails, it falls back to cargo build (needs Rust). SHA256SUMS is an integrity
check, not provenance; releases also carry a
build provenance attestation,
verifiable out of band with
gh attestation verify --repo scott-the-programmer/vscode-devcontainers-herdr <tarball>.
The defaults assume Docker Desktop, which forwards host.docker.internal to the
host's loopback. Plain Linux Docker does neither, so point both ends at the
docker bridge:
# in the herdr pane, or your shell profile
export HERDR_RELAY_HOST=172.17.0.1 # exec forwards this into the container
export HERDR_BRIDGE_BIND=172.17.0.1 # the host bridge listens here tooUse your own docker bridge address — ip -4 addr show docker0 — which is also
what --add-host=host.docker.internal:host-gateway resolves to, if you would
rather keep the name and set only HERDR_BRIDGE_BIND.
Set both or neither. Moving only the container end leaves the bridge on
loopback, which turns a name-resolution failure into a refused connection, and
moving only the host end leaves the container dialling a name that does not
resolve. ~/.herdr/relay.log inside the container names which one you have:
relay: host.docker.internal:47100: failed to lookup address information # no name
relay: 172.17.0.1:47100: Connection refused # bridge still on loopback
HERDR_BRIDGE_BIND widens who can reach herdr's control socket, and that socket
is not authenticated — on the docker bridge address, every container on that
bridge can reach it. That is why it is opt-in and why the default stays on
loopback.
Note that none of this affects whether the agent appears in herdr. Pane identity is passed inward as environment at exec time and needs no socket, so a broken relay looks like a working session whose state is screen-detected rather than reported. That is the failure this is easy to miss.
The binary always runs on the host. Never build or run it inside the container.
./build.sh --source # compile to ./bin/herdr-devcontainer-status (what `make build` runs)
herdr plugin link "$(pwd)" # use this checkout instead of an installed copy./build.sh with no flags tries a prebuilt download first, same as herdr plugin install.
HERDR_PLUGIN_BUILD=prebuilt ./build.sh is useful for debugging just that path.
This repo carries a .devcontainer/ of its own, so it doubles as the test fixture. From a
herdr pane here:
make up # create/start the devcontainer
make status # what the plugin reports for this pane
make claude # run Claude in the container, visible in `herdr agent list`
make shell # shell in, claiming the pane as an agent
make test # cargo testmake help lists the rest (rebuild, reshell, relay, clean, e2e, …), and
.devcontainer/README.md covers the container in depth:
credential seeding, the bridge/relay design, and the security tradeoffs of mounting host
credentials into a container.
Maintainers cutting a release should see docs/RELEASING.md.
MIT