Metadata-Version: 2.4
Name: eval-workspace
Version: 1.5.1
Summary: A local HTTP service for sandboxed multi-language code execution (Python, JS, Java, Rust) with auto-provisioning toolchains
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: aiohttp>=3.9

# 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 /run` with `{"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_AS` for Python/Java/Rust; Node uses `--max-old-space-size` instead — V8 reserves large virtual address space upfront, so `RLIMIT_AS` kills 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
- **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

```bash
pip install eval-workspace
```

## Usage

**Check what's already available:**
```bash
python -m eval_workspace.cli check
```

**Pre-install any missing toolchains:**
```bash
python -m eval_workspace.cli install
```

**Start the server:**
```bash
eval-workspace
```
Starts on `http://127.0.0.1:8765` by default.

**Run some code:**
```bash
curl -X POST http://127.0.0.1:8765/run \
  -H "Content-Type: application/json" \
  -d '{"language": "python", "code": "print(2 + 2)"}'
```

```json
{"ok": true, "stdout": "4\n", "stderr": "", "exit_code": 0, "timed_out": false, "duration_seconds": 0.05, "error": ""}
```

**Custom limits per request:**
```bash
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:**
```bash
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
