SSH Straight Into Your Agent Sandboxes: A Hands-On Look at `sbx ssh`

Share
SSH Straight Into Your Agent Sandboxes: A Hands-On Look at `sbx ssh`

AI coding agents are brilliant right up until the moment they run rm -rf in the
wrong directory, curl a script from who-knows-where, or leak a token you forgot was in your environment. The whole promise of Docker Sandboxes (sbx) is to give those agents a room of their own Docker-native isolation for your filesystem,
network, and tools so they can work in YOLO mode without putting your machine
at risk.

The latest nightly builds add something I've wanted since day one: a proper SSH bridge into your sandboxes. No docker exec gymnastics, no fishing for container IDs. Just:

ssh sbxlab@sbx

Let's unpack what this new sbx ssh command actually does, why it's designed the
way it is, and how I tested it end to end on my own machine.

Everything below was run against sbx v0.34.0-rc1 (nightly). The ssh command
is marked experimental, so expect the flags and behavior to keep evolving.


First, what is sbx?

If you haven't met it yet: Docker Sandboxes creates isolated environments for
AI agents, powered by the same containerization principles 20M+ developers already trust. You point it at an agent and a workspace, and it spins up a locked-down box:

sbx create claude .        # a sandbox for Claude in the current directory
sbx run claude             # create AND drop into it in one step

Inside that box, the agent gets:

  • YOLO mode by default - it works without stopping to ask permission
  • A private Docker daemon for running test containers
  • File access controls between host and sandbox
  • Network access control via policies
  • Compatibility with Claude Code, Codex, Gemini CLI, OpenCode, Cursor, and more

It's vendor-neutral and works with the models and tools you already use. Great. But once an agent is happily churning away inside a sandbox, sooner or later you want in to poke around, debug something, copy a file, or just watch it work. That's
where the new SSH support comes in.


The mental model: sbx ssh is a setup command, not a connect command

Here's the single most important thing to understand, because it trips people up:

sbx ssh does not connect you to anything. It configures your normal SSH
client so that plain old ssh can reach the sandbox daemon's SSH endpoint.

Run it once, and from then on you use the ssh client you already know. The design has three elegant properties:

  1. One host alias. A single Host sbx block is added to your ~/.ssh/config.
    There are no per-sandbox entries and no name prefixes to remember.
  2. The sandbox name is the SSH username. Want to reach the sandbox called
    sbxlab? That's ssh sbxlab@sbx. The part before @ is the sandbox; sbx is
    the single host alias.

A dedicated managed key. It generates a key that's only for sandbox access, separate from your personal identity, stored 0600 and registered where you can audit or revoke it.

So the flow is: provision once, connect forever.

sbx ssh setup   ─────►   writes ~/.ssh/config alias + managed key (idempotent)
                             │
ssh sbxlab@sbx  ─────────────┘   connects, no prompts

Setting it up

The whole thing is a single command:

sbx ssh setup

Here's the actual output from my machine:

Managed key registered: /Users/ajeetraina/Library/Application Support/com.docker.sandboxes/sandboxes/ssh/id_sbx.pub
Host-key verification: live (KnownHostsCommand) — a rotated daemon key won't break connections.
Done. Connect with:
  ssh <sandbox-name>@sbx
Credentials forwarded when set in your shell (ssh.acceptEnv): ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN ANTHROPIC_BASE_URL CLAUDE_CODE_OAUTH_TOKEN OPENAI_API_KEY OPENAI_BASE_URL GEMINI_API_KEY GOOGLE_API_KEY GH_TOKEN GITHUB_TOKEN

Three things jump out of that output, and each one is a nice bit of design:

1. A dedicated, managed key

The key lives under Docker Sandboxes' own config directory:

~/Library/Application Support/com.docker.sandboxes/sandboxes/ssh/id_sbx.pub

It's stored 0600, it's not your personal id_ed25519, and it's registered into
the ssh.authorizedKeys setting where you can see it and remove it. If you'd rather
bring your own keys, set ssh.manageKey=false and manage them yourself.

The command is idempotent ~ re-run sbx ssh setup any time and it won't create duplicate config blocks. Need to rotate the key? sbx ssh setup --regenerate.

2. Live host-key verification (no annoying prompts)

Notice Host-key verification: live (KnownHostsCommand). Instead of pinning a
static host key into known_hosts which breaks the moment the daemon rotates its
key and greets you with that scary REMOTE HOST IDENTIFICATION HAS CHANGED wall of text, it verifies the host key dynamically. A rotated daemon key won't break your connections, and you never get a host-key confirmation prompt.

3. Automatic credential forwarding

This one is genuinely convenient. If you have any of these set in your shell, they're
forwarded into the sandbox via SSH's AcceptEnv:

ANTHROPIC_API_KEY   ANTHROPIC_AUTH_TOKEN   ANTHROPIC_BASE_URL
CLAUDE_CODE_OAUTH_TOKEN   OPENAI_API_KEY   OPENAI_BASE_URL
GEMINI_API_KEY   GOOGLE_API_KEY   GH_TOKEN   GITHUB_TOKEN

