8000 T124 — A box's package build fetches its declared web sources under the box's egress verdict by minimal-foundry[bot] · Pull Request #2795 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T124 — A box's package build fetches its declared web sources under the box's egress verdict - #2795

Merged
norrietaylor merged 25 commits into
mainfrom
agent/2730-a-box-s-package-build-fetches-its-declared-web-s
Oct 31, 2026
Merged

norrietaylor merged 25 commits into
mainfrom
agent/2730-a-box-s-package-build-fetches-its-declared-web-s

Conversation

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

T124 — A box's package build fetches its declared web sources under the box's egress verdict

slice: S16
kind: task
covers: NET-155
size: M
est: 200 code, 220 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: when a box's rules refuse a web source its package declares, the build fails and its error names the source host and the box, in the words T104 pins: "the build of could not reach : the egress rules of box do not allow it"
observability: the daemon logs one line per web-source fetch it makes for a box's build, naming the box, the source host and the classifier leaf the fetch ran in; the NET-080 node-plane fetch record no longer lists a box's web-source fetch
layers:
- op:
- crates/op/src/sources.rs: a web-source fetch for a build placed for a box runs in 8000 that box's classifier leaf, so the box's egress verdict decides it; the cache lookup and the content-hash check stay as they are
- crates/common/src/remote_storage.rs: the web download takes the placement it runs under and opens its connection in it
- crates/op/src/specs.rs: both SourceLoad sites (SpecBuild::build_deps_mapped and materialize_prebuilt) pass the build's classifier_leaf to the source load; its test fetchers follow the new signature
- crates/op/src/patched.rs: its test UnusedFetcher follows the new download_web signature
test: cargo nextest run -p op web_source_fetch_runs_in_the_given_classifier_leaf
- daemon:
- crates/minimald/src/sandbox_progress.rs: record_fetch stops recording a box build's FetchSource with a scheme as node-plane traffic; registry, cache and index fetches stay node-plane (§5.3 line 145)
- crates/minimald/src/box_actor/tests.rs: a deny-all box whose package declares a web source fails its build naming the host and the box, and a box allowed that host builds it
- crates/minimald/src/env.rs: a box build's web-source fetch refused by the box's rules maps to the refusal T104 pins, "the build of could not reach : the egress rules of box do not allow it", with the source host as the destination
- crates/minimald/tests/classifier_root_integration.rs: its NoSources fetcher follows the new download_web signature
test: cargo nextest run -p minimald box_build_web_source_under_box_verdict
- spec:
- docs/specs/18-spec-box-networking/18-spec-box-networking.md: NET-155 says a package build run for a box fetches its declared web sources under that box's egress verdict, and NET-080 lists the node plane's own lookups as registry, cache and control plane only
produces: none
consumes: none
proof: A deny-all box whose package declares a web source sees its build fail naming the source host and the box, and a box allowed that host builds it.
depends on:
- blocked by #2691


Generated by Claude Code

Proof

A deny-all box whose package declares a web source now has its build fail with "the build of could not reach : the egress rules of box do not allow it". A box whose leaf allows that host builds the package. box_build_web_source_under_box_verdict shows both: it runs a real op::SpecBuild of a prebuilt package with a web source in a deny leaf and in an allow leaf, through the build side operation and through the in-box patched build. The fetcher in that test stands in for the kernel verdict.

Notes from the agent: Mechanism: a socket belongs to the cgroup of the process that creates it, and one thread cannot join a leaf alone. So a placed download in common::RemoteStorage sends reqwest through a loopback relay that requires a per-download token. reqwest uses the relay as its proxy, so https stays end to end through CONNECT. For each connection, a forked child writes itself into the leaf's cgroup.procs, creates the socket and passes it back over SCM_RIGHTS. The child uses only write, socket, sendmsg and _exit. The relay then connects that socket. common tests drive this end to end against a local HTTP server, with a plain file in place of cgroup.procs. No test here runs it against a real cgroup and nft table, because this box has no cgroup2 mount and runs without root. The root lane (just test-root-integration) still owes that live proof. DNS for the source host still resolves in the daemon, not in the leaf. Only the TCP connection is under the box's verdict. The daemon finds the refused host because op's spec build now writes 'could not fetch the source : ' to the build's stderr when a web-source load fails, and env.rs refused_destination reads that line. I used this route so box_ops.rs (BuildTap) did not change, because it is outside the layers. The observability log line is in op/src/sources.rs: one info line per web-source load placed in a leaf, with box_id (the leaf's name), host and …[truncated]

Residuals

  • #2817: the #[tracing::instrument] spans on download_web* record the source URL, with any credentials.
  • #2802: the daemon still fetches an own-address box's declared web sources, and every gs:// source, from its own network namespace with the node's address. No box leaf decides those fetches.

Tests

line exit tail
cargo nextest run -p op web_source_fetch_runs_in_the_given_classifier_leaf 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-cb88... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minimald box_build_web_source_under_box_verdict 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-05db... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling stdlib v0

Verify

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

Closes #2730

A web-source fetch for a build placed for a box opens each connection
from a socket created in that box's classifier leaf, through a loopback
relay, so the box's egress verdict decides the fetch (NET-155). The
cache lookup and the content-hash check stay as they are. A failed
fetch is written to the build's stderr with its URL.
A box build's web-source fetch that the box's rules refuse fails with
the error that names the source host and the box (NET-155). The
node-plane fetch record no longer lists a box build's web sources,
which the build now fetches in the box's own classifier leaf.
NET-155 now says a package build run for a box fetches its declared
web sources in the box's classifier leaf. NET-080 lists the node
plane's own lookups as the registry, the cache and the control plane.
@coderabbitai
coderabbitai Bot commented Oct 31, 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: aecfb222-1de7-4a8a-aec3-4959c656f923

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 Oct 31, 2026 •
Copy link
Copy Markdown
Contributor Author

Reviewed: 1 finding, none blocking at 162041383103.

Address the comments on the diff.

Clobber: none found.

@github-actions
github-actions Bot commented Oct 31, 2026 •
Copy link
Copy Markdown
Contributor

Spec lint

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

docs/specs/18-spec-box-networking/18-spec-box-networking.md:159: 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:344: P004 NET-040 names test 'fresh_install_own_ip_ingress_publishes_loopback', which was not found in the repository
docs/specs/18-spec-box-networking/18-spec-box-networking.md:349: 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:387: 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:392: 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:397: 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:402: 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:763: 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:789: 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:813: 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 — advisory review

Verdict COMMENT — 3 findings, 3 inline.

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

Comment thread crates/common/src/remote_storage.rs Resolved
Comment thread crates/op/src/specs.rs Resolved
Comment thread crates/minimald/src/env.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 connection of a placed web-source fetch now leaves from the box's leaf, and that conforms to §4.1 line 54. The DNS lookup does not: Relay::connect resolves the source host in the daemon. §5.3 line 145 limits the daemon's own lookups to registry, cache and control plane. C1 moves the lookup into the leaf. record_fetch now drops web sources that a leaf-less install still fetches as node-plane traffic. C2 restores the record for those installs. C3 files the own-address gap, and C4 adds the live root-lane proof that the PR body says is not run.

Ruled at c7f2edc against gominimal/arch@8640f85e193d by claude-opus-5-5.

Comment thread crates/common/src/remote_storage.rs Resolved
Comment thread crates/minimald/src/sandbox_progress.rs Resolved
A placed download for a leaf in the deny subtree now refuses each
connection before the relay resolves the host. The node's resolver is
not under the box's rules. The line a failed web-source fetch writes to
the build's stderr now strips credentials and queries from every URL
that its error quotes.
A failed web-source fetch names a refused destination only when its
error says the download could not connect. A bad hash or a failed
extract no longer reads as an egress refusal.
@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/common/src/remote_storage.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 deny-leaf lookup fix and the narrower refusal mapping close part of the earlier ruling. Two items stay open. For an allow leaf, placed::Relay::connect still resolves the box's source host in the daemon's cgroup. Section 5.3 line 145 keeps that path for the daemon's own lookups only. C1 moves the lookup into the leaf. The live test that the spec names never runs a web-source fetch, and section 4.1 line 54 requires a measured placement. C2 adds that root-lane test.

Ruled at a033216 against gominimal/arch@8640f85e193d by claude-opus-5-5.

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

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

line exit tail
cargo nextest run -p op web_source_fetch_runs_in_the_given_classifier_leaf 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-93fc... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minimald box_build_web_source_under_box_verdict 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-803a... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling stdlib v0

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6yOtO3: A placed download for a deny-subtree leaf now refuses each connection in Relay::connect before lookup_host runs, so a deny-all box makes the node resolve no name. Allow leaves still resolve on the node. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — PRRT_kwDOPdfcxM6yOtO4: report_failed_fetch now passes the error text through strip_urls, which removes credentials, query and fragment from every URL in it. A URL that does not parse becomes . Covered by failed_fetch_line_strips_urls. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — PRRT_kwDOPdfcxM6yOtO6: refused_destination now maps a could not fetch the source line only when it also contains could not connect to . A hash-mismatch line gives None, and box_build_web_source_under_box_verdict asserts it. (not posted on the thread: not an open thread on the pull request)

A deny-all box whose package declares a web source sees its build fail with "the build of fetcher could not reach downloads.example.org: the egress rules of box sealed do not allow it". A box whose leaf allows that host builds the package. The node now resolves no host name for a deny-all box's fetch.

Notes from the agent: Revise round 1. The download_web_with_verification_and_caching signature gained an admits_nothing bool, so common's lib.rs needs no new export. Thread 1 is fixed only for deny leaves. A box whose rules allow some destinations still has its source host resolved by the node's resolver. To resolve inside the leaf, the fork child must run a DNS query, and that is not async-signal-safe. That case needs its own task. clippy -p common -p op -p minimald --all-targets is clean. I did not run just clippy-strict.

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

Review of head a033216 against T124's plan block (#2730) and NET-155/NET-080. Approve.

Checked: both SourceLoad sites (build_deps_mapped, materialize_prebuilt) receive the box's classifier_leaf on the in-box build path (env.rs → PatchedBuild → SpecBuild) and the side-op path (orchestrator/local_backend.rs); RemoteStorage::download_web places each connection's socket in the leaf (forked child writes cgroup.procs, creates the socket, passes it back over SCM_RIGHTS, async-signal-safe calls only); a deny leaf is refused before resolution; cache lookup and content-hash check unchanged; registry and index fetches stay node-plane; the refusal text matches T104's exactly, names the source host, and appears only on a connect refusal, not a hash mismatch or a 404; credentials and queries are stripped from the stderr line; both named tests exist and assert behaviour; the spec edits match the layer.

Non-blocking:

  1. crates/common/src/remote_storage.rs:611: a_placed_download_connects_from_the_placement has no #[cfg(target_os = "linux")], unlike its neighbour; on macOS socket_in_cgroup returns Unsupported and the test panics under cargo test -p common. No lane runs that today.
  2. crates/minimald/src/sandbox_progress.rs:213: record_fetch now drops every scheme-bearing FetchSource, including fetches made with classifier_leaf: None (own-address boxes; host-address boxes with host_ip_enforcement = none). Those fetches leave from the daemon's cgroup and are now neither placed nor recorded. This matches NET-155's scope; the record gap needs a follow-up.

@norrietaylor
Copy link
Copy Markdown
Member

/revise Architecture review on a033216: CONFIRM with four conditions. The task's layers now include crates/common/Cargo.toml and a root-lane test line (see #2730).

  1. C1: the name lookup must leave from the leaf. tokio::net::lookup_host resolves in the daemon's cgroup, so the box verdict does not decide the DNS query. In the placed path, make the forked child that joins the leaf also create a SOCK_DGRAM socket and hand it back, as it does for the TCP socket. Encode the A/AAAA query with the workspace hickory-proto (add it to crates/common/Cargo.toml as hickory-proto.workspace = true), send it to the first nameserver in /etc/resolv.conf, and parse the answer. Add a placed test that proves the query socket is made in the leaf.
  2. C2: root-lane live proof. Add #[ignore] test web_source_fetch_in_a_box_leaf_follows_its_verdict_over_a_loaded_table in crates/minimald/tests/classifier_root_integration.rs, modelled on package_build_in_a_box_leaf_follows_its_verdict_over_a_loaded_table: a box leaf under a deny verdict fails its web-source fetch, and an allow verdict succeeds.
  3. C3: residual. An own-address box's web source and every gs:// source still fetch from the daemon cgroup. That is filed as #2802. Name it as a residual in the PR body. Do not change the spec in this PR.
  4. C4: keep the fetch record for unplaced fetches. record_fetch now returns early for every placed box. Return early only for an http/https source when the sources were placed: add sources_placed: bool to FetchRecord, set it in fetch_record() in crates/minimald/src/env.rs, and keep recording gs:// and unplaced fetches. Add a test for both cases and update the NET-080 comment there.

Also (my review, non-blocking): a_placed_download_connects_from_the_placement in crates/common/src/remote_storage.rs (around line 611) uses Linux-only calls but has no #[cfg(target_os = "linux")]. Gate it.

The relay of a placed web download now asks the first resolv.conf
name server from a UDP socket that a child creates in the cgroup, so
the box's egress rules decide the query as well as the connection.
Only an http or https source of an install placed in the box's leaf
is the box's traffic. A gs:// source, and a web source of a box with
no leaf, still leave from the daemon, so NET-080 records them.
Add the root-lane proof over a loaded table: a deny leaf refuses the
placed download, and an allow leaf fetches the source.
@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/common/src/remote_storage.rs Resolved
Comment thread crates/common/src/remote_storage.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: PASS_WITH_CONDITIONS · 1 condition · the full ruling

The compare closes the items from the last two rounds. The name query now leaves from the box's leaf, the NET-080 record is exact, and the root lane carries a live proof. One gap stays, and this PR did not cause it. An own-address box has no leaf, so the daemon fetches its web sources with the node-plane address. Section 4.1 line 54 says box traffic and node-plane traffic never share an identity. C1 files an issue for that gap and names it in the NET-155 comment. Then it is a filed residual.

Ruled at d4cbd3e against gominimal/arch@8640f85e193d by claude-opus-5-5.

Comment thread crates/minimald/src/env.rs Resolved
@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 Oct 31, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: the diff is +2108/-53 (2161 lines), past 3x the plan's estimate of 420 lines (est: 200 code, 220 tests, as #2730 reads now), at round 1 (the last trigger was a /revise note). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2730 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 Oct 31, 2026
@norrietaylor
Copy link
Copy Markdown
Member

/revise Last round on 44d23de. The cap is lifted for this round. Change only these three, all in the task's layers. Do not edit the PR body.

  1. Refusal names the wrong host (crates/minimald/src/env.rs, refused_destination, around line 3025). For a web-source line, take the host from the relay's could not connect to <host>:<port> text in rest. Fall back to the source URL's host only when that text is missing. A source that redirects to a CDN must name the CDN host. Add a test for the redirect case and for the fallback.
  2. Accept loop spins (crates/common/src/remote_storage.rs, serve, around line 263). On an accept error, sleep briefly (for example 50 ms) before continue, so a persistent error such as EMFILE does not spin. Add a test if it is cheap. Otherwise give the reason in the commit message.
  3. Fetch log line (crates/op/src/sources.rs:141). Log one line that says what happened: a cache hit, or a fetch in the box's leaf. Log it after download_web returns, from a signal that says whether the download opened a connection. If that signal needs a change outside the layers, keep the current line and say so in the commit message.

The placed download's relay now records whether its client asked it for
a connection. download_web returns that flag beside the path, so the
log line says either that the fetch ran in the box's classifier leaf or
that the download cache served the source.

The relay's accept loop pauses 50 ms after an accept error, so a lasting
error such as EMFILE does not spin. No test forces an accept error: a
TcpListener gives no cheap way to make accept fail on demand.
A source that redirects to a CDN host fails on the CDN host. The refusal
now reads the host from the download's could not connect to <host>:<port>
text, and falls back to the source URL's host only when that text names
no host.
@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 review of 528dc85 against T124. All three items of the last /revise are closed:

  • 528dc85d: the refusal takes the host from the relay's could not connect to <host>:<port> text (connect_host), and falls back to the source host only when that text is missing. Tests cover the redirect and the fallback.
  • df81191c: serve pauses 50 ms (ACCEPT_RETRY) after an accept error. The log line now comes after the download and says which happened, a fetch in the leaf or a cache hit with no connection, from the relay's asked flag.

@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_WITH_CONDITIONS · 1 condition · the full ruling

The compare conforms. The fetch log line now says exactly what happened, and the refusal names the host that the box's rules refused. One item is still open. Section 4.1 line 54 says the cgroup placement holds only when it is measured. The live proof web_source_fetch_in_a_box_leaf_follows_its_verdict_over_a_loaded_table did not run on this head. Run it on the root lane as C1 says, and record the result in the PR.

Ruled at 528dc85 against gominimal/arch@8640f85e193d 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 — 2 findings, 2 inline.

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

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

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

line exit tail
cargo nextest run -p op web_source_fetch_runs_in_the_given_classifier_leaf 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-de93... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minimald box_build_web_source_under_box_verdict 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-3251... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling stdlib v0.0.22 (/workbench/crates/stdlib) Compiling version v0.8.0 (/workb

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6yS8Lv: refused_destination now takes the host from the download's 'could not connect to :' text, so a redirect names the CDN host. It falls back to the source URL host only when that text names none. Tests cover both cases. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — PRRT_kwDOPdfcxM6yS8Lx: serve now sleeps 50 ms (ACCEPT_RETRY) after an accept error before it continues, so a lasting EMFILE does not spin. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — crates/op/src/sources.rs:157: The line now logs after download_web returns. The relay reports whether a connection was asked for, so the line says fetched in the leaf, served by the download cache, or failed.

A deny-all box whose package declares a web source sees its build fail, and the error names the box and the host the download could not reach. For a redirect, that is the redirect target's host. A box allowed that host builds the package.

Notes from the agent: Revise round: addressed the three directive items. (1) refused_destination reads the host from the 'could not connect to :' text and falls back to the source URL host. New test box_build_web_source_under_box_verdict_names_the_host_it_could_not_reach covers redirect, IPv6 and fallback. (2) The relay accept loop sleeps 50 ms after an accept error. No test was added, because accept cannot be made to fail cheaply. The commit message gives this reason. (3) The relay records whether its client asked for a connection. SourceFetcher::download_web now returns (PathBuf, bool), and sources.rs logs after the fetch: fetched in the leaf, served by the cache, or failed. A tuple is used in place of a named type, because a named type needs an export in crates/op/src/lib.rs and crates/common/src/lib.rs, which are outside the layers. RemoteStorage gets a new download_web_placed method that returns the flag. Also ran cargo nextest run -p common placed (11 passed) and -p minimald box_package_build_refusal (5 passed). Deferred (lane: root): cargo nextest run -p minimald --run-ignored only web_source_fetch_in_a_box_leaf_follows_its_verdict_over_a_loaded_table. It now also asserts that the allow-leaf fetch reports a connection. It was not run.

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

The runtime will not revise this pull request again on its own: the diff is +2244/-64 (2308 lines), past 3x the plan's estimate of 420 lines (est: 200 code, 220 tests, as #2730 reads now), at round 1 (the last trigger was a /revise note). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2730 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 Oct 31, 2026
@norrietaylor
Copy link
Copy Markdown
Member

/revise One fix on 528dc85. The cap is lifted for this round. Change only this. Do not edit the PR body.

Credentials in the fetch failure log (crates/op/src/sources.rs:153, the Err(e) arm of the new info!). error = %format!("{e:#}") writes the whole error, and a reqwest error quotes the request URL, with any user:password@ and signed query token. Run the text through the same scrubbing that report_failed_fetch uses: strip_urls in crates/op/src/specs.rs:375. Make it pub(crate) there, or move it to a shared module in crates/op, both in the layers. Add a test: an error whose text holds https://user:secret@host/x?sig=abc logs neither secret nor sig=abc.

Leave the existing debug! line after it alone unless it also prints the URL with credentials. If it does, log the stripped URL there too.

The info line for a box's failed web-source fetch logged the whole
error, and a reqwest error quotes the request URL with its user,
password and signed query. Run it through strip_urls, as the build's
stderr report does. The debug line after a fetch logs the stripped
URL too.
@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/common/src/remote_storage.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: PASS_WITH_CONDITIONS · 1 condition · the full ruling

The change conforms to §4.1 line 54 and §5.3 line 145. A box's web-source fetch and its name query now leave from the box's classifier leaf, and the node-plane record lists only the daemon's own fetches. The new commit removes URL credentials from the fetch log, and a test pins that. The live proof against a real cgroup and a loaded table is still open. C1 requires the root-integration job to run it green on this head before merge. Residual #2802 covers own-address boxes and gs:// sources.

Ruled at 1620413 against gominimal/arch@8640f85e193d by claude-opus-5-5.

@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 review of 1620413. 16204138 runs the failure log's error through strip_urls, and logs the stripped URL in the later debug! line. A test with a fetcher whose error quotes https://user:secret@host/x?sig=abc checks that neither secret reaches the log. This closes both HIGH threads.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p op web_source_fetch_runs_in_the_given_classifier_leaf 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-092a... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/wo
cargo nextest run -p minimald box_build_web_source_under_box_verdict 0 dalek v5.0.0 Compiling version v0.8.0 (/workbench/crates/version) Compiling sandbox v0.0.1 (/workbench/crates/sandbox) Compiling ssh-encoding v0.3.0 Compiling mfile v0.0.1 (/workbench/crates/mfile) Compiling opentelemetry-proto v0.33.0 Compiling opentelemetry-http v0.33.1 Compil

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6yT-vZ: The failed-fetch info! line now logs strip_urls(&format!("{e:#}")). A new test checks that an error quoting https://user:secret@host/x?sig=abc logs neither secret nor sig=abc. (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — PRRT_kwDOPdfcxM6yT-va: Same fix as the thread on line 153: the error goes through strip_urls before it is logged. The debug! line after a fetch also logs the stripped URL now. (not posted on the thread: not an open thread on the pull request)

A deny-all box whose package declares a web source sees its build fail naming the source host and the box, and a box allowed that host builds it. In this round, the daemon log line for that failed fetch also no longer carries the user, password or signed query of any URL the fetch error quotes.

Notes from the agent: Revise round 1: only the change the note asked for. strip_urls and stripped_url in crates/op/src/specs.rs are now pub(crate). The failed-fetch info! line in SourceLoad::run passes its error through strip_urls. The debug! line after a successful fetch printed the full Url, credentials and query included, so it now logs stripped_url(url) as the note asked. New test web_source_fetch_failure_log_strips_url_credentials captures events with a small in-test tracing Subscriber, because op has no tracing-subscriber dev-dependency and crates/op/Cargo.toml is not in the layers. I checked that it fails with the old line. I did not re-run the minimald test line this round, because no minimald file changed. Deferred (lane: root): cargo nextest run -p minimald --run-ignored only web_source_fetch_in_a_box_leaf_follows_its_verdict_over_a_loaded_table.

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

The runtime will not revise this pull request again on its own: the diff is +2366/-65 (2431 lines), past 3x the plan's estimate of 420 lines (est: 200 code, 220 tests, as #2730 reads now), at round 1 (the last trigger was a /revise note). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2730 by hand.

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit c7f6ddd Oct 31, 2026
37 checks passed
@norrietaylor
norrietaylor deleted the agent/2730-a-box-s-package-build-fetches-its-declared-web-s branch October 31, 2026 22:07
norrietaylor pushed a commit that referenced this pull request Nov 6, 2026
A web-source fetch for a build placed for a box opens each connection
from a socket created in that box's classifier leaf, through a loopback
relay, and asks its name query from the same leaf. The box's egress
verdict then decides the fetch (NET-155). On a VM host the frames carry
the box mark, so the host gate decides them by the box's rules and not
by the node-plane baseline. The cache lookup and the content-hash check
stay as they are.

A fetch that the box's rules refuse fails the build with the source
host. The node-plane fetch record no longer lists a web source fetched
in a box's leaf. A gs:// source, and a web source of a box with no leaf,
still leave from the daemon and are recorded.

Backports [#2795](#2795) to the 0.7 line.
The change is T124 from main with these names adapted, because the line
predates two moves on main:

- sandbox::config is sandbox2::config, in op and in minimald's tests.
  The line predates the crate rename in
  [#2106](#2106).
- box_ops::{BuildPlacement, BuildTap} is session_sop::{BuildPlacement,
  BuildTap}, in the new test in minimald's session/tests.rs. main's
  box_actor/tests.rs is that file on the line.

(cherry picked from commit c7f6ddd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T124 — A box's package build fetches its declared web sources under the box's egress verdict

1 participant

0