Runtime policy¶
Control what a tool can see and touch at runtime: environment variables and filesystem paths.
ixt isolates tool dependencies by design — each tool gets its own venv, node_modules, or extracted binary. Runtime policy goes one step further and restricts the tool process launched through ixt: what environment variables it receives and which filesystem paths it can reach.
The two implemented axes are independent: env and fs. Tools with no policy run exactly as before — a direct symlink, zero overhead.
Scope — runtime only
Runtime policy applies when you run an installed command through an ixt shim. It does not sandbox install-time package-manager behaviour (uv pip install, bun add, npm lifecycle scripts) or tool lifecycle hooks. For a tool whose install scripts are untrusted, use a disposable VM/container/user until install-time sandboxing exists.
Network policy is not implemented. A sandboxed process can still make outbound network connections unless you restrict that outside ixt.
Hygiene vs effective protection — read this first
The protection level depends on whether bubblewrap is installed.
| Mode | Effective protection | What's still leakable |
|---|---|---|
| env-only without bwrap (degraded) | Hygiene only. Cleans logs, traces, naive telemetry. The filtered env reaches the tool. | A malicious tool can still read /proc/$PPID/environ, /proc/*/environ, or files under ~/.aws, ~/.netrc, ~/.ssh. |
| env-only with bwrap | Effective. PID isolation closes /proc/*/environ. Filesystem stays fully visible (read-write) — that's what the user opted in to. |
Files the tool can already read on its own (no fs policy was set). |
| env + fs with bwrap | Effective. Combine env scrubbing with fs sandboxing for a tight perimeter. | Network egress (roadmap). |
| fs without bwrap | Hard error. fs policy requires bwrap. | n/a |
The shim prints a one-shot warning per shell session when running in
degraded mode — silence with IXT_POLICY_QUIET=1.
Platform support — Linux only for now
Both axes rely on POSIX execve and Linux-specific kernel features. On
macOS and Windows the policy is saved to ixt.json but not enforced
at runtime — the tool runs with the full environment and full filesystem
access.
| Axis | Extra requirement |
|---|---|
| env | None for hygiene mode; bubblewrap for effective protection |
| fs | bubblewrap required — fs policy errors out without it |
Install bwrap once:
sudo apt install bubblewrap # Debian / Ubuntu
sudo dnf install bubblewrap # Fedora / RHEL
sudo pacman -S bubblewrap # Arch
sudo apk add bubblewrap # Alpine
macOS enforcement, Landlock fallback, and network policy are future security tracks, not part of the current runtime-policy contract.
What this is not¶
Runtime policy is deliberately narrow. It is not a general malware sandbox and it is not a substitute for a disposable VM/container/user when running hostile install scripts.
- It does not protect
ixt tool installitself from package-manager scripts. - It does not block outbound network access.
- It does not make macOS or Windows execution safer; policies are recorded there, not enforced.
- It does not hide files unless an fs policy is active and bwrap is available.
Implemented axes¶
| Axis | Command | Protects against (with bubblewrap) |
|---|---|---|
| Environment | ixt tool config <target> env |
secret exfiltration via env vars (AWS_SECRET_KEY, GH_TOKEN, …) and /proc/*/environ reads |
| Filesystem | ixt tool config <target> fs |
reads outside the intended mounts, writes outside allowed paths, access to ~/.ssh / ~/.aws |
Network policy is on the roadmap; there is no ixt tool config <target> net command today.
How policies compose¶
The enforcement mechanism depends on which policies are active and whether bwrap is on PATH:
no policy → symlink → os.execve(real_bin) [zero overhead]
env only, bwrap available → shim → bwrap → --unshare-pid --as-pid-1 --proc /proc
--bind / / (full host fs visible, rw)
--clearenv --setenv K V… -- real_bin
os.execve(bwrap, args, _e) [defense in depth]
env only, bwrap missing → shim (degraded) → warn once per session, then
os.execve(real_bin, filtered_env) [hygiene only]
fs only / env + fs → shim → bwrap → --unshare-pid --as-pid-1 --proc /proc
--die-with-parent + preset binds + extras
--clearenv --setenv K V… -- real_bin
os.execve(bwrap, args, _e)
The sandbox constant (--unshare-pid --as-pid-1 --proc /proc --die-with-parent) is added every time the shim enters bwrap, regardless of which axis triggered it. PID isolation closes /proc/*/environ reads from inside the tool. --as-pid-1 makes the tool itself PID 1 in its namespace, so /proc/1/environ shows its filtered env (no separate reaper holding the host environ).
The defense in depth pattern — passing _e (filtered) instead of os.environ to execve(bwrap, …) — means the bwrap process itself starts with the filtered env, so /proc/<bwrap>/environ is also clean from outside the namespace.
Opt-out and escape hatches¶
| Variable | Effect |
|---|---|
IXT_POLICY_QUIET=1 |
Silence the runtime degraded-mode warning (read before env filtering, so it works even with strict env policies). |
IXT_DISABLE_BWRAP=1 |
Force degraded mode for env-only policies even when bwrap is installed (debugging / fs policy still errors out without bwrap). |
IXT_SHIM_DEBUG=1 |
Print the env classification (passed/blocked) and fs preset to stderr before exec. |
Quick reference¶
# env
ixt tool config <target> env list
ixt tool config <target> env base none|all|os-common
ixt tool config <target> env allow 'PATTERN'
ixt tool config <target> env deny 'PATTERN'
# fs
ixt tool config <target> fs list
ixt tool config <target> fs base all|app-common|app-minimal|none
ixt tool config <target> fs ro PATH # auto-promotes fs_base "all" → "app-minimal"
ixt tool config <target> fs rw PATH # idem
ixt tool config <target> fs scratch PATH # idem
# debug / opt-out
IXT_SHIM_DEBUG=1 <tool> [args] # dump effective policy to stderr
IXT_POLICY_QUIET=1 <tool> [args] # silence the degraded-mode warning
IXT_DISABLE_BWRAP=1 <tool> [args] # force degraded mode for env-only
Typical workflow¶
ixt tool install ruff
# lock env first — only OS vars + RUFF_*
ixt tool config ruff env base os-common
ixt tool config ruff env allow '*RUFF*'
# lock fs — read-only root, rw cwd, hide secrets
ixt tool config ruff fs base app-common
ixt tool config ruff fs scratch ~/.aws ~/.ssh
# verify
ixt tool config ruff env list
ixt tool config ruff fs list
IXT_SHIM_DEBUG=1 ruff check .