Skip to content

review: whole-repo round on the SOCKS5 and locked-NIC state - #168

Merged
CMGS merged 10 commits into
mainfrom
review/post-165
Sep 14, 2026
Merged

CMGS merged 10 commits into
mainfrom
review/post-165

Conversation

@CMGS

@CMGS CMGS commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Replaces #166, which GitHub closed when its base branch (feat/egress-socks5, #164) was deleted at merge and cannot be reopened. Same ten commits, rebased onto main after #164 and #165 landed; the cherry-picked #165 commits it used to carry are gone. Whole-repo review round run on the state "#164 and #165 both merged"; three of the commits come from the Codex passes on it (two rounds, converged with "No blockers remain").

Scope

The hygiene ledger decided what was read: 24 Go files due for the style lens and 17 for the judge lens (every file the two PRs touched plus what had moved since the 2026-09-11 round), every Rust and Python file changed since that round (9 silkd/boot sources, 10 silkd test files, 5 SDK/mcp modules and their tests), and all 18 docs pages in full. Readers: 3 Go style (sonnet), 2 Go judge/LOC (opus), 1 Rust source (opus, ownership-weighted), 1 Rust tests (sonnet), 1 Python (sonnet), 4 docs-drift (sonnet); 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 (three defects, each in its own commit with a test)

  • sandboxd/egress/socks.go — the SOCKS5 accept loop returned on any error other than net.ErrClosed and the pool discarded the return, so an EMFILE on a busy node killed the SOCKS5 door for the claim's life while the HTTP door (http.Server, which backs off) kept serving. The loop now backs off 50 ms and continues; only the listener closing ends it.
  • sandboxd/pool/reconcile.go — the restart sweep locks an adopted egress claim's tap when it finds it open (a node that moved from CNI networks to bridges) but never told the guest, so silkd kept routing into the lock; the marker now follows the late lock, gated by the same open-tap test so an ordinary restart pays no RPC.
  • silkd/src/session.rs — an env key such as A B survived shell quoting but failed bash's export, and the init script's status was discarded, so the session came up with the variable silently missing. session_create now refuses a name that is not a shell identifier with bad_request, and each export in the init script exits the shell on failure like the cd step, so a name bash itself refuses (UID, BASHOPTS) fails the create instead of vanishing. The test that pinned the silent drop asserts the refusal; a second test pins the readonly case. This is a deliberate contract change from partial application to loud rejection.

Also: interceptsmoke's direct-egress probe now bypasses the proxy (the same defect #165 fixed in egresssmoke; verified on hardware, see below).

Applied from the lenses

  • Comment register and budget on the due files: inline WHY lines in pool.go and reconcile.go in the STE 101 register, two restating godocs deleted, three multi-line smoke comments condensed and stripped of issue-tracker narration, Tunnels' godoc cut to two facts, the find-budget comment states the invariant it protects. Comment lines in the round: +19 / −25.
  • type AuditFunc func(Event) in proxy.go (the func type was spelled twice; sibling DialFunc already existed); the refill-concurrency table test runs under t.Run like every other table in its file.
  • Python: -> None on every constructor, _peers -> list[str], _request(body: dict | None), __exit__(*exc: object).
  • Docs: README's driver list gains sockssmoke and qaab; desktop.md says guest-proxy.service tests NIC presence only, so it does not follow the lock marker, and its claim-shape advice now sends web tasks through a none-lane egress policy instead of net=egress (a real gap the docs reader found; teaching the desktop unit to wait for a lane verdict is desktop: guest-proxy-setup should follow the lane the host decides, not NIC presence #167).

