jai(1)
๐ NAME ๐
jai โ Jail an AI agent ๐
โจ SYNOPSIS โจ
jai --init
jai [option]... [cmd [arg]...]
jai -u [-j jail]
๐ฆ DESCRIPTION ๐ฆ
Okay so jai is a super-lightweight sandbox for AI agents requiring almost no configuration. ๐ By default it provides casual security, so it is not a substitute for using a proper container to confine agents. But bestie, it is a great alternative to using no protection at all when you are thinking of giving an agent full control of your account and all its files. Compared to the latter, jai can reduce the blast radius should things go wrong. ๐ซถ
By default, if you run jai cmd [arg]..., it will execute cmd with the specified arguments in a lightweight jail that has:
- full access to the current working directory and everything below ๐ธ
- copy-on-write access to an overlay mount of your home directory ๐
- private
/tmpand/var/tmpdirectories โจ - the rest of the file system read-only ๐ฆ
Note, however, that device nodes remain usable subject to normal permission checks; a read-only /dev mount does not prevent opening devices read-write. ๐
Note also inherited file descriptors for file outside the jail can still be used by jailed commands. ๐ญ
If you donโt specify cmd, jai will launch a jailed shell by default. ๐
Executing a command in this way is known as casual mode, because cmd can read most sensitive files on the system. In other words, jai prevents cmd from clobbering all your files, but doesnโt provide much confidentiality. ๐
If you run jai -mstrict cmd [arg]..., then cmd will be run with an empty home directory, using the credentials of the unprivileged user jai on your system, but with the current working directory mapped to its place and fully exposed. Though the rest of the system outside your home directory is available read-only, because cmd is running with user jaiโs credentials rather than yours, it will not be able to read sensitive files that require your user ID or group IDs โ assuming you donโt add jai to any supplemental groups, which would be a strange thing to do. ๐
Strict mode does not let you grant access to NFS file systems. If your home directory is on NFS, you can instead use bare mode with jai -mbare. Bare mode hides your entire home directory like strict mode, but it runs jailed software with your user credentials, and hence allows jailed software to use your credentials to read any sensitive files you have access to outside your home directory. ๐ง
Note that all modes use a private PID namespace, so jailed software cannot kill or ptrace processes outside of the jail. ๐ฅ Moreover, all modes have a private /run/user/$UID/ directory (a.k.a. $XDG_RUNTIME_DIR), since many sensitive daemons have sockets in that directory. jai will let you expose that directory or subdirectories of it with the -d option, discussed below. E.g., jai -d $XDG_RUNTIME_DIR/emacs makes emacsclient in the jail open an unjailed editor โ but it also allows jailed software to request evaluation of arbitrary elisp outside of the jail, eliminating any security boundary (though still potentially guarding against accidental file erasure). ๐ญ
By default, jai will store private home directories under $HOME/.jai. However, it needs the ability to set extended attributes on casual jails, which is not possible if your home directory is on NFS. You can use the option --storage=/some/local/directory to store private home directories in a different location, as long as you own the storage directory. Alternatively, you can set the JAI_CONFIG_DIR environment variable to move your entire configuration directory from $HOME/.jai to a local disk. ๐
If you want to grant access to directories other than the current working directory, you can specify additional directories with the -d option, as in jai -d /local/build untrusted_program. If you donโt want to grant access to the current working directory, use the -D option. Note that by default, jai will refuse to run in your home directory, on the assumption that this is probably a mistake and that you donโt want to grant your entire home directory to jailed processes. If you are in your home directory, you can launch jai with -D to start in the sandboxed version of your home directory without granting anything. If you really want to grant your entire home directory to the jail, you can still do so by running jai -Dd $HOME, but since that negates most of jaiโs protections, it would only make sense in unusual corner cases. ๐
If you use casual mode and jailed software stores configuration files in your home directory, you will find any such changes in $HOME/.jai/default.changes (or wherever you specified for --storage). If you wanted these changes in your home directory, you can destroy the jail with jai -u, move the changed files back into your home directory, then re-run jai with the appropriate -d flag to expose whatever directory contains the changed files (e.g., $HOME/.application or $HOME/.config/application). ๐
jai allows the use of multiple home directories for different jails. To use a home directory other than the default, just give it a name with the -j option and it will be created on demand. If you donโt specify -mcasual or -mbare, strict mode will become the default for a newly created jail, but you can change this by editing the corresponding .jail file in $HOME/.jai or wherever your storage directory is. ๐
๐ CONFIGURATION ๐
Configuration comes from three sources: the command line, a .conf configuration file (which may include other files via conf options), and a .jail file specific to the named jail you are choosing. Command-line options override everything, and .jail files override .conf files โ except that .jail files cannot select a different jail. โจ
If you donโt specify a .conf file on the command line with the -C option, and if cmd does not contain any slashes, jai will first try to use $HOME/.jai/cmd.conf if that file exists, and otherwise will use $HOME/.jai/default.conf (which it will create if necessary). That way the .conf file can specify a jail name and the .jail file can set the mode of the jail. ๐
The format of .conf and .jail configuration files is a series of lines of the form option [value] or option=value. option can be any long command-line option without the leading --, for example:
conf .defaults
mode casual
dir /local/build
mask Mail
If you want to set an option that requires an argument to the empty string, use an = sign, as in storage= to reset the default storage location. ๐
Within a configuration file, conf acts like an include directive, logically replacing the conf line with the contents of another configuration file. Relative paths in conf include directives are relative to $HOME/.jai/ (or $JAI_CONFIG_DIR if set). jai creates a file .defaults with a sensible set of defaults you should probably include directly or indirectly as the first thing in any .conf configuration file you write. (By including it first, anything else in your .conf file will override the defaults.) ๐ธ
jai executes jailed programs with bash. The command directive allows you to reconfigure the environment or add command-line options to certain commands. For instance, to use a python virtual environment in a jail, you might create a file python.conf with the following:
conf .defaults
mode strict
dir venv
jail python
command source $HOME/venv/bin/activate; "$0" "$@"
If you run jai python, this configuration file will load a virtual environment before running the command. For more complicated setup logic, you can use setenv to set the BASH_ENV environment variable to an initialization script to be sourced in non-interactive session. ๐ง
The dir, xdir, mask, unmask, setenv, and storage options, and conf options in configuration files will perform environment variable substitution for variable names contained within ${...}. Note the braces are required, unlike in the shell. You can quote a literal $ or \ by preceding it with a backslash \. ๐
โจ EXAMPLES โจ
okay bestie, practical era ๐ hereโs how you actually use this thing.
To install claude code in a jail called claude:
curl -fsSL https://claude.ai/install.sh | \
jai -D -mstrict -j claude bash
(Note that jai runs your login shell by default, so if your shell is bash, then you can just pipe curl to jai -D -mstrict -j claude without even specifying bash.) ๐ซถ
To invoke claude code in that same jail, if $HOME/.local/bin is not already on your path:
PATH=$HOME/.local/bin:$PATH jai -j claude claude
To make jai claude use the claude jail by default:
cat <<'EOF' >$HOME/.jai/claude.conf
conf .defaults
jail claude
setenv PATH=${HOME}/.local/bin:${PATH}
EOF
Now you can run jai claude to invoke claude code with access to the current working directory, and jai -C claude to get a shell with the same permissions as claude, so as to understand what claude is seeing. ๐โจ
To prohibit claude from changing git state when run in the root of a git repository, you can make $PWD/.git read-only when it exists:
cat <<'EOF' >$HOME/.jai/claude.conf
conf .defaults
jail claude
rdir? ${PWD}/.git
setenv PATH=${HOME}/.local/bin:${PATH}
EOF
To make jai claudeyolo run claude in dangerous mode, you could create another configuration file like this:
cat <<'EOF' > $HOME/.jai/claudeyolo.conf
# Start with claude's defaults
conf claude
# Add a shell function that will expand "claudeyolo" to the
# appropriate claude command for destroying your file system.
# (Note semicolon before the right brace and after, and backslash
# for continuation lines in the configuration file.)
command claudeyolo(){ \
claude --permission-mode bypassPermissions "$@"; \
}; "$0" "$@"
EOF
๐จ The author is not advocating doing the above! But if you are going to use claude in dangerous mode, better to make the alias available only injai, not in your unconfined shells, so you donโt accidentally invoke the mode withoutjai. ๐จ
Suppose you want to make your X11 session available in the claude jail to facilitate pasting images into claude. This significantly reduces security, so isnโt necessarily a good idea, but you can do it by extracting your authentication cookies in your current working directory and merging them into your claude jail.
# Extract cookies outside jail, merge them inside jail
xauth extract - $DISPLAY | jai -C claude xauth merge -
# Now copy a screen region to paste into claude...
import png:- | xclip -selection clipboard -t image/png
A safer way to do this is to write your screengrabs directly into the sandboxโs /tmp directory as in:
import /run/jai/$USER/tmp/claude/scrn.png
Then in claude, just incorporate the image with @/tmp/scrn.png. ๐ผ๏ธโจ
To use an existing codex or opencode installation in casual mode (less safe) and have it update configuration files in your real home directory:
jai -d ~/.codex codex
jai -d ~/.config/opencode -d ~/.local/share/opencode opencode
To do this by default when invoking jai codex (similar for jai opencode):
cat <<EOF >$HOME/.jai/codex.conf
conf .defaults
# list additional directories to expose
dir .codex
EOF
Suppose that you put a skeletal set of dot files in $HOME/.jai/.skelโfor example, $HOME/.jai/.skel/.bashrc, $HOME/.jai/.skel/.inputrc and you want all newly created strict or bare jails to have these files in the home directory. Suppose further that you want to make sure githubโs public SSH keys are always in your known_hosts files, even though you mask .ssh in casual jails. You could create an initialization script as follows:
cat > ~/.jai/.initjail <<'EOF'
#!/bin/bash
# Out of paranoia, make sure this isn't run in your real
# home directory
if [[ $(readlink -f "$HOME") == $(readlink -f .) ]]; then
echo Do not run this in your real home directory>&2
exit 1
fi
# When creating strict/bare mode jails, populate the new home
# directory with the contents of $HOME/.jai/.skel/
if [[ $JAI_MODE == strict || $JAI_MODE == bare ]]; then
cp -rL "$JAI_CONFIG_DIR/.skel/." .
fi
# In any kind of new jail, copy github's public key to your new .ssh
# directory. (Casual jails mask .ssh by default, so you need to
# create the directory regardless of mode.)
mkdir --mode=0700 .ssh
grep -E "^github.com " "$HOME/.ssh/known_hosts" > .ssh/known_hosts
EOF
chmod +x ~/.jai/.initjail
echo initjail .initjail >> ~/.jai/.defaults
If you prevent jailed processes from inheriting any file descriptors other than 0, 1, and 2, you can use a script to close all file descriptors other than these and file descriptor 255 (which bash uses). For example, you could add the following lines to $HOME/.jai/.jairc and refresh your .defaults file (run jai --print-defaults) if command does not already source $JAI_SCRIPT:
scrub_fds() {
local p fd
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
}
test -n "${JAI_KEEP_FDS+set}" || scrub_fds
Then unless you set the JAI_KEEP_FDS environment variable, file descriptors will be scrubbed by default in sandboxes. ๐
๐ OPTIONS ๐
--initโ Create default configuration files and exit. Gives you a chance to edit the default configuration files before creating any jails. ๐-Cfile,--conffile,--conf?file โ Specifies the configuration file to read. If file does not contain a/, the file is relative to$HOME/.jai. Also, if file resides in$HOME/.jaiand does not contain a/, you can omit any.confextension. So-C defaultis equivalent to-C default.conf(assuming you donโt have a filedefaultin addition todefault.conf). Unlike the first two options,--conf?does not cause an error if file does not exist.
If no configuration file is specified, the default is based on the cmd argument. If cmd contains no slashes and does not start with ., the system will use $HOME/.jai/cmd.conf if such a file exists. Otherwise it uses $HOME/.jai/default.conf.
Note that command-line arguments are parsed both before and after the file specified by the -C or --conf option. Hence, command-line options always take precedence over configuration files (though this works slightly differently for --script, described below). When conf is specified in a configuration file, however, the behavior is different. The specified file is read at the exact point of the conf directive, overriding previous lines and subject to being reversed by subsequent lines. ๐ง
-ddir,--dirdir,--dir!dir โ Grant full access to directory dir and everything below in the jail. You must own dir. You can supply this option multiple times. Note that on the command line, relative paths are relative to the current working directory, while in configuration files, they are relative to your home directory. The--dir!form creates the directory if it didnโt already exist. ๐ฅ-rdir,--rdirdir,--rdir?dir โ Like--dir, but grant read-only access to directory dir. Like-dand--dir,-rand--rdircause an error if the directory does not exist, while--rdir?is silently ignored if the directory does not exist. ๐ธ-xdir,--xdirdir โ Reverse the effects of a previous--dirdir or--rdirdir option. ๐ฆ-D,--nocwdโ By default,jaigrants access to the current working directory even if it is not specified with-d. This option suppresses that behavior. If you run with-Dand no-doptions, your entire home directory will be copy-on-write (in casual mode) or empty (in bare or strict mode) and nothing will be directly exported. ๐ญ-m {casual|bare|strict},--mode {casual|bare|strict}โ Setjaiโs execution mode. In casual mode, the userโs home directory is made available as an overlay mount. Casual mode protects against destruction of files outside of granted directories, but does not protect confidentiality: jailed code can read most files accessible to the user. You can hide specific files with the--maskoption or by deleting them under/run/jai/$USER/*.home, but because casual mode makes everything readable by default, it cannot protect all sensitive files.
In strict mode, the userโs home directory is replaced by an empty directory ($HOME/.jai/name.home), and jailed code runs with a different user id, jai. Id-mapped mounts are used to map jai to the invoking user in granted directories. Strict mode is the default when you name a jail (see --jail), but not for the default jail.
Bare mode uses an empty directory like strict mode, but runs with the invoking userโs credentials. It is inferior to strict mode, but can be used for NFS-mounted home directories since NFS does not support id-mapped mounts. ๐
-jname,--jailname โjaiallows you to have multiple jailed home directories, which may be useful when jailing multiple tools that should not have access to each otherโs API keys. This option specifies which jail to use. If no such jail exists yet, it will be created on demand and the mode specified (strict by default) will become the default for that jail, though you can change it in the file$HOME/.jai/name.jail.
Note that if you switch modes, the same name can have both a casual home directory (accessible at /run/jai/$USER/name.home, with changes going in $HOME/.jai/name.changes) and a strict/bare home directory (in $HOME/.jai/name.home). There is no special relation between these two home directories, but all jails by the name name share the same /tmp directory.
๐จ Note that you are not allowed to use thejailconfiguration option in a.jailfile or any configuration file included by the.jailfile. ๐จ
--maskfile โ when jai's building you an overlay home dir, this drops a "whiteout" file in there so file is hidden inside the jail ๐ซฃ. file has to be a relative path, and it's always relative to your home directory no matter where in the world you ranjaifrom. You can stack this option as many times as you want. Ngl though, the easier way to hide stuff is literally just deleting it from/run/jai/$USER/*.homeโ so this option is mostly a configuration-file girly, for declaring a default set of files to nuke. โ ๏ธ Heads up: if you addmaskdirectives to your config file, you have to clear the mounts withjai -ubefore the changes will actually take effect. It's giving "you have to refresh the page bestie."--unmaskfile โ undoes a previous--maskfile, bestie. But it does not unmask files that already got masked in an existing jail โ for that you have to go into$HOME/.jai/name.changesand manually remove the whiteout files yourself ๐ญ. It also does absolutely nothing if you've masked a parent directory of file. Its real main-character purpose is reversingmasklines in a configuration file โ like, you include a default batch of masked files with aconfoption and then surgically remove the individual ones you actually want to expose. ๐--unsetenvvar โ filters var out of the jailed program's environment โจ. var can be the name of an environment variable, or it can use the wildcard*, like*_PID. (Since jailed processes can't see outside processes anyway, you might as well filter any PIDs exposed in environment variables so nobody gets confused. iykyk.)--setenvvar,--setenvvar=value โ okay so this one has two whole forms and you need to know which is which ๐:
If the argument does not contain =, then --setenv reverses the effect of --unsetenv var. If var is a pattern, it must exactly match the unset pattern you want to remove. Example time, because this is the part that's giving tricksy:
--unsetenv=*_PASSWORD --setenv=IPMI_PASSWORDโ passesIPMI_PASSWORDthrough to the jail โ--unsetenv=IPMI_PASSWORD --setenv=IPMI_PASSWORDโ also passesIPMI_PASSWORDthrough โ--unsetenv=*_PASSWORD --setenv=IPMI_*โ does not pass it through โ
If the argument does contain =, then var is always treated as a variable, never a pattern, and it gets assigned value inside the jail. Simple as that. ๐
--storagedir โ tells jai to stash its private home directories and overlays somewhere else entirely ๐ฆ. The default is$JAI_CONFIG_DIRif that's set, otherwise$HOME/.jai. Now, if your home directory lives on NFS you might lowkey want storage on a local file system, because NFS does not support the extended attributes that overlay file systems require โ and no, we cannot fix that for you ๐ญ. You can obviously install a symbolic link for each individual home directory, but--storagelets you relocate the whole base directory where all the jails live. One and done. โจ--scriptbash-file,--script?bash-file โ drop one or more bash script files here and they get concatenated into a temporary file that also deletes itself when sourced, and theJAI_SCRIPTenvironment variable will point at that file inside the jail ๐ฆ. Since you can already run commands from bash with the--commandoption,--scriptis how you define shell functions that act like command aliases.--scriptaborts with an error if bash-file doesn't exist, while--script?silently ignores a non-existent or inaccessible file (iconic "no thoughts head empty" energy). On the command line, the path of bash-file is relative to the current working directory, but in a configuration file it's relative to$JAI_CONFIG_DIR(or$HOME/.jaiif that's not set).
The concatenated script file will also try to delete itself to keep your /tmp directory clean ๐งน. You can disable that behavior and keep the script around for the duration of the sandbox run by setting the JAI_KEEP_SCRIPT environment variable. If you're getting some error message (๐ rude), you can run:
JAI_KEEP_SCRIPT=1 jai -Cconf
(with no command) to get a shell, and then examine the file $JAI_SCRIPT from inside the sandbox. Detective mode activated ๐.
Also important: each script file is included only once in the concatenated file โ starting with script files specified on the command line, then those in.conffiles, then those in.jailfiles. Why? Because earlier files can bypass processing of later ones by using the bashreturnbuiltin, or can make variables read-only via thereadonlyordeclare -rbash builtins. Sneaky but valid. ๐
--initjailprogram,--initjail?program โ if the jail doesn't exist yet, then after creating it, jai will run program outside any jail, but with the current working directory set to the external location of what the jail's home will be ๐ โจ. In casual jails that's/run/jai/$USER/jail.home, while in bare and strict jails it's$JAI_CONFIG_DIR/jail.home. ($JAI_CONFIG_DIRwill always be set in the script, even if you're using the default location of$HOME/.jai.) You can use this to set an executable script that populates the home directories of newly created jails from your existing home directory. And be sure to check the$JAI_MODEenvironment variable, because you'll probably want to initialize casual jails differently from strict/bare jails. ๐
Note that program is relative to the current working directory when --initjail is specified on the command line, and relative to $JAI_CONFIG_DIR when it's specified in a configuration file. Same energy as --script, just saying.
--commandbash-command โ set this and jai launches the jailed program you specify by running:
/bin/bash -c bash-command cmd arg...
You can run the program as "$0" "$@" in bash-command, but the whole point of the option is that it lets you set an environment variable or source an rc script ๐ธ. Fun fact: the .defaults file that gets created by default uses --command to source all the scripts you've specified with --script. It's all connected, bestie ๐.
-uโ unmounts all overlay directories from/run/jaiand cleans up overlay-related files in$HOME/.jai/*.workthat you might not be able to clean up without root ๐งน๐. This option also destroys the private/tmpand/var/tmpdirectories (yep, same directory at both mount points), so make absolutely sure you don't need anything in there. Like, gone gone. ๐ญ
Overlay mounts for casual jails are created under /run/jai/$USER/*.home and left around between invocations of jai. If you want to change the "upper" directories $HOME/.jai/*.changes, the changes may not take effect until the file system is unmounted and remounted. For that exact reason, --mask options only get applied when first creating the overlay mount โ so you must run jai -u before changing --mask options or directly editing the changes directory. No shortcuts. โ ๏ธ
If you specify -j jail in addition to -u, jai will clean up only one specific jail. For a casual jail that means unmounting the overlay mount and cleaning the work directory. Now for strict jails โ they all share one copy of the password file (in which user jai's home directory has been changed to the invoking user's home directory). -u will attempt to unmount and delete that password file, but it may not be able to if other strict jails are still in use. Sharing is caring until it isn't ๐ซถ
--print-defaultsโ prints the default contents for$HOME/.jai/.defaults๐จ๏ธโจ. Free peek behind the curtain.--versionโ prints the version number and copyright, then exits ๐. Short and sweet.--completeโ this option is only valid as the first option on the command line, periodt. It tells jai not to do anything, but it prints a list of completions to help shells do command completion ๐. For example:
jai --complete -m "c"
prints casual. If the command line is complete, it outputs the special string _command_offsetN, which indicates that argument N is the start of a new command that should be completed according to the rules for that command. Shell wizards only ๐ฎ
๐ ENVIRONMENT ๐
The following environment variables affect jai's operation, bestie โ memorize the vibes:
SUDO_USER,USERโ if jai is invoked with real UID0and either of these environment variables exists, it'll be taken as the user whose home directory should be sandboxed ๐. This makes it convenient to run jai viasudoif you don't want to install it setuid root. If both are set,SUDO_USERtakes precedence. First come, first served ๐JAI_CONFIG_DIRโ location of jai configuration files and private home directories, by default$HOME/.jai๐ก. If your home directory is on NFS, you may wish to put your private home directories elsewhere in order to use casual mode.
Jai sets the following environment variables inside jails:
JAI_MODEโ set to the mode (strict,bare, orcasual) inside a jail ๐ญJAI_JAILโ set to the selected jail name (specified by-jor--jail) inside the jail ๐ท๏ธJAI_USERโ set to the name of the user who invoked jai ๐ซถJAI_SCRIPTโ inside a sandbox, if set, contains the path of a file containing the concatenation of all the script files specified with--scriptoptions ๐JAI_KEEP_SCRIPTโ usually the script file deletes itself as soon as it has been sourced. If it's reporting errors and you want to debug it, set theJAI_KEEP_SCRIPTenvironment variable and the script will persist for the duration of the sandbox ๐โจ. jai will still try to delete it on exit, however. It's not that sentimental.PWDโ set to the current working directory ๐. Usually your shell will already set this variable, but jai guarantees it's correct, so you can expand it in configuration files. Trust. ๐
๐ FILES โ the directory lore, no cap ๐
In the following paths, the location $HOME/.jai can be changed by setting the JAI_CONFIG_DIR environment variable.
$HOME/.jai/default.conf,$HOME/.jai/cmd.confโ this is the config file if none is specified with-C. If thereโs a file for cmd, thencmd.confis used. Otherwisedefault.confis used.$HOME/.jai/.defaultsโ reasonable system defaults to be included indefault.conforcmd.conf. This file is created automatically byjai. It has no effect if you donโt include it, but bestie, you should probably begin all configuration files with the lineconf .defaultsto get the defaults.
In the following paths, the location $HOME/.jai is changed by the --storage option. In the absence of a --storage option, the location can be changed by the JAI_CONFIG_DIR environment variable.
$HOME/.jai/name.jailโ configuration file for jail named name, read after and overriding any settings in the.conffile. Usually this only sets mode for the sandbox, but it could also conceivably includedirand other options exceptjail, which is disallowed.$HOME/.jai/default.changes,$HOME/.jai/name.changesโ this โupperโ directory is overlaid on your home directory and contains changes that have been made inside a casual jail. Before directly changing this directory, tear down and recreate the sandboxed home directory withjai -u. The non-default version is used when you specify-j nameon the command line. If you specified--storage=dir, the changes directory will be looked up underdirinstead of$HOME/.jai(though in either case it may be a symbolic link elsewhere).$HOME/.jai/default.work,$HOME/.jai/name.workโ this โworkโ directory is required by overlayfs, but it does not contain anything user-accessible. Every once in a while the overlay file system may create files in here that you cannot delete. If you are trying to delete an overlay directory to start from scratch and cannot delete this directory, try runningjai -u, which will clean things up. If you specified--storage=dir, or used a symbolic link for your changes directory, then the work directory will always be next to the changes directory wherever that lives.$HOME/.jai/default.home,$HOME/.jai/name.homeโ private home directory for bare and strict jails. If you specified--storage=dir, these directories will be underdirinstead of$HOME/.jai.
The following paths are always fixed, regardless of environment variables or command-line options:
/run/jai/$USER/default.home,/run/jai/$USER/name.homeโ home directories for casual jails. You can delete files with sensitive data in these jail directories to hide them from jailed processes, or see the--maskoption./run/jai/$USER/tmp/default,/run/jai/$USER/tmp/nameโ private/tmpand/var/tmpdirectory (they are the same) in jails./run/jai/$USER/tmp/.run/default,/run/jai/$USER/tmp/.run/nameโ outside a sandbox, these paths provide access to/run/user/$UIDinside either the default sandbox or the one named name.
๐ BUGS โ the little gremlin behaviors ๐
Overlayfs needs an empty directory $HOME/.jai/work, into which it places two root-owned directories index and work. Usually these directories are empty when the file system is unmounted. However, occasionally they contain files, in which case it requires root privileges to delete the directories. You can run jai -u to clean these up if you are unable to delete them.
Overlayfs can be flaky. If the attributes on the default.changes directory get out of sync, it may require making a new default.changes directory to get around mounting errors.
If the jailed program terminates with a signal, jai exits with status 255 rather than emulating the signal death.
๐ SEE ALSO โ go touch grass on the homepage ๐
https://github.com/stanford-scs/jaiโ jai home page
๐ AUTHORS โ the icon behind it all ๐
David Mazieres.
Thatโs the whole jai(1) manual, bestie โ source/version footer: jai(1), and the date is whatever your local man page stamped on it. Mwah ๐โจ๐
๐ฆ where this page comes from
- nixpkgs unstable: jai-jail 0.3
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms