Add the Session gateway for model, MCP and generic egress - #347
Merged
Merged
Conversation
The gateway serves, inside the Session's loopback-only network namespace, one listener per frozen model upstream, one per MCP HTTP binding and one generic proxy. Model listeners relay only the routes internal/modelprovider declares, strip every credential header and inject the upstream credential from the agent host. MCP listeners admit each binding's connection origin first, then relay to the server with the bearer injected: environment origin through the sandbox Network service, service origin from the agent host. The generic proxy carries CONNECT tunnels and absolute-form plain HTTP, and connects only through a new sandboxnet stream per connection. Redirects are returned unfollowed and the gateway logs nothing. Plan returns the Endpoints before the view exists, so the Harness environment can be built before sessionview starts; Start opens the listeners from the launcher's network hook at the same fixed ports.
- Model and MCP relays remove every response header and trailer value that contains the injected key or bearer, informational responses included. Bodies pass unchanged. model-execution.md declares the rule. - Config.TLS becomes Config.RootCAs: upstream TLS always verifies the destination's hostname. - The listeners record every accepted connection, so the end of the Session aborts hijacked tunnels and upgraded connections that http.Server.Close leaves open, including a relay blocked on a Harness that does not read. - Every upstream dial ends with the Session, and a sandbox open plus Connect has a local deadline of the connect timeout plus a margin. - The forward proxy restores the query that the reverse proxy cleans of semicolon parameters.
Relay transports close each HTTP/1 connection after its response, so http.Transport never logs bytes an idle connection receives; HTTP/2 connections stay shared. Relays inject and withhold each credential in the form the header writer sends. ModifyResponse rejects a mismatched 101, and every upgraded upstream connection closes with its request.
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.
Adds the Session gateway,
apps/daemon/internal/gateway. The Harness on the agent host never holds a real credential and has no network route of its own. Inside the Session's loopback-only network namespace, the gateway serves:internal/modelprovider:UpstreamPathand the query unchanged;CONNECTand absolute-formhttp://requests, and every connection goes throughsandboxnet.Connecton a new Network stream. It has no direct outbound path, and it rejects any other request form.MCPHTTPServerbinding:ValidateConnectionOriginruns first;environment-origin binding dials throughsandboxnet, and aservice-origin binding dials from the agent host;http://127.0.0.1:<port>plus the binding's path.The listeners open on a thread that joins the Session's network namespace, and nothing listens outside it. The gateway logs nothing, and its errors never include credentials or URLs. There is no per-Harness branch.
API for the adapter wiring (a later lane):
Plan(cfg)returnsEndpointsbefore the view starts. Ports are fixed inside the private namespace: the proxy on 17100, then the models, then MCP.Start(ctx, SessionNetwork, cfg)is passed assessionview'sNetwork.Setup.Tests:
CONNECTto a TLS server and a plain-HTTP forward, both throughsandboxnetover thesandboxlinktestrelay.ENETUNREACHbut succeeds through the proxy;Checks:
go test -raceDocs: none in this PR.
model-execution.mdalready holds the credential-gateway rule (#343). The MCP and proxy documentation changes come with the adapter wiring, which changes behaviour.Part of the agent-outside-sandbox work, milestone M1, lane L6.
Blind review fixes
Two blind review rounds; their fixes are in 3f8061c and 806000f.
Locationthat contain the injected key or MCP bearer are removed. Bodies pass unchanged, asmodel-execution.mddeclares. The injected value is normalized as the HTTP/1 header writer sends it, and filtered in that form.Config.RootCAsreplacesConfig.TLS. The server name always comes from the destination.Connect, and Session cancellation ends every dial.