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