8000 T102 — Flows to host.min.internal are refused outside the design's openings by minimal-foundry[bot] · Pull Request #2871 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T102 — Flows to host.min.internal are refused outside the design's openings - #2871

Merged
norrietaylor merged 17 commits into
mainfrom
agent/2689-flows-to-host-min-internal-are-refused-outside-t
Nov 4, 2026
Merged

norrietaylor merged 17 commits into
mainfrom
agent/2689-flows-to-host-min-internal-are-refused-outside-t

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T102 — Flows to host.min.internal are refused outside the design's openings

slice: S21
kind: task
covers: NET-156, NET-158
size: M
est: 220 code, 270 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: min bug bundles carry the daemon log of minvmd, where each refused flow to the host alias records the row (node, cohort or box), the destination port and the rule host-alias
observability: the egress gate's rate-limited drop line names the host-alias rule and the row for every refused flow, logged by minvmd on the host
layers:
- gate:
- crates/minvmd/src/net/egress_gate.rs: infrastructure_destination admits at the host alias only the design's openings: a configured host exposure (a port the current [host-exposures] table of the client config.toml opens, per the gominimal/arch#110 ruling in networking design v0.9.2 §7.1), the gateway resolver's port, a credentialed-lane box's steered flow to the node-local Box Egress Proxy, and, for the node-plane identity alone, the node-plane baseline set; a box row or the host-address cohort never gets the baseline set
- crates/minvmd/src/net/baseline.rs: exposes the baseline set's endpoints at the alias as a port set the gate reads for the node-plane identity only, and retires MINVMD_HOST_EXPOSURE_PORTS and its reader
- crates/minvmd/src/net/host_exposures.rs (new): reads the [host-exposures] table of the client config.toml and reads it again when the file changes, as bep serve does for [secret-store-rules], so the gate decides each flow by the current table: an entry added opens its port, an entry removed closes it
- crates/minvmd/src/net.rs: hands the gate the current table in place of host_exposure_ports_from_env
test: cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes
test: cargo nextest run -p minvmd host_alias_admits_baseline_for_node_plane_only
test: cargo nextest run -p minvmd baseline_rules_admit_the_enumerated_categories_alone
test: cargo nextest run -p minvmd host_alias_admits_a_port_the_current_table_opens
test: cargo nextest run -p minvmd host_alias_refuses_a_port_removed_from_the_table
produces: none
consumes: none
proof: On a VM host, a flow from any box to host.min.internal reaches only the design's openings, and the node alone also reaches the node-plane baseline set there.


Generated by Claude Code

Proof

On a VM host, the host-side egress gate in minvmd now refuses every flow from a box to host.min.internal unless a port in the current [host-exposures] table of the client config.toml opens it, and the box's own egress rules then decide that flow. The node alone also reaches the ports that its baseline set's own endpoints open at the alias. Each refused flow logs a rate-limited line with rule_matched="host-alias", the row (node, cohort or box) and the port. A person who edits the table sees an added entry open its port and a removed entry close it within about a second, without a restart. This is proved by unit tests at the gate. No VM run was made on this box.

Notes from the agent: 1. The spec excerpt does not contain NET-156 or NET-158. Its highest ID is NET-155. I worked from the task's layers and from the nearby spec text: NET-003, the four port-scoped openings under NET-134, and the design-reasoning paragraph on host.min.internal.
2. The resolver opening and the Box Egress Proxy opening are not at the alias address. The resolver is on the gateway, and the spec says the proxy's steered destination is its own address. The gate keeps both openings at their own arms. At the alias, port 53 and port 8118 are refused like any other port. host_alias_refused_outside_exposures_for_boxes pins this, and also pins that both openings still pass at their own addresses.
3. Behaviour change: an own-address box now reaches a configured exposure, under its own rules. Before this change, every port refused it. A deny-all box still reaches nothing.
4. Table format, which this task defines: in [host-exposures], each key is a name and each value is an integer port, or a string with a port or a 'lo-hi' range. A bad entry is skipped and logged. A missing or unparseable file fails closed, with no exposure. Only the user-level config.toml in the default config dir is read, the same file bep serve reads. A min --config-dir override and /etc/minimal/config.toml are not read. Th…[truncated]

Tests

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-e278... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-60c3... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-10ed... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implemen 7438 t-test-3-09b5... 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 host_alias_refuses_a_port_removed_from_the_table 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-bfb0... 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]).

