The sandbox's own log is followed from outside; a refusal reaches us up to half a second late.
A failed command waits for it; one that arrives after its result is told with the next result.
Sites from Settings are listed plain; sites allowed for this session are marked "Session only".
The person can allow one more for the session or take one back; the running sandbox follows at once.
A new tool, request_network_access, raises a card only a person answers: this session, always, or deny.
A command's result names the sites the sandbox blocked while it ran, and the agent is told the live list each turn.
The header chip lists the sites allowed for this session apart from the machine's.
"This session" and "Always allow" now reach the session's commands at once.
OpenShell: the policy is rendered again and set on the sandbox; macOS and Windows: the session's own proxy takes the host.
"Always allow" now copies the list back as the store keeps it, so the chip matches Settings.
The three sandbox pages say web search and fetch follow the list; connectors and MCP do not.
A throwaway container asks the kernel; without Landlock the Docker row says update Docker Desktop.
A sandbox that exits at start on such a Mac gets the same message instead of ContainerExited.
Network allow list holds only the machine's ticked sites, none by default; strict/standard removed.
The app no longer installs OpenShell; setup is two checks and one step with progress.
DMG build requires grpcio so the sidecar can use OpenShell.
Network: registries and search (strict folded in) with the machine's own hosts, or allow everything.
Credentials list only what was added; shipped entries become presets with the variable each tool reads.
A hung docker info no longer fails the OpenShell checklist.
The OpenShell checklist and setup job show under the OpenShell type; the Windows setup keeps its own routes.
Co-Authored-By: Devika Verma <devikaverma11@gmail.com>
From a fresh-user run on WSL (2026-09-27): "Set up sandbox" handed over two
commands, gave no install instructions, and put a Copy button on notes
("found none") and file paths.
- steps carry `command` (run in a terminal, gets Copy) apart from `hint`
(a note) and `docs` (a Guide link). `checks()` prints command or hint.
- `admin_prefix()`: `sudo -n` when this user may sudo without a password,
else `pkexec` (the desktop's own password prompt) when there is a display
and this is not WSL. The app never sees or holds a password.
- `install_openshell()`: NVIDIA's installer as the user on a Mac; on Linux
under the admin prefix with `loginctl enable-linger` FIRST in the same
run, because the installer starts the gateway through the user's systemd
manager, which on WSL does not run until linger is on (it otherwise
prints "could not reach the user systemd manager" and exits 0 without
registering the gateway). The job streams its output as progress and it
can be cancelled.
- No prefix (WSL): the openshell row hands over ONE command, linger + the
installer, and the job stops until "Check again".
- `enable_linger()` tries the user, then the admin prefix, then hands over.
- Terminal `setup`: linger (sudo) before the installer; Guide URL printed.
- Page: "Run this in a terminal on this machine:" above a command; notes
plain, no Copy; Guide links; install progress row; a fixable row shows
its command only once the app could not do it.
- Wording: switch-off text says what it means ("directly on this machine,
with the same access as your user account"); "stored in <path>" only
while the switch is on; the credential editor explains itself with an
npm example and placeholders; "needs download" hint points at the
button, not a terminal command.
The page listed "No sandbox", "macOS sandbox" and "OpenShell" as three equal radio
buttons (with "ready" next to no-sandbox, and "on this Mac" on every OS), and always
showed the network allow list and shared credentials even when they applied to nothing.
Choosing OpenShell did not say what the machine still needed; the user found out when a
session was refused.
Now:
- One switch, "Run agents in a sandbox". Off removes `sandbox_provider` from
config.toml (it used to write "direct", which also disabled a headless machine's
automatic-OpenShell rule); on picks Seatbelt on a Mac, OpenShell elsewhere, with a
dropdown when both exist. On Windows the switch is disabled with the WSL hint.
- Under the switch, for OpenShell: a readiness checklist, the same rows `openworker
machine sandbox status` prints, now with keys and whether the app may fix each one
itself, plus two new rows: free disk space for the image (about 10 GB) and Landlock in
the kernel (OpenShell refuses to start a sandbox without it). Each missing row shows
the command to run, with Copy.
- "Set up sandbox" runs `SetupJob` on the machine's server: it fixes what the app may
(bind-mount config and gateway restart, the config line, linger when no administrator
is needed, the image download with per-layer progress and Cancel) and stops at a step
that needs an administrator or an installer, showing the command; "Check again"
continues. It never runs anything as root and never turns the sandbox off. A desktop
notification says when the download is done.
- Network allow list and shared credentials are collapsible and shown only while the
switch is on.
`setup_cmd` is refactored into `steps()` (rows with keys and fixability) and one
function per fix, used by both the terminal `setup` and the job, so there is one list
of checks. New endpoints: GET /v1/settings/sandbox/readiness, GET/POST
/v1/settings/sandbox/setup, POST /v1/settings/sandbox/setup/cancel.
Copied credentials (and the policy file) went into tempfile.mkdtemp() under /tmp and
were bind-mounted into the sandbox. NVIDIA's installer runs the gateway as a systemd
user service with PrivateTmp=true, so the gateway has its own empty /tmp: the mount
source "does not exist" for it and every session with a shared credential failed to
create. Verified through /proc/<gateway pid>/mountinfo and /proc/<pid>/root/tmp.
The folder is now `<state dir>/sandbox-runtime/ow-openshell-<pid>-<random>/`: on the
real filesystem, owner-only, and a SIBLING of `sandbox/`, never a child, because
`sandbox/` is mounted read-only into every sandbox for the runner and copied keys must
not be readable from another session's sandbox. The server pid in the name lets the
registry's reap sweep the folders of a dead server, which /tmp used to do on its own.
On first use of the OpenShell sandbox, `openshell sandbox create` pulls the pinned base
image (about 5 GB). OpenWorker waited for that create with a 600 s timeout, inside the
session-start request; on a slow link the pull outlived it, the session failed with
`TimeoutExpired`, the UI showed "Starting OpenWorker..." and retried, and nothing told
the user a one-time download was the cause. Settings > Sandbox said "ready" throughout.
Now the image is part of the readiness check, keyed on the gateway's compute driver
(`openshell gateway info`), not the operating system, since the same image serves
Linux, WSL and macOS and only the Docker driver can be asked:
- `preflight()` raises `OpenShellImageMissing` (an `OpenShellUnavailable`) with the
`docker pull` command when the driver is Docker and the image is absent, so a session
is refused in a second with a message that says what to do.
- Settings > Sandbox shows OpenShell as "needs download" with a hint, and keeps it
selectable; `state` joins each provider row of /v1/settings/sandbox.
- `openworker machine sandbox status` gets a row for the image; `setup` offers the pull
last, with Docker's own progress and no time limit, on macOS too.
Any host, no proxy; the files stay confined. Strict remains the default elsewhere.
Settings offers it as a third choice; credential rows hide their hosts when it is on.