docs(daytona): document operator enablement for the advisory bwrap wrapper (#10560)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Daytona sandbox provider now uses an advisory bwrap wrapper
> - Operators need clear host and image setup for bubblewrap, sudo, and
user namespaces
> - The repo should document that setup, but it should not own
provisioning
> - This pull request adds the operator guidance to the shared sandbox
requirements and the Daytona README
> - The benefit is that operators can enable the wrapper with the same
steps the code expects

## Linked Issues or Issue Description

Refs #10554 and #10541.

## What Changed

- Added an advisory bwrap prerequisites section to
`packages/plugins/sandbox-providers/SANDBOX-REQUIREMENTS.md`.
- Added an operator enablement section to
`packages/plugins/sandbox-providers/daytona/README.md`.
- Documented the install commands, the sudoers rule, the user namespace
setting, and the verification command.
- Kept provisioning out of the repo and left it to the image or snapshot
layer.

## Verification

- `git diff --check origin/master...HEAD`
- `gh pr checks 10560`

## Risks

- Low risk. This change updates documentation only.
- The docs can drift if the host setup changes later.
- Provisioning still lives outside the repo.

## Model Used

- OpenAI Codex, GPT-5, tool use enabled.

## 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 linked existing issues with `Fixes: #` / `Closes: #`
/ `Refs #` OR described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [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-07-31 11:50:11 -07:00 committed by GitHub
parent 51bb41c7e3
commit c4f62644b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 113 additions and 0 deletions

View File

@ -6,6 +6,32 @@ 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)
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.
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:
- 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.
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.
## Required on PATH
- `node` must be installed and on the PATH.

View File

@ -40,6 +40,93 @@ The driver wraps a sandbox command with an advisory bubblewrap (`bwrap`) wrapper
- **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 uid/gid pair 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 uid/gid is missing. A wrap without a uid/gid 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 numeric user id and group id with `id -u` and `id -g`. 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` package in the
image or snapshot if it is absent.
### 3. Allow user namespaces
The `bwrap` wrapper creates a user namespace. The kernel must support user
namespaces for the wrapper to run. Confirm the kernel allows them:
```bash
sysctl user.max_user_namespaces
```
A value greater than zero means the kernel supports user namespaces. This value
is the requirement for the wrapper.
The wrapper runs `bwrap` as root with `sudo -n`. Root creates the user namespace
directly, so the Debian/Ubuntu `kernel.unprivileged_userns_clone` setting does
not apply here. That setting only limits an unprivileged process. A managed
sandbox that denies `sysctl kernel.unprivileged_userns_clone=1` still runs the
wrapper when `user.max_user_namespaces` is greater than zero.
### 4. Verify the three prerequisites
Run this exact command as the sandbox user:
```bash
sudo -n bwrap --unshare-user --uid 0 --gid 0 --ro-bind / / -- true
```
A zero exit code means all three prerequisites are met. A non-zero exit code
means one prerequisite is missing. The wrapper then stays off and runs the plain
command.
The `--uid 0` and `--gid 0` flags map the check to root inside the test
namespace. This command is only a capability check. It does not need to match
the sandbox user id. The live wrapper maps to the real sandbox user id and group
id.
## Local development
```bash