0.2.1 (2026-08-03)
A patch bump: nothing about the boundary moved. These are all fixes to what
bento tells you - one report that claimed more than it enforced, and several
failures that said what was wrong without saying what to do about it.
Boundary Reporting
doctorno longer reports the exec block as total: the exec-block layer
deniesexecveand neverexecveat, which the launcher itself needs to reach
the target.bento validatesaid so over a manifest that blocks exec; doctor
claimedenforcedand stopped there. The layer now carries that seam even when
it holds, as a note under doctor's table and aconsequencesfield on the
enforced row ofdoctor --jsonandrun --json. The boundary did not move -
what moved is how much of it the report admits to.
What a Run Tells You
- Exit 127 from a shell now explains itself: unless the manifest passes
PATHthrough, the sandbox uses its own, and a bare command name is only
looked for in those two directories. The shell just says it could not find the
command, so you never learn where it looked.runnow prints that path along
with the three ways out: grant the tool's directory, allowlistPATHin
env:, or call the tool by absolute path. Only shells get this, since other
languages are free to use 127 for whatever they like. - The 127 and missing-
HOMEnotes now check what the sandbox actually got:
they used to check the manifest'senv:list, which can name a variable the
host never set. IfHOMEwas allowlisted but unset, the note stayed quiet
when it should have fired. Both now look at the environment the sandbox was
handed. runstops suggesting--allow-degradedwhen it would fail: on a host
that cannot enforce resource limits, the refusal offered that flag even under
--strict, which rejects the two together. Under strict you now get the one
fix that works - droplimits:from the manifest - and only when limits are
the whole problem, since dropping them does nothing for a degraded filesystem
tier.- Ownership warnings say what to do: when the manifest or its directory
belongs to another uid, which is normal in a container that checks out sources
as one user and runs the job as another, bento reported the problem and left
it there. Root now gets thechown; everyone else is told to move the
manifest somewhere they own, rather than a command that would just fail.
Profiling (bento profile)
- A shell that cannot find a command gets its own warning: the usual advice
is "fix the run and profile again", but that goes nowhere here. Looking up a
bare name is all existence probes, which the observer drops by design, so
nothing gets recorded and the next round comes out the same. That case now
gets its own message with the sandbox PATH and the absolute-path fix. If
something was exec'd, the target does get recorded, so the usual advice stands
and this message stays out of the way. PATHstill stays out of discovery, with the reasoning now written down:
bento cannot pass it without also recording it, and a manifest carryingPATH
resolves bare commands against whoever's shell ran the profile, so it stops
naming the same programs on every machine.
Documentation
examples/embedcovers driving bento from another language over the
subprocess contract.- The README spells out the shared-kernel boundary, and lists crossbuild among
the gates.
Changelog
- a9b5c9f: ci(cache): bump cache action version (@whiskeyjimbo)
- 1874347: ci: add scorecard workflow (@whiskeyjimbo)
- cbd4660: ci: run the release gate on push and pull request (@whiskeyjimbo)
- 4887e00: fix(doctor): disclose the execveat seam on an enforced exec-block (@whiskeyjimbo)
- 7c94bb9: fix(profile): claim the mechanism, not a complete proposal (@whiskeyjimbo)
- 6004cac: fix(profile): correct the remedy a 127 warning offers (@whiskeyjimbo)
- c435af9: fix(profile): name PATH when a shell finds no command (@whiskeyjimbo)
- 976e493: fix(profile): withhold the PATH story when exec was reached (@whiskeyjimbo)
- 9819108: fix(run): key the env miss notes on what reached the box (@whiskeyjimbo)
- 307b577: fix(run): name the sandbox PATH when a shell exits 127 (@whiskeyjimbo)
- 94a995b: fix(run): name the way past a strict limits refusal (@whiskeyjimbo)
- 90bd7c9: fix(run): separate the 127 remedies by failure shape (@whiskeyjimbo)
- d82fdbd: fix(run): withhold the limits remedy when it would not help (@whiskeyjimbo)
- 876cfc5: fix(trust): name the remedy for ownership warnings (@whiskeyjimbo)
- 3b3bd90: fix(trust): trim the ownership hint to its effect (@whiskeyjimbo)