github akitaonrails/ai-jail v3.3.1

one hour ago

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 an env_pass entry, and three surfaces printed its
    value: a TOML parse error quoted the offending source line, --dry-run
    printed --setenv NAME VALUE, and plain status printed NAME=VALUE.
    Parse errors now mask every NAME=VALUE value while keeping the file, line,
    and column; --dry-run prints --setenv NAME <redacted>; status lists
    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_command cleared 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 through exec
    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 declares claude, codex, and
    grok that way), mise locks <data dir>/command-wrappers/bin.lock on every
    shim and wrapper run, and the read-only data dir refused it, so ai-jail claude never started the agent and python3 or node from 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 --mise when mise
    integration is off, instead of only failing inside mise. Thanks
    @diogochaves.

  • Unanswered --prompt-hosts notifications are closed (#232). GNOME Shell
    never expires a critical notification, notify-send --wait stops 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
    sends SIGINT (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.md document 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

Don't miss a new ai-jail release

NewReleases is sending notifications on new releases.