Closes #2689

The egress gate now refuses a flow to the host alias unless a port in
the current [host-exposures] table of the client config.toml opens it.
The row's own rules then decide the flow. The node-plane identity alone
also reaches the ports that the baseline set's own endpoints open at the
alias. A box row and the host-address cohort never get that set.

minvmd reads the table again when the file changes, so an added entry
opens its port and a removed entry closes it without a restart. This
retires MINVMD_HOST_EXPOSURE_PORTS. Each refused flow logs under the
host-alias rule with the row and the port.
@coderabbitai
coderabbitai Bot commented Nov 3, 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: 9a7eb36c-2970-4109-a32d-678b9091d986

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

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

Approved: every review thread resolved at 37de4e147f93.

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

Nothing to do.

@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/net.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 · 4 conditions · the full ruling

The gate is in the right place, and the alias is default-deny for every row. The table format does not follow §7.1 line 250. The reader skips the documented { proto, port } form, and the gate opens UDP at every exposure. C1 fixes this. A box can reach the baseline set's alias port through an exposure, and line 252 forbids that. C2 fixes this. The session e2e and docs/reference/cli-minvmd.md still use the retired MINVMD_HOST_EXPOSURE_PORTS. C3 and C4 fix them.

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

Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread crates/minvmd/src/net.rs Resolved
The variable no longer opens a port at host.min.internal. minvmd now
logs a warning that names it and points to the [host-exposures] table
of the client config.toml, so an operator who still sets it sees why
the alias stays closed at those ports.
@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 · 4 conditions · the full ruling

The gate is in the right place and fails closed. Three §7.1 rules are not met yet. The reader skips the entry form that line 250 gives, name = { proto = "tcp", port = N }, and accepts ranges (C1). An exposure opens UDP, but proto defaults to tcp (C2). A table port that a Minimal listener or a baseline endpoint holds is open to every box, and line 252 refuses this (C3). The new warning moved the Linux cfg gate off signal_via_pidfd, and the macOS build breaks (C4).

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

Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net.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 — 2 findings, 2 inline.

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

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

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

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-5603... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-22e9... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-b88f... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-f832... 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 host_alias_refuses_a_port_removed_from_the_table 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-ade9... 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/net.rs:829: minvmd now logs a warning when MINVMD_HOST_EXPOSURE_PORTS is set, and names the [host-exposures] table. The doc and scripts/session-e2e.sh are outside this task's layers. They need a follow-up change to write a [host-exposures] entry.

On a VM host, a flow from any box to host.min.internal reaches only the ports that the current [host-exposures] table of the client config.toml opens, the gateway resolver and the credentialed lane's steered Box Egress Proxy flow. The node alone also reaches the node-plane baseline set there. If MINVMD_HOST_EXPOSURE_PORTS is still set, the minvmd log now says that minvmd ignores it.

Notes from the agent: Revise round 1: minvmd now logs a warning when MINVMD_HOST_EXPOSURE_PORTS is set (crates/minvmd/src/net.rs). The doc and the e2e script are outside this task's layers, so this round did not change them. A follow-up must change docs/reference/cli-minvmd.md line 58 to describe the [host-exposures] table. It must also change scripts/session-e2e.sh (lines 395-406, 21777, 21818) to write a [host-exposures] entry for E2E_HOST_EXPOSURE_PORT into the client config.toml that minvmd reads. Until that change, the e2e host-exposure arm at port 18081 will fail. I did not run the e2e lane (it needs KVM).

Parse the design's inline-table entry form, with proto defaulting to
tcp, and drop the bare-port and range forms. Refuse at load an entry at
a Minimal listener's port, and log the holder. The gate matches the
frame protocol against the entry, and refuses a box or the cohort at the
baseline set's alias ports and at listener ports.

Move the retired-variable items above the pidfd doc comment, so the
linux cfg gates signal_via_pidfd again and macOS builds.
@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/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/egress_gate.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 gate now conforms to §7.1 for NET-156 and NET-158. The compare adds the { proto, port } entry form and refuses baseline and listener ports to box and cohort rows. But the change retires MINVMD_HOST_EXPOSURE_PORTS, and two consumers still use it. scripts/session-e2e.sh exports it to open port 18081 for the host-address box on VM lanes. At this head that fetch is refused, so the session proof fails. Write the port into the [host-exposures] table in C1. docs/reference/cli-minvmd.md still tells users that the variable opens ports. Replace that text in C2.

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

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

