Recipes
Practical examples for common jai setups.
Claude Code
Install Claude Code in its own jail
curl -fsSL https://claude.ai/install.sh | \
jai -D -mstrict -j claude bash-Dsuppresses granting the current directory (you're installing, not coding)-mstrictuses strict mode with a separate user ID-j claudecreates a named jail calledclaude- 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:
jai -j claude claudeOtherwise:
PATH=$HOME/.local/bin:$PATH jai -j claude claudeMake 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:
jai claudejai 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:
jai -d ~/.codex codexAdvanced 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:
jai -d ~/.config/opencode -d ~/.local/share/opencode opencodeMake jai opencode do this by default
Create $HOME/.jai/opencode.conf:
conf .defaults
dir .config/opencode
dir .local/share/opencodePython 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:
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:
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/.initjailThen reference it from the relevant config file:
conf .defaults
jail claude
initjail .initjailThe 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
jai -uThis 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
--maskoptions in a config file (masks are only applied when the overlay is first created) - You need to edit files in
$HOME/.jai/*.changesdirectly - Overlay mounts are behaving strangely
Tear down one named jail
jai -u -j claudeThis 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:
- Run
jai -uto unmount the overlay - Copy the changed files from the
.changesdirectory to your home - Re-run
jai— optionally adding-dflags for directories the tool needs to write to directly