Repository navigation
T84 — Record a box's runtime-admitted ports in the VM host daemon's table on the in-VM daemon's report, within the host-held grant - #1960
Conversation
The box control protocol gains the in-VM daemon's two report verbs — admit-port and withdraw-port — as fixed, size-bounded messages carrying the row key, the port, the protocol and the reporting source, and a read-only row verb keyed by the box's name (NET-138, NET-045). The read answers with the row's switch address, its derived egress allow-list and its declared and runtime-admitted ports, or says no row is held under the asked name. The report channel's host-side port is pinned here because both ends of it depend on this crate. The new reply shapes are disjoint from every older one by a required field each carries, so the untagged reply keeps its discrimination and old documents decode exactly as they did.
…hen its 7200s ran out the coder's uncommitted work as it stood when its 7200s ran out The coder was stopped at the end of its time with these changes not committed. The runtime committed its working tree as it stood so the work is kept: this commit is a snapshot, not a finished step, and may not build or pass its tests.
…ox-s-runtime-admitted-ports-in-the-vm
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
2 similar comments
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
The branch's last round committed mid-edit: the report verbs did not compile (the dispatch passed borrowed requests to owned-parameter handlers, the refusal enum derived Copy over a String, the row derived PartialEq over its interior mutex half, an admit report moved the box's name into the rangeless closure), and the tests that had been written against the finished shapes failed (the outside-range sentence was read from the shadowed rangeless refusal; the refused-reports-record-nothing assertion ran after the range's boundaries had recorded; the source field rendered quoted where the test read it bare; the pre-existing register/withdraw round-trips' reply matches went non-exhaustive). The gate's row also admitted only declared ports: a runtime port the grant recorded never joined the set a publish at the box's address is admitted by, so the publish a runtime admission exists to make was refused. The row's admitted set is now its declared ports plus its recorded runtime ports, and the missing gate test pins the whole arc: the publish of a reported port is applied, a port inside the grant no report recorded stays refused, and the withdrawal report ends the admission.
|
Design review (ARCH): the ask-yes admission must be host-recorded. This needs addressing before merge. As drafted, the in-VM daemon reports a port "answered yes under ask", and the host checks that report only against the row's stance and range. Spec 18's host-table comment (:605) says the ask-yes set is what the client records in the table. In other words, the host side records it, not the guest. With the check as drafted, a guest that escapes its box under Required: one of these.
Either way, the guest's own report must not be enough to admit under Also in T84's scope, per the T82 ruling (#1959):
These apply once T82 lands. T84's plan block is being updated to match. |
The host-side registration now carries the dynamic_ingress stance and the dynamic_allowed_range from the same create inputs the session record holds (NET-045), so the VM host daemon's published row holds every runtime port report against the host-held grant instead of a guest-supplied claim. The reply matches cover the read-row and port-report shapes the wire added: neither is an answer to a registration or a withdrawal, so both leave the row published and the line says so.
The manual PartialEq took both rows' runtime-ports locks in one expression, so a row compared with itself deadlocked — and the table's live rows are the registrations' own Arcs, which is exactly what the existing table tests compare. Take each side's ports under one lock at a time, and compare the ports alone: the rate window beside them is the limiter's bookkeeping, not the row's identity.
The status subcommand gains --row <name>: the read-only row verb's own client, printing the reply the VM host daemon's table answers with — the row's switch address, egress allow-list and declared and runtime-admitted ports while a live box holds the name, the no-row marker once none does — as JSON with --json or a human line. The exit code carries the verdict (0 a row, 1 the no-row marker) so a script can watch a row go without parsing the reply.
… 7200s ran out the coder's uncommitted work as it stood when its 7200s ran out The coder was stopped at the end of its time with these changes not committed. The runtime committed its working tree as it stood so the work is kept: this commit is a snapshot, not a finished step, and may not build or pass its tests.
…ox-s-runtime-admitted-ports-in-the-vm
|
Round 1 of 1 on a failed check suite, pushed as The coder ran out of its 7200s before it finished. If it left work uncommitted, its last commit is the runtime's snapshot of that work as it stood — the commit message says so — and not a finished step: it may not build or pass its tests.
Verify Outside the task's
Converted to a draft: something above needs a person before review. On a VM-backed host, a port published at runtime by an allow, a yes answer or a permitted listener is admitted at the host-side gate only after the VM host daemon has checked it against the stance and range it holds from the host-side registration; an admit report outside that grant, past the per-row cap or over the per-row admit rate is refused and the in-VM publish unwinds, while a withdraw report is accepted under the rate limit; withdrawal clears the host-side admission; and each recorded admission has a host-side audit copy. A host-side read of a box's row shows the switch address and egress allow-list its registration produced while the box lives, and no row once it is destroyed. Commit headers reworded by the runtime to fit commitlint. The header the coder wrote is the first paragraph of each of these commits' bodies:
|
- control: stop binding guest-control.sock at runtime; T94 binds the guest
door with the vsock 7352 bridge. Tests serve the guest door directly.
- box_registry: refuse a guest admit report under `ask` with a typed
AskNotHostRecorded refusal ("ask-yes must be host-recorded").
- egress_gate: key the runtime half of a row on (port, protocol), so an
admitted 3000/udp does not admit a 3000/tcp publish or retraction.
- control: create the audit copy owner-only (0600).
- box_registry: resolve row_by_name under the alias rule, newest live
creation first, since the table does not hold names unique.
…ox-s-runtime-admitted-ports-in-the-vm
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a conflict with the base branch). A person decides now. Remove |
|
/review |
|
Reviewed: 1 finding, none blocking at Address the comments on the diff. Clobber: none: |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was the runtime's review findings). A person decides now. Remove |
T84 — Record a box's runtime-admitted ports in the VM host daemon's table on the in-VM daemon's report, within the host-held grant
slice: S13
kind: task
covers: NET-138
size: M
est: 300 code, 360 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: the VM host daemon's log carries one line per admit or withdraw report with the box, the port, the protocol, the reporting source (expose, ask or listen) and the outcome, including each refusal against the host-held grant
observability: one info line per recorded admission or withdrawal and one warn line per report refused against the host-held grant; minvmd appends an owner-only host-side copy of each recorded admission to its own audit log
layers:
- schema:
- crates/minimald-rpc/src/lib.rs: the box control protocol gains admit-port and withdraw-port verbs as a fixed, size-bounded message carrying only the row key, the port and the protocol, and a read-only row verb keyed by the box's name that returns the row's switch address, its derived egress allow-list and its declared and runtime-admitted ports, or says no row is held
test: cargo nextest run -p minimald-rpc box_control_admit_and_withdraw_round_trip
test: cargo nextest run -p minimald-rpc box_control_read_row_round_trip
- vmhost:
- crates/minvmd/src/box_registry.rs: a row holds the box's dynamic_ingress stance and allowed range from the host-side registration and its runtime-admitted ports keyed by port and protocol; an admit report is recorded only when the row exists, its stance is allow and the port is inside its range, and is refused otherwise; under ask, an admit report from the guest is refused with the typed reason that the ask yes must be host-recorded, until the host records it (T94); an admit report is also refused once the row holds the per-row cap of runtime ports (256) or exceeds the per-row admit rate (10 per second); a withdraw report is never refused by the cap or rate; a withdraw report removes the port; a row's withdrawal clears its runtime ports; a name resolves to the newest live box under it
- crates/minvmd/src/control.rs: carries the admit-port and withdraw-port verbs' handler for the guest report door, enforced per door and driven directly in tests, but binds no guest report door at runtime — T94 binds it with the vsock bridge; writes an owner-only (0600) host-side audit copy for each recorded admission; serves the read-only row verb on the host control socket only, whose owner-only file mode is its access control, and refuses it on the guest report door; the verb changes no state; when no box under the name is live the read reports no row, never a destroyed box's last row
- crates/minvmd/src/cmd/status.rs:
minvmd status --row <name> --jsonprints the read-only row verb's reply- crates/minvmd/src/main.rs:
minvmd statusgains the--row <name>flag- crates/minvmd/src/net/egress_gate.rs: the gate's admitted set for a box is its declared ports plus the runtime ports recorded under the request's protocol, so a port admitted for udp never admits tcp
test: cargo nextest run -p minvmd admit_report_recorded_within_host_grant
test: cargo nextest run -p minvmd admit_report_refused_outside_range_or_under_deny
test: cargo nextest run -p minvmd ask_admit_from_guest_refused_without_host_record
test: cargo nextest run -p minvmd admit_report_refused_past_row_cap_or_rate
test: cargo nextest run -p minvmd runtime_port_withdrawn_on_report_and_on_row_withdrawal
test: cargo nextest run -p minvmd admitted_runtime_port_reaches_the_box_through_the_gate
test: cargo nextest run -p minvmd admitted_runtime_udp_port_does_not_admit_tcp
test: cargo nextest run -p minvmd read_row_reports_switch_address_allow_list_and_ports
test: cargo nextest run -p minvmd read_row_refused_on_guest_channel
test: cargo nextest run -p minvmd read_row_absent_for_destroyed_name
test: cargo nextest run -p minvmd row_by_name_resolves_newest_live_creation
test: cargo nextest run -p minvmd audit_copy_created_owner_only
- cli:
- crates/minimal/src/cmd/session.rs: the host-side registration carries the box's dynamic_ingress stance and allowed range, from the same create inputs the session record holds
test: cargo nextest run -p minimal registration_carries_dynamic_ingress_grant
produces: none
consumes:
- crates/minvmd/src/box_registry.rs
- crates/minvmd/src/control.rs
proof: The VM host daemon holds each box's dynamic-ingress grant from the host-side registration and records a runtime port only inside it: an admit report outside the stance or range, past the per-row cap or over the per-row admit rate is refused, an ask-stance admit from the guest is refused until a host-recorded yes exists, a withdraw report is never refused by the cap or rate, a row's withdrawal clears its runtime ports, and each recorded admission has an owner-only host-side audit copy; the gate admits a runtime port only under the protocol it was recorded for. A host-side read of a box's row shows its switch address, egress allow-list and ports while the newest live box of that name lives, and no row once it is destroyed. The guest report door is inert until T94 binds and bridges it.
depends on:
- blocked by #1669
- blocked by #1663
- blocked by #1896
Scope after the split
The plan split T84 to fit the walk's file bound. This PR is the host-held half, and the block above is the current scope:
minvmd status --row;Files beyond the block's layers. The diff touches more files than the block lists.
crates/minimal/src/cmd/session.rsis in scope: it is the creator-side fill of the dynamic_ingress grant into the host registration (NET-138: the grant comes from the host-side creator's expanded spec, never from the guest), plus the exhaustive reply arms. The other extras (crates/minvmd/src/cmd/run.rs,bep_stub.rs,crates/minvmd/tests/common/mod.rs) carry fixture fields only. None of this is T94's bridge or report path.The guest door is inert at runtime. The admit-port and withdraw-port verbs are served per door and covered by tests, but minvmd binds no guest report door in production. Nothing can reach the admit path from a guest until #1967 (T94) bridges the in-VM daemon's report vsock port and binds the door behind it. T94 also carries the daemon's report-and-unwind and the allow and listen e2e legs.
An ask admit arriving on the guest door is refused (
AskNotHostRecorded) until the host records the human's answer: #1969 (T96), on the client-owned attach relay of #1968 (T95).Proof
On a VM-backed host, a port published at runtime by an allow, a yes answer or a permitted listener is admitted at the host-side gate only after the VM host daemon has checked it against the stance and range it holds from the host-side registration; an admit report outside that grant, past the per-row cap or over the per-row admit rate is refused and the in-VM publish unwinds, while a withdraw report is accepted under the rate limit; withdrawal clears the host-side admission; and each recorded admission has a host-side audit copy. A host-side read of a box's row shows the switch address and egress allow-list its registration produced while the box lives, and no row once it is destroyed.
Commit headers reworded by the runtime to fit commitlint. The header the coder wrote is the first paragraph of each of these commits' bodies:
feat(minimald-rpc,minvmd): the coder's uncommitted work as it stood when its 7200s ran outVerification at
ab23ded5The coder's run timed out, and its test table and
agent-verifyoutput (21 compile errors) described a snapshot that no longer exists. The branch was finished by hand (71dbacd), then merged with main (ab23ded). At this head:ci-success,ci-linux-native-success,ci-linux-kvm-success,ci-macos-success,ci-shell-installer-success,ci-kani-success).just fixandjust clippy: clean.just test(macOS scope): 984 passed.-p minimal --lib -- cmd::session: 22 passed.registration_carries_dynamic_ingress_grant. Also added:ask_admit_from_guest_refused_without_host_record,admitted_runtime_udp_port_does_not_admit_tcp,audit_copy_created_owner_onlyandrow_by_name_resolves_newest_live_creation.vm_backed_expose_reports_admission_to_hostandrefused_admission_report_leaves_no_partial_mapping, and the e2e row-read case. See #1967.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/minvmd/src/cmd/run.rscrates/minvmd/src/net/bep_stub.rsCloses #1897