https://github.com/thecomputerm/localbox
An easy-to-host, general purpose and fast code execution system for running untrusted code in sandboxes
https://github.com/thecomputerm/localbox
isolate sandbox
Last synced: 7 months ago
JSON representation
An easy-to-host, general purpose and fast code execution system for running untrusted code in sandboxes
- Host: GitHub
- URL: https://github.com/thecomputerm/localbox
- Owner: TheComputerM
- License: mit
- Created: 2025-09-09T08:20:02.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-10-01T07:52:02.000Z (7 months ago)
- Last Synced: 2025-10-01T09:11:47.634Z (7 months ago)
- Topics: isolate, sandbox
- Language: Go
- Homepage:
- Size: 410 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
LocalBox
An easy-to-host, general purpose and fast code execution system for running untrusted code in sandboxes.
## Why?
[Piston](https://github.com/engineer-man/piston) and [Judge0](https://github.com/judge0/judge0) are already well established and tested code execution systems, so why the need to reinvent the wheel?
Both of these projects have some *issues* which did not fit my usecase, namely:
1. In both Piston and Judge0
- You cannot install the latest versions of compilers and runtimes, you are only limited to the versions which the creators bothered to add and update.
- Very difficult to run arbitrary commands for specific usecases.
2. In Piston
- Isolate doesn't share cgroups with host system which can lead to inaccurate program limits and runtime metrics.
- All programs are run in bash which adds additional overhead to measuring program runtime metrics.
- You have to compile/build the runtime on your system for many languages instead of getting prebuilt binaries.
3. In Judge0
- Only supports outdated linux cgroups v1, you will have to change kernel options in any machine with a recent linux kernel.
- Cannot take multiple files as input.
## Getting Started
### With Docker
The image exposes port 2000 by default, the `--privileged --cgroupns=host` are important as they allow localbox to manipulate cgroups.
```sh
docker run --rm -it --privileged --cgroupns=host -p 2000:2000 ghcr.io/thecomputerm/localbox:latest
```
## Usage
You can visit http://localhost:2000/docs to get the full API documentation.

Here are the options you can provide the sandbox with to configure it:
```go
type SandboxPhaseOptions struct {
MemoryLimit int `json:"memory_limit,omitempty" doc:"Maximum total memory usage allowed by the whole control group in KB, '-1' for no limit" default:"-1"`
TimeLimit int `json:"time_limit,omitempty" doc:"Maximum CPU time of the program in milliseconds, '-1' for no limit" default:"5000"`
WallTimeLimit int `json:"wall_time_limit,omitempty" doc:"Maximum wall time of the program in milliseconds, '-1' for no limit" default:"10000"`
FilesLimit int `json:"files_limit,omitempty" doc:"Maximum number of open files allowed in the sandbox, '-1' for no limit" default:"64"`
FileSizeLimit int `json:"file_size_limit,omitempty" doc:"Maximum size a file created/modified in the sandbox in KB, -1 for no limit" default:"10000"`
ProcessLimit int `json:"process_limit,omitempty" doc:"Maximum number of processes allowed in the sandbox" default:"64"`
Network bool `json:"network,omitempty" doc:"Whether to enable network access in the sandbox" default:"false"`
Stdin string `json:"stdin,omitempty" doc:"Text to pass into stdin of the program" default:""`
BufferLimit int `json:"buffer_limit,omitempty" doc:"Maximum kilobytes to capture from stdout and stderr" default:"8"`
Environment map[string]string `json:"environment,omitempty" doc:"Environment variables to set in the sandbox" example:"{}"`
}
```
And here are the options you have to define to mount files in the sandbox
```go
type SandboxFile struct {
Name string `json:"name" doc:"Path of the file within the sandbox" example:"hello.txt"`
Content string `json:"content" doc:"Content of the file" example:"Hello World"`
Encoding string `json:"encoding,omitempty" doc:"Encoding of the content field" enum:"utf8,base64,hex" default:"utf8" `
}
```
### Using an Engine
`GET /engine`: List all the available engines.
Here is a [list of available languages/runtimes](./engines/) packaged as engines, these provide preconfigured compile and execute steps so you don't have to set stuff up.
`POST /engine/{engine_name}/execute`: Execute a predefined engine with an execution phase whose options can be overridden.
```jsonc
// Request Body
{
"options": {
"stdin": "hello world"
},
"files": [
{
"content": "print(input())",
"encoding": "utf8",
"name": "@" // use "@" to denote that this file is the main executable for the engine
}
]
}
// Response
{
"$schema": "http://localhost:2000/schemas/SandboxPhaseResults.json",
"time": 24,
"wall_time": 55,
"memory": 6748,
"max_rss": 11212,
"status": "OK",
"message": "Executed",
"exit_code": 0,
"stdout": "hello world",
"stderr": ""
}
```
```go
type SandboxPhaseMetadata struct {
Time int `json:"time" doc:"Run time of the program in milliseconds" example:"500"`
WallTime int `json:"wall_time" doc:"Wall time of the program in milliseconds" example:"1000"`
Memory int `json:"memory" doc:"Total memory use by the whole control group in KB" example:"256"`
MaxRSS int `json:"max_rss" doc:"Maximum resident set size of the program in KB" example:"128"`
Status string `json:"status" doc:"Two-letter status code" example:"OK" enum:"OK,RE,SG,TO,XX,OE,CE"`
Message string `json:"message" doc:"Human-readable message" example:"Executed"`
ExitCode int `json:"exit_code" doc:"Exit code/signal from the program" example:"0"`
}
type SandboxPhaseResults struct {
SandboxPhaseMetadata
Stdout string `json:"stdout" doc:"stdout of the program" example:"program output"`
Stderr string `json:"stderr" doc:"stderr of the program" example:""`
}
```
### Custom workflow
Instead of using a predefined engine, you can go the full custom route by specifying your own packages, commands and options for each phase.
```go
type SandboxPhase struct {
Command string `json:"command" doc:"Command to execute in the sandbox" example:"cat hello.txt"`
SkipShell bool `json:"skip_shell,omitempty" doc:"Doesn't use a shell to run the command to if true, can be used to get more accurate results" default:"false"`
Packages []string `json:"packages,omitempty" doc:"Nix packages to install in the sandbox" example:"nixpkgs#cowsay,nixpkgs/nixos-25.05#busybox"`
}
```
`POST /execute`: Execute a series of phases, where each of them can have different options, packages and commands with persistent files.
```jsonc
// Request Body
{
"files": [
{
"content": "Hello World",
"encoding": "utf8",
"name": "hello.txt"
}
],
"phases": [
{
"buffer_limit": 8,
"command": "cat hello.txt",
"environment": {},
"files_limit": 64,
"memory_limit": -1,
"network": false,
"packages": [
"nixpkgs#cowsay",
"nixpkgs/nixos-25.05#busybox"
],
"process_limit": 64,
"skip_shell": false,
"stdin": "string",
"time_limit": 5000
}
]
}
// Response Body
[
{
"time": 4,
"wall_time": 14,
"memory": 500,
"status": "OK",
"message": "Executed",
"exit_code": 0,
"stdout": "Hello World",
"stderr": ""
}
]
```
Here are the status codes the result can have:
- `OK`: no errors
- `RE`: run-time error, i.e., exited with a non-zero exit code
- `SG`: program died on a signal
- `TO`: timed out
- `XX`: internal error of the sandbox
- `OE`: buffer limit exceeded for stdout/stderr
- `CE`: compile error (only when executing engines)
## Security
As both localbox and piston use isolate, they both make use of Linux namespaces, chroot, multiple unprivileged users, and cgroup for sandboxing and resource limiting; thereby providing [similar battle-tested security](https://github.com/engineer-man/piston/tree/master?tab=readme-ov-file#security).