Repository navigation
feat(minimald): steer credentialed names to the proxy and fail closed while it is down - #2519
Conversation
The DNS gate answers a steered box's A lookup of a credentialed name with the Box Egress Proxy's address while its listener is live, and SERVFAIL while it is down, so the lookup never falls back to real DNS (BEP-062). AAAA, HTTPS and SVCB lookups of a steered name stay NODATA (BEP-078). ProxyListenerState in net::dns carries the listener's state to the gate. The relay's gate construction does not wire it yet.
…entialed-names-to-the-proxy-and-fail-c
The merge from main added BoxPolicy::quic443, so the fixture no longer compiled.
|
Important Review skippedBot user detected. To trigger a single review, invoke the
|
|
Approved: every review thread resolved at @norrietaylor resolved the last thread, which releases the approval this review had withheld. Nothing to do. |
There was a problem hiding this comment.
arch-review: CONDITIONS · 2 conditions · the full ruling
The gate logic conforms to §5.4 line 169 and §5.3 line 148. A steered name is never forwarded, and a new listener state reads down. But no production code calls DnsGate::with_steering or ProxyListenerState::set_live, so a running daemon steers no name. The PR closes #2281 and names no issue for the wiring. C1 changes the PR body to Refs #2281 and records the wiring as a residual with an issue. C2 puts that issue and the flip condition in the two dead_code comments.
Ruled at 9b90b4d against gominimal/arch@109541a48c31 by claude-opus-5-5.
A query with a steered and an unsteered question was answered with the steered addresses only, so the other question got no answer and no error. Forwarding it would leak the steered name to real DNS, so the relay now answers the whole query SERVFAIL.
…entialed-names-to-the-proxy-and-fail-c
|
No revise round started on the runtime's review findings, because epic #1501 is stopped. The runtime starts no round on its own while its epic is not walking, and none was counted. A person's |
|
Round 1 of 10 on the runtime's review findings, pushed as
Verify Review threads the coder judged:
With the proxy stopped, a dns-steered box that resolves github.com gets SERVFAIL, never a real address. A query that mixes github.com with an unsteered name now also gets SERVFAIL. It is not forwarded and it is not half answered. Notes from the agent: Revise round 1: one thread. I chose SERVFAIL over forwarding for a mixed query, because forwarding sends the steered name to real DNS and the real resolver can answer it with a real address. The servfail test now covers the mixed case while the listener is live. I did not run just ci or clippy-strict. |
|
/revise Round 2 of #2519, on the arch ruling at 9b90b4d and the review findings that stay open. No round is in flight. The epic owner ruled on 2026-10-25 that this PR keeps
One commit, Conventional Commits subject, body lines at most 100 characters. Generated by Claude Code |
A query with a steered A question and an AAAA, HTTPS or SVCB question was answered NODATA by decide, so the steered A got no SERVFAIL while the proxy was down. Such a query now goes to the steered rule. REFUSED and NXDOMAIN still come first, and a query of only AAAA, HTTPS or SVCB stays NODATA. The interim comments on ProxyListenerState and DnsGate::with_steering now name #2523 and the condition that removes their dead_code allow.
There was a problem hiding this comment.
minimal-review — approval withheld
Verdict COMMENT — APPROVE withheld: 5 unresolved threads — minimal-foundry[bot] crates/minimald/src/net/dns_gate.rs:683, minimal-arch[bot] crates/minimald/src/net/dns_gate.rs, minimal-arch[bot] crates/minimald/src/net/dns.rs, minimal-foundry[bot] crates/minimald/src/net/dns_gate.rs:631, minimal-foundry[bot] crates/minimald/src/net/dns_gate.rs:506. No findings.
Panel: 3 of 3 personas ran (correctness, security, supply-chain).
There was a problem hiding this comment.
arch-review: PASS_WITH_CONDITIONS · 1 condition · the full ruling
The gate now fails closed for every steered query shape that §5.3 line 151 and line 147 govern, and BEP-078 conforms. No production code calls DnsGate::with_steering or ProxyListenerState::set_live yet. So a running daemon steers no box, and BEP-062 holds only at the gate. C1 wires BoxGate::for_session and removes the dead_code allows. Issue #2523 owns C1 as a filed residual, so the change can merge with C1 open.
Ruled at 4e50ffe against gominimal/arch@109541a48c31 by claude-opus-5-5.
|
Round 2 of 10 on a /revise note, pushed as
Verify Review threads the coder judged:
In the gate tests, a dns-steered box that resolves github.com while the proxy is stopped gets SERVFAIL and never a real address. This includes a query that holds the A question beside an AAAA, HTTPS or SVCB question. A running daemon does not steer yet. #2523 tracks the wiring in BoxGate::for_session. Notes from the agent: Commit 4e50ffe addresses directive items 2, 3 and 4. Item 1 (the ## Residuals section) and item 5 (thread replies) are edits to the pull request. I have no credential for them. The runner writes the body, so it must add this residual: #2523 — BoxGate::for_session in crates/minimald/src/net/switch.rs calls DnsGate::with_steering(policy, , , ), and the switch-side bep host calls ProxyListenerState::set_live when the listener goes up or down. Closes #2281 stays as it is. My instructions do not let me resolve threads. For item 3: a query that holds a steered A question now skips the NODATA rule of decide and goes to answer_steered. REFUSED and NXDOMAIN still come first, and a query of only steered AAAA, HTTPS or SVCB questions stays NODATA. A temporary revert of the fix made the new mixed-type assertion fail. The dns.rs comment uses the directive's exact wording on one line, which is longer than 100 characters. rustfmt accepts it. Clippy reports no warnings in the changed files. I did not run just clippy-strict. |
There was a problem hiding this comment.
minimal-review — approved
Verdict APPROVE — every review thread is resolved; @norrietaylor closed the last one. The review of 4e50ffe9c892 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.
There was a problem hiding this comment.
Babysitter approval at 4e50ffe: every merge gate passes at this head (arch PASS with its condition held on the filed residual #2523, zero open threads, every lane aggregator green, bot resolutions re-verified). Squash title feat(minimald): steer credentialed names to the proxy and fail closed while it is down and the squashed message pass commitlint. Queued for squash merge.
Generated by Claude Code
T30 — Steer credentialed names to the proxy and fail closed while it is down
slice: S4
kind: task 8000
covers: BEP-062, BEP-078
size: M
est: 280 code, 120 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: the bundle's daemon log tail carries each SERVFAIL answered for a steered name while the proxy was down, with the name and the box
observability: one rate-limited warn log line per steered name answered SERVFAIL, and one info line when the proxy's listener comes back
layers:
- dns:
- crates/minimald/src/net/dns_gate.rs: steered names answer the proxy's address; SERVFAIL while it is down; NODATA for AAAA, HTTPS, SVCB
- crates/minimald/src/net/dns.rs: carries the proxy's listener state to the gate
test: cargo nextest run -p minimald steered_name_servfail_while_proxy_down
test: cargo nextest run -p minimald steered_name_aaaa_https_svcb_are_nodata
produces: none
consumes: none
proof: With the proxy stopped, a dns-steered box resolving github.com gets SERVFAIL, never a real address.
depends on:
- blocked by #2276
Proof
At the gate level, a dns-steered box (steering dns, both, or unset) that resolves github.com while the proxy's listener is down gets SERVFAIL and no address. Its query is never forwarded. When the listener is live, the box gets the proxy's address. AAAA, HTTPS and SVCB lookups of a steered name get NODATA in both states. The two named tests show this. In a running daemon this is not true yet, because nothing builds the gate with steering. See notes.
Notes from the agent: The proof is not delivered end to end. DnsGate::with_steering and net::dns::ProxyListenerState exist and are tested, but no production code calls them. The gate is built in crates/minimald/src/net/switch.rs (BoxGate::for_session, about line 1469), and that file is outside this task's layers, so I did not touch it. To wire it, one call is needed there:
.with_steering(policy, <module hosts>, subnet.box_egress_proxy_address(), <shared ProxyListenerState>). Two more things are missing for that. (1) minimald does not depend on the bep crate, so it has no access to bep::module::GITHUB's host set. The caller must pass the hosts in, or minimald/Cargo.toml must add bep. (2) No code in minimald knows whether the proxy's listener is live. The proxy runs in the switch crate's bep_host beside gvproxy (minvmd). Something must call ProxyListenerState::set_live, and that is probably the blocker #2276. Until the wiring lands, both items carry #[cfg_attr(not(test), allow(dead_code))] with a comment that says why. Behaviour: the steering check runs after the existing REFUSED/NXDOMAIN/NODATA rules, so AAAA/HTTPS/SVCB of a steered name keep the NET-136 NODATA (BEP-078). A steered query with only non-A questions (TXT, MX) gets an empty NOERROR while live, so it is never forwarded. The steered A answer has a 30 s TTL. Observability: a SERVFAIL writes one warn line per name per box per minute through the gate's PolicyWarnLimiter, with session_id (the box's switch address) and name. A change from down to live…[truncated]Tests
cargo nextest run -p minimald steered_name_servfail_while_proxy_downcargo nextest run -p minimald steered_name_aaaa_https_svcb_are_nodataVerify
min task run agent-verifyexited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.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/net/dns_gate.rsResiduals
BoxGate::for_sessionincrates/minimald/src/net/switch.rscallsDnsGate::with_steering(...)for everydnsorbothbox, and the switch-side bep host callsProxyListenerState::set_livewhen the listener goes up or down. Until it lands, the steering in this PR has no production caller and the BEP-062 proof holds in the gate's tests only; both interim comments name the issue.Closes #2281
Generated by Claude Code