v3.3.1
Three fixes keep secrets and internal descriptors out of the wrong places, and
two make mise wrappers and unanswered approval prompts behave as users expect.
Backward compatible: no configuration key, CLI flag, or saved policy changes.
Fixed
-
Secret values no longer appear in diagnostics (#231). A secret enters
the sandbox through anenv_passentry, and three surfaces printed its
value: a TOML parse error quoted the offending source line,--dry-run
printed--setenv NAME VALUE, and plainstatusprintedNAME=VALUE.
Parse errors now mask everyNAME=VALUEvalue while keeping the file, line,
and column;--dry-runprints--setenv NAME <redacted>;statuslists
names only. Phantom placeholders (AIJAIL-PHANTOM-…) stay visible, because
they are not secrets and show that substitution is active. -
The supervision gate no longer leaves a writable descriptor in the jail.
wrap_supervised_commandcleared close-on-exec on the gate's read end so
the supervisor could read it, but the write end kept the default flags from
pipe(2). The supervisor inherited a copy and carried it throughexec
into bwrap and the sandboxed command. The write end is now close-on-exec;
only the read end survives the exec. -
mise command wrappers work inside the jail (#226, #227). With wrappers
configured ([wrappers.<name>]; Omarchy 4.5 declaresclaude,codex, and
grokthat way), mise locks<data dir>/command-wrappers/bin.lockon every
shim and wrapper run, and the read-only data dir refused it, soai-jail claudenever started the agent andpython3ornodefrom mise failed
inside. On Linux ai-jail now binds a jail-owned empty file over that one
path when a read-only map holds it: the data dir stays read-only, the host's
lock is never touched, and Landlock grants write to that file alone. A
command that is a mise wrapper now warns to use--misewhen mise
integration is off, instead of only failing inside mise. Thanks
@diogochaves. -
Unanswered
--prompt-hostsnotifications are closed (#232). GNOME Shell
never expires a critical notification,notify-send --waitstops waiting at
its expire time without closing it, and ai-jail killed the helper at its own
deadline, so an unanswered prompt stayed in the notification list forever
for a connection that had already been refused. At the deadline ai-jail now
sendsSIGINT(libnotify closes the notification on its own connection),
waits briefly, and kills only a stuck helper. Prompts still waiting when
ai-jail exits are interrupted the same way, and the helper's expire time now
outlives ai-jail's deadline so the daemon cannot answer first.
Notes
- New unit tests cover env-value redaction in parse errors, dry-run and status,
the close-on-exec flags on both halves of the supervision gate, the mise lock
mount ordering and its Landlock rule, and the SIGINT-then-close path for
prompt notifications. - README and
docs/SECURITY.mddocument the redacted diagnostics, the
notification lifetime, and the mise lock bind.
See the security model for the diagnostics and egress
boundaries.
Checksums (SHA256)
0be39963f1e3a1930cfa09ceb9390d44db9df85f7ac4a2813d48c7dcc9cf39a1 ai-jail-linux-x86_64.tar.gz
df182e182701682162bb188679b928b348fb23782b85d9a6bf7a2a944e0962a7 ai-jail-macos-aarch64.tar.gz
63814c33e46d711325eac9a98aebce66e31902c3559d2e8837997a01d38c4c93 ai-jail_3.3.1_amd64.deb
Install
brew tap akitaonrails/tap && brew install ai-jail
yay -S ai-jail-bin
cargo install --locked ai-jail
# Debian/Ubuntu: download the .deb below, then
sudo apt install ./ai-jail_*_amd64.deb