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
Release files for eval-workspace 1.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| eval_workspace-1.5.1.tar.gz | 14.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| eval_workspace-1.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.7 kB
Release files / eval_workspace-1.5.1.tar.gz
| Download URL | eval_workspace-1.5.1.tar.gz |
|---|---|
| Size | 14.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0e84b2c0d4febe39fa41d0863f1d18a8d62c5a4498c985edd415cd7ac60d2a5e
|
|
BLAKE2b-256 checksum How to use checksums |
b28a0fc156e44fed0b2db2f7ebd5cbb1f1dc7373fbaabee1fabcd6dbc01025cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|
Release files / eval_workspace-1.5.1-py3-none-any.whl
| Download URL | eval_workspace-1.5.1-py3-none-any.whl |
|---|---|
| Size | 14.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c5fca25c8576a4b112f8de5e7c1051133cceab2fa1aa8040aeef61df30418057
|
|
BLAKE2b-256 checksum How to use checksums |
b8d8797f75ec9df2b9f0e887f16ea8498c1335d429877690fe709686ee73c0e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|