docs(sandbox-providers): state the sandbox security boundary (#12286)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox providers run agent work in isolated environments
> - The Daytona documents described a command wrapper that the provider
no longer uses
> - Those documents therefore described a control that the code does not
have
> - This pull request states the real sandbox boundary and the controls
for paths that cross it
> - The benefit is accurate security guidance for sandbox provider
authors and operators

## Linked Issues or Issue Description

**Issue type**

Outdated (no longer matches behavior).

**Where is the issue?**

`packages/plugins/sandbox-providers/SANDBOX-REQUIREMENTS.md` and
`packages/plugins/sandbox-providers/daytona/README.md`.

**What's wrong?**

The Daytona provider no longer uses the documented command wrapper,
package installation commands, or sudoers rule. The requirements
document also lacked a clear statement of the sandbox security boundary.

**Suggested fix**

State that the sandbox provides the boundary. Name outbound workspace
synchronization and the application programming interface bridge as the
paths that cross the boundary. State that a provider must not map a host
path into a sandbox synchronization path.

## What Changed

- Replace stale wrapper requirements with the actual sandbox security
boundary.
- State the controls that apply to outbound workspace synchronization
and the application programming interface bridge.
- State that this repository does not enforce the provider path-mapping
duty today.
- Remove obsolete Daytona package-install commands and the sudoers rule.

## Verification

- Confirm the difference contains the two documentation files and the
test file changed by the follow-up fix.
- Confirm that no unrelated source, configuration, or fixture file
appears in the difference.
- Run the repository continuous integration checks and confirm that
every required check passes.
- Run the repository review bot and confirm its final verdict.

## Risks

Low risk. This pull request changes two documents and closes a database
client in one integration test. It does not change product runtime
behavior or configuration.

## Model Used

OpenAI Codex, GPT-5, with tool use and code execution. The model
produced the documentation change and the pull request text.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run the affected test locally; continuous integration
provides complete test verification.
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Nicky Leach 2026-08-27 09:25:06 -07:00 committed by GitHub
parent 1ba7b2cc71
commit 949e975b0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 173 additions and 100 deletions

View File

@ -6,31 +6,183 @@ exec time. The environment is a requirement, not a build step.
This document states requirements. It does not state build steps.
## bwrap prerequisites (advisory, optional)
## Security boundary
A sandbox provider can wrap a command with an advisory bubblewrap (`bwrap`)
wrapper. The wrapper is advisory, best-effort, and automatic. It adds no
security. The ephemeral sandbox model stays the only security posture. A missing
prerequisite degrades to the plain command. It never fails the lease. Daytona is
the current provider with bwrap support.
Paperclip runs agent work inside a sandbox. Paperclip protects the host from
the code in that sandbox. This section states the rule for that protection. It
does not state the implementation.
The wrapper needs three run-time prerequisites. Each prerequisite is a fact of
the image or snapshot, not a fact of the runtime code. The runtime does not
build the image or snapshot. The runtime only probes for the capability and
degrades when the capability is absent. So each prerequisite is an owner
responsibility under the requirement-not-build contract above:
A sandbox is an untrusted execution environment. Paperclip assumes that a
sandbox process can read or change all accessible data.
- The `bubblewrap` package is installed, and the `bwrap` binary is on the PATH
(normally `/usr/bin/bwrap`).
- A passwordless `sudo` rule lets the sandbox user run `bwrap` as root.
- The host and kernel allow an unprivileged user namespace.
Paperclip does not protect sandbox files, processes, credentials, or code from
other code in the same sandbox.
The owner supplies these prerequisites through the image or snapshot. The
provider README states the distro-specific install commands, the exact sudoers
rule, the user-namespace setting, and the verification command. The install and
the sudoers change are environment provisioning at the image or snapshot layer.
Route them to DevOps through the board. Do not add a provisioning script to the
repository.
This section covers sandbox providers. It does not cover a local run on the
host. A local run has a different boundary.
### What the boundary protects
Paperclip protects two authorities across the boundary:
- The authority to write host files
- The authority to call the Paperclip API
Sandbox code holds each authority through one surface only:
1. **Outbound workspace synchronization.** Paperclip copies sandbox files to
host paths that the Paperclip orchestrator selects.
2. **The Paperclip HTTP bridge.** Sandbox code calls the Paperclip API through
the bridge.
A boundary control limits one of these two authorities. A control that only
protects data inside the sandbox is not a boundary control. A control that only
manages resource use is not a boundary control.
### Provider isolation assumption
The rule below depends on this assumption.
The sandbox provider must isolate the sandbox from the host and the provider
control plane. The provider must isolate these items from direct sandbox access:
- Host files
- Host credentials
- Cluster credentials
- Management sockets
- Provider management interfaces
The provider must use isolated sandbox storage for each sandbox path that
synchronization uses. This includes workspace paths and staging paths.
The provider must keep every host path outside a sandbox synchronization path.
A host path inside a synchronization path makes host files available to sandbox
code. Sandbox code can then change host files, and no synchronization control
applies to the change. This removes the boundary.
A transfer and sandbox code must have the same read authority over sandbox
files. More transfer authority can turn a sandbox symbolic link into a way to
read a protected file.
This repository does not enforce these provider rules today, and Paperclip
cannot verify them for an externally supplied sandbox.
The provider and the operator set the policy for general internet access.
Paperclip does not enforce this policy inside the sandbox.
### How to apply this rule
The test is authority, not location. A change is a boundary change only in one
of these two conditions:
- The change modifies one of the two authorities above.
- The change makes the provider isolation assumption false. A change that
exposes a management socket to the sandbox is an example.
A boundary change needs a boundary review. A reviewer must examine the change
against this contract. This section does not remove any other review of a
change.
The test gives these results:
- Code inside the sandbox can modify these authorities. The in-sandbox part of
synchronization and the in-sandbox part of the bridge are examples.
- A change that meets neither condition above is not a boundary change. This is
true when the change removes a control inside the sandbox.
- A developer can remove a control that only protects data inside the sandbox.
A developer can also change the mechanism of a boundary control. The authority
of sandbox code must stay the same or decrease.
- A requested control is a boundary security requirement only if the control
limits one of the two authorities above.
A change creates a new boundary surface in either of these conditions:
- The change lets sandbox code write host files outside workspace
synchronization.
- The change lets sandbox code call the Paperclip API outside the HTTP bridge.
The developer must update this contract before the change is released. A
reviewer must examine the change against this contract.
### Where a boundary control must run
Paperclip or the provider must enforce each boundary control outside the
sandbox. Sandbox code can change a control that runs inside the sandbox. A
control inside the sandbox can give an early error message, but it gives no
protection at the boundary.
This rule applies to enforcement. It does not apply to the tools that create or
move data. A tool inside the sandbox can create data, and a control outside the
sandbox validates that data. The outbound archive is an example. The sandbox
creates the archive. The host validates each member before extraction.
Each boundary control below must run outside the sandbox.
### Surface 1: outbound workspace synchronization
These boundary controls limit the authority of sandbox code over host files. The
synchronization implementation must:
- Accept only source and destination mappings that the orchestrator supplies.
- Keep host destinations in the specified host workspace or asset roots.
- Reject path traversal and symbolic links that escape a host destination root.
- Validate archive member paths and link targets before extraction on the host.
- Handle sandbox file contents only as data during synchronization.
Native synchronization hooks and the command fallback must meet the same
requirements.
Synchronization also confines each sandbox source path to a synchronization
root. A path check outside the sandbox is lexical, and only a check inside the
sandbox resolves the symbolic links on the path. The check inside the sandbox is
not a boundary control. It gives an early error.
Sandbox source confinement is safe as an early check, because a transfer reads a
sandbox source with the authority of sandbox code. The bytes that cross the
boundary are bytes that sandbox code can already read. The provider isolation
assumption above states this rule for the provider.
These requirements protect host reliability and host resources. They are not
boundary controls:
- Use atomic replacement for each single-file mapping.
- Move file data with bounded memory.
### Surface 2: Paperclip HTTP bridge
Sandbox code must call the Paperclip API only through the HTTP bridge.
These boundary controls limit the API authority of sandbox code. The bridge
must:
- Accept only requests that have valid bridge authentication.
- Limit bridge authentication to bridge access.
- Use only the run agent's API authority for each request.
- Permit only approved HTTP methods and routes.
- Forward only approved request headers.
- Add the correct run identity to each request.
These requirements protect host reliability and host resources. They are not
boundary controls:
- Limit request size, response size, and request time.
- Limit the queue length of the file-queue transport.
- Limit the number of concurrent requests on the bidirectional channel.
All other HTTP bridge requirements apply to both transports.
### What is not a boundary surface
Paperclip sends commands from the host to the sandbox. Command execution can
return output to the host. This output does not give sandbox code authority to
write host files or call the Paperclip API.
The host records this output as run logs and reads it as agent protocol
messages. Neither use gives sandbox code one of the two authorities above.
A persistent process session stays in the sandbox. A bidirectional channel is a
transport. Sandbox authority stays limited to outbound workspace synchronization
and the HTTP bridge.
## Required on PATH

View File

@ -29,85 +29,6 @@ Notes:
- The driver supports both `snapshot`-based and `image`-based sandbox creation. If both are set, validation rejects the config as ambiguous.
- Reusable leases map to Daytona stop/start semantics. Non-reusable leases are deleted on release.
## Advisory bwrap wrapper
The driver wraps a sandbox command with an advisory bubblewrap (`bwrap`) wrapper. The wrapper is advisory, best-effort, and automatic. At lease time the driver probes the sandbox for the wrapper capability and records the result on the lease metadata. At execute time the driver wraps the command when the capability is present. The command builder is a pure function.
- **The wrapper adds no security.** The ephemeral sandbox stays the only security posture. The wrapper only gives an agent real-time feedback when the agent tries to change a file that the ephemeral sandbox will not keep.
- **The read-only root is a feedback signal.** The wrapper binds the root as read-only (`--ro-bind / /`) and re-binds only the writable directories. A write to a path outside the writable set fails at once, so the agent learns the change is not durable.
- **A capability probe records the wrapper capability.** No configuration field turns it on. At lease time the driver reads the sandbox username with `id -un`, then probes the end-to-end `bwrap` capability by running `sudo -n bwrap` with a workspace bind and an `su` user switch. It stores `bwrapAvailable` and `sandboxUsername` on the lease metadata.
- **The probe is best-effort.** A missing `bwrap` binary, a missing passwordless `sudo -n` rule, a missing `su` binary, or an inaccessible workspace bind records `bwrapAvailable: false` and never fails the lease.
- **The writable set is the workspace plus the read-write sync destinations.** The wrapper binds the workspace directory read-write as the baseline; the workspace is always durable. It adds the read-write sync destinations that a sync-in recorded for the same lease. The set deduplicates the directories. The baseline keeps a safe result even when the collected set is empty.
- **The wrapper runs at execute time when the capability is present.** The driver wraps the command only when the lease reports `bwrapAvailable: true` and a username is known. It binds the workspace and the read-write sync destinations, keeps the root read-only for feedback, and re-binds the stdin file after the fresh `/tmp`. It runs the plain command when the capability or the username is missing. A wrap without a username would run as root and give the agent's files root ownership, so the driver keeps the plain command in that case.
## Operator enablement (advisory bwrap)
The advisory `bwrap` wrapper needs three run-time prerequisites on the image or
snapshot. The repository does not build the Daytona image or snapshot. It
references an external `image` or `snapshot`. So the three prerequisites are
image facts, not code facts. The runtime only probes for the capability and
degrades when the capability is absent.
The wrapper is advisory, best-effort, and automatic. It adds no security. The
ephemeral sandbox model stays the only security posture. A missing prerequisite
degrades to the plain command. It never fails the lease. So the enablement below
is optional. It gives the agent real-time feedback on a non-durable write. It
does not change the security posture.
The install and the sudoers change are environment provisioning at the image or
snapshot layer. Route them to DevOps through the board. Do not run the steps
from the runtime and do not commit a provisioning script to the repository.
### 1. Install the `bubblewrap` package
The repository does not state the Daytona base distribution. Confirm the
distribution on the referenced image or snapshot first, then run the matching
command:
```bash
# Debian/Ubuntu
apt-get install -y bubblewrap
# Alpine
apk add bubblewrap
# Fedora/RHEL
dnf install -y bubblewrap
```
Confirm the binary path is `/usr/bin/bwrap` after the install.
### 2. Add the passwordless sudoers rule
The wrapper runs `bwrap` as root with `sudo -n`. Add this exact sudoers line.
Use the real sandbox user name and the real `bwrap` path:
```text
<sandbox-user> ALL=(root) NOPASSWD: /usr/bin/bwrap
```
The `<sandbox-user>` is the account name that `id -un` returns inside the
sandbox. Use the account name, not a numeric id, in the sudoers line. The probe
reads the username with `id -un`. The driver resolves the sandbox work directory
first, then the user home directory. It uses `/home/daytona` only as a fallback
default when both are empty. Confirm the real home directory for your image or
snapshot. Install the `sudo` and `util-linux` (for `su`) packages in the image
or snapshot if they are absent.
### 3. Verify the prerequisites
Run this exact command as the sandbox user, replacing `<sandbox-user>` and
`<workspace>` with real values:
```bash
sudo -n bwrap --ro-bind / / --bind-try <workspace> <workspace> -- su -s /bin/sh <sandbox-user> -c true
```
A zero exit code means all prerequisites are met. A non-zero exit code means one
prerequisite is missing. The wrapper then stays off and runs the plain command.
The probe binds the workspace directory and switches to the sandbox user with
`su`. This matches the exact invocation the live wrapper uses, so a passing probe
guarantees that execution commands will also succeed.
## Local development
```bash