Run AI coding agents in a lightweight Bubblewrap sandbox, with project tooling
managed by direnv and Nix devShells.
aibox is the lightweight successor to
aidock: no container image, no image commits
on exit, no per-session agent binary drift, and no runtime configuration file.
aibox codex
aibox claude
aibox copilot
aibox hermes
aibox omp
aibox opencode
aibox pi
aibox ori codex
aibox -- bashSupported agent state/config policy is maintained for Codex, Claude Code, GitHub Copilot CLI, Hermes, OMP, OpenCode, and Pi. OpenRouter support uses Ori. Other commands can be useful for debugging, but they are best-effort and do not get dedicated persistent state/config mounts.
Nix is the recommended installation method:
nix profile install github:ruifm/aibox/v0.3.0Run without installing:
nix run github:ruifm/aibox/v0.3.0 -- codexFrom a clone:
nix profile install .Secondary direct-script install:
mkdir -p ~/.local/bin
curl -fsSL https://raw.githubusercontent.com/ruifm/aibox/v0.3.0/aibox -o ~/.local/bin/aibox
chmod +x ~/.local/bin/aiboxAfter installing, run an end-to-end smoke check:
aibox -- true && echo "aibox smoke check passed"This also creates the known persistent agent state paths when they are missing.
Select a tested version from GitHub Releases. Each release includes compatibility notes for mounts, environment handling, and command-line changes. Published version tags are not moved.
For a host or project flake, pin the selected tag:
inputs.aibox.url = "github:ruifm/aibox/v0.3.0";Commit the flake lock with its exact revision and NAR hash. To update, change the
tag, run nix flake update aibox, and test the locked result before deployment.
Release automation can select a GitHub release and retain that same exact lock.
main remains the development branch. Sandbox startup does not check for or
install updates. See the 0.3.0 compatibility notes.
Use direnv and a Nix devShell per project. The agent can edit flake.nix,
refresh the devShell, and install project-specific tools declaratively.
Example .envrc:
use flakeIf flake.nix exists but .envrc does not, an interactive aibox launch
offers to create the .envrc above. If neither file exists, add a minimal
flake.nix devShell first. Aibox does not create a devShell for you.
Then launch the agent from the repository root:
cd my-project
aibox codexWhen .envrc is present, Aibox loads it before it starts the agent. If it is
not approved, Aibox shows its content and asks before it runs direnv allow.
Approval persists in host direnv state: a later host shell can run the file
outside Aibox. Review it and any files it loads before you approve it. With no
terminal, Aibox stops and shows the manual approval command. If direnv is not
installed, Aibox continues with the inherited environment and warns.
In a Git repository, Nix does not see an untracked flake.nix. Aibox asks
before git add -N -- flake.nix; this records intent to add but does not stage
the file content. It never runs git init, force-adds ignored files, or adds
other untracked files. If Nix reports another missing project file, review that
file and add it to the Git index yourself. Non-interactive launches stop when
Git approval is needed. A local flake outside a Git repository needs no Git
setup.
The agent inherits the loaded environment, so routine commands need no
direnv exec . prefix. This also works when your host shell has already loaded
direnv; aibox -p remains available inside the sandbox.
If you change .envrc or the devShell during a session, the running agent's
environment does not update. Use direnv exec .
for the next command, or restart Aibox after any needed approval.
aibox mounts the current directory, not its parent directories. If you start
it from a directory below the repository root, parent files such as .git,
AGENTS.md, CLAUDE.md, and agent settings are not visible.
Use --host-ro when a command needs to inspect host files or run an installed
host tool that the default sandbox cannot see:
aibox --host-ro -- codexThis flag is available on main. The v0.3.0 install commands above predate it;
use a checkout or a locked main revision until the next release.
This opt-in mode exposes host files, including other projects, home files,
SSH keys, and credentials, read-only with your normal user permissions. The
current directory, the usual persistent agent and direnv state, and explicit
--rw-mount paths remain writable. Host /tmp, /var/tmp, /run, /proc,
/dev, and /sys remain private. The Nix daemon and selected desktop services
work as in the default mode. Aibox prints a notice before the Bubblewrap
command. AIBOX_MODE=host-ro identifies the active mode inside the box; it is
not proof of isolation.
This mode helps prevent direct edits and deletion outside the project. Readable credentials, host services, and the shared network can still permit changes elsewhere. A read-only mount of a Unix socket does not make service requests read-only. The mode does not expose the host D-Bus or systemd sockets. Use the separate service flags only when needed.
Package queries such as pacman -Qi and apt-cache policy can read available
host metadata. Package updates and installs cannot write the host package
database. journalctl can read journals under /var/log/journal and, when
present, /run/log/journal, subject to normal file permissions. Live host
systemctl status is unavailable because it needs access to the host service
manager. Some tools will still fail if they need writes outside the selected
paths or access to hidden runtime services.
Generic home cache and config paths are readable but not writable in this
mode. Agent state symlinks to / or other protected paths are rejected.
Start Aibox from the repository root when you need to edit project files;
files above that root remain read-only. aibox --host-ro -p shows the agent
prompt for this mode. Inside a running box, aibox -p shows the active mode.
Use --ro-mount PATH or --rw-mount PATH to expose an existing file,
directory, or Unix socket. Each option can be repeated. Relative paths and
symlinks resolve to their absolute host paths; the same resolved paths appear
inside the sandbox. The default mounts remain unchanged. Missing paths and
mounts that overlap existing mounts or Aibox's internal paths fail at startup.
These options never create a host path.
A socket is a connection to a service. A read-only socket mount still permits service requests. Share a dedicated directory when a server needs to create a socket inside Aibox, then give clients the socket path alone:
shared=$(mktemp -d "${XDG_RUNTIME_DIR}/aibox-session.XXXXXX")
aibox --rw-mount "$shared" -- server-command "$shared/session.sock"
# In another terminal, with the same value of shared:
aibox --rw-mount "$shared/session.sock" -- client-command "$shared/session.sock"The directory must be private to your account; mktemp -d creates it with
mode 0700. A client bound to one socket must restart if the server replaces the
socket. Share the dedicated directory in both processes if clients must follow
socket replacement. Remove the directory after both processes stop.
The Podman devShell example runs a limited Podman engine
inside Aibox. It uses another Bubblewrap namespace to provide a private
/sys/fs/cgroup path and masks host subordinate ID maps. It works in both
default and --host-ro modes. It does not connect to a host container service.
For a new project, put the example flake.nix, flake.lock, .envrc,
.gitignore, .containerignore, .dockerignore, aibox-podman.sh,
containers.conf, and policy.json in its root. Add them to Git before using
use flake, so Nix includes the source files. In a project that already has a
flake or ignore files, merge the package and ignore rules instead of replacing
those files. Keep the project's own pinned Nix inputs. Both container ignore
files exclude .aibox-podman/ from image build contexts. Activate the
devShell with direnv allow or nix develop, then run from the project root:
aibox -- aibox-podman run --rm docker.io/library/alpine:3.22 echo hello
aibox -- aibox-podman build --network=host --isolation=rootless -t localhost/demo .The example keeps images in .aibox-podman/storage in the project and ignores
that directory in Git. Live sockets and process state use Aibox's private
XDG_RUNTIME_DIR and end with the box. Registry login data stored there is
also temporary; use a separate, private credential path if it must persist.
Do not use the image store from concurrent boxes. The example uses VFS storage,
disables container cgroups,
shares the host network, and maps only one user and group. An image that runs
as another UID, such as 1234:1234, can fail. Cgroup resource limits and
container network isolation are unavailable. The private cgroup path can cause
a Podman cgroups-v1 warning; this setup is version-sensitive and is pinned by
the example lock file. The example accepts unsigned images in policy.json;
TLS certificate checks still apply. Edit that policy if the project needs
signature rules.
The Docker CLI can use a Podman API socket created inside the box. Add
pkgs.docker-client to the devShell, then start the API and set DOCKER_HOST
inside the box:
aibox -- bash
aibox-podman system service --time=0 "unix://$XDG_RUNTIME_DIR/podman.sock" &
DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman.sock" docker psStop the API process when done. This socket grants its clients control of the
Podman engine in that box. It is not a host Docker daemon. Nested dockerd,
LXD, Docker Compose, and images that need multiple user IDs are not covered by
this example.
On a Wayland desktop, select the host services an agent needs:
aibox --keyring --wayland --browser -- copilot--keyring connects to the host Secret Service through a filtered D-Bus proxy.
Copilot can then use a token that it stored in the host keyring. This permission
also lets sandboxed programs read or change other secrets that the service
allows the same account to access. Aibox does not copy or migrate tokens.
If Copilot has already stored a plaintext token in ~/.copilot/config.json,
that file remains until you remove it through Copilot's account controls or
after you verify another login method.
--wayland exposes one host Wayland socket. Programs such as wl-copy and
wl-paste can then copy or paste text and images. This socket also permits
other Wayland operations; it is not a clipboard-only permission. A clipboard
selection can disappear when the process that supplies it stops. This option
does not support X11.
--browser makes xdg-open and BROWSER use the host OpenURI portal. Browser
login can use the host's existing SSO session and a local callback because
Aibox shares the host network. The proxy permits OpenURI and introspection on
the portal object. It does not permit file-opening methods, and OpenURI does
not accept file:// URLs. The portal reports the final result asynchronously.
A successful xdg-open exit means the request was sent, not that a browser
opened. Remote SSH sessions can need separate loopback forwarding. Browser
profiles are not mounted.
The Nix package includes the proxy and client tools. A direct-script install
needs xdg-dbus-proxy and gdbus for --keyring or --browser, and
wl-clipboard for wl-copy and wl-paste. The host must have a working D-Bus
session and Secret Service for --keyring, a desktop portal for --browser,
and a Wayland socket for --wayland. Missing services stop startup with a
named error. Without these flags, Aibox does not mount desktop sockets.
Use aibox --hostname project-agent -- codex to set a display name. The default
is aibox. Names contain 1-64 ASCII letters, digits, dots, or hyphens, with a
letter or digit at each end. --hostname=project-agent also works.
Scripts can use [ "${AIBOX:-}" = 1 ] to select sandbox behavior. The marker is
set inside every sandbox, including when the hostname changes. It does not prove
isolation or grant authority. Replace hostname checks with this marker and
remove source patches for --hostname aibox. The option changes no mounts or
credential policy.
Install Ori and the agent in a project devShell or Nix profile. Sign in without trying to start a host browser from the sandbox, then run the agent through Ori:
aibox ori login --no-browser
aibox ori codexYou can also set OPENROUTER_API_KEY before you start aibox. The sandbox
keeps inherited environment variables.
Ori keeps shared login and settings in ~/.ori, which aibox mounts
read/write. Ori also writes logs and session transcripts to .ori/ in the
project. Add .ori/ to the project .gitignore because these files can contain
repository content and prompts.
Before starting a session, paste the generated prompt into the agent. Inside an
active agent session, aibox is available on PATH, so you can also run
!aibox -p and ask the agent to follow it. The prompt names common sandbox
limits and the flags or devShell steps that address them.
aibox -pTo start the sandbox:
- Linux.
- Bash 4+.
- Bubblewrap.
- Unprivileged user namespaces available to Bubblewrap.
- Core commands such as
realpath,mktemp, andteeonPATH. The Nix package supplies these commands and Bubblewrap. - An existing
$HOMEwith writable agent state paths, and a writable host/tmp. Start from a project directory, not/or$HOME.
For the recommended devShell workflow, install Nix with
a multi-user daemon and direnv. Check nix store info
on the host. Aibox can start without either tool. If the project has a Nix
shell file or .envrc, Aibox warns when the needed tool is not visible inside
the sandbox. A missing Nix daemon socket also gets a warning. Warnings do not
stop a command that does not need these tools.
For an agent session, install the supported agent command you intend to run
(codex, claude, copilot, hermes, omp, opencode, pi, or ori)
somewhere the sandbox can see it: the project Nix devShell, a Nix profile, or
the host system paths mounted read-only at /usr and /bin. Ori also needs the
target agent command.
In its default mode, aibox builds a Bubblewrap command that:
- binds the current directory read/write at the same absolute path;
- keeps host networking available;
- allows nested user namespaces so programs can create their own sandboxes;
- starts
$HOMEas a private tmpfs skeleton at the same absolute path; - mounts selected real agent state/config directories back into that home;
- exposes
/nix/storeread-only; - binds the Nix daemon socket so
nix develop,nix build, anddirenv exec .can work; - mounts
aiboxitself read-only at/run/aibox/bin/aiboxand prepends that directory toPATH; - leaves the inherited
PATHvisible, but does not discover or bind-mount arbitrary agent binaries from host-only paths such as~/.local/binor~/.opencode/bin; - remounts the synthetic root read-only after setup;
- prints the final
bwrapcommand to stderr before execution.
aibox does not try to bind a single resolved agent executable into the
sandbox. Many agent commands are scripts or wrappers that need interpreters,
dynamic libraries, Node package trees, plugin files, or a Nix closure. Keeping
agent CLIs in a project devShell or Nix profile makes the executable and its
runtime closure available through the already-mounted Nix paths without adding
an arbitrary host-path mount interface.
Persistent agent state/config mounts:
~/.agents/skills~/.agents/plugins~/.codex~/.claude~/.claude.json~/.config/anthropic~/.copilot~/.cache/copilot~/.hermes~/.omp~/.pi~/.cache/opencode~/.config/opencode~/.local/share/opencode~/.local/state/opencode~/.ori
These writable state paths are created on the host before launch when missing.
~/.claude.json is initialized as {}. If one of the expected directories is
a file, or ~/.claude.json is not a file, aibox fails before starting the
sandbox.
~/.agents/skills is shared by Codex and Copilot CLI. Codex uses
~/.agents/plugins for personal plugin records. Claude Code can use Anthropic
CLI profiles and credentials from ~/.config/anthropic. A selected Anthropic
API profile can use API billing instead of Claude subscription billing.
~/.cache/copilot is persistent to prevent repeated downloads.
OpenCode uses ~/.config/opencode for user settings, instructions, agents,
commands, skills, and plugins. It uses ~/.local/share/opencode for login,
MCP, session, and log data. It uses ~/.local/state/opencode for state and
~/.cache/opencode for downloaded tools and plugin packages. Project
.opencode files are available through the project mount.
Hermes keeps its config, credentials, sessions, memories, skills, plugins, and
managed tools in ~/.hermes. OMP keeps its config and default state in
~/.omp. Pi keeps its config, credentials, sessions, packages, skills, and
extensions in ~/.pi.
OMP can move data, state, and cache to ~/.local/share/omp,
~/.local/state/omp, and ~/.cache/omp. aibox mounts these paths when they
already exist. It does not create them because OMP uses their existence to
decide if XDG migration is complete. Run omp config init-xdg outside aibox
if you want to migrate an existing OMP profile.
System agent config mounts are read-only and are added only when they exist:
/etc/codex/etc/claude-code/etc/github-copilot/etc/hermes/etc/opencode
On NixOS, the matching /etc/static subdirectories are also mounted read-only.
This includes the existing Nix and certificate directories. The complete host
/etc/static directory is not mounted. Links to arbitrary host targets are not
followed to add mounts. Nix store contents remain readable through /nix/store.
To require system policy files before a command starts:
aibox --require-config /etc/codex/managed_config.toml \
--require-config=/etc/codex/requirements.toml -- codexEach path must be an absolute file path below one of the five system agent
directories above, without . or .. components. The check runs inside the
sandbox and stops startup if a file is missing or unreadable. The option does
not add mounts or check whether the agent obeys the policy. Without the option,
missing system configuration remains optional. NixOS packages can remove their
source patches for /etc/static/codex.
For required configuration and certificate files, startup errors name the requirement and its path. Aibox keeps Bubblewrap's original error and adds a short hint when it detects a namespace, hostname, or mount setup failure. These hints name possible systemd restrictions; they do not identify a unit setting with certainty. Application errors keep their status and terminal stderr without a sandbox startup hint.
aibox uses the default agent state paths. It removes CODEX_HOME,
CODEX_SQLITE_HOME, CLAUDE_CONFIG_DIR, ANTHROPIC_CONFIG_DIR,
COPILOT_HOME, COPILOT_CACHE_HOME, HERMES_HOME, HERMES_MANAGED_DIR,
PI_CONFIG_DIR, PI_CODING_AGENT_DIR, PI_CODING_AGENT_SESSION_DIR,
PI_SERVER_DIR, and OPENCODE_CONFIG_DIR inside the sandbox. It does not use
environment variables to add mounts. Profile variables such as
ANTHROPIC_PROFILE, HERMES_PROFILE, and OMP_PROFILE still pass through.
OPENCODE_CONFIG and OPENCODE_TUI_CONFIG also pass through. The file must be
in the project or another path that the sandbox can read.
Other inherited agent variables also pass through. This includes API tokens,
model settings, provider settings, and profile selectors. A new variable does
not need a change in aibox unless it contains a file path that must be visible
inside the sandbox.
aibox does not mount GitHub CLI config, Git config, SSH or GPG agents, Docker,
Kubernetes config, or browser profiles. The host D-Bus socket stays hidden even
when the filtered desktop proxy is active. Inherited token environment
variables remain another sign-in method. In --host-ro mode, host files such
as Git and GitHub config, SSH and GPG keys, and browser profiles are readable
with normal user permissions. Host agent and D-Bus sockets under the private
runtime paths stay hidden unless explicitly selected.
This is an accident-safety tool, not hostile-code containment.
The sandbox is intended to prevent routine agent/tool mistakes from writing
outside the project or reading broad host state. It still gives the agent full
network access, and it mounts real agent auth/session/config directories. Any
readable mounted data can be exfiltrated by code running in the sandbox.
--host-ro intentionally permits broad host reads, so it does not provide the
default mode's read boundary. Neither mode prevents use of readable credentials
or services over the network.
aibox does not clear the ordinary process environment. API keys, cloud
credentials, package tokens, and other variables inherited by aibox are
visible to processes inside the sandbox. The printed bwrap command also
contains the command arguments you passed to aibox, so do not pass secrets as
CLI arguments.
Nix builds are performed by the host Nix daemon, outside Bubblewrap. Use host Nix sandboxing if build isolation matters.
Nested sandboxes inherit Aibox's filesystem limits. They can change their own
mount layout, but cannot make inherited host read-only mounts writable or
remove individual inherited mounts to reveal hidden files. Allowing nested
user namespaces exposes more kernel operations; Aibox does not block these as
an extra hardening measure. This behavior is available on main, after v0.3.0.
This does not configure nested container runtimes or expose host container services. A host Docker, Podman, or LXD service runs outside Aibox's filesystem boundary, even if its client runs inside Aibox.
| Paths | Access and lifetime |
|---|---|
Workspace, including .direnv |
Read/write; persists on the host |
| Agent state/config paths listed above | Read/write; persists on the host |
Explicit --rw-mount and --ro-mount paths |
Selected host access; persists on the host |
~/.local/share/direnv, ~/.cache/direnv |
Read/write; persists on the host |
Other files below ~/.cache, ~/.config, ~/.local |
Private temporary storage, except listed read-only mounts |
/tmp, /var/tmp, /run/aibox/runtime |
Private temporary storage; each new sandbox starts empty |
| Home skeleton outside writable mounts | Read-only; not the host home contents |
| Nix store, system configuration, Nix profiles, direnv configuration | Read-only host mounts |
With --host-ro, other host files are visible read-only. The workspace,
selected agent and direnv state, and explicit writable mounts still persist.
Temporary paths and the private runtime directory have the same lifetime as in
the default mode.
XDG_RUNTIME_DIR is /run/aibox/runtime, a separate tmpfs mount owned by the
invoking user with mode 0700. The inherited runtime path is replaced. Concurrent
sandboxes do not share it. Use it for sockets and active GPG state that must not
survive a restart. Storage is released when the sandbox processes end.
aibox removes SSH_AUTH_SOCK, GPG_AGENT_INFO, GNUPGHOME,
DBUS_SESSION_BUS_ADDRESS, DOCKER_HOST, and KUBECONFIG, plus the agent path
variables listed above. It replaces HOME, the XDG base directories, and
TMPDIR, and selects NIX_REMOTE=daemon when the daemon socket exists.
It clears inherited direnv tracking before it loads the project environment.
With --keyring or --browser, it sets DBUS_SESSION_BUS_ADDRESS to the
private proxy. With --wayland, it sets the private Wayland socket name and
removes inherited X11 display settings.
Other variables remain visible, including credentials. No host SSH or GPG agent
is forwarded automatically.
Sending SIGHUP, SIGINT, or SIGTERM to the launcher stops the sandbox.
The launcher returns exit status 129, 130, or 143, respectively. Commands keep
their standard input and normal interrupt handling.
Persistent agent directories can contain credentials, live databases, locks, and sockets. Do not synchronize complete live state directories without checking the application's backup requirements. Exclude sockets, locks, temporary runtime files, and credential bundles from ordinary source synchronization.
Use a dedicated credential bundle inside the workspace when a command needs it. Create it outside the sandbox, restrict its directory to mode 0700 and private files to mode 0600, and exclude it from Git and synchronization. Do not put secret keys in Nix expressions or the Nix store.
For SSH, select the dedicated key and known-hosts file explicitly:
aibox -- ssh -F /dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
-o UserKnownHostsFile="$PWD/.credentials/known_hosts" \
-i "$PWD/.credentials/id_ed25519" operator@server.exampleFor GPG and a dedicated password store, set paths after sandbox entry:
aibox -- bash -c '
export GNUPGHOME="$XDG_RUNTIME_DIR/gnupg"
export PASSWORD_STORE_DIR="$PWD/.credentials/password-store"
install -d -m 0700 "$GNUPGHOME"
gpg --batch --import "$PWD/.credentials/gpg-secret.asc" || exit
exec pass show service/account
'Install these commands in the project devShell. An authorized self-SSH key can permit commands on the host through the shared network, outside the filesystem boundary. Credential access is an explicit operator choice, not an ordinary account default.
The NixOS service example runs a project command as the
dedicated aibox account. The VM test imports this same example. For a NixOS
flake with an aibox input, include it in the host configuration:
imports = [
(import "${inputs.aibox}/examples/systemd.nix" {
aibox = inputs.aibox.packages.${pkgs.stdenv.hostPlatform.system}.default;
workspace = "/srv/project";
command = [ "${pkgs.nix}/bin/nix" "develop" "--command" "project-task" ];
hostname = "project-agent";
requiredConfig = [
"/etc/codex/managed_config.toml"
"/etc/codex/requirements.toml"
];
})
];Provision the checkout and its devShell outside the sandbox. The aibox account
must own the workspace. Enable Nix flakes for this command, and provision the
required policy files through environment.etc. Omit requiredConfig for a
command that does not need these files. Start or inspect the service with
systemctl start aibox and journalctl -u aibox, outside the sandbox.
If a service workspace has .envrc, approve it as the service account before
starting the service; a non-interactive launch cannot answer Aibox prompts.
The service supplies explicit command paths and a clean environment, including
both certificate variables. Persistent agent state lives in /var/lib/aibox,
with mode 0700. ProtectSystem=strict restricts persistent filesystem writes,
with writable exceptions for the workspace and the systemd state directory.
PrivateTmp supplies separate temporary directories. Bubblewrap limits access within those
paths to its normal mount policy. The complete host state directory is not bound
into the sandbox.
The tested service uses ProtectHostname=private, ProtectKernelLogs=no, and
ProtectKernelTunables=no. It retains ProtectKernelModules=yes,
PrivateTmp=yes, ProtectClock=yes, ProtectControlGroups=yes, and
ProtectProc=invisible. These settings were tested together with the locked
nixpkgs. The service manages process lifetime; aibox does not manage sessions.
Bubblewrap prints its original startup error. Check the unit's journal and restrictions before changing host-wide settings:
| Error | Check |
|---|---|
Can't set hostname |
ProtectHostname=yes prohibits the syscall; the example uses private |
Can't mount proc |
ProtectKernelLogs and ProtectKernelTunables each caused this failure in the VM test |
No permissions to create a new namespace |
Check RestrictNamespaces, syscall filters, and host user-namespace restrictions |
| Mount permission error | A filter such as SystemCallFilter=~@mount prevents Bubblewrap setup |
| Writable state creation fails | Check ownership, StateDirectory, ReadWritePaths, and any ProtectHome setting |
The same EPERM can have several causes. These messages do not identify one
specific unit setting on every host. A syscall filter can also terminate the
process with SIGSYS before Bubblewrap can print an error. Do not remove the
whole service restriction set to repair a single conflict. See the
systemd execution reference
for the scope of each setting.
aibox preserves SSL_CERT_FILE and NIX_SSL_CERT_FILE. When either is set,
it must name a readable regular file inside the sandbox. An empty value is an
error. Relative paths refer to the workspace. The variables can name different
files. Unset variables leave the client's default certificate selection intact.
For NixOS or a service with a minimal environment, set both variables to the Nix store bundle:
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";The check runs before the command starts and names an invalid variable without printing its value. It adds no mounts and does not parse certificates or prove that a TLS connection will succeed. TLS verification remains the client's job. Other client-specific certificate variables are inherited without this check.
On Ubuntu 24.04+, AppArmor may restrict unprivileged user namespaces. If
aibox -- true fails with a Bubblewrap namespace permission error, add a
targeted profile for Bubblewrap instead of disabling the restriction globally:
cat <<'EOF' | sudo tee /etc/apparmor.d/local-aibox-bwrap >/dev/null
abi <abi/4.0>,
include <tunables/global>
profile local-aibox-bwrap /usr/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/aibox-bwrap>
}
profile local-aibox-bwrap-nix /nix/store/*-bubblewrap-*/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/aibox-bwrap-nix>
}
EOF
sudo apparmor_parser -r /etc/apparmor.d/local-aibox-bwrap
aibox -- trueIf bwrap is installed somewhere else, add another profile stanza for the
absolute path printed by readlink -f "$(command -v bwrap)".
Development setup and test commands live in CONTRIBUTING.md.