# Running Untrusted Code

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/examples/security/untrusted-code>.

This example builds a code-execution sandbox for running untrusted code, such as code generated by an LLM in a "code interpreter" feature of your product, code submitted by your users, or the tool calls of an autonomous agent. Every execution runs in its own ephemeral *Workspace* with no network access, a read-only root filesystem, tight resource limits and no access to any API, and the *Workspace* is deleted right after the execution.

The layers of defense are:

1. The *Workspace*'s sandbox itself: a rootless container in its own user, PID, mount and network namespaces, inside a restricted supervisor container (read more [here](https://octelium.com/docs/cordium/latest/overview/how.md#workspace-isolation-model)).
2. A default-deny network policy, enforced outside of the sandbox (read more [here](https://octelium.com/docs/cordium/latest/workspaces/network.md)).
3. A read-only root filesystem and dropped Linux capabilities (read more [here](https://octelium.com/docs/cordium/latest/workspaces/runtime.md#filesystem-and-capabilities)).
4. An Octelium *Policy* that denies the sandboxes' *Sessions* any access to the *Cluster*, including the Cordium and Octelium APIs.
5. Small resource limits, and a dedicated *Space* with SSH disabled.

## The Space

A dedicated `ORGANIZATION` *Space* keeps the sandboxes separate from your development environments, caps their resources, and disables SSH access to them (read more [here](https://octelium.com/docs/cordium/latest/workspaces/spaces.md#space-configuration)):

```yaml
spec:
  limit:
    defaultLimit:
      cpu:
        millicores: 1000
      memory:
        megabytes: 1024
      storage:
        megabytes: 4000
    maxLimit:
      cpu:
        millicores: 2000
      memory:
        megabytes: 4096
      storage:
        megabytes: 8000
  authorization:
    disableSSH: true
```

```bash
cordium create space sandboxes.cordium
```

The *Space*'s configuration above can then be applied from the **Settings** tab of the *Space*'s page in the web portal, or via the `UpdateSpace` API.

## The Template

The sandbox *Template* bakes the Python packages into its image. This matters because the network policy also applies to the pre-builds' tasks, while the image is built before the network policy is applied:

```yaml
spec:
  image:
    dockerfile:
      inline: |
        FROM python:3.13-slim
        RUN pip install --no-cache-dir numpy pandas matplotlib scipy
        RUN useradd -m -u 1000 sandbox
  runtime:
    filesystem:
      readOnly: true
    capabilities:
      drop:
        - NET_RAW
        - NET_BIND_SERVICE
        - SYS_CHROOT
        - MKNOD
    network:
      egress:
        defaultAction: DENY
```

```bash
cordium create template python-sandbox.sandboxes.cordium --file python-sandbox.yaml
cordium build python-sandbox.sandboxes.cordium
```

With the pre-build ready, each sandbox is restored from it instead of building the image again, and starts within seconds. The code runs as the image's `sandbox` user, and the read-only root filesystem prevents it from modifying the image, its packages or any system file. `/workspace`, the home directory and `/tmp` remain writable.

## Denying the Cluster to the Sandboxes

Every *Workspace*, including a sandbox, has its own Octelium *Session* that belongs to its owner, and the `cordium`, `octelium` and `octeliumctl` CLIs inside it are authenticated with that *Session* through a local socket, which does not depend on the *Workspace*'s network. A global *Policy* makes sure that the sandboxes' *Sessions* cannot do anything at all, whatever their owners are allowed to do (read more [here](https://octelium.com/docs/cordium/latest/management/access-control.md#policies-for-workspace-sessions)):

```yaml
kind: Policy
metadata:
  name: deny-sandboxes
spec:
  rules:
    - effect: DENY
      condition:
        all:
          of:
            - match: '"ext" in ctx.session.status && "cordium" in ctx.session.status.ext'
            - match: ctx.session.status.ext.cordium.spaceRef.name == "sandboxes.cordium"
---
kind: ClusterConfig
spec:
  authorization:
    policies: ["deny-sandboxes"]
```

> **Note:**
>
> The second resource above is the Octelium `ClusterConfig`, which is applied via `octeliumctl`, not the Cordium `ClusterConfig`. Remember to include your other global *Policies* in its list, if any.

## Running Code

Your application, typically a backend service authenticated as a `WORKLOAD` *User* that is a *Member* of the `sandboxes` *Space*, runs each piece of code in a fresh sandbox via the SDKs. Here is an example via the Python SDK that runs a Python program, returns its output, and collects the chart that it may have generated:

```python
from dataclasses import dataclass

from cordium import Cordium


@dataclass
class Execution:
    exit_code: int
    output: str
    chart: bytes | None


def run_untrusted(client: Cordium, code: str) -> Execution:
    sandbox = client.workspaces.run(
        template="python-sandbox.sandboxes.cordium",
        ephemeral=True,
        timeout=120,
    )
    try:
        sandbox.files.write_text("/workspace/main.py", code)
        result = sandbox.exec(
            "cd /workspace && MPLBACKEND=Agg timeout 60 python main.py < /dev/null",
            timeout=90,
            max_capture_bytes=256 * 1024,
        )
        chart = None
        if sandbox.exec("test -f /workspace/chart.png").exit_code == 0:
            chart = sandbox.files.read_bytes("/workspace/chart.png", max_bytes=10 * 1024 * 1024)
        return Execution(result.exit_code, result.stdout + result.stderr, chart)
    finally:
        sandbox.delete()


if __name__ == "__main__":
    code = """
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 200)
plt.plot(x, np.sin(x))
plt.savefig("/workspace/chart.png")
print("mean:", np.sin(x).mean())
"""
    with Cordium() as client:
        execution = run_untrusted(client, code)
    print(execution.exit_code, execution.output)
```

Here are a few notes about this program:

- `timeout 60` inside the sandbox and the `exec` timeout bound the execution time, while the *Space*'s limits bound its CPU, memory and storage. The output capture is bounded too.
- The sandbox is deleted in every case, which discards its storage.
- The `WORKLOAD` *User* creates one sandbox per execution, which counts towards its quota of non-stopped *Workspaces* (128 by default). Raise it via the `ClusterConfig` according to your peak concurrency (read more [here](https://octelium.com/docs/cordium/latest/management/clusterconfig.md#limits)).

> **Note:**
>
> For multi-step agent sessions that need to keep state between tool calls (e.g. variables, files and installed packages), keep one ephemeral sandbox per session instead of per execution, and delete it when the session ends or after an idle period.
