Repository navigation
review: batch-end round on the SOCKS5 opt-in, ports and lane-file state - #173
Merged
Merged
Conversation
CMGS
force-pushed
the
feat/egress-lane-file
branch
from
September 14, 2026 06:56
11e2fcb to
fe3416d
Compare
hostPort substituted the default port for one it could not parse, and serveConnect dialed the raw authority, so the verdict and the dial could name different ports. CONNECT example.com:99999 was judged and audited as port 443 (a rule listing 443 allowed it, the intercept path even terminated TLS for it) and only the dial failed; a path-form CONNECT with Host: example.com:+22 was judged as 443 and then dialed to port 22, which Go's dialer accepts, so a port a rule forbids was reachable with an allow verdict. An unparsable or zero port is now a deny with an audit line carrying no port; serveConnect dials the parsed host and port, and the intercept handler builds its authority from the same pair, so the verdict, the dial and the inner requests agree on one target.
The SOCKS5 door is the pool policy's call: the tenant's rules already gate
every tunnel through Eval, so the tenant-side AND removed a capability
without removing any reach and failed silently when a tenant policy omitted
the flag. ServesSocks trusts Validate instead of re-checking the tunnel rule
it guarantees. A raw SOCKS5 request for port 0 is refused like the HTTP
doors refuse one. hostOnly folds into hostPort.
The desktop unit's wait outlives the host's probe and write timeouts
(90 s + 120 s) instead of 30 s, exits non-zero when no verdict arrives so a
mis-timed build is visible, tests device-backed NICs the way silkd does, and
reads the file without tripping set -e; desktopsmoke checks the unit is
active and passes its probe URL as a positional argument. sockssmoke's IMAP
leg asserts curl's SOCKS5 refusal code, since %{http_code} is 000 for any
imap:// URL.
silkd latches a relay verdict (the host never unlocks a lane, a late lock
can land after the first exec) so the per-exec read happens once on the
bridge lane, base_env returns a static slice instead of a Vec per spawn,
the watcher moves its owned path into the frame, and an import moves to the
file's use group. Deploy docs gain the daemon/image lockstep for the lane
file, the strict-config rollback and the opt-in behavior change; the desktop
README names the installed apps and the unit's contract; two restating
comments and the logger locals in reconcile and refillOne follow the
house style. Comment lines: +5 / -8.
… docs hostPort treated every split error as a bare host, so a path-form CONNECT with Host: example.com:443:8443 was judged as 443 and audited as allowed before the dial failed; only a host with no colon, or a bracketed IPv6 literal, takes the default now. Three doc sentences still said "the policy" opts into the SOCKS5 door where it is the pool policy, or the tenant's only on a claim outside any configured pool.
…-bracketed A path-form CONNECT whose Host was a bracketed IPv6 literal without a port kept its brackets as the policy host, so the verdict was recorded on "[addr]" and the dial went to "[[addr]]:443" and failed after an allowed audit line; any bracketed text passed the same way. The literal is now unwrapped and parsed with netip, a valid IPv6 address becomes the bare host the rules and the dial both see, and anything else is refused.
A bracketed authority with no colon inside, such as "[not-an-address]", reached the bare-host branch first and was allowed under a wildcard rule before the dial failed. An authority that opens a bracket must now close it around a valid IPv6 address, or it is refused; the test runs under a wildcard policy and requires a no-port deny for the malformed forms.
A stray bracket such as "echo.internal]" or an empty Host still reached the bare-host default and was allowed under a wildcard rule before the dial failed. The default now applies only to a nonempty authority that contains no colon and no bracket; the wildcard-policy test gains rows for the stray bracket and the empty host, each a deny with no port.
CMGS
force-pushed
the
review/post-lane
branch
from
September 14, 2026 06:59
25299db to
feec461
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Batch-end round on the assumed-merged state of #169, #170 and #171, stacked on #171 (base
feat/egress-lane-file); merge order #169 → #170 → #171 → this, retargeting each child tomainbefore its base branch is deleted. Two commits: a fix with tests and the round itself. Codex reviewed the stack on the same thread as the three PRs and converged (see the end).Scope
The hygiene ledger decided what was read: 18 Go files due for the style lens and 12 for the judge lens (exactly the files the three PRs touched), every silkd and boot/init source file for an ownership-weighted Rust pass on the owner's request, all 17 docs pages plus the os-image READMEs in full. Readers: 2 Go style (sonnet), 2 Go judge (opus), 1 Rust ownership (opus), 2 docs (sonnet), the four /simplify lenses (3 sonnet, altitude on opus), 2 adversarial correctness/contract (opus). Every finding was re-read at the cited line before it was applied or declined; the ledger closes with 0 files due on both lenses.
Fixed —
fix: deny a tunnel whose authority names a port outside 1-65535(with tests)hostPortsubstituted the scheme default for a port it could not parse, andserveConnectdialed the raw authority, so the verdict and the dial could name different ports. Two readers proved it reachable:CONNECT example.com:99999was judged and audited as 443 (aports: [443]rule allowed it; the intercept path terminated TLS for it) and only the dial failed; a path-formCONNECT /xwithHost: example.com:+22was judged as 443, allowed, and dialed to port 22, which Go's dialer accepts — a port a rule forbids was reachable with an allow verdict, from the guest's own loopback relay. An unparsable or zero port is now a deny with an audit line carrying no port on both doors,serveConnectdials the parsed pair, the intercept handler builds its authority from the same pair, andserveSocksrefuses port 0 the same way. Tests:TestUnparsablePortIsDeniedNotDefaulted(99999 on CONNECT and forward, the+8443path-form CONNECT) andTestSocksPortZeroIsDeniedEvenByABareRule.Applied —
review: settle the door's owner, the lane wait and the ownership roundcomposite.ServesSocks()returns the pool's flag: the tenant's rules already gate every tunnel throughEval, so the tenant-side AND removed a capability without removing any reach and failed silently when a tenant policy omitted the flag (the docs example having to repeat the flag on the tenant was the tell). A tenant's flag counts only where the tenant policy is the whole policy. This reverses egress: make the SOCKS5 door opt-in per policy #169's composition; the altitude lens raised it and Codex accepted it.Policy.ServesSocks()returns the flag;Validatealready guarantees an opted-in policy has a rule that admitsCONNECT, and no runtime path mutates policies.set -e;desktopsmokeasserts the unit is active and passes its probe URL as a positional argument.routes_directly()latches arelayverdict (the host never unlocks a lane, but a late lock can land after the first exec, so only the positive answer is cached);base_envreturns a static slice instead of a Vec per spawn; the watcher moves its owned path into the frame; an in-function import moves to the file's group. The audit found nojson!churn, nounwrapoutside tests, every fan-out bounded; kept with triggers in perf: remaining performance ledger after the 2026-09 SOCKS5 batch #172: the eager 256-slot broadcast ring per exec, per-git-verb label strings.hostOnlyfolded intohostPort;sockssmoke's IMAP-on-143 leg asserts curl's SOCKS5 refusal code (%{http_code}is 000 for anyimap://URL, so the old assertion could not fail); a cold-provision row for the unlocked egress lane.deploy.mdUpgrading gains the daemon/image lockstep for/etc/silkd-lane, the strict-config rollback (a config withsocks5/portsdoes not load on an older sandboxd) and the opt-in behavior change;silkd.mdsays the 1080 dial is refused when the policy did not opt in; the desktop README names the installed apps and the unit's contract;egress.md/desktop.mdfollow the door decision; README lists thesockse2e driver.reconcileandrefillOne, exported consts first inlane.go, twot.Errorfwith constant strings becomet.Error. Comment lines in the round: +5 / −8.Declined, with reasons
Validate(judge: no behavioral effect) — kept, egress: let a rule constrain destination ports #162's shape, config-time.Lane,.nicsidecar rename (would force a golden rebuild),sync.OnceFuncrestructuring, six-parameterrelay,fmt.Errorfwith a static string (house style for config errors), two-clause WHY comments that need both halves.deploy.md; the marker shipped in no release.LOC
Round (
11e2fcb..edec7ec, Go + Rust, e2e drivers counted as prod): prod +93 −59 (net +34), test +119 −31 (net +88). The prod adds are the port fix's deny branches and the bare-host/IPv6 validation (+18), the SOCKS port-0 refusal (+1), silkd's relay latch (+9) and the twobase_envstatics (+4), desktopsmoke's unit check and positional probe (+3), sockssmoke's exit-code leg (+3); removals arehostOnly(−7), the comment cuts, the logger hoists and theServesSocksconjunct. Whole batch againstmain(ca6d08f..edec7ec): prod +330 −157 (net +173), test +258 −54 (net +204) — the three features plus the round.Gates
make go-lint10 ×0 issues.;asl -forwarder=falseclean on linux and darwin;go test -raceon egress/pool/engine/config;go vet ./...on e2e.cargo fmt --check,clippy --all-targets -D warnings, 111 tests on macOS and 111 in arust:1.98.0linux/arm64 container.rt-lane5):scripts/socks-e2e.shPASS (nine legs),scripts/egress-e2e.shNET=none PASS and NET=egress BRIDGE=sbxbr0 PASS,scripts/sandboxd-e2e.shBRIDGE=sbxbr0 PASS. Desktop lane ondesktop-lane5(desktop:24.04 + this silkd + thisguest-proxy-setup):desktopsmoke -net egressPASS on a 2xlarge egress-lane pool with a policy —guest-proxy.serviceactive,http_proxyin the session's environment, the allowed origin answering 200 through the relay (audited asGETport 80);desktopsmoke -net nonePASS with the checkpoint/branch leg.hostPortwith a test:c710dderefuses a malformed authority such ashost:443:8443instead of defaulting it (and names the pool policy as the door's owner in three doc sentences),edec7ecunwraps a bracketed IPv6 literal without a port so the rules and the dial see the bare address,c42f1a8checks bracket syntax before the bare-host default so[not-an-address]is a no-port deny under a wildcard rule too,25299dbdefaults only a nonempty bare host with no colon or bracket soecho.internal]and an empty Host are denied as well.hostPort's malformed-authority handling (all applied above), round 5 on25299db: "No blockers remain for the review branch." Lane branch egress: write the host's lane verdict into every NIC-bearing guest #171: "No blockers remain for the lane branch."