# Sandbox
The **sandbox** restricts the process to a configurable set of
filesystem paths using Linux Landlock. It's opt-in, works in every
host (`deft`, `retro`, `retra`), and is enforced by the kernel — a
stray `[fs/write]` or file open outside the granted paths simply
fails, instead of silently succeeding.
Networking is **not** restricted. Landlock is filesystem-only; the
process keeps full network access.
## Enabling
Set `DEFT_SANDBOX=1` (or `true`) in the environment:
```bash
$ DEFT_SANDBOX=1 deft script.dft
$ DEFT_SANDBOX=1 retro
```
Without the variable the sandbox is a no-op. It is also a no-op on
non-Linux platforms and on kernels too old for Landlock (kernel 5.13+,
see [Kernel Support](#kernel-support)); those cases print a message
and continue unsandboxed rather than refusing to start.
The sandbox is applied after the prelude and config registry are
loaded but **before** user code runs and before the TUI event loop
starts, so all initialisation reads complete first. Application is a
one-way door (`landlock_restrict_self`): the process can never widen
its own access afterwards.
## Configuration
The static path lists live in the project prelude under the
`:sandbox` key (see [Prelude](prelude)):
%Prelude{
:sandbox %{
:readonly @{ "/etc" "/usr/bin" "/usr/lib" "/usr/lib64" "/usr/share" "/proc" "/sys" }
:writable @{ "~/.config" "~/.local" }
}
}
- `:readonly` — paths readable (and executable) but not writable.
- `:writable` — paths fully writable: read, write, create, remove.
Both lists are optional. A leading `~` is expanded to `$HOME` when
the sandbox is applied, so `"~/.config"` grants the subtree beneath
the real home directory.
`deft init` writes a template `:sandbox` block for you. Edit the
lists directly in `.deft/prelude.dft`, or use the `deft sandbox` CLI
which edits them for you:
```
$ deft sandbox list # show both lists
$ deft sandbox add /srv/data # add read-only
$ deft sandbox add ~/code/write-dir -w # add writable
$ deft sandbox remove /srv/data
```
See [The `deft` Binary](../07-Tooling/cli) for the full subcommand
reference.
## What Is Always Granted
A small set of host-essential, environment-derived grants is applied
by the runtime itself and cannot be edited away:
| Path | Access | Why |
|------|--------|-----|
| cwd subtree | full | the project the runtime runs in |
| `/tmp` | full | temp files, sockets |
| `$XDG_RUNTIME_DIR` | rw socket | Wayland/X11 compositor socket |
| `$HOME` | read-only | Xauthority, fontconfig cache, config reads |
| `/dev`, `/dev/pts` | rw + ioctl | GPU render nodes, input, integrated-terminal PTYs |
| `/` | execute-only | absolute-path resolution of binaries |
| font path (TUI) | read-only | when the configured font lives outside cwd + `/tmp` |
Writable sub-trees like `~/.config` from the `:writable` list union
over the read-only `$HOME` rule within the same ruleset, so they
become writable without widening the rest of the home directory.
## Semantics and Limits
- **Directory-granular grants.** A rule roots on a directory and
covers its subtree. Adding a list entry that names a *file* is
warned about and skipped — add the parent directory instead.
- **One-way door.** After startup the process's filesystem view is
fixed for its lifetime. There is no runtime escape hatch.
- **Filesystem only.** Sockets, network, and process signals are
unaffected. This is not a full security sandbox — for untrusted
code, pair it with `runtime/*` heap isolation (see
[Runtimes](runtimes)); child runtimes share the process and
therefore the same sandbox.
- **Not a per-app boundary.** The sandbox bounds the whole process.
All panels in a TUI host and all child runtimes run inside the one
set of grants.
## Kernel Support
Landlock ABIs are detected automatically and the widest supported
set of access rights is used:
| ABI | Kernel | Notes |
|-----|--------|-------|
| v6 | 6.13+ | scoped access |
| v4 | 6.7+ | networking field in the ruleset attr |
| v3 | 6.2+ | up to `TRUNCATE` |
| v2 | 5.19+ | up to `REFER` |
| v1 | 5.13+ | baseline: read/write/create/remove/exec |
On kernels without Landlock the process prints
`sandbox: landlock unavailable (kernel too old)` and continues
without restriction.
## Diagnosing
Denied operations fail with the usual OS-level errors (`permission
denied`, `no such file or directory` on open), which surface as
normal Deft errors from `fs/*` and `sqlite/*` stdlib functions. Check the
startup line `sandbox: active (landlock, cwd + /tmp + prelude paths)`
to confirm the sandbox engaged, then add the missing path to the
appropriate list:
```
$ deft sandbox list
read-only:
/etc
/usr/bin
writable:
~/.config
$ deft sandbox add ~/data -w
added ~/data (writable)
```
## See Also
- [Prelude](prelude) — the `:sandbox` key lives in `.deft/prelude.dft`
- [The `deft` Binary](../07-Tooling/cli) — `deft sandbox` subcommand
- [Runtimes](runtimes) — heap isolation vs. OS sandboxing