Skip to content

Releases

v0.1.0-alpha.25

v0.1.0-alpha.25 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 12 Nov 08:37

--approve call now works under tui, and compaction has a flag. The
question that tui used to refuse for want of a terminal is drawn in rows under
the transcript, and one key answers it: y for the call, n to refuse it, a
for every later call to that tool. Keys typed before the question was drawn are
discarded, escape or ctrl-c refuses the call and ends the turn, and a screen
that cannot show the question or read keys ends the turn rather than guess. A
pane too short for the whole question shows its first and last rows, and a
resize while one waits is not redrawn until it is answered (#225, #508).
--compact-at <TOKENS> turns compaction on for agent-run and tui; without
it nothing is left out, and it acts only on a --session history (#302).

read and edit stop at 2 MiB, whatever the file reports. read returns
the head of a file up to max_bytes, with the truncation marker, where it used
to load the whole file first and refuse one that was not wholly UTF-8, and it
no longer hangs on /dev/zero; edit refuses a file past 2 MiB and leaves it
untouched (#493). The seccomp denylist gains open_tree_attr, the one call of
the descriptor mount API it did not name. It is defence in depth, as stage 2
holds no CAP_SYS_ADMIN, and a kernel older than 6.15 now answers EPERM
where it answered ENOSYS (#494).

Nothing else breaks. rustix is bumped to 1.1.6, which clears
RUSTSEC-2026-0486. Embedders see new public items in sandbx-screen,
sandbx-tui-input and sandbx-tui-transcript, and one removal from
sandbx-cli.

What's Changed

  • fix(tools): bound what read and edit allocate from one file by @danczw in #504
  • fix(core): deny open_tree_attr in the stage-2 filter by @danczw in #505
  • feat(cli): turn compaction on with --compact-at by @danczw in #506
  • build(deps): bump rustix to 1.1.6 by @danczw in #509
  • feat(cli): ask --approve call on the tui screen by @danczw in #507
  • chore(release): bump to 0.1.0-alpha.25 with its notes by @danczw in #510

Full Changelog: v0.1.0-alpha.24...v0.1.0-alpha.25

v0.1.0-alpha.24

v0.1.0-alpha.24 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Nov 12:37

Nothing breaks for the CLI, and a stopped tui turn is now kept. A turn
stopped on the screen, by a keypress or a hung-up terminal, stores the prompt
and every round it finished under --session, and loses the round it stopped in
whole: a tool call already running still finishes, and the transcript records
neither its effect nor its result. A stop before the first round finishes still
stores nothing (#271). Transcripts stay at version 1, and the turn line gains
the stops interrupted and hung_up, which an older build reads as no stop
recorded. A turn out of rounds under tui now gets the wrap-up round
agent-run sends, one more request with no tool, and --no-wrap-up, parsed and
ignored there until now, is honoured (#232).

A granted path that is not UTF-8 is enforced as named. Its bytes reached the
helper as a U+FFFD spelling, so the kernel refused the ruleset and the command
never ran; they now cross as they are (#495). Embedders: HelperArgs::encode
returns, HelperArgs::decode takes and CommandLine::argv holds OsString,
and a hand-invoked sandbx-helper refuses a non-UTF-8 token anywhere but a
granted path.

The six --help pages with an example print it as written, not joined onto
one line, and wrap at about 85 columns rather than the terminal's width (#498).
A resumed prompt is now a compaction cut point, though no flag turns compaction
on yet (#208, #302), and SECURITY.md names the one #[allow] on the spawn
lint, in the dev-only sandbx-helper-fixture.
Verified 2026-11-09 from the published archives: both checksums matched;
the x86_64 binary reports 0.1.0-alpha.24, is refused /etc/hostname, got a
301 from 1.1.1.1:80 under --allow-network 80 and exit 7 under 443 and
with none, and wrote into an --allow-write grant-\xff that alpha.23 refused,
its sibling still denied. aarch64: checksum only.

What's Changed

  • feat(tui): send the wrap-up round when a turn runs out of rounds by @danczw in #488
  • feat(compact): cut onto a resumed prompt, shedding its results by @danczw in #489
  • feat(tui): keep the rounds a stopped turn finished by @danczw in #490
  • docs: the truth-and-trim pass over the crates' comments by @danczw in #491
  • docs(context): the truth-and-trim pass by @danczw in #492
  • fix(cli): help examples that print as lines, and two false sentences by @danczw in #498
  • test(turn): stop a mid-call turn before its call can finish by @danczw in #499
  • fix(core): carry a granted path to the helper as its own bytes by @danczw in #501
  • test: close #496's three gaps with the fixture crates by @danczw in #502
  • chore(release): bump to 0.1.0-alpha.24 with its notes by @danczw in #503

Full Changelog: v0.1.0-alpha.23...v0.1.0-alpha.24

v0.1.0-alpha.23

v0.1.0-alpha.23 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Nov 10:51

tui ends differently, and a file tool's audit record gains a field. When
a turn ends the screen goes without waiting for a keypress, and the transcript
is printed to stdout in its place, so the answer stays in scrollback (#224). A
terminal stderr's audit trail is still held until then, and a signal in that
window loses it, which SECURITY.md now states; redirected, each record is
written as it happens (#485). allowed, absent and denied carry session=
right after decision, - where there is none, so a parser keyed on field
order sees one more (#429).

A failed run names its cause, and the gate's question spells what it shows.
A provider error prints its source chain, so a DNS failure, a refused
connection and a rejected certificate no longer read alike, and that line is
escaped as the gate's question is (#457). Both spell \ as \\, and U+2028 and
U+2029 as \u{…}, so a command holding a newline no longer reads like one
holding a literal \n, and every backslash in the question shows doubled
(#478). An --allow-dns name that resolved to nothing is looked up once more
after 100ms, so a host rewriting /etc/hosts at run start no longer drops it
for the run (#472).

Transcripts stay at version 1, and resume says why a turn ended. The turn
line records its stop, so a gate abort is no longer resumed as a round limit, a
capped turn the wrap-up answered reads summarised, and a turn recorded before
this release hedges; an older build ignores the field (#226). Leaf modules are
named after their primary item (#449), and deny.toml refuses reqwest outside
sandbx-anthropic and a production edge into a fixture crate (#456, #465).

Verified 2026-11-06 from the published archives: both checksums matched;
the x86_64 binary reports 0.1.0-alpha.23, reads inside its working directory
and is refused /etc/hostname, and curl to 1.1.1.1:80 got a 301 under
--allow-network 80, exit 7 under 443 and with none. aarch64: checksum only.

What's Changed

  • ci: bump Swatinem/rust-cache from 2.9.2 to 2.9.3 in the actions group by @dependabot[bot] in #471
  • test(helper): retry the resolver lookups a hosts rewrite can fail by @danczw in #473
  • refactor: name leaf modules after their primary item by @danczw in #474
  • feat(session): persist why a turn ended on its turn line by @danczw in #476
  • fix(cli): print a failed run's cause chain, not only its message by @danczw in #477
  • refactor(sse): take any error in tokenize, and drop the reqwest edge by @danczw in #479
  • fix(text): spell U+2028, U+2029 and a backslash in the strips by @danczw in #480
  • ci(deny): refuse a production edge into a dev-only fixture crate; cite the per-call gate as built by @danczw in #481
  • fix(core): retry an allowlisted name that resolved to nothing by @danczw in #482
  • feat(core): name the session on every file access record by @danczw in #483
  • fix(tui): give the screen back before the audit trail is released by @danczw in #484
  • fix(tui): hold the audit trail only over a terminal stderr by @danczw in #486
  • chore(release): bump to 0.1.0-alpha.23 with its notes by @danczw in #487

Full Changelog: v0.1.0-alpha.22...v0.1.0-alpha.23

v0.1.0-alpha.22

v0.1.0-alpha.22 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 02 Nov 12:56

One stderr line is gone, and a contributor's unwrap() outside a test no
longer builds.
A sandbx tui run whose cursor cannot be restored at exit no
longer prints ratatui's "Failed to show the cursor": the screen's backend
reports a cursor it cannot show as shown, which is what removed the
catch_unwind in Screen::drop while a hangup still exits 3 (#450).
clippy::unwrap_used is denied workspace-wide, exempting #[test] fns and
#[cfg(test)] modules but not plain helpers in tests/*.rs, which take an
expect with a message; the sandbox CI job now runs clippy with
sandbox-integration, so a gated target is checked too (#453).

Eight crates' public surface moved, and none is published. Every crate is
publish = false, so no embedder sees the baselines change: a bool parameter
some call site passed bare became an enum (#458, #459), a returned tuple became
a struct with named fields (#462, #463), and ProviderError::Transport boxes
its source, so naming the provider seam no longer links reqwest or hyper
(#455). A poisoned mutex is never reached into: the seven sites that recovered
one now expect, poison being unreachable at each (#453).

The on-ramp is rustdoc, and context/ holds only what spans crates. Eight
single-crate docs moved into the items they describe, guide-repo-map.md is
retired, and context/README.md indexes the thirteen that remain in reading
order (#336). SECURITY.md makes no new claim and drops none; it now counts
three cases that keep the by-path shape, where it said four of a list of
three. This is the last phase of the Google style migration (#328).

Verified 2026-11-02, after the fact, from the published archives: both checksums matched, and
the x86_64-unknown-linux-musl binary reports 0.1.0-alpha.22, reads a file inside its working
directory, and is refused /etc/hostname outside it. The network denial was not checked against a
control this time: curl to a host name exits 6 with and without --allow-network 80, the name not
resolving with no DNS grant, so the check reached the resolver and not the port rule. The aarch64
archive was checksum-verified only, this host being x86_64.

What's Changed

  • fix(screen): quiet the backend so Terminal::drop cannot panic by @danczw in #450
  • build: declare ratatui's pin once, in the workspace by @danczw in #451
  • refactor: concrete parameter types and convention names (#335) by @danczw in #452
  • fix: never reach inside a poisoned mutex; check the panic policy (#335) by @danczw in #453
  • refactor(provider): box Transport's source (#335) by @danczw in #455
  • refactor(core): enums for bool parameters (#335) by @danczw in #458
  • refactor: enums for bool parameters outside core (#335) by @danczw in #459
  • fix(cli): rename locals CodeQL reads as credentials by @danczw in #461
  • refactor(core): named fields for tuples (#335) by @danczw in #462
  • refactor: named fields for tuples outside core (#335) by @danczw in #463
  • docs: move the turn and tools guides into rustdoc (#336) by @danczw in #466
  • docs: move the tui, logging and process guides into rustdoc (#336) by @danczw in #467
  • docs: retire guide-repo-map.md for context/README.md (#336) by @danczw in #468
  • chore(release): bump to 0.1.0-alpha.22 with its notes by @danczw in #470

Full Changelog: v0.1.0-alpha.21...v0.1.0-alpha.22

v0.1.0-alpha.21

v0.1.0-alpha.21 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Oct 20:58

Nothing behavioural changed, and a contributor's first commit after this
will fail until one toolchain is installed.
Three of the four rustfmt
options the Google style guide names are nightly-only, so the fmt gate moved
to nightly-2026-09-11 and both .githooks/pre-commit and CI need it:
rustup toolchain install nightly-2026-09-11 --component rustfmt. Both
halves also fail on a Warning: line, because rustfmt skips an option it
cannot set and still exits 0 — left on stable, cargo fmt --check reported
green having enforced none of them (#334).

The workspace follows the guide's import rules with no departure left on
the books.
1,162 inline std:: paths became module-qualified calls and
unqualified type names, all 49 glob imports across 45 files took explicit
item lists, and the exception that excused use super::* in a sibling test
module is deleted rather than re-recorded. 27 lines still spell a path out,
each for a reason the PR that left it gives. Phase 5 of #328; phases 6 and 7
remain.

Nothing widened, and git blame needs one line of local setup. All 27
public-api.txt baselines are byte-identical to alpha.20, and the visibility
sweep rewrote 50 pub(super) items to pub(crate) only where the module is
a direct child of its crate root and the two spellings name the same set.
Eight of the phase's twenty commits are listed in .git-blame-ignore-revs,
so blame skips them for the commit that last changed the code; GitHub honours
it automatically, locally it is git config blame.ignoreRevsFile .git-blame-ignore-revs.

Verified 2026-10-29, after the fact, from the published archives: both checksums matched, and
the x86_64-unknown-linux-musl binary reports 0.1.0-alpha.21, reads a file inside a granted
working directory, and is refused /etc/hostname outside it. The network denial was checked
against a control rather than asserted: with no grant curl exits 7 having failed to connect in
0 ms, with --allow-network 80 the same run writes 167 bytes and reports HTTP 301, and
unsandboxed it reports 301 too — so the refusal is the policy and not this host. The aarch64
archive was checksum-verified only, this host being x86_64.

What's Changed

  • ci: enforce the style guide's rustfmt options on the pinned nightly by @danczw in #441
  • refactor: one spelling for crate-internal visibility, where it is one by @danczw in #442
  • refactor(core): import types, qualify functions, name every glob by @danczw in #443
  • refactor(cli): import types, qualify functions, name every glob by @danczw in #444
  • refactor(anthropic,tools,helper): import types, qualify functions, name the last globs by @danczw in #445
  • refactor: the last globs, the last inline paths, and the glob exception goes by @danczw in #447
  • chore(release): bump to 0.1.0-alpha.21 with its notes by @danczw in #448

Full Changelog: v0.1.0-alpha.20...v0.1.0-alpha.21

v0.1.0-alpha.20

v0.1.0-alpha.20 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Oct 14:31

Every .rs file in a crate now sits directly under its src/, so a link or a
path citation naming one re-points.
57 files left command/, error/,
policy/, the helper/ tree, agent/, anthropic/, wire/ and tools/; a
file's name is its module path joined with _, so helper_ruleset_rights.rs is
helper::ruleset::rights and context/guide-repo-map.md is the index. No
code edit follows.
A #[path] on each mod line whose file moved keeps the
module tree, so no use, no crate:: link and no super::-relative path
changed — which also kept it out of other crates' API baselines, those spelling
an item by its defining path, private modules included.
docs/release-notes/*.md keep their old paths: a shipped note describes the
tree at its tag.

A module's unit tests are a sibling file rather than an inline block, and two
test fixtures are crates.
54 *_tests.rs siblings arrived, declared
#[cfg(test)] #[path = "…"] mod tests;, and a lib.rs names its sibling after
the crate where a main.rs takes main_tests.rs. sandbx-helper-fixture and
sandbx-turn-fixture take the workspace to 28 crates, both dev-only and in
nobody's build, which deny.toml now states and CI checks against the real
graph. The helper one takes the binary's path as an argument: CARGO_BIN_EXE_*
is set only for a target of the package declaring that bin, so the env! the
module used could not survive the move. Seven integration-test roots stopped
declaring mod support;.

Nothing widened. Every public-api.txt that existed at alpha.19 is
byte-identical; the only baselines added are the two fixtures' own. That is a
claim about reachability and not about tidiness — a baseline records only pub
items reachable from a crate root, so each crate's full visibility multiset was
compared across the move as well, and the three pub(super) mod tests that a
neighbouring module reads stayed pub(super) rather than widening to pub.
Phase 4 of #328 (#333); phases 5 to 7 remain.

What's Changed

  • refactor(core): flatten src/ and move test modules to siblings by @danczw in #431
  • refactor(cli): flatten src/ and move test modules to siblings by @danczw in #432
  • refactor(anthropic): flatten src/ and move the test module to a sibling by @danczw in #433
  • refactor(tools): flatten src/ and move the test module to a sibling by @danczw in #434
  • refactor: move the last inline test modules to siblings by @danczw in #436
  • refactor(helper): promote the enforcement fixture to a crate by @danczw in #437
  • refactor(turn): promote the turn-loop fixture to a crate by @danczw in #438
  • chore(release): bump to 0.1.0-alpha.20 with its notes by @danczw in #440

Full Changelog: v0.1.0-alpha.19...v0.1.0-alpha.20

v0.1.0-alpha.19

v0.1.0-alpha.19 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Oct 18:43

Every audit record gained fields, and a reader pairing a spawn with its exit
by position has to use them instead.
spawned, exited, failed and
degraded each carry spawn=, a per-process nonce ahead of a counter, and
session=; spawned also carries args=, capped at 512 rendered characters
and last on the line (#249). A guard that should name its session on those
records is built with FsGuard::for_session or ExecutionContext::for_session,
there being no setter: the record is written while the guard is built, so one
told afterwards has already reported without a session. FsGuard::read_dir
returns an owned Listing rather than std::fs::ReadDir, and new keeps its
signature on both types (#230).

On Linux 5.6 and above, a filesystem access sandbx makes on its own behalf is
resolved beneath a descriptor it holds on the granted root
, so a directory
swapped in after the policy check is refused rather than reached; below 5.6, and
for a grant naming a file, the older two-syscall window remains and
SECURITY.md says so (#230). SECURITY.md also now defines the three severity
rungs a security issue takes, a label on a weakness rather than a change to the
sandbox (#426), and the six refusals behind its default-root claim are published
rather than private (#413).

The workspace is 26 crates where alpha.18 had seven, and four names are
gone:
sandbx-agent, sandbx-session and sandbx-tui dissolved into their
parts and sandbx-providers is now sandbx-anthropic, so a dependency or link
naming any of the four re-points — context/guide-repo-map.md is the index
(#332). Unlike phase 2, visibility did widen: 27 baselines moved 1,032 lines in
and 967 out while 55 items became newly reachable, 34 of them not derive or
auto-trait impls and each now crossing a crate line. sandbx-core's own
baseline was untouched by all 22 extractions; its 70 added lines are the two
changes above. CI now fails when deny.toml's allowlist disagrees with the real
graph, the mistyped block name that ungated a crate at exit 0 included (#416).

Verified 2026-10-28, after the fact, from the published archives: both checksums matched, and
the x86_64-unknown-linux-musl binary reports 0.1.0-alpha.19, denies /etc/hostname from a
granted working directory, and writes the trail this release is about — one spawn= shared by
spawned and exited, session="-" where no session exists, and args= last on the line. The
aarch64 archive was checksum-verified only, this host being x86_64.

What's Changed

  • refactor(tui): give the pty fixture a crate both sides can reach by @danczw in #390
  • refactor(agent): move the prompt measurement to the compaction side by @danczw in #391
  • refactor(session): the error carries an id's spelling, not the type by @danczw in #392
  • refactor(core): extract the helper binary and the suite that drives it by @danczw in #393
  • refactor(cli): extract the audit subscriber into sandbx-logging by @danczw in #396
  • refactor(session): move the stored shapes to a crate of their own by @danczw in #395
  • refactor(providers): extract the terminal-text denylist into sandbx-text by @danczw in #397
  • docs: the cli chapter stops counting its dependencies by @danczw in #398
  • refactor(agent): extract the approval vocabulary into sandbx-approval by @danczw in #399
  • refactor(cli): extract the provider key and its file into sandbx-auth by @danczw in #400
  • refactor(tui): extract the transcript fold and its layout into sandbx-tui-transcript by @danczw in #401
  • docs: the guide tags five rules chrome, and sandbx adopts two by @danczw in #403
  • ci: api-diff sees a baseline that was never added by @danczw in #404
  • refactor(session): extract the session error into a crate of its own by @danczw in #405
  • refactor(tui): extract the screen and its measurement into a crate by @danczw in #407
  • refactor(agent): extract the cut planner into sandbx-compact by @danczw in #406
  • refactor(tui): extract the key reader and retire the crate it left by @danczw in #408
  • refactor(session): sandbx-session-bridge takes the stored/request seam by @danczw in #409
  • refactor(grants): sandbx-grants takes the flags and the default policy by @danczw in #410
  • refactor(cli): sandbx-cli re-exports SessionChoice by @danczw in #412
  • docs(grants): publish the six refusals SECURITY.md cites by @danczw in #413
  • refactor(session): extract sandbx-session-id by @danczw in #414
  • refactor(turn): extract sandbx-turn, retiring sandbx-agent by @danczw in #415
  • refactor(session): extract sandbx-session-paths by @danczw in #417
  • ci(deny): read the dependency allowlist back against the graph by @danczw in #416
  • refactor(session): extract sandbx-session-store, retiring sandbx-session by @danczw in #418
  • refactor(event): extract sandbx-event, and the screen sheds the client by @danczw in #419
  • docs: remove the onboarding read-through, repointing its citations by @danczw in #420
  • refactor(prompt): extract sandbx-prompt, and two crates shed reqwest by @danczw in #422
  • refactor(provider): extract sandbx-provider by @danczw in #421
  • chore: gate the widenings the public-API baseline cannot see by @danczw in #423
  • refactor(sse): extract sandbx-sse by @danczw in #424
  • refactor(anthropic): rename sandbx-providers to sandbx-anthropic by @danczw in #425
  • docs(security): define the three severity rungs a security issue takes by @danczw in #426
  • feat(core): resolve FsGuard accesses beneath a held root descriptor by @danczw in #427
  • Name a run on every record of it: a correlation id, the session, and the command's args by @danczw in #428
  • chore(release): bump to 0.1.0-alpha.19 with its notes by @danczw in #430

Full Changelog: v0.1.0-alpha.18...v0.1.0-alpha.19

v0.1.0-alpha.18

v0.1.0-alpha.18 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 19:47

Nothing a command can reach changes in this release: no flag, no policy, no
enforcement path.
It is phase 2 of moving the workspace onto the Google Rust
style guide (#328, tracked as #331), nineteen pull requests of pure module moves
so that a module holding submodules now holds only mod lines and re-exports.
SECURITY.md's claims, non-claims and known weaknesses are unchanged, and no
item's visibility widened anywhere in the phase — five of the moves narrowed
what a sibling module can reach.

Where the code lives moved, but nothing was deleted. Seventeen parent
modules became re-export shims, so a link to core/src/policy.rs or
cli/src/agent.rs still resolves and lands on ten lines of re-exports instead
of the code, which is in policy/sandbox.rs and agent/run.rs. Twenty-one
files are new. Only three links actually broke, all test files hoisted to
#[path] siblings: compact/tests.rs, agent/gate/tests.rs and
agent/prompt/tests.rs are now compact_tests.rs, gate_tests.rs and
prompt_tests.rs. context/guide-repo-map.md is the index to where everything
went, and each crate's onboarding chapter was re-pointed with it.

Three public-API baselines moved without a widening, which is worth knowing
before you read them as one.
cargo-public-api records a type's declaration
path, so moving a type one module deeper re-renders it in the baseline of every
crate whose public signatures name it — eleven lines across sandbx-agent,
sandbx-cli and sandbx-tools, for instance policy::SandboxPolicy becoming
policy::sandbox::SandboxPolicy. No item was added, removed or made more
visible; sandbx-core's and sandbx-providers' own baselines are untouched.
The consequence for anyone extending this work: grepping the moved crate's own
baseline does not detect it, so run .github/scripts/public-api.sh and commit
the result rather than reasoning about whether a move is visible.

What's Changed

  • docs(ci): api-diff is a required check by @danczw in #343
  • refactor: hoist three test directories to #[path] siblings by @danczw in #369
  • refactor(providers): move the Anthropic client out of its parent module by @danczw in #370
  • refactor(session): move the store and session out of their parent module by @danczw in #371
  • refactor(agent): move the turn loop out of its parent module by @danczw in #372
  • refactor(tools): move the built-in enum and the shared helpers out of lib.rs by @danczw in #373
  • refactor(cli): move the clap surface out of lib.rs by @danczw in #374
  • refactor(cli): move the key resolution out of its parent module by @danczw in #375
  • refactor(cli): move the grant flags out of their parent module by @danczw in #376
  • refactor(cli): split the error module one file per error type by @danczw in #377
  • refactor(cli): move the agent-run sequence out of its parent module by @danczw in #378
  • refactor(core): move the ruleset request out of its parent module by @danczw in #379
  • refactor(providers): move the wire fixtures out of their parent module by @danczw in #380
  • refactor(core): move the BPF interpreter out of its parent module by @danczw in #381
  • refactor(core): move the filter builders out of their parent module by @danczw in #382
  • refactor(core): move the policy types out of their parent module by @danczw in #383
  • refactor(core): move the two stages out of their parent module by @danczw in #384
  • refactor(core): move the supervision sequence out of its parent module by @danczw in #385
  • refactor(core): move the refusal set out of its parent module by @danczw in #386
  • chore(release): bump to 0.1.0-alpha.18 with its notes by @danczw in #389

Full Changelog: v0.1.0-alpha.17...v0.1.0-alpha.18

v0.1.0-alpha.17

v0.1.0-alpha.17 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Oct 17:33

Nothing a command can reach changes in this release: no flag, no policy, no
enforcement path.
tokio moves to 1.53.3 for RUSTSEC-2026-0386. Everything else is
the first phase of moving the workspace onto the Google Rust style guide (#328),
which is groundwork for splitting sandbx-core later and carries no behavioural
change of its own. SECURITY.md's claims, non-claims and known weaknesses are
unchanged. The published x86_64 archive was exercised: checksum verified,
static-pie, --version matching the tag, and a read allowed inside the granted
root but refused outside it — on a file plainly readable without the sandbox, so the
refusal is the sandbox's and not the filesystem's. Network resolution is denied too.

One context/ file was renamed and three were deleted, so an external link into
any of them is now dead. decision-axis-table.md is decision-policy.md, matching
the title it already had; every anchor is unchanged and SECURITY.md's five links
to it were re-pointed. guide-naming.md, guide-code-comments.md and
guide-module-layout.md are gone, replaced by the Google guide plus
guide-testing.md for what that guide leaves to the project — what a test has to
assert, where it lives, and a fifty-character ceiling on its name. The departures
sandbx takes from the guide are recorded in decision-google-style.md, including
the one still undecided, and CLAUDE.md now states the comment pass outright
rather than citing a guide for it.

Two CI gates are new, and either can fail a pull request that changes no
behaviour.
cargo-deny now checks which workspace crate may depend on which, so an
unrecorded edge between crates fails audit. And every crate has a committed
public-API baseline at crates/<crate>/public-api.txt, which api-diff regenerates
and fails on when stale — so a pub(crate) widened to pub arrives as an added
line in the pull request's own diff instead of a line in a log nobody opened.
Regenerate with .github/scripts/public-api.sh, which needs the pinned nightly it
names: rustdoc JSON is nightly-only, while the build and the tests stay on stable.

What's Changed

  • docs(release-notes): scope the verification note; bump tokio for RUSTSEC-2026-0386 by @danczw in #337
  • docs: record the Google style decisions by @danczw in #338
  • docs: replace the three budget guides with guide-testing.md by @danczw in #339
  • docs: rename decision-axis-table.md to decision-policy.md by @danczw in #340
  • ci: public-API baseline gate, dependency allowlist, and the alpha.17 cut by @danczw in #342

Full Changelog: v0.1.0-alpha.16...v0.1.0-alpha.17

v0.1.0-alpha.16

v0.1.0-alpha.16 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Oct 12:06

A sandboxed command could outlive its timeout and a killed supervisor, and the namespace's init
is now sandbx's own process
(#272). The command used to be PID 1, the process the kill chain
ends, and it could shed both links: setsid left the group sandbx kills, and an exec from a
second thread dropped the per-thread parent death signal with no prctl at all. Measured: such a
command outlived a 1s timeout and a SIGKILLed supervisor alike, while sandbx reported it killed.
Stage 2 now spawns the command and stays as a single-threaded, non-dumpable init that never execs,
so neither link can be taken from it. What a command sees changes: it is PID 2 with parent 1,
and an unhandled SIGTERM aimed at it now ends it instead of being ignored. A signalled command
still exits 128 + n, but a library caller's ExitStatus::signal() is no longer set. One survivor
remains possible, on an untimed call whose kernel does not deliver the init's parent death signal,
as #272 measured on WSL2; SECURITY.md says so.

Model output can no longer hide or erase sandbx's own lines. Under --show-thinking a
reasoning delta reached stderr verbatim, so \x1b[8m concealed every sandbx: line after it and
\x1b[A\x1b[2K could erase the gate's verdict; control and invisible characters are now replaced
with U+FFFD (#239). Six combining overlays that drew the TUI's gutter mark on a space, U+20D2 among
them, are replaced too (#280). A tui turn that lost its terminal no longer forks tput, found
through PATH, from sandbx's unsandboxed process (#269); crossterm's key reader still can when a
resize races a hangup, which SECURITY.md now states.

A resumed session reads the transcript in the directory it vetted (#317): every open is an
openat on the vetted root's descriptor, closing a rename window between the check and the read.
Its owner must now be both the real and the effective uid (#318), so a setuid install refuses where
it used to replay files its caller chose. rustls moves to 0.23.46 and rustls-webpki and
webpki-root-certs with it, for RUSTSEC-2026-0346 to -0349, -0382 and -0383. sandbx-providers
loses MockProvider and its mock feature (#305), ProviderError's retry classification (#306)
and AnthropicClient::from_env with resolve_api_key (#301); a key now enters only through
AnthropicClient::new. The CLI's flags, key sources and exit codes are unchanged.

Verified from the published x86_64-unknown-linux-musl archive: checksum matched, a read inside
the working directory succeeded and /etc/hostname was denied, the command ran as PID 2 with parent
1, and a setsid child did not outlive a --timeout 1 kill — no survivor left behind. On WSL2, so
the untimed parent-death case #272 names is not covered.

What's Changed

  • ci: bump the actions group with 2 updates by @dependabot[bot] in #282
  • build: bump rustls, rustls-webpki and webpki-root-certs for six advisories by @danczw in #284
  • fix(tui): replace combining vertical overlays that draw the gutter by @danczw in #286
  • fix(cli): strip reasoning deltas and reset before the turn's own lines by @danczw in #288
  • fix(tui): measure the window ourselves, so no draw forks tput by @danczw in #289
  • refactor: shorter non-test code, behaviour unchanged by @danczw in #290
  • docs: cite the open issues that now track #234 and #235 by @danczw in #308
  • refactor(core): decide an error's label and refusal in one match by @danczw in #309
  • refactor(core): make with_helper_dispatch the only helper-mode entry by @danczw in #310
  • refactor(cli): vet a grant once and judge the vetted path by @danczw in #311
  • refactor: build the wrap-up turn from a cloned Turn, drop wrapup::Next by @danczw in #312
  • refactor(providers): delete MockProvider and the mock feature by @danczw in #313
  • refactor(providers): drop the retry classification nothing reads by @danczw in #314
  • refactor(providers): a framed SSE event is just its data by @danczw in #315
  • refactor: one owner check, one read-open, no from_env in the session store by @danczw in #316
  • Session store: open transcripts through the vetted root, check both uids; tools: drop two dead wrappers by @danczw in #319
  • TUI: one harness-line kind, Screen owns the transcript; credentials: one nonblank rule in the CLI by @danczw in #320
  • fix(core): keep sandbx's own process as the namespace's init by @danczw in #321
  • chore: 0.1.0-alpha.16 by @danczw in #322
  • docs: merge the credentials decision docs into one by @danczw in #324
  • docs: merge the helper's decision docs into one by @danczw in #325
  • docs: merge the network decision docs into one by @danczw in #326
  • docs: merge the policy and grant decision docs into one by @danczw in #327

Full Changelog: v0.1.0-alpha.15...v0.1.0-alpha.16