Skip to content

Tags: gominimal/minimal

Tags

release-fad7c721

Toggle release-fad7c721's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(minimal): keep store rules under min --config-dir in step with b…

…ep serve (T92) (#2666)

* feat(minimal): refuse a store handle under a rule bep serve does not read

A store rule read from a min --config-dir override reaches the handle
only when bep serve, which reads the default config dir, holds the same
rule. Otherwise min refuses the mint and names both locations (BEP-032).

* docs(bep): name the min refusal in place of the config-dir issue link

* chore(minimal): allow the store mint's eight arguments in clippy

* docs(minimal): name the two copies of the store rule conversion

proxy_rule in min copies store_rule in the bep binary. Each doc comment
now names the other copy, so a change to one reaches both.

* refactor(bep): share the store rule conversion with min's mint check

Move store_rule into the bep library as StoreRule::from_config. bep serve
and min's mint check now decide inject with one function, so the copy in
mint.rs and its notes are gone.

Give each cause of a store handle refusal under --config-dir its own
text: an unread file, a refused rule, a deny rule and a rule that
differs. Each names the file that bep serve reads.

---------

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>

release-5a93a07a

Toggle release-5a93a07a's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(bep): inject store values in bep serve (T67) (#2597)

BoxRedeemer now overrides redeem_reference. An admitted store member is
given to the forwarder as Redeemed::Store, with the store, identifier and
injection form, so the forwarder reads the value per request. A store
member is judged against the handle's upstream and the current rule's
authorities. Any other value outside Authorization is still refused as
sealed_header_misplaced before it is decided.

Each flow bep serve builds now reads from the host store. The redeemer
takes its [secret-store-rules] through with_rules. bep serve passes none
until T25 reads them.

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>

release-dd753658

Toggle release-dd753658's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs(design): split-user VMM provider directory and socket ownership …

…(T57) (#2546)

* docs(minvmd): design provider dir ownership for a split-user VMM

Names the creating user, the mode and the opening users of every socket
and file in the provider directory when gvproxy and the VMM child run as
a dedicated user. A test in sock.rs checks the table against the socket
names the code uses. The architecture ruling is still pending.

* docs(minvmd): add sticky bit and entry checks to split-user design

A 2770 provider directory lets minimal-vmm unlink control.sock and bind
its own socket at the path. The model now uses 3770, and it says how the
supervisor, bep serve and min check each entry before they bind, chmod
or connect.

The design test now also checks the gvproxy.yaml, switch, bep and
control socket rows, and the fallback table.

* docs(minvmd): give the VM user its own dir in the split-user design

Apply the arch ruling: the provider dir is 0710 group minimal-vmm and the
VM user cannot write it. Everything minimal-vmm creates moves to vmm/.
State the peer uids each door admits, the sudo -n NOPASSWD spawn and its
refusal, and the one-member group. The test now checks each row's creator.

* docs(minvmd): keep the gvproxy switch socket away from the VM user

Address round 3 of the split-user design review.

- Arch C1: the VMM child sets the mode of boot.log (0640) and
  data-vol.raw (0600) itself, and the supervisor refuses group or other
  write bits. Fact 4 says umask 0007 gives 0660 to a file with no mode.
- gvproxy stays with the login user, so its 0600 switch socket is
  reachable only through the egress gate. A new section lists what a
  compromised VMM can and cannot reach. The fallback says it does not
  protect the gate.
- Status is "proposed, arch conditions open".
- The sudoers drop-in names root-owned absolute paths, fixed arguments
  and env_reset with a named allow-list. Fact 8 follows.
- The sock.rs test comment promises only what its hand-written list
  proves, and cites the review thread, not a ruling.

* docs(minvmd): run gvproxy as a third user in the split-user design

Apply the arch ruling at e8be045. gvproxy runs as minimal-gvproxy, apart from
<login> and minimal-vmm, so it shares no writable state with bep serve.

- The switch socket moves to a root-created gvproxy dir (0750, minimal-gvproxy),
  bound under spawn umask 0007 as 0660 group minimal-gvproxy.
- gvproxy.yaml moves to a gvproxy config dir that <login> owns (0750, group
  minimal-gvproxy). The file is 0640, and minimal-vmm cannot read it.
- The sudoers rule for gvproxy sets umask=0007 with umask_override.
- min finalize-install moves data-vol.raw with openat, O_NOFOLLOW, nlink and
  inode checks, and fchown on descriptors, never by path.
- The open item on the switch socket is closed by the ruling.
- The test checks the gvproxy dir, the switch socket and gvproxy.yaml rows.

* docs(minvmd): open the egress gate placement under Gatehouse 6.10

Quote Gatehouse section 6.10 line 831 as written: "the BEP role and the EGW/gvproxy role".
Add a section on what a compromised supervisor can reach. The egress gate and
switch::bep_host parse guest frames as the login user, so the model alone does
not meet section 6.10. Add the open item for arch to rule on their placement,
and set the status to "proposed, arch conditions open".

check_table now refuses a row whose mode has no group or other bits but names an
opener other than the owner.

* docs(minvmd): run the egress gate in an EGW process as minimal-gvproxy

Write in the arch ruling at 4308298 on Gatehouse section 6.10 line 831.
The egress gate and switch::bep_host move out of the supervisor into an
EGW process that runs as minimal-gvproxy. bep serve stays the login user.

The EGW process creates gvproxy-gate.sock in a new setgid gate dir that
minimal-gvproxy owns with group minimal-vmm. minimal-gvproxy is never in
the minimal-vmm group, so it gets no access to vmm/. gvproxy-bep.sock
moves to the gvproxy config dir, where minimal-gvproxy can search and
minimal-vmm cannot. listener.rs admits the minimal-gvproxy uid and keeps
the token check. The switch socket becomes 0600.

Replace the compromised-supervisor section with one for the EGW process,
remove the open placement item, and set the status to name the ruling.
The table test now expects the gate socket from minimal-gvproxy and the
proxy socket in the gvproxy config dir.

* docs(minvmd): model the supervisor channel to the EGW process

Name the socketpair that carries the box table, the withdrawals, the
attachments and the token from the supervisor to the EGW process, with its
creating user, opening users, direction and peer check, and add it to the
ownership table. The EGW process drops every frame and refuses every
publish request until it holds the first complete table, and exits when the
channel closes. The supervisor handles that exit on the gvproxy exit path.

Pass every per-run value on descriptor 0, so each sudoers rule names only
literal arguments for one VM. Link the arch review comments from the
status. Add the gate and BEP socket rows to the fallback table and to the
test, and drop the review history from the test comments.

* docs(minvmd): bound the EGW withdrawal ack and cite networking 4.3

The supervisor waits at most 5 seconds for a withdrawal acknowledgement.
On timeout it stops the EGW process and gvproxy on the EGW exit path, then
answers the withdrawal. "What T38 changes" adds the test for that path.

Cite networking design section 4.3 lines 83 and 85 for default deny and
fail closed, in place of Gatehouse. Rename the design test to say that it
checks a fixed list. The new name keeps the plan's test filter as a prefix.

* docs(minvmd): stop the EGW process as minimal-gvproxy before the ack

A SIGKILL from the supervisor reaches sudo, not the EGW process behind it.
Add a stop rule to the drop-in that sends SIGKILL to the process group of
the spawn as its own user, after a SIGTERM that sudo relays. The supervisor
answers a withdrawal as done only after it observes the exit, and names the
fail-closed result when no exit comes within the bound (networking 4.3).
The T38 stub EGW process now also ignores SIGTERM.

* docs(minvmd): shut down the write side of the EGW channel on stop

Step 1 of the stop sequence now calls shutdown(2) with SHUT_WR on the supervisor end
and keeps it open, so the supervisor can read end of file there after the EGW exit.
The supervisor closes its end only after the observed exit or the fail-closed result.
The first stop test in "What T38 changes" now pins the SHUT_WR step.

---------

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>

release-608ab9c9

Toggle release-608ab9c9's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(minimald): take an exec's input off the SSH channel as it arrives…

…, not as the command reads it (#2197)

russh credits the client's send window on receipt and gives the server no
way to hold it back, so a command that stops reading its stdin parked the
whole SSH connection, every channel included (inbox#1089). The pump now
takes input off the channel as it arrives into a held queue the bridge
reads from, bounded per exec (256 MiB) and for the daemon (512 MiB): past
a bound a command that reads nothing for 10 s is ended with a notice on
its stderr, a command that has not started is waited for up to 300 s, and
an exec holding nothing at the daemon bound waits for the others up to
60 s. A single program that closed its stdin is not charged for input it
will never read, which needs the injection shim to let go of its own copy
of that stdin once the command is spawned. An exec whose end comes while
its command is starting ends at once, and the command a late start yields
is reaped.

Ported onto main across the destroy-notice work (#2351): the destroy
wrapper takes an optional token so the bridge hands back one stream type
for a late start, and bridge_noting_reason names the three ways an exec
ends.

release-5378e56c

Toggle release-5378e56c's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
chore(release): merge tag v0.7.1 back into main (#2474)

* fix(promote): find the draft release by listing and publish it under its tag (#2220)

The publish job looked the release up with `gh release view v<semver>`.
A draft has no tag yet, so the lookup answered 404 for exactly the
release the job exists to publish, and the 0.7.0 promotion failed there.
Publishing a draft without naming tag_name also tags it with the draft's
untagged-<hex> placeholder rather than v<semver>.

The job now finds the release by listing releases and matching
tag_name, refuses when more than one carries the tag, and publishes by
id with tag_name and target_commitish set.

* fix(deps): bump rustls-webpki and webpki-root-certs past the emSign advisories (#2466)

RUSTSEC-2026-0382 (rustls-webpki 0.103.15) and RUSTSEC-2026-0383 (webpki-root-certs
1.0.9), both published 2026-10-20, red cargo-deny on every PR. rustls-webpki 0.103.16 rejects
paths through the distrusted emSign intermediates, and webpki-root-certs 1.0.10 drops the emSign
ECC Root CA - G3. Lockfile-only change.

(cherry picked from commit c0ac8c5)

* chore(release): bump version to 0.7.1

Hotfix release carrying the rustls-webpki and webpki-root-certs bumps
from #2466 (RUSTSEC-2026-0382, RUSTSEC-2026-0383) onto 0.7.0.

Claude-Session: https://claude.ai/code/session_01FnY4WzhrnmSEByMntmqsVF

v0.7.1

Toggle v0.7.1's commit message
chore(release): bump version to 0.7.1

Hotfix release carrying the rustls-webpki and webpki-root-certs bumps
from #2466 (RUSTSEC-2026-0382, RUSTSEC-2026-0383) onto 0.7.0.

Claude-Session: https://claude.ai/code/session_01FnY4WzhrnmSEByMntmqsVF

release-248b259c

Toggle release-248b259c's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(bep): verify store handles against the client's registered key a…

…nd the current rule (#2467)

* feat(bep): verify store handles against client keys and the current rule

Add the store-handle wire format (an ES256 compact JWS), the registry of
client keys, and the BEP-064 check. The proxy reads the rule for each
request, so an edited [secret-store-rules] rule decides the next request.
A sign-in reference has the GitHub module as its rule.

The redemption decision makes the handle check after the revocation
check, and refuses a bad handle as store_handle_invalid. Add a Kani
harness for it and raise the bep harness count in scripts/kani.sh.

* fix(bep): bind the store handle to the opened member

Add handle::read_opened, which reads the handle from the sealed member's
own jws. A handle of another kind than the member, or a value opened for
another module, has no rule and is refused as store_handle_invalid.

A sign-in reference whose store is not the module's id now has no rule.
The debug line logs the unverified store and id with Debug escaping.

* fix(bep): bind store handles to the box and the Box Host

Follow the Gatehouse store-handle wire format: typ
gatehouse-store-handle+jws, and sub, node_id, iat and jti in the
payload. The sealed member gives the kind, not the payload. A handle
whose sub or node_id differs from the opened value refuses as
context_mismatch. read is private, so only read_opened builds a
handle, from the sealed member.

* docs(bep): carry the handle's box and host binding into spec 24

BEP-063 names the handle's typ and the sub, node_id, iat and jti
claims. BEP-064 and its property refuse a handle whose sub or node_id
differs from the sealed value's box or host, as Gatehouse §6.10 does.

* fix(bep): log an unparsed handle upstream apart from the signature

A handle that verifies but names an upstream that does not parse is
still refused, but its debug line now says unparsed_upstream, not
signature.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3Guwr3mQPqSeGMcjzVjTd

* fix(bep): bound a redeemed authority by the handle's upstream

decide now refuses an authority outside the handle's own upstream as
outside_bound_set, so a bound set wider than the checked handle cannot
widen where the value goes. Adds a test for the mismatch.

---------

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

release-03466f74

Toggle release-03466f74's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(bep): refuse off-set authorities, off-port and expired values (#…

…2448)

decide now refuses a value whose clock is at or after its expiry, or
whose box a revocation in force names (BEP-024). It refuses a connection
to a credentialed hostname on an undeclared port (BEP-031), and an
authority outside the bound or the current host set (BEP-021). Each
refusal names its check as an audit marker: expired, revoked, off_port,
outside_bound_set or outside_current_set. Add three Kani harnesses and
expect six in scripts/kani.sh.

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>

release-d67d78ee

Toggle release-d67d78ee's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
refactor(minvmd): move canonical_box_name into box_registry (#2344)

box_registry imported net::answerer::canonical_box_name while
answerer imported box_registry's types, so the two modules
imported each other. The helper belongs with the registry that
holds the rows it names: move it to box_registry, keep the
one-directional edge (answerer imports box_registry), and repoint
the two fully-qualified call sites and the intra-doc links.

Co-authored-by: gominimal-aw-bot[bot] <281738952+gominimal-aw-bot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

release-1d13b179

Toggle release-1d13b179's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(minimald): log the box name plainly in shared-port hand-over lines (

#2308)

* fix(minimald): log the box name plainly in shared-port hand-over lines

Four log sites in box_actor.rs logged the box name with Rust Debug
formatting (name=Some("n2-a8")), which defeats the greps a diagnostics
bundle is written against and diverges from the session_name=<plain>
convention used by every sibling line. Render the name through
as_deref().unwrap_or("-") under the session_name field instead,
matching the ANONYMOUS_SESSION idiom in rpc.rs.

Tests: pin the rendered hand-over info line and the refused-report
warn line to session_name=<plain>.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(minimald): name unnamed boxes with the shared anonymous placeholder

The shared-port hand-over lines and the session-start network line now
fall back to ANONYMOUS_SESSION ("<anonymous>"), the placeholder every
other session_name line uses. The refused host-address create line
renders session_name plainly instead of as a Debug-formatted Option.

---------

Co-authored-by: gominimal-aw-bot[bot] <281738952+gominimal-aw-bot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>