Skip to content

Recipes ​

Practical examples for common jai setups.

Claude Code ​

Install Claude Code in its own jail ​

bash
curl -fsSL https://claude.ai/install.sh | \
    jai -D -mstrict -j claude bash
  • -D suppresses granting the current directory (you're installing, not coding)
  • -mstrict uses strict mode with a separate user ID
  • -j claude creates a named jail called claude
  • The installer runs inside the jail and writes to $HOME/.jai/claude.home

Run Claude Code from that jail ​

If $HOME/.local/bin is already on your PATH:

bash
jai -j claude claude

Otherwise:

bash
PATH=$HOME/.local/bin:$PATH jai -j claude claude

Make jai claude use the claude jail by default ​

Create $HOME/.jai/claude.conf:

conf .defaults
jail claude
setenv PATH=${HOME}/.local/bin:${PATH}

(Note the braces are required around environment variables: ${HOME} expands to your home directory while $HOME does not expand.) Now you can simply run:

bash
jai claude

jai will find claude.conf, use the claude jail in strict mode, and prepend the executable directory to PATH.

More advanced claude configuration ​

Here is how the author stores all claude state in his actual home directory, rather than a sandbox (so that it persists across deleting the sandbox) and makes the .git subdirectory read-only when invoking claude in the root of a git repository:

# ~/.jai/claude.conf
conf .defaults
jail claude
rdir? ${PWD}/.git
dir! .claude
dir! .local/share/claude
dir! .local/state/claude
setenv PATH=${HOME}/.local/bin:${PATH}

Note the use of dir! will create these directories when they don't already exist (which they shouldn't because you should never run claude outside of jai).

Codex ​

Run Codex in casual mode ​

If Codex stores configuration in ~/.codex, expose that directory so you can delete your jail state without losing your codex configuration:

bash
jai -d ~/.codex codex

Advanced configuration so jai codex just works ​

Create $HOME/.jai/codex.conf:

conf .defaults
jail codex
dir! codex-install
dir! .codex
rdir? ${PWD}/.git
setenv PREFIX=${HOME}/codex-install
setenv PATH=${PREFIX}/bin:${PATH}

Now jai codex automatically exposes ~/.codex and ~/codex-install. If you want to run a shell and see what codex's sandbox looks like, just specify the configuration explicitly as in jai -C codex. If you run jai -C codex npm i -g @openai/codex, it will see the $PREFIX environment variable and install its files in $HOME/codex-install.

OpenCode ​

Run OpenCode in casual mode ​

OpenCode uses two directories for state:

bash
jai -d ~/.config/opencode -d ~/.local/share/opencode opencode

Make jai opencode do this by default ​

Create $HOME/.jai/opencode.conf:

conf .defaults
dir .config/opencode
dir .local/share/opencode

Python virtualenv ​

Run Python with a jailed virtualenv ​

Create $HOME/.jai/python.conf:

conf .defaults
mode strict
dir venv
jail python
command source $HOME/venv/bin/activate; "$0" "$@"

Now jai python enters strict mode, exposes the venv directory, activates the virtualenv, and runs whatever Python command you provide.

Multiple named jails ​

Named jails let you isolate tools from each other. Each gets its own home directory:

bash
jai -j claude claude      # strict jail named "claude"
jai -j codex codex        # strict jail named "codex"
jai -j scratch bash       # strict jail named "scratch" (bash is optional)

Each jail has independent state:

  • Strict/bare home: $HOME/.jai/<name>.home
  • Casual overlay changes: $HOME/.jai/<name>.changes
  • Private tmp: /run/jai/$USER/tmp/<name>

Seed new jails from a skeleton directory ​

If you want new strict or bare jails to start with a few dotfiles, put those files in $HOME/.jai/.skel/, then create an init helper:

bash
cat > ~/.jai/.initjail <<'EOF'
#!/bin/sh
set -eu

case "$JAI_MODE" in
  strict|bare)
    cp -rL "$JAI_CONFIG_DIR/.skel/." .
    ;;
esac
EOF
chmod +x ~/.jai/.initjail

Then reference it from the relevant config file:

conf .defaults
jail claude
initjail .initjail

The helper runs only when jai creates the jail home directory for the first time. It runs outside the jail, but as your real user, with the current working directory set to the new jail home.

Cleanup and reset ​

Tear down all jails ​

bash
jai -u

This unmounts all overlay directories from /run/jai, cleans up .work directories, and destroys private /tmp and /var/tmp contents. Run this when:

  • You want to start from scratch
  • You changed --mask options in a config file (masks are only applied when the overlay is first created)
  • You need to edit files in $HOME/.jai/*.changes directly
  • Overlay mounts are behaving strangely

Tear down one named jail ​

bash
jai -u -j claude

This unmounts and cleans up runtime state for the named jail only. It is useful when you want to reset one overlay or one private /tmp without touching other jails.

Recover changes from casual mode ​

If jailed software modified config files in your home directory, you'll find those changes in:

$HOME/.jai/default.changes/

(Or $HOME/.jai/<name>.changes/ for named jails.)

To move these changes into your real home:

  1. Run jai -u to unmount the overlay
  2. Copy the changed files from the .changes directory to your home
  3. Re-run jai — optionally adding -d flags for directories the tool needs to write to directly