# Security

> How sandboxes are isolated from each other, how network access is restricted, and how secrets and keys are handled.

Sandboxes are meant to run untrusted code — agent output, user submissions, random packages from the internet. The design assumes the code inside is hostile.

## Isolation

- **One VM per sandbox.** Each sandbox is a separate Cloud Hypervisor microVM with its own kernel. There is no shared kernel between sandboxes, unlike container-based platforms.
- **No control channel on the network.** The agent inside the VM talks to the host over **vsock**, not TCP. Nothing on the sandbox's network — including code you run as root — can reach the host's control API.
- **Separate disks.** Each root disk is its own copy-on-write image; forks and snapshots are reflinks, never shared writable state.
- **Fresh identity on fork.** Forks regenerate the machine-id, SSH host keys and hostname, so copies can't impersonate each other.

## Network

| Traffic | Policy |
| - | - |
| Sandbox → internet | Allowed (IPv4 via NAT, IPv6 direct) |
| Sandbox → another sandbox | **Blocked** — VMs have no shared bridge |
| Sandbox → host and internal networks (RFC 1918, our tailnet) | **Blocked**, except the node's DNS relay |
| Sandbox → SMTP port 25 | Blocked (use your mail provider's submission port) |
| Internet → sandbox IPv6 | Reaches the VM, whose firewall (`ufw`) is **closed except SSH** by default. Open a port with `sudo ufw allow <port>`; `sudo ufw status` lists the rules |
| Internet → sandbox IPv4 | Only the SSH port forward (`sshEndpoint`) and hosted ports; nothing else can address the VM |
| Internet → hosted ports | Only through the HTTPS proxy, only for ports you hosted, token-gated unless public |

Bandwidth is shaped per VM so one sandbox can't starve its neighbours.

Outbound traffic shares the node's IPv4 address and IPv6 /64 with the other sandboxes on that node, so outside services that rate-limit per IP (Docker Hub pulls, speed tests) see one shared limit. Sign in to Docker Hub (`docker login`) to get your own account's limit.

## Secrets

- Environment variables and secret files are written into a sandbox only when its [environment](/docs/environments) passes them.
- `noEnv` sandboxes and environments marked **safe for third parties** receive none of your secrets or credentials. Use them for anything you hand to someone else.
- Removing a secret from an environment and upgrading **deletes** it from the sandboxes' disks; it doesn't just hide it.
- Share codes for named snapshots strip your logins, keys, secret files, repository credentials and SSH keys from the copy.

## Keys and tokens

- API keys are stored as SHA-256 hashes; secrets are shown once. Scope them to the actions, sandboxes and environments they need, and give them an expiry. See [API keys](/docs/api-keys).
- The in-sandbox `SANDBOXER_TOKEN` can only act on its own sandbox.
- Dashboard sessions use a `__Host-` prefixed, `Secure`, `HttpOnly`, `SameSite` cookie.
- Hosted-port tokens and desktop URLs are redacted from our logs.

## Webhooks

Every delivery is signed with HMAC-SHA256 and timestamped. Verify the signature and reject stale timestamps — see [Webhooks](/docs/webhooks#verify-the-signature).

## Data retention

- Stopped sandboxes keep their disks until you delete them. Deleting a sandbox removes its disk from our sandbox servers; copies in backups and long-term storage may remain for a time.
- **Zero data retention** (account setting) deletes the data of every archived sandbox and every named snapshot instead of keeping it.
- **Snapshots off** and **monitoring off** can be set per sandbox or for the whole account, so no disk snapshots, command output, process lists or metrics are kept. With monitoring off the sandbox itself keeps no command history on its disk either.
- **Close your account** on the dashboard's [Account](/app/account) page. Closing cancels your subscription at once, archives your sandboxes and revokes every API key. You can reopen the account for 30 days; then its sandbox data is deleted, except copies in backups and long-term storage, which may remain for a time. For a GDPR request, choose **Erase permanently**: it deletes your sandboxes, secrets and identity without the 30-day wait and cannot be undone. Billing and audit records are kept; see the [Privacy Policy](/privacy).

```bash
sbx data-retention status
sbx data-retention enable
```

## Reporting a vulnerability

Email **security@sandboxer.com** with a description, the steps to reproduce, and the impact you think it has. The same contact is published in [/.well-known/security.txt](/.well-known/security.txt).

**What we'll do**

- Acknowledge your report within 3 business days and keep you updated until it's resolved.
- Fix critical issues (sandbox escape, cross-tenant access, control-plane compromise) as our top priority.
- Credit you when we publish the fix, if you'd like.

**In scope**

- Escaping a sandbox to its host, or reaching another customer's sandbox, data or traffic.
- The control plane: sandboxer.com, the API, the dashboard, authentication, API keys and the in-sandbox token.
- The hosted-port proxy (`*.box.sandboxer.com`), SSH forwarding and desktop streaming.
- The `sbx` CLI and the SDKs.

**Out of scope**

- Anything that needs access to another customer's account or sandboxes. Test only against sandboxes you own.
- Denial of service, load testing, spam and social engineering of our staff or providers.
- Issues in third-party services we use (report those to the vendor) and findings from automated scanners with no demonstrated impact.

**Safe harbour.** If you act in good faith, stay in scope, avoid privacy violations and service disruption, and give us reasonable time to fix an issue before disclosing it, we won't pursue legal action against you for your research. Testing that follows this policy is authorized under our [Acceptable Use Policy](/acceptable-use).
