8000 T53 — Evaluate min net expose against dynamic_ingress and publish only under an allow by minimal-foundry[bot] · Pull Request #1865 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T53 — Evaluate min net expose against dynamic_ingress and publish only under an allow - #1865

Merged
norrietaylor merged 4 commits into
mainfrom
agent/1666-evaluate-min-net-expose-against-dynamic-ingress
Oct 3, 2026
Merged

norrietaylor merged 4 commits into
mainfrom
agent/1666-evaluate-min-net-expose-against-dynamic-ingress

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T53 — Evaluate min net expose against dynamic_ingress and publish only under an allow

slice: S13
kind: task
covers: NET-043, NET-044, NET-047
size: L
est: 400 code, 300 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: the bundle's daemon log tail carries each expose request with its decision and outcome
observability: one info log line per expose request with the box, the port, the decision and the outcome
layers:
- daemon:
- crates/minimald/src/env_min_helper.sh: the in-box helper gains net expose, sent over the box's daemon socket
- crates/minimald/src/env.rs: the session channel handles the expose request
- crates/minimald/src/session.rs: the session actor evaluates dynamic_ingress, publishes and records the live mapping
- crates/minimald/src/net/policy.rs: one-mapping expose that leaves nothing behind on failure
- crates/minimald/src/rpc.rs: the policy handler returns the live mappings
- crates/minimald-rpc/src/lib.rs: a live-mappings response beside the strict session policy
- crates/minimald/src/session/tests.rs: hosts the expose tests
test: cargo nextest run -p minimald expose_unenrolled_local_rpc_evaluates_dynamic_ingress
test: cargo nextest run -p minimald expose_allow_publishes_and_lists
test: cargo nextest run -p minimald expose_deny_typed_error
test: cargo nextest run -p minimald expose_rejected_leaves_no_partial_mapping
- cli:
- crates/minimal/src/cmd/session.rs: session policy shows live mappings
test: cargo nextest run -p minimal policy_shows_live_mappings
produces: none
consumes: none
proof: min net expose 3000 inside a box publishes the port and lists it in min session policy when the box's dynamic_ingress allows, returns a typed error when it denies, and leaves no partial mapping when the port is out of range. With two VMs running, min net expose finds the VM from the box name alone, through the resolution T60 adds for attach (NET-057).
depends on:
- blocked by #1657
- blocked by #1625

Proof

min net expose 3000 inside a box now publishes the port and lists it in min session policy as a live mapping beside the declaration when the box's dynamic_ingress allows; a box that denies (explicitly or by the deny-all default) answers with the typed refusal dynamic ingress is denied for this box instead of a bare message; and a request outside the declared range — or one whose bind the switch itself refuses — leaves no partial mapping and lists nothing. Every request says one info line in the daemon log naming the box, the port, the decision and the outcome.

Notes from the agent: All five test: lines pass, as do the supplementary tests this task adds outside them (three dynamic_ingress_decision/ExposeRefusal unit tests in net/policy.rs, the session-channel test in env.rs, and the GetLiveIngress round-trip in minimald-rpc — one batched run, 5 passed). cargo fmt --all -- --check, cargo clippy -p minimald -p minimal -p minimald-rpc --all-targets and just clippy-strict are all clean; the strict gate's ten hits on the new lines are quieted by #[expect] markers in the third commit. Three things the next slice should know: (1) a publish needs the address pair from record.box_addresses — the T66 hand a VM host's registration gives the box — and the registry's own switch-lease map (net/dns.rs) is private with no accessor, so a native host's self-allocated box is refused with the typed NoPublishedAddress rather than published at its lease; closing that gap means an accessor on the lease book or recording the pair for self-allocated boxes, both outside this task's layers. (2) dynamic_ingress: ask fails closed with AskNeedsAnswer — the attached-human prompt is NET-045. (3) the box's relay SessionGate admits inbound by declared internal ports only, so a dynamically published port binds and lists but is not yet admitted through the box's gate; that is the listen-published set of NET-016/017. Also: GetLiveIngress resolves the sess…[truncated]

Tests

line exit tail
cargo nextest run -p minimald expose_unenrolled_local_rpc_evaluates_dynamic_ingress 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-30cc... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/32135/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/32135/tree/crates/ot) C
cargo nextest run -p minimald expose_allow_publishes_and_lists 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-4fcc... Compiling stdlib v0.0.21 (/home/runner/.local/state/minimal/sessions/83035/tree/crates/stdlib) Compiling version v0.7.0 (/home/runner/.local/state/minimal/sessions/83035/tree/crate
cargo nextest run -p minimald expose_deny_typed_error 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-6300... Compiling stdlib v0.0.21 (/home/runner/.local/state/minimal/sessions/ae276/tree/crates/stdlib) Compiling version v0.7.0 (/home/runner/.local/state/minimal/sessions/ae276/tree/crate
cargo nextest run -p minimald expose_rejected_leaves_no_partial_mapping 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-a399... Compiling stdlib v0.0.21 (/home/runner/.local/state/minimal/sessions/a74e1/tree/crates/stdlib) Compiling version v0.7.0 (/home/runner/.local/state/minimal/sessions/a74e1/tree/crate
cargo nextest run -p minimal policy_shows_live_mappings 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-c65c... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/35708/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/35708/tree/crates/ot) C

Verify

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

Base

The base branch moved while the coder worked and was merged in before the tests ran. These files changed on BOTH sides since the branch point — read them first, because a rewrite from a stale reading can undo a merge without a conflict:

  • crates/minimald/src/session.rs

Closes #1666

min net expose <port> inside a box reaches the session channel as
net-expose%<port>, and the session actor decides it against the box's
dynamic_ingress: mode first — the deny-all default for a box that declared
nothing, and ask failing closed until NET-045's prompt exists — then the
declared dynamic_allowed_range. An allowed port is published through the
switch's forwarder with the same expose request a declared mapping takes,
recorded as a live mapping, unbound when the box stops, and served over a
new GetLiveIngress RPC. A denied or out-of-range request answers a typed
refusal and leaves nothing behind: one mapping is one request, so a bind
that failed binds nothing.

Every request says one info line naming the box, the port, the decision
and the outcome, so a diagnostics bundle's daemon log tail carries each
expose request with what became of it.
min session policy reads GetLiveIngress beside the effective policy and
lists each port the box published at runtime under its declaration — one
row a publish, naming the address the forward is bound on and the in-box
port it delivers to, shaped like the declared mapping rows. The read is a
view of live state, so it degrades with a warning rather than failing the
listing when a daemon cannot serve it, and a box that published nothing
prints no live section at all.
The strict gate holds changed lines to lints the tree is not yet clean
for, so the expose path's best-effort writes — the session channel's
replies, the actor's mailbox sends, and the stand-in forwarder's answers
— each say why their result may be dropped, and the channel test's two
handle calls say why their futures are as large as the harness channel
itself.
@coderabbitai
coderabbitai Bot commented Oct 3, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

This PR was authored by a bot without an assigned CodeRabbit review seat. Ask an organization administrator to assign a seat to the bot. See the seat-assignment guide. After the seat is assigned, request a review again.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c2b47905-321c-466b-84f7-0d6846676df5

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

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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

Approved: no findings at 57587331ddb7.

Nothing to do.

Verdict APPROVE, posted as a comment: the App cannot review its own pull request; the check and the label carry it.

Clobber: none found: git log on session.rs shows only this feature's commits and the acknowledged main merge; no recent fix is silently reverted.

@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 — No findings.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 3, 2026
@norrietaylor
Copy link
Copy Markdown
Member

/revise Round 1.

Allowed files (all inside the plan's layers):

  • crates/minimald/src/session.rs
  • crates/minimald/src/session/tests.rs
  • crates/minimald/src/net/policy.rs
  • crates/minimald/src/env.rs
  • crates/minimald/src/env_min_helper.sh (message text only)
  • crates/minimald/src/rpc.rs
  • crates/minimald-rpc/src/lib.rs
  • crates/minimal/src/cmd/session.rs

Do not touch other files. In particular, do not change the relay gate or net/switch.rs: admitting the port at the gate belongs to T22. 8000

  1. Publish at the box's registered address, never the raw hand.

    • In publish_exposed_port (session.rs), take the local address from the hostname registry's published_own_address for the box. The declared ingress path reads the same source through OwnAddressReporter::published_address.
    • Keep box_addresses.switch_address as the forward target.
    • If the registry holds no published address, or the bind there fails, return a typed error and leave nothing behind (NET-047). Never fall back to the raw handed loopback address.
    • Acceptance: a box whose hand the loopback verdict has not vouched for, so that it stands on the 127.0.0.1 interim, publishes its dynamic port at the same address its declared ports use, and the live mapping names that address.
    • Test: expose_publishes_at_the_registered_address. The served expose request's local and LiveMapping.local both equal the registry's published address.
  2. Refuse an expose for a box that is not running, without a race.

    • Check that the box is Active with a live host inside the actor, in the same step that pushes to live_ingress, so a stop cannot run between the check and the push. stop_running's unexpose sweep then stays complete.
    • The refusal is a new typed ExposeRefusal variant saying the box is not running.
    • Acceptance: after Stop, an expose returns that refusal, the stand-in forwarder records no request, and the live-mappings reply lists nothing.
    • Test: expose_on_stopped_box_refused.
  3. Log one info line per expose request, on every path. The record-read failure and the non-numeric-port reply in env.rs each emit the same "dynamic ingress expose" info line, carrying the box, the port as given, and the outcome.

    • Acceptance: a captured-log assertion for the non-numeric case in the existing env.rs channel test.
  4. Give "no published address" its own error.

    • When the box has no published address for runtime exposure (today, every self-allocated box on a native host), refuse with a distinct typed error whose message says exactly that.
    • It must never be DeniedByPolicy or share the deny code, because a user must not read a capability gap as a policy deny. The follow-up that makes these boxes publish is net expose: publish runtime ports for self-allocated boxes on a native host #1870.
    • Also make AlreadyPublished either idempotent success or its own distinct code, never the deny code.
    • Test: expose_without_published_address_is_not_a_policy_deny asserts the error kind differs from the deny kind. The AlreadyPublished behaviour is asserted in an existing or new test.
  5. Don't claim reachability you don't have yet.

    • Until T22 (T22 — Publish a permitted port when a process listens and withdraw it when it closes #1856) admits runtime-published ports at the relay gate, inbound connections to the new port are dropped. So the in-box min net expose must not print a bare "published". Print "published; not yet reachable (the gate admits it once T22 lands)".
    • Add a pending/reachability field to LiveMapping in minimald-rpc. min session policy shows that state for the mapping in both its text and its JSON output, so scripts can tell.
    • Test: extend policy_shows_live_mappings, or add policy_shows_pending_live_mapping, to assert the pending state in both the text and JSON forms.

Keep every existing test passing, and re-run the five plan test lines plus the new tests.

The PR body is edited by hand; do not rewrite it.

@norrietaylor
Copy link
Copy Markdown
Member

The CLI-level proof of NET-043 and NET-044 (min net expose in an allow box, with the mapping listed by min session policy) belongs to T82 #1895. min session create sends dynamic_ingress = None today, so this change is proved at unit level only.

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.

T53 — Evaluate min net expose against dynamic_ingress and publish only under an allow

1 participant

0