So the agent inside the sandbox gets the API keys and GitHub tokens it needs, without you baking them into an image or copying them around. They ride along with your SSH session and vanish when it ends.


Connecting

With setup done, connecting is just ssh. Here's the real session:

$ sbx ls
SANDBOX   AGENT    STATUS    PORTS   WORKSPACE
sbxlab    claude   stopped           /Users/ajeetraina/sbx-lab

$ ssh sbxlab@sbx
Connecting to sandbox "sbxlab"…
agent@sbxlab:~/workspace$

Look closely at what happened there:

  • My sandbox sbxlab was stopped, and SSH transparently started it for me.
  • No password. No passphrase. No host-key prompt. Zero friction.
  • I landed as agent@sbxlab in ~/workspace i.e. inside the sandbox, not on
    my host.

That agent@sbxlab prompt is the proof that isolation is intact. I'm not on my Mac
anymore; I'm in the box.

You can also run one-off commands or copy files over the same endpoint:

ssh sbxlab@sbx -- echo hello       # run a single command and exit
scp ./data.csv sbxlab@sbx:/tmp/    # copy a file in

How I tested it (a repeatable recipe)

"It worked once" isn't a test. Here's the layered approach I'd recommend for anyone kicking the tires on sbx ssh, verify each layer before moving to the next.

0. Back up your SSH config first. Setup edits real files (~/.ssh/config and
your known_hosts machinery), so give yourself an undo button:

cp ~/.ssh/config ~/.ssh/config.bak

1. Provision and inspect the side effects. Don't just trust "Done." ~ look at
what changed:

sbx ssh setup
grep -A8 "Host sbx" ~/.ssh/config
ls -l ~/Library/Application\ Support/com.docker.sandboxes/sandboxes/ssh/

You want to see a clean Host sbx block and a 0600 managed key.

2. Smoke test - the connection must be prompt-free. This is the core promise:

sbx create shell .        # or reuse a sandbox from `sbx ls`
ssh <name>@sbx -- echo hello

If that prints hello with no passphrase and no host-key question, the
managed key and live verification are wired up correctly.

3. Interactive shell. Confirm a stopped sandbox auto-starts and you land
inside it:

ssh <name>@sbx
# expect: agent@<name>:~/workspace$

4. Isolation and negative checks - the part people skip. A happy-path demo isn't a real test. Prove you're actually sandboxed, and that bad input fails cleanly:

ssh <name>@sbx -- 'whoami; hostname'   # should be the sandbox, not your host
ssh doesnotexist@sbx -- echo hi        # a bogus name should fail gracefully

5. Idempotency and rotation. Re-running setup should be a no-op, and rotating the key shouldn't lock you out:

sbx ssh setup               # no duplicate blocks in ~/.ssh/config
sbx ssh setup --regenerate  # rotate the key, then reconnect to confirm

Run through those five and you've genuinely exercised the feature ~ provisioning,
authentication, auto-start, isolation, and key lifecycle, not just the demo.


Where SSH fits in the bigger sbx picture

SSH is one door into the sandbox; here's the rest of the house, so you know which
tool to reach for:

I want to… Command
Make a sandbox sbx create <agent> <path>
Make one and attach sbx run <agent>
Run a command inside sbx exec -it <name> bash
Configure SSH access sbx ssh setupssh <name>@sbx
Copy files in/out sbx cp ./f <name>:/tmp/
Expose a host path sbx mount <name> /path:/workspace/data:ro
Publish a port sbx ports <name> --publish 8080
Control network egress sbx policy allow/deny …
Store credentials sbx secret set <service>
List / stop / remove sbx ls · sbx stop <name> · sbx rm <name>

A handy mental model:

  • create makes it · run makes + attaches · exec runs inside ·
    ssh sets up ssh <name>@sbx
  • stop keeps state · rm deletes · reset wipes everything
  • Isolation knobs: mount (files) · ports (network in) · policy
    (network out) · secret (credentials)

Why this matters

On the surface sbx ssh is a convenience, one alias, one command, you're in. But
the details are where the thoughtfulness shows:

A dedicated managed key means sandbox access is quarantined from your personal identity, and revocable in one place.

Live host-key verification kills the single most common source of SSH friction:
the "identification has changed" wall after a key rotation.

Scoped credential forwarding hands agents exactly the tokens they need for the life of a session, and no longer.

The username-is-the-sandbox trick means your ~/.ssh/config stays clean no
matter how many sandboxes you spin up.

Add it all up and you get the thing SSH always should have been for ephemeral
environments: secure by default, and completely out of your way.


Try it yourself

# macOS
brew install docker/tap/sbx@nightly

# then
sbx ssh setup
sbx run shell .
ssh <name>@sbx

The ssh command is experimental today, so keep an eye on the
release notes as it stabilizes.

If you hit something weird, sbx diagnose will collect what you need to file a good
bug report.

Give your agents a room of their own and now, an SSH key to it. 🔐