Declined, with reasons

  • Rust integration tests: helpers before tests (uniform across all 10 files, the crate's convention), bare assert! without messages, fixed sleeps, a 7× three-line write block in fs_e2e.rs — test-only, previous rounds accepted them, no rule broken.
  • net_egress::serve and boot::run without /// — one-line docs on public items are an allowance, not a requirement, and a review commit never raises a file's comment count.
  • Unbounded guest→host relay fan-out (one vsock connection per guest connection, no cap) — pre-existing on 3128, doubled by 1080; a per-sandbox bound belongs in its own change.
  • git verbs still refused on the none lane although the relay could carry them — the follow-up named in egress: tell the guest when its NIC is locked so the bridge lane gets the proxy variables #165.
  • json alias on encoding/json/v2, membership-list loops without t.Run, Counters placement, per-connection readN allocations, smoke poll loops that ignore ctx — kept and recorded in the ledger.
  • Codex asked for an unconditional guest mark of every adopted egress claim at reconcile, to cover a node that kept live egress claims across an upgrade of sandboxd (an old binary locked the tap without marking). Declined: deploy.md makes draining old claims part of upgrading, the in-contract flip (CNI→bridge) is marked in the same sweep that locks, and an unconditional mark would make startup depend on every adopted guest answering a silkd RPC.

LOC (loc-justify, exact counts)

2026-08-14 base (7b9021b) this HEAD delta
Go prod / effective (prod − blank − comments) 19752 / 15605 19852 / 16545 +100 / +940
Go prod comments 2452 1506 −946
Go prod without e2e scaffolding 17544 17178 −366
e2e scaffolding (counted as prod) 2208 2674 +466
Rust prod / comments 4488 / 650 4441 / 294 −47 / −356
Python prod 1888 2051 +163

Comments are 7.6 % of Go prod: comments are not the story. Growth since the base is features (#148 +232, #159 qaab +199, #152 desktop flavor +180, #161 +180, #127 +159, #129 +156) while every review commit is net-negative (#126 −624, #130 −377, #154 −87, #155 −58). Per package the month is pool −222, types −79, store/peer −69, config −50 against egress +106 (the SOCKS5 door) and e2e +466.

This round's own commits (ten): prod +80 −55 (net +25), test +36 −10 (net +26). Itemized prod adds: session-name validation and the export guard +14 (bug fixes), the is_env_name unit test inside mod tests +11 (counted as prod by the file split; test in substance), AuditFunc +3 (signature rule), reconcile late-mark +3 (bug fix), accept backoff +2 (bug fix); comment deletions −7. The two feature PRs together: prod +224 / test +325 against main.

Gates

  • make go-lint: 10 × 0 issues. (5 modules × linux/darwin); make go-test all modules ok with -race; asl gate clean on both GOOS.
  • silkd: cargo fmt --check, clippy -D warnings, cargo test 110 passed on macOS and in a rust:1.98.0 linux/arm64 container; boot/init the same (16 passed).
  • Python: ruff format --check (28 files), ruff check clean; pytest 156 / 6 / 5 (sdk/python, langchain, openai) on Python 3.12.
  • Hardware, cocoon-test1, this branch's sandboxd on the rt-socks2:24.04 guest image: scripts/intercept-e2e.sh PASS (direct egress blocked with the fixed probe), scripts/socks-e2e.sh PASS, scripts/sandboxd-e2e.sh PASS. The silkd session change is covered by the crate's Linux test run; no guest image was rebuilt for it.
  • After the rebase onto main (816c5b1): go build ./... and go test of pool, egress, server pass; the two conflicted files of the egress: tell the guest when its NIC is locked so the bridge lane gets the proxy variables #165 rebase (docs/silkd.md, pool/egress_test.go) resolve byte-identical to this branch.

CMGS added 10 commits September 14, 2026 10:10
The accept loop returned on any error that was not net.ErrClosed, and
the pool discarded the return, so an EMFILE on a busy node killed the
SOCKS5 door for the rest of the claim's life while the HTTP door on the
same sandbox kept serving; http.Server backs off and retries in that
case. The loop now does the same and ServeSOCKS returns nothing, since
only the listener closing ends it.
resyncEgress locks a tap it finds open — a node that moved from CNI
networks to bridges across a restart — without telling the guest, so
silkd kept routing directly into a lock. The marker now follows the
late lock, gated by the same open-tap test so an ordinary restart pays
no RPC. Inline comments in the file move to the STE 101 register and
quarantineClaim's godoc leads with its name.
A key such as "A B" survived shell quoting but failed bash's export,
and the init script's exit code was discarded, so the session came up
with the variable silently missing. session_create now refuses a name
that is not a shell identifier with bad_request before spawning the
shell; the hostile-key test that pinned the silent behavior asserts the
refusal instead.
The same defect egresssmoke fixed: the image's proxy variables sent the
plain curl through the intercepting relay, which answered, so the
blocked-egress check could not pass. sockssmoke's last leg now says
what it proves — no tunnel opens from a pool without a policy — instead
of naming the host-side reason it cannot observe.
Inline WHY comments in pool.go move to the STE 101 register, two godocs
that restated their identifier go, three multi-line comments in the
smokes become one line each and drop issue-tracker narration, the find
budget comment states the invariant it protects, Tunnels' godoc keeps
two facts, the proxy's audit callback becomes the named AuditFunc like
its sibling DialFunc, and the refill-concurrency table runs under t.Run
like every other table in its file.
desktop.md now says guest-proxy.service tests NIC presence only and runs
before the host can mark a locked NIC, so desktop pools belong on the
none lane.
A readonly name such as UID passes the identifier check, so bash
refuses the export and the session used to come up without the value;
each export now exits the shell on failure like the cwd step, so create
answers with an error instead.
The claim-shape advice said net=egress for web tasks, which on a guarded
bridge gives the session neither a route nor the relay; the unit's
NIC-presence test is the reason, not its start order.
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