Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,25 @@ The `dev` user can invoke passwordless package-management/install commands.
inside the Box, so the sudo allowlist is an operational control, not a security
barrier against a malicious Box user.

### Runtime controls at a glance

Each row is a control that shapes what code running in the Box can reach. The
sections below give the details.

| Control | Default | What you can change | Risk if relaxed or misused | Recommendation |
| --- | --- | --- | --- | --- |
| Linux capabilities | All dropped, then `CHOWN`, `DAC_OVERRIDE`, `FOWNER`, `SETUID`, `SETGID`, `KILL` re-added (Dev Container also adds `SYS_CHROOT`). | Only by editing Compose, `devcontainer.json`, or the runtime command yourself. The adapters expose no setting for this. | Extra capabilities such as `SYS_ADMIN` or `NET_ADMIN` widen the host kernel surface that container root can reach. | Keep the default set. |
| PID limit | `4096`. | Same as capabilities. | Without a limit, a fork bomb in the Box can exhaust the host's process table. | Keep the limit. |
| Package-management sudo | Passwordless `apt-get`, `apt-mark`, `dpkg`, `chown`, and `install` for `dev`, plus narrow `mv`/`rm` forms for staged `/usr/local/bin` promotion. | Not configurable. | This is effectively root inside the Box. It is not a barrier. | Treat every Box process as able to become root inside the Box. |
| Workspace mount | Read-write at `/workspace`. | Choose the host directory at install time. | Box processes can modify or delete project files, including `.git/hooks` and other config that host tools later run. | Push work regularly, and check `.git/hooks` and build scripts before running them on the host after an unattended session. |
| Managed home | Persistent volume holding history, `gh` auth, assistant logins, and SDKs. | Not configurable. | Any Box process can read every persisted token. | Use fine-grained, narrowly scoped tokens. Revoke them if the Box may be compromised. |
| Git identity | Private name/email config only. The host's Git config and credential helpers are never mounted. | Not configurable. | — | — |
| SSH access | Agent socket plus read-only `config`/`known_hosts` when an agent is available (Bash adapters). Otherwise none. | `SQUAREBOX_MOUNT_SSH=1` / `-MountSsh` mounts `~/.ssh` read-only when no agent is forwarded. | The opt-in exposes private keys to every Box process. A forwarded agent signs for any Box process while it is available. | Use an agent with per-use confirmation, or a dedicated narrow-scope key. Leave the directory mount off. |
| Network egress | Unrestricted outbound. The Box publishes no ports. | Not yet. An opt-in allowlist is proposed in [ADR 0011](docs/adr/0011-opt-in-egress-allowlist.md). | Any reachable host can receive Workspace content or tokens. On cloud VMs, `169.254.169.254` can hand out instance credentials. | Don't give unattended assistants credentials or Workspace content you couldn't afford to have exfiltrated. |
| Unattended assistants | Off. The `*-yolo` aliases skip approval prompts only when you invoke them. | Use them or not, per invocation. | They combine every exposure in this table with no human in the loop. | Prefer approval modes. Use `*-yolo` only in a Box whose Workspace, tokens, and SSH access are scoped for it. |
| Rootless Podman SELinux | `--security-opt label=disable` for the Box. | Not configurable. | SELinux container separation is off. Host file permissions and the declared mounts are the only boundary. | Validate on the target host before using sensitive credentials. |
| Additional mounts | None beyond those listed above. | Add your own through Compose, Dev Container config, or the runtime command. | Every added mount is fully exposed to Box processes. | Mount the narrowest path you can, and read-only where possible. |

Dev Container and Codespaces builds add the digest-locked official SSH server
Feature to the derived development image; the published Squarebox Candidate
does not contain that server. The upstream Feature enables root login in its
Expand Down
97 changes: 97 additions & 0 deletions docs/adr/0011-opt-in-egress-allowlist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# ADR 0011: Offer an opt-in egress allowlist enforced outside the Box

Status: Proposed

## Context

The Box has unrestricted outbound network access. AI assistants can run
unattended inside it (the `*-yolo` aliases), and they can read the Workspace
and the credentials persisted in the Managed home. Any reachable host is
therefore a path for exfiltrating source, tokens, or conversation history, and
on cloud hosts `169.254.169.254` can hand out instance credentials.

Setting `HTTP_PROXY`/`HTTPS_PROXY` inside the Box is not a control: any process
can ignore those variables. Enforcement only holds if the proxy is the sole
network path out of the Box, and the allowlist lives where Box processes
cannot edit it. NVIDIA OpenShell uses this shape (a per-sandbox network
namespace whose only route is a policy-checking CONNECT proxy).

The Box still has to install and update tools at runtime: `setup.sh`,
`sqrbx-update`, mise SDK backends, npm assistants, the Claude installer, and
Box-tier APT reconciliation all need the network. A mode that breaks them is
not usable.

## Decision

Add an opt-in egress mode selected at install or rebuild time and recorded in
the Install identity:

- `SQUAREBOX_EGRESS=open` (default): current behavior.
- `SQUAREBOX_EGRESS=audit`: all traffic goes through the proxy and is allowed,
but requests not on the allowlist are logged as would-be denials.
- `SQUAREBOX_EGRESS=allowlist`: requests not on the allowlist are refused.

Native PowerShell takes the same values through `-Egress`. Any other value
fails before lifecycle mutation.

In `audit` and `allowlist` modes the adapter creates, as Managed resources
labeled with the Install identity:

- an `internal` network with no route off the host, which is the Box's only
network;
- a proxy container attached to both that network and an egress network,
whose image is pinned by digest in the Candidate like other image inputs.

The Box gets `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` pointing at the proxy for
convenience, but enforcement does not depend on them. The proxy filters
CONNECT and plain HTTP by destination hostname and port. It does not terminate
TLS and never sees request bodies or credentials.

The proxy always refuses loopback, link-local (including `169.254.169.254`),
and unspecified addresses after DNS resolution, whatever the allowlist says.
Private RFC 1918 destinations are refused unless an exact hostname is listed.

The effective allowlist is the union of:

1. a Squarebox-owned default list shipped with the Candidate, covering the
hosts that Squarebox's own setup, update, and reconcile flows contact
(derived from `scripts/lib/tools.yaml`, the APT sources, mise backends, the
npm registry, and supported assistant API hosts);
2. an operator list at `<SQUAREBOX_DIR>/.squarebox/egress-allowlist`, one
hostname or `*.suffix` pattern per line.

The operator list is mounted read-only into the proxy only. It is never
mounted into the Box and never stored in the Workspace or Managed home, so an
assistant cannot widen its own access. Proxy logs are readable from the host
with a lifecycle command (for example `sqrbx-egress log`).

The Box must not resolve external names directly; the proxy resolves them.
Acceptance tests must show that DNS lookups, direct IP connections, and
non-proxied TCP from inside the Box all fail in `allowlist` mode.

Recording the mode requires a new Install identity format under ADR 0007:
`FORMAT=3` appends `EGRESS=open|audit|allowlist`. `FORMAT=2` and `FORMAT=1`
records read as `EGRESS=open`. The verifier and shared fixtures cover every
accepted format in all four adapters.

## Consequences

This makes egress a security control for operators who choose it, but the
SECURITY.md posture is unchanged: the Box remains a development environment,
not a hostile-code sandbox. Allowed hosts are still exfiltration paths. Most
code-hosting and package registries accept arbitrary uploads, and an agent with
a GitHub token can push data to any repository that token can write.

Host-level filtering cannot restrict HTTP methods or paths, and cannot keep
credentials out of the Box. Both would need TLS termination with a per-Box CA,
which this ADR rejects as too invasive for a development environment.

Every new tool, SDK backend, or assistant can need a default-list update.
`audit` mode is the migration path: operators run in `audit`, review the
would-be denials, and then switch to `allowlist`.

Compose and the Dev Container need equivalent definitions. GitHub Codespaces
controls its own networking, so this mode is not offered there.

Rootless Podman needs internal-network and proxy coverage on Ubuntu CI and
manual UAT on Fedora with SELinux enforcing, as for other Podman changes.
70 changes: 70 additions & 0 deletions docs/comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Squarebox compared with other environments and sandboxes

> **Draft.** Last reviewed 2026-10-29. Several of these tools are pre-1.0 and
> change quickly. Check each project's own docs before relying on a cell.

People reach for Squarebox, dev containers, and AI-agent sandboxes for
different jobs. Some give you a consistent, tool-rich environment. Others limit
what an agent can touch. A few do both. This page compares them on the
properties that matter when you let AI assistants run code.

