Repository navigation
T82 — Carry the box's dynamic ingress setting into session create and prove a runtime expose end to end - #1959
Conversation
`min session create` takes the box's dynamic ingress setting from two flags spelled like the egress flags -- `--dynamic-ingress <allow|ask|deny>` and `--dynamic-range <lo>-<hi>` -- and carries both into the create request's IngressPolicy (NET-043). A set mode or range makes the ingress declaration even with no static mapping; nothing set keeps ingress None and the deny-all default. A malformed or inverted range, or a range with no mode, is refused at the flag, before any session exists. `min session policy` shows the resolved stance in both surfaces: the text render always prints the dynamic ingress row (deny when nothing was given, the evaluation the absent setting takes) and the range beside it, and the JSON document carries `dynamic_ingress` resolved in the deny_all kind as well as the declared one, never a null a parser would have to default itself.
…line The one line every session start logs gains the ingress half of the diagnosis (NET-043): the dynamic_ingress mode the record holds, resolved the way the box runs it — an absent stance reads as the deny the policy module evaluates it as, the same reading `min session policy` prints — and the dynamic_allowed_range beside it. A diagnostics bundle's daemon-log tail can now explain why the outside can reach a box as well as why the box can reach out. The boundary is pinned from both sides: an absent dynamic_ingress decides exactly as an explicit deny does (the same typed refusal, at every port, with a range declared or without one), and the in-box expose request carries a port and no policy field — a publish, allowed or refused, and any request trying to smuggle more than a port, leave the record's IngressPolicy bit-identical to what the create stored.
Two cases in scripts/session-e2e.sh, the legs the CLI carry was cut against: - min_net_expose_publishes_lists_and_refuses (NET-043/044): the dynamic_ingress stance a box is created with is what 'min net expose' is decided against. An in-range ask of an allow box publishes (the box's own reply), both 'min session policy' renderings list the runtime publish beside the declaration, and out-of-range and deny asks come back with their own typed errors. The listener starts after the expose on purpose: the publication set has one owner per port, and a listener standing first would hand the port to the watcher instead. - listen_published_port_reaches_peer_and_host (NET-016/017): a listen inside the declared range publishes with no expose. A peer box reaches it on its lane's own surface (in-fabric by name on a VM lane, the published address on a native host's loopback), a host probe reaches it at the address the zone answerer returns for the box's name, closing the listener withdraws the publication — both clients refused fast, never a connect timeout (NET-014) — and an out-of-range listen reaches neither. Also drops the now-stale "the CLI has no flag for a dynamic allow range" clause from the port-publish proof's comment: the flag exists now, and that proof's box deliberately creates without one.
…box-s-dynamic-ingress-setting-into-ses
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a conflict with the base branch). A person decides now. Remove |
The KVM lane failed at both dynamic-ingress proofs' in-range arms: a runtime publish the box's own stance allows is still a publish the HOST side must carry, and a VM-backed target's host registration is built from the create's declared --ingress mappings alone, so the dynamic range reaches the daemon's own decision but not the host row, and both the in-range expose and the watcher's listen-publish are answered with nothing written on. Each proof now splits on what the target's host side can carry, one shared reason (`runtime_publish_gap_reason`): everything decided daemon-side -- the typed out-of-range and deny refusals, the fail-closed policy listings, the refusals read against a DECLARED control mapping -- runs on every target, while the publish arms run where the host side does not refuse them and say exactly why where it does. The listen case reads the split off the box's own listing (the watcher records a live row only once the switch said yes) and fails loudly rather than skip if a live section appears that its row poll does not match, so the arm self-heals when the host side learns the range. Proof 2's answerer-port sed matched only the native ZONE ANSWERER spelling; it now reads the VM host daemon's holder and registered forms too, and the host legs are WHERE-skipped with the actual min ls lines printed when no answerer is on record. Also fixed the expose proof's JSON helper, which could never have passed on any lane: it read its document from stdin beside a `python3 -` heredoc, and the heredoc is the interpreter's stdin, so the piped document never arrived. The document now rides argv.
…box-s-dynamic-ingress-setting-into-ses # Conflicts: # crates/minimal/src/cmd/session.rs
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
1 similar comment
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
…nd ingress fields
|
Round 1 of 1 on a failed check suite, pushed as Resolved conflicts in:
Verify Outside the task's
Converted to a draft: something above needs a person before review. The two dynamic-ingress e2e legs now run to a verdict on every target a lane owns instead of killing the KVM lane at the in-range ask: on a VM-backed box a person sees the daemon-decided half asserted whole — the typed out-of-range refusal, the typed deny refusal, the fail-closed policy listings on both the text and the -o json renderings (a refused publish lists nothing), and an out-of-range listen refused by the peer and, where the answerer serves the box an address, by the host probe, read against a declared control mapping on the same box — while the runtime publish arms (the expose's publish line and its live row, the listen's peer/host reach and its withdrawal) run where the host side carries a runtime publish and elsewhere print runtime_publish_gap_reason, the exact host-side carry (the minvmd/sessions half of NET-043, T80 #1878) whose landing turns them on without this script changing again; the six unit lines behind the CLI/daemon half of the task's proof (create carries the stance and range, the malformed range is refused, policy shows the resolved stance in text and JSON, the create line names the stance, an absent stance is deny, nothing in a box can set or widen the setting) are green on this branch, and the lane's own end-to-end verdict is the runner's run of Notes from the agent: Revise round for the failed ci-linux-kvm-success / test-kvm checks. Both failures were this script asserting, on the only lane that runs these proofs, arms that target cannot carry. (1) The host-side egress gate answers an in-range runtime publish with nothing written on — the box's host registration is built from the create's declared --ingress mappings alone (register_box_for_activation), so the dynamic range reaches the daemon's decision but not the host row — so the in-range expose read 'publishing port N failed: malformed gvproxy status line: ""' and the case failed on it. Each proof now splits on that…[truncated] |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
…til host admission
…launch check's bound
…ng the publish arms
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was a failed check suite). A person decides now. Remove |
|
Approved: no findings at 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: the base was merged in twice (T88 #1958, T93 #1957) and the PR's commits sit on top. I confirmed the relaxed |
|
The runtime will not revise this pull request again on its own: round 2 would exceed the cap of 1 (the last trigger was the runtime's review findings). A person decides now. Remove |
The flags are documented as requiring --network own_ip, but a host_ip or none box accepted them and showed a declared stance nothing would honour. The create now refuses them before any box exists.
There was a problem hiding this comment.
Reviewed against T82's plan block as amended. The CLI carries --dynamic-ingress/--dynamic-range into the record. A VM-backed host refuses allow/ask with the typed reason (until T84, #1897), and every lane runs the refusal, the out-of-range checks and the policy-posture legs. The resolved stance renders in policy text and JSON, including deny (default). The dynamic flags require own_ip. ARCH PASS (C1/C2 closed; C3 native gvproxy harness is gominimal/inbox#887).
T82 — Carry the box's dynamic ingress setting into session create and prove a runtime expose end to end
slice: S13
kind: task
covers: NET-016, NET-017, NET-043, NET-044
size: M
est: 120 code, 260 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: the daemon's session-create info line names the box's dynamic_ingress mode and allowed range, so a bundle shows which setting a box was created with
observability: the existing session-create info line gains the dynamic_ingress mode and range fields
layers:
- cli:
- crates/minimal/src/cmd/session.rs:
min session createcarries the box's [network] dynamic_ingress mode (allow, ask, deny) and its dynamic_allowed_range into the create request's IngressPolicy, from flags spelled in the same style as the egress flags (--dynamic-ingress <mode>,--dynamic-range <lo>-<hi>); a set mode or range makes the ingress declaration even with no static mapping; a malformed or inverted range, or a range with no mode, is a create-time error; nothing set keeps ingress None and the deny-all default;min session policyshows the resolved dynamic_ingress mode and range in both text and JSON, deny when no flag was given; any stdout write site this task adds to a-o jsonpath is tagged withOutputWriteError(.context(OutputWriteError)), so its write failure answersoutput_failedrather thanunspecifiedtest: cargo nextest run -p minimal create_carries_dynamic_ingress
test: cargo nextest run -p minimal create_rejects_bad_dynamic_range
test: cargo nextest run -p minimal policy_shows_resolved_dynamic_ingress
- daemon:
- crates/minimald/src/session.rs: the session-create info line names the dynamic_ingress mode and range the record holds
- crates/minimald/src/net/policy.rs: an absent dynamic_ingress (None) evaluates as deny, the same as an explicit deny
- crates/minimald/src/env.rs: the dynamic_ingress mode and range are host-side create inputs only; no in-box RPC sets or rewrites IngressPolicy, and the in-box expose request carries a port and no policy field
test: cargo nextest run -p minimald create_logs_dynamic_ingress
test: cargo nextest run -p minimald absent_dynamic_ingress_is_deny
test: cargo nextest run -p minimald in_box_expose_cannot_change_ingress_policy
- e2e:
- scripts/session-e2e.sh: a new leg creates a box with
--dynamic-ingress allow --dynamic-rangecovering one port, starts a listener on that port inside the box, runsmin net expose <port>in the box, asserts it reports published, assertsmin session policy(text and JSON) lists the runtime mapping, and asserts a port outside the range is refused with the out-of-range error and a deny box is refused with the deny error; a listen-publish leg, in a box created with--dynamic-ingress allowover a range, starts a server on an in-range port with no expose, asserts a peer box reaches it and a host-side probe reaches it on the box's address (resolving the box's name through the host answerer and connecting to the in-range port at the address it returns, the published loopback address NET-016 publishes on), closes the server and asserts both the peer's and the host probe's next connect are refused, and starts a server on an out-of-range port and asserts neither the peer nor the host probe can reach it; both legs run on the native lane, and on the VM lanes once VM-backed boxes publish runtime exposures (T80 #1878)test: just e2e
produces: none
consumes:
- crates/minimald/src/session.rs
- crates/sessions/src/lib.rs
proof: A box created with dynamic ingress allowed over a range accepts
min net exposefor a port in that range, the mapping shows inmin session policytext and JSON, an out-of-range port and a deny box are each refused with their own typed error, a create with no dynamic flags records and shows deny, and nothing inside a box can set or widen the setting; the flags are the interim source until the Box Spec [network] block lands (gominimal/inbox#570), when they become narrow-only; a server listening on an in-range port with no expose is reachable from a peer until it closes and an out-of-range listener never is; this task owns the CLI-level proof of NET-043 and NET-044 that T53 #1865 and #1884 carry only at unit level, and the reachability proof of NET-016 and NET-017 that T22 #1856 carries against the forwarder's control channel.depends on:
- blocked by #1896
Out of time
The coder ran out of its 7200s before it finished. If it left work uncommitted, its last commit is the runtime's snapshot of that work as it stood — the commit message says so — and not a finished step: it may not build or pass its tests. So this PR is a draft.
Proof
A box created with dynamic ingress allowed over a range accepts
min net exposefor a port in that range, the mapping shows inmin session policytext and JSON, an out-of-range port and a deny box are each refused with their own typed error, a create with no dynamic flags records and shows deny, and nothing inside a box can set or widen the setting; the flags are the interim source until the Box Spec [network] block lands (gominimal/inbox#570), when they become narrow-only; a server listening on an in-range port with no expose is reachable from a peer until it closes and an out-of-range listener never is; this task owns the CLI-level proof of NET-043 and NET-044 that T53 #1865 and #1884 carry only at unit level, and the reachability proof of NET-016 and NET-017 that T22 #1856 carries against the forwarder's control channel.Tests
cargo nextest run -p minimal create_carries_dynamic_ingresscargo nextest run -p minimal create_rejects_bad_dynamic_rangecargo nextest run -p minimal policy_shows_resolved_dynamic_ingresscargo nextest run -p minimald create_logs_dynamic_ingresscargo nextest run -p minimald absent_dynamic_ingress_is_denycargo nextest run -p minimald in_box_expose_cannot_change_ingress_policyjust e2eVerify
min task run agent-verifyexited 101, so this PR is a draft.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/minimal/src/cmd/list.rscrates/minimal/src/cmd/session.rscrates/minimald/src/session.rscrates/minimald/src/session/tests.rsscripts/session-e2e.shOut of scope
These files are outside the task's
layers:, so this PR is a draft:crates/minimal/src/cli.rscrates/minimal/src/cmd/list.rscrates/minimal/tests/cli.rscrates/minimald/src/session/tests.rsCloses #1895
Deferred to T84 (#1897)
On a VM-backed host the runtime-publish half cannot pass yet: the host-side registration does not carry the dynamic grant, so the host gate refuses the bind. Until T84 lands, this PR:
--dynamic-ingress allow|askon VM-backed hosts with a typed error naming T84 — Record a box's runtime-admitted ports in the VM host daemon's table on the in-VM daemon's report, within the host-held grant #1897;T84's plan block now takes over removing that refusal, plus the positive legs on both VM lanes:
min net exposereports published;min session policytext and JSON;allowpublishes and withdraws.On the native lane those legs need gvproxy installed (gominimal/inbox#887).
Schema note (
min/v1/session-policy)The ingress object gains
dynamic_ingressanddynamic_ingress_source(declaredordefault) in both thedeny_allanddeclaredshapes. The text render marks an absent stancedeny (default).Follow-ups from review
--ingressflag offown_ip, the same pre-create check this PR adds for the dynamic flags.