OpenSandbox on the NDM Server
Overview
OpenSandbox is deployed on the NDM server as a separate Docker service. It provides an API that the NDM backend can use to create isolated sandboxes for package analysis and code execution.
The basic architecture is:
NDM Backend
│
│ OpenSandbox SDK / API
▼
ndm-opensandbox
│
│ Sandbox Runtime
▼
isolated sandbox
OpenSandbox does not run directly inside the backend container. It is deployed as a separate service to keep the backend and sandbox infrastructure isolated from each other.
Docker Setup
The OpenSandbox service is started using Docker Compose. The official OpenSandbox server image is used:
opensandbox:
image: opensandbox/server:latest
container_name: ndm-opensandbox
ports:
- "8090:8090"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
extra_hosts:
- "host.docker.internal:host-gateway"
The Docker socket is mounted so that OpenSandbox can manage sandbox containers.
host.docker.internal is made available through host-gateway so that the container can reach the Docker host when required.
The OpenSandbox configuration is mounted into the container separately. Its location is provided through SANDBOX_CONFIG_PATH.
Communication with the Backend
The backend uses the OpenSandbox Python SDK.
Because the backend and OpenSandbox run on the same Docker network, OpenSandbox is accessed internally through the Compose service name:
opensandbox:8090
An API key is used for authentication and is provided to both the OpenSandbox service and the backend through environment variables.
The internal Docker address is only intended for communication inside the Docker network. It does not need to be exposed through Caddy or a public domain.
Why a Separate Container?
OpenSandbox was intentionally deployed as a separate service.
This means:
- the Django backend remains separated from the sandbox system,
- OpenSandbox can be restarted or updated independently,
- sandbox resources can be managed separately from the backend,
- the backend does not have to manage sandbox containers itself,
- the sandbox infrastructure can be extended or replaced more easily later.
The backend therefore handles the application logic, while OpenSandbox is responsible for creating and managing the isolated execution environment.
Sandbox Test
For the initial proof of concept, the backend created a sandbox and executed code inside it.
The simplified flow is:
Backend
↓
Sandbox.create(...)
↓
OpenSandbox Server
↓
isolated sandbox
↓
execute code
↓
output / errors returned to backend
↓
Sandbox.destroy()
The sandbox is destroyed after the analysis so that the execution environment does not remain permanently active.
The PoC was later extended from a simple echo test to executing JavaScript with Node.js. A Node.js Alpine image can be used as the sandbox environment for this purpose.
gVisor
The intended runtime for the actual sandbox isolation is gVisor (runsc).
The concept is:
OpenSandbox
↓
Sandbox Runtime
↓
gVisor / runsc
↓
sandbox process
This provides stronger isolation for executed code than using a normal Docker runtime alone.
Firecracker was not pursued further for this setup because it is not the appropriate runtime solution for the intended OpenSandbox deployment.
Health Check
As part of the monitoring setup, we initially considered checking OpenSandbox through an HTTP endpoint.
It turned out that an assumed endpoint such as
http://opensandbox:8090/health
cannot automatically be assumed to exist.
The health check therefore needs to use an endpoint or API request that is actually provided by the deployed OpenSandbox version.
Current Status
OpenSandbox is currently running on the NDM server as:
ndm-opensandbox
and listens on port:
8090
The NDM backend can communicate with OpenSandbox through the internal Docker network and use the Python SDK to create sandboxes, execute code, and destroy the sandboxes afterwards.
The integration therefore provides the technical foundation for the future package-analysis functionality in NDM.