# Give your agent its own computer, not SSH to production

> Don't give a coding agent a shell on production. Give it a Computer with staging-only credentials, deny-by-default egress and a snapshot, and review its diff before you deploy.

- Source: Sanbox knowledge base, by Sanlabs GmbH
- URL: https://sanbox.cloud/kb/security/agent-own-computer-not-ssh-to-production/
- Type: how-to
- Section: Secrets, egress and blast radius
- Language: en
- Published: 2026-11-06
- Updated: 2026-11-06

Give the agent a machine of its own instead of a shell on a production server. On Sanbox, that's a Computer whose network policy doesn't include any production host, whose only credential is a staging one held in a Secret Proxy, and which you snapshot before risky work. You review the diff it produces and ship it through your normal pipeline. The same pattern works anywhere: a separate machine, separate credentials, a separate network path and a human check before production.

## Run this with your agent

```text
Read https://sanbox.cloud/kb/security/agent-own-computer-not-ssh-to-production.md and follow it to set up a staging-only agent Computer.
If the Sanbox CLI is missing, install it with `npm install -g @sanlabs/sanbox-cli@latest`
and sign in with `sanbox login`. Show me each command before you create, stop or delete a Computer.
```

## Why not a shell on production?

When an agent has a shell on a production server, any misunderstanding plays out on production: a request to restart a service can become changed mounts, removed containers or scripts nobody asked for. You can't fully control how a model reads an instruction, but you can control where its commands run.

## What does "its own computer" need?

- **No route to production.** The egress allowlist leaves out every production host.
- **No production credentials.** The only key is a staging key, kept outside the machine.
- **A restore point.** A snapshot taken before the risky part.
- **A human gate.** You read the diff, and your CI and deploy process take it to production.

## Steps

### 1. Write a network policy

List only what the work needs, and save it as `staging-policy.json`:

```json
{
  "default_action": "deny",
  "rules": [
    { "destination": "registry.npmjs.org", "ports": [{ "from": 443, "to": 443 }], "description": "npm packages" },
    { "destination": "api.staging.example.com", "ports": [{ "from": 443, "to": 443 }], "description": "Staging API only" }
  ]
}
```

### 2. Store the staging token as a Secret Proxy

Save the settings, without the token, as `proxy.json`:

```json
{
  "name": "staging-api",
  "host": "api.staging.example.com",
  "authentication": { "type": "bearer" },
  "attach_run_id": false
}
```

Then read the token from a private file outside the repository. Replace `--all-paths` with one `--path` per API path if the work needs only part of the API:

```bash
sanbox secret-proxies create --file proxy.json --secret-stdin --all-paths --json < ~/.config/staging-api-token
```

### 3. Create the template

Creating templates needs the admin or owner role. Pick the provider and model from the lists, then validate the template:

```bash
sanbox model-providers list --json
sanbox model-providers models <provider-id> --json
sanbox templates create --name "Staging agent" --type opencode-computer \
  --model-provider <provider-id> --model <model-id> \
  --network-policy staging-policy.json --secret-proxy staging-api --json
sanbox templates validate <template-id> --json
```

### 4. Start a Computer and copy in a clean checkout

Start the Computer and wait until `runs get` shows it running. Then clone your repository from its root and copy the clone in. A clone carries committed files and history only, so untracked files such as `.env` stay behind:

```bash
sanbox run --template <template-id> --json
sanbox runs get <run-id> --json
git clone . /tmp/app
sanbox cp /tmp/app <run-id>:/workspace
```

### 5. Snapshot it before the risky part

```bash
sanbox runs stop <run-id> --wait --json
sanbox snapshots create --run <run-id> --name app-before-change --wait
sanbox runs resume <run-id> --wait --json
```

You can also keep the stopped Computer as your restore point and do the work in a fork, which starts on its own: `sanbox runs fork <run-id> --request-key <uuid> --wait --json`.

### 6. Let the agent work

Use the console chat, or connect your own code with `sanbox opencode connect <run-id> --expires 1h --json`. Tell the agent that the staging API is the only service it can reach.

### 7. Review the diff

Look at the change before anything leaves the Computer, then copy it out as a patch:

```bash
sanbox exec <run-id> --cwd /workspace/app -- 'git add -A && git diff --cached --stat'
sanbox exec <run-id> --cwd /workspace/app -- 'git diff --cached > /workspace/change.patch'
sanbox cp <run-id>:/workspace/change.patch .
git apply --check change.patch
```

Apply the patch on a branch, open a pull request, and let your normal checks and deploy process take it from there.

### 8. Roll back if it went wrong

Start a fresh copy from the snapshot and delete the broken Computer:

```bash
sanbox runs stop <run-id> --wait --json
sanbox run --snapshot app-before-change --json
sanbox runs delete <run-id> --yes --json
```

## Lock down egress

Check three things in the policy before you start:

- **No production hostnames**, including databases, admin panels and internal APIs.
- **Separate staging hosts.** If staging and production share a hostname, a host rule can't tell them apart. Give staging its own host.
- **No inbound ports.** Inbound access is off unless you add `--inbound-port` to the template, and even then each caller needs a short-lived token.

## What this doesn't do

- **It doesn't make the change correct.** Your review and your tests do that.
- **Staging credentials are still credentials.** The agent can do anything the staging token allows. Limit the Secret Proxy to the API paths the work needs, and keep real customer data out of staging.
- **It doesn't help with work that has to happen on production**, such as an actual reboot. Do that yourself, or through a runbook or pipeline a person approves.
- **A snapshot restores the Computer, not the world.** Anything the agent changed through the staging API stays changed.

## Related

- [How do I give an AI agent API keys without exposing them?](https://sanbox.cloud/kb/security/api-keys-your-agent-cannot-see/)
- [How do I restrict which domains an AI agent can reach?](https://sanbox.cloud/kb/security/restrict-which-domains-an-agent-can-reach/)
- [How do I use --dangerously-skip-permissions in a disposable computer?](https://sanbox.cloud/kb/security/snapshot-before-skip-permissions/)
- [What survives when an agent's computer stops?](https://sanbox.cloud/kb/agent-computers/what-survives-a-stop/)

## Next step

Talk to us at sales@sanlabs.ai. Sanlabs' forward-deployed engineers build agent use cases with your team, with experience in aviation, pharmaceuticals, supply chain and logistics, and internal admin workflows. How we work: https://sanbox.cloud/kb/teams/forward-deployed-engineering.md

Existing customers can use Sanbox from a coding agent: install the CLI with `npm install -g @sanlabs/sanbox-cli@latest`, sign in with `sanbox login`, then run `sanbox skill` for the full agent guide.
