8000 feat(minimald): proxy runtime-published ports like a direct connection (T99) by minimal-foundry[bot] · Pull Request #2714 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(minimald): proxy runtime-published ports like a direct connection (T99) - #2714

Merged
norrietaylor merged 6 commits into
mainfrom
agent/2688-the-hostname-proxy-serves-runtime-published-port
Oct 28, 2026
Merged

norrietaylor merged 6 commits into
mainfrom
agent/2688-the-hostname-proxy-serves-runtime-published-port

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T99 — The hostname proxy serves runtime-published ports like a direct connection

slice: S0
kind: task
covers: NET-044, NET-069, NET-071
size: L
est: 320 code, 400 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: min bug bundles carry the daemon log, where each proxied refusal records the target box, the port and whether the port was declared, runtime-published or neither
observability: the proxy's per-request log line gains the route source (declared, runtime) for an admitted request; a refusal keeps its rate-limited warn line
layers:
- routes:
- crates/minimald/src/net/dns.rs: record_dynamic_hold and the listen-publish path add the port to the box's proxy route (own_route, Route::upstream) as well as to own_holders, and the withdrawal removes it, so the proxy's published set is the direct connection's
- crates/minimald/src/net/listeners.rs: a runtime publish and its withdrawal update the route through the registry in the same step the forward is bound or unbound
- crates/minimald/src/box_actor/tests.rs: an allow-decided expose lists the mapping in min session policy and is reachable through the proxy at once
test: cargo nextest run -p minimald expose_allow_publishes_and_lists
test: cargo nextest run -p minimald expose_deny_typed_error
- proxy:
- crates/minimald/src/net/proxy.rs: the proxy's verdict equals the direct connection's in every network mode, and the status mapping stays as it is: a refused verdict reads 403 naming the reason, an admitted request whose upstream refuses or times out reads 502; a runtime-published port is forwarded; a host-address target's bound port is forwarded even when undeclared, since that lane does not filter
- crates/minimald/src/net/switch.rs: the refusal path reads the published set, declared and runtime, rather than the declared ingress alone
test: cargo nextest run -p minimald proxy_undeclared_port_refused_like_direct
test: cargo nextest run -p minimald proxy_parity_across_network_modes
test: cargo nextest run -p minimald proxy_forwards_runtime_published_port
produces:
- crates/minimald/src/net/dns.rs
consumes: none
proof: A port published inside a box with min net expose or by starting a listener opens at http://<name>.min.internal:7654 as it does directly, and a port a direct connection cannot reach is not reached through the proxy either, in every network mode.

Proof

A port that a box publishes at runtime, with min net expose decided allow or by a listener, now opens at http://<name>.min.internal:7654 from the moment the box's relay gate admits it, the same as a direct connection. The proxy refuses the port with the direct connection's 403 refusal before the publish and after the withdrawal. A port that no declaration and no runtime publish names stays refused in every network mode. expose_allow_publishes_and_lists shows a real request through the proxy that reaches an exposed port, and proxy_parity_across_network_modes holds the proxy's verdict to the gate's verdict for runtime ports in all three network modes.

Notes from the agent: Design: the route does not keep a second copy of the runtime ports. HostnameRegistry::resolve reads them from the box's live relay gate (new BoxGate::runtime_published_tcp, which is the listen-published set plus the exposed set), keyed by the lease the attach reported. Route carries them in a new runtime set, and Route::upstream and the new Route::source read it. The proxy and a direct connection therefore read the same set, and no lifecycle step can let the two drift. Because of this, two layer notes needed no change. (1) dns.rs record_dynamic_hold and the withdrawal do not add or remove route ports: the gate admission that the listen watcher already makes is what the route reads. (2) listeners.rs is not changed. The expose path in box_actor/ports.rs commits its publication before the audit record and the gate admission. A route update hooked on BoxPublications (the only listeners.rs hook the expose path reaches) would have forwarded an unaudited expose that the gate refuses, so I did not use one. Status mapping: I did not change it. The task says an upstream timeout reads 502, but the code before this change answers 504 Gateway Timeout for a dial timeout, and that is still so. A refused upstream reads 502. Observability: admitted requests log a debug line 'forwarding a proxied…[truncated]

