Skip to content

row 130 E5a: every framed connector dial honours the proxy environment, as 1.5.5 did - #804

Open
MattJackson wants to merge 14 commits into
predevfrom
lane-egress-e5a-proxy
Open

MattJackson wants to merge 14 commits into
predevfrom
lane-egress-e5a-proxy

Conversation

@MattJackson

Copy link
Copy Markdown
Collaborator

/status row 130 (E5a), per the ARCHITECT ruling of 2026-10-12. The engine's proxy behaviour moves into the connector unchanged, so every dial through the connector honours HTTPS_PROXY / HTTP_PROXY / ALL_PROXY / NO_PROXY exactly as 1.5.5's reqwest clients did. This keeps 1.5.5 behaviour, so no owner signature is needed.

Why. Neither the connector nor the transport plugins supported a proxy. Every dial already on the connector (the door planes, oidc, cimd) went straight to its target and ignored a configured proxy. This also unblocks E6 (the plugins.fetch download through the connector) and E5 (deleting the engine pool).

What moves. busbar-core-connector/src/proxy.rs is the kernel engine's mod tunnel decision, moved verbatim:

  • per-scheme slots with the ALL_PROXY fallback, uppercase variable names read before lowercase;
  • NO_PROXY with reqwest's full rule set (host, domain suffix, IP literal, CIDR);
  • Proxy-Authorization: Basic built from the proxy URL's userinfo;
  • an https:// proxy and a garbage value both refuse the boot, and the refusal never echoes the password;
  • the CONNECT request bytes, and the rule that only a 2xx answer within an 8 KiB head opens the tunnel.

Where it runs.

  • compose.rs: a dial through a proxy connects to the proxy and writes the CONNECT request first. It reads the proxy's answer raw, under the engine's 10 s CONNECT bound. Only then do the secure layer (the target's name and certificate) and the framer run, over the tunnel.
  • lib.rs (Connector::proxying): the proxy applies to a framed dial that is not pinned to an address set. The scheme's slot must name a proxy and NO_PROXY must not exclude the host.
    • A carrier carried as itself (a database's or directory's raw stream) is never proxied, because 1.5.5's drivers never used one.
    • Judging happens in two steps. First the target: its name check, plus its own resolution when it resolves here. A name that does not resolve here is passed to the proxy, as the engine's tunnelled path did.
    • Then the proxy's own address, judged with reach: a private proxy is admitted, cloud metadata never is.
  • process::build reads the environment once at boot.

Tests (src/tests/proxy_tests.rs):

  • The engine's 1.5.5-semantics rows, ported: precedence, garbage values, the NO_PROXY suffix and IP/CIDR matrix, scheme scoping, password redaction, and the answer rule.
  • End to end through Connector::open: a framed dial to a name only the proxy resolves writes exactly CONNECT upstream.test:8080 HTTP/1.1, Host and the Basic credential, and its exchange then rides the tunnel.
  • RED arms:
    • a 407 from the proxy refuses the exchange;
    • a NO_PROXY host and a raw-stream need both dial direct, and the proxy is never asked;
    • with no proxy configured, a name nothing resolves is refused.

Proof: ModernCI only (owner rule 2026-10-12, no M3 builds).

#780 also touches lib.rs, so whichever lands second rebases. Rows 104 and 105 are untouched.

…t, as 1.5.5 did

ARCHITECT ruling 2026-10-12: the kernel engine's proxy behaviour moves into the connector
unchanged, so every dial through the connector honours HTTPS_PROXY / HTTP_PROXY / ALL_PROXY /
NO_PROXY exactly as 1.5.5's reqwest clients did. The connector and the transport plugins had no
proxy support, so every dial already on the connector (the door planes, oidc, cimd) went direct
past a configured proxy.

- busbar-core-connector/src/proxy.rs: the engine's decision moved verbatim (busbar-kernel
  egress/engine/mod.rs `mod tunnel`): scheme-scoped slots with the ALL_PROXY fallback, uppercase
  before lowercase, NO_PROXY's full rule set (host, domain suffix, IP literal, CIDR), Basic
  Proxy-Authorization from userinfo, an https:// proxy and a garbage value refused without echoing
  the password; the CONNECT request bytes and the 2xx / 8 KiB answer rule of the engine's
  connect_handshake.
