Run ad-hoc, heavy, or untrusted code off your machine — from inside Claude Code.
A Claude Code plugin that gives Claude a skill + 21 slash commands driving the authed createos CLI. Work runs in disposable CreateOS Sandboxes — roughly 200 ms from create to your first command — that self-destruct when done.
- Why offload?
- How it works
- Two patterns
- Install
- Requirements
- Command reference
- Egress control
- Heavy builds
- Uploads & excludes
- Shapes
- The skill
- Hooks
- Direct CLI cookbook
- Safety & scope
- Environment variables
- Troubleshooting
Heavy builds, flaky test suites, and untrusted code don't belong on your laptop:
- Untrusted code — a dependency's post-install script or an AI-generated snippet runs in a disposable Sandbox, not your shell. Egress can be locked to an exact allowlist.
- Heavy work —
npm ci,cargo build,pytest,go test, torch installs — keep your machine cool and free while a bigger box does the grind. - Parallel / matrix — split a test suite or a version matrix across N isolated boxes at once.
- Clean-room repros — reproduce a bug on a pristine Linux box with nothing of yours leaking in.
- Live dev loops — a reusable per-repo box with file sync, so a dev server or watcher inside the box reacts to your local edits.
Claude Code
│ slash command (/createos-sandbox:offload …) or the skill decides to offload
▼
scripts/cos ← this plugin's driver (a portable bash script)
│ wraps + hardens
▼
createos CLI ← authed control-plane client (auto-installs on first use)
│
▼
CreateOS Sandbox ← your code runs here, then the box is destroyed
The plugin is a thin Claude-facing surface over the createos CLI. It ships four things:
| Piece | Path | Role |
|---|---|---|
| Slash commands | commands/*.md |
22 commands (offload, exec, fanout, shell, …), each a thin wrapper that calls scripts/cos |
| Skill | skills/using-createos-sandbox/SKILL.md + references/ |
teaches Claude when to reach for the sandbox on its own, with depth loaded on demand |
| Hooks | hooks/hooks.json + scripts/ |
SessionStart publishes the driver's absolute path; PreToolUse(Bash) nudges on heavy build/test commands |
| Driver | scripts/cos |
the actual logic — staging, egress, keepalive, sync, networking, lifecycle, state |
Slash commands invoke ${CLAUDE_PLUGIN_ROOT}/scripts/cos, which Claude Code expands when the command is loaded. The Bash tool's own environment does not carry that variable, so when the skill decides to offload on its own it needs a real path — that is what the SessionStart hook supplies. Without it the path collapses to /scripts/cos, and the observed failure mode is Claude quietly rebuilding the offload out of raw CLI primitives, losing egress restriction, keepalive, auto-destroy, and the auth preflight along the way. See ADR-0001.
1. One-shot offload (default, safe)
Stage a dir → exec → optionally pull artifacts → auto-destroy. One-way: box-side changes never touch local unless you ask (-o). Best for builds, tests, and untrusted code.
2. Live session (opt-in)
A reusable per-repo box + one-way file sync (default; -2 for two-way). A dev server or watcher inside the box reacts to your local edits. Best for interactive dev loops, long-running services, and networking.
From the marketplace (recommended):
/plugin marketplace add NodeOps-app/createos-plugin
/plugin install createos-sandbox@createos
Dev (instant, no install):
claude --plugin-dir /path/to/createos-plugin/packages/claude-code-plugin
/reload-plugins # after editing plugin filescos is not on PATH by default. To use bare cos in your own terminal, run its installer once by absolute path:
/path/to/createos-plugin/packages/claude-code-plugin/scripts/cos install # symlinks to ~/.local/bin/cos${CLAUDE_PLUGIN_ROOT} only expands inside slash-command frontmatter — it is not set in your shell, nor in Claude's Bash tool environment. Slash commands resolve the path for you; for autonomous skill use the SessionStart hook publishes it. Running cos install once removes the question entirely.
- A CreateOS account. At session start the
SessionStarthook runscos setup, which installs thecreateosCLI with the official one-liner (curl -sfL …/install.sh | sh -) if missing, and otherwise re-runs it in the background to upgrade in place (same directory, neversudo). Opt out withCOS_NO_AUTOINSTALL=1; override the install source withCOS_CLI_INSTALL_URL. A binary set viaCOS_CLIis never auto-installed. Re-runcos setupby hand any time. - Sign in, one of two ways. Verify either with
cos auth.- Browser (recommended) — if
createos whoamisays you are signed out andtmuxis available,cos setupstartscreateos loginin a hidden tmux session (createos-login), picks "Sign in with browser", and your default browser opens the CreateOS sign-in page — finish it there (a fallback URL is printed if no browser opens). Withouttmux, runcreateos loginin your own terminal and pick "Sign in with browser". The OAuth session lands in~/.createos/.oauth. - API key —
export CREATEOS_API_KEY=<key>(grab one from the dashboard). Set it and browser sign-in is skipped entirely — the right choice for CI, headless boxes, and devcontainers. Export it in the shell that launches Claude Code; never paste it into a Claude conversation, where it would be written to the transcript. To store a token via the CLI instead,tmux kill-session -t createos-login, then runcreateos loginyourself and pick "Sign in with API token".
- Browser (recommended) — if
- Host tools:
jq,tar,bash,base64(required);perl(ANSI stripping / path resolution);shasum(falls back tosha1sum/sha256sum);curl(CLI install/upgrade);tmux(optional, automatic browser sign-in). - Optional per feature:
wg-quick+sudoforvpn; the currentcreateosCLI for sync--mode/--exclude(an old CLI falls back to two-way with a warning).
| Command | Summary |
|---|---|
offload [flags] <dir> <cmd> |
one-shot: stage → run (keepalive) → pull → destroy |
exec [-l lang] [-i stdin] [-t secs] [-N] <file|-> [args] |
run one source file (untrusted/ad-hoc) in a throwaway box |
fanout [-j N] [flags] <dir> <cmd1> [cmd2] … |
run each command in its own throwaway box, in parallel |
agent [flags] <agent> <dir> <prompt> |
run claude/codex/opencode/pi/cursor on your code in a box |
shell [-s] [-r] [-e|-p|-E] |
instant throwaway interactive Linux (destroyed on exit) |
up [-s] [-r] [-n] [-e|-p|-E] |
create/reuse the per-repo project box |
run <cmd> |
exec in the project box (streamed, state persists) |
sync [-2|-M] [-x glob] <local-dir> [remote-dir] |
file sync into the project box (background) |
tunnel <remote> [local] |
forward a box port to 127.0.0.1 (private) |
expose <port> |
public HTTPS URL for a box port |
unexpose |
revoke the public URL / disable ingress |
desktop [-s shape] [-S screen-N] |
graphical desktop:1 box + a live noVNC URL |
computer <op> |
drive that desktop — screenshot, click, type, key, open, windows |
cluster up <N> | run […] <cmd> | ls | down |
N boxes on one private network, name-addressable |
disk create | ls | show | attach | detach | rm |
BYO S3 bucket mounts on the project box |
vpn [register <name> | up] |
WireGuard L3 into your private networks |
fork |
snapshot the project box → independent clone |
pause · resume |
park the warm project box at zero compute cost / restore it exactly |
template submit <name> [-f Dockerfile] | ls | show | logs | rm |
build a custom rootfs so boxes boot pre-provisioned |
down |
stop sync/tunnels + destroy the project box (+ cluster) |
status |
show active box + sync + tunnels + cluster |
Flag order: flags (
-s/-r/-e/-p/-E/-o/-x/-w/-v/-K) come before the positional<dir> <cmd>.
The core command. Stages a directory into a fresh box, runs a command, optionally pulls artifacts back, then destroys the box.
/createos-sandbox:offload [-p preset] [-e dom] [-E] [-x glob] [-o out] [-w GB] [-K] [-s shape] [-r rootfs] <dir> <cmd>
| Flag | Meaning |
|---|---|
-s <shape> |
box size (default s-1vcpu-1gb; see Shapes) |
-r <rootfs> |
root filesystem image (default devbox:1) |
-o <out> |
tar a box-side dir and pull it back to local |
-w <GB> |
attempt swap (see caveat under Heavy builds) |
-K |
keep the box on a real failure so you can inspect it |
-e <domain> |
allow one egress domain (repeatable) |
-p <preset> |
egress preset — python-uv | rust-cargo | npm | github | openrouter | openai | anthropic | cursor (repeatable, composes with -e) |
-E |
unrestricted egress (trusted offload) |
-x <glob> |
extra upload exclude (repeatable) |
-v KEY[=VAL] |
declare an env var in the box (repeatable). Bare KEY forwards the value from your own shell, so a secret stays out of shell history and the Claude transcript |
# Run a test suite, pull nothing, box auto-destroys
/createos-sandbox:offload . "npm ci && npm test"
# Python build with locked egress, pull the dist/ folder back
/createos-sandbox:offload -p python-uv -o dist . "uv sync --frozen && uv run python -m build"Run untrusted code or any ad-hoc script — anything you would rather not run locally — as one source file in a throwaway box. No directory to stage.
/createos-sandbox:exec [-l lang] [-i stdin-file] [-t secs] [-N] [-p preset] [-e dom] <file> [args...]
Languages py js mjs cjs ts go sh rb c cpp rs, picked from the extension or -l. -i feeds stdin, -t caps wall-clock time (default 120 s, exit 124), -N denies all egress (default is unrestricted, like offload). stdout/stderr and the exit code are the program's own.
Keepalive: long or quiet compiles no longer die to exec-stream idle resets — the command runs detached with a heartbeat and re-attaches if the stream drops, so the build (and its cache) survives.
Run each command in its own throwaway box, in parallel, then collect results. Ideal for splitting a test suite or a version matrix.
/createos-sandbox:fanout [-j N] [-p preset] [-s shape] [-r rootfs] [-e dom] [-E] [-x glob] <dir> <cmd1> [cmd2] …
-j N— max concurrent boxes (default 2, the external-key running-quota). Other flags mirroroffload.- Exit codes are aggregated across boxes; each box is fully isolated from the others.
/createos-sandbox:fanout -j 2 -p python-uv . "pytest -q tests/a" "pytest -q tests/b" "pytest -q tests/c"An instant, interactive Linux box that is destroyed on exit.
/createos-sandbox:shell [-s shape] [-r rootfs] [-e dom | -p preset | -E]
Interactive — it takes over the terminal, so it can't run as a foreground agent command. Run it yourself: type
!cos shellin the Claude prompt (needscos installfirst, or use the full path). Egress is unrestricted by default likeoffload;-e/-prestrict it.
A reusable, per-repo box addressed by your working directory. up creates it (or adopts an existing running one); run execs in it with persistent state; sync streams files in; down tears it all down.
/createos-sandbox:up [-s shape] [-r rootfs] [-n name] [-e dom | -p preset | -E]
/createos-sandbox:run <cmd>
/createos-sandbox:sync [-2 | -M] [-x glob] <local-dir> [remote-dir]
/createos-sandbox:down
/createos-sandbox:status
upis idempotent — run it again on the same repo and it reuses the same box instead of creating a new one.runstreams output and keeps box-side state between calls (installed deps, running processes).syncis one-way by default (laptop → box).-2= two-way (box writes flow back),-M= mirror (deletes box-side extras),-x <glob>= exclude. First run downloads Mutagen (~60–90 s before edits propagate). Install deps inside the box (cos run 'npm ci') rather than syncingnode_modulesup — big/regenerable dirs are excluded by default.pause/resumepark the box at zero compute cost and bring it back exactly — see Pause, resume, and custom images. Preferpauseoverdownwhen the box has a warm toolchain you'll want again.downstops sync + tunnels and destroys the project box (and any cluster). The next session starts from scratch.statusshows the active box + sync + tunnels + cluster.
/createos-sandbox:up
/createos-sandbox:run "npm ci"
/createos-sandbox:sync . /work # local edits stream into /work
/createos-sandbox:run "npm run dev &"
/createos-sandbox:down| Command | What it gives you |
|---|---|
tunnel <remote> [local] |
Reach a box-side service on your laptop. Run a dev server in the box, then tunnel 3000 → http://127.0.0.1:3000. Private, background, no public URL. Stopped by down. |
expose <port> |
A public HTTPS link for a port — <id>-<port>.app.sb.createos.sh, stable for the box's lifetime. The service must bind 0.0.0.0. Anyone with the link can reach it. Revoke with unexpose. |
unexpose |
Revoke the public URL / disable ingress on the active box. |
cluster up <N> |
N boxes on one private network, reaching each other by fully-qualified name (curl http://cos-cl-<key>-2.fc.local:8080 — the bare short name is NXDOMAIN). cluster run -a '<cmd>' fans a command across all; cluster run <name|idx> '<cmd>' targets one. cluster ls / cluster down manage them. For distributed-system / DB-replication / p2p / load-test repros. Counts against quota — keep N small. |
vpn register <name> then vpn up |
Join your laptop to the whole private network over WireGuard (reach every sandbox by name/IP). vpn up needs wg-quick + sudo and blocks until Ctrl-C — run it in your own terminal (!cos vpn up). |
fork |
Snapshot the warm project box → an independent clone for matrix/parallel experiments. The fork is self-managed. |
Some work needs a screen — a real (not headless) browser, a GUI app, an installer that only exists as a wizard. desktop puts the project box on the desktop:1 rootfs (XFCE, Google Chrome, xdotool/wmctrl/scrot/xclip) and hands back a live noVNC URL; computer drives that same desktop from the agent side.
cos desktop # desktop:1 box + ingress + noVNC URL (waits for the desktop to boot)
cos computer screen # {"width":1280,"height":800} — the coordinate space
cos computer screenshot # PNG → prints a path (Claude opens it with Read)
cos computer open https://example.com
cos computer click 640 400
cos computer type 'hello'
cos computer key ctrl l # a chord
cos computer help # every op, plus `raw` for the rest of the APIThe two halves compose: the URL lets you watch and take over in a browser while Claude acts through computer. desktop:1 ships the same five agent CLIs devbox:1 does, so "run an agent on a box and watch its screen" needs no extra setup — see Coding agents.
| Gotcha | Detail |
|---|---|
| The link is a bearer | Anyone holding the noVNC URL can drive the desktop, and its token expires (the command prints when). Re-running desktop mints a fresh one and invalidates the old link for new connections. |
| Boots late | The desktop stack starts after the box reports running. cos desktop polls for readiness; a bare cos up -r desktop:1 does not. |
409 is ambiguous |
The control plane returns desktop_unavailable both while the desktop is coming up and when an action fails on a live desktop. Not a signal that the box is broken. |
| Raw pixels | Coordinates are unscaled X11 pixels of that screen. Read the bounds from cos computer screen. |
| Needs ingress | desktop enables it for you. unexpose turns it off and kills the link. |
desktopandcomputerare the onlycoscommands that call the CreateOS REST API directly — thecreateosCLI has no computer or desktop command yet. Everything else shells out to the CLI as usual. Auth is reused as-is:CREATEOS_API_KEYor~/.createos/.tokengo out asX-Api-Key, a browser session's JWT asX-Access-Token.
/createos-sandbox:desktop [-s shape] [-S screen-N]
/createos-sandbox:computer screenshot | screen | cursor | move <x> <y> | click [<x> <y>]
| type <text> | key <k>… | open <url> | windows | raw <M> <path> [json]
/createos-sandbox:pause
/createos-sandbox:resume
/createos-sandbox:template submit <name> [-f Dockerfile] | ls | show <name> | logs [-f] <name> | rm <name>
pause is the cheap way to end a session. It snapshots the whole box — disk, memory, and running processes — frees the host, and stops compute billing, so a box with a warm toolchain costs nothing while parked. resume restores it exactly — files, installed dependencies, and processes that were running when it paused. End to end through the CLI both operations land around 6–8 s (measured), and resume is slower when the snapshot has to be pulled to a different host than the one it was taken on. down, by contrast, destroys the box and forces the next session to reinstall everything.
pause stops the file sync and any tunnels first, since a paused box serves no traffic. Re-run sync / tunnel / expose after resuming. The project box also carries a 30-minute idle auto-pause as a backstop; set it at create time with cos up -P 22h (max 24h), or on a live box with createos sandbox edit <id> --auto-pause 4h, when a box is serving an exposed URL that people will hit intermittently, or the demo will look dead between visitors.
template bakes a toolchain into an image so boxes boot pre-provisioned instead of running the same install prelude on every offload. The build service enforces constraints that would otherwise only surface as a rejection after upload, so cos preflights the Dockerfile locally: exactly one FROM (single-stage), an operator-allowlisted base written literally (no ARG substitution), no COPY/ADD (no build context is uploaded — fetch inside a RUN), and 64 KiB of source at most. Two builds run concurrently per account.
/createos-sandbox:template submit myimage -f Dockerfile
/createos-sandbox:up -r myimage # or: offload -r myimage . "<cmd>"A custom image is slow on its first boot on each host (that host has to fetch it) and fast on every boot after. The built-ins — devbox:1 (Ubuntu 24.04, batteries included), ubuntu:26.04, debian:13, alpine:3.20 — are kept warm and never pull; createos sandbox rootfs lists them.
Mount your own S3 bucket into the project box.
/createos-sandbox:disk create <name> --bucket <b> --endpoint <url> --access-key <k> --secret-key <s>
/createos-sandbox:disk ls
/createos-sandbox:disk show <name>
/createos-sandbox:disk attach <disk> <mount>
/createos-sandbox:disk detach
/createos-sandbox:disk rm
/createos-sandbox:disk create data --bucket my-bucket --endpoint https://s3.amazonaws.com --access-key … --secret-key …
/createos-sandbox:disk attach data /mnt/datadevbox:1 ships five agent CLIs, so running one in a box needs no install step:
| CLI | Version seen | Third-party providers |
|---|---|---|
claude |
2.1.260 | OpenRouter, Anthropic, any Anthropic-compatible endpoint |
codex |
0.153.4 | OpenRouter, OpenAI, any Responses-compatible endpoint |
opencode |
1.18.30 | anything — catalog providers or a custom base URL |
pi |
0.85.1 | anything — built-in providers or a custom base URL |
cursor-agent |
2026.09.10 | none — Cursor's own service only |
desktop:1 ships the same five.
/createos-sandbox:agent [-P provider] [-m model] [-k KEYVAR] [-o .] [-p preset] [-s shape] <agent> <dir> <prompt>
export OPENROUTER_API_KEY=sk-or-...
/createos-sandbox:agent -m openai/gpt-5.6-luna -o . claude . "fix the failing tests"
/createos-sandbox:agent -P anthropic -m claude-sonnet-4-5 -p anthropic -o . pi . "add type hints"
/createos-sandbox:agent -P https://gw.example.com/v1 -k MY_KEY -o . codex . "port this to v2"It stages the directory, wires the agent to the provider, runs it headless with permission gates off — the microVM is the isolation — pulls the edits back with -o ., and destroys the box. -P defaults to openrouter; -k names the host env var holding the key (default OPENROUTER_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY / CURSOR_API_KEY). Every offload flag works too.
| Gotcha | Detail |
|---|---|
-o . or it's gone |
Without it the box is destroyed with the agent's edits inside. Run on a clean tree so git diff shows what changed. |
| Key comes from your shell | -v/-k forward the value out of the launching shell. Never paste a provider key into a Claude conversation — it lands in the transcript. |
openrouter.ai/api ≠ /api/v1 |
The first is Anthropic-compatible (what claude needs), the second OpenAI-shaped (what codex and opencode need). Wrong one gives an unhelpful 404. |
| codex is Responses-only | wire_api = "chat" is rejected at config load, so a plain Chat-Completions gateway (vLLM, LiteLLM in chat mode) cannot drive it. |
| cursor-agent can't be repointed | Its --endpoint/CURSOR_API_ENDPOINT expects Cursor's own protocol; BYO-key is an IDE-chat-only feature. |
claude needs IS_SANDBOX=1 |
The box is root, and --dangerously-skip-permissions refuses to run as root without it. cos agent sets it. |
Full per-agent env blocks and the wire-protocol matrix live in references/coding-agents.md.
By default, egress is unrestricted — the box can reach any host, and cos prints a one-line ⚠ egress UNRESTRICTED notice. To isolate an untrusted build, restrict outbound to an exact set:
-e <domain>— allow one domain (repeatable).-p <preset>— a curated bundle of the registries + CDNs a build actually reaches (repeatable, composes with-e).-E— force explicitly unrestricted (silences the warning for trusted offloads).
| Preset | Hosts allowed |
|---|---|
python-uv |
astral.sh, releases.astral.sh, pypi.org, files.pythonhosted.org |
rust-cargo |
crates.io, static.crates.io, index.crates.io, static.rust-lang.org, cdn.pyke.io |
npm |
registry.npmjs.org |
github |
github.com, objects.githubusercontent.com, raw.githubusercontent.com, codeload.github.com |
openrouter |
openrouter.ai |
openai |
api.openai.com |
anthropic |
api.anthropic.com, statsig.anthropic.com |
cursor |
the *.cursor.sh / downloads.cursor.com set Cursor CLI needs |
cdn.pyke.iois included inrust-cargobecauseort-sys(ONNX Runtime) fetches from it. Compose presets and add stragglers with-e <host>:/createos-sandbox:offload -p python-uv -p rust-cargo -e cdn.example.com . "…"
How the allowlist is enforced. Rules can be a domain, *.domain, an IP, a CIDR, or any of those with a :port, and the list is allow-only — there is no deny token, so every destination a job needs has to be enumerated. Enforcement is not uniform, and the difference matters when blocking exfiltration is the actual goal:
- IP and CIDR rules apply immediately and cannot be bypassed from inside the box.
- Domain rules take roughly 30 seconds to apply after being set, and are a strong control for HTTPS traffic but a weak one for cleartext HTTP.
So a domain allowlist is the right control for "this build should only reach pypi and crates.io", and IP/CIDR rules are the right control for an adversarial workload. DNS keeps resolving for blocked destinations — a blocked connection fails as a connection error (e.g. curl exit 35), not a name-resolution failure, so checking whether DNS works will mislead you when debugging.
- Egress: default unrestricted; use
-p/-eto lock it down (see above). - Keepalive: long/quiet compiles run detached with a heartbeat and survive stream drops.
-Kkeeps the box on a real failure for inspection. - Excludes:
.git/target/node_modules/__pycache__/.venv/media are excluded from the upload by default;-x <glob>adds more. - Swap caveat:
-w <GB>attempts swap, butdevbox:1can'tswapontoday — so a torch/maturin build on a small box may OOM/ENOSPC. Install only the extra/group you need (e.g.uv sync --group dev) rather than--all-extras. - Shape rejection: a shape your account can't use fails with a clean
Allowed: [...]list — pick from it. - Bandwidth: each box starts with a fixed allowance for traffic it initiates (5 GiB by default). A large model download or
docker pullcan exhaust it, after which outbound traffic stops. Top it up additively viacreateos sandbox edit <id>; the exec, file-transfer, and tunnel channels stay reachable regardless.
Uploads (offload/fanout) and one-way sync skip big/regenerable dirs by default so you don't ship node_modules over the wire:
.git target node_modules __pycache__ .venv .mypy_cache .pytest_cache
.gradle .cargo/registry dist build .next .turbo *.gif *.mp4 *.mov *.zst
Add more with -x <glob> (repeatable). Install dependencies inside the box (cos run 'npm ci') rather than syncing them up.
- Default shape:
s-1vcpu-1gb. Default rootfs:devbox:1. - Pick a bigger box with
-s <shape>. A shape your account can't use is rejected with anAllowed: [...]list — pick from it rather than guessing. - Discover what you can boot:
createos sandbox shapes(sizes) andcreateos sandbox rootfs(images).
using-createos-sandbox teaches Claude when to offload — untrusted code, heavy builds/tests, parallel/matrix work, clean-room repros, live dev loops — so it reaches for the sandbox on its own instead of grinding on your laptop. It drives the same scripts/cos helper the slash commands use.
SKILL.md stays a decision surface; the depth lives in skills/using-createos-sandbox/references/ and is loaded only when a task needs it:
| Reference | Covers |
|---|---|
offload-and-egress.md |
offload flag table, egress presets and enforcement caveats, fanout, upload excludes, heavy-build OOM/disk/bandwidth traps |
networking.md |
choosing between tunnel/expose/cluster/vpn, cluster DNS names, expose gotchas, WireGuard setup |
lifecycle-and-images.md |
pause/resume, auto-pause tuning, fork caveats, built-in rootfs vs custom templates, env vars, remote editor, self-terminating jobs, single-file transfer, measured timings |
docs.md |
index of every live CreateOS Sandbox docs page as a raw .md URL, so Claude fetches the current REST / SDK / CLI reference on demand instead of carrying it |
SessionStart(scripts/session-start.sh) publishes the driver's absolute path into Claude's context. This is what makes autonomous, skill-driven offload work at all — see How it works and ADR-0001. It also runscos setup(CLI install/upgrade, browser sign-in if signed out) and adds the resulting CLI version/path and sign-in status to Claude's context.PreToolUse(Bash)(scripts/offload-hint.sh) watches for heavy build/test commands (npm ci,make,pytest,go test,cargo build,pip install, …) and adds a one-line nudge to consider/createos-sandbox:offload. The command still runs — the hook only suggests — and it skips sandbox/git/docker commands. Silence it withCOS_NO_HINT=1.
cos works standalone, no Claude required:
cos install # symlink onto PATH (once); createos CLI auto-installs on first use
cos setup # install/upgrade the createos CLI; start browser sign-in if signed out
cos offload -p python-uv . 'uv sync --frozen --group dev && uv run pytest -q'
cos offload -p python-uv -p rust-cargo -x target -o dist . 'uv sync --frozen && uv run pytest -q'
cos up && cos run 'npm ci' && cos sync ~/app /work # reusable box + one-way sync
cos fanout -j 2 -p python-uv . 'pytest tests/a' 'pytest tests/b' # parallel, isolated boxes
cos shell # instant throwaway Linux (destroyed on exit)
cos run 'npm run dev &' && cos tunnel 3000 # dev server → http://127.0.0.1:3000
cos expose 8080 # public HTTPS URL for port 8080
cos desktop && cos computer open https://example.com # graphical box + noVNC URL, then drive it
cos computer screenshot # PNG of the desktop → prints a path
cos cluster up 3 && cos cluster run -a 'hostname' # 3 boxes, one private net
cos disk create data --bucket my-b --endpoint https://s3.amazonaws.com --access-key … --secret-key …
cos disk attach data /mnt/data # mount S3 into the project box
cos fork # snapshot → independent clone
cos template submit myimage -f Dockerfile # bake a toolchain into a reusable image
cos pause # park the warm box at zero compute cost
cos resume # bring it back exactly as it was
cos down # stops sync/tunnels, destroys box + cluster- One-way by default.
syncand offload uploads are laptop → box; box-side writes never touch local unless you opt into-2(two-way) or pull withoffload -o. Use-2deliberately — never casually on a repo root.-M(mirror) additionally deletes box-side extras. - Scoped statefile.
cosonly ever touches boxes it created (cos-*) or the project box recorded in its statefile. Your other sandboxes are never touched. State lives at${COS_STATE_DIR:-${XDG_CACHE_HOME:-~/.cache}/createos-sandbox}/<project>.json(plus a.tunnelssidecar). - Quota. External keys have been observed to allow 2 boxes running at once, with a daily creation cap. Neither is published policy — treat them as observed behaviour, budget
cluster/fanoutagainst them, and expect excess jobs to queue rather than fail. - Ending a session.
pausekeeps the box (and its warm state) at zero compute cost;downdestroys it. Either is fine — leaving a box running is not. - Untrusted code. Restrict egress (
-p/-e) so a malicious dependency can't exfiltrate or phone home.
| Variable | Effect |
|---|---|
CREATEOS_API_KEY |
sign in without browser login (CI / headless / devcontainers); verify with cos auth |
COS_NO_AUTOINSTALL=1 |
don't auto-install the createos CLI |
COS_CLI_INSTALL_URL |
override the CLI install-script source |
COS_CLI |
use a specific createos binary instead of the one on PATH |
COS_STATE_DIR |
override the statefile directory (default ${XDG_CACHE_HOME:-~/.cache}/createos-sandbox) |
COS_NO_HINT=1 |
silence the auto-suggest hook |
cos: not signed in to CreateOS— runcos setup(starts browser sign-in in a hidden tmux session), orcreateos loginin your own terminal (not via Claude — it needs a TTY), orexport CREATEOS_API_KEY=<key>. Verify withcos auth.- Browser sign-in never opened — use the fallback URL
cos setupprinted, ortmux kill-session -t createos-loginand runcreateos loginyourself. createos: command not found— the auto-install landed outsidePATH; add~/.local/binto it and retry.cos: command not found— runcos installby its full path (theSessionStarthook prints that path into Claude's context), or callcosby full path. Do not type${CLAUDE_PLUGIN_ROOT}into a Bash command — that variable is unset in the Bash tool's environment and the path resolves to/scripts/cos./scripts/cos: no such file— same cause. Never work around it by rebuilding the offload from rawcreateos sandbox create/push/exec: that drops egress restriction, keepalive, auto-destroy, and the auth preflight.- Shape rejected — run
createos sandbox shapesand pick from theAllowed: [...]list in the error. - Box says paused /
cos runrefuses — the 30 m idle auto-pause fired, or someone ranpause.cos resumebrings it back; raise the timeout withcos up -P 22hat create time orcreateos sandbox edit <id> --auto-pause 4hon a live box. - Template build rejected —
cos template submitpreflights single-stage/no-COPY/64 KiB locally; a rejection past that is usually a base image outside the operator allowlist. - Build can't reach a host — you restricted egress; add the host with
-e <domain>or the right-p <preset>. synccopiednode_modulesanyway — yourcreateosCLI is old and lacks--exclude; upgrade it (coswarns when this happens).- Build OOM/ENOSPC on a small box — bump
-s <shape>;-wswap doesn't work ondevbox:1. Install only the deps you need. - Public
exposeURL returns nothing — the service must bind0.0.0.0, not127.0.0.1.
Part of the createos Claude Code plugin marketplace · createos.sh