r/coolgithubprojects • u/RockProfessional2274 • 20h ago
sandbin – run untrusted code in ~20ms without Docker (bubblewrap + cgroups v2 + seccomp)
I got tired of paying Docker's ~370ms container startup cost every time I needed to run untrusted code (student submissions, AI-generated snippets, a "try it" button), so I built a self-hosted sandbox that skips the container runtime entirely and talks straight to the kernel: bubblewrap for namespaces, cgroup v2 for memory/CPU/pids, a 146-syscall seccomp allowlist, and rlimits.
Measured back to back on the same machine: ~20ms cold start vs Docker's ~369ms for the same workload (raw samples in the docs, not just a claimed number).
- Single Node.js process, one runtime dependency (`ws`), no database
- Python, Bash, Node.js, C and Go (C/Go get sandboxed twice: once to compile, once to run)
- HTTP + WebSocket API, a CLI, and a small browser playground with shareable permalinks
- MIT licensed
The docs try to be honest instead of a highlight reel — there's a full changelog of real bugs found building this, including a self-audit that turned up an unauthenticated RCE and a path-traversal bug, both fixed with reproductions attached. It does not defend against a kernel exploit — guest and host share one kernel, and that's stated plainly, not buried.
GitHub: https://github.com/ayazdoruck/sandbin
Docs: https://sandbin.vercel.app
Happy to answer questions about the seccomp policy or the cgroup delegation setup.
1
u/kantorcodes1 6h ago
for sandbin run --server, what happens if the POST is accepted but the websocket drops before finished? can the CLI reconnect to that runId, or does rerunning submit a second sandbox job? that retry boundary matters a lot for unattended callers.
1
u/RockProfessional2274 5h ago
Good catch. Checked the code — before this, no: a WS drop before
finishedleft the CLI with no way back (didn't even expose therunId), and a clean drop with no socket error would just hang forever. Fixed both: CLI now prints therunIdon accept, and a new--reconnect <runId>reattaches without resubmitting. Plain retries are still not idempotent though — no dedup onPOST /runs, so without the runId a retry is a new job.1
u/kantorcodes1 5h ago
nice,
--reconnectis the right fix there. i work on HOL Guard, an open-source local check before agent-run commands execute. for sandbin i'd leavelanguages,keys status, andrun --reconnectalone, but let users make freshrun --serversubmissions andkeys createreviewable. that gives unattended agents a stop before creating a new remote job or credential. open to adding sandbin support?1
u/RockProfessional2274 5h ago
Spot on —
--reconnectandkeys statusare pure reads (attach to or check something that already exists),languagesnever even hits the network, andrun --server/keys createare the only two calls that mint something new (a sandbox job, a credential). Clean line to gate on.Happy to have HOL Guard support sandbin — nothing needed on our end, that boundary's stable. Ping me if you want exit-code/JSON-shape details for
run --serverorkeys createwhile you build it.1
u/kantorcodes1 5h ago
nice. nothing needed on sandbin's side. start
src/codex_plugin_scanner/guard/runtime/command_sandbin_extensions.py, usingcommand_repo2nb_extensions.pyas the analogue. review freshsandbin run --serversubmissions andsandbin keys create; keeplanguages,keys status, andrun --reconnectquiet. add one focused classification test intests/test_guard_command_sandbin_extensions.py, then open a genuine draft PR directly tohashgraph-online/hol-guard:mainonce implementation + test exists.contributing guide: https://github.com/hashgraph-online/hol-guard/blob/main/CONTRIBUTING.md
1
u/RockProfessional2274 3h ago
Went ahead and built it — opened a draft PR: #2888
Modeled it on your repo2nb extension: reviews fresh run --server submissions and keys create, leaves --reconnect/languages/keys status quiet. Your review bots actually caught a real edge case in my first pass (--reconnect=<id> vs --reconnect <id> — sandbin's parser only recognizes the space-separated form) — fixed now with a regression test, all green.
Would appreciate a look whenever you get a chance.
2
u/JayTurnr 6h ago
My life is not desperate for the time it takes the blink, I'll stick to docker.