Claude Code and OpenAI Codex on any Windows, macOS, or Linux machine — or a flash drive — with no system-wide install.
PortableCoder bundles a portable Node.js runtime and the selected tool's npm package inside one self-contained folder. Credentials are stored portably inside the folder (no registry writes, no system config, no admin rights), so the whole thing travels — USB stick, network share, or a plain copy to a new machine.
Supported tools: Claude Code and OpenAI Codex CLI. More can be added via
scripts/adapters/catalog.json— no code changes required.
| 🚀 Portable runtime | 🔐 Portable auth | 🔄 Auto-updates | 🛡️ VM mode |
|---|---|---|---|
Bundled Node.js + tool installs under runtime/ — no system Node needed, ever: pcoder bootstraps its own |
OAuth credentials live in state/auth/<tool>/ and travel with the folder |
Daily npm check at launch, fail-open when offline, safe reinstall path | Optional QEMU Linux VM on Windows for isolated runs (Claude only) |
No system Node.js required. Clone the repo and run the commands below — pcoder obtains its own Node.js on first use.
:: Windows
scripts\pcoder setup --init
scripts\pcoder runtime bootstrap-host-native --tool all# Linux / macOS
scripts/pcoder setup --init
scripts/pcoder runtime bootstrap-host-native --tool allThis installs the requested tools into the folder (~50 MB per tool). Pass --tool claude or --tool codex to install just one. Run pcoder doctor afterwards to verify — it reports the resolved runner for every supported tool.
runtime/ is gitignored, so a fresh clone contains no bundled Node.js. pcoder resolves a runtime in three steps:
- bundled
runtime/node/— used if present - system
nodeonPATH— used if found - neither: auto-bootstrap.
pcoderdownloads a portable Node.js intoruntime/node/, verifies it against the officialSHASUMS256.txt, extracts it, and re-runs itself. Nothing is installed system-wide and nothing outside the folder is touched.
Step 3 runs without Node.js by design — the regular bootstrap (bootstrap-host-native.cjs) can't do this job because it is itself a Node script:
| Host | Script | Requires |
|---|---|---|
| Windows | scripts\runtime\bootstrap-node.ps1 |
Windows PowerShell 5.1 (built in) or pwsh |
| Linux / macOS | scripts/runtime/bootstrap-node.sh |
bash, curl or wget, tar, and xz on Linux |
Ubuntu and Alma/RHEL have everything needed out of the box, except that minimal RHEL/Alma images may omit xz — install it with sudo dnf install -y xz (Debian/Ubuntu: sudo apt-get install -y xz-utils) if the bootstrap asks for it.
You can also run either script directly, for example to pre-seed a machine before using pcoder:
powershell -ExecutionPolicy Bypass -File scripts\runtime\bootstrap-node.ps1bash scripts/runtime/bootstrap-node.shWindows: use the full command above, not
scripts\runtime\bootstrap-node.ps1on its own. The scripts in this repo are unsigned, so on a machine whose execution policy isAllSigned(orRemoteSigned, if the folder was downloaded as a zip) PowerShell refuses to load the file:... is not digitally signed. You cannot run this script on the current system.
-ExecutionPolicy Bypassapplies to that one invocation only and changes nothing on the machine. You do not need it when usingpcoder—scripts\pcoderalready passes it when it auto-bootstraps. See Execution policy if the flag doesn't help.
To turn the automatic step off — for an air-gapped host, or when you'd rather supply Node.js yourself — set PCODER_AUTO_BOOTSTRAP=0 (matching PCODER_AUTO_UPDATE=0). pcoder will then fail with instructions instead of downloading anything.
After the first bootstrap the whole folder is self-contained: copy it to a machine with no Node.js at all and it runs as-is.
scripts\pcoder auth login :: Claude Code (default) — Anthropic OAuth
scripts\pcoder auth login --tool codex :: OpenAI Codex — ChatGPT account or API keyEach login runs the tool's own login flow with HOME rewritten to a portable directory, so credentials land under state/auth/<tool>/host/home/ (gitignored) instead of your user profile. pcoder auth status shows the state for every tool.
Codex logs in with device authorization. pcoder invokes codex login --device-auth, which prints a URL and a short code to enter on any device with a browser, instead of opening a local browser and waiting on a localhost callback. That suits how PortableCoder is typically run — over SSH, on a headless or locked-down box, or from a folder on a flash drive — where a loopback redirect often can't complete. Claude Code continues to use its own default login flow.
scripts\pcoder :: default tool, current directory
scripts\pcoder codex :: a specific tool
scripts\pcoder run --project C:\my-project :: a specific project folder
scripts\pcoder claude -p "explain this codebase"After a tool name, remaining arguments are forwarded as-is — no -- separator needed unless an argument collides with a pcoder flag (--project, --mode, --tool, --no-sync-back). On Linux/macOS use scripts/pcoder with forward slashes.
flowchart LR
A["scripts/pcoder<br/>(.cmd / bash)"] --> B["pcoder.cjs<br/>launcher"]
B --> C{"run mode?"}
C -->|host-native| D["auto-update check<br/>(daily, fail-open)"]
D --> E["rewrite HOME to<br/>state/auth/<tool>/"]
E --> F["launch bundled tool<br/>runtime/<tool>/"]
C -->|linux-portable<br/>Windows only| G["QEMU VM<br/>WHPX → TCG fallback"]
G --> H["sync project via SSH,<br/>run tool in guest"]
scripts/adapters/catalog.json is the source of truth for tool dispatch: every entry with an npm_package becomes a launchable tool, with its own env vars, config directory, and auth wiring resolved from the catalog at startup.
| Mode | How it works | File access | Tools | Setup |
|---|---|---|---|---|
| host-native (default) | Runs the tool directly on the host via bundled Node.js | Full host filesystem | claude, codex | runtime bootstrap-host-native |
| linux-portable | Runs the tool inside a QEMU Linux VM | Synced project directory only | claude only | runtime bootstrap |
On Windows, the configured windows_default_mode is used unless an auto-fallback applies: host-native is preferred when the bundled tool runtime exists (no VM startup cost), and forced when the tool's catalog entry has vm_supported: false (currently codex). On Linux/macOS, host-native is always used; linux-portable there reuses the same isolated-auth wiring without a VM.
scripts\pcoder setup --windows-mode linux-portable :: change the default
scripts\pcoder run --mode linux-portable :: override one session
scripts\pcoder setup --default-tool codex :: pick a default toolBy default, pcoder checks npm for a newer version of the bundled tool at most once per 24 hours when launching it in host-native mode (3-second timeout; if the registry is unreachable the installed version launches immediately). When an update is found it is installed via the safe bootstrap path before the tool starts.
scripts\pcoder setup --auto-update false :: disable permanently
set PCODER_AUTO_UPDATE=0 :: disable for one invocationUse the bootstrap with --force — it pulls the latest version published to npm and writes cleanly into runtime/<tool>/:
scripts\pcoder runtime bootstrap-host-native --tool all --force--force re-installs the tools but keeps the bundled Node.js, which cannot replace itself while it is the interpreter running the bootstrap. To force a Node.js re-download too, run scripts/runtime/bootstrap-host-native.cjs with a system Node.js, or delete runtime/node/ first and let it re-bootstrap.
Warning: Do not use the tools' built-in self-updaters (
claude --update,codex update, …) on a pcoder install. They rewrite their own binary in place, and the bundled install hardlinks that binary across two locations — an interrupted update breaks the next launch.pcoderrefuses these invocations; setPCODER_ALLOW_TOOL_UPDATE=1to bypass (not recommended).
Each tool tracks its own auth mode (oauth or api).
OAuth (default) — credentials are written to state/auth/<tool>/host/ and travel with the folder. Nothing is stored in system directories. The exact login command per tool comes from login_args in the catalog, so Codex gets --device-auth while Claude gets a plain login.
API key mode — keys are injected from environment variables at launch and never written to disk by PortableCoder:
scripts\pcoder setup --claude-auth api
set ANTHROPIC_API_KEY=sk-ant-...
scripts\pcoder
ANTHROPIC_AUTH_TOKENis accepted as an alias forANTHROPIC_API_KEY. Codex usesOPENAI_API_KEY.
Runs Claude Code inside a self-contained QEMU Linux VM for environments where host-native execution isn't wanted. The tool has no direct host file access — project files sync in and out over SSH. Uses WHPX acceleration when available, falling back to software emulation (TCG).
scripts\pcoder runtime bootstrap :: one-time: QEMU + Ubuntu image (~1 GB, SHA-256 verified)
scripts\runtime\windows\smoke-check.cmd :: optional: verify the VM boots
scripts\pcoder run --mode linux-portable :: launchBoth tools expose flags that disable their interactive approval prompts. These are forwarded as-is — pcoder does not parse, gate, or persist them.
scripts\pcoder claude --dangerously-skip-permissions
scripts\pcoder codex --dangerously-bypass-approvals-and-sandboxWarning: these flags let the model run shell commands and modify files without asking. Anything your user account can do, the model can do. Use only in throwaway directories or VMs where the blast radius is contained.
| Command | Description |
|---|---|
pcoder |
Launch the default tool in the current directory |
pcoder <tool> [args...] |
Launch a specific tool (e.g. pcoder codex) |
pcoder doctor |
Check environment health |
pcoder setup --init |
Initialize state/settings.json with defaults |
pcoder setup --show |
Print current settings |
pcoder setup --default-tool <claude|codex> |
Change the default tool |
pcoder setup --claude-auth <oauth|api> |
Change Claude authentication mode |
pcoder setup --codex-auth <oauth|api> |
Change Codex authentication mode |
pcoder setup --windows-mode <linux-portable|host-native> |
Change Windows run mode |
pcoder setup --auto-update <true|false> |
Enable/disable automatic updates |
pcoder setup --sync-back <true|false> |
Control whether VM files sync back after a run |
pcoder auth status |
Show authentication status for all tools |
pcoder auth login|logout [--tool <name>] |
Log in / out via OAuth |
pcoder runtime probe |
Show available runtime backends |
pcoder runtime bootstrap [--force] |
Download VM runtime (QEMU + Ubuntu) |
pcoder runtime bootstrap-host-native [--tool <name|all>] [--force] |
Download Node.js + tool(s) |
pcoder run [--tool <name>] [--project <path>] [--mode <mode>] [-- <tool args>] |
Full launch control |
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN |
Claude API key (api auth mode) |
OPENAI_API_KEY |
Codex API key (api auth mode) |
ANTHROPIC_BASE_URL / OPENAI_BASE_URL |
Override API base URLs |
PCODER_CLAUDE_CMD / PCODER_CODEX_CMD |
Override the resolved tool command |
PCODER_AUTO_UPDATE |
Set to 0 to skip the automatic update check for one invocation |
PCODER_ALLOW_TOOL_UPDATE |
Set to 1 to allow the tools' native self-updaters (not recommended) |
PCODER_VM_SSH_TIMEOUT_SECONDS |
Max seconds to wait for VM SSH (default: 300) |
PCODER_VM_HOST / PCODER_VM_USER / PCODER_VM_SSH_KEY |
VM SSH connection overrides |
PCODER_VM_PROJECTS_ROOT |
Remote path for synced projects |
PCODER_SSH_CMD |
Override SSH client path |
HTTP_PROXY / HTTPS_PROXY / NO_PROXY |
Proxy settings forwarded into the VM |
PortableCoder/
scripts/
pcoder.cmd / pcoder ← Windows / POSIX entry points
pcoder.cjs ← launcher logic (Node.js, cross-platform)
lib/paths.cjs ← shared path helpers
adapters/catalog.json ← tool adapter definitions (claude, codex, …)
runtime/
bootstrap-host-native.cjs ← downloads Node + tools, verifies checksums
bootstrap-node.ps1 ← no-Node fallback for Windows (PowerShell)
bootstrap-node.sh ← no-Node fallback for Linux/macOS (bash)
windows/ ← QEMU VM helper scripts
linux/smoke-check.sh ← Linux smoke test
tests/ ← unit tests (node:test, zero dependencies)
runtime/ ← bundled binaries (gitignored, travels with the folder)
state/ ← settings, auth, VM state (gitignored)
docs/ ← design specs and implementation plans
scripts/adapters/catalog.json drives everything. To add a new CLI: add an entry with npm_package, bin_name, candidate_commands, command_env, config_dir_name/config_dir_env, auth_env_vars, api_key_env, and vm_supported, then run pcoder runtime bootstrap-host-native --tool <name>. No code changes needed — the launcher treats every catalog entry with an npm_package as launchable.
Optional login_args / logout_args set the argv pcoder auth login|logout invokes the tool with, for CLIs whose auth flow needs flags — codex uses ["login", "--device-auth"]. Omit them and the bare verb is used.
- OAuth credentials are stored in gitignored
state/auth/<tool>/— never committed, never written to system directories. - API keys are passed via environment variables and never written to disk by PortableCoder.
- Downloads are verified: Node.js against
SHASUMS256.txt, the Ubuntu VM image againstSHA256SUMS, the QEMU installer against its SHA-512. A failed verification removes the artifact rather than leaving it to be trusted later. - Host-native mode gives the tool the same file access as any program you run; VM mode confines it to explicitly synced files.
- CI runs gitleaks on every push.
The .ps1 scripts in this repo are not code-signed. Windows only cares about that when you
run a script file directly, and each entry point already handles it:
| How you run it | Signature needed? |
|---|---|
scripts\pcoder … (normal use, including auto-bootstrap) |
No — it passes -ExecutionPolicy Bypass internally |
powershell -ExecutionPolicy Bypass -File scripts\runtime\bootstrap-node.ps1 |
No — the flag scopes to that one process |
scripts\runtime\bootstrap-node.ps1 (bare) |
Yes, and it fails without one |
If the Bypass flag doesn't help, your policy is being enforced by Group Policy, which
command-line flags cannot override. Check which scope is responsible:
Get-ExecutionPolicy -ListA value on MachinePolicy or UserPolicy means it's set by your organization — the supported
fix is to ask IT to allow it or to sign the scripts with a certificate your machines already
trust, not to work around the policy. A value on LocalMachine or CurrentUser is yours to
change:
Set-ExecutionPolicy -Scope CurrentUser RemoteSignedIf the folder arrived as a downloaded .zip, Windows also tags the files as web content, which
RemoteSigned blocks. Clear the tag with:
Get-ChildItem -Recurse *.ps1 | Unblock-FileThis codebase is developed with Claude Code using a spec → plan → review loop: design documents live in docs/superpowers/specs/, implementation plans in docs/superpowers/plans/, and every change lands through CI that runs the unit suite, bootstraps both tools for real, and executes the smoke test — no masked failures.
npm test # unit tests (node:test, no dependencies)
scripts/pcoder doctorIssues and PRs welcome. Before submitting: npm test, npx prettier --check "scripts/**/*.cjs" "tests/**/*.cjs", and make sure scripts/pcoder doctor passes. PRs run through the full CI gate.
MIT © 2026 Mike Jenkins