FAQ
General
What is jai?
jai is a super-lightweight Linux sandbox for AI agents and coding assistants. It aims to provide meaningful protection without altering your workflow, thereby eliminating any excuse to run unconfined agents on any Linux machine other than a throw-away VM or container.
In casual mode, the least obtrusive way to run jai, it uses Linux mount namespaces and overlayfs to give a sandboxed process full access to your working directory, copy-on-write access to your home directory, private temp directories, and read-only access to the rest of the filesystem. In strict mode, jai hides the contents of your home directory and provides access to the rest of the system with a different UID. The goal is to reduce the blast radius when running AI tools on your real machine — not to promise perfect isolation.
Was jai written by an AI coding agent?
No. While this web site was obviously made by an LLM (ChatGPT read the man page, asked some follow-up questions, and produced a prompt from which claude code built a vitepress site), jai itself was hand implemented by a Stanford computer science professor with decades of C++ and Unix/linux experience. As an experiment, the author did previously try vibe-coding a container, but the results were disastrous and repeatedly put his machine in a state that required a reboot (e.g., recursively changing the attributes of all mounts in the wrong mount namespace). The author does use coding agents to look for bugs, get feedback, and develop tests. However, rest assured that a single human understands every line of in jai.
Does jai work on MacOS?
The way jai configures jails is deeply specific to linux kernel APIs for mounting file systems and managing namespaces. However, jaim is an independent effort to port jai to MacOS. It manages to keep about 75% of jai's code, so should feel familiar to jai users, although the exact semantics of the different modes is not the same.
The author of jai is unfamiliar with MacOS and does not have access to MacOS for testing, so cannot directly vouch for jaim, but the project looks promising. In addition, several other Mac users have suggested that spawn and bx-mac may be relevant to jai users, though again the author cannot vouch for them.
Modes
Which mode should I use?
You should use strict mode, because it provides the best protection. However, the real question is which mode will you use, because jai only protects you when you actually use it. Once you have configured things just right, you may find that all you really need is strict mode. But if your plan is to use jai eventually when you get around to configuring it, then start off in casual mode today, because it should just work without any configuration.
Casual (the default when you don't specify a specific jail name) provides the least friction, with little to no confidentiality but reasonable protection against file system damage outside of your current working directory.
Strict (the default for named jails) provides meaningful confidentiality. It's like running the jailed software in a dedicated agent account on your machine, separate from your normal login, except without the hassle of creating another account and moving your project files to the agent account.
Bare is useful if you need strict-like home isolation but your home directory is on NFS, which does not support id-mapped mounts.
See Modes for a full comparison.
Why is casual mode not enough for confidentiality?
In casual mode, the jailed process runs as your user. It can read anything you can read — SSH keys, API tokens, browser session data, and every other file in your home directory. The copy-on-write overlay protects integrity (writes go to a separate layer), but it does not hide the underlying files.
The --mask option and default blacklists hide some known sensitive dotfiles, but casual mode exposes everything readable by default. You cannot exhaustively enumerate and mask every sensitive file. If confidentiality matters, use strict mode.
Compatibility
Why does jai not support Linux versions earlier than 6.13?
Linux 6.13 added support for fsconfig FSCONFIG_SET_FD to overlayfs. That makes it much easier for privileged software like jai to avoid time-of-check-to-time-of-use (TOCTTOU) vulnerabilities when creating overlay mounts. Linux 6.12 was released in 2024, three months before claude code. You really shouldn't let your kernel lag so far behind AI agents that may try to attack it.
Can I run jai on a machine with an old C++ compiler?
Yes, as long as your kernel is 6.13 or later, you only need a modern compiler to build jai, not to run it. If you have a machine with a newer compiler, you can build a version of jai that works on older distro releases by configuring your build not to depend on the compiler runtime as follows:
./configure LDFLAGS="-static-libstdc++ -static-libgcc"
What should I do if my home directory is on NFS?
You should place your jai overlay storage on a local file system. You can do this with symbolic links, but it is easier to relocate the storage base directory with --storage=/local/path (or storage /local/path in $HOME/.jai/.defaults). You will also need to use bare mode instead of strict mode in most cases, so might want to add mode bare to your $HOME/.jai/.defaults file.
Why does casual mode require a non-NFS storage directory?
The linux overlay file system implementation makes use of extended file attributes (e.g., trusted.overlay.origin) to record aspects of the correspondence between an "upper" changes directory and the "lower" read-only home directory. Unfortunately, these attributes cannot be set or accessed over NFS. Also, jai needs root privileges to create whiteout files for masked paths, and often NFS clients do not have root access to remote file systems. Fortunately, the "lower" directory can be on NFS, so you can use casual mode with an NFS home directory as long as you put the upper ".changes" directory someplace local.
Why does strict mode not work with NFS?
Strict mode uses id-mapped mounts to make granted directories appear owned by the unprivileged jai user inside the jail, even though the real files are owned by the invoking user. NFS does not support id-mapped mounts — this is a kernel/NFS implementation limitation, not a jai limitation. This is why bare mode exists.
Note that if your home directory is on NFS but you don't grant access to any part of your home directory (e.g., jai -D), and if you relocate your storage with --storage=/local/path, then strict mode will work. But generally people want to run jai to work in some subdirectory of their home directory.
Overlay & Storage
Where do changes go in casual mode?
Writes to your home directory inside a casual jail go to:
$HOME/.jai/default.changes/ # unnamed jails
$HOME/.jai/<name>.changes/ # named jailsThese directories are the "upper" layer of the overlayfs mount. To move changes back to your real home directory, run jai -u to unmount the overlay, then copy the files.
If you specified --storage=dir, the changes directory is under dir instead.
Can I re-expose a file in the overlay home after deleting or masking it?
Yes. The overlay file system uses whiteout files in the "upper" directory to record deletion. These are just character device nodes with major/minor number 0/0. To remove a whiteout file, first unmount the overlay file system with jai -u. Then change to the upper directory, generally in ~/.jai/default.changes (or substitute the name of your jail for default). Find the whiteout file among the changed files there, and just delete it.
Note that if you delete a whiteout file without first tearing down the overlay with jai -u, you may not see the effects, as the overlay file system can cache information and doesn't expect concurrent modifications of the upper directory.
Can I access my overlay home directory outside of jai?
Yes, once jai has created an overlay home, you can find it (outside of a sandbox) under /run/jai/$USER/default.home. You can also find the private /tmp directories under /run/jai/$USER/tmp/. One use for this is to delete files containing sensitive data from the overlay (as an alternative to --mask) from outside a sandbox. Be extra, extra careful, however, because the overlay will look like your home directory, and you don't want to get into the habit of deleting important files in places that look like your home directory. One sanity check is to run df .. In an overlay directory, the file system will be named something like jai-default.home as opposed to a block device like /dev/sda4.
What does jai -u do?
jai -u tears down the sandbox state:
- Unmounts all overlay directories from
/run/jai/$USER - Cleans up the overlay work directories (
$HOME/.jai/*.work), which sometimes contain root-owned files users cannot delete - Destroys private
/tmpand/var/tmpdirectories
Run jai -u when you want a fresh start, when you've changed --mask options (masks are only applied at overlay creation time), or when overlays are behaving strangely.
Is there a system-level jai -u that applies to all users?
As root, to destroy all mounts of all users, run umount -R /run/jai. You will see errors for sandboxes that are still in use, and can kill these processes to free the sandbox.
Note: avoid running umount -Rl /run/jai, which forcibly detaches mounts even if they are in use, as that does not actually free the mounts. Hence, if processes are still using overlay home directories, they will be unable to launch new sandboxes, because the old overlay will still exist, just not be accessible from the primary mount namespace.
Using jai
How can I protect my local git repository?
By default, git stores all of its repository information below your project directory, in a subdirectory called .git. That means if jai gives an agent access to your project directory, the agent can wipe not just your project but its local git history.
The first level of defense should be that you always push your changes to a remote server such as github. This is usually how people work, so that if you lose your laptop you don't lose huge amounts of your work. That said, if you have local branches or other state you don't want to lose in your local git repository, you should protect your .git directory.
The easiest way to protect your git directory is to make it read-only with the --rdir option. For example, the author uses the following in his codex.conf and claude.conf files:
rdir? ${PWD}/.git
With that configuration, whenever jai is run in a directory with a .git subdirectory, that subdirectory is made read-only. However, the use of rdir? rather than rdir means it is not an error if no .git subdirectory exists.
If you ever want to override this to have an agent create git commits, you can run jai -x .git, where -x (short for --xdir) undoes the effects of the rdir in your configuration file. However, the author prefers to have git state be read-only by default.
Can I make emacsclient in the jail invoke emacs outside the jail?
Yes, but at a cost in security. The tldr is that the author one-shot vibe-coded an emacs proxy for jai in under 5 minutes, and it seems to work, so you can do the same, or start from his and see what it does (though he in no way vouches for it).
In a bit more detail, emacs listens for client connections under $XDG_RUNTIME_DIR/emacs/server, so merely running jai -d $XDG_RUNTIME_DIR/emacs will expose your emacs server to the sandbox, but doing that is a bad idea for three reasons.
emacsclientcan ask emacs to execute general-purpose elisp code, which means jailed code is essentially no longer confined, as it can ask emacs to do arbitrary things outside the sandbox./tmpin your jail is actually/run/jai/$USER/tmp/jail outside the jail, soemacsclientwon't work on files in/tmpand/var/tmp.Code in a sandbox can potentially trick you into editing files outside the sandbox.
Assuming $XDG_RUNTIME_DIR is /run/user/$UID, jai makes the sandboxed runtime directory available outside the sandbox at /run/jai/$USER/tmp/.run/jail. So the author prompted an AI to build at tool that listens at that address for a jail specified on the command line, filters out elisp evaluation requests, translates /tmp/ and paths, does a half-hearted attempt at blocking symbolic links, and then passes the request to a real emacs server outside the sandbox. What came out might or might not be useful to you, but it's a good example of little tools that are much easier to build with AI.
Can I run Xorg applications inside the jail?
This works by default in casual sandboxes, but it's not very secure. Among other things, sandboxed code can read your clipboard (so don't be cutting and pasting passwords!), and it can use sendevents to type in other windows. If you want to do this in a bare or strict sandbox (again, not a great idea), you need to copy your Xorg authentication cookies into your sandbox. For example, to do this for the sandbox configured by $HOME/.jai/claude.conf, you would run:
xauth extract - $DISPLAY | jai -C claude xauth merge -
If you don't want sandboxed code accessing your Xorg server even in casual mode, add the following line to $HOME/.jai/.defaults and run jai -u:
mask .Xauthority
Or you can simply run jai rm .Xauthority to delete the file in your overlay.
Why does jai use bash?
jai launches jailed programs through bash to support the command directive in configuration files. The command directive lets you set environment variables, activate virtual environments, or modify the command line before execution. For example:
command source $HOME/venv/bin/activate; "$0" "$@"If you are using the .defaults file created by jai, you will have the following command:
command source "${JAI_SCRIPT:-/dev/null}"; "$0" "$@"
This allows you to set up multiple scripts that get concatenated into a single file ${JAI_SCRIPT} and sourced before running your command. Here are some examples of things you can do.
Create
$HOME/.jai/.pythonas follows, then runjai --script .python ...to run programs in the python virtual environment.source $HOME/venv/bin/activateCreate
$HOME/.jai/.yoloas follows, then runjai --script .yolo claudeto start the assistant in dangerous mode.claude() { command claude --dangerously-skip-permissions "$@"; } codex() { command codex --dangerously-bypass-approvals-and-sandbox "$@"; }Create
$HOME/.jai/.scrubas follows, then runjai --script .scrubto scrub all open file descriptors before launching sandboxed code. (The default is to inherit all file descriptors.)for p in /proc/$$/fd/*; do fd=${p##*/} case $fd in 0|1|2|255) continue ;; ''|*[!0-9]*) continue ;; esac eval "exec $fd<&- $fd>&-" 2>/dev/null || : done
Can I use sudo instead of setuid installation?
Yes. jai reads the SUDO_USER environment variable to determine whose home directory to sandbox:
sudo jai bash
sudo jai -j claude claudeThis works transparently. If both SUDO_USER and USER are set, SUDO_USER takes precedence. However, sudo usage has a few annoyances. You will need to be root to kill jai from the outside.
How do named jails help?
Named jails (via -j name) give each tool its own home directory. This is useful when:
- Multiple AI tools should not see each other's API keys or configuration
- You want a clean, persistent home directory for a specific tool
- You want strict mode isolation (named jails default to strict)
Each named jail has independent state — its own home directory (for strict/bare mode), changes directory (for casual mode), and /tmp. You can have as many named jails as you want.
My home directory is empty in casual mode — what should I do?
You may have modified the upper directory while the overlay file system was mounted or something else may have put the overlay file system into a bad state. Try unmounting it with jai -u or jai -u default (assuming the default jail). If the unmount fails, you will need to kill any existing jai process first.
Comparison with Other Tools
Why not just Docker?
Docker is excellent for reproducible, image-based environments. But for ad-hoc sandboxing of a CLI tool on your host machine, it requires:
- Building or pulling an image with the tool installed
- Configuring bind mounts for your working directory
- Managing the impedance mismatch between container and host (UIDs, paths, shell config)
- No native overlay-on-home workflow
jai trades Docker's reproducibility and stronger isolation for zero-setup, host-native sandboxing. One command, no images. See the Comparison page for details.
Why not just bubblewrap?
bubblewrap is a powerful, flexible namespace sandbox. It can do almost everything jai does — but it requires you to explicitly assemble the filesystem view from scratch with 10+ flags. In practice, this turns into a wrapper script per tool, which is exactly the friction jai is designed to remove.
bubblewrap also cannot easily set up overlayfs (it runs unprivileged and would need fuse-overlayfs), so you lose the copy-on-write home directory that makes casual mode convenient. See the Comparison page for a side-by-side.
Why not just chroot?
Linux documents chroot as not intended to be used as a security mechanism. It provides no mount namespace isolation, no PID namespace isolation, no credential separation, and is trivially escapable by root. It is not a meaningful sandbox. Any of the other tools on this page would be an improvement.