oc-box
Persistent rootless Podman development environments for unmodified OpenCode.
Run OpenCode, MCP servers, formatters, language servers, plugins, and development tools together inside a container. Give the agent control of that environment while exposing only your selected project directory by default.
Copyright (C) 2026 Paulo Dias paulo.miguel.dias@gmail.com. Licensed GPL-3.0-or-later; see NOTICE for attribution and the acknowledgment of OpenAI coding-assistant contributions through OpenCode.
Why oc-box?
oc-box grew out of wanting OpenCode to have more freedom without giving it unrestricted access to the host. A Bubblewrap-based host sandbox is useful for restricting commands, but installing system packages and experimenting with services can mean working around host restrictions. oc-box gives OpenCode its own persistent Linux development environment, where it can install tools, change system configuration, run services, and experiment.
The practical advantages for that workflow are:
- Install what the project needs: compilers, SDKs, databases, and distro packages live inside the container rather than changing the host system.
- Keep integrations working: OpenCode itself, MCP servers, formatters, language servers, and plugins run together inside the same environment.
- Choose the project's distro: Tumbleweed, Leap, Ubuntu, Debian, Fedora, Arch, or Alpine, with dependencies specific to that project.
- Pick up where you left off: installed tools, project files, and
conversations persist. Run
oc-boxagain to resume, or import an existing host OpenCode conversation and continue it inside the container. - Recover a broken environment: rebuild the container OS while retaining your project, private home, conversation history, and session IDs.
- Control resources and hardware: set CPU/RAM/process ceilings and explicitly pass through GPU/NPU devices when the project needs them.
OpenCode stays on mainline without patches, and the container matches the host's installed OpenCode version. The goal is a more capable, replaceable development environment. Rootless containers still share the host kernel, and the mounted project remains fully writable; see SECURITY.md for the boundary and tradeoffs.
Requirements and installation
- Linux, Python 3.10+, rootless Podman with pasta networking.
- Delegated CPU, memory, and process cgroup controllers.
- A host OpenCode 1.x installation. Integration checks use OpenCode 1.19.5.
- Git on the host for installation from GitHub and self-updates; curl for the one-line installer.
- Network access for initial image, package, and plugin downloads.
Install directly from GitHub on the host as your regular user, without sudo:
curl -fsSL --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/groopcd/oc-box/master/install.sh | sh
This downloads and runs the public installer; it trusts the published GitHub
source. To inspect it first, download install.sh, review it, and run sh install.sh. Git clones the full public branch history anonymously into a
temporary directory so installed versions retain their accurate commit count.
It does not use or save your GitHub/provider tokens.
Alternatively, install from a source checkout:
git clone https://github.com/groopcd/oc-box.git
cd oc-box
python3 install.py
oc-box --version
Python package channel
PyPI releases are an additional distribution channel using the same code and workspace format. After the first release is published, install the CLI in an isolated tool environment:
pipx install oc-box
# Or:
uv tool install oc-box
For standard pip, activate a virtual environment first and use
python -m pip install oc-box. System/user-site pip installs are unnecessary;
Podman, host OpenCode, and Linux setup are still external requirements.
Upgrade using the tool that owns that installation:
pipx upgrade oc-box
uv tool upgrade oc-box
# Inside the installation's virtual environment:
python -m pip install --upgrade oc-box
oc-box upgrade detects Python-package installations and prints the appropriate
command; it does not overwrite package-managed files or create a competing
GitHub-installed launcher. GitHub/curl installations retain oc-box upgrade and
opt-in automatic GitHub updates. If both channels are installed, check
command -v oc-box to select the intended launcher. PyPI package versions use
0.1.N, while oc-box --version also records +gHASH for source provenance.
Published PyPI releases are selected separately from ordinary GitHub commits.
Packaged manuals are in the package's docs/ and the tool environment's
share/man/share/info; those directories may need explicit reader paths.
The GitHub/curl installer copies the launcher to ~/.local/share/oc-box and creates
~/.local/bin/oc-box. Add that bin directory to PATH if needed. No Python/npm
dependencies or documentation build tools are required for installation.
Restart OpenCode to load new configuration. The installer stages a complete
versioned copy under ~/.local/share/oc-box/releases/, then atomically switches
the launcher. Existing sessions keep their original code until they exit;
earlier installed copies are retained, not removed while they may be in use.
For the first migration from an older flat installation, exit its active oc-box
launchers before reinstalling. The pinned-release guarantee applies afterward.
Upgrade from GitHub
oc-box upgrade --check # Check for a changed published version
oc-box upgrade # Install the current public GitHub master commit
oc-box upgrade --auto-update on
oc-box upgrade --auto-update off
Automatic updates are opt-in. When enabled, normal installed oc-box starts
check GitHub before opening a workspace, install a changed version, and continue
with the same arguments. Network/update failures print a message and continue
with the installed version. There is no background daemon or update during an
active session. Informational commands, maintenance terminals, reset/purge, and
direct source-checkout launches do not perform automatic checks.
Upgrades replace only the installed launcher/modules/manuals. They do not pull into or change a source checkout, reset containers, delete workspaces, touch provider credentials, or change the host OpenCode version. A locally installed commit ahead of the published branch is retained. No GitHub token is required; updates use the fixed public HTTPS upstream without global Git config, helpers, hooks, or forwarded environment secrets. This follows the development branch, not signed release artifacts; see SECURITY.md.
Quick start
mkdir -p ~/devel/demo
oc-box --project ~/devel/demo
On Tumbleweed the default image is
registry.opensuse.org/opensuse/tumbleweed:latest. Initial setup installs
Node/npm, Python/uv, Bash, Git, curl, ripgrep, PDF tools, and the exact host
OpenCode version. Named startup stages show progress. Subsequent launches
reuse the container, dependency caches, files, and last saved conversation.
Your project is mounted writable at the same absolute path. A private named volume provides the container home: it has your home pathname but is not your host home. You can install more system packages inside the container.
Exit OpenCode normally to synchronize conversation archives to the host and stop the environment. Running the same command resumes the original container session; stopped processes must be restarted.
Common commands
oc-box # Create/resume in the current directory
oc-box --new # New conversation, same environment
oc-box list # Workspace table and latest session IDs
oc-box --status # CPU, RAM, storage, state, limits, sessions
oc-box --status --project ~/devel/demo --json
oc-box distros # Supported images and host default
oc-box stop --project ~/devel/demo
oc-box terminal --project ~/devel/demo # Maintenance shell, without OpenCode
oc-box sync --project ~/devel/demo # Retry host history sync after a crash
oc-box repair --project ~/devel/demo # Accept a verified directory identity change
oc-box reset --project ~/devel/demo --cpus 8 --memory 16g
oc-box purge --project ~/devel/demo # Remove environment; retain source files
oc-box --about
oc-box upgrade --check
--workspace ID selects a registered workspace from any directory. It also
allows cleanup after a project directory was deleted. --status does not start
or stop containers and can run while OpenCode is active.
Persistence and deletion
| Operation | Project and unfinished edits | Private home/session IDs | Container OS |
|---|---|---|---|
Exit or stop |
Kept | Kept | Kept, stopped |
sync |
Kept | Kept; host archive refreshed | Kept, stopped |
terminal |
Kept | Kept; no history sync | Kept, prior run state restored |
reset |
Kept | Kept | Rebuilt |
purge |
Kept | Deleted | Deleted |
purge --all |
Deleted | Deleted | Deleted |
Reset removes system packages and files outside the project/private home. Home-installed dependencies, Git history, and session IDs remain. CPU/RAM/process limits and device mappings are create-time settings in oc-box; changing them for an existing container requires reset, not purge. CPU and RAM limits are ceilings, not reservations. Defaults are 4 CPU cores, 8 GiB RAM, and 2048 processes.
Purge asks for an ID-prefix confirmation. --delete-sessions additionally
deletes recorded host archive copies; --all includes that action and deletes
the project directory. Shared image caches and unrelated resources remain.
Maintenance and debugging
After exiting the workspace's OpenCode client, enter its existing container:
oc-box terminal --project ~/devel/demo
# Or, from another directory:
oc-box terminal --workspace WORKSPACE_ID
This opens a clean interactive shell at the project path, with the same mounts,
devices, limits, and rootless permissions. Type exit or Ctrl-D to leave. A
container started by this command is stopped on exit; one already running is
left running. No OpenCode launch, configuration refresh, dependency install,
session import/sync, or environment recreation occurs.
Host tokens, API keys, custom pass_env, proxies, and agent variables are not
forwarded. The shell's environment is cleared, startup files are skipped, and
history persistence is disabled. Credentials already stored in the container's
files or held by its other processes remain accessible: a maintenance shell is
not isolation from the existing container. It requires a real terminal and the
workspace's exclusive lock, so exit an active OpenCode launcher first.
Directory recovery and duplicate detection
Clearing host ~/.cache does not delete workspace registrations or private
session volumes. Workspace identity uses generic Linux VFS interfaces, not
filesystem-specific tools or branches: the canonical path and marker select the
workspace, while the filesystem ID, opaque persistent directory handle, and
inode verify its identity. Device numbers can change after reboot without
changing the directory; a matching recorded stable baseline accepts that change
automatically. The same algorithm is used for ext4, XFS, Btrfs, bcachefs, ZFS,
and other drivers that expose these capabilities. It also works when the private
oc-box registry is on a different filesystem from the project.
Existing registrations are upgraded at a normal verified launch, or by repair. If an old registration already has a device-number mismatch and no stable baseline, it needs one explicit repair: an unchanged inode alone cannot prove the filesystem is the same. Driver/kernel support varies: filesystems that do not provide a usable persistent handle/filesystem ID retain the conservative device/inode check and explicit repair when it changes. There is no universal physical-directory identifier on every Linux filesystem. Relying on only a UUID marker would accept copied/restored directories indistinguishably, so oc-box does not claim that as replacement detection. Install the updated launcher before the next reboot, then normally resume each healthy workspace to record its baseline. No container reset or session import is needed.
For a genuine or unverifiable identity change, keep the marker and use
oc-box repair after checking the project and stopping its launcher. If the
marker is missing, the launcher detects existing registrations instead of
silently creating a duplicate:
oc-box --status
oc-box repair --workspace WORKSPACE_ID
oc-box --workspace WORKSPACE_ID --resume
Repair restores a missing marker or accepts the current identity while retaining
the container, settings, and session IDs. It never replaces another workspace's
marker. If several records reference the same path, select the intended ID.
list/status distinguish identity changes, missing markers, and marker
mismatches, with specific recovery or cleanup guidance.
For a stale duplicate left by an older launcher, first recover any needed work,
then run plain oc-box purge --workspace STALE_ID, without --all or
--delete-sessions. It leaves the current project/marker and host histories
intact and retains private archive provenance under the state directory's
retired/ folder. This is saved recovery data, not a third active workspace.
Continue an existing host session
Exit the host OpenCode client first, then import once:
oc-box --project ~/devel/demo --import-session ses_HOST_SESSION_ID
The source session must belong to that project. oc-box saves a private recovery
export, verifies the imported transcript, and resumes the same session ID.
Your original host conversation is unchanged. Later launches need no import
flag. See session recovery for retry behavior.
The same flag can recover an oc-box host archive into a fresh environment after
its marker was replaced; keep the original registry and saved history export.
After plain purge with a current launcher, the retained private retired/
provenance can verify the old host archive instead. Older already-purged records
cannot be reconstructed automatically; the imported live session is unaffected.
Select a distro or map hardware
oc-box --project ~/devel/demo --distro ubuntu
oc-box reset --project ~/devel/demo --distro fedora
oc-box reset --project ~/devel/demo --device /dev/accel/accel0
oc-box reset --project ~/devel/demo --podman-arg=--shm-size=1g
Choices: Tumbleweed, Leap, Ubuntu, Debian, Fedora, Arch, and Alpine. Distro and
device settings persist per workspace. Repeat --device/--podman-arg for
multiple settings; clear old values with --clear-devices/--clear-podman-args.
Podman arguments are literal long-option tokens, not shell snippets.
Rootless device access may require crun with --group-add=keep-groups, host
ACLs, and SELinux's container_use_devices setting. oc-box checks these
prerequisites and prints advisory warnings before device-enabled creation.
See device troubleshooting for exact
host commands; oc-box never changes host policy automatically.
Integrations and permissions
Global OpenCode configuration, skills, agents, plugins, MCP settings, and auth are copied into private storage at startup. MCPs, formatters, and language servers run natively inside the container. Host-only executables must be installed there too. Changes to host configuration apply on the next launch.
Build mode allows all OpenCode tool permissions inside oc-box; Plan and
subagent rules are unchanged. The existing Bubblewrap plugin is disabled for
the container launch. HTTP service endpoints using localhost in global config
are mapped to host.containers.internal, including the host Lemonade endpoint.
Plain host OpenCode retains its normal configuration and permissions.
API-key/token environment variables are forwarded, and additional names can be
listed in ~/.config/oc-box/config.json under pass_env. Credentials remain
available to the container's processes and copied config/auth may contain
secrets. Only forward credentials you intend the agent to use. Forwarded secret
values are not embedded in Podman's command-line arguments.
Read SECURITY.md for the trust boundary and reporting guidance. Rootless containers share the host kernel, have network access, and do not have a disk quota. Snapshots/archives are not backups against disk or volume loss.
Documentation
The complete CLI reference covers every command and option, including defaults, examples, persistence, crash recovery, expected failures, configuration, resource metrics, and troubleshooting.
The installer includes prebuilt man and GNU Info pages:
man oc-box
info oc-box
If local documentation directories are not searched automatically:
man "$HOME/.local/share/man/man1/oc-box.1"
info --file "$HOME/.local/share/info/oc-box.info"
Readers are host packages (man and info on openSUSE). See
CONTRIBUTING.md to regenerate the manuals from docs/cli.md.
oc-box --version uses 0.1.N+gHASH, advancing automatically with Git commits;
installed copies retain their source version until upgraded or reinstalled.
Development status
The project is under active development. Tumbleweed host/container launch, warm resume, host-session import, MCP connection, and mapped NPU access have been exercised. The automated mainline test uses temporary databases and local model/MCP/LSP/formatter fixtures; it does not prove Podman isolation or certify every distro/runtime/device combination.
python3 -B -m unittest discover -s tests -v
python3 -B tests/smoke_mainline.py
python3 -B tools/build_docs.py --check
Other invoked programs retain their own licenses. oc-box is provided without warranty under the GPL; see LICENSE and NOTICE.
Metadata
Release files for oc-box 0.1.23
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| oc_box-0.1.23.tar.gz | 216.1 kB | With provenance | Details for oc_box-0.1.23.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| oc_box-0.1.23-py3-none-any.whl | Python 3 | none | any | Details for oc_box-0.1.23-py3-none-any.whl |
Total release size: 447.3 kB
Release files / oc_box-0.1.23.tar.gz
| Download URL | oc_box-0.1.23.tar.gz |
|---|---|
| Size | 216.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cce6564ebd671f72aa80e4f710a6d4cffd95b5d74f468a468e820267616afcc7
|
|
BLAKE2b-256 checksum How to use checksums |
8d4803b7c73971fbf463b0e9391be4149bc0d62a8768fbc2f5356887903a30f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 6, 2026.
Transparency logRelease files / oc_box-0.1.23-py3-none-any.whl
| Download URL | oc_box-0.1.23-py3-none-any.whl |
|---|---|
| Size | 231.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8931da4632ca2204945cc6286b235646c7180cd3c66f93b88e58505c9194976a
|
|
BLAKE2b-256 checksum How to use checksums |
47071e356e7ae248246a320895cda4d893d5acdf1acc3d9d82b1f03248f53931
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 6, 2026.
Transparency logSecurity for oc-box 0.1.23
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for oc_box-0.1.23.tar.gz
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 Nov 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.