Round 2 of 10 on a failed check suite, pushed as 6b8a9eb6e7d1.

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-b191... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-af01... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-0550... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-7af5... 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 host_alias_refuses_a_port_removed_from_the_table 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-b65c... 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/net/host_exposures.rs: entry_exposure now parses only { proto, port } with proto defaulting to tcp. The integer, string and range forms are gone, and HostExposure keeps the protocol.
  • 👍 valid — crates/minvmd/src/net/host_exposures.rs: The module doc example is now the design's inline-table form, and the range form is gone.
  • 👍 valid — crates/minvmd/src/net/egress_gate.rs: AliasOpenings::admit refuses a Box or Cohort row at baseline.alias_ports() before the exposure check. It matches the frame protocol against each entry's proto.
  • 👎 invalid — crates/minvmd/src/net.rs:829: The finding is correct about the script, but scripts/session-e2e.sh is outside this task's layers. The change is to write a [host-exposures] e2e = { proto = "tcp", port = 18081 } entry into the e2e client config.toml in place of the export at line 406. It is listed in notes for a follow-up.
  • 👍 valid — crates/minvmd/src/net/host_exposures.rs: entry_exposure parses the inline table, proto defaults to tcp, and the bare integer and lo-hi range forms are removed.
  • 👍 valid — crates/minvmd/src/net/host_exposures.rs: The doc example is now postgres = { proto = "tcp", port = 5432 }.
  • 👍 valid — crates/minvmd/src/net/egress_gate.rs: The gate compares the frame protocol with the entry's proto. It refuses a Cohort or Box row at the baseline alias ports and at LISTENER_PORTS before the exposure check.
  • 👍 valid — crates/minvmd/src/net/host_exposures.rs: parse_table skips an entry at a Minimal listener port (hostname proxy, answerer, Box Egress Proxy) and logs the entry and the holder. The baseline ports move on a re-point, so the gate refuses them for boxes for each flow.
  • 👍 valid — crates/minvmd/src/net.rs: The const and warn function now sit above the pidfd doc comment, so #[cfg(target_os = "linux")] gates signal_via_pidfd again.
  • 👍 valid — PRRT_kwDOPdfcxM6zTYFh: The new items are above the doc comment. signal_via_pidfd has its doc comment and its linux cfg back. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — PRRT_kwDOPdfcxM6zTYFp: The same fix as the thread at line 652: the doc comment and the cfg attach to signal_via_pidfd again. (not posted on the thread: not an open thread on the pull request)

On a VM host, a box reaches host.min.internal only at a port and protocol that a name = { proto = "tcp", port = N } entry in the current [host-exposures] table of the client config.toml opens. An entry added or removed changes this for the next flow with no restart. An entry at a Minimal listener port is refused at load. A box or the host-address cohort is refused at the baseline set's alias ports. Only the node reaches the node-plane baseline set at the alias.

Notes from the agent: Round 2. The macOS build failure came from new items placed between the pidfd doc comment plus #[cfg(target_os = "linux")] and signal_via_pidfd. The items now sit above the doc comment, so the cfg gates the function again. This fixes all four E0425 errors. I did not build on macOS here, so the macOS lanes owe the evidence. The table now accepts only the inline-table form { proto, port } with proto defaulting to tcp. HostExposure carries the protocol, and the gate matches it. The load-time refusal covers the Minimal listener ports: 7654 (hostname proxy, a literal that mirrors cmd/run.rs NODE_DEFAULT_PROXY_PORT, which is private and outside the layers), 7656 (answerer) and 8118 (Box Egress Proxy). The baseline set's alias ports change when an entry is re-pointed, so the gate refuses a box or cohort there for each flow and does not refuse them at load. Outside the layers and not changed: scripts/session-e2e.sh (lines 396-406, 21777, 21818) still exports MINVMD_HOST_EXPOSURE_PORTS. It must write [host-exposures] e2e = { proto = "tcp", port = 18081 } into the e2e client config.toml, or the e2e exposed-port fetch fails. docs/reference/cli-minvmd.md line 58 still documents the retired variable and the range form. A follow-up task must change both.

@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 Nov 3, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: the diff is +1208/-376 (1584 lines), past 3x the plan's estimate of 490 lines (est: 220 code, 270 tests, as #2689 reads now), at round 2 (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 #2689 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 Nov 3, 2026
@norrietaylor
Copy link
Copy Markdown
Member

/revise address review concerns and fix CI

current() now stats and reads config.toml with the lock released, so a
slow disk does not stall other frames to host.min.internal. The gate
applies the baseline ports to TCP alone, so the table decides a UDP
exposure at the same port for the node row.
minvmd no longer reads MINVMD_HOST_EXPOSURE_PORTS, so the e2e writes the
port into the hermetic client config.toml instead. The client config
now accepts the [host-exposures] table, so min still parses a file that
has it.
The reference still said MINVMD_HOST_EXPOSURE_PORTS opens ports. Describe
the client config.toml table, its entry form, the reread on change, the
listener ports it refuses and the warning for the retired variable.
@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/net/egress_gate.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 host gate now holds host.min.internal default-deny and reads the [host-exposures] table live, as §7.1 lines 248 and 250 require. Three gaps remain. The listener exclusion is a fixed list, but the hostname proxy port can be a pin or a drawn fallback, and line 252 excludes any Minimal listener at each flow. Make the change in C1. The table does not refuse a baseline alias port at load, and the log does not name that holder. Make the change in C2. The in-VM relay still drops an own-address box at every alias port, but the reference and the PR body say that a box reaches the exposed ports. Correct the text and file the residual in C3.

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

Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread crates/minvmd/src/net/host_exposures.rs Resolved
Comment thread docs/reference/cli-minvmd.md Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-005b... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-14ce... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-5a66... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-2640... 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 host_alias_refuses_a_port_removed_from_the_table 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-b3cf... 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.

Outside the task's layers::

  • crates/client-config/src/config.rs
  • docs/reference/cli-minvmd.md
  • scripts/session-e2e.sh

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

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6zT7US: current() now takes the lock only to decide whether a recheck is due and to store the result. The stat and the read of config.toml run with the lock released, so other frames keep the current table. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — crates/minvmd/src/net/egress_gate.rs: admit() applies the baseline alias ports to TCP alone. Another protocol at the same port goes to the exposure check, and the node-plane test now asserts that a udp/443 entry is open to the node and the cohort.
  • 👍 valid — crates/minvmd/src/net.rs:827: scripts/session-e2e.sh no longer exports the variable. It writes e2e = { proto = "tcp", port = 18081 } under [host-exposures] in the hermetic client config.toml. The client config parser now also accepts that table, so min does not reject the file.
  • 👍 valid — crates/minvmd/src/net.rs:629: docs/reference/cli-minvmd.md now describes the [host-exposures] table: the file, the entry form, the reread on change, the skipped listener ports and the warning for the retired variable.

On a VM host, a flow from a box to host.min.internal reaches only a port that the [host-exposures] table of the client config.toml opens, under the box's egress rules. The node alone also reaches the baseline set's TCP port there. A person can add or remove a table entry, and the alias opens or closes that port with no restart. min still reads a config.toml that has the table.

Notes from the agent: Revise round 1. In-layer fixes: HostExposures::current now stats and reads config.toml with the lock released. The gate applies the baseline alias ports to TCP alone, so the table decides a UDP exposure at the same port. The host_alias_admits_baseline_for_node_plane_only test now checks this case. Out-of-layer files, touched because the person asked to fix CI: (1) scripts/session-e2e.sh. The e2e failed at NET-003 because it still exported the retired MINVMD_HOST_EXPOSURE_PORTS. It now appends e2e = { proto = "tcp", port = 18081 } under [host-exposures] in the hermetic $XDG_CONFIG_HOME/minimal/config.toml when E2E_VM is set. (2) crates/client-config/src/config.rs. The client Config is deny_unknown_fields. Without the change, min attach and every other config.toml reader fail on a file that has the [host-exposures] table. That defect affects users, not only the e2e. The field keeps the table as written, and minvmd still decides each entry. This was a planning gap: the task did not name the client config parser. (3) docs/reference/cli-minvmd.md. The paragraph about the retired variable now describes the table. Not verified here: just e2e, test-vm and the macOS lane (no KVM or VM on this box), and vale (not installed, so just lint-prose did not run). I could not read the cause of the test-kvm failure from the excerpt in the directive. I did not run clippy for the crates that depend on client-config. The field is additive and Config is non_exhaustive.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft November 3, 2026 22:36
@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 Nov 3, 2026
@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 · 1 condition · the full ruling

The gate now refuses host.min.internal outside the design's openings, so NET-156 and NET-158 conform. One gap has no owner. In the VM, the guest relay in minimald gets no exposure ports, so an own-address box that names the host is still refused at every port. Section 7.1 lines 250 and 253 say every box that declares the name reaches the exposure, on the own_ip lane too. The reference states the limit, but no issue owns it, and the PR body says the opposite. File the issue and name it in the three places that C1 lists.

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

Comment thread docs/reference/cli-minvmd.md Resolved
Comment thread scripts/session-e2e.sh Resolved
@norrietaylor
Copy link
Copy Markdown
Member

/revise Deliver arch C1, using #2990 (T154) as the issue for the guest half of an own-address box's exposure. Make the three edits the ruling names: mark the sentence at docs/reference/cli-minvmd.md line 58 as a current limit with that link, replace note 3 of the PR body with the residual and that link, and name it in the E2E_HOST_EXPOSURE_PORT comment in scripts/session-e2e.sh. Then resolve the threads that are addressed. T102 lands first, and T148 moves both readers of the table onto one shared reader afterwards, so do not change the reader here.

Link #2990 as the owner of the guest half in the reference and in the
e2e comment beside E2E_HOST_EXPOSURE_PORT.
@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 head conforms to design §7.1 for NET-156 and NET-158. The host gate opens the alias only at the current table's exposures. The node-plane identity alone gets the baseline ports. The resolver and the Box Egress Proxy keep their own arms. The last commit marks the own-address refusal as a current limit and links #2990. I accept that as a filed residual.

Ruled at 24b733f 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/minvmd/src/net/egress_gate.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a /revise note, pushed as 24b733f7f90f.

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-63c7... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-38d4... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-b8ee... 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 host_exposures_read_from_client_config 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-9d14... 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 host_alias_exposure_needs_the_box_to_name_the_host 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-5f66... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-491c... 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 host_exposure_removal_ends_established_flows 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-0c29... 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 host_exposure_on_minimal_listener_port_refused 0 Applying loadouts: default Running task runtime-implement-test-7 in session task-runtime-implement-test-7-f03e... 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 host_exposure_shadowed_by_a_later_minimal_listener 0 Applying loadouts: default Running task runtime-implement-test-8 in session task-runtime-implement-test-8-3885... 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:

On a VM host, the gate admits at host.min.internal only the design's openings. A box reaches a host exposure only under the current [host-exposures] table and its own rules, and only the node reaches the node-plane baseline set there. The reference and the e2e comment now say that the refusal of an own-address box at every port is a current limit, and they link #2990 as its owner.

Notes from the agent: Round 4 changed only docs and a comment. The code and the table reader did not change, because T148 owns the shared reader. I cannot edit the PR body. Use this text for note 3: "Residual: an own-address box in a VM still gets a refusal at every host.min.internal port, whatever the table says, because the guest relay (minimald compiled_egress) gets no exposure ports. Design section 7.1 lets such a box reach an exposure when it declares the name. #2990 (T154) tracks the guest half." just lint-prose did not run, because vale is not installed on this box. The e2e script passed bash -n. I did not run it.

@norrietaylor
Copy link
Copy Markdown
Member

/revise Design decision on the MEDIUM security thread at crates/minvmd/src/net/egress_gate.rs:674: a box must name the host. An undeclared allow_subnets does not cover the alias. In AliasOpenings::for_box, replace is_none_or with a check that needs a declared list holding a cover of the alias, so a box with no egress section is refused at every exposure. Add that case to host_alias_exposure_needs_the_box_to_name_the_host. Add a sentence saying so to NET-160 in docs/specs/18-spec-box-networking/18-spec-box-networking.md, which is now in the task's layers. Update docs/reference/cli-minvmd.md if it says otherwise.

An undeclared egress.allow_subnets allows every subnet but names
nothing, so it no longer covers the host alias. A box with no egress
section is refused at every host exposure (NET-160).
@github-actions
github-actions Bot commented Nov 4, 2026
Copy link
Copy Markdown
Contributor

Spec lint

docs/specs/18-spec-box-networking/18-spec-box-networking.md — 13 finding(s)

docs/specs/18-spec-box-networking/18-spec-box-networking.md:171: P004 NET-009 names test 'native_resolution_without_proxy_env', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:378: P004 NET-040 names test 'fresh_install_own_ip_ingress_publishes_loopback', which was not found in the repository
docs/specs/18-spec-box-netwo
10BC8
rking/18-spec-box-networking.md:388: P004 NET-041 names test 'installer_switch_binary_executable', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:435: P004 NET-048 names test 'linux_amd64_manifest_ships_vm_stack', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:440: P004 NET-049 names test 'fresh_linux_kvm_activate_local_minvmd', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:445: P004 NET-050 names test 'linux_arm64_manifest_ships_vm_stack', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:450: P004 NET-051 names test 'fresh_arm64_kvm_activate_local_minvmd', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:634: P004 NET-163 names test 'zone_name_rule_admits_sibling_lease_under_its_ingress', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:772: P004 NET-160 names test 'native_own_ip_host_exposure_needs_the_box_to_name_the_host', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:782: P004 NET-161 names test 'native_own_ip_follows_the_current_host_exposures_table', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:893: P004 NET-132 names test 'proxy_sees_each_vm_box_by_its_switch_address', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:919: P004 NET-134 names test 'deny_all_box_reaches_proxy_and_no_other_host_port', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:943: P004 NET-107 names test 'session_outbound_request', which was not found in the repository

Advisory. None of this blocks the merge; the review decides.

Advisory, and never a merge gate. Linter: gominimal/foundry@fb5932b8, spec format: How Minimal Writes Specs.

@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: 18 unresolved threads — minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, minimal-arch[bot] crates/minvmd/src/net/egress_gate.rs, minimal-arch[bot] crates/minvmd/src/net.rs:829, minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, …. 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: CONDITIONS · 3 conditions · the full ruling

The compare refuses an allow-all box at every host exposure. In this tree, an undeclared allow_subnets at the gate is the allow-all box (box_registry.rs:4694). That box still reaches its siblings. Section 7.1 line 251 says "an allow-all box reaches exposures as it reaches siblings", and architecture.md line 933 says the same. Revert the gate arm and its test in C1. Revert the NET-160 amendment in C2, because the spec follows the design. Remove the incorrect doc sentence in C3. The rest of the PR keeps the PASS on 24b733f7.

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

Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread crates/minvmd/src/net/egress_gate.rs Resolved
Comment thread docs/reference/cli-minvmd.md Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-d261... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-999d... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f641... 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 host_exposures_read_from_client_config 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-55f1... 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 host_alias_exposure_needs_the_box_to_name_the_host 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-4a65... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-15f4... 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 host_exposure_removal_ends_established_flows 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-cd87... 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 host_exposure_on_minimal_listener_port_refused 0 Applying loadouts: default Running task runtime-implement-test-7 in session task-runtime-implement-test-7-9b94... 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 host_exposure_shadowed_by_a_later_minimal_listener 0 Applying loadouts: default Running task runtime-implement-test-8 in session task-runtime-implement-test-8-3475... 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/net/egress_gate.rs:675: for_box now needs a declared allow_subnets that covers the alias (is_some_and), so a box with no egress section is refused at every exposure. Added that case to host_alias_exposure_needs_the_box_to_name_the_host, and stated it in NET-160 and cli-minvmd.md.

On a VM host, the gate admits at host.min.internal only the design's openings. A box row reaches a host exposure only when the current [host-exposures] table opens the port and the box names the host, by host.min.internal in egress.allow_dns_hosts or by a declare 10BC8 d egress.allow_subnets that covers the alias. A box with no egress section is refused at every exposure. Only the node reaches the node-plane baseline set at the alias.

Notes from the agent: Revise round 5 follows the /revise note. AliasOpenings::for_box now uses is_some_and in place of is_none_or. host_alias_exposure_needs_the_box_to_name_the_host has a new case for a box with no egress section. Tests that expected a box with no egress section to reach an exposure now give that box an egress section that names host.min.internal, through a new test helper names_the_host(). The 'locked' box in host_alias_refused_outside_exposures_for_boxes also names the host now, so that its own deny rule refuses the flow, as the test intends. The host-address cohort row does not change. NET-160 now reads 'is undeclared or does not cover the alias', and its rationale comment no longer says an allow-all box reaches an exposure. cli-minvmd.md now says that a box with no egress section reaches no exposure. Vale is not installed on this box, so I did not run just lint-prose. I ran the full minvmd suite once: 688 passed and 2 stalled-switch timing tests failed near their 5 s limit. On a rerun, a_stalled_switch_does_not_hold_the_relay_past_a_guest_close passed 3 of 3 times with this change, and it also passed on the unchanged head. Neither of those tests sends to the host alias. The spec's second verify line (minimald native_own_ip_host_exposure_needs_the_box_to_name_the_host) names a test that does not exist in this tree, and minimald is outside this task's layers.

@norrietaylor
Copy link
Copy Markdown
Member

/revise This replaces the design decision in the previous note: follow the arch ruling at 410fb54f. An allow-all box reaches an exposure as it reaches a sibling box. Apply arch C1, C2 and C3 exactly as the check names them: for_box goes back to is_none_or, the undeclared test case becomes Ok(GateAdmit::Row), NET-160's text and rationale return to their earlier wording, and the sentence "A box with no egress section reaches no exposure." leaves docs/reference/cli-minvmd.md. Keep the rest of the current-limit sentence and the #2990 link.

An undeclared egress.allow_subnets covers host.min.internal again, so
a box with no egress section reaches an exposure as it reaches a
sibling box (design 7.1). This reverts the gate change of 410fb54,
restores the NET-160 text, and keeps the undeclared test case as an
admit.
@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: 22 unresolved threads — minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, minimal-arch[bot] crates/minvmd/src/net/egress_gate.rs, minimal-arch[bot] crates/minvmd/src/net.rs:829, minimal-arch[bot] crates/minvmd/src/net/host_exposures.rs, …. 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

This head conforms to the design. Section 7.1 line 251 says that an allow-all box reaches a host exposure as it reaches a sibling box. AliasOpenings::for_box now treats an undeclared allow_subnets as coverage of the alias again, and the test asserts that admit. The other hunks are the same as on 24b733f7, which got a PASS. Issue #2990 holds the own-address limit as a residual. The task names host_alias_refuses_a_port_removed_from_the_table, but that test is now host_exposure_removal_ends_established_flows. Update the task's test line.

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

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 6 of 10 on a /revise note, pushed as 37de4e147f93.

line exit tail
cargo nextest run -p minvmd host_alias_refused_outside_exposures_for_boxes 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-7b16... 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 host_alias_admits_baseline_for_node_plane_only 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-7b74... 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_rules_admit_the_enumerated_categories_alone 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-bbf8... 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 host_exposures_read_from_client_config 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-987d... 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 host_alias_exposure_needs_the_box_to_name_the_host 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-e440... 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 host_alias_admits_a_port_the_current_table_opens 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-d1c9... 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 host_exposure_removal_ends_established_flows 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-420f... 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 host_exposure_on_minimal_listener_port_refused 0 Applying loadouts: default Running task runtime-implement-test-7 in session task-runtime-implement-test-7-97ad... 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 host_exposure_shadowed_by_a_later_minimal_listener 0 Applying loadouts: default Running task runtime-implement-test-8 in session task-runtime-implement-test-8-3155... 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/net/egress_gate.rs: for_box uses is_none_or again, so an allow-all box covers the alias. The doc sentence about an undeclared allow_subnets is gone.
  • 👍 valid — crates/minvmd/src/net/egress_gate.rs: The undeclared case now expects Ok(GateAdmit::Row). The doc sentence on the test is gone.
  • 👍 valid — docs/specs/18-spec-box-networking/18-spec-box-networking.md: NET-160 says 'its egress.allow_subnets does not cover the alias' again, and the rationale has the allow-all fall-through text again.
  • 👍 valid — docs/reference/cli-minvmd.md: Removed 'A box with no egress section reaches no exposure.' The rest of the current-limit sentence and the T154 — A VM host's own-address box reaches a host exposure through the guest relay #2990 link stay.
  • 👎 invalid — crates/minvmd/src/net/egress_gate.rs:674: The arch ruling at 410fb54 replaces this finding. Design section 7.1 lets an allow-all box reach an exposure as it reaches a sibling box, so for_box uses is_none_or again.

On a VM host, the gate admits at host.min.internal only the design's openings. A box reaches a host exposure only under the current [host-exposures] table and its own egress rules. A box with no egress section is allow-all and reaches an exposure as it reaches a sibling box. Only the node reaches the node-plane baseline set at the alias.

