8000 feat(minvmd): decide the node plane's own traffic by its baseline set by minimal-foundry[bot] · Pull Request #2723 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content
< 8000 script type="application/json" data-target="react-partial.embeddedData">{"props":{"contextRegion":{"crumbs":[{"crumb_type":"organization","label":"gominimal","is_root":false,"href":"/gominimal"},{"crumb_type":"repository","label":"minimal","is_root":false,"href":"/gominimal/minimal"}],"localNavigation":[{"id":"code","icon":"code","label":"Code","href":"/gominimal/minimal","selectedLinks":["repo_source","repo_downloads","repo_commits","repo_releases","repo_tags","repo_branches","repo_packages","repo_deployments","repo_attestations"],"popoverTarget":false,"commandId":"repositories:go-to-code","reactNav":{"appTarget":"code-view","anchor":"code-view-repo-link"},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"issues","icon":"issue-opened","label":"Issues","href":"/gominimal/minimal/issues","selectedLinks":["repo_issues","repo_labels","repo_milestones"],"count":240,"popoverTarget":false,"commandId":"repositories:go-to-issues","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"pull-requests","icon":"git-pull-request","label":"Pull requests","href":"/gominimal/minimal/pulls","selectedLinks":["repo_pulls","checks"],"count":31,"popoverTarget":false,"commandId":"repositories:go-to-pull-requests","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"discussions","icon":"comment-discussion","label":"Discussions","href":"/gominimal/minimal/discussions","selectedLinks":["repo_discussions"],"popoverTarget":false,"commandId":"repositories:go-to-discussions","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"actions","icon":"play","label":"Actions","href":"/gominimal/minimal/actions","selectedLinks":["repo_actions"],"popoverTarget":false,"commandId":"repositories:go-to-actions","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"security-and-quality","icon":"shield","label":"Security and quality","href":"/gominimal/minimal/security","selectedLinks":["security","overview","alerts","policy","token_scanning","code_scanning"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-security","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"insights","icon":"graph","label":"Insights","href":"/gominimal/minimal/pulse","selectedLinks":["repo_graphs","repo_contributors","dependency_graph","dependabot_updates","pulse","people","community"],"popoverTarget":false,"commandId":"repositories:go-to-insights","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}}],"localNavigationUpdateChannel":null,"selectedLink":"repo_pulls"},"owner":null,"headerLogo":{"href":"/","aria-label":"Homepage "},"notifications":{"indicatorMode":"disabled","websocketChannel":null,"fetchIndicatorSrc":"/notifications/indicator","fetchIndicatorEnabled":false},"issues":{"href":"/issues"},"pulls":{"href":"/pulls"},"contributedRepos":{"href":"/repos"},"copilot":{"show":false,"showAgentsButton":false,"showRelaunchAnnouncement":false,"copilotChatUrl":null,"copilotApiUrl":"https://api.githubcopilot.com"},"search":{"show":true,"showCommandPalette":false,"isSearchPage":false,"isJumpToSearch":false,"searchContext":{"scope":"repo:gominimal/minimal","current_repo_name":"minimal","current_repo_nwo":"gominimal/minimal","current_repo_org":"gominimal","user_id":"gominimal"}},"commandPalette":null,"enterpriseBar":{"show":false},"globalTransactionalMessage":[],"payloadsUrl":"/_global-navigation/payloads.json?can_toggle_site_admin_and_employee_status=0\u0026is_admin_mode_on=0\u0026is_ui_opted_out=0\u0026show_ui_opt_out=0\u0026v=8","contextRegionUrl":"/_global-navigation/context-region.json"}}

feat(minvmd): decide the node plane's own traffic by its baseline set - #2723

Merged
norrietaylor merged 37 commits into
mainfrom
agent/2690-decide-the-node-plane-s-own-traffic-by-its-basel
Oct 31, 2026
Merged

norrietaylor merged 37 commits into
mainfrom
agent/2690-decide-the-node-plane-s-own-traffic-by-its-basel

Conversation

@minimal-foundry
@minimal-foundry minimal-foundry Bot commented Oct 28, 2026 •
Copy link
Copy Markdown
Contributor

T103 — Decide the node plane's own traffic by its baseline set

slice: S16
kind: task
covers: NET-085, NET-130
size: L
est: 400 code, 460 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: the diagnostics bundle's VM host daemon log carries the start-up line that names the baseline phase as in force and lists each entry with the addresses it pinned
observability: the VM host daemon logs one line per baseline entry pinned or re-pinned, with the entry, the addresses and the source of the lookup, and its rate-limited drop line names a node-plane frame refused for want of a pin
layers:
- pins:
- crates/minvmd/src/net/baseline.rs: the registry and cache entries become named endpoints taken from the daemon's configured registry and cache, never absent and every change logged; the shipped phase becomes in force, and the posture the policy row and the start-up line spell drops the interim wording
- crates/minvmd/src/net/dns_pins.rs: the node plane's own lookups for a baseline entry pin the addresses they resolved to against that entry, under the same expiry and cap as a box's pins
test: cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers
- gate:
- crates/minvmd/src/net/egress_gate.rs: a frame from the node's own address is admitted only to an address a baseline entry pinned, the host alias and the resolver carve-out; the announced-arm tests move to the in-force arm
test: cargo nextest run -p minvmd unenrolled_baseline_set_from_helper_enumeration
test: cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache
test: cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address
test (deferred: vm): cargo nextest run -p minvmd vm_escape_bounded_to_resident_union
- policy:
- crates/minimal/tests/cli.rs: the policy assertion reads the in-force posture beside the baseline set, so min session policy shows the baseline set as what bounds Minimal's own downloads in the VM
test: cargo nextest run -p minimal policy_shows_baseline_set
produces: none
consumes: none
proof: On a VM host, a process inside the VM that sends from the node's own address reaches the registry and the cache Minimal downloads from, and nothing else outside the resident boxes' rules.

Proof

On a VM host the gate decides every unmarked frame from the node's own address by the node-plane baseline set, and that set is in force. Such a frame reaches the registry (github.com) and the cache (cache.minimal.dev) only at the addresses the node's own lookups pinned, and only on TCP 443. It also reaches the resolver, the host alias at a configured exposure and a resident box's lease. A frame that carries the box mark is decided by the union of the resident host_ip boxes' rules, and dropped when no host_ip box is resident. Any other destination is dropped with a rate-limited egress-node-plane-unpinned line. min session policy shows the set as in force, with the rows registry github.com and cache cache.minimal.dev.

Out of layers: these files carried product code outside T103's layers:. arch_rulings 600 C2 asked the plan entry on #2690 to list them; arch_rulings 602 dropped that condition, and the owner kept the plan as it is. They are here because §7.1 makes the host-side creator own each host_ip box's rules and the gate needs them: crates/minvmd/src/box_registry.rs (admit_host_ip_box, retire_host_ip_box), crates/minvmd/src/control.rs, crates/minimald-rpc/src/lib.rs, crates/minimald/src/rpc.rs, crates/minimal-client/src/attach.rs, crates/minimal-client/src/box_registration.rs, crates/minimal/src/cmd/boxes/create.rs, crates/minimal/src/cmd/boxes/vm_host.rs, crates/minimal/src/dash/rpc.rs and crates/minimal/src/task.rs. The box_registry.rs and egress_gate.rs tests that sent a node frame to 10.1.2.3 now send a resolver query instead.

Design decisions:
(1) The registry entry defaults to github.com (DAEMON_REGISTRY_ENDPOINT), where the in-VM daemon fetches package sources, and the cache entry to cache.minimal.dev (DAEMON_CACHE_ENDPOINT), the host of mctx's DEFAULT_REMOTE_CACHE_READ_URL. MINVMD_BASELINE_REGISTRY and MINVMD_BASELINE_CACHE re-point each category within itself, through with_registry and with_cache, which log each change. Whether github.com belongs in the baseline is gominimal/arch#153.

Tests

line exit tail
cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-8d0a... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd unenrolled_baseline_set_from_helper_enumeration 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-2add... 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
cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f2a3... 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
cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-2595... 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
cargo nextest run -p minimal policy_shows_baseline_set 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-9717... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo

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.

kind": "GetNetSwitch",
                "parent_span_id": "e3e829cc81182935",
                "span_id": "bd84c30c9d31063e",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetDenyAllOptOut",
                "parent_span_id": "e3e829cc81182935",
                "span_id": "de1bfbc1dba3cf16",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetPaths",
                "parent_span_id": "e3e829cc81182935",
                "span_id": "f211de98d99543de",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "GetRecord",
                "parent_span_id": "e3e829cc81182935",
                "queued_behind": "0",
                "span_id": "5bccfe778befcbfb",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "DeleteSession",
                "parent_span_id": "e3e829cc81182935",
                "queued_behind": "0",
                "span_id": "0078654f3d422bc7",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "Destroy",
                "parent_span_id": "0078654f3d422bc7",
                "span_id": "3afb23791b2ec51a",
                "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 [   3.014s] ( 812/1441) minvmd box_registry::tests::a_closed_shuttle_detaches_rows_until_the_grace
        PASS [   2.222s] ( 813/1441) minvmd box_registry::tests::a_reattributed_box_keeps_its_row
        PASS [   5.013s] ( 814/1441) minvmd box_registry::tests::a_registration_refused_for_a_held_address_spends_no_switch_address
────────────
     Summary [  42.588s] 814/1441 tests run: 813 passed, 1 failed, 2 skipped
        FAIL [   3.298s] ( 811/1441) minimal::otel_cli exec_run_attach_and_task_run_share_the_commands_trace
warning: 627/1441 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

Deferred

The plan marked these test lines deferred: the executor lacks the lane, so nothing here ran them and they still owe evidence.

line lane
cargo nextest run -p minvmd vm_escape_bounded_to_resident_union vm: ran at ca1dbf7 on the KVM lane with MINVMD_E2E=1, PASS in 77.9 s (job 124314615395, 39/39 passed)

Out of scope

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

  • crates/minvmd/src/box_registry.rs

Residual

Closes #2690


Generated by Claude Code

The registry and cache baseline entries now name the hosts the
daemon's own fetches go to. The node plane's own lookups pin the
addresses those names resolve to, under the same window and cap as a
box's pins, and every pin and re-pin is logged. The baseline phase
ships in force: a frame from the node's own address reaches a pinned
address, a subnet entry, the resolver, the host alias at a configured
exposure and a resident box's lease, and nothing else. A refused
frame logs a rate-limited line naming the drop for want of a pin.

Covers NET-085 and NET-130 (T103).
min session policy now shows the baseline set as what bounds
Minimal's own downloads in the VM, with each entry naming the host
those downloads go to (NET-130, T103).
@coderabbitai
coderabbitai Bot commented Oct 28, 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: 5699e9af-adf9-4e4e-b1e9-893fc5be584c

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 review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Member

/revise Fix the test-kvm timeout. The last round failed inside the runtime on a git fetch connection reset, so nothing changed since bbe06556.

  • Failing line (test-kvm on bbe06556): TIMEOUT [ 360.005s] ( 7/38) minvmd::guest_classifier_integration guest_classifier_table_present. On main that test takes about 10 s.
  • This PR puts the node-plane baseline in force, so the in-VM daemon's own traffic reaches only the addresses its own lookups pinned. The test's box launch runs a package fetch first, and the hang fits that fetch to cache.minimal.dev being dropped. The PR notes already flag this risk.
  • Find the egress-node drop line in the boot-daemon log of that run (artifact minvmd-linux-boot-log-x86_64), and admit the destination it names in crates/minvmd/src/net/egress_gate.rs / crates/minvmd/src/net/dns_pins.rs. Likely causes are a CNAME or redirect target, or a lookup that does not go through the pin path.
  • Prove it with guest_classifier_table_present under just test-vm, and keep the baseline in force for everything else.
  • Change nothing else. The macOS lane failed only because await-kvm reported the KVM lane red.

Generated by Claude Code

A box launch inside the VM first git-fetches its project's package
source repository (github.com/gominimal/pkgs) through the in-VM daemon.
The baseline named cache.minimal.dev for the registry, so the in-force
gate dropped that fetch for want of a pin and the launch hung.

The registry entry now names github.com. Its lookups pin against the
registry entry. The cache entry keeps cache.minimal.dev, where the
package index and content live (NET-130, T103).
The baseline's registry entry now names the package source host, so the policy view's registry row
and JSON entry name github.com (NET-130, T103).
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-49f4... 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 unenrolled_baseline_set_from_helper_enumeration 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-6071... 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
cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-b8e1... 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
cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-868c... 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
cargo nextest run -p minimal policy_shows_baseline_set 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-0c35... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo

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.

kind": "GetNetSwitch",
                "parent_span_id": "a3ea88bd881243e0",
                "span_id": "13cd500603402292",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetDenyAllOptOut",
                "parent_span_id": "a3ea88bd881243e0",
                "span_id": "e6e07aa30a072e68",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetPaths",
                "parent_span_id": "a3ea88bd881243e0",
                "span_id": "c90d8eb84763179f",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "GetRecord",
                "parent_span_id": "a3ea88bd881243e0",
                "queued_behind": "0",
                "span_id": "c6d550b30d4020d4",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "DeleteSession",
                "parent_span_id": "a3ea88bd881243e0",
                "queued_behind": "0",
                "span_id": "9e7da35ac94f48d8",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "Destroy",
                "parent_span_id": "9e7da35ac94f48d8",
                "span_id": "a7f5bb8dddc8cbd8",
                "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 [   3.019s] ( 812/1441) minvmd box_registry::tests::a_closed_shuttle_detaches_rows_until_the_grace
        PASS [   2.219s] ( 813/1441) minvmd box_registry::tests::a_reattributed_box_keeps_its_row
        PASS [   5.009s] ( 814/1441) minvmd box_registry::tests::a_registration_refused_for_a_held_address_spends_no_switch_address
────────────
     Summary [  37.013s] 814/1441 tests run: 813 passed, 1 failed, 2 skipped
        FAIL [   2.872s] ( 811/1441) minimal::otel_cli exec_run_attach_and_task_run_share_the_commands_trace
warning: 627/1441 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

Outside the task's layers::

  • crates/minvmd/src/box_registry.rs

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

On a VM host, the in-VM daemon's own traffic reaches the package source repository's host (github.com, the registry entry) and the cache (cache.minimal.dev), each only at the addresses its own lookups pinned, and nothing else outside the resident boxes' rules. min session policy shows registry github.com and cache cache.minimal.dev under the in-force baseline set.

Notes from the agent: Revise round 2. I was not able to read the boot-daemon log artifact (no gh or network credential here), and this box has no /dev/kvm. So I did not run just test-vm or guest_classifier_table_present. The cause comes from the code, not from the log line. The test's box launch uploads a minimal.toml with [upstream] repo = "https://github.com/gominimal/pkgs" (crates/minvmd/tests/common/mod.rs PKGS_UPSTREAM, whose comment says the guest daemon fetches it as node-plane traffic). The checkouts crate runs git fetch over HTTPS to github.com. The baseline named cache.minimal.dev for both the registry and the cache, so no entry named github.com, and the in-force gate dropped the fetch as egress-node-plane-unpinned. The DNS pin path itself is sound for this case: the guest resolv.conf points at the gateway (minimald guest.rs install_resolv_conf), queries are UDP, and CNAME chains are followed. Fix: DAEMON_REGISTRY_ENDPOINT is now "github.com". The cache entry keeps cache.minimal.dev. The baseline stays in force for everything else. I updated the tests that pinned the old registry name (baseline enumeration, display, re-point log, start-up line, pin line entry=cache). I added a registry leg to unenrolled_baseline_set_from_helper_enumeration: the daemon's lookup of github.com pins, and its fetch is admitted. Risk: if the CI log names a different destination (for example a source tarball host for a cache miss), this fix does not cover it. The KVM lane must confirm. Printed text changed: the min session policy registry row now reads github.com. Deferred: `cargo next…[truncated]

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on a failed check suite: the coder changed nothing. Round 3 changed nothing, on purpose. The three failed checks do not need a code change on this branch that I can make. (1) commitlint: commit 5b2e973 has a body line of 146 characters, and the limit is 100 (body-max-line-length). The only fix is to reword that commit. The rules forbid me to rewrite commits or force-push. A person can fix it with a squash merge that uses a short body, or with a reword and a force-push of 5b2e973. (2) cargo-deny: the advisory is 'Exponential event growth with ENABLE_WIKILINKS', in pulldown-cmark 0.13.4. Cargo.lock and deny.toml on this branch are the same as on origin/main. This branch does not cause the failure, and main has the same failure. The fix (update pulldown-cmark, or ignore the advisory in deny.toml) is outside this task's layers and must be a separate pull request. The log also shows a missing musl toolchain line, which is a CI image problem, not this branch. (3) ci-success is the aggregator. It fails because the two checks abov…[truncated]

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a failed check suite: the coder changed nothing. No code change this round. None of the failed checks points to code inside this task's layers. (1) commitlint: the body of the existing commit 5b2e973 has a line longer than 100 characters. Only a rewrite of that commit can fix it, and the revise rules forbid a rewrite or a force-push. A person must reword it before the merge, or merge with a squash that uses a new message. (2) cargo-deny: the advisory is on pulldown-cmark ('Exponential event growth with ENABLE_WIKILINKS'). This branch does not change Cargo.lock or deny.toml, so the failure is also on main, and the fix is outside the layers. (3) test-kvm: the job excerpt shows only script source lines and no failing test or error output. I was not able to find the failure, and this box has no /dev/kvm to reproduce it. ci-success and ci-linux-kvm-success are aggregators that fail because of the checks above. All five test lines pass at 5b2e973. Deferred: cargo nextest run -p minvmd vm_escape_bounded_to_resident_union (lane…[truncated]

@norrietaylor
norrietaylor force-pushed the agent/2690-decide-the-node-plane-s-own-traffic-by-its-basel branch from 5b2e973 to 1b3d1e0 Compare October 28, 2026 11:23
Copy link
Copy Markdown
Member

/revise Fix the e2e failure on test-kvm at 5b2e973 (job 122751751056; the tree is unchanged at 1b3d1e0, which only rewords a commit body for commitlint). Change nothing else.

  • Failing check: group "session outbound request proof (NET-107)" in scripts/session-e2e.sh, on the VM lane (E2E_VM=1, --provider local-minvmd). Every curl to https://example.com and https://example.org timed out on connect (exit 28, HTTP:000), six attempts in all: the session has no working egress (NET-107).
  • The gvproxy switch was alive (listening, forwarder expose for 7654 succeeded) and min ls showed the session active with EGRESS per_box, so this is not a lost switch. It fits the same cause as the earlier guest_classifier_table_present timeout: the node-plane baseline this PR puts in force in crates/minvmd/src/net/egress_gate.rs / crates/minvmd/src/net/dns_pins.rs also drops the box's own egress, or the session's package fetch before it.
  • Find the drop line for the session's traffic in the run's boot-daemon log (artifact minvmd-linux-boot-log-x86_64) and in minvmd.log, and scope the baseline to the node plane's own traffic only, so box traffic stays under the box's own verdict.
  • Prove it with just e2e on a KVM host or, at least, a unit test in egress_gate.rs that a box-sourced frame to a public address is not judged by the node-plane baseline.
  • cargo-deny fails on main (RUSTSEC); leave it.

Generated by Claude Code

@minimal-foundry
8000
Copy link
Copy Markdown
Contributor Author

Revise round 4 ran against ci-success, cargo-deny and left it red; a coder round cannot clear it, a person should look. No round was started for the failed suite on 1b3d1e0ee59a.

Copy link
Copy Markdown
Member

/revise (Re-post: round 5 failed inside the runtime on a mip check timeout, so nothing changed.) Fix the e2e failure on test-kvm at 5b2e973 (job 122751751056; the tree is unchanged at 1b3d1e0, which only rewords a commit body for commitlint). Change nothing else.

  • Failing check: group "session outbound request proof (NET-107)" in scripts/session-e2e.sh, on the VM lane (E2E_VM=1, --provider local-minvmd). Every curl to https://example.com and https://example.org timed out on connect (exit 28, HTTP:000), six attempts in all: the session has no working egress (NET-107).
  • The gvproxy switch was alive (listening, forwarder expose for 7654 succeeded) and min ls showed the session active with EGRESS per_box, so this is not a lost switch. It fits the same cause as the earlier guest_classifier_table_present timeout: the node-plane baseline this PR puts in force in crates/minvmd/src/net/egress_gate.rs / crates/minvmd/src/net/dns_pins.rs also drops the box's own egress, or the session's package fetch before it.
  • Find the drop line for the session's traffic in the run's boot-daemon log (artifact minvmd-linux-boot-log-x86_64) and in minvmd.log, and scope the baseline to the node plane's own traffic only, so box traffic stays under the box's own verdict.
  • Prove it with just e2e on a KVM host or, at least, a unit test in egress_gate.rs that a box-sourced frame to a public address is not judged by the node-plane baseline.
  • cargo-deny fails on main (RUSTSEC); leave it.

Generated by Claude Code

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 7 of 10 on a failed check suite: the coder changed nothing. Revise round 7: no code change, on purpose. The fix needs a planning decision, and it is outside this task's layers.

Failed checks:

  1. cargo-deny: the advisories gate fails on a RUSTSEC vulnerability ("Exponential event growth with ENABLE_WIKILINKS") in a dependency. It also warns that RUSTSEC-2026-0009 in deny.toml matched no crate. This branch does not change Cargo.toml, Cargo.lock or deny.toml. The failure is not from this diff. ci-success is red only because of this job.
  2. test-kvm: the excerpt in the directive does not show the failing case. ci-linux-kvm-success, await-kvm and ci-macos-success follow from it. The most probable cause is in this diff. boxes::NetworkMode defaults to HostNet, so a default session in the VM shares the VM's network namespace. Its frames leave from the node address (SwitchSubnet::daemon_ip). With NODE_BASELINE_PHASE = InForce, gate_verdict sends every frame from that address to node_plane_verdict. That function admits only baseline pins, th…[truncated]

Copy link
Copy Markdown
Member

/revise The owner ruled on the planning question from round 7: gate by cgroup, not by address. Change only what this needs.

  • Problem: with NODE_BASELINE_PHASE = InForce, gate_verdict sends every frame from the node address (SwitchSubnet::daemon_ip) to node_plane_verdict. A default session in the VM (boxes::NetworkMode defaults to HostNet) sends from that same address, so its egress is judged as node traffic and dropped. NET-107 fails: the session has no outbound network (e2e job 122751751056).
  • Fix: decide node-plane versus box traffic by the sending process's cgroup / classifier leaf, not by the source address. Traffic from the daemon's own cgroup is node-plane and is judged by the baseline set; traffic from a box's cgroup or leaf is judged by that box's own egress verdict, whatever address it leaves from. Put the identity check where the frame is classified (in-VM classifier marks or the egress gate's lookup), and keep the baseline in force for the daemon's own traffic.
  • Tests: a unit test in crates/minvmd/src/net/egress_gate.rs that a frame from a box's cgroup at the node address is judged by the box's verdict, and one that the daemon's own frame is still judged by the baseline. Prove it with guest_classifier_table_present and the NET-107 e2e group under just test-vm / just e2e if the box can run them.
  • If the change needs files outside T103's layers, list them in a "Layer exception" section of the PR body. Keep every commit message line at or under 100 characters.

Generated by Claude Code

A host-address box, the default network mode, sends from the node
address, so the in-force baseline judged its egress as node traffic and
dropped it (NET-107). The guest classifier now writes the cohort ct mark
into the IPv4 DSCP field: BOX_COHORT_DSCP for a connection a box opened,
and zero on every other packet. The host gate judges a marked frame at
the node address by the node row and the box's classifier leaf, and an
unmarked one by the baseline set, so a guest without the table fails
closed.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 8 of 10 on a /revise note, pushed as ee7e4dca02cc.

line exit tail
cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-ca5d... 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 unenrolled_baseline_set_from_helper_enumeration 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-b338... 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
cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-4f1b... 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
cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-2a7d... 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
cargo nextest run -p minimal policy_shows_baseline_set 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-263c... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo

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.

1f,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,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,librand_core-74a7e5adabb8eba8,libtokio-a9eca338333a2325,libsignal_hook_registry-e265f9d490b018bd,liberrno-291e13a216c83bcc,libsocket2-c988197d1479973c,libbytes-9282c936c0bb502c,libmio-bebfc86ff8b42e2f,liblog-988c240361c9c66b,libparking_lot-1fe9ee26c4ca8e46,libparking_lot_core-233db7ebb1bab9cf,libsmallvec-862665f79eb15c14,liblock_api-c20874736af451c1,libscopeguard-372586e7debb359d,libpin_project_lite-b723086f0fbdf6a7,libserde-f4c387cd7fdceb6d,libnix-86d9ff8b22c0e5cd,libmemoffset-f58e2ae4c2812aa5,libbitflags-9403def895af79cd,libserde_core-f631b432aa5c92dc,libcfg_if-218cf50e72d02d86,liblibc-34425f9a644580bc}.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/rustcZSSqXc/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-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/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/blake3-12a86e91b52050e7/out" "-L" "<sysroot>/lib64/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/work/build-cache/cargo-home/build/debug/deps/minimald-bf138639d97eb3e7" "-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` (lib test) due to 1 previous error
error: command `/usr/bin/cargo '--color=auto' test --no-run --message-format json-render-diagnostics --package boxes --package minimal --package minimald --package minvmd --locked` exited with code 101

Outside the task's layers::

  • crates/boxes/src/core/egress.rs
  • crates/minimald/src/net/classifier.rs
  • crates/minvmd/src/box_registry.rs
  • scripts/install-host-classifier.sh

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

On a VM host, a process inside the VM that sends from the node's own address and is not in a box's cgroup reaches only the registry and the cache that Minimal downloads from: an unmarked frame from that address is judged by the baseline set. A host-address box (the default session) also sends from that address. Its frames now carry the guest classifier's box mark, so the gate judges them by the box's own rules and the session keeps its outbound network.

Notes from the agent: Round 8: gate by cgroup, not by address, as the owner ruled. The guest classifier already gives each new connection a ct mark from the sender's cgroup (the cohort bit for the boxes subtree, the node bit for the rest of the slice). That mark does not leave the VM. So on the guest only, the classifier script now renders a postrouting filter chain, mark_cohort. It sets the IPv4 DSCP field to boxes::core::egress::BOX_COHORT_DSCP (55, RFC 2474 pool 2) on packets of a cohort-marked connection, and clears DSCP on every other packet. Thus no process outside the boxes cohort can wear the mark. FrameSummary now reads DSCP. In gate_verdict, the in-force node-plane arm takes only frames from the node address that do not carry the mark. A marked frame falls through to the node row, and the box's classifier leaf in the guest decides it. The reply, control-surface, infrastructure and proxy-lane floors still apply to it. A guest whose classifier table never loaded sends no marks, so its traffic fails closed to the baseline set. A native host install takes no --cohort-dscp and renders no DSCP chain.

Layer exception (put this in the PR body): the fix needs three files outside T103's layers. crates/boxes/src/core/egress.rs holds the shared BOX_COHORT_DSCP constant and the DSCP read in FrameSummary. crates/minimald/src/net/classifier.rs passes --cohort-dscp in the guest render and updates its argv and digest tests. scripts/install-host-classifier.sh adds the --cohort-dscp option and the mark_cohort ch…[truncated]

Copy link
Copy Markdown
Member

/revise Fix the two failures on ee7e4dc (round 8, gate by cgroup). No round started on its own for this failed suite. Change nothing else.

  • tests (job 123049670569): common::shell_lint all_scripts_pass_shellcheck fails. Run shellcheck on the scripts this branch changed (scripts/session-e2e.sh and any other .sh the diff touches) and fix every finding, without changing behaviour.
  • test-kvm (job 123053442451): the KVM lane fails. Read the failing group or test from that job and fix it if it comes from this branch's change (the cgroup-based node/box split in crates/minvmd/src/net/egress_gate.rs). The goal of the round stays: a default VM session reaches the network (NET-107) while the daemon's own traffic stays under the baseline.
  • Keep every commit message line at or under 100 characters.

Generated by Claude Code

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

That /revise is held, not dropped. A coder is already running on #2690. Your note is kept and goes into the next round here, which starts when that coder finishes.

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.

shellcheck 0.9.0 on the CI runner flags the old form as SC2015.
The accepted range stays 1 to 63.
The guest renders its table with --cohort-dscp, so the native render the
KVM harness compares its load digest against must name the same flag.
@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 28, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: round 11 would exceed the cap of 10; 0 base merges were not counted (the last trigger was a failed check suite). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2690 by hand.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-9ead... 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 unenrolled_baseline_set_from_helper_enumeration 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-02d1... 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
cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-de1c... 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
cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-4ef2... 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
cargo nextest run -p minimal policy_shows_baseline_set 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-b1e2... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo

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.

ent_span_id": "cd4ab39c4c21bb97",
                "span_id": "38e0172e0ad46772",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetDenyAllOptOut",
                "parent_span_id": "cd4ab39c4c21bb97",
                "span_id": "00c2bb7f1cf32e47",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetPaths",
                "parent_span_id": "cd4ab39c4c21bb97",
                "span_id": "65e641781720b2ac",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "GetRecord",
                "parent_span_id": "cd4ab39c4c21bb97",
                "queued_behind": "0",
                "span_id": "f16dd51bea9b1261",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "DeleteSession",
                "parent_span_id": "cd4ab39c4c21bb97",
                "queued_behind": "0",
                "span_id": "3ca31db910396c28",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "Destroy",
                "parent_span_id": "3ca31db910396c28",
                "span_id": "31873207b608166d",
                "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.351s] (1439/3234) minimald box_actor::tests::a_box_destroyed_between_enumeration_and_grant_is_released_once
        PASS [   2.360s] (1440/3234) minimald box_actor::tests::a_box_destroyed_inside_the_landing_window_is_not_resurrected
        PASS [   2.366s] (1441/3234) minimald box_actor::tests::a_box_holding_a_host_row_refuses_remint_after_its_host_loop_ends
────────────
     Summary [  36.098s] 1441/3234 tests run: 1440 passed, 1 failed, 21 skipped
        FAIL [   2.933s] (1438/3234) minimal::otel_cli exec_run_attach_and_task_run_share_the_commands_trace
warning: 1793/3234 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

Outside the task's layers::

  • crates/boxes/src/core/egress.rs
  • crates/minimald/src/net/classifier.rs
  • crates/minvmd/src/box_registry.rs
  • crates/minvmd/tests/guest_classifier_integration.rs
  • scripts/install-host-classifier.sh

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

On a VM host, a process inside the VM that sends from the node's own address reaches the registry (github.com) and the cache (cache.minimal.dev) that Minimal downloads from, and nothing else outside the resident boxes' rules. A box frame that the guest classifier marks is still decided by the box's rules. This round makes the shellcheck gate and the KVM digest check agree with that change.

Notes from the agent: Revise round 10. Two fixes, and nothing else changed.

  1. shellcheck: the tests job runs shellcheck 0.9.0. It flagged SC2015 at the [ -ge 1 ] && [ -le 63 ] || die range check for --cohort-dscp in scripts/install-host-classifier.sh, which this branch added. scripts/session-e2e.sh is clean with 0.9.0 and with 0.11.0 at ee7e4dc and at HEAD, so the finding was not in that file. The check is now an if with the same 1 to 63 range. I ran the script by hand: it refuses 0 and 64, and it accepts 1 and 63.

  2. test-kvm: the job log excerpt names no failing test. I found the cause by reading the code. guest_classifier_integration.rs::guest_loaded_ruleset_matches_native_print_ruleset compares the guest load digest with a native --print-ruleset render. Commit 13ade0b added --cohort-dscp to the guest render (render_guest_ruleset) but not to that native render, so the two digests can no longer match. I rendered both forms by hand and the sha256 values differ. The test now passes --cohort-dscp BOX_COHORT_DSCP. This file is outside the task's layers, and I touched it only because the directive asks for the KVM fix. This box has no /dev/kvm and no libkrun, so the file did not compile here (it is #![cfg(minvmd_libkrun)]). I checked it with rustfmt only, and the KVM lane must confirm it. If the lane still fails after this fix, the next suspect is the baseline: an unmarked daemon fetch, for example a redirect from the cache to another host, gets no pin.

The macOS and aggregator failures only follow the KVM lane (await-kvm).

Deferred: cargo nextest run -p minvm…[truncated]

@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/vm_host.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on the runtime's review findings: the coder changed nothing. Revise round 1: the one unresolved thread is judged invalid for this pull request, so this round changes no code and runs no tests. The gap that the thread names is real, but this branch already documents it as a residual. The doc comments on HostIpResidency::keep in crates/minimal/src/cmd/boxes/vm_host.rs and on BoxRegistry::admit_host_ip_box in crates/minvmd/src/box_registry.rs name the cases, and issue #2747 owns them. The earlier threads PRRT_kwDOPdfcxM6x6vZh and PRRT_kwDOPdfcxM6x6vZt got the same verdict. A daemon-side bound needs a new liveness signal: a host_ip box has no row in the VM host daemon's registry, so the daemon cannot check its residents against live boxes. Neither file is in this task's layers. The deferred test vm_escape_bounded_to_resident_union (lane: vm) did not run on this box.

Review threads the coder judged:

@norrietaylor
norrietaylor marked this pull request as ready for review October 30, 2026 13:47
@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

This compare conforms to the design. An equal re-admit keeps the cohort pins, the gate asks the shared pins once, and a bare IPv4 baseline term narrows the set. The VM escape leg ran at 8206300b, and this compare changes the code that the leg drives. Re-run it at this head and record the job (C1). Amend the plan's T103 layers: to list the out-of-layers files (C2).

Ruled at 49e8878 against gominimal/arch@42d214dc8ac8 by claude-opus-5-5.

Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread crates/minvmd/src/box_registry.rs Resolved
@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.

Own review at 49e8878. The only change since 5c6dba0 is the round-10 commit fix(minvmd): keep the host_ip cohort record on an equal re-admit.

  • admit_host_ip_box now returns before it touches the set when the held rules equal the new ones. HostIpBoxRules derives PartialEq/Eq. The cohort Arc therefore stays, and so do the other boxes' pins and flows. That closes 4422402614.
  • The doc comment states that a real change to the set still rebuilds the entry.
  • host_ip_verdict asks the pins once, after the loop, so a frame counts once (4422402619). An earlier admit by another box's own rules still returns first.
  • baseline_endpoints_from_raw skips a bare IPv4 term with a warning that names <term>/32 (4410130045). This only narrows the set.

I see no design change. My arch verdict is PASS at this head, and C1 stays carried as residual #2747. Still open: security thread 4423599090, which waits on the owner's ruling, and the macOS lane, which waits on #2757.


Generated by Claude Code

@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

This head merges main cleanly, and the PR's own diff did not change since 49e88786. The ruling on that head stands. The node plane is decided by the in-force baseline set, as section 5.1 and section 4.3 rule 2 require. Two items stay open as filed residuals. C1 is #2747: the gate's union can keep a host_ip box that is not live, and section 8 bounds the union to resident boxes. C2 is gominimal/arch#153: the design must decide if github.com is a baseline endpoint.

Ruled at 9bf4c0d against gominimal/arch@42d214dc8ac8 by claude-opus-5-5.

Comment thread crates/minvmd/src/box_registry.rs Resolved
Comment thread crates/minimal/src/cmd/boxes/vm_host.rs Resolved
Comment thread crates/minvmd/src/net/baseline.rs Resolved
@norrietaylor norrietaylor changed the title T103 — Decide the node plane's own traffic by its baseline set feat(minvmd): decide the node plane's own traffic by its baseline set Oct 30, 2026
@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.

Babysitter approval at 9bf4c0d: every merge gate passes at this head (arch PASS at 9bf4c0d, zero open threads, every lane aggregator green). Squash title feat(minvmd): decide the node plane's own traffic by its baseline set and the squashed message pass commitlint. Queued for squash merge.


Generated by Claude Code

…-node-plane-s-own-traffic-by-its-basel

# Conflicts:
#	crates/minimal-client/src/box_registration.rs
@norrietaylor
norrietaylor disabled auto-merge October 30, 2026 21:06
@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 · 1 condition · the full ruling

The merge of main is clean, and it does not change the PR's own work. The host_ip verbs stay on the host door, and main's MintBoxId stays on the guest door. One condition is open. The deferred VM leg vm_escape_bounded_to_resident_union ran at 49e88786, not at this head. Two merges since then changed crates/minvmd/src/control.rs, and the leg drives that file. Section 8 line 310 bounds the escape to the resident union plus the baseline set. Only that leg proves the bound. Run the leg at ca1dbf76 and record the job in the PR body, as C1 says.

Ruled at ca1dbf7 against gominimal/arch@42d214dc8ac8 by claude-opus-5-5.

Comment thread crates/minvmd/src/control.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

A merge of main before round 2, not counted against the cap (1 of 3), pushed as ca1dbf765e09.

Resolved conflicts in: crates/minimal-client/src/box_registration.rs

line exit tail
cargo nextest run -p minvmd baseline_entry_pins_the_daemon_lookup_answers 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-561d... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minvmd unenrolled_baseline_set_from_helper_enumeration 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-295c... 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
cargo nextest run -p minvmd baseline_enumeration_always_carries_registry_and_cache 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-756e... 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
cargo nextest run -p minvmd node_plane_frame_admitted_only_to_pinned_baseline_address 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-f258... 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
cargo nextest run -p minimal policy_shows_baseline_set 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-1994... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling client-config v0.0.1 (/wo

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.

span_id": "4c0e65e0ddf05c44",
                "span_id": "28323fd097210457",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetDenyAllOptOut",
                "parent_span_id": "4c0e65e0ddf05c44",
                "span_id": "e550e87d4b6a17e4",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "GetPaths",
                "parent_span_id": "4c0e65e0ddf05c44",
                "span_id": "604a9291f4fdf76a",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "GetRecord",
                "parent_span_id": "4c0e65e0ddf05c44",
                "queued_behind": "0",
                "span_id": "f70f6c0647e8cbc2",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "sessions.manager.message",
            fields: {
                "kind": "DeleteSession",
                "parent_span_id": "4c0e65e0ddf05c44",
                "queued_behind": "0",
                "span_id": "9e907223301a6812",
                "trace_id": "0af7651916cd43dd8448eb211c803111",
            },
            parent: None,
            closed: true,
        },
        LoggedSpan {
            name: "session.message",
            fields: {
                "kind": "Destroy",
                "parent_span_id": "9e907223301a6812",
                "span_id": "6e1eb11f614e29e5",
                "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 [   5.008s] (1529/3529) minimal-client box_registration::tests::resume_timeout_goes_on_and_an_absent_host_fails_closed
        PASS [   4.332s] (1530/3529) minimal-client tty_relay::tests::resume_after_drop_prints_line_and_nudges_winsize
        PASS [   6.098s] (1531/3529) minimal-client box_registration::tests::a_silent_daemon_does_not_hold_the_exit_past_the_deadline
────────────
     Summary [  38.884s] 1531/3529 tests run: 1530 passed, 1 failed, 24 skipped
        FAIL [   3.884s] (1528/3529) minimal::otel_cli exec_run_attach_and_task_run_share_the_commands_trace
warning: 1998/3529 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

Outside the task's layers::

  • crates/boxes/src/core/egress.rs
  • crates/minimal-client/src/attach.rs
  • crates/minimal-client/src/box_registration.rs
  • crates/minimal/src/cmd/boxes/create.rs
  • crates/minimal/src/cmd/boxes/vm_host.rs
  • crates/minimal/src/dash/rpc.rs
  • crates/minimal/src/task.rs
  • crates/minimald-rpc/src/lib.rs
  • crates/minimald/src/net/classifier.rs
  • crates/minimald/src/rpc.rs
  • crates/minvmd/src/box_registry.rs
  • crates/minvmd/src/control.rs
  • crates/minvmd/tests/guest_classifier_integration.rs
  • crates/minvmd/tests/vm_escape_integration.rs
  • scripts/install-host-classifier.sh
  • scripts/session-e2e.sh

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

On a VM host, a process inside the VM that sends from the node's own address reaches the registry and the cache Minimal downloads from, and nothing else outside the resident boxes' rules. This round only merges main into the branch, and the node-plane tests still pass after the merge.

Notes from the agent: Conflict round. The one conflict was in crates/minimal-client/src/box_registration.rs. The branch added a BoxControlReply::HostIpBox arm and main added a BoxControlReply::BoxIdMinted arm in the same place. I kept both arms and committed the merge. That file is outside this task's layers, but the merge put it in the diff. cargo clippy -p minimal-client -p minvmd -p minimal --all-targets is clean after the merge. Deferred: cargo nextest run -p minvmd vm_escape_bounded_to_resident_union (lane: vm). I did not run it.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 30, 2026 21:14
@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 30, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: the diff is +3149/-629 (3778 lines), past 3x the plan's estimate of 860 lines (est: 400 code, 460 tests, as #2690 reads now), at round 2 (the last trigger was a conflict with the base branch). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2690 by hand.

@norrietaylor norrietaylor removed the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 30, 2026
@norrietaylor
norrietaylor marked this pull request as ready for review October 30, 2026 22:39
@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.

Approve at ca1dbf76.

  • This head adds one conflict-resolving merge of main (#2759) to 9bf4c0da. The PR's own diff is unchanged. In crates/minimal-client/src/box_registration.rs, the resolution keeps both new BoxControlReply arms: this PR's HostIpBox and main's BoxIdMinted. Each arm refuses the reply as an answer to the registration.
  • The merge changed crates/minvmd/src/control.rs, which serves the verbs the VM leg drives. So arch ruling 614 asked for the leg to run again. vm_escape_bounded_to_resident_union passed on the KVM lane at this head in 77.9 s (job 124314615395, 39/39), and the PR body records that run.
  • Arch ruling 614 rules PASS_WITH_CONDITIONS, and its one condition is met. The residuals it carries are filed: #2747, and gominimal/arch#153. Every lane is green.

Generated by Claude Code

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit 486ea13 Oct 31, 2026
36 checks passed
@norrietaylor
norrietaylor deleted the agent/2690-decide-the-node-plane-s-own-traffic-by-its-basel branch October 31, 2026 00:53
norrietaylor pushed a commit that referenced this pull request Nov 4, 2026
…2723)

T103. The node-plane baseline ships in force. The registry and cache entries name the
hosts the daemon's own fetches go to (github.com, cache.minimal.dev), and the node plane's
own lookups pin the addresses they resolve to, under a box's window and cap. A frame from
the node's own address reaches a pinned address at TCP 443, a subnet entry, the resolver,
the host alias at a configured exposure and a resident box's lease, and nothing else.
MINVMD_BASELINE_REGISTRY and MINVMD_BASELINE_CACHE re-point the entries.

A host-address box sends from the node address too, so the guest classifier writes a box
mark into the IPv4 DSCP field. The host gate decides a marked frame by the union of the
resident host_ip boxes' rules and drops it when none is resident. The client admits a
host_ip box's egress over new host-door verbs before the box runs anything, re-admits it
at attach and exec, and retires it when the session goes.

Backport of #2723. Lands before T102 here, as on main: T102 edits this commit's baseline
and gate code, and its baseline.rs does not apply without it.

Adapted to the line:
- cmd/boxes/create.rs and vm_host.rs hunks land in cmd/session.rs, dash/rpc.rs in
  minimal-tui, and the task hunks use the line's config variable.
- host_ip_box_with_vm_host and HostIpResidency live in minimal-client's box_registration,
  since minimal-tui is its own crate here and cannot reach minimal's. minimal-tui reads
  the record through a new session_record_at there, and its activation admits no declared
  egress because its policy is the default.
- The activation's failure path has no cleanup outcome, so the guard retires on every
  failed finalize. Main keeps the rules when the destroy may have failed.
- DropLimiter stays in egress_gate.rs, since the line has no drop_limiter module.
- The line has no ProxyListening or BoxIdMinted replies, and no SessionId::mint, so tests
  draw their ids from a v4 UUID. boxes:: reads sessions:: throughout.

(cherry picked from commit 486ea13)
norrietaylor pushed a commit that referenced this pull request Nov 5, 2026
…2723)

T103. The node-plane baseline ships in force. The registry and cache entries name the
hosts the daemon's own fetches go to (github.com, cache.minimal.dev), and the node plane's
own lookups pin the addresses they resolve to, under a box's window and cap. A frame from
the node's own address reaches a pinned address at TCP 443, a subnet entry, the resolver,
the host alias at a configured exposure and a resident box's lease, and nothing else.
MINVMD_BASELINE_REGISTRY and MINVMD_BASELINE_CACHE re-point the entries.

A host-address box sends from the node address too, so the guest classifier writes a box
mark into the IPv4 DSCP field. The host gate decides a marked frame by the union of the
resident host_ip boxes' rules and drops it when none is resident. The client admits a
host_ip box's egress over new host-door verbs before the box runs anything, re-admits it
at attach and exec, and retires it when the session goes.

Backport of #2723. Lands before T102 here, as on main: T102 edits this commit's baseline
and gate code, and its baseline.rs does not apply without it.

Adapted to the line:
- cmd/boxes/create.rs and vm_host.rs hunks land in cmd/session.rs, dash/rpc.rs in
  minimal-tui, and the task hunks use the line's config variable.
- host_ip_box_with_vm_host and HostIpResidency live in minimal-client's box_registration,
  since minimal-tui is its own crate here and cannot reach minimal's. minimal-tui reads
  the record through a new session_record_at there, and its activation admits no declared
  egress because its policy is the default.
- The activation's failure path has no cleanup outcome, so the guard retires on every
  failed finalize. Main keeps the rules when the destroy may have failed.
- DropLimiter stays in egress_gate.rs, since the line has no drop_limiter module.
- The line has no ProxyListening or BoxIdMinted replies, and no SessionId::mint, so tests
  draw their ids from a v4 UUID. boxes:: reads sessions:: throughout.

(cherry picked from commit 486ea13)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T103 — Decide the node plane's own traffic by its baseline set

1 participant

0