feat(runner): C3 runner control producer (AgentGateway.Control) - #38
Open
sealedsecurity-bot wants to merge 1 commit into
Conversation
Greptile SummaryAdds the Runner control producer and integrates it with gateway and session lifecycle handling.
Confidence Score: 3/5The PR is not yet safe to merge because retirement can still race subscription binding and leave a detached Control stream alive. serve releases the producer lock after resolving a session and only later acquires the session lock to bind; Retire can delete and tear down that session between those operations, after which serve installs fresh live subscription state on the detached object with no later retirement to close it. Files Needing Attention: go/internal/runner/gateway/control.go
|
| Filename | Overview |
|---|---|
| go/internal/runner/gateway/control.go | Implements control production and subscription draining, but the previously reported retirement-versus-binding race remains at current HEAD. |
| go/internal/runner/gateway/gateway.go | Wires the control producer into the served gateway. |
| go/internal/runner/gateway/socket.go | Threads control-state binding and retirement through the socket listener lifecycle. |
| go/internal/runner/host.go | Invokes control binding and retirement from session Start and Stop. |
| go/internal/runner/gateway/control_test.go | Adds broad behavioral and concurrency coverage for control sequencing, retention, takeover, replay, and retirement. |
| go/internal/runner/gateway/control_untrusted_test.go | Covers hostile acknowledgement values and post-retirement agent traffic. |
| go/internal/runner/gateway/retire_wiring_test.go | Verifies socket lifecycle calls reach the control producer. |
| go/internal/runner/e2e_retire_test.go | Verifies ordinary Stop-driven retirement terminates the active Control subscription. |
Reviews (2): Last reviewed commit: "feat(runner): C3 runner control producer..." | Re-trigger Greptile
seal-agent
force-pushed
the
compass-runner-1364-c2-telemetry-ingest
branch
from
July 29, 2026 17:26
88ec220 to
a1b9207
Compare
seal-agent
force-pushed
the
compass-runner-1364-c3-runner-control-producer
branch
from
July 29, 2026 17:28
620f6ee to
6720c91
Compare
seal-agent
force-pushed
the
compass-runner-1364-c2-telemetry-ingest
branch
from
July 29, 2026 22:37
a1b9207 to
c040720
Compare
seal-agent
force-pushed
the
compass-runner-1364-c3-runner-control-producer
branch
from
July 29, 2026 22:39
6720c91 to
cee7015
Compare
seal-agent
force-pushed
the
compass-runner-1364-c2-telemetry-ingest
branch
from
July 30, 2026 03:07
c040720 to
eec89d9
Compare
seal-agent
force-pushed
the
compass-runner-1364-c3-runner-control-producer
branch
from
July 30, 2026 03:08
cee7015 to
3852944
Compare
seal-agent
force-pushed
the
compass-runner-1364-c2-telemetry-ingest
branch
from
July 30, 2026 17:06
eec89d9 to
670d2c4
Compare
seal-agent
force-pushed
the
compass-runner-1364-c3-runner-control-producer
branch
from
July 30, 2026 17:07
3852944 to
39171b8
Compare
Port the real controlProducer behind C2's ControlRouter seam: a per-session retention buffer with a Runner-assigned monotonic control_seq, a replay barrier released by the agent's ReplayCompleteAck, and a subscription drain that transfers retained ops on takeover/reconnect. Bind/Retire wire through SocketListener into agentHost.Start/Stop so control state is created and reclaimed with the session on a socket that outlives it. Refs SEA-1364 Ports sealedsecurity/sealed#911 Co-Authored-By: seal <noreply@sealedsecurity.com>
seal-agent
force-pushed
the
compass-runner-1364-c3-runner-control-producer
branch
from
July 30, 2026 17:32
39171b8 to
a5b22a0
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.
Summary
The Runner's control producer:
AgentGateway.Controlplus theControlSenderseam thesession lifecycle writes through. Stamps a monotonic
control_seq, retains each op pastthe agent's ack cursor, and drains it to whichever subscription is currently bound.
Ports
sealedsecurity/sealed#911(SEA-1364 C3) intosealedsecurity/compass. The port isfaithful —
control.gois byte-identical to the sealed source (both trees share thegithub.com/sealedsecurity/compass/gomodule path, so no import rewrite was needed), andthe three integration edits (
gateway.go,socket.go,host.go) apply against C2's treewith only line-offset shifts.
Stacked on #24 (C2)
C2 (#24) defines
ControlRouter— the ack-routing seam with anoopControlRouterdefaultand a
SetControlRouterinjection point, built explicitly for this lane to fill. This PR:Serveinjects the real*controlProducer, so the session lifecycle can reach the agent;before this the producer was constructed nowhere in production and only tests could reach it.
Controlupgrades the injected router to acontrolLane(C2's interface plus the drain);an ack-only router refuses the subscription (
CodeUnimplemented) rather than accepting astream nothing can ever write to.
SocketListener.Bind/RetireSessionthread throughagentHost.Start/Stopunder the samelock that records/drops the session, so control state is created and reclaimed with the
session on a socket that outlives it. C2's seam and its test fake are untouched.
Base:
compass-runner-1364-c2-telemetry-ingest. Merge #24 first.Three invariants carry the contract
order.
Ops live past the agent's
ControlAckcursor, so an agent or container replacement losesnothing and needs no durable store of its own. Bounded at
maxRetainedOps; past the capSend returns
CodeResourceExhausted(backpressure, an ack frees room) rather than evictingan op already reported durably-queued.
agent's
ReplayCompleteAck(routed here from the Publish stream via C2's seam). Replay-pathops are exempt from both the hold and the cap: the barrier's own release is the one op whose
ack lifts it, so holding or rejecting it would wedge the stream permanently.
Untrusted-side hardening (agent drives acks + subscribe)
AckControlclamps the ack cursor to the highest issued seq — an unclamped cursor pastnextSeqwould make every future op invisible to every future subscription.applied_aboveis agent-sized and unordered;boundedAboveSetintersects it against liveretention (not the seq window, which is unbounded over a session's life), capping what one
ack can allocate for any input in any order.
serve,AckControl,ReleaseReplayBarrier) resolve sessions throughexistingSession(non-creating): an ack or subscribe racingStopcannot resurrect anentry nothing would ever retire again.
Concurrency scopes, deliberate and documented
sink.Sendruns outside the session lock (it is network I/O): holding the mutex acrossit would stall every Send and ack behind one slow socket. The cost — an ack landing mid-batch
can re-deliver one already-acked op — is covered by the at-least-once seq-dedup and is
documented so it is not "fixed" into a lock-across-I/O bug.
absorb the live one's wake token (the stolen-wakeup strand).
Tests
Four new test files (
control_test.go,control_untrusted_test.go,retire_wiring_test.go,e2e_retire_test.go), covering: seq stamping + ordering, pointer-reuse seq distinctness,retain-with-no-subscription, takeover transfer + the two-stale-drainer wake,
replay_completethrough the barrier,
SendIfLiveliveness, the retention cap, untrusted ack clamping +applied_abovebounding, and the Bind/Retire wiring (both the leak assertion and thenil-producer guards on
Start/Stop).Verified locally (go 1.26.5, devenv):
go build ./...clean;go vet -tags "pgtest unix" ./internal/runner/...clean.go test -tags "pgtest unix" -race:internal/runnerok,internal/runner/gatewayok,and downstream
internal/runnerhub+internal/runtimeok — no regression on the stack.hkpre-push gate (moon run :ci) ran: 23 taskscompleted.
Refs SEA-1364
Ports sealedsecurity/sealed#911
Co-Authored-By: seal noreply@sealedsecurity.com
Open Questions (parked for Matt —
'/home/mattw/.agents/rules/decision-authority.md')OQ-1 — serve()-vs-Retire() TOCTOU (SEA-1550, review floor: medium). Greptile (P1) and
the mandatory
reviewagent (graded medium) both flagged a race incontrol.go:serveresolves the session under
p.mu, releases it, then acquiress.mu~12 lines later to bind —a full
Retirefits between, soservebinds on a detached session and the drainer parks on awake nothing will re-close. Benign today (Retire is Stop-driven, Stop kills the agent child
→ the drainer's Control-RPC ctx cancels promptly, a complete backstop), but a live goroutine +
session-state leak the moment T9 in-process reattach lands (host.go:194), if reattach ever
retires without dropping the agent connection. Hard blocker on T9.
Carried verbatim from sealed#911 (
control.gois byte-identical), so NOT port-introduced.Two paths, and this is Matt's call because it is a control-flow + source-of-truth fork:
p.muacross the bind block inserve(preserves the existingp.mu→s.muorder Retire uses, so no deadlock; closes the window). Diverges compass's copyfrom byte-identical sealed.
Not auto-fixed: a lock-ordering change to concurrency control flow that also forks the
source-of-truth copy is a design decision, not a mechanical fix.