Documentation
¶
Overview ¶
Package gate is the per-session egress gate: a forward proxy the sandbox reaches through HTTP_PROXY / HTTPS_PROXY. It is the enforcement point for the two-level gate (docs/plan/12_vaults-credentials.md, D3): the environment's networking policy decides which hosts a request may reach at all, and — for plain HTTP, where the platform holds the request plaintext — egress substitution rewrites vault placeholders into their secrets on admitted requests (internal/egress). HTTPS rides through as an opaque CONNECT tunnel, admitted or refused on the target host but never inspected, so in-sandbox TLS bodies keep their placeholders until the TLS-terminating phase (#166).
The package is transport-only: it holds resolved credentials for the life of one session's gate and opens sockets, but reads no store and emits no events. A credential the request host is not allowed to use is left as its literal placeholder (never the secret) — documented reference behavior, not an error — and surfaced through the diagnostic OnUnreachable seam. The wire-visible credential_host_unreachable_error is a *configuration conflict* (a credential's allowed_hosts vs the environment's networking policy), detected and emitted controlplane-side when the gate config is rendered — never here.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Networking domain.Networking
// MCPServerEndpoints are the `host:port` endpoints the session's agent
// declares MCP servers at. They widen a `limited` policy that sets
// allow_mcp_servers and nothing else — see newPolicy.
MCPServerEndpoints []string
// IPAllowed is the address floor a dial admitted only by MCPServerEndpoints
// is held to, run on the resolved address. Nil selects dialguard.IPAllowed,
// which is what the platform's own MCP client uses on the same declarations;
// a test overrides it to reach a loopback server.
IPAllowed func(net.IP) error
Credentials []egress.Credential
OnUnreachable func(host string, placeholders []string)
// ResponseHeaderTimeout bounds a stalled origin's hold on the serving
// goroutine for a plain-HTTP request. It caps time-to-response-headers only,
// so a slow-streaming body is unaffected. Zero selects
// defaultResponseHeaderTimeout.
ResponseHeaderTimeout time.Duration
// MaxBodyBytes bounds a plain-HTTP request body the gate buffers for
// substitution; a larger body is refused with 413 rather than read into
// memory, since the sandbox controls its size. Zero selects
// defaultMaxBodyBytes.
MaxBodyBytes int64
// TunnelIdleTimeout closes a CONNECT tunnel after no bytes have moved in
// either direction for the window. Activity on one side alone keeps the
// tunnel alive — a long download is silent upstream-ward — so this cuts
// only tunnels with no end-to-end byte movement for the whole window
// (liveness is measured on successful reads; a tunnel both of whose ends
// stop draining for the entire window counts as abandoned). Zero selects
// defaultTunnelIdleTimeout.
TunnelIdleTimeout time.Duration
}
Config constructs a Gate. Networking is the environment's request-level policy; Credentials are the session's resolved env-var credentials for substitution (nil is a valid gate that only host-filters). OnUnreachable, when set, is called with the request host and the placeholders of credentials whose allowed_hosts did not admit it — never a secret.
There is deliberately no seam for the dialer or the transport. The gate opens every socket through one dialer of its own so the address floor below cannot be substituted away: a caller who replaced either would have kept IPAllowed — which the type would still accept — and silently lost the floor on the path they replaced, with a missing refusal as the only symptom.