- compose.rs: a dial through a proxy connects to the proxy, writes the CONNECT request before
  anything else, reads the proxy's head raw (under the 10 s CONNECT bound), then runs the secure
  layer (the target's name and certificate) and the framer over the tunnel. Bytes after the head
  are the target's.
- lib.rs: Connector::proxying; a FRAMED dial (never a carrier carried as itself, as 1.5.5's
  drivers never used a proxy) not pinned `within` an address set, to a host the scheme's slot has
  a proxy for and NO_PROXY does not exclude, is judged in two steps: the target (its name arm, and
  its own resolution when it resolves here; a name that does not resolve here goes to the proxy, the
  engine's tunnelled arm), then the proxy's address with reach (a private proxy admitted, cloud
  metadata never). process::build reads the environment once; a set value that does not parse
  refuses the boot, as the engine's install did.
- Tests (src/tests/proxy_tests.rs): the engine's semantics rows ported (precedence, garbage,
  NO_PROXY suffix and IP/CIDR matrix, scheme scoping, password redaction, the answer rule), and end
  to end through Connector::open: a framed dial to a name only the proxy resolves writes exactly
  `CONNECT upstream.test:8080 HTTP/1.1`, `Host`, the Basic credential, and its exchange rides the
  tunnel; RED arms: a 407 refuses the exchange; a NO_PROXY host and a raw-stream need dial direct
  and the proxy is never asked; with no proxy a name nothing resolves is refused.
@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

promote into predev: DENY @73ab28a2a: 0 failing test row(s), 5 DENY row(s)

DENY rows (5)

gate row detail
secret-hygiene secret-hygiene:check3-message 1 violation(s) (BLOCKING, #53; SECRET_GATE_REPORT_ONLY=1 is the report-only escape): crates/busbar-core-connector/src/proxy.rs:397 secret-subject-parameter (target) — wrap each secret VALUE in busbar_
kind-isolation kind-isolation:law0 6 hit(s) off the [[law0]] ceilings, 4 finding(s) over 14 neutral crate(s): law0-rise busbar × instance:secret 37 hit(s) against a ceiling of 35: this landing grew a neutral crate's instance vocabulary
kind-isolation kind-isolation:law0-base 1 hit(s) grown over the merge-base 62fdbdf, 1 cell(s): law0-grown busbar-core-connector × transport 68 -> 69 hit(s) over the same cell at the merge-base 62fdbdf (its '[[law0]]' row 68 there): this b
kind-isolation-ship kind-isolation:law0 6 hit(s) off the [[law0]] ceilings, 4 finding(s) over 14 neutral crate(s): law0-rise busbar × instance:secret 37 hit(s) against a ceiling of 35: this landing grew a neutral crate's instance vocabulary
kind-isolation-ship kind-isolation:law0-base 1 hit(s) grown over the merge-base 62fdbdf, 1 cell(s): law0-grown busbar-core-connector × transport 68 -> 69 hit(s) over the same cell at the merge-base 62fdbdf (its '[[law0]]' row 68 there): this b
Denied because (20)
  • shard 1/4: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base
  • shard 1/4: gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base
  • shard 1/4: gate:secret-hygiene: green at base, red now (secret-hygiene:check3-message)
  • shard 1/4: gate:kind-isolation row kind-isolation:law0: figure rose 5 -> 6
  • shard 1/4: gate:kind-isolation-ship row kind-isolation:law0: figure rose 5 -> 6
  • shard 2/4: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base
  • shard 2/4: gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base
  • shard 2/4: gate:secret-hygiene: green at base, red now (secret-hygiene:check3-message)
  • shard 2/4: gate:kind-isolation row kind-isolation:law0: figure rose 5 -> 6
  • shard 2/4: gate:kind-isolation-ship row kind-isolation:law0: figure rose 5 -> 6
  • shard 3/4: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base
  • shard 3/4: gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base
  • shard 3/4: gate:secret-hygiene: green at base, red now (secret-hygiene:check3-message)
  • shard 3/4: gate:kind-isolation row kind-isolation:law0: figure rose 5 -> 6
  • shard 3/4: gate:kind-isolation-ship row kind-isolation:law0: figure rose 5 -> 6
  • shard 4/4: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base
  • shard 4/4: gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base
  • shard 4/4: gate:secret-hygiene: green at base, red now (secret-hygiene:check3-message)
  • shard 4/4: gate:kind-isolation row kind-isolation:law0: figure rose 5 -> 6
  • shard 4/4: gate:kind-isolation-ship row kind-isolation:law0: figure rose 5 -> 6

Judged against base 62fdbdf3b: 3 new red, 2 worse, 3 standing (excused).

New reds: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base, gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base, gate:secret-hygiene: green at base, red now (secret-hygiene:check3-message)

Already red on predev, not this PR's: 8
gate row detail
construction one-pick-site 3 production call site(s) of 'pick_among(' (ceiling 2): crates/busbar-kernel-egress/src/walk.rs:298; crates/busbar-llm/src/engine/exhaustion/fallback.rs:120; crates/busbar-llm/src/engine/pipeline.rs:8
kind-isolation kind-isolation:deps 3 finding(s), 93 shipped edge instance(s) over 30 class(es), 93 declaration(s); 56 '[[dep]]' row(s), 0 question(s): unlisted-dep-edge legacy -> plane busbar-llm -> busbar-plane-llm is a shipped edge w
kind-isolation-ship kind-isolation:deps 10 finding(s) over 93 shipped edge(s): ship-edge kernel -> hooks busbar-kernel -> busbar-hook-ranking is 'not-allowed': the architecture grants no kernel -> hooks edge, and the ship criterion is the a
kind-isolation-ship kind-isolation:test-deps 8 finding(s) over 32 test edge(s): ship-edge cleanliness -> export busbar-core-admin -> busbar-export-prometheus is 'not-allowed': the architecture grants no cleanliness -> export edge, and the ship c
kind-isolation-ship kind-isolation:faces 2 finding(s) over 40 crate(s): foreign-entry crates/busbar busbar is kind 'root' and implements 'Transport' 1 time(s) in shipped source — the entry face of kind 'transport'. A trait implementation is
kind-isolation-ship kind-isolation:legacy-drain 3 finding(s): transitional-live qa/kind-isolation.toml 'busbar-a2a -> busbar-core-admin' (legacy drain: the retiring A2A engine's tests drive the admin surface that drained into the cleanliness tier)
ship-ready ship-ready:ship-twin 'kind-isolation-ship' is not green: kind-isolation:deps (a dependency the architecture does not grant is still in the graph); kind-isolation:test-deps (a dependency the architecture does not grant is
structure-lint structure-lint:plane-dup:unledgered 22 finding(s): PLANE-DUPLICATE (module): 'config.rs' — a2a:crates/busbar-a2a/src/a2a/config.rs decisions:crates/busbar-plane-decisions/src/config.rs voice:crates/busbar-voice/src/config.rs (the ledger

tests passed: 0, failed: 0. Run: https://github.com/GetBusbar/busbar/actions/runs/38312118329 . Artifact verdict-73ab28a2a33a4993ddf6ae53010ecbd8f6b5ee3c (failures.json, junit.xml, raw.log; 90 days).

…d parameter

secret-hygiene check 3 read connect_answer's `authority` parameter as a secret subject: the
CONNECT target is `target` throughout (ProxySpec::endpoint for the proxy's own host:port). The
kind-isolation law0 scan read the verbatim engine word for the proxy's choice as the decisions
plane's bare id: it is `choice` here. Three comments say socket instead of naming a carrier.
MattJackson added a commit that referenced this pull request Oct 12, 2026
… the one connector

The kernel's last dial of its own, preflight::plugin_fetch_downloader (the engine on a cold
one-shot posture), is deleted. The root downloads every plugins.fetch artifact through the
process's one connector (BUSBAR-1.6.0.md §3 stage 3c, "fetch plugins through the connector"):
crates/busbar/src/root/plugin_fetch.rs declares a provider-class need per URL scheme under the
root's own identity (NeedId 1 and 2; 0 is the authorization server's) and sends one GET, so the
connector's one guard judges and pins the address, the proxy environment applies (#804), and the
http door writes the request with the client defaults 1.5.5's Client::new().get() sent:
`accept: */*` before `host`. That is the oracle cell egress.fetch|plugins|url, known-red as
egress-plugin-fetch-accept; its mark stays until ModernCI shows the cell green.

What stays the download's own moves verbatim: https for a public host and plaintext only for a
loopback or private one, the cloud-metadata denylist extended by security.blocked_metadata_hosts,
no redirect followed, a declared Content-Length over the cap refused before a body byte and the
body counted against the cap, every refusal in 1.5.5's words, and the dedicated thread with its own
current-thread runtime.

The kernel's PluginsFetch row now hands the root the operator's denylist instead of a downloader;
the kernel test build's stand-in refuses every download (it has no connector).

Tests: the kernel's two downloader tests move to root/tests/plugin_fetch.rs, driven over a
connector of the build's linked doors against scripted loopback far ends (an honest and an absent
Content-Length over the cap, a within-cap body byte for byte, a redirect refused in its 1.5.5
words), plus the wire row (exactly `GET <path>`, `accept: */*`, `host`) and a pre-check RED row.
# Conflicts:
#	crates/busbar-core-connector/src/compose.rs
#	crates/busbar-core-connector/src/lib.rs
…he kernel engine takes its choice

ARCHITECT ruling 2026-10-12 (a move, not a copy): proxy environment parsing (the variables, the
scheme rule, the credential) lives in busbar-core-connector only. The kernel engine's own parser
(parse_proxy, the environment read, the precedence resolve, the redaction) is deleted with its
suite's rows, which the connector's suite holds.

- busbar-contract proxy_route: the shared shape, ProxyHop (endpoint and credential, Debug redacted)
  and ProxyChoice (the secured and plain slots and the exemption list, with curl's rule set, moved
  verbatim), naming no transport.
- busbar-core-connector proxy: the parser fills ProxyChoice; Connector::proxy hands it out.
- busbar-kernel egress engine: install_proxy(choice) replaces install_proxy_tunnel_if_configured;
  the root installs the connector's choice right after the connector is installed; busbar-llm no
  longer reads the environment at its runtime build.
…on, the per-dial choice names no plane, a CONNECT refusal names its addr; law0 rows settle at the merged tree's measure

- busbar-contract ProxyHop::auth() -> proxy_authorization(): a contract-exported `auth`
  identifier is masked tree-wide by kind-isolation's contract_identifiers, which hid every
  `auth-header`/`busbar-auth-*` hit outside the contract (busbar x auth 49 -> 21, kernel x auth
  50 -> 31, plugin-loader x auth 5 -> 0 were that mask, not a drain).
- proxy_route.rs: "The per-dial decision:" read as a `decision:` section key (contract x plane +1);
  reworded.
- proxy.rs connect_answer: the refused-CONNECT message interpolated a `target` parameter beside
  the word "proxy" (secret-hygiene check 3); the parameter is the `host:port` addr it always was.
- qa/kind-isolation.toml: kernel x plane 1132 -> 1128, kernel x auth 50 -> 49 (drains with the
  tunnel the engine gave up); kernel x transport 284 -> 278 and core-connector x transport
  68 -> 69, a ruled net-flat MOVE of the `"http"` proxy-scheme check, reason on both rows.
MattJackson added a commit that referenced this pull request Oct 13, 2026
…nto lane-egress-e6-plugin-fetch

Both sides carry the same E5a commits (e56e48f, 73ab28a, 3587dab), so every shared row
takes #804's side rather than theirs + ours - base, which would count E5a twice:

- busbar-core-connector x transport: O == T == 69 (the one ruled MOVE of the "http" proxy-scheme
  check) -> theirs, count and cite.
- busbar-kernel x transport: O == T == 278 -> theirs (cite: 5 drained + 1 moved, both E5a; this
  branch's E6 download move is net 0 on the row, as its gates measured 278 over a 284 base).
- busbar-kernel x plane: theirs 1128 (predev 1132 - E5a's 4); ours 1137 was E5a's same -4 over the
  older 1141, so T + O - B would drain it twice.
- busbar x auth 21 -> 49, busbar-kernel x auth 31 -> 49, busbar-plugin-loader x auth 0 -> 5: this
  branch's lowered values measured the mask #804 removes (busbar-contract exported a bare `auth`
  accessor, which kind-isolation's contract_identifiers masks tree-wide). With
  ProxyHop::proxy_authorization the rows read predev's 49 and 5, and kernel x auth 49 is the one
  real drain (the `auth header` comment that left with the engine's proxy-parse test). E6's diff
  adds or removes no auth-vocabulary text.
- proxy_route.rs doc and proxy.rs connect_answer: #804's text (the `addr` parameter keeps the
  1.5.5 refusal wording, so proxy_tests.rs takes #804's expectation).
…a miscount

busbar-contract exports a `header` identifier, and kind-isolation's contract_identifiers masks every
whole-token `header` outside the contract, so the engine test's "the auth header is prebuilt Basic"
comment was never an `auth-header` hit. Its deletion drained nothing: the row keeps predev's count
and cite (gates measured 50 on #805's 979c4b1, which carries this change).
qa/kind-isolation.toml busbar-kernel x transport: base 284, ours 278 (E5a: -5 drained, -1 ruled MOVE
to busbar-core-connector), theirs 267 (#741's -17 listener MOVE to the root): different changes, so
theirs + ours - base = 261, predev's cite kept with E5a's clause renumbered 267 -> 261.
qa/kind-isolation.toml busbar-kernel x transport: base 267, ours 261 (E5a: -5 drained, -1 ruled MOVE to
busbar-core-connector), theirs 266 (#532: auth/token.rs's "http" check deleted, a different hit):
theirs + ours - base = 260, predev's cite kept with E5a's clause renumbered 266 -> 260.
qa/kind-isolation.toml busbar-kernel x plane: base 1132, ours 1128 (E5a's proxy-tunnel drain: two
`llm engine` prose mentions, the `decision:` doc key, the `installed decision` assert message),
theirs 1128 (#674: the ask-state seal's `crate::mcp::roots` links and `\x6dcp` domains). O == T,
but these are two different drains of equal size, not one change carried twice, so
theirs + ours - base = 1124, predev's cite kept with E5a's clause renumbered 1128 -> 1124.
qa/kind-isolation.toml busbar-kernel x plane: base 1128, ours 1124 (E5a's proxy-tunnel drain), theirs
1122 (E2's net_guard deletion): different drains, so theirs + ours - base = 1118, predev's cite kept with
E5a's clause renumbered 1122 -> 1118. The kernel no-socket pin (#796) stays exact on the merged tree.
@MattJackson

Copy link
Copy Markdown
Collaborator Author

row 155 B2 (#974) edits crates/busbar-kernel/src/proxy/mod.rs at fn auth_failure_status_and_kind and the tail of fn ingress_error; disjoint from this PR's hunks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant