8000 T134 — Expose decisions for a box with no switch, and answered asks, reach the host's audit log by minimal-foundry[bot] · Pull Request #2969 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T134 — Expose decisions for a box with no switch, and answered asks, reach the host's audit log - #2969

Merged
norrietaylor merged 21 commits into
mainfrom
agent/2929-expose-decisions-for-a-box-with-no-switch-and-an
Nov 4, 2026
Merged

norrietaylor merged 21 commits into
mainfrom
agent/2929-expose-decisions-for-a-box-with-no-switch-and-an

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T134 — Expose decisions for a box with no switch, and answered asks, reach the host's audit log

slice: S16
kind: task
covers: NET-046
size: M
est: 150 code, 250 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: a person reads the audit log the VM host daemon keeps on the host, which now holds a record for a refused min net expose from a box with no switch, and an admission record when a person answers yes to the ask dialog
observability: the VM host daemon appends one audit record per reported decision, now including boxes with no switch and yes-answered asks, and logs at debug which key, switch address or box id, found the row
layers:
- guest:
- crates/minimald/src/net/listeners.rs: report_expose_decision sends a box with no switch address its report by box id over the box control channel, instead of returning without a report
- crates/minimald-rpc/src/lib.rs: ExposeDecisionReport carries the box id beside an optional switch address, both serde-defaulted, so an older guest's report still reads
test: cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id
- host:
- crates/minvmd/src/control.rs: audit_expose_decision finds the row by box id when the report has no switch address; record_ask_answer appends the admission record for a yes answer, as an allow-decided expose does
test: cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box
test: cargo nextest run -p minvmd ask_yes_writes_admission_line
produces: none
consumes: none
proof: After a refused min net expose from a host_ip box and a yes answer in the ask dialog, the audit log on the host holds a record for each.

Proof

Partly delivered. A yes answer in the ask dialog now writes an admission record (source "ask") to the host audit log at audit/box-admissions.log. The VM host daemon also records an expose decision report that names a box with no switch by its box id. But a refused min net expose from a host_ip box still leaves no record on the host. The guest never sends that report: see notes.

Notes from the agent: Guest half not done. The task cannot be done inside its layers. (1) report_expose_decision in listeners.rs gets only the box name, from audit::report_to_host and the DecisionRecord. Nothing in listeners.rs maps a name to a box id or a control channel for a box with no switch. The host audit routes exist only for boxes with a listen watcher, and a listen watcher needs a lease. To send the box id, the id must pass from box_actor/ports.rs (answer_expose / answer_expose_because) through audit::DecisionRecord or audit::report_to_host into report_expose_decision. Those files are not in the layers. (2) A host_ip (HostNet) box never reaches the audit path. box_actor/ports.rs answers its expose with NeedsNoExposing before any decision, and its comment says nothing is audited on purpose. So the proof's host_ip case also needs a change in ports.rs, and a decision on whether that refusal counts as a NET-046 decision. Today the only no-switch box that reaches report_expose_decision is an ask box on a VM host with no box_addresses, which is refused NotAttached. The minimald test no_switch_box_reports_expose_decision_by_id is not written, and its test line exits 4 (no tests to run).

Outside the layers: crates/minimald/src/box_runtime/tests.rs has two ExposeDecisionReport struct literals in expose_unenrolled_decision_audited. The new box_id field and the Option switch address forced me to change them (Some(row), box_id: None). That test passes. In listeners.rs the only change is the same literal shape.

Host details: a repo…[truncated]

Tests 8000

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 4 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-884a... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-67bc... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-7c23... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify

min task run agent-verify passed ([tasks.verify]).

Base

The base branch moved while the coder worked; it was merged in before the tests ran.

Out of scope

These files are outside the task's layers:, so this PR is a draft:

  • crates/minimald/src/box_runtime/tests.rs

Closes #2929

ExposeDecisionReport now carries an optional box id beside an optional
switch address, both serde-defaulted, so an older guest's report still
reads. The host names a report with no switch address from what it holds
under the box id: a row, a held name, or a resident host_ip box. It logs
at debug which key found the box.

A yes answer to a pending ask now appends the same admission record an
allow-decided expose writes, with source "ask" (NET-046).

The guest still sends no report for a box with no switch: the report
path has no box id to send. The two struct literals in minimald follow
the new field shape.
@coderabbitai
coderabbitai Bot commented Nov 4, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fe54e552-184d-4fd5-9482-1a92316f6b88

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger 8000 review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@minimal-foundry
minimal-foundry Bot commented Nov 4, 2026 •
Copy link
Copy Markdown
Contributor Author

Approved: every review thread resolved at e38a0bc0b79a.

@norrietaylor resolved the last thread, which releases the approval this review had withheld.

Nothing to do.

@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

arch-review: CONDITIONS · 2 conditions · the full ruling

The host can now record an expose decision for a box with no switch, but no guest sends one. So the task's proof is not met, and the PR must not close #2929. Section 7.1 line 265 requires that the result is audited locally. C1 files the guest half as a residual with an issue. C2 gives each no-switch box its own audit cap, so one box cannot suppress the records of another. The escalation asks if a host_ip refusal is a NET-046 decision.

Ruled at f4c131d against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

Comment thread crates/minvmd/src/control.rs Resolved
Comment thread crates/minimald/src/net/listeners.rs Resolved
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 3 findings, 2 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minvmd/src/control.rs Resolved
Comment thread crates/minimald/src/net/listeners.rs Resolved
Every box with no switch shares the node's own address, so a cap
window keyed by that address let one box spend the cap of all of
them. The window is now keyed by the row's switch address or, for a
box with no switch, by its box id. The audit_suppressed line names
the box id for such a box and carries no switch address.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minimald/src/net/listeners.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: CONDITIONS · 2 conditions · the full ruling

The host half of NET-046 conforms. The new cap key per box fixes the shared-address cap from the last head. The guest half is missing. report_expose_decision still returns early for a box with no switch, and it always sends box_id: None. So the host gets no record for that box, and §7.1 line 265 requires one. C1 delivers the guest half and its test. C2 keeps #2929 open until then. The host_ip case in the proof goes to a person as an escalation.

Ruled at 6d4b0c6 against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

Comment thread crates/minimald/src/net/listeners.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on the runtime's review findings, pushed as 6d4b0c671948.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 4 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-8c42... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-527d... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f399... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 100 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

ps_at_argc
        PASS [   0.024s] (1942/2089) minvmd net::stale_gvproxy::tests::reap_fails_closed_on_a_live_minvmd_owned_gvproxy
        PASS [   0.032s] (1943/2089) minvmd net::stale_gvproxy::tests::reap_kills_this_vms_leftover
        PASS [   0.648s] (1944/2089) minvmd net::proxy_reclaim::tests::proxy_reclaims_preferred_port_when_free
        PASS [   0.019s] (1945/2089) minvmd net::tests::argv_listens_on_switch_socket
        PASS [   0.048s] (1946/2089) minvmd net::stale_gvproxy::tests::reap_leaves_another_vms_and_another_binarys_gvproxy_alone
        PASS [   0.017s] (1947/2089) minvmd net::tests::config_yaml_carries_subnet_gateway_and_leases
        PASS [   0.030s] (1948/2089) minvmd net::tests::drop_sigkills_supervised_child_without_blocking
        PASS [   0.015s] (1949/2089) minvmd net::tests::empty_config_still_emits_a_lease_map
        PASS [   0.022s] (1950/2089) minvmd net::tests::host_gvproxy_serves_the_registrys_subnet
        PASS [   0.026s] (1951/2089) minvmd net::tests::host_gvproxy_drop_stops_the_switch
        PASS [   0.020s] (1952/2089) minvmd net::tests::host_gvproxy_spawn_reports_launch_failure
        PASS [   0.030s] (1953/2089) minvmd net::tests::host_gvproxy_spawns_supervises_and_stops
        PASS [   0.029s] (1954/2089) minvmd net::tests::host_gvproxy_starts_the_gate_beside_the_switch_socket
        PASS [   0.028s] (1955/2089) minvmd net::tests::pidfd_signal_to_reaped_child_returns_esrch
        PASS [   0.029s] (1956/2089) minvmd net::tests::stop_terminates_supervised_child
        FAIL [   5.024s] (1957/2089) minvmd net::egress_gate::tests::stalled_switch_keeps_guest_side_draining
  stdout ───

    running 1 test
    test net::egress_gate::tests::stalled_switch_keeps_guest_side_draining ... FAILED

    failures:

    failures:
        net::egress_gate::tests::stalled_switch_keeps_guest_side_draining

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 666 filtered out; finished in 5.01s

  stderr ───

    thread 'net::egress_gate::tests::stalled_switch_keeps_guest_side_draining' (11374) panicked at crates/minvmd/src/net/egress_gate.rs:18956:9:
    the guest side keeps draining; it does not wedge
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

  Cancelling due to test failure: 3 tests still running
        PASS [   0.042s] (1958/2089) minvmd net::tests::unexpected_exit_fires_notify
        PASS [   0.304s] (1959/2089) minvmd net::tests::lost_datapath_warns_within_one_minute
        PASS [   8.026s] (1960/2089) minvmd net::egress_gate::tests::the_holds_release_ends_with_the_gate_and_gives_up_bounded
────────────
     Summary [ 337.959s] 1960/2089 tests run: 1959 passed (1 slow, 2 leaky), 1 failed, 25 skipped
        FAIL [   5.024s] (1957/2089) minvmd net::egress_gate::tests::stalled_switch_keeps_guest_side_draining
warning: 129/2089 tests were not run due to test failure (run with --no-fail-fast to run all tests, or run with --max-fail)
error: test run failed

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — crates/minvmd/src/control.rs: Keyed the cap window by GuestAuditKey::Box(box_id) for a box with no switch; the audit_suppressed line now writes box_id and no switch_address for it. Test covers two such boxes.
  • 👎 invalid — crates/minimald/src/net/listeners.rs:901: The gap is real, but the fix is outside this task's layers: the box id is only known in crates/minimald/src/box_actor/ports.rs and must pass through crates/minimald/src/audit.rs to reach listeners.rs. The guest half needs a follow-up task; the PR body and issue link are the runner's to change.
  • 👍 valid — PRRT_kwDOPdfcxM6zaQhg: Same fix as the C2 thread: a box with no switch now has its own cap window, keyed by its box id, not by the node's address. (not posted on the thread: not an open thread on the pull request)
  • 👎 invalid — crates/minimald/src/net/listeners.rs:903: Correct that the guest sends no box id, but the change needs crates/minimald/src/box_actor/ports.rs and crates/minimald/src/audit.rs, which this task does not name. The result now states the proof as partial and names the residual.

Partial. A yes answer in the ask dialog now writes an admission record to the audit log on the host. The host also records an expose decision report that names a box with no switch by its box id, and each such box has its own cap window. But the guest still sends no report for a refused min net expose from a host_ip box, so that record does not reach the host audit log yet.

Notes from the agent: The guest half cannot be done inside this task's layers. The expose path that knows the box id is crates/minimald/src/box_actor/ports.rs (answer_expose_because). The record it hands to crates/minimald/src/audit.rs carries only the box name, and report_to_host in audit.rs calls report_expose_decision in listeners.rs with that name only. Nothing in listeners.rs maps a box with no switch to its box id or to a host channel: HOST_AUDIT_ROUTES is filled only by the listen watcher, which runs only for boxes with a switch. A fix needs a box id passed from ports.rs through audit.rs (or a route registered from box_manager.rs at the mint_box_id call), so a follow-up task must name those files. The PR must say Refs, not Closes, for the issue, and the guest half needs its own issue. This round: the host cap window is keyed by switch address or by box id (GuestAuditKey), and the audit_suppressed line names box_id and omits switch_address for a box with no switch. A no-switch box's window is not flushed on withdrawal, because no withdrawal names it by id; it ends by the window timeout or the tracked-box pruning. cargo clippy -p minvmd --all-targets and cargo fmt are clean.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft November 4, 2026 02:39
@norrietaylor
Copy link
Copy Markdown
Member

/revise Design decision on the arch ruling: a host-address box's NeedsNoExposing refusal decides nothing, so it stays unaudited. T134's proof in #2929 now names an ask box on a VM host with no switch address, refused as not attached, and a yes answer in the ask dialog. Deliver arch C1 in this PR. crates/minimald/src/box_actor/ports.rs and crates/minimald/src/audit.rs are now in the task's layers. Do not apply arch C2: keep Closes #2929, because this PR now delivers the guest half too.

A box with no switch has no audit route by switch address, so its
refused or published expose decision never reached the VM host daemon.
The box actor now hands the audit its box id and control channel when
the box has no switch, and the report goes to the host door keyed by
the box id. A host-address box's needs-no-exposing answer stays
unaudited, because it decides nothing.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on a /revise note, pushed as 291c81d4dcfa.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-5bd2... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-b196... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-0fa6... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 100 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

d
        PASS [   0.609s] ( 196/2091) minimald box_actor::tests::stop_with_a_failing_event_write_still_records_stopped
        PASS [   0.585s] ( 197/2091) minimald box_actor::tests::stop_with_an_unwritable_record_refuses_launches_until_retried
        PASS [   2.651s] ( 198/2091) minimald box_actor::tests::stop_box_with_an_unwritable_record_still_marks_name_stopped
        PASS [   2.628s] ( 199/2091) minimald box_actor::tests::stop_unexposes_before_withdrawing_the_report
        PASS [   0.023s] ( 200/2091) minimald box_actor::tests::stopping_a_wedged_host_detaches_its_loop_instead_of_parking
        PASS [   0.588s] ( 201/2091) minimald box_actor::tests::terminate_option_deletes_session_and_closes_channel
        PASS [   0.043s] ( 202/2091) minimald box_actor::tests::the_write_lock_promotion_moves_an_interim_to_a_vouched_hand_only
        PASS [   2.605s] ( 203/2091) minimald box_actor::tests::stop_with_unreadable_record_still_marks_name_stopped
        PASS [   2.600s] ( 204/2091) minimald box_actor::tests::stopped_shared_address_box_is_nodata_through_actor
        PASS [   2.653s] ( 205/2091) minimald box_actor::tests::stopped_shared_address_box_resumes_at_its_address_through_actor
        PASS [   0.941s] ( 206/2091) minimald box_actor::tests::unbound_subcommand_swallows_and_cancels_command_mode
        FAIL [   0.696s] ( 207/2091) minimald box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_before_queued_ensure_host
  stdout ───

    running 1 test
    test box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_before_queued_ensure_host ... FAILED

    failures:

    failures:
        box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_before_queued_ensure_host

    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 1304 filtered out; finished in 0.66s

  stderr ───

    thread 'box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_before_queued_ensure_host' (2684) panicked at crates/minimald/src/box_actor/tests.rs:16249:5:
    the queued exec launched a host in an exited box
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

  Cancelling due to test failure: 3 tests still running
        PASS [   0.771s] ( 208/2091) minimald box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_records_exited
        PASS [   0.985s] ( 209/2091) minimald box_actor::tests::until_complete_exit_record_write_retries_after_failure
        PASS [   2.651s] ( 210/2091) minimald box_actor::tests::vm_backed_expose_reports_admission_to_host
────────────
     Summary [ 105.273s] 210/2091 tests run: 209 passed, 1 failed, 25 skipped
        FAIL [   0.696s] ( 207/2091) minimald box_actor::tests::until_complete_ends_on_entrypoint_return_without_client_before_queued_ensure_host
warning: 1881/2091 tests were not run due to test failure (run with --no-fail-fast to run all tests, or run with --max-fail)
error: test run failed

Review threads the coder judged:

  • 👍 valid — crates/minimald/src/net/listeners.rs:917: report_expose_decision now sends switch_address None and the box id over the box's control channel when no route names the box. ports.rs passes the id for a box with no switch, and no_switch_box_reports_expose_decision_by_id pins it.
  • 👍 valid — crates/minimald/src/net/listeners.rs: When no route exists, report_expose_decision falls back to box_report_channel(control) and sends switch_address None, box_id Some(id). The id and control come from the box actor through audit::HostReportKey.

An ask box on a VM host with no switch address is refused as not attached. The in-VM daemon now sends that decision to the VM host daemon keyed by the box id, and the host writes a record for it in its audit log. A yes answer in the ask dialog writes an admission record in the host audit log, as an allow-decided expose does. The guest test shows the report with switch_address None and the box id, and the two minvmd tests show the host records.

