CLI
A third way to talk to sandbin, alongside the browser playground and the raw HTTP API — run code straight from a terminal, with no server required by default.
Install
git clone https://github.com/ayazdoruck/sandbin.git
cd sandbin
npm install
npm link
npm link puts sandbin on PATH. Without it, node bin/sandbin.mjs ... works identically.
Two modes, one command
Local (the default). No server, no port —
bin/sandbin.mjs imports sandbox.mjs directly
and calls run() itself:
sandbin run script.py
Remote, with --server. Submits over the
exact same HTTP + WebSocket protocol the browser frontend speaks
against a running instance, local or not:
sandbin run script.py --server localhost:8080
Local is the default on purpose: the more common case for someone who
already has the repo checked out is "run this file through the real
sandbox right now," which needs nothing running and no port to pick.
Local mode needs the same host requirements as npm start
itself — see Architecture.
Getting code in
Exactly one source, never ambiguous about which wins:
sandbin run script.py # from a file
sandbin run -l python -e 'print(1 + 1)' # inline
cat script.sh | sandbin run -l bash # piped stdin
Language is inferred from the file extension (.py,
.sh/.bash, .js/.mjs/.cjs,
.c, .go) when a file is given.
-l/--language overrides it, or supplies it
outright for -e or piped input.
Output and exit codes
Stdout and stderr stream live, each to their own real stream, as
chunk events arrive — in local mode via
run()'s own onChunk, in remote mode via the
same WebSocket messages the frontend renders. A summary prints to
stderr once it finishes:
$ sandbin run -l python -e 'print("hi")'
hi
verdict ok
duration 14ms
cpu 6ms
peak memory 3.9 MB
exit code 0
The process exits 0 for verdict ok,
1 otherwise — deliberately not a passthrough
of the guest's own exit code. A guest that calls sys.exit(3)
still means the CLI itself succeeded at running it and observing that;
the verdict (printed alongside duration, CPU and peak memory) is what
actually failed. Safe to use directly in shell scripts and CI:
sandbin run deploy-check.py || echo "check failed, see verdict above"
run()'s onChunk only ever covers the
execute phase — the compile phase (c,
go) never gets the callback at all. A compile failure
was streaming nothing and then printing a bare
verdict compile_error with zero indication of what the
compiler actually said. The CLI now prints the result's own captured
stderr in full for exactly the two verdicts where the
streamed callback wouldn't have shown it
(compile_error, setup_failed) — not a
change to sandbox.mjs itself, the diagnostic was already
there, the CLI just wasn't showing it.
If the connection drops mid-run: --reconnect
--server mode is two steps — POST /runs
accepts the job and hands back a runId, then a WebSocket
streams it. Those two steps can be separated by an arbitrary amount of
time and an arbitrary number of dropped connections without losing the
run itself: the server keeps executing regardless of whether anything
is attached to watch it, and GET /runs/:runId/stream can be
attached to at any point — while it's still running, it replays
every buffered chunk/stats event and then
keeps streaming live; once it's finished, it immediately replays the
final result and closes. The CLI now prints the runId the
moment a run is accepted, specifically so a caller has something to
reconnect with if the socket dies before finished arrives:
$ sandbin run server.js --server localhost:8080
sandbin: run 7e2b1c4a-...-91fd accepted — if this connection drops, reconnect with: sandbin run --server localhost:8080 --reconnect 7e2b1c4a-...-91fd
(connection drops here)
sandbin: connection to run 7e2b1c4a-...-91fd closed before it finished
sandbin: the run may still be in progress — reconnect with: sandbin run --server localhost:8080 --reconnect 7e2b1c4a-...-91fd
$ sandbin run --server localhost:8080 --reconnect 7e2b1c4a-...-91fd
... streams the rest, or the final result if it already finished ...
Two things worth being precise about, since they matter for unattended
callers specifically. First, --reconnect attaches to an
existing run — it never submits a new one, so it's safe to retry
without risking a duplicate job. Second, a plain
sandbin run retry is not idempotent:
POST /runs has no idempotency key or dedup concept, so
re-running the original command after a drop submits a genuinely
independent sandbox job rather than resuming the first one. If a caller
can't hold onto the printed runId, that's the boundary
it's up against today.
Other commands
| Command | Does |
|---|---|
sandbin languages | lists what this host can run — reads local IMAGES directly, so go only appears if a toolchain was actually found |
sandbin keys create | wraps POST /keys against a running server |
sandbin keys status <key> | wraps GET /keys/:key |
sandbin --help | every flag, including the limit overrides below |
Flags
| Flag | Meaning |
|---|---|
-l, --language <lang> | override or supply the language |
-e, --eval <code> | inline code instead of a file |
-i, --stdin <file> | file whose contents become the guest's stdin (- reads this process's own stdin) |
-s, --server <url> | submit to a running server instead of running locally (env SANDBIN_SERVER) |
-k, --key <key> | API key sent as X-Sandbin-Key (env SANDBIN_KEY) |
--reconnect <runId> | skip submission, reattach to a run already in flight on --server |
--json | print the full result object once, instead of streaming |
--memory / --cpu / --timeout / --pids | override the matching resource limit |
--json skips live streaming entirely and prints the whole
result object — verdict, stdout,
stderr, durationMs, cpuMs,
peakBytes, and the rest — once, for scripting:
sandbin run server.js --json | node -pe "JSON.parse(require('fs').readFileSync(0)).verdict"
Tested as a real subprocess
src/cli-test.mjs spawns the actual bin/sandbin.mjs
with node, not by importing its functions — both
against run() directly (local) and against a real
createServer() on an ephemeral port (remote), asserting on
captured stdout/stderr/exit code exactly the way a real caller would
observe them. See Testing.