A local HTTP service for sandboxed multi-language code execution (Python, JS, Java, Rust) with auto-provisioning toolchains
Project description
eval_workspace
A local HTTP service for running untrusted code in Python, JavaScript, Java, and Rust, with resource limits (CPU time, memory, wall-clock timeout, process count) and automatic toolchain provisioning — no root required.
What it does
- Multi-language execution:
POST /runwith{"language": "python", "code": "..."}, get back stdout/stderr/exit code - Local sandboxing: each execution runs in its own process group with:
- CPU time limit (
RLIMIT_CPU) - Memory limit (
RLIMIT_ASfor Python/Java/Rust; Node uses--max-old-space-sizeinstead — V8 reserves large virtual address space upfront, soRLIMIT_ASkills it before user code even runs) - Process count limit (fork-bomb protection,
RLIMIT_NPROC) - Wall-clock timeout (catches processes waiting on I/O rather than burning CPU)
- No inherited environment variables
- Fresh temp directory per execution, deleted after
- CPU time limit (
- Auto-provisioning toolchains: if a language runtime isn't found on
PATH, it's downloaded as an official prebuilt binary into~/.eval_workspace/toolchains/— no system package manager, no root:- Node.js — official prebuilt tarball from nodejs.org
- Java — Temurin JDK prebuilt tarball from Adoptium
- Rust — via
rustup, which already installs to user-space by design - Python — detected only; not auto-installed (no good universal static build to fetch)
Honest limitations — read this before relying on it
This is local (rlimit-based) sandboxing, not container/VM isolation. It reliably stops:
- Infinite loops (CPU limit + wall-clock timeout)
- Memory bombs (memory limit)
- Fork bombs (process count limit)
- Runaway processes waiting on stdin/network (wall-clock timeout)
It does not provide the same guarantees as Docker, gVisor, or Firecracker against a genuinely adversarial payload. There's no filesystem or namespace isolation beyond a scratch temp directory, and no syscall filtering. If your threat model includes deliberately hostile code trying to escape the sandbox (not just buggy/runaway code), you need real container/VM isolation — this tool does not provide that, and no amount of rlimit tuning makes it equivalent.
Java requires a specific class name. Submitted Java code must define public class Main — same convention most online judges use, since the filename must match the public class name to compile.
Network access is required to auto-install missing toolchains. If the machine running this has no outbound internet access, toolchain auto-install will fail cleanly (returns a clear error) rather than hang — but it can't conjure a compiler out of nowhere either. Pre-install what you need if you're running somewhere network-restricted.
What's actually been tested vs. what hasn't
To be direct about verification status:
Tested end-to-end, with real cross-checks:
- Python execution (basic output, errors)
- JavaScript execution via real Node.js (basic output, thrown errors)
- CPU-limit enforcement (infinite loop correctly killed)
- Wall-clock timeout enforcement (sleeping process correctly killed)
- Memory-limit enforcement (large allocation correctly triggers
MemoryError) - Clean error handling when a toolchain is missing (no crash, structured error returned)
- Clean error handling when network is unavailable during auto-install (no crash, structured error returned)
- Limit-clamping logic (abuse-scale requests get capped to sane maximums)
Not testable in the environment this was built in, and not yet verified on a real machine:
- Java compilation + execution (the build environment had a JRE but no JDK)
- Rust compilation + execution (Rust wasn't installed, and the sandbox had no network to fetch it)
- The actual toolchain download/extract logic for Node, JDK, and Rust (needs real network access to confirm the URLs and archive layouts are exactly right)
- The aiohttp HTTP server itself running live (aiohttp wasn't installable in the build sandbox — the route handlers are thin, tested wrappers around the executor, but the actual server process hasn't been smoke-tested)
If you're the first to run this somewhere with real network and a full toolchain set, treat the untested parts above as "should work, please verify" rather than "confirmed working."
Install
pip install eval-workspace
Usage
Check what's already available:
python -m eval_workspace.cli check
Pre-install any missing toolchains:
python -m eval_workspace.cli install
Start the server:
eval-workspace
Starts on http://127.0.0.1:8765 by default.
Run some code:
curl -X POST http://127.0.0.1:8765/run \
-H "Content-Type: application/json" \
-d '{"language": "python", "code": "print(2 + 2)"}'
{"ok": true, "stdout": "4\n", "stderr": "", "exit_code": 0, "timed_out": false, "duration_seconds": 0.05, "error": ""}
Custom limits per request:
curl -X POST http://127.0.0.1:8765/run \
-H "Content-Type: application/json" \
-d '{"language": "rust", "code": "fn main() { println!(\"hi\"); }", "limits": {"cpu_seconds": 10, "memory_mb": 512}}'
Check server + toolchain health:
curl http://127.0.0.1:8765/health
API reference
POST /run
| Field | Type | Required | Notes |
|---|---|---|---|
language |
string | yes | python, javascript/js, java, or rust |
code |
string | yes | max 100,000 characters |
stdin |
string | no | piped to the program's stdin |
limits |
object | no | see below, all optional |
limits fields (all clamped server-side to the max shown):
| Field | Default | Max |
|---|---|---|
cpu_seconds |
5 | 30 |
memory_mb |
256 | 1024 |
wall_clock_seconds |
10 | 60 |
max_processes |
16 | 32 |
max_output_bytes |
200000 | 1000000 |
GET /languages
Returns the list of supported language identifiers.
GET /health
Returns toolchain availability for each language.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eval_workspace-1.5.1.tar.gz.
File metadata
- Download URL: eval_workspace-1.5.1.tar.gz
- Upload date:
- Size: 14.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e84b2c0d4febe39fa41d0863f1d18a8d62c5a4498c985edd415cd7ac60d2a5e
|
|
| MD5 |
b3dc6d202bb17b99ebc2c66d86f45c17
|
|
| BLAKE2b-256 |
b28a0fc156e44fed0b2db2f7ebd5cbb1f1dc7373fbaabee1fabcd6dbc01025cd
|
File details
Details for the file eval_workspace-1.5.1-py3-none-any.whl.
File metadata
- Download URL: eval_workspace-1.5.1-py3-none-any.whl
- Upload date:
- Size: 14.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5fca25c8576a4b112f8de5e7c1051133cceab2fa1aa8040aeef61df30418057
|
|
| MD5 |
e9cc7e8b9e654d7e9dd46de70c3a8578
|
|
| BLAKE2b-256 |
b8d8797f75ec9df2b9f0e887f16ea8498c1335d429877690fe709686ee73c0e8
|