maf-sandbox-bubblewrap
Experimental. Not yet released.
maf-sandbox-bubblewrapmay change or be removed without notice and emitsMafSandboxBubblewrapExperimentalWarningon import.
Experimental Linux sandbox backend using Bubblewrap and cgroup v2, without Docker, an OCI image or a container engine. The runtime is a separately provisioned, trusted Linux root directory. Runtime downloads never happen during a tool call.
Prerequisites
- Linux with unprivileged user, mount, PID, network, IPC, UTS and cgroup namespaces; Bubblewrap supporting
--disable-userns,--assert-userns-disabled,--as-pid-1and bounded tmpfs mounts. Missing support refuses startup. - A private cgroup v2 delegation with
cpu,memoryandpidsenabled,memory.swap.maxandcgroup.kill. The host process must already belong to that delegation so it can move children into sibling groups. A systemd service withDelegate=cpu memory pidscan provide it; the host must place itself in a leaf and enable the controllers on its empty parent. This package does not grant privileges or change host-wide policy. - A trusted runtime directory containing
/usr/bin/python3and/bin/sh, with no host credentials or sockets. Keep it immutable while sandboxes run. Runtime, cgroup and state paths and their ancestors must be controlled by the host administrator. State and cgroup roots must resolve outside the runtime. The state directory must be owned by the service user and mode0700.
Use
from pathlib import Path
from maf_sandbox import Isolation, SandboxRouter
from maf_sandbox_bubblewrap import BubblewrapSandboxBackend, BubblewrapSandboxConfig
async def make_router() -> SandboxRouter:
backend = await BubblewrapSandboxBackend.create(BubblewrapSandboxConfig(
runtime_root=Path("/opt/maf-runtime"),
state_root=Path("/var/lib/my-service/sandbox-state"),
cgroup_root=Path("/sys/fs/cgroup/my-service"),
runtime_id="drawio-native",
))
return SandboxRouter([backend], min_isolation=Isolation.CONTAINER)
SandboxSpec.image is an optional exact match against the configured runtime_id, not an OCI reference. image_id is unsupported. The storage base is /maf-sandbox/work; only that explicit override or None is accepted. Relative working directories address the base. Commands are opaque; a string runs through /bin/sh -c, while a sequence executes directly.
The protocol's Isolation.CONTAINER means Linux namespaces plus cgroups. It describes the boundary even though no container engine is used. The router's default MICROVM floor still refuses this backend. Keep a separate router or use per-spec routing for a native renderer alongside a stronger CodeAct backend; never lower a shared host floor implicitly.
Boundary and lifecycle
Every sandbox gets mandatory private namespaces, no capabilities, disabled nested user namespaces, a new session and a cleared environment. The runtime is read-only. /tmp, /run and /maf-sandbox are separate bounded tmpfs mounts; aggregate memory, swap, process count and CPU are constrained before any guest starts. Default limits are 1 GiB memory, no swap, 256 processes, two CPU equivalents and 128 MiB per writable mount. No host home, display, network or cgroup filesystem is mounted. Only Egress.CLOSED, POSIX EXEC, FILES_IN and FILES_OUT are declared. This is a shared-kernel boundary, not a microVM or a claim of protection against kernel exploits. No seccomp syscall filter is currently supplied.
A private PID 1 broker uses no-follow directory descriptors for transfers. It runs at guest filesystem authority; its stat responses are guest-side metadata, not host attestations. Its control descriptors are protected from ptrace, and commands receive separate stdin/stdout/stderr. Individual transfers are capped at 8 MiB; the router applies its collection count and aggregate byte limits. Combined command output is capped at 1 MiB by default; excess output fails explicitly. A command's descendants, including detached processes, are killed before its reply. Timeout or cancellation after dispatch destroys the entire sandbox; while queued, it refuses only the waiting request. There is no reclamation or snapshot capability; the router uses disposal.
Key identity includes scope, thread, agent, call and kind. An exclusive host file lock prevents another backend object or process from acquiring a live instance. Repeated acquire within one backend can reuse a matching spec; another owner is refused. Files live only in tmpfs and disappear on owner death. Records allow a subsequent host to purge dead owners' cgroups; this is cleanup recovery, not restoration of guest state. Stale instance disposal cannot delete a replacement. A scope purge reports busy or failed records and retains them for retry. Hosts must stop new work across replicas before purging a conversation.
Lock files remain after disposal so a concurrent owner cannot lock a replacement inode under the same name. Remove the state directory only after stopping all owners; long-lived deployments should account for those small files in retention policy.
Ownership records are published atomically before cgroup creation, so a host that dies during startup leaves either no cgroup or a complete cleanup record. Failed setup removes its record after cgroup cleanup succeeds. Commands whose requested timeout exceeds max_timeout (180 seconds by default) are rejected with ValueError before execution. Accepted deadlines include time waiting for the request lock; only the remaining budget reaches the broker.
Offline Draw.io
The runtime provisioning script uses debootstrap and the pinned Draw.io Desktop package directly, with the same offline assets, fonts and exporter as the image profile. Provisioning needs network access and root; rendering needs neither. Run the host outside that runtime and register this backend with maf-sandbox-drawio, passing the configured runtime id as image. Linux is the only supported host platform; native Windows and macOS backends are separate work.
Metadata
Release files for maf-sandbox-bubblewrap 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| maf_sandbox_bubblewrap-0.2.0.tar.gz | 15.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| maf_sandbox_bubblewrap-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.0 kB
Release files / maf_sandbox_bubblewrap-0.2.0.tar.gz
| Download URL | maf_sandbox_bubblewrap-0.2.0.tar.gz |
|---|---|
| Size | 15.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b73406b46dc5848015d2bdbd5f69fd77a0f517192080b94eb5308bf9aade1f09
|
|
BLAKE2b-256 checksum How to use checksums |
cda749a5ede4471233f563ac88caf3d10de058403ba3f800e8d8710fa7a78bb8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency logRelease files / maf_sandbox_bubblewrap-0.2.0-py3-none-any.whl
| Download URL | maf_sandbox_bubblewrap-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d010e97653590d52b36b42f5787c40c7c5b14e0db6346473b245631604db8f84
|
|
BLAKE2b-256 checksum How to use checksums |
f5fe0983037ed07c520499ada44e9725b63bd014d06cde7a5daa20a8862d2948
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency log