Repository navigation
egress, server, pool: bound guest hosts and claim refs, sleeping relay, exec kill, renew idle clock - #429
Merged
Conversation
A guest could send CONNECTs with a host up to the header limit, or a stream of short denied ones, and every refusal wrote the host into the usage and audit journals; ~135 one-megabyte CONNECTs rotated another tenant's release event out of usage.jsonl and its .1. A wildcard intercept rule also signed and cached a multi-megabyte leaf per long host. hostPort now denies a host past the DNS limit, a recorded host and method are clipped to it, and each proxy records refusals at 10/s after a burst of 100, which keeps a flooding sandbox hours away from rotating a 64 MiB journal.
Both reached the claim record unchecked. A NUL made the archive and replica row put fail in PG jsonb (22P05), mapped to unavailable, so the claim never replicated or archived and capture looped; a 1 MB claim_ref slowed every claims.json commit on the node about 97x. Both now take at most 512 bytes of printable UTF-8 and answer 400. Metadata keys and values and env names and values already refuse a NUL; an env inject query did not, so it now refuses control characters as an env value does.
A lone info is held in pending while more bytes follow within 100 ms, so a client streaming info frames grew pending without bound (117 MiB in a test) and never woke the claim. A pipelined info has one legitimate frame behind it, so a further info now hands the connection to the guest with the pending frames replayed.
Any relay error after started sent a kill, and killExec wakes the claim to deliver it, so a guest connection that ended because /hibernate or the reaper stopped the VM restored that VM. The kill now runs only on a timeout or client cancel, the output cap, or the node budget, the cases where this node drops a live command.
readFrame's io.EOF reached the relay as a clean end, so a port stream that silkd or the VM dropped mid-transfer half-closed the client as if the guest had finished. Only a done frame ends the stream cleanly now; a cut answers io.ErrUnexpectedEOF and the relay closes both sides.
Renew stamped data-plane activity, so a client that only renewed kept an idle claim from hibernating and a hibernated one from archiving. The idle clock is defined by data-plane connections and egress (docs/deploy.md), and a renew is a control-plane call like the polls that already skip the stamp.
A partial write (ENOSPC, file size limit) left a torn prefix, and the next event was appended onto it, so both lines failed to decode; endTornLine only ran at open. The journal now tracks whether its last byte ends a line and prefixes the next append with a newline when it does not. Truncating the torn bytes instead would shrink a file that a log shipper may be tailing.
CMGS
force-pushed
the
fix/audit-round2
branch
from
November 3, 2026 08:24
836aa51 to
b648245
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.
Fixes from a whole-repo correctness read of the egress proxy, the claim API, the sleeping relay, exec and the usage journal. Stacked on #420.
Fixes
CONNECTwith a host of about 1 MiB wrote a line that size; about 135 of them rotated another tenant'sreleaseevent out ofusage.jsonland its.1. With a wildcard intercept rule each such host also signed and cached a leaf of about 4.8 MiB. A host longer than 253 bytes (the DNS limit) is refused, recorded hosts and methods are clipped, and a sandbox records at most 10denyandrate_limiteddecisions per second after a burst of 100 (allowed decisions are never dropped).docs/egress.mdstates it.claim_refandclaim_ref_prefixhad no bound. A NUL made every archive and replica row write fail in PostgreSQLjsonb(SQLSTATE 22P05, read as a registry outage), so the claim never replicated or archived and recaptured on every attempt; a 1 MB ref made every claims commit on the node about 97x slower. Both are now at most 512 bytes of printable UTF-8, like a metadata value, answering 400. An env injectquerynow refuses control characters as an env value does.infoframes without bound (117 MiB from one client in a test). A secondinfobefore any call now wakes the claim.donereached the client as a clean close. It now reads as an unexpected EOF, so the relay closes both ends./renewrestarted the idle clock, so a client that renews kept an idle claim from hibernating and a hibernated one from archiving, againstdocs/deploy.md. It no longer touches it.Cost
Nothing on the claim path. One limiter check per recorded egress refusal.
Tests
Each fix has a regression test that fails against the old code or a mutant of the fix.
go test -raceon egress, server, engine, pool and types; asl clean on darwin and linux.Update
No code change since opening. Conflict to expect with #422 in
Renew: keep the row rewrite, dropsb.Touch().