Releases: whiskeyjimbo/bento
Release list
v0.2.1
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)
v0.2.0
0.2.0 (2026-08-02)
A minor bump because bento run --json changed shape - see the breaking section
at the end. Pre-1.0 that is what a breaking change earns; see
SECURITY.md.
Boundary Hardening
- Off-Linux is a refusal, not a crash: every command that would enforce
something refuses on a non-Linux host before it does any work.version,
helpand shell completion still answer, since they enforce nothing and a
build identifier is the first thing a bug report needs. Bento's guarantees are kernel
features that only exist on Linux, so a build that ran anywhere else enforced
nothing while looking like it did. The refusal stays inside the--json
envelope, so a machine consumer reads it as a refusal rather than a crash. validatepredicts the grants the run refuses: a write grant naming an
existing file, and a read or write grant whose symlinks loop, aborted the run
at sandbox setup whilebento validatesaid nothing. Validate now reports both
in the same words the run refuses them in, andvalidate --strictfails on
them, so a CI gate and the run agree on what is grantable.superviseno longer prompts for the walk down to the script: the example
supervisor asked about each directory on the path to the script it was told to
run. The boundary moved tighter - a routine "yes" to one of those prompts
granted a recursive read several levels above anything the script named
(~/srcand up).- Grant and shield paths are cleaned before they are compared: a
.or..
segment on either side of a containment test - a grant, a denylist query, a
record awaiting judgement - was compared literally, so a path that resolved
inside a shield could read as outside it. Both sides are cleaned now. - An unexpanded
~is refused at enforcement: a~that reached the
enforcer without being expanded was treated as a literal directory name.
It is refused instead, and a nil policy is answered rather than assumed
expanded. - A grant that lifts a shield is raised before the stamp:
validateand
approvenow resolve grants against the shields the wayrundoes, refuse
the onesrunrefuses, and call out an exact-shield opt-in - including on the
already-approved shortcut, which previously returned early. A manifest stamped
by an earlier bento no longer reads as approved for permissions the run
refuses. - A run whose in-sandbox setup never attested is refused:
enforcerefuses
rather than reporting an outcome for a stage that never said it got there, the
backend refuses aNew()that never dispatched, and an undispatched re-exec
stage panics rather than continuing as the parent. approverefuses a non-terminal stdin: a stamp nobody read is now
something a caller asks for with--yes, not something the absence of a
terminal decides. The example supervisor'srunrefuses the same way.- The shared-write warning proves group membership without NSS: the check
for "somebody else can write this" read routes thatLD_PRELOADcould put
back under the caller's control, and warned on a private group holding only
its owner. It now resolves members through the same pure-Go path the shields
anchor on, and a member passwd cannot resolve is not taken as proof. profileproposes less: unix socket grants are withheld entirely, the
entrypoint's ancestor chain is no longer proposed as a read grant, and a host
that cannot sandbox is refused before anything is observed rather than after.- A shebang's interpreter arguments reach the exec policy: an interpreter
line carrying arguments (#!/usr/bin/env -S python -u) had them dropped, so
the policy attested an exec the run did not make. - The example supervisor's trial is read-only: the trial run no longer
writes, and no longer trims/tmpworkspaces it did not create.
approve is a review step, not a stamp
approve printed four numbered steps whose last command printed one line, which
made typing it the path of least resistance over reading the policy. It now
prints the permissions it is about to stamp, calls out the entries that deserve a
second look, and asks before writing - --yes for scripts and CI.
- It resolves the entrypoint the way
rundoes, so the reviewer sees what will
actually execute. - It says when nobody reviewed and when the stamped policy has drifted, with the
drift notice after the callouts rather than buried above them; an unattested
run is worded as unknown rather than as unrun, and a stale stamp says why it
has no diff to show. - Egress a profiling run reached and the guard refused is recorded on the
manifest asblocked-hostsand called out here. The record is provenance, not
permission: it does not shift the approval fingerprint and it does not widen
anything.
Profiling (bento profile)
--json: the draft, the notes and the refusals come out as one document,
with probed-versus-resolved carried through the envelope so a consumer can tell
a path the target opened from one bento resolved for it.- Manifests are written in the relocatable form: a path under the manifest's
own directory is emitted./-relative and one under your home~/-prefixed,
so the result can be committed and used by someone else. A path under neither
stays absolute and names this machine. /tmpgrants are disclosed as the target's request: a proposed grant under
/tmpreaches the draft because the name exists on this host, which is the
only way a real workspace there can be told from the sandbox's own scratch - so
a target opening guessed names can steer what lands in the proposal. It is now
named as such rather than presented as an observation.- The interpreter comes from the script's shebang, not its extension, with
the interpreter the run actually used merged back into the draft and its
argument cost stated. A whole-workdir grant is called out, a granted write
directory is created the wayruncreates it, and a merge into an existing
manifest says what it changed. - The run is honest about its own shape: the target's stdout passes through
rather than being swallowed, every converge round runs under the base
invocation, and a run that ended before the rounds converged says so instead of
presenting the last draft as settled.
What a Run Tells You
The boundary did not move for any of these; what a user can see about it did.
- Denials name the manifest field that caused them:
bento runprints a
legend mapping a denial's errno to the grant that would have permitted it -
"Read-only file system" towrite:, "Operation not permitted" toexec:.
This is new output on runs that previously said nothing, including runs that
exit 0. - A degraded refusal leads with its remedy: a refusal on a host that cannot
fully enforce a core layer opened with what is broken and the command that
fixes it, then buried it under a tier-consequence enumeration identical on
every degraded host. The run refusal now carries the diagnosis and points at
bento doctorfor the rest;doctorstill prints every fact it printed
before.enforce.LayerStatusgained aConsequencesfield and a
Disclosure()method for embedders that describe a layer in full, plus
Report.AddStatus/SetStatusfor forwarding a status whole. No disclosure was
dropped, only relocated. - A userns refusal in a container names the flags that lift it: the probe's
reason now spells out thedocker run --security-optflags rather than leaving
the reader to find them in the README. - A file-shaped write grant says what it will actually do: a
write:entry
that does not exist yet and is spelled like a file (./out/log.txt) becomes a
directory under that name, so the script's own write to it fails with "is a
directory".validateandrunboth say so before the run rather than leaving
the reader to infer it from the failure. - A death by SIGSYS names the filter that caused it: the filters bento
installs kill only on a foreign-architecture syscall, and a withheld permission
is refused with EPERM instead - so the run says the signal is most likely that
guard rather than a grant the reader can add. - Exit 126 under a blocked exec is explained, with the hint worded for a
manifest that omitted theexecline and gated on the block actually landing.
A non-zero exit points atbento profile. HOMEinside the sandbox is stated up front: it is not passed through, so
~expands somewhere else and a script resolving~itself misses grants
matched against host paths. The note repeats when a run trips it.- A rule covering an egress the guard refused is noted by
validateand
run- the destination resolved to loopback, private space or cloud metadata,
and this run refuses it the same way rather than the rule widening it. - A killed run says it was killed, without guessing who did it, naming the
signal, and blaming limits only for a cgroup kill. - A usage mistake answers in the
--jsonenvelope: a bad flag, an unknown
subcommand and a size-spelling error come back as a refusal with usage and a
hint rather than as bare cobra output, with every spelling of--jsonread
during the scan and unknown refusal shapes rejected at every depth. - A manifest reports every bad field in one pass instead of one per parse.
run --jsoncarries a missing read grant, denied egress and a flag
conflict, and discloses the alias scan a degraded tier skips.- A limits refusal names the way past it: it said the limits could not be
enforced without saying whether to waive the tier or drop thelimits:block.
enforce.Refusalgained aWaivablefield ...
v0.1.1
0.1.1 (2026-07-29)
Boundary Hardening
- Refused network stdio: The launcher now refuses execution if
stdin,stdout, orstderrare network sockets, regardless of manifest egress grants. This closes socket-inheritance bypasses where a sandboxed process could communicate over pre-opened network file descriptors without passing through the host proxy. - Refused
write: /: Manifest validation now rejects a write grant of the root directory (write: /) outright. - Fail closed on non-amd64 architectures: The seccomp filter now explicitly refuses execution on non-amd64 architectures rather than quietly skipping the seccomp architecture guard.
- Fail closed on proxy & NAT64 failure: The HTTP CONNECT egress proxy fails closed (refuses connection) when NAT64 prefix discovery cannot answer, when RFC 6052 address layout is invalid, or when NAT64 translation fails to derive a target.
- Fail closed on unwalkable credential paths: Credential alias resolution and home directory traversal fail closed when directory walking cannot complete due to permission errors or unreadable paths.
- Expanded default shields: Added
.claude.json.backupto default credential denylist shields and ensured relocatedXDG_RUNTIME_DIRpaths are shielded across all user anchors. - Normalized proxy hostnames: Trailing DNS root dots (e.g.
example.com.) in HTTP CONNECT targets are stripped before matching against manifest host rules.
Boundary & Information Disclosure Fixes
- Proxy refusal privacy: Proxy refusal bodies no longer disclose resolved destination IP addresses to the sandboxed caller.
- Embedder observer protection: Embedder observer panic handling prevents proxy panics from disrupting host enforcement.
- Standardized guard refusal: Guard-blocked connection attempts return standard dial failure responses rather than disclosing internal gate errors.
Profiling & Observability (bento profile)
- Entry-stop syscall decoding: Syscall pathnames and
execveevents are now decoded at entry stops rather than exit stops, preventing missed system calls (such asexecveat) and eliminating false phantom drop counts. - Thread probe accounting: Fixed probe leak and drop accounting during thread termination,
execvethread retirement, and root exit. - Credential alias scanning in profiling:
bento profilenow executes credential alias scanning to detect foreign-home credential stores during profiling runs.
Operator Surface & Platform Refinements
- Surfaced guard blocks: Operator and supervisor summaries now report destinations blocked by network guards.
- Landlock degraded tier: Added
resolve_unixhandling to Landlock's degraded tier and stopped requesting ungranted Landlock rights. - Shield mount cleanup: Shield mount points created during a sandbox run are explicitly reclaimed upon exit.
Changelog
- add0fcb: build: run the example modules under make check (@whiskeyjimbo)
- a324595: feat(credhunt): hunt a real home for unshielded credential shapes (@whiskeyjimbo)
- f001a42: feat(landlock): handle resolve_unix in the degraded tier (@whiskeyjimbo)
- a3ef0a6: feat(proxy): surface guard-blocked destinations to the operator (@whiskeyjimbo)
- a7f0683: fix(alias): refuse a credential walk that could not finish (@whiskeyjimbo)
- b2e24d2: fix(alias): skip an anchor with no file behind it (@whiskeyjimbo)
- 815a0dd: fix(cli): quote host paths in the resolved-target lines (@whiskeyjimbo)
- e08ab76: fix(cli): quote the grant name and the shield anchors too (@whiskeyjimbo)
- e4d91a7: fix(credhunt): refuse an unwalkable home instead of reporting clean (@whiskeyjimbo)
- 37bb396: fix(denylist): key the file-relocation store check on every anchor (@whiskeyjimbo)
- e26a460: fix(denylist): shield a relocated XDG_RUNTIME_DIR (@whiskeyjimbo)
- d6d3fd6: fix(denylist): shield the suffix-less .claude.json.backup (@whiskeyjimbo)
- 2bc086a: fix(denylist-audit): fail when a scope keyword stops matching (@whiskeyjimbo)
- 9e535c6: fix(embed): render the shielded grant's bound store (@whiskeyjimbo)
- a0d98d2: fix(landlock): stop handling rights no rule grants (@whiskeyjimbo)
- 529b14e: fix(launcher): claim a layer only for a run that reached its target (@whiskeyjimbo)
- cc27ba4: fix(launcher): examine every stdio descriptor before waiving one (@whiskeyjimbo)
- 67e5594: fix(launcher): keep the unreached-target record from lying either way (@whiskeyjimbo)
- 0cedbe9: fix(launcher): make the bridge's death report reachable (@whiskeyjimbo)
- 3a17fca: fix(launcher): refuse network stdio whatever the egress grant (@whiskeyjimbo)
- b8953cc: fix(launcher): report an in-sandbox bridge that stopped serving (@whiskeyjimbo)
- a623b1f: fix(launcher): warn only when a socket is really on stdio (@whiskeyjimbo)
- 206a4e6: fix(linux): bound the wait on the bridge liveness pipe (@whiskeyjimbo)
- 9ebeedf: fix(linux): claim only the shield artifacts the run creates (@whiskeyjimbo)
- 0aeb51f: fix(linux): reclaim the file shield mount points a run creates (@whiskeyjimbo)
- a0878e9: fix(linux): refuse an observation report whose status is unreadable (@whiskeyjimbo)
- 7e2d1cc: fix(linux): refuse write:/ in the shared grant checks (@whiskeyjimbo)
- 9a1d343: fix(linux): report an unanswered host probe as unknown (@whiskeyjimbo)
- a33932a: fix(linux): run the credential alias scan on the profiling path too (@whiskeyjimbo)
- 2f4ff8e: fix(observe): count a dead thread's held probe once (@whiskeyjimbo)
- f2575e4: fix(observe): count a probe lost with a dying thread (@whiskeyjimbo)
- ed9336d: fix(observe): count an unreadable stop's held probe once (@whiskeyjimbo)
- 2344e02: fix(observe): count execve at the entry stop, so execveat is not one (@whiskeyjimbo)
- 047c518: fix(observe): count probes held when root exits (@whiskeyjimbo)
- 5efb5c0: fix(observe): count what the retired leader thread held (@whiskeyjimbo)
- d18eb19: fix(observe): decoder accuracy pass over the phantom and missing paths (@whiskeyjimbo)
- 658da53: fix(observe): exempt futimesat's NULL pathname like utimensat's (@whiskeyjimbo)
- e8b85f0: fix(observe): forget a tid retired by an execve (@whiskeyjimbo)
- 26a3a9d: fix(observe): read pathnames at the entry stop, not the exit stop (@whiskeyjimbo)
- e412662: fix(observe): resolve a dead thread's phantom drops (@whiskeyjimbo)
- 2cbb7ec: fix(observe): stop counting a dead thread's entry stop (@whiskeyjimbo)
- 7a1cb47: fix(observe): stop counting signal returns as lost file accesses (@whiskeyjimbo)
- 0a50e7e: fix(pathresolve): return the input at the depth cutoff, not a mid-chain hop (@whiskeyjimbo)
- b9acc39: fix(profile): key foreign-home warnings on the run's anchors (@whiskeyjimbo)
- febfaf1: fix(proxy): answer a guard block as an ordinary dial failure (@whiskeyjimbo)
- 123ec09: fix(proxy): contain an embedder observer panic (@whiskeyjimbo)
- 8bbe7b1: fix(proxy): fail closed on a NAT64 answer that derives nothing (@whiskeyjimbo)
- 032ad41: fix(proxy): fail closed when NAT64 discovery cannot answer (@whiskeyjimbo)
- 532f3cb: fix(proxy): keep a mid-run Accept failure across the drain (@whiskeyjimbo)
- bc444ad: fix(proxy): stop refusal bodies disclosing the resolved address (@whiskeyjimbo)
- b1e64f3: fix(proxy): strip the DNS root label from a CONNECT target (@whiskeyjimbo)
- adaf6af: fix(proxy): validate the RFC 6052 layout before an 8215 verdict (@whiskeyjimbo)
- 8919820: fix(seccomp): refuse off amd64 rather than skip the arch guard (@whiskeyjimbo)
- bd9db19: fix(supervise): report the guard-blocked destinations too (@whiskeyjimbo)
- 806dc49: refactor(denylist): share one path-coverage predicate (@whiskeyjimbo)
v0.1.0
v0.1.0
First release. Linux (amd64) is the enforced platform; arm64 and macOS are not
yet supported.
Enforcement
- Deny-by-default filesystem: only manifest-granted paths are visible inside the
sandbox. Reads are bound read-only, writes are bound per-directory (so
save-via-rename keeps working), and the sandbox root is remounted read-only. - Egress denied by default via an unshared, empty network namespace. Declared
host:portrules are routed through a host-side HTTP CONNECT proxy reached
over an isolated unix socket, with hostname validation and IP pin checks. - Subprocess execution blocked by a seccomp filter.
exec: none-strict
additionally blocksfork/cloneon amd64. - Memory, CPU, and PID limits enforced through a transient systemd scope on
cgroup v2 controllers. - Landlock rules applied as a best-effort second filesystem layer behind the
mount namespace.
Shielded by default
A mandatory denylist covers these even under a broad grant such as read: ~,
and covers paths that do not exist yet so a sandboxed program cannot create
them:
- Credentials and secret stores: SSH keys, cloud CLI tokens, GPG keyrings, OS
keyrings, crypto wallets, environment-relocated secret directories, and shell
histories. - Persistence vectors:
.git/hooks,.vscode,.idea, and shell startup files
such as.bashrcand.zshrc. - Host control sockets under
/runand/var/run- the Docker daemon socket,
gpg-agent, the session bus, and similar.
The shields anchor on both $HOME and the running uid's passwd entry, so a
caller-chosen environment cannot relocate them off the real credential stores:
under HOME=/ those stay shielded rather than the shields moving to /.ssh,
/.aws and so on. Two limits: where the uid has no passwd entry at all (an
LDAP host whose module is not loaded, an unmapped container uid) $HOME is the
only anchor left, and the passwd lookup must not route through libc NSS, which
LD_PRELOAD would put back under the caller's control - the shipped build is
static and tagged osusergo, which keeps it in pure Go. $HOME still decides
where a ~ grant points and which spellings count as a deliberate shield
opt-in; see the threat model.
A write grant that covers a shielded path is refused outright - including a
grant above a home directory that is itself a symlink, where the shield's
resolved location leaves the granted tree while the symlink inside it stays
writable. A read grant naming an exact shield path is honored as a deliberate,
warned exception.
make audit checks the denylist against upstream firejail reference definitions.
Workflow
bento profileobserves a program under default-deny and drafts a manifest.
It reads syscall registers via ptrace rather than opening host files, so a
hostile program cannot use profiling to probe secrets. Egress is recorded but
still blocked.- Manifest paths resolve against the manifest's own directory, and a leading
~
expands to the invoking user's home - so aread: "~"grant means home and is
shielded accordingly, rather than naming a file beside the manifest. Another
user's home (~operator/...) is refused rather than guessed at. Because the
fingerprint attests the manifest as written, a~grant resolves against
$HOMEat run time; see the threat model. bento validateparses a manifest, rejects malformed fields, and prints the
requested permissions and resource limits (--jsonfor machine output). Under
each grant it also prints what that grant lands on for the host it is run on,
following symlinks as well as~and relative prefixes, so a reviewer can see
what the grant reaches before approving it - a~grant whose.sshis a link
elsewhere would otherwise read as a path under$HOME.--jsoncarries the
same answer asresolved_read/resolved_write, andrun --jsoncarries
shielded_grant_targetsfor an opted-in shield, so a CI gate reads what the
human summary shows rather than the spelling alone. The literalread/write
are unchanged: they are what the fingerprint attests.- The refusals a manifest can earn without consulting the host are raised by
validateandapprove, not left forrun: a~operator/...path, and a
write grant of the home directory itself (whatever$HOMEis, the credential
stores sit inside it, so such a grant would make their parent writable). Both
were already refused at run, so on an ordinary host the gate simply moved
earlier. The one manifest this newly stops iswrite: ["~/.."]on a host
whose home directory is itself a symlink, which the enforcer accepted and
should not have. The same grant spelled absolutely (write: /home/u) still
needs$HOMEto recognize and is still refused at run. bento approvestamps a fingerprint over the policy fields.bento run
refuses an unapproved or since-edited manifest unless--allow-unapprovedis
passed, and re-checks the fingerprint at run time rather than trusting an
earliervalidate.bento doctorreports which isolation layers this kernel actually enforces.
No quiet degradation
When a hardening layer is unavailable, Bento reports the shortfall instead of
falling back silently, and --strict makes bento run refuse to execute under
degraded enforcement.
Embedding
The Go API (backend, enforce, manifest, policy) is importable for
in-process enforcement, including a NetworkGate callback that lets a host
application decide undeclared egress at connect time. See examples/embed and
examples/supervise. Pre-1.0, this API may change between minor versions.
Changelog
- 29092f3: Revert "feat(linux): enforce resource limits in the degraded tier via a systemd scope" (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- bb9d30a: bd init: initialize beads issue tracking (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 1c092d0: build(lint): add golangci-lint config and make lint (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 560383a: build(lint): narrow the manifest test exclusions to their rules (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 0465c15: build(release): add goreleaser with commit-derived stamps (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 1408aae: build: fail the repro check when it cannot hash (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 17dbe20: build: make the binary reproducible and add vuln/repro gates (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 0257bf3: c (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- f64ed61: ci(release): build, sign and publish on tag (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 8530522: ci(release): give the gate its sandbox host deps (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 26f166a: ci(release): pin cosign-installer to v4.1.2 (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- e9e7b3e: ci(release): probe sandbox preconditions before the gate (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- fdbb85c: denylist(audit): classify credential stores by name (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 3318075: denylist(audit): extend classifier to mail, remote, wallet stores (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- a37454b: denylist(audit): fix shield kinds, close mail and chat gaps (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- f859df5: denylist(audit): hide X11-login and systemd persistence trees (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- ce49561: denylist(audit): parse blacklist-nolog, hide nvim trees (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 6f8ffd7: denylist(audit): shield WM/session and editor-init persistence surfaces (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- bd14e7b: denylist(audit): shield browser profile stores and clipboard/REPL history (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 0c2ede6: denylist(audit): shield mail stores, X cookie, crypto socket; review keyfile globs (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 43d6bed: denylist(linux): drop stale nvim example from shield doc (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 4d1e870: examples(probe): add a sandbox probe script and manifests (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- ef9e33b: feat(audit): add AppArmor abstractions as a second corpus (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 505cbd4: feat(audit): add a volume floor and list the discarded gaps (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- d253117: feat(authoring): manifest approval fingerprint, bento approve, validate --strict (whiskeyjimbo 15094606+whiskeyjimbo@users.noreply.github.com)
- 89ba755: fea...