Repositories / jai.git

jai.1.md

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch
24147 bytes · ab4c11108c6f
--- title: jai(1) author: David Mazieres outline: [1,2] --- # NAME jai - Jail an AI agent # SYNOPSIS `jai` `--init` \ `jai` [*option*]... [*cmd* [*arg*]...] \ `jai` `-u` # DESCRIPTION jai is a super-lightweight sandbox for AI agents requiring almost no configuration. By default it provides casual security, so is not a substitute for using a proper container to confine agents. However, 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 `/tmp` and `/var/tmp` directories, and 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. 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 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 addition 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 To install claude code in a jail called `claude`: curl -fsSL https://claude.ai/install.sh | \ jai -D -mstrict -j claude bash (Note the `bash` argument is optional, because jai runs `bash` by default.) 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 `jail -C claude` to get a shell with the same permissions as claude, so as to understand what claude is seeing. 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 in jai, not in your outside shells, so you don't accidentally do it without jai. 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 # OPTIONS `--init` : Create default configuration files and exit. Gives you a chance to edit the default configuration files before creating any jails. `-C` *file*, `--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/.jai` and does not contain a `/`, you can omit any `.conf` extension. So `-C default` is equivalent to `-C default.conf` (assuming you don't have a file `default` in addition to `default.conf`). 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. 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. `-d` *dir*, `--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. `-x` *dir*, `--xdir` *dir* : Reverse the effects of a previous `--dir` *dir* option. `-D`, `--nocwd` : By default, `jai` grants access to the current working directory even if it is not specified with `-d`. This option suppresses that behavior. If you run with `-D` and no `-d` options, 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`} : Set jai'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 `--mask` option 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. `-j` *name*, `--jail` *name* : jai allows 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 the `jail` configuration option in a `.jail` file or any configuration file included by the `.jail` file. `--mask` *file* : When creating an overlay home directory, create a "whiteout" file to hide *file* in the jail. *file* must be a relative path and is always relative to your home directory, regardless of where you run jai. You can specify this option multiple times. An easier way to hide files is just to delete them from `/run/jai/$USER/*.home`; hence, this option is mostly useful in configuration files to specify a set of files to delete by default. If you add `mask` directives to your configuration file, you will need to clear mounts with `jai -u` before the changes take effect. `--unmask` *file* : Reverse the effects of a previous `--mask` option. This does not unmask files that have already been masked in an existing jail. For that, you need to go into `$HOME/.jai/`*name*`.changes` and manually remove the whiteout files. It also does nothing if you have masked a parent directory of *file*. The main utility of this option is to reverse `mask` lines in a configuration file. For instance, you can include a default set of masked files with a `conf` option and then surgically remove individual masked files that you want to expose. `--unsetenv` *var* : Filters *var* from the environment of the jailed program. Can be the name of an environment variable, or can use the wildcard `*` as in `*_PID`. (Since jailed processes don't see outside processes, you might as well filter any PIDs exposed in environment variables to avoid confusion.) `--setenv` *var*, `--setenv` *var*`=`*value* : There are two forms of this command. 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. For example, `--unsetenv=*_PASSWORD --setenv=IPMI_PASSWORD` and `--unsetenv=IPMI_PASSWORD --setenv=IPMI_PASSWORD` will both pass the `IPMI_PASSWORD` environment variable through to the jail, while `--unsetenv=*_PASSWORD --setenv=IPMI_*` will not. If the argument contains `=`, then *var* is always treated as a variable, not a pattern, and it is assigned *value* in the jail. `--storage` *dir* : Specify an alternate location in which to store private home directories and overlays. The default is `$JAI_CONFIG_DIR` if set, otherwise `$HOME/.jai`. However, if your home directory is on NFS you may wish to use storage on a local file system, as NFS does not support the extended attributes required by overlay file systems. You can of course install a symbolic link for each individual home directory, but `--storage` allows you to relocate the base directory where all jails are located. `--command` *bash-command* : jai launches the jailed program you specify by running "`/bin/bash -c` *bash-command* *cmd* *arg*...". By default, *bash-command* just runs the program as `"$0" "$@"`, but in configuration files for particular programs, you can use *bash-command* to set environment variables or add additional command-line options. `-u` : Unmounts all overlay directories from `/run/jai` and cleans up overlay-related files in `$HOME/.jai/*.work` that the user might not be able to clean up without root. This option also destroys the private `/tmp` and `/var/tmp` directories (same directory at both mount points), so make sure you don't need anything in there. Overlay mounts for casual jails are created under `/run/jai/$USER/*.home` and left around between invocations of jai. If you wish to change "upper" directories `$HOME/.jai/*.changes`, the changes may not take effect until the file system is unmounted and remounted. For that reason, `--mask` options are only applied when first creating the overlay mount. Hence, you must run `jai -u` before changing `--mask` options or directly editing the changes directory. `--print-defaults` : Prints the default contents for `$HOME/.jai/.defaults`. `--version` : Prints the version number and copyright and exit. `--complete` : This option is only valid as the first option on the command line. It tells jai not to do anything, but it prints a list of completions to assist shells in doing command completion. For example: `jai --complete -m "c"` prints `casual`. If the commands line is complete, then it will output the special string `_command_offset `*N*, which indicates that argument *N* is the start of a new command that should be completed according to the rules for that command. # ENVIRONMENT The following environment variables affect jai's operation: `SUDO_USER`, `USER` : If jai is invoked with real UID 0 and either of these environment variables exists, it will be taken as the user whose home directory should be sandboxed. This makes it convenient to run `jai` via `sudo` if you don't want to install it setuid root. If both are set, `SUDO_USER` takes precedence. `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, or casual) inside a jail. `JAI_JAIL` : Set to the selected jail name (specified by `-j` or `--jail`) inside the jail. `JAI_USER` : Set to the name of the user who invoked jai. # FILES 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` : Configuration file if none is specified with `-C`. If there is a file for *cmd*, then *cmd*`.conf` is used. Otherwise `default.conf` is used. `$HOME/.jai/.defaults` : Reasonable system defaults to be included in `default.conf` or *cmd*`.conf`. This file is created automatically by jai. The file has no effect if you don't include it, but you should probably begin all configuration files with the line `conf .defaults` to 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 `.conf` file. Usually only sets `mode` for the sandbox, but could also conceivably include `dir` and other options except `jail`, 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 with `jai -u`. The non-default version is used when you specify `-j` *name* on the command line. If you specified `--storage=`*dir*, the changes directory will looked up under *dir* instead of `$HOME/.jai` (though in either case may be a symbolic link elsewhere). `$HOME/.jai/default.work`, `$HOME/.jai/`*name*`.work` : This "work" directory is required by overlayfs, but 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 running `jai -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 under *dir* instead 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 `--mask` option. `/run/jai/$USER/tmp/default`, `/run/jai/$USER/tmp/`*name* : Private `/tmp` and `/var/tmp` directory (they are the same) in jails. # BUGS 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. # SEE ALSO <https://github.com/stanford-scs/jai> - jai home page