Runtimes

Filesystem Sandbox

Limit Agent filesystem visibility with Linux bubblewrap mount namespaces and workspace-scoped data isolation.

The filesystem sandbox places each Agent subprocess and its tools in a Linux bubblewrap (bwrap) mount namespace. It controls filesystem visibility and writability; it does not restrict network access, environment-variable values, or executable permissions.

MOPHEUS_SANDBOX_ENABLED=true

The sandbox is enabled by default, including when this variable is unset or blank. Set it to false or off to disable it; other values also keep the sandbox enabled.

It is effective only on Linux hosts with a usable bwrap installation. The daemon runs a minimal capability probe (bwrap --ro-bind / / true) before wrapping tasks, because an installed binary can still be blocked from creating a user namespace by AppArmor, SELinux, seccomp, kernel settings, or a container runtime. If the probe fails, the daemon logs the original error and runs the task unsandboxed. Only a successful probe and command wrap mark the task as sandboxed.

To diagnose a Linux host, run:

bwrap --ro-bind / / true
sysctl kernel.apparmor_restrict_unprivileged_userns 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true
sysctl user.max_user_namespaces 2>/dev/null || true
unshare -Ur true

Ubuntu 23.10 and later commonly enforce the restriction through AppArmor. Docker, LXC, and CI environments may impose the same restriction at the host or container security-policy level. This is a Linux capability issue, not an Ubuntu-only feature; check the active kernel and security policy for other distributions.

Ubuntu Troubleshooting

The Ubuntu APT package is named bubblewrap; bwrap is the installed command. Install it and verify it as the regular user that runs the Mopheus daemon:

sudo apt update
sudo apt install bubblewrap
bwrap --version
bwrap --ro-bind / / true

Do not rely on a test run only as root: root may succeed even when the daemon user cannot create a user namespace. If APT cannot locate bubblewrap, check that the package sources for the installed Ubuntu release are enabled, then run sudo apt update again.

On Ubuntu 23.10 and later, if the probe fails, check the AppArmor restriction and the kernel limits:

sysctl kernel.apparmor_restrict_unprivileged_userns 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true
sysctl user.max_user_namespaces 2>/dev/null || true
sudo aa-status | grep -i bwrap || true
sudo journalctl -k | grep -i apparmor | tail -50

If the kernel logs show an AppArmor denial, there are two host-level options:

  1. Disable the restriction globally for a temporary diagnosis.

    sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
    bwrap --ro-bind / / true

    To persist this setting, add kernel.apparmor_restrict_unprivileged_userns=0 to a file under /etc/sysctl.d/ and run sudo sysctl --system. This allows unconfined applications to create user namespaces, so it should be limited to machines where that broader security tradeoff is acceptable.

  2. Allow user namespaces for bwrap while keeping the global restriction enabled.

    First check whether a profile is already attached to bwrap. Do not add a second profile for the same executable; inspect and adjust the existing profile instead. The package may also provide a tested profile under /usr/share/apparmor/extra-profiles/:

    command -v bwrap
    sudo aa-status | grep -i bwrap || true
    find /etc/apparmor.d /usr/share/apparmor/extra-profiles -iname '*bwrap*' -o -iname '*bubblewrap*' 2>/dev/null
    grep -R "userns" /etc/apparmor.d 2>/dev/null

    If there is no existing profile and command -v bwrap returns /usr/bin/bwrap, create /etc/apparmor.d/usr.bin.bwrap with this Ubuntu profile:

    sudo vi /etc/apparmor.d/usr.bin.bwrap
    abi <abi/4.0>,
    include <tunables/global>
    
    /usr/bin/bwrap flags=(unconfined) {
      userns,
    }
    
    sudo apparmor_parser -r /etc/apparmor.d/usr.bin.bwrap
    sudo aa-status | grep -i bwrap
    bwrap --ro-bind / / true

    Replace /usr/bin/bwrap and the profile filename if command -v bwrap reports another path. The userns, rule grants namespace creation to processes attached to this bwrap profile; flags=(unconfined) means AppArmor does not otherwise confine that executable. Review whether this host-wide bwrap exception is appropriate, and test the actual Mopheus providers. To roll back this manually created profile, unload it and remove the file:

    sudo apparmor_parser -R /etc/apparmor.d/usr.bin.bwrap
    sudo rm /etc/apparmor.d/usr.bin.bwrap

The second option is preferred for a shared host because it preserves the restriction for unrelated executables. If the probe still fails after AppArmor allows userns, check that kernel.unprivileged_userns_clone is enabled and user.max_user_namespaces is greater than zero. Mopheus logs the probe output and runs the task unsandboxed when the capability remains unavailable.

Workspace Isolation Boundary

The sandbox isolates by workspace, not by individual task. Tasks in one workspace can collaborate and share required runtime data; data from other workspaces is hidden.

Path or scopeAccessDetails
/Read-onlySystem commands, libraries, and configuration remain readable and executable.
Current task work directoryRead-writeCode, generated files, and task logs.
Other task directories in the current workspaceRead-writeThe collaboration scope for tasks in the same workspace.
Current workspace .repos/<workspaceId> bare cacheRead-writeRequired by worktree .git pointers and normal Git operations.
/tmpRead-writeMaps to /tmp/mopheus-<workspaceId> on the host and is shared by this workspace.
Other workspaces' task directories and bare cachesHiddenA tmpfs overlay prevents both reads and writes.
Other host /tmp contentHiddenOnly the current workspace temporary directory is remounted.
$HOMERead-writeProvider configuration, sessions, and symlinks continue to work.
Paths in MOPHEUS_SANDBOX_DENYHiddenCovered by an empty tmpfs.

The sandbox therefore isolates task data across workspaces, but does not isolate tasks within the same workspace or fully isolate $HOME by default.

Sensitive Paths

~/.ssh remains visible by default so SSH Git operations keep working. Hide extra credentials for untrusted agents with comma-separated absolute paths:

MOPHEUS_SANDBOX_DENY=~/.ssh,~/.aws,~/.gnupg

Any operation requiring a hidden credential will fail. Evaluate the minimum credential set together with the task's Git and cloud-access requirements.

Personal Configuration Files

When sandboxing is active, enabled personal configuration files are staged under <WorkDir>/.config/<workspaceId>/<taskId>/. The daemon preserves each file's path relative to $HOME and overlays individual files into the sandbox; the staging directory itself is never mounted.

  • File modes determine read-only or read-write mounting.
  • Staging is removed after success, failure, cancellation, timeout, or startup failure.
  • If sandboxing is unavailable, the task continues without configuration delivery.
  • Startup cleanup removes only UUID-shaped Mopheus staging roots under .config.

Managed SSH keys and SSH configuration fragments are delivered through this path. Private key bytes are encrypted at rest and are never exposed to browsers; existing host SSH configuration remains unchanged.

Providers and Limits

Supported CLI providers use this outer sandbox on Linux. Codex disables its inner sandbox when this layer is active to avoid nesting. Unsupported providers warn and degrade to unsandboxed execution. See Provider CLI for the capability matrix.

This is a process-level mount sandbox. It can be combined with containers, but does not replace network controls, credential management, access control, or container isolation.

VariableDefaultDescription
MOPHEUS_SANDBOX_ENABLEDtrueEnable a bwrap mount namespace for Agent subprocesses. Enabled when unset or blank; only false or off disables it.
MOPHEUS_SANDBOX_DENY—Additional absolute paths to hide, comma-separated.