Notes from the agent: Round 2 delivers arch C1, the guest half. The box id and control channel do not go into DecisionRecord as fields. A new field breaks the struct literal in crates/minimald/src/diag.rs, which is outside the layers. They travel as a separate audit::HostReportKey argument to audit::append, audit::try_append and report_to_host. answer_expose and answer_expose_because keep their signatures, because crates/minimald/src/box_actor/host.rs (outside the layers) calls answer_expose. answer_expose_because reads the box record and sends the box id only when the record has no box_addresses. A box with a switch still reports by its route, so its report is unchanged. The listen watcher passes an empty key, because it always holds a route. A host-address box's NeedsNoExposing refusal stays unaudited, as the note directs. Arch C2 is not applied, as the note directs. The failed ci-linux-kvm-success and test-kvm checks show only runner setup lines in the excerpt and no test failure. I did not change anything for them. Extra check: cargo nextest run -p minimald expose ask report audit unaudited ran 160 tests, all passed. I did not run just clippy-strict.

@norrietaylor
norrietaylor marked this pull request as ready for review November 4, 2026 06:45
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minvmd/src/control.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: CONDITIONS · 3 conditions · the full ruling

The guest now sends a no-switch box's decision by box id, but the host cannot name the box that sends it. On a VM host only a none box sends a report by id. no_switch_box_name finds ids only for host_ip boxes, so the host answers Error and writes no record. §7.1 line 265 says the helper audits the decision locally. C1 gives the host a record for a none box. C2 makes both tests use a box a VM host can create. C3 corrects the task proof and the PR body.

Ruled at 291c81d against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

Comment thread crates/minvmd/src/control.rs Resolved
Comment thread crates/minvmd/src/control.rs Resolved
A box with no switch files its audit window by box id. Its hold release
and its host-address retire now remove that window and write the count
of the records it suppressed, as a row withdrawal does for its switch.
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The change conforms to §7.1 lines 244 and 265. A box with no switch reports its expose decisions by box id. The host names the box only from records it holds. A yes answer to an ask writes an admission record on the host. Commit 928f2de9 flushes the audit window of a box with no switch when its name hold is released or it retires. Only the host door serves those verbs. The host_ip record in the proof is not a design requirement. Amend the proof line in #2929.

Ruled at 0991384 against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on the runtime's review findings, pushed as 099138439da0.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-4b5a... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-35ae... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-875d... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/minvmd/src/control.rs:2733: flush_guest_audit now takes a GuestAuditKey. ReleaseBoxName (with a session id) and RetireHostIpBox flush the box-id window: they write its audit_suppressed count and remove it. expose_decision_recorded_for_no_switch_box now checks both paths.

After an ask box on a VM host with no switch address is refused as not attached, and after a yes answer in the ask dialog, the audit log on the host holds a record for each. When such a box ends, the log also holds the count of the records its cap suppressed.

Notes from the agent: Revise round 3. A box with no switch never sends Withdraw. It ends through ReleaseBoxName (with its session id) or RetireHostIpBox. Both arms now flush the box-id audit window: they write the audit_suppressed line when the count is above zero, and they remove the window from GUEST_AUDIT_WINDOWS. flush_guest_audit now takes a GuestAuditKey. A ReleaseBoxName with no session id cannot name the box id, so it flushes nothing. cargo clippy -p minvmd --all-targets is clean. I did not run just clippy-strict.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 7 unresolved threads — minimal-arch[bot] crates/minvmd/src/control.rs, minimal-arch[bot] crates/minimald/src/net/listeners.rs, minimal-arch[bot] crates/minimald/src/net/listeners.rs, minimal-foundry[bot] crates/minvmd/src/control.rs:2733, minimal-arch[bot] crates/minvmd/src/control.rs:2845, …. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The compare conforms to §7.1. min holds a none box's name over the host door at create, as a host_ip box does. So the host knows the box id from its own record (line 244, line 255). The guest still sends one key, and the switch route wins. A report for an unknown id is refused. The PASS on 8da17e83 stands for the rest of the change.

Ruled at f1e89da against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 2 findings, 2 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minimald/src/box_actor/ports.rs Resolved
Comment thread crates/minimal/src/cmd/boxes/create.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 5 of 10 on a /revise note, pushed as f1e89da6b5ef.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-0ea4... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-6924... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-53c4... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 101 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