Tests

line exit tail
cargo nextest run -p minimald expose_allow_publishes_and_lists 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-a965... 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 minimald expose_deny_typed_error 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-50c3... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_undeclared_port_refused_like_direct 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-4d09... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_parity_across_network_modes 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-4216... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_forwards_runtime_published_port 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-bf81... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.

Verify

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

Base

The base branch moved while the coder worked; it was merged in before the tests ran.

Closes #2688

The hostname proxy now reads the target box's runtime-published ports
(an allowed expose, a published listener) from its live relay gate when
the name resolves. The proxy verdict then admits exactly the ports a
direct connection reaches. Admitted requests log their route source, and
refusals record route_source=none.
expose_allow_publishes_and_lists now registers the box's relay gate and
lease, then shows the port refused through the proxy before the expose
and reachable through it right after.
@coderabbitai
coderabbitai Bot commented Oct 28, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 521751bb-d0cb-41a6-a04d-c22df708b220

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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

Approved: every review thread resolved at 221770f9e396.

@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: 2 of 3 personas ran (correctness, security); did not complete: supply-chain.

Comment thread crates/minimald/src/net/proxy.rs Resolved
The daemon's default filter is info, so a debug line never reached the
log a min bug bundle carries.
@minimal-arch minimal-arch Bot left a comment
8000 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 change conforms to the parity rule in §7.1 line 249. The proxy reads the runtime ports from the box's live relay gate, which is the gate a direct connection meets. A port that is not declared and not admitted at runtime is refused with the direct connection's refusal. A box with no gate gets no runtime ports, so the change fails closed. The host_ip lane stays unfiltered, as line 249 says. The task text says 502 for an upstream timeout, but the code answers 504. The design does not name a status for this, so correct the plan text, not the code.

Ruled at 221770f against gominimal/arch@109541a48c31 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 — approval withheld

Verdict COMMENT — APPROVE withheld: 1 unresolved thread — minimal-foundry[bot] crates/minimald/src/net/proxy.rs:542. No findings.

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

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p minimald expose_allow_publishes_and_lists 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-c63b... 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 minimald expose_deny_typed_error 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-400f... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_undeclared_port_refused_like_direct 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-5461... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_parity_across_network_modes 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-906f... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.
cargo nextest run -p minimald proxy_forwards_runtime_published_port 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-c048... Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workbench/crates/version) Compiling decode v0.0.1 (/workbench/crates/decode) Compiling mlog v0.

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/minimald/src/net/proxy.rs:542: Raised the admitted-request line in handle_connection_io from debug! to info!. The daemon's default filter is info (logging.rs:75), so the line now reaches the log that min bug bundles carry.

A port published inside a box with min net expose or by a listener opens at http://<name>.min.internal:7654 as it does directly. A port that a direct connection cannot reach is refused through the proxy too, in every network mode. Each admitted proxied request now leaves an info line with its route source in the default daemon log.

Notes from the agent: Revise round 1: raised the per-request 'forwarding a proxied request' line from debug to info, because the daemon's default EnvFilter is info (crates/minimald/src/logging.rs:75). No other change.

@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 221770f9e396 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.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 28, 2026
@norrietaylor norrietaylor left a comment
8000 Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Babysitter review at 221770f9. I read the diff against the T99 plan block (NET-044, NET-069, NET-071). The route keeps no second copy of the runtime ports: HostnameRegistry::resolve reads them from the box's live relay gate (BoxGate::runtime_published_tcp, the listen-published set plus the exposed set), and Route::upstream and the new Route::source read that set. So the proxy and a direct connection read one set, and a publish or withdrawal cannot leave them apart. That covers the dns.rs and listeners.rs layer notes with no listener change, as the PR body explains. Route::source returns Declared, Runtime or Ungated, a port in neither set is refused with the direct connection's 403, and a host-address (Ungated) target forwards its bound port. The admitted-request line in handle_connection_io is now info! with route_source, which meets the observability line. All five plan test lines are in the Tests table with exit 0, and proxy_forwards_runtime_published_port is new. Arch PASS at this head, no open threads. No findings.


Generated by Claude Code

@norrietaylor norrietaylor changed the title T99 — The hostname proxy serves runtime-published ports like a direct connection feat(minimald): proxy runtime-published ports like a direct connection (T99) Oct 28, 2026
@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Babysitter approval at 221770f: every merge gate passes at this head (arch PASS at 221770f, zero open threads, every lane aggregator green). Squash title feat(minimald): proxy runtime-published ports like a direct connection (T99) and the squashed message pass commitlint. Queued for squash merge.


Generated by Claude Code

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 7097b96 Oct 28, 2026
37 checks passed
@norrietaylor
norrietaylor deleted the agent/2688-the-hostname-proxy-serves-runtime-published-port branch October 28, 2026 08:57
norrietaylor pushed a commit that referenced this pull request Oct 28, 2026
… (T99) (#2714)

* feat(minimald): forward runtime-published ports through the proxy

The hostname proxy now reads the target box's runtime-published ports
(an allowed expose, a published listener) from its live relay gate when
the name resolves. The proxy verdict then admits exactly the ports a
direct connection reaches. Admitted requests log their route source, and
refusals record route_source=none.

* test(minimald): prove an allowed expose opens through the proxy

expose_allow_publishes_and_lists now registers the box's relay gate and
lease, then shows the port refused through the proxy before the expose
and reachable through it right after.

* style(minimald): format the proxy and expose tests

* fix(minimald): log admitted proxy requests at info

The daemon's default filter is info, so a debug line never reached the
log a min bug bundle carries.

---------

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>
(cherry picked from commit 7097b96)
norrietaylor added a commit that referenced this pull request Oct 28, 2026
The cherry-picked change names main's types and helpers. Release/0.7.0
predates the box renames and the traced spawn wrapper, so its tests and
one doc link use the release's own names:

- BoxGate is SessionGate, BoxPolicy is SessionPolicy, and
  boxes::DynamicIngress is sessions::DynamicIngress.
- crate::traced does not exist here, so the new tests spawn with
  tokio::spawn, as the release's other proxy tests do.
- The release's spawn_backend_on takes a full address and returns
  nothing, so proxy_forwards_runtime_published_port picks the backend
  port with a probe listener, the pattern the release's tests use.
- The proptest's no-wider-reach check uses the outer gate, which now
  carries the runtime publishes, and asks admits_tcp. On this branch
  admits_direct_tcp covers the declared ports only, while admits_tcp
  also covers the listen-published and exposed ports. That is the set
  main's admits_tcp covers.

The behaviour is #2714's: runtime-published ports join the proxy's
route set, and the proxy's verdict equals the direct connection's.
norrietaylor pushed a commit that referenced this pull request Nov 5, 2026
…#2714)

T99. The hostname proxy reads the target box's runtime-published ports (an allowed
expose, a published listener) from its live relay gate when the name resolves, so its
verdict admits exactly the ports a direct connection reaches. Admitted requests log
their route source at info, and refusals record route_source=none.

Backport of #2714, folded from #2724.

Adapted to the line:
- BoxGate, BoxPolicy and boxes::DynamicIngress become SessionGate,
  sessions::SessionPolicy and sessions::DynamicIngress.
- crate::traced::spawn becomes tokio::spawn in the new tests.
- The line's spawn_backend_on takes a full address, so
  proxy_forwards_runtime_published_port picks its port with a probe listener.
- The parity proptest checks the gate that carries the runtime publishes and asks
  admits_tcp, since admits_direct_tcp covers only the declared ports here.

Also keeps proxy_refuses_a_withdrawn_port_like_direct (NET-069, from #2732): it
publishes and withdraws through the gate only, because the line's listen watcher has no
record_dynamic_hold.

(cherry picked from commit 7097b96)
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.

T99 — The hostname proxy serves runtime-published ports like a direct connection

1 participant

0