Skip to content

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 install itself 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 .