Squarebox is a portable development Box. It is not a hostile-code sandbox (see
[SECURITY.md](../SECURITY.md#container-authority-and-isolation)).

## At a glance

| Tool | Primary job | Isolation boundary | Outbound network | Credentials the agent can see | Workspace access | Runs on |
| --- | --- | --- | --- | --- | --- | --- |
| **Squarebox** | Portable dev environment with curated CLI/TUI tools and assistants | Container, with reduced capabilities and a PID limit | Unrestricted ([opt-in allowlist proposed](adr/0011-opt-in-egress-allowlist.md)) | Everything persisted in the Managed home; SSH agent socket if forwarded | Read-write bind mount | Docker or Podman on Linux, macOS, Windows; Compose; Dev Containers; Codespaces |
| **Dev Containers** (spec, VS Code) | Reproducible per-project dev environment | Container, configured per project | Unrestricted unless you configure it | Whatever the config and editor forward (Git credentials, SSH agent, env vars) | Read-write bind mount | Any Docker-compatible runtime |
| **GitHub Codespaces** | Hosted Dev Containers | Container on a dedicated cloud VM | No built-in allowlist | Codespaces secrets, exposed as environment variables; GitHub token | Repo clone on the VM | GitHub-hosted |
| **Docker Sandboxes** (`sbx`) | Run coding agents unattended | microVM with its own kernel | HTTP/HTTPS allowlist (`sbx policy allow`); raw TCP, UDP, and ICMP blocked | A host-side proxy injects credentials into requests, so the agent doesn't hold the raw values | Read-write mount by default; `--clone` gives a private copy | macOS (Apple silicon), Windows 11, Ubuntu 24.04+ with KVM |
| **Anthropic sandbox-runtime** (`srt`, used by Claude Code's sandbox) | Confine one process, with no container | OS sandbox: bubblewrap on Linux, Seatbelt on macOS | Domain allowlist through host-side HTTP/SOCKS proxies | Whatever the filesystem rules allow it to read | Filesystem allow/deny rules | Linux, macOS |
| **Container Use** (Dagger) | Parallel agents, each in its own branch | Container per agent, via Dagger | Not a focus | Whatever the container gets | Its own Git branch or worktree, reviewed through Git | Anywhere Dagger runs; MCP server for agents |
| **NVIDIA OpenShell** | Policy-governed runtime for fleets of agents | Container or VM, plus Landlock, seccomp, and a network namespace | Default deny; per-binary host rules with optional HTTP method/path rules | Provider credentials injected by a TLS-terminating proxy | Policy-defined filesystem access | Linux, macOS (Apple silicon), WSL 2; Kubernetes gateway |
| **Hosted agent sandboxes** (E2B, Daytona, and others) | Code-execution API for agent products | Firecracker microVM (E2B); varies by provider | Provider-specific | What your application passes in | Files uploaded through the API | Vendor cloud; Daytona can self-host |
| **Distrobox / Toolbx** | Run another distro's userland on the host | Container, deliberately well integrated with the host | Host network | Your host home directory, by default | Your whole host home directory | Linux |
| **Nix devshells / mise alone** | Pin toolchains per project | None | Host network | Everything your user can read | The host filesystem | Host OS |

## Choosing

- **You want the same terminal tools and assistants everywhere, and you trust
what runs in them:** use Squarebox, or Dev Containers if you prefer to build
your own image per project.
- **You want an agent to run unattended with a hard network and credential
boundary:** use Docker Sandboxes or OpenShell, or confine individual commands
with `srt`. Squarebox's `*-yolo` aliases don't give you this today.
- **You want several agents working in parallel without conflicts:** use
Container Use, or Git worktrees inside one Box.
- **You are building a product that runs model-generated code:** use a hosted
sandbox API.
- **You just want host-integrated tools from another distro:** use Distrobox or
Toolbx. They are not isolation.

## What Squarebox borrows, and where it differs

- Like Dev Containers, Squarebox is a container you reuse. It adds a
persistent Managed home and runs on Docker, Podman, Compose, Dev Containers,
and Codespaces.
- Unlike Docker Sandboxes and OpenShell, it doesn't yet enforce network egress
or keep credentials out of the Box. [ADR 0011](adr/0011-opt-in-egress-allowlist.md)
proposes host-level egress filtering. It deliberately does not include TLS
interception or credential injection.
- Unlike `srt`, it doesn't confine individual processes. Everything in the Box
shares the same access.
- Unlike hosted sandboxes, it runs on your own machine or server, and your
state stays in your own volume.

## Sources

- [Docker Sandboxes docs](https://docs.docker.com/ai/sandboxes/),
[security defaults](https://docs.docker.com/ai/sandboxes/security/defaults/),
[get started](https://docs.docker.com/ai/sandboxes/get-started/)
- [anthropic-experimental/sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime)
- [dagger/container-use](https://github.com/dagger/container-use)
- [NVIDIA/OpenShell](https://github.com/NVIDIA/OpenShell) and its
[security best practices](https://docs.nvidia.com/openshell/latest/security/best-practices)
- [E2B vs Daytona comparison (third party)](https://www.morphllm.com/comparisons/e2b-vs-daytona)
- [Dev Containers specification](https://containers.dev/)
- [GitHub Codespaces docs](https://docs.github.com/en/codespaces)
- [Distrobox](https://distrobox.it/) and [Toolbx](https://containertoolbx.org/)
Loading