{"slug": "run-coding-agents-in-herdr-on-a-lens-agents-sandbox", "title": "Run coding agents in herdr on a Lens Agents sandbox", "summary": "Herdr, a terminal workspace manager for AI coding agents, can now be attached over SSH to a Lens Agents sandbox via `herdr --remote`, using `nexusctl sandbox ssh-proxy` as an OpenSSH ProxyCommand so no sshd or open port is needed in the sandbox image. The setup requires Lens Agents 0.34.0 or later, the `nexusctl` CLI logged in via `nexusctl auth login`, the ADMIN role on the project hosting the sandbox, herdr on the local laptop, and a fine-grained GitHub token scoped to only the repositories the agents work on. The result keeps Claude Code and Codex running in a sandbox with default-deny egress and secrets they never see, while the user works in herdr locally and agents persist across detach and reattach.", "body_md": "**TL;DR**\n\n- [herdr](https://herdr.dev) is a terminal workspace manager for AI coding agents. Its panes run in a background server, and`herdr --remote` attaches to that server over SSH.\n- A Lens Agents sandbox speaks SSH through `nexusctl` , so it can be that remote. No sshd in the image, no open port.\n- The result: you work in herdr on your laptop, while Claude Code and Codex run in a sandbox with default-deny egress and secrets they never see.\n\nRunning several coding agents at once has two practical problems. You want them to keep working when you close the laptop, and you don't want them running on your laptop with your credentials and an open network.\n\nherdr solves the first one. A Lens Agents sandbox solves the second. This post connects them.\n\n## How the pieces fit\n\nherdr keeps every pane in a server process. When you run `herdr --remote <host>`, the server runs on the remote host and owns the agents, while your local herdr draws the UI: sidebar, theme, and your own keybindings. Detach, and the agents keep running. Attach again from any machine, and they're where you left them.\n\n`herdr --remote` uses plain OpenSSH. That's the only thing it needs from the remote side.\n\nA Lens Agents sandbox doesn't run an SSH server. Instead, `nexusctl sandbox ssh-proxy` acts as one on your machine, as an OpenSSH `ProxyCommand`. Each SSH session becomes one exec in the sandbox, authenticated with your Lens Agents login. Shells, commands, PTYs, and window resizes work, so a full-screen TUI like herdr works too.\n\n## What you need\n\n- Lens Agents 0.34.0 or later. SSH access to sandboxes arrived in that release. If you don't have a platform yet, follow the [getting started guide](https://docs.lenshq.io/lens-agents/getting-started/) .\n- `nexusctl` , installed from your Lens Agents platform and logged in with`nexusctl auth login` .\n- The ADMIN role on the project where the sandbox lives. SSH access requires it.\n- herdr on your laptop. See the [herdr install guide](https://herdr.dev/docs/install/) .\n- A [fine-grained GitHub token](https://github.com/settings/personal-access-tokens/new) with access to only the repositories the agents work on.\n\n## Step 1: Define the sandbox\n\nThe sandbox is a home directory on a persistent volume, with the agent CLIs and herdr installed into it. Everything installs into `$HOME`, so tools, logins, and repositories survive a restart, and the install scripts skip what's already there.\n\n```\nname: Dev Box\nimage: ubuntu:24.04\ncommand: sleep infinity\ncpu: \"4\"\nmemory: 16Gi\n\nuser: ubuntu\nworkdir: /home/ubuntu/work\n\nvolumes:\n  - mountPath: /home/ubuntu\n    size: 30Gi\n\nenv:\n  MISE_YES: \"1\"\n\nscripts:\n  - when: pre-start\n    description: install base packages\n    user: root\n    run: |\n      apt-get update -qq\n      DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \\\n        curl git ca-certificates unzip xz-utils less >/dev/null\n  - when: pre-start\n    description: give the home volume to the agent user\n    user: root\n    run: chown ubuntu:ubuntu /home/ubuntu\n  - when: pre-start\n    description: set up the shell\n    run: |\n      [ -f ~/.bashrc ] || cp -rT /etc/skel ~\n      grep -q 'mise activate' ~/.bashrc || cat >> ~/.bashrc <<'EOF'\n      export PATH=\"$HOME/.local/bin:$PATH\"\n      eval \"$(mise activate bash)\"\n      EOF\n  - when: pre-start\n    description: install mise\n    run: |\n      [ -x ~/.local/bin/mise ] && exit 0\n      curl -fsSL https://mise.run -o /tmp/install-mise.sh\n      sh /tmp/install-mise.sh\n  - when: pre-start\n    description: install Claude Code\n    run: |\n      [ -x ~/.local/bin/claude ] && exit 0\n      curl -fsSL https://claude.ai/install.sh -o /tmp/install-claude.sh\n      bash /tmp/install-claude.sh\n  - when: pre-start\n    description: install Codex\n    run: |\n      ~/.local/bin/mise which codex >/dev/null 2>&1 && exit 0\n      ~/.local/bin/mise use -g codex@latest\n  - when: pre-start\n    description: install herdr\n    run: |\n      [ -x ~/.local/bin/herdr ] && exit 0\n      curl -fsSL https://herdr.dev/install.sh -o /tmp/install-herdr.sh\n      sh /tmp/install-herdr.sh\n  - when: pre-start\n    description: open herdr panes in bash\n    run: |\n      [ -f ~/.config/herdr/config.toml ] && exit 0\n      mkdir -p ~/.config/herdr\n      printf '[terminal]\\ndefault_shell = \"/bin/bash\"\\n' > ~/.config/herdr/config.toml\n\npolicy:\n  name: Dev Box\n  networkDefaultVerdict: deny\n  allowedDomains:\n    - { pattern: github.com, verdict: allow, transport: direct }\n    - { pattern: \"*.github.com\", verdict: allow, transport: direct }\n    - { pattern: \"*.githubusercontent.com\", verdict: allow, transport: direct }\n    - { pattern: api.anthropic.com, verdict: allow, transport: direct }\n    - { pattern: console.anthropic.com, verdict: allow, transport: direct }\n    - { pattern: claude.ai, verdict: allow, transport: direct }\n    - { pattern: platform.claude.com, verdict: allow, transport: direct }\n    - { pattern: api.openai.com, verdict: allow, transport: direct }\n    - { pattern: auth.openai.com, verdict: allow, transport: direct }\n    - { pattern: chatgpt.com, verdict: allow, transport: direct }\n    - pattern: \"*\"\n      verdict: allow\n      transport: direct\n      description: Read-only internet\n      rules:\n        - { method: GET }\n        - { method: HEAD }\n  credentials:\n    - { credentialName: gh-token, envVarKey: GH_TOKEN }\n\ncredentials:\n  gh-token:\n    value: ${GH_TOKEN}\n    injections:\n      - domain: api.github.com\n        headerName: Authorization\n        headerFormat: \"Bearer {value}\"\n      - domain: github.com\n        headerName: Authorization\n        headerFormat: \"Basic {base64:x-access-token:{value}}\"\n```\n\nA few things to note:\n\n- **`command: sleep infinity`** keeps the sandbox up. herdr starts its own server the first time you attach, and that server keeps running after the SSH session ends.\n- **`ubuntu:24.04`** is the official image. It has no curl or git, so the first script installs them as root. The agent user must exist in the image, and`ubuntu` does.\n- **herdr goes to `~/.local/bin`** , because that is where` herdr --remote` looks for it. The last script makes herdr open panes in bash, so`~/.bashrc` puts the mise tools on the`PATH` .\n- **The policy denies by default.** The agents can reach GitHub and their model providers. For the rest of the internet, only`GET` and`HEAD` pass, which cuts off the obvious upload paths.\n- **The GitHub token never enters the sandbox.**`$GH_TOKEN` inside the sandbox holds a placeholder. The real token is added to requests to GitHub at the platform boundary. The agents can still push to any repository the token can reach, so scope it to the repositories they need.\n\n## Step 2: Create the sandbox\n\n```\nread -rs GH_TOKEN && export GH_TOKEN\nnexusctl sandbox create --project <org>/<project> -f sandbox.yaml\n```\n\nPaste the token at the prompt. `read -s` keeps it off the screen and out of your shell history, and `create` expands `${GH_TOKEN}` from your environment. The first start runs the install scripts, so give it a minute.\n\n## Step 3: Make the sandbox an SSH host\n\nLet `nexusctl` write the SSH config entry for every sandbox in the project:\n\n```\nnexusctl sandbox ssh-config --project <org>/<project> >> ~/.ssh/config\n```\n\nThe entry matches `*.<project>.<org>.nexus` and routes through `ssh-proxy`:\n\n```\nHost *.<project>.<org>.nexus\n  User sandbox\n  ProxyCommand nexusctl sandbox ssh-proxy %n --project <org>/<project> --server <url>\n  StrictHostKeyChecking no\n  UserKnownHostsFile /dev/null\n  LogLevel ERROR\n```\n\nHost key checking is off because the proxy is a local process with a throwaway key. Trust comes from your Lens Agents login and TLS to the platform, not from the SSH host key.\n\nCheck that plain SSH works first. It's the first thing herdr's own troubleshooting asks you to check:\n\n```\nssh <sandbox>.<project>.<org>.nexus '~/.local/bin/herdr --version'\n```\n\n## Step 4: Attach with herdr\n\n```\nherdr --remote <sandbox>.<project>.<org>.nexus\n```\n\nherdr finds the binary in `~/.local/bin`, starts the server in the sandbox, and opens the workspace. Open a pane, run `claude` or `codex`, and log in once. The login is stored on the volume, so you do it only once.\n\nTo let herdr show when an agent is working, waiting for you, or done, install its integration inside the sandbox:\n\n```\nherdr integration install claude\nherdr integration install codex\n```\n\nDetach with `ctrl+b q`. The agents keep running in the sandbox. Run the same `herdr --remote` command later, from this laptop or another one, to pick up where you left off.\n\n### Keep the sandbox next to your local work\n\nIf you also run herdr locally, save the sandbox as a machine instead:\n\n```\nherdr machine add <sandbox>.<project>.<org>.nexus --label \"Lens Agents\"\n```\n\nThe sandbox then shows in the herdr sidebar next to Local, and the agent list covers both, so you can see which agent needs an answer wherever it runs.\n\n## What the sandbox adds\n\nherdr gives you persistence and one place to watch every agent. The sandbox decides what those agents can do:\n\n- **Off your laptop.** The agents run in the sandbox, so they can't read your files, SSH keys, or cloud credentials.\n- **Default-deny egress** with per-domain, per-method rules, enforced outside the agent.\n- **No secrets in the sandbox.** Secrets that you store in Lens Agents, such as the GitHub token, reach the agents only as placeholders. The real values are added at the boundary. Your Claude and ChatGPT logins are different: they are stored on the sandbox volume, as on any machine where you log in.\n- **Audit.** Every request the sandbox proxy handles is recorded, with the policy decision.\n\nYour workflow stays the same: the same herdr, the same agents, the same keybindings. Only the place the agents run changes.\n\nFor why these controls matter, read [how to run Hermes Agent securely](https://lenshq.io/blog/how-to-run-hermes-agent-securely/) and [the OpenClaw fallout](https://lenshq.io/blog/banning-ungoverned-ai-agents/).\n\n## Run your coding agents governed\n\nRun Claude Code, Codex and other compatible agents with identity, policy, and audit built in.\n\n## FAQ\n\n**Does the sandbox need an SSH server?**\n\nNo. `nexusctl sandbox ssh-proxy` acts as the SSH server on your machine and turns each SSH session into an exec in the sandbox.\n\n**Do my agents stop when I close the laptop?**\n\nNo. The herdr server and its panes run in the sandbox. Closing the laptop only disconnects the UI.\n\n**Can I use SSH agent forwarding for Git inside the sandbox?**\n\nNo. The proxy refuses agent forwarding. Use a token credential instead, as in the example: the sandbox sees a placeholder and the platform adds the real token to requests to GitHub.\n\n**What happens to my work when the sandbox restarts?**\n\nThe home volume stays, so repositories, tool installs, and agent logins stay too. Running processes, including the herdr server, start again from nothing, and herdr restores the saved layout of its session.", "url": "https://wpnews.pro/news/run-coding-agents-in-herdr-on-a-lens-agents-sandbox", "canonical_source": "https://lenshq.io/blog/herdr-on-lens-agents/", "published_at": "2026-10-27 00:00:00+00:00", "updated_at": "2026-10-29 07:17:37.476506+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-safety"], "entities": ["herdr", "Lens Agents", "nexusctl", "Claude Code", "Codex", "GitHub", "OpenSSH", "mise"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/run-coding-agents-in-herdr-on-a-lens-agents-sandbox", "markdown": "https://wpnews.pro/news/run-coding-agents-in-herdr-on-a-lens-agents-sandbox.md", "text": "https://wpnews.pro/news/run-coding-agents-in-herdr-on-a-lens-agents-sandbox.txt", "jsonld": "https://wpnews.pro/news/run-coding-agents-in-herdr-on-a-lens-agents-sandbox.jsonld"}}