# Sandbox and limits

> Restrict an Elide program's filesystem, network, subprocess, thread and native access with `--sandbox` and `--allow-*` flags, and limit resources and run time.

Programs run unrestricted until you pass a sandbox flag. `--sandbox` denies filesystem access; network and
subprocesses stay allowed until you list hosts or commands:

```bash
elide --sandbox --allow-read=. --allow-net=example.com run app.js
```

<img src="https://elide.help/assets/demos/sandbox.gif" alt="elide --sandbox — filesystem, network and subprocess policy" width="1591" height="546" loading="lazy" decoding="async" class="rounded-lg border border-border my-6 w-full" />

## Allow file access

```bash
elide --allow-read=. --fs-audit run app.js
```

```text
[elide.fs] allow read open hello.txt
[elide.fs] DENY  write open out.txt
```

A denied access fails like a protected file: `EACCES: permission denied, open 'hello.txt'`. Relative paths resolve
from the working directory.

WASI programs cannot access host directories until you map one with `WasiMapDirs`; see
[WebAssembly](https://elide.help/docs/wasm#directory-access). These grants apply to mapped directories, and a WASI open needs both read
and write access.

## Allow network and subprocesses

```bash
elide --allow-net=localhost --allow-run=ls run app.js
```

A denied call names the grant it needs:

```text
TypeError: fetch to 'example.com:443' is not permitted; grant it with --allow-net=example.com
Error: spawn of 'echo' is not permitted; grant it with --allow-run=echo
```

A `--deny-*` match reports `denied by --deny-net` or `denied by --deny-run`. Policy checks the command that runs, so a
shell invocation is checked as `sh`. Spawn with `execFileSync` from `node:child_process`.

## Threads and native access

| Program | Threads by default | Native access by default |
| --- | --- | --- |
| JavaScript, TypeScript | Not applicable: `node:worker_threads` workers run under every flag | Allowed |
| Python entry | Allowed | Allowed |
| Python entry with `--sandbox` or an `--allow-read`/`--allow-write` grant | Denied; add `--allow-threads` | Allowed |
| Python under `--interop` | Denied, even with `--allow-threads` | Allowed |

```python
# threads.py
import threading
out = []
t = threading.Thread(target=lambda: out.append("worker ran"))
t.start(); t.join()
print(out[0])
```

```bash
elide python threads.py                                         # worker ran
elide --sandbox --allow-read=. python threads.py                # Creating threads is not allowed.
elide --sandbox --allow-read=. --allow-threads python threads.py  # worker ran
```

`--no-native` denies native access. Python then lacks some features, such as `AF_UNIX` sockets
(`AttributeError: module 'socket' has no attribute 'AF_UNIX'`) and arbitrary signals, and starts more slowly.
`--allow-native` states the default explicitly; combining it with `--no-native` is a usage error (exit `2`).

## Set limits

`--limit` applies to JavaScript and TypeScript programs. A value is a number, `0` to forbid, or `unlimited`, the
default. Other names, languages or commands are usage errors.

```bash
elide --limit=workers=4 --limit=fds=128 --max-mem=512m --timeout=30s run app.js
```

Workers inherit their parent's grants. These flags do not isolate the process from a native crash.

## Pick a profile

`--sandbox=trusted` is the default profile. `--sandbox=sandboxed` and `--sandbox=isolated` are unavailable and exit
with code `2`.

## Flags

| Flag | Effect |
| --- | --- |
| `--sandbox` | Deny filesystem access. Network and subprocesses stay allowed. |
| `--allow-read[=PATHS]` | Allow reads everywhere, or at the listed paths. Implies `--sandbox`. |
| `--allow-write[=PATHS]` | Allow writes everywhere, or at the listed paths. Implies `--sandbox`. |
| `--fs-audit` | Log each filesystem decision to stderr. |
| `--allow-net[=HOSTS]` | Allow all network access, or only the listed hosts (`HOST` or `HOST:PORT`). |
| `--allow-run[=CMDS]` | Allow all subprocesses, or only the listed commands. |
| `--deny-net=HOSTS` | Deny hosts even when allowed. |
| `--deny-run=CMDS` | Deny commands even when allowed. |
| `--allow-env[=NAMES]` | Let `elide.pkl` read more environment variables, or all of them. `process.env` is unchanged. |
| `--allow-threads` | Allow Python threads in a sandbox. |
| `--allow-native` | Allow native access (the default). |
| `--no-native` | Deny native access. |
| `--limit=workers=N` | Limit live workers; exceeding it throws `ERR_WORKER_LIMIT`. |
| `--limit=fds=N` | Limit open `node:fs` handles; exceeding it throws `EMFILE`. |
| `--max-mem=SIZE` | Limit the heap size; exceeding it is an out-of-memory error with a nonzero exit. |
| `--timeout=DURATION` | Stop the program after `DURATION` with exit `124`. |

Lists are comma-separated. `--deny-net` and `--deny-run` need the `=` form.

## Samples

- [Sandboxed subprocess](https://elide.help/docs/code-samples#sandboxed-subprocess)
- [Limit workers](https://elide.help/docs/code-samples#limit-workers)
- [Request limits](https://elide.help/docs/code-samples#request-limits)