Notes from the agent: Round 6 follows the arch ruling at 410fb54. The commit reverts the gate and spec changes of 410fb54: for_box uses is_none_or again, and the NET-160 text and rationale have their earlier wording. The test keeps the undeclared box case, and it now expects Ok(GateAdmit::Row). The revert also takes out the names_the_host() helper that 410fb54 added to other tests. Those tests pass without it, because an allow-all box covers the alias. In docs/reference/cli-minvmd.md only the sentence 'A box with no egress section reaches no exposure.' is gone. The rest of the current-limit sentence and the #2990 link stay. The earlier thread PRRT_kwDOPdfcxM6zfIEo now gets the verdict invalid, because the ruling replaces it. 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 — approved

Verdict APPROVE — every review thread is resolved; @norrietaylor closed the last one. The review of 37de4e147f93 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.

Reviewed 37de4e14 against T102's plan block. Flows to host.min.internal are refused outside the design's openings: the baseline ports open for the node row alone, Minimal's own listeners are refused at each flow ahead of the table, and a box must name the host or be covered by allow_subnets. An allow-all box reaches an exposure as it reaches a sibling, per the design. The table is reread on change and fails closed. An own-address box in a VM stays refused until #2990. Arch-review passes on this head.

@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 964a71b Nov 4, 2026
36 checks passed
@norrietaylor
norrietaylor deleted the agent/2689-flows-to-host-min-internal-are-refused-outside-t branch November 4, 2026 17:58
norrietaylor pushed a commit that referenced this pull request Nov 4, 2026
…penings (#2871)

T102. Behaviour change, for the release notes: host-address boxes reach only ports opened
in [host-exposures]. Before this, a host-address box reached the host through
host.min.internal at every port. Own-address boxes stay refused end to end: the host gate
would admit a configured exposure under their rules, but the guest still refuses them.

The egress gate refuses a flow to the host alias unless a port in the current
[host-exposures] table of the client config.toml opens it, and the row's own rules then
decide the flow. Each entry is { proto, port }, with proto defaulting to tcp. The node
plane alone also reaches the ports its baseline endpoints open at the alias. minvmd rereads
the table when the file changes, so an entry opens or closes its port without a restart. A
missing or unparseable file fails closed. An entry at a Minimal listener's port is refused
and logged. Each refused flow logs under the host-alias rule with the row and the port.
This retires MINVMD_HOST_EXPOSURE_PORTS, and minvmd warns if it is still set.

Backport of #2871. Lands after T103 here, as on main: its baseline and gate edits sit on
that commit. T147 #2956, the project-file warning, stays out of 0.7.3. A project's
[host-exposures] table still opens nothing, because the reader takes only the client file.

Adapted to the line:
- The client config lives in sessions/src/client/config.rs, and host_exposures joins it
  without main's [secrets] and [secret-store-rules] sections.
- boxes:: reads sessions::, tests draw session ids from a v4 UUID, and DropLimiter stays
  in egress_gate.rs.

(cherry picked from commit 964a71b)
norrietaylor pushed a commit that referenced this pull request Nov 5, 2026
…penings (#2871)

T102. Behaviour change, for the release notes: host-address boxes reach only ports opened
in [host-exposures]. Before this, a host-address box reached the host through
host.min.internal at every port. Own-address boxes stay refused end to end: the host gate
would admit a configured exposure under their rules, but the guest still refuses them.

The egress gate refuses a flow to the host alias unless a port in the current
[host-exposures] table of the client config.toml opens it, and the row's own rules then
decide the flow. Each entry is { proto, port }, with proto defaulting to tcp. The node
plane alone also reaches the ports its baseline endpoints open at the alias. minvmd rereads
the table when the file changes, so an entry opens or closes its port without a restart. A
missing or unparseable file fails closed. An entry at a Minimal listener's port is refused
and logged. Each refused flow logs under the host-alias rule with the row and the port.
This retires MINVMD_HOST_EXPOSURE_PORTS, and minvmd warns if it is still set.

Backport of #2871. Lands after T103 here, as on main: its baseline and gate edits sit on
that commit. T147 #2956, the project-file warning, stays out of 0.7.3. A project's
[host-exposures] table still opens nothing, because the reader takes only the client file.

Adapted to the line:
- The client config lives in sessions/src/client/config.rs, and host_exposures joins it
  without main's [secrets] and [secret-store-rules] sections.
- boxes:: reads sessions::, tests draw session ids from a v4 UUID, and DropLimiter stays
  in egress_gate.rs.

(cherry picked from commit 964a71b)
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.

T102 — Flows to host.min.internal are refused outside the design's openings

1 participant

0