0fede5bd,libserde_json_lenient-92929ecc2f1def0c,libitoa-cbb854fdc2dced63,libryu-2773a0b4dd1d509e,libglobset-f217781657d82ea3,liblog-988c240361c9c66b,libbstr-e83fb879b6622143,libregex_automata-4c439c3d322954af,libregex_syntax-51960efa806714c4,libaho_corasick-ebfce1742b109ce1,libmemchr-fca3302d1b4cf9f9,libwalkdir-78bf723f3e841c1f,libsame_file-7e2274c175d94bd9,libnutype-fe098727c755029a,libclient_config-35b779d6f0be8a91,libtoml-c5cc97c5a19b6998,libtoml_writer-fd6080d9260a89b5,libtoml_parser-4b46d8295a313e9d,libwinnow-72d9fcc36f7ddb90,libserde_spanned-612a6723e4e45310,libtoml_datetime-17cb3073aa4a055d,libtracing-be36af57e81cc6b6,libpin_project_lite-b723086f0fbdf6a7,libtracing_core-0c0618d180a921e1,libonce_cell-c4bdd1ba748758be,libportable_atomic-9c00c7ad23fc8f5a,libpaths-4301b8e4efa75112,libdirs-ad046feead606c63,libdirs_sys-316570a9bbf11722,liboption_ext-4b59d732c42732bc,libthiserror-31ad7b029b93b712,libcamino-4fa3f6c669db4b97,libuuid-19bd2dbddf178337,libgetrandom-9b991c0996fd79a1,liblibc-34425f9a644580bc,librand_core-74a7e5adabb8eba8,libcfg_if-218cf50e72d02d86,libserde-f4c387cd7fdceb6d,libserde_core-f631b432aa5c92dc,libanyhow-31e14b5c13385b8c}.rlib" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/work/build-cache/cargo-home/build/debug/deps/rustc8onXaZ/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/work/build-cache/cargo-home/build/debug/build/zstd-sys-3df630743cf715ff/out" "-L" "/work/build-cache/cargo-home/build/debug/build/ring-3c60c1595d114743/out" "-L" "/work/build-cache/cargo-home/build/debug/build/aws-lc-sys-4c33c964f163c571/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-64b47a9d246aab62/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-nickel-50eec70fb8e68069/out" "-L" "/work/build-cache/cargo-home/build/debug/build/blake3-12a86e91b52050e7/out" "-L" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/work/build-cache/cargo-home/build/debug/deps/minimal-3f16035553648b4a" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: /usr/bin/ld: final link failed: No space left on device
          collect2: error: ld returned 1 exit status
          

error: could not compile `minimald` (bin "minimald") due to 1 previous error
error: could not compile `minimal` (lib test) due to 1 previous error
error: command `/usr/bin/cargo '--color=auto' test --no-run --message-format json-render-diagnostics --package minimal --package minimald --package minimald-rpc --package minvmd --locked` exited with code 101

Review threads the coder judged:

  • 👎 invalid — crates/minimald/src/box_actor/ports.rs:627: The task says a host_ip box's NeedsNoExposing refusal stays unaudited, because it decides nothing. The proof is the ask box with no switch, which a none box now delivers: min holds its name at create, so the host finds its box id and writes the record.
  • 👍 valid — crates/minimald/src/box_actor/ports.rs: answer_expose_because no longer reads the box record for the key. It always passes the box id and control channel. The switch route still wins where it exists, so an unreadable record cannot drop the host copy.

On a VM host, min now holds a none box's name with the VM host daemon at create, under the box's id. When that ask box's min net expose is refused because nobody is attached, the host finds the box by its id and its audit log holds an expose_decision record for it. A yes answer in the ask dialog writes an admission record in the same log.

Notes from the agent: create.rs: a none box on a VM host now takes the same HoldBoxName request as a host_ip box (new holds_name_with_vm_host), after the session is active and for a kept session. Its name answers NODATA in the zone, as a non-resident host_ip hold does, and the destroy releases it through the existing release_held_box_name path (it releases any box with no addresses). Residual: rehold_held_name_before_attach in vm_host.rs (outside the layers) re-holds only host_ip names, so after a VM host daemon restart a none box's hold is not re-made, and its reports are refused until the box is recreated. The control.rs test now seeds the named box as a none box through the host-door HoldBoxName request and asserts its ask/daemon/refused expose_decision record. ports.rs: the host report key always carries the box id; report_expose_decision still prefers the switch route when one exists. cargo clippy -p minimald -p minimal --all-targets printed no warnings. I did not run just ci or clippy-strict.

@norrietaylor
Copy link
Copy Markdown
Member

/revise Round 6 lost its container, so this replaces it. The tests lane fails five minimald tests at this head (run 41867378425): box_actor::tests::refused_admission_report_leaves_no_partial_mapping, box_actor::tests::stop_unexposes_before_withdrawing_the_report, box_runtime::tests::ask_no_or_no_tty_records_nothing, box_runtime::tests::ask_yes_publish_withdraws_on_unexpose and box_runtime::tests::unaudited_allow_keeps_a_forward_the_switch_would_not_unbind. Each one reads an unexpected ReportExposeDecision { switch_address: None, box_id: Some(..) } on the control channel, ahead of the AdmitPort or AdmitAsk it expects for a box at switch address 100.64.128.21. So a box that has a switch address is taking the no-switch path. The by-id report must go only for a box with no switch address. Fix that in the code and leave those tests unchanged.

A box with a switch address reports only by its route again. When the
box record cannot be read, the reported switch address decides.
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The compare from f1e89da6 changes only the guest's choice of host report key, plus a doc comment move in a test. A box with a switch address reports by its route again. A box with no switch address reports by box id. When the box record cannot be read, the reported switch address decides. NET-046 conforms to §7.1 lines 244 and 265, as it did at the PASS on f1e89da6. No conditions.

Ruled at 42590fe against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minimal/src/cmd/boxes/create.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 7 of 10 on a /revise note, pushed as 42590fedca5a.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-620e... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-5235... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-7b7b... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 101 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

2b62,libcmov-c553b7ae0509e338,libzeroize-aacfadb4f0a1ef22,libtypenum-aff3b00d0fede5bd,libserde_json_lenient-92929ecc2f1def0c,libitoa-cbb854fdc2dced63,libryu-2773a0b4dd1d509e,libglobset-f217781657d82ea3,liblog-988c240361c9c66b,libbstr-e83fb879b6622143,libregex_automata-4c439c3d322954af,libregex_syntax-51960efa806714c4,libaho_corasick-ebfce1742b109ce1,libmemchr-fca3302d1b4cf9f9,libwalkdir-78bf723f3e841c1f,libsame_file-7e2274c175d94bd9,libnutype-fe098727c755029a,libclient_config-35b779d6f0be8a91,libtoml-c5cc97c5a19b6998,libtoml_writer-fd6080d9260a89b5,libtoml_parser-4b46d8295a313e9d,libwinnow-72d9fcc36f7ddb90,libserde_spanned-612a6723e4e45310,libtoml_datetime-17cb3073aa4a055d,libtracing-be36af57e81cc6b6,libpin_project_lite-b723086f0fbdf6a7,libtracing_core-0c0618d180a921e1,libonce_cell-c4bdd1ba748758be,libportable_atomic-9c00c7ad23fc8f5a,libpaths-4301b8e4efa75112,libdirs-ad046feead606c63,libdirs_sys-316570a9bbf11722,liboption_ext-4b59d732c42732bc,libthiserror-31ad7b029b93b712,libcamino-4fa3f6c669db4b97,libuuid-19bd2dbddf178337,libgetrandom-9b991c0996fd79a1,liblibc-34425f9a644580bc,librand_core-74a7e5adabb8eba8,libcfg_if-218cf50e72d02d86,libserde-f4c387cd7fdceb6d,libserde_core-f631b432aa5c92dc,libanyhow-31e14b5c13385b8c}.rlib" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/work/build-cache/cargo-home/build/debug/deps/rustcLgZ9xQ/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/work/build-cache/cargo-home/build/debug/build/zstd-sys-3df630743cf715ff/out" "-L" "/work/build-cache/cargo-home/build/debug/build/ring-3c60c1595d114743/out" "-L" "/work/build-cache/cargo-home/build/debug/build/aws-lc-sys-4c33c964f163c571/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-64b47a9d246aab62/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-nickel-50eec70fb8e68069/out" "-L" "/work/build-cache/cargo-home/build/debug/build/blake3-12a86e91b52050e7/out" "-L" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/work/build-cache/cargo-home/build/debug/deps/minimal-3f16035553648b4a" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: /usr/bin/ld: final link failed: No space left on device
          collect2: error: ld returned 1 exit status
          

error: could not compile `minimal` (lib test) due to 1 previous error
error: command `/usr/bin/cargo '--color=auto' test --no-run --message-format json-render-diagnostics --package minimal --package minimald --package minimald-rpc --package minvmd --locked` exited with code 101

Review threads the coder judged:

  • 👎 invalid — PRRT_kwDOPdfcxM6zsQy_: The task proof names an ask box with no switch address, not a host_ip box. That box reaches answer_expose_because and reports by id. A host_ip box's NeedsNoExposing stays unaudited on purpose, as the task says. The daemon names every box (BOX-163, daemon_box_name in create.rs), so a none box goes (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — crates/minimal/src/cmd/boxes/create.rs:3818: Moved none_box_holds_its_name_with_the_vm_host above the BEP-017 doc block, so that doc is on dns_steering_is_admitted_where_the_switch_resolves_boxes again.

After an ask box on a VM host with no switch address is refused as not attached, the guest sends its report by box id, and the host's audit log holds a record for it. A yes answer in the ask dialog also writes an admission record on the host. A box with a switch address reports only by its switch route, as before.

Notes from the agent: The CI failure came from commit ae01543. It gave every box the box-id key, so a switch box with no registered route took the by-id path. answer_expose_because now gives the box-id key only when the box record has no box_addresses. When the record cannot be read, the key follows self.reported_switch_address. The five failing minimald tests pass unchanged, in the same run as the minimald test line. I also ran the two create.rs tests (cargo nextest run -p minimal none_box_holds_its_name_with_the_vm_host dns_steering_is_admitted_where_the_switch_resolves_boxes, exit 0), and cargo clippy -p minimald --all-targets is clean.

The dashboard destroy and min session rename test holds_name, which
covers only a host_ip box, so they do not release or move a none box's
name hold. The comment claimed every destroy releases it.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/minimal/src/cmd/boxes/create.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS_WITH_CONDITIONS · 2 conditions · the full ruling

The code is the same as at the PASS on 42590fed. The new comment is accurate. It states that the dashboard's destroy and min session rename do not release or move a none box's name hold. Section 7.1 line 255 says an unknown name answers NXDOMAIN, and line 265 says the host audits the decision. C1 makes vm_host::holds_name cover none boxes. C2 makes the re-hold before an attach cover them, so a report after a VM host daemon restart still gets an audit record. File each as an issue and name it in the PR body, or make the edits.

Ruled at 15cf459 against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

Comment thread crates/minimal/src/cmd/boxes/create.rs Resolved
Comment thread crates/minimal/src/cmd/boxes/create.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 8 of 10 on a failed check suite, pushed as 15cf4598043f.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-55ee... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-1722... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-09e1... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 100 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

d1b",
                "span_id": "a0a29a3aa2fbcc12",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetPaths",
                "parent_span_id": "2e3cbe7e63062d1b",
                "span_id": "35471a8f5ffd9dad",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "GetRecord",
                "parent_span_id": "2e3cbe7e63062d1b",
                "queued_behind": "0",
                "span_id": "3bb42814cafcd9e0",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "DeleteSession",
                "parent_span_id": "2e3cbe7e63062d1b",
                "queued_behind": "0",
                "span_id": "3b69a25ea542dffe",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "Destroy",
                "parent_span_id": "3b69a25ea542dffe",
                "span_id": "4ea0440614a021a6",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "HandOverName",
                "parent_span_id": "3b69a25ea542dffe",
                "queued_behind": "0",
                "span_id": "1692953389e012d7",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
    ]
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

  Cancelling due to test failure: 3 tests still running
        PASS [   2.384s] ( 899/2999) minimald box_actor::tests::a_box_nobody_handed_an_address_publishes_at_the_answerers_grant
        PASS [   2.404s] ( 900/2999) minimald box_actor::tests::a_box_re_registered_inside_the_landing_window_keeps_its_grant
        PASS [   2.366s] ( 901/2999) minimald box_actor::tests::a_contended_walk_never_replaces_a_present_verdict
────────────
     Summary [  47.288s] 901/2999 tests run: 900 passed, 1 failed, 27 skipped
        FAIL [   3.329s] ( 898/2999) minimal::otel_cli exec_run_attach_and_task_run_share_the_commands_trace
warning: 2098/2999 tests were not run due to test failure (run with --no-fail-fast to run all tests, or run with --max-fail)
error: test run failed

Review threads the coder judged:

  • 👎 invalid — crates/minimal/src/cmd/boxes/create.rs:363: The gap is real, but its fix is outside this task's layers. holds_name in crates/minimal/src/cmd/boxes/vm_host.rs must include a none box on a VM host. Its callers in dash/rpc.rs:385 and cmd/session.rs:736 then release and move the hold. The unnamed none box case is also open. This round corre

On a VM host, a none ask box refused as not attached sends its expose decision by box id, and the host audit log holds an expose_decision record for it. A yes answer in the ask dialog writes an admission record to the same host audit log. The three tests pin both records.

Notes from the agent: Revise round 8. The failed check is test-kvm, which runs scripts/session-e2e.sh against a VM (ci-linux-kvm.yml). The log excerpt in the directive holds only script source lines and job cleanup, and names no failing step. This box has no /dev/kvm, so I could not run the VM lane. I read the e2e legs that this PR touches: the ask legs (yes, no, nobody attached), the deny box's expose_decision count, and the allow box's admission line. I found no assertion that the new records break. So this round makes no change for the failed check, and a person must read the full test-kvm log to find the failing step. A rerun can show if the failure is flaky. The three test: lines pass on the merged head (3e3ac08 plus this commit). The one commit this round corrects the comment in create.rs, which said that every destroy releases a none box's name hold. The thread's code fix is outside the layers: it needs holds_name in crates/minimal/src/cmd/boxes/vm_host.rs to include a none box on a VM host. That change also covers its callers in crates/minimal/src/dash/rpc.rs (destroy at 385, re-hold at 430 and 643) and crates/minimal/src/cmd/session.rs:736 (rename). A follow-up task needs it, and it must also decide how an unnamed none box is held, because the host has no record of it now.

@norrietaylor
Copy link
Copy Markdown
Member

/revise Deliver arch C1 and C2 of the ruling at 15cf4598. crates/minimal/src/cmd/boxes/vm_host.rs and crates/minimal/src/tests.rs are now in the task's layers. C1: holds_name returns true for NetworkMode::NoNet as well as HostNet when box_addresses is None, with a test for the dashboard destroy and for min session rename, and the create.rs comment updated. C2: rehold_held_name_before_attach re-holds a named none box's name, and the egress re-admit stays HostNet only.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

That /revise is held, not dropped. The container platform had no instance to give the coder: its pool of prepared containers was full, which happens most just after a deploy. Your note is kept and asked again every 10 minutes for up to 30; a wait that outlives that is refused here with the reason.

What the platform said

could not start the coder: Error: there is no container instance that can be provided to this durable object

Nothing to type again: the command gets a rocket when its round starts, or a refusal that names the reason. No round was counted for it yet.

holds_name is now true for a named none box with no row, as for a
host_ip box, so the dashboard's destroy releases its hold and a rename
moves it. An attach or exec re-holds a none box's name after a VM host
daemon restart. The egress re-admit stays for host_ip boxes only.
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

This head closes the open item from the ruling on 15cf4598. A none box's name hold is the host's record of its box id. The dashboard's destroy now releases that hold, a rename moves it, and an attach or exec re-holds it after a VM host daemon restart. Egress stays for host_ip boxes only. Section 7.1 line 265 requires the host to audit each decision locally, and the host now keeps the record that the audit needs. NET-046 conforms. There are no conditions.

Ruled at e38a0bc against gominimal/arch@7a86ec7bb3c0 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 11 unresolved threads — minimal-arch[bot] crates/minvmd/src/control.rs, minimal-arch[bot] crates/minimald/src/net/listeners.rs, minimal-arch[bot] crates/minimald/src/net/listeners.rs, minimal-arch[bot] crates/minvmd/src/control.rs:2845, minimal-arch[bot] crates/minvmd/src/control.rs, …. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 9 of 10 on a /revise note, pushed as e38a0bc0b79a.

line exit tail
cargo nextest run -p minimald no_switch_box_reports_expose_decision_by_id 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-f188... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd expose_decision_recorded_for_no_switch_box 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-88ca... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling version v0.8.0 (/workbench/crates/version) Compiling minvmd v0.8.0 (/workb
cargo nextest run -p minvmd ask_yes_writes_admission_line 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-9163... Compiling version v0.8.0 (/workbench/crates/version) Compiling mlog v0.0.1 (/workbench/crates/mlog) Compiling minvmd v0.8.0 (/workbench/crates/minvmd) Finished 'test' profil

Verify min task run agent-verify exited 101 under rustc 1.99.0, which the repository does not pin (1.99.1 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

2b62,libcmov-c553b7ae0509e338,libzeroize-aacfadb4f0a1ef22,libtypenum-aff3b00d0fede5bd,libserde_json_lenient-92929ecc2f1def0c,libitoa-cbb854fdc2dced63,libryu-2773a0b4dd1d509e,libglobset-f217781657d82ea3,liblog-988c240361c9c66b,libbstr-e83fb879b6622143,libregex_automata-4c439c3d322954af,libregex_syntax-51960efa806714c4,libaho_corasick-ebfce1742b109ce1,libmemchr-fca3302d1b4cf9f9,libwalkdir-78bf723f3e841c1f,libsame_file-7e2274c175d94bd9,libnutype-fe098727c755029a,libclient_config-35b779d6f0be8a91,libtoml-c5cc97c5a19b6998,libtoml_writer-fd6080d9260a89b5,libtoml_parser-4b46d8295a313e9d,libwinnow-72d9fcc36f7ddb90,libserde_spanned-612a6723e4e45310,libtoml_datetime-17cb3073aa4a055d,libtracing-be36af57e81cc6b6,libpin_project_lite-b723086f0fbdf6a7,libtracing_core-0c0618d180a921e1,libonce_cell-c4bdd1ba748758be,libportable_atomic-9c00c7ad23fc8f5a,libpaths-4301b8e4efa75112,libdirs-ad046feead606c63,libdirs_sys-316570a9bbf11722,liboption_ext-4b59d732c42732bc,libthiserror-31ad7b029b93b712,libcamino-4fa3f6c669db4b97,libuuid-19bd2dbddf178337,libgetrandom-9b991c0996fd79a1,liblibc-34425f9a644580bc,librand_core-74a7e5adabb8eba8,libcfg_if-218cf50e72d02d86,libserde-f4c387cd7fdceb6d,libserde_core-f631b432aa5c92dc,libanyhow-31e14b5c13385b8c}.rlib" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-*,libpanic_unwind-*,libobject-*,libmemchr-*,libaddr2line-*,libgimli-*,libcfg_if-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libminiz_oxide-*,libadler2-*,libunwind-*,liblibc-*,librustc_std_workspace_core-*,liballoc-*,libcore-*,libcompiler_builtins-*}.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-L" "/work/build-cache/cargo-home/build/debug/deps/rustc7yKS1D/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/work/build-cache/cargo-home/build/debug/build/zstd-sys-3df630743cf715ff/out" "-L" "/work/build-cache/cargo-home/build/debug/build/ring-3c60c1595d114743/out" "-L" "/work/build-cache/cargo-home/build/debug/build/aws-lc-sys-4c33c964f163c571/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-64b47a9d246aab62/out" "-L" "/work/build-cache/cargo-home/build/debug/build/tree-sitter-nickel-50eec70fb8e68069/out" "-L" "/work/build-cache/cargo-home/build/debug/build/blake3-12a86e91b52050e7/out" "-L" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/work/build-cache/cargo-home/build/debug/deps/minimal-3f16035553648b4a" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: /usr/bin/ld: final link failed: No space left on device
          collect2: error: ld returned 1 exit status
          

error: could not compile `minimal` (lib test) due to 1 previous error
error: command `/usr/bin/cargo '--color=auto' test --no-run --message-format json-render-diagnostics --package minimal --package minimald --package minimald-rpc --package minvmd --locked` exited with code 101

Review threads the coder judged:

  • 👍 valid — crates/minimal/src/cmd/boxes/create.rs:2716: holds_name in vm_host.rs is now true for a none box with no row, so the dashboard destroy releases its hold and min session rename moves it.
  • 👍 valid — crates/minimal/src/cmd/boxes/create.rs: Made vm_host::holds_name true for NoNet with no box_addresses, and updated the create.rs comment to say destroy releases, rename moves, and attach re-holds the hold.
  • 👍 valid — crates/minimal/src/cmd/boxes/create.rs:357: rehold_held_name_before_attach now re-holds a named none box's name. The egress re-admit stays HostNet only, and a new test pins both.

After an ask box on a VM host with no switch address is refused as not attached, and after a yes answer in the ask dialog, the audit log on the host holds a record for each. A named none box's name hold, which is the host's record of its box id, is now released by the dashboard's destroy, moved by min session rename, and re-held by an attach or exec after a VM host daemon restart.

Notes from the agent: C1: holds_name is now true for NoNet as well as HostNet when box_addresses is None. The dashboard destroy, the dashboard rename, min session rename and the dashboard activate all gate on it. The create.rs comment now says these paths release, move and re-hold the hold. The test in tests.rs pins the predicate that the dashboard destroy and the rename paths gate on. It does not drive those paths end to end, because they need a full dashboard Host or a CLI daemon connection. C2: rehold_held_name_before_attach now continues for any record that holds_name accepts. The egress re-admit runs only for HostNet. rehold_undo now hands a hold back to any live session that holds_name accepts, so a none box that took a name over keeps its hold. The new vm_host.rs test pins one hold request and no admit for a none box. I did not run just clippy-strict.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approved

Verdict APPROVE — every review thread is resolved; @norrietaylor closed the last one. The review of e38a0bc0b79a withheld this approval on the threads open then. Posted as a comment: the App cannot approve its own pull request; the check and the label carry it.

@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read at e38a0bc. The disputed threads are met at this head: a none box holds its name, holds_name and the re-hold cover it, and report_expose_decision falls back to the box id. Arch passes.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Nov 4, 2026
@norrietaylor
norrietaylor added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit 090a624 Nov 4, 2026
42 of 44 checks passed
@norrietaylor
norrietaylor deleted the agent/2929-expose-decisions-for-a-box-with-no-switch-and-an branch November 4, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T134 — Expose decisions for a box with no switch, and answered asks, reach the host's audit log

1 participant

0