Documentation
¶
Overview ¶
Package sandbox is the Go SDK for the cocoon sandbox control plane: claim a microVM from a sandboxd node, run commands in it over the relayed silkd protocol, release it.
Index ¶
- Constants
- type APIError
- type Checkpoint
- type Client
- func (c *Client) Attach(ownerAddr, id, token string) *Sandbox
- func (c *Client) Checkpoint(id string) *Checkpoint
- func (c *Client) Checkpoints(ctx context.Context) ([]*Checkpoint, error)
- func (c *Client) DeleteTemplate(ctx context.Context, template string, opts ...Option) error
- func (c *Client) Drain(ctx context.Context) (*NodeInfo, error)
- func (c *Client) Info(ctx context.Context) (*NodeInfo, error)
- func (c *Client) Lookup(ctx context.Context, id, token string) (*Sandbox, error)
- func (c *Client) New(ctx context.Context, template string, opts ...Option) (*Sandbox, error)
- func (c *Client) Sandboxes(ctx context.Context) ([]SandboxSummary, error)
- func (c *Client) SetPools(ctx context.Context, pools []PoolSpec) (*NodeInfo, error)
- func (c *Client) SetPoolsCluster(ctx context.Context, pools []PoolSpec) ([]PoolResult, error)
- func (c *Client) Uncordon(ctx context.Context) (*NodeInfo, error)
- func (c *Client) Volumes(ctx context.Context) ([]VolumeInfo, error)
- type ClientOption
- type Cmd
- type ExitError
- type Lsp
- type NetShape
- type NodeInfo
- type Option
- type PoolKey
- type PoolResult
- type PoolSpec
- type PoolStatus
- type PortConn
- func (p *PortConn) Close() error
- func (p *PortConn) CloseWrite() error
- func (p *PortConn) LocalAddr() net.Addr
- func (p *PortConn) Read(b []byte) (int, error)
- func (p *PortConn) RemoteAddr() net.Addr
- func (p *PortConn) SetDeadline(time.Time) error
- func (p *PortConn) SetReadDeadline(time.Time) error
- func (p *PortConn) SetWriteDeadline(time.Time) error
- func (p *PortConn) Write(b []byte) (int, error)
- type Pty
- type PtyOpts
- type Sandbox
- func (s *Sandbox) Attach(ctx context.Context, pid uint32, stdout, stderr io.Writer) (code int32, exited bool, err error)
- func (s *Sandbox) Checkpoint(ctx context.Context, name string) (*Checkpoint, error)
- func (s *Sandbox) Close() error
- func (s *Sandbox) DialPort(ctx context.Context, port uint16) (*PortConn, error)
- func (s *Sandbox) Exec(ctx context.Context, argv ...string) (string, error)
- func (s *Sandbox) Find(ctx context.Context, path, pattern, glob string) ([]wire.Match, error)
- func (s *Sandbox) Fork(ctx context.Context, count int, ttl time.Duration) ([]*Sandbox, error)
- func (s *Sandbox) GitAdd(ctx context.Context, path string, files ...string) error
- func (s *Sandbox) GitBranches(ctx context.Context, path string) (*wire.GitBranches, error)
- func (s *Sandbox) GitCheckout(ctx context.Context, path, name string) error
- func (s *Sandbox) GitClone(ctx context.Context, url, path, branch string, depth uint32, auth string) error
- func (s *Sandbox) GitCommit(ctx context.Context, path, message, author string) (string, error)
- func (s *Sandbox) GitCreateBranch(ctx context.Context, path, name string) error
- func (s *Sandbox) GitDeleteBranch(ctx context.Context, path, name string) error
- func (s *Sandbox) GitPull(ctx context.Context, path, auth string) error
- func (s *Sandbox) GitPush(ctx context.Context, path, auth string) error
- func (s *Sandbox) GitStatus(ctx context.Context, path string) (*wire.GitStatusResult, error)
- func (s *Sandbox) Hibernate(ctx context.Context) error
- func (s *Sandbox) Kill(ctx context.Context, pid uint32, sig int32) error
- func (s *Sandbox) ListDir(ctx context.Context, path string) ([]wire.DirEntry, error)
- func (s *Sandbox) Logs(ctx context.Context, pid uint32, stdout, stderr io.Writer) (code int32, exited bool, err error)
- func (s *Sandbox) Mkdir(ctx context.Context, path string, parents bool) error
- func (s *Sandbox) NewSession(ctx context.Context, opts ...SessionOption) (*Session, error)
- func (s *Sandbox) OpenPty(ctx context.Context, opts PtyOpts) (*Pty, error)
- func (s *Sandbox) Owner() string
- func (s *Sandbox) PreviewURL(ctx context.Context, port uint16, ttl time.Duration) (string, error)
- func (s *Sandbox) Promote(ctx context.Context, template string) (*Template, error)
- func (s *Sandbox) ProxyPort(ctx context.Context, localAddr string, port uint16) (net.Listener, error)
- func (s *Sandbox) Ps(ctx context.Context) ([]wire.ProcInfo, error)
- func (s *Sandbox) Pull(ctx context.Context, path string, tarStream io.Writer) error
- func (s *Sandbox) Push(ctx context.Context, dest string, tarStream io.Reader) error
- func (s *Sandbox) ReadFile(ctx context.Context, path string) ([]byte, error)
- func (s *Sandbox) Remove(ctx context.Context, path string, recursive bool) error
- func (s *Sandbox) Rename(ctx context.Context, from, to string) error
- func (s *Sandbox) Replace(ctx context.Context, files []string, pattern, replacement string) ([]wire.Replaced, error)
- func (s *Sandbox) Run(ctx context.Context, cmd Cmd) (int, error)
- func (s *Sandbox) Sessions(ctx context.Context) ([]string, error)
- func (s *Sandbox) Spawn(ctx context.Context, cmd Cmd) (uint32, error)
- func (s *Sandbox) StartLsp(ctx context.Context, language, root string) (*Lsp, error)
- func (s *Sandbox) Stat(ctx context.Context, path string) (wire.FileInfo, error)
- func (s *Sandbox) Token() string
- func (s *Sandbox) Watch(ctx context.Context, path string, recursive bool) (*Watcher, error)
- func (s *Sandbox) WriteFile(ctx context.Context, path string, data []byte, mode *uint32) error
- type SandboxSummary
- type Session
- type SessionOption
- type Size
- type Template
- type Volume
- type VolumeInfo
- type Watcher
Constants ¶
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Checkpoint ¶
type Checkpoint struct {
ID string
Name string
SandboxID string
CreatedAt time.Time
// contains filtered or unexported fields
}
Checkpoint is a captured sandbox state bound to the node that holds it. New claims fresh sandboxes branched from the captured moment, any number of times; the source sandbox is unaffected and can keep being checkpointed, so successive captures form a tree.
func (*Checkpoint) Delete ¶
func (ck *Checkpoint) Delete(ctx context.Context) error
Delete removes the checkpoint from its node and asks every peer that node currently sees to drop any replica a heal pulled — best-effort eventual cleanup, not a fleet-wide revocation. A peer that misses that broadcast (offline, partitioned, or joined later) keeps serving branches from its replica until the node's checkpoint_ttl_hours ages it out; with that TTL at its default of 0 (keep forever), an unreachable peer's replica has no cleanup bound at all.
func (*Checkpoint) New ¶
New claims a sandbox cloned from the checkpoint, on the checkpoint's node. The snapshot pins the key axes; WithTimeout may set the TTL. A node that no longer holds the checkpoint redirects to a probed owner, which New follows transparently; if every candidate fails transiently, New falls back to the origin once so it heals (pulls the checkpoint) locally.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to one sandboxd node.
func Connect ¶
func Connect(addr string, opts ...ClientOption) (*Client, error)
Connect returns a client for a sandboxd node. addr accepts a comma-separated seed list for forward compatibility; v0 uses the first entry. Calls are bounded by their ctx — checkpoint and promote run as long as the snapshot takes, so the client sets no blanket deadline.
func (*Client) Attach ¶ added in v0.1.3
Attach binds a handle to an already-claimed sandbox whose owner data-plane address is already known (e.g. delivered by the L3 apiserver as annotations), with no lookup round-trip. ownerAddr is the node's sandboxd data-plane address; token is the per-sandbox credential.
func (*Client) Checkpoint ¶ added in v0.1.6
func (c *Client) Checkpoint(id string) *Checkpoint
Checkpoint returns a handle for a known checkpoint id, bound to the entry node — no listing round-trip; an unknown id surfaces as 404 at claim time.
func (*Client) Checkpoints ¶
func (c *Client) Checkpoints(ctx context.Context) ([]*Checkpoint, error)
Checkpoints lists the CONNECTED node's checkpoints, newest first; the returned handles are bound to that node.
func (*Client) DeleteTemplate ¶
DeleteTemplate removes a promoted template by name. When the entry node does not hold it but the mesh's gossip names an owner, the delete follows the redirect there (one hop); gossip lags a fresh promote by about a tick, so right after promoting prefer Template.Delete on the returned handle.
func (*Client) Drain ¶
Drain cordons the entry node (root token): new claims/forks/branches are refused, live claims run to their leases; poll Info until Claimed is zero.
func (*Client) Lookup ¶
Lookup relocates a sandbox handle whose owner address was lost, given its id and token: it asks the entry node, then scatters across the cluster's peers concurrently, and returns a handle bound to whichever node confirms ownership first — one hung peer must not stall the whole lookup.
func (*Client) New ¶
New claims a sandbox for template. Without options the node serves its defaults: the no-network lane and the smallest size tier. New returns when the sandbox's silkd is reachable. Against a cluster, a warm miss redirects to a peer that holds one, which New follows transparently; if every candidate fails transiently (full, mid-heal, unreachable), New falls back to the origin once so it provisions or heals locally.
func (*Client) Sandboxes ¶ added in v0.1.6
func (c *Client) Sandboxes(ctx context.Context) ([]SandboxSummary, error)
Sandboxes lists the live claims this token may see.
func (*Client) SetPools ¶
SetPools replaces the entry node's desired warm pools (PUT /v1/pools): a declarative full replace, so omitted pools drain. Requires the operator token.
func (*Client) SetPoolsCluster ¶
SetPoolsCluster applies pools to the entry node and every peer, returning a per-node result; retrying failed nodes is the whole protocol (idempotent replace). A non-nil error means peer discovery failed and only the entry node was reached — an incomplete apply to retry, not a single-node cluster (nil).
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures Connect.
func WithAPIToken ¶
func WithAPIToken(token string) ClientOption
WithAPIToken sets the operator bearer for every node-scoped call — claim and info, plus drain, pools, templates, checkpoints, and fork/promote/preview.
func WithHTTPClient ¶ added in v0.1.6
func WithHTTPClient(hc *http.Client) ClientOption
WithHTTPClient replaces the control-plane HTTP client, for callers that need their own transport, proxy, or timeout.
type Cmd ¶
type Cmd struct {
Argv []string
Cwd string
Env map[string]string
User string
Session string // when set, runs inside that persistent shell session
// Stdin is consumed until EOF or until the command exits. A blocking
// reader (e.g. os.Stdin) whose command exits first keeps its pump
// goroutine parked in Read until the next bytes arrive — do not share
// one reader across Runs, the stale pump would swallow them. nil closes
// the child's stdin immediately.
Stdin io.Reader
Stdout io.Writer // nil discards
Stderr io.Writer // nil discards
}
Cmd describes a streaming Run.
type Lsp ¶
type Lsp struct {
ServerID string
// contains filtered or unexported fields
}
Lsp is a language server running in the sandbox, spoken to over the relay. silkd is a broker: it spawns the flavor image's server and pipes JSON-RPC bytes; the caller frames and correlates (agents already speak LSP).
func (*Lsp) Request ¶
Request opens the JSON-RPC byte stream to the language server: the returned PortConn writes go to the server's stdin, reads come from its stdout. A server serves one Request for its lifetime — closing the stream ends the session and reaps the server (start a new one to work again).
type NodeInfo ¶
type NodeInfo struct {
Pools []PoolStatus `json:"pools"`
Claimed int `json:"claimed"`
Hibernated int `json:"hibernated"`
Archived int `json:"archived"`
Draining bool `json:"draining,omitempty"`
Peers []string `json:"peers,omitempty"`
}
NodeInfo is one node's operational state from GET /v1/info.
type Option ¶
type Option func(*claimRequest)
Option configures a New claim.
func WithClaimRef ¶ added in v0.1.6
WithClaimRef records an opaque caller reference on the claim (the aggregated apiserver passes its k8s "<namespace>/<name>"), echoed by Client.Sandboxes.
func WithTimeout ¶
WithTimeout bounds the sandbox's lifetime: the owning node reaps it after d (rounded up to seconds) even if the client vanishes.
func WithVolumes ¶ added in v0.1.5
WithVolumes requests catalog volumes for a claim; each entry defaults to read-only, set Volume.Mode to "rw" for a writable mount.
func WithVolumesAttachOnly ¶ added in v0.1.6
func WithVolumesAttachOnly() Option
WithVolumesAttachOnly attaches the claim's volumes without mounting them; the workload finds each device by its serial and owns the mount — and its flush — from there: release without a clean self-umount discards unsynced guest pages, since the sandbox performs no sync of its own.
type PoolKey ¶
type PoolKey struct {
Template string `json:"template"`
Net NetShape `json:"net"`
Size Size `json:"size"`
}
PoolKey identifies one warm pool on a node.
type PoolResult ¶
PoolResult is one node's outcome from SetPoolsCluster.
type PoolSpec ¶
type PoolSpec struct {
Template string `json:"template"`
Net NetShape `json:"net,omitempty"`
Size Size `json:"size,omitempty"`
Warm int `json:"warm"`
WarmMax int `json:"warm_max,omitempty"`
IdleHibernateSeconds int `json:"idle_hibernate_seconds,omitempty"`
ArchiveAfterSeconds int `json:"archive_after_seconds,omitempty"`
ArchiveDeleteAfterSeconds int `json:"archive_delete_after_seconds,omitempty"`
}
PoolSpec is a desired warm pool for SetPools. Egress is config-owned on the node and rejected by the API, so it has no field here.
type PoolStatus ¶
type PoolStatus struct {
Key PoolKey `json:"key"`
Warm int `json:"warm"`
Refilling int `json:"refilling"`
Target int `json:"target"`
Golden bool `json:"golden"`
}
PoolStatus reports one warm pool on a node.
type PortConn ¶
type PortConn struct {
// contains filtered or unexported fields
}
PortConn is a net.Conn to a TCP port inside the sandbox, relayed over the silkd protocol (works on the no-network lane — the vsock relay is its only transport). Read returns io.EOF when the guest server closes; CloseWrite half-closes the guest socket.
func (*PortConn) CloseWrite ¶
CloseWrite half-closes the guest socket (the server sees EOF) while reads keep draining its response.
func (*PortConn) LocalAddr ¶
LocalAddr implements net.Conn; the relay has no meaningful socket address.
func (*PortConn) RemoteAddr ¶
func (*PortConn) SetDeadline ¶
SetDeadline implements net.Conn; deadlines are unsupported — bound the DialPort ctx instead.
func (*PortConn) SetReadDeadline ¶
SetReadDeadline implements net.Conn; unsupported, see SetDeadline.
func (*PortConn) SetWriteDeadline ¶
SetWriteDeadline implements net.Conn; unsupported, see SetDeadline.
type Pty ¶
type Pty struct {
PID uint32
// contains filtered or unexported fields
}
Pty is an open pseudo-terminal in the sandbox.
type Sandbox ¶
type Sandbox struct {
ID string
Deadline time.Time
// Volumes lists the volumes finalized on this claim; only a writable entry
// echoes a mode.
Volumes []Volume
// TemplateDigest is the content identity of the promoted-template export
// this sandbox was cloned from; empty for any other source.
TemplateDigest string
// FromCheckpoint names the checkpoint this sandbox branched from; empty
// for pool and template claims.
FromCheckpoint string
// contains filtered or unexported fields
}
Sandbox is a claimed sandbox handle; all data-plane calls dial its owning node directly.
func (*Sandbox) Attach ¶
func (s *Sandbox) Attach(ctx context.Context, pid uint32, stdout, stderr io.Writer) (code int32, exited bool, err error)
Attach replays the buffered output, then follows live output until the process exits, returning its exit code. exited is false only when the proc table dropped the process mid-attach (reap race).
func (*Sandbox) Checkpoint ¶
Checkpoint captures the sandbox's full state — memory, disk, running processes — without stopping it, and returns a handle that branches new sandboxes from that exact moment. name is an optional label.
func (*Sandbox) Close ¶
Close releases the sandbox on its node; releasing one already gone is not an error. It takes no ctx so it stays defer-friendly — bounded internally.
func (*Sandbox) DialPort ¶
DialPort connects to 127.0.0.1:port inside the sandbox. The ctx governs the connection's lifetime: canceling it (or Close) tears the relay down. A port nobody listens on fails here with silkd's not_found error.
func (*Sandbox) Exec ¶
Exec runs argv to completion and returns its stdout; a non-zero exit surfaces as *ExitError carrying stderr, alongside the partial stdout.
func (*Sandbox) Find ¶
Find returns the lines under path matching pattern (a regular expression); glob, when non-empty, narrows the walk to file names matching it (`*` and `?` wildcards).
func (*Sandbox) Fork ¶
Fork clones the sandbox into count children — memory, disk, and guest state (sessions, processes, tmpfs) duplicate at the fork point, and each child gets a fresh machine identity and its own lease. ttl bounds every child's lifetime; zero means the server default. All-or-nothing: on error no child survived. Forking a hibernated sandbox reuses its memory image without waking it.
func (*Sandbox) GitBranches ¶
GitBranches lists the repo's branches and the current one.
func (*Sandbox) GitCheckout ¶
GitCheckout checks out branch name.
func (*Sandbox) GitClone ¶
func (s *Sandbox) GitClone(ctx context.Context, url, path, branch string, depth uint32, auth string) error
GitClone clones url into path in the sandbox; depth > 0 makes a shallow clone. Auth, when non-empty, is a token sent as an in-memory Authorization header. Needs the egress lane.
func (*Sandbox) GitCommit ¶
GitCommit stages nothing (call GitAdd first); it commits the index with message and author ("Name <email>") and returns the new commit hash.
func (*Sandbox) GitCreateBranch ¶
GitCreateBranch creates branch name.
func (*Sandbox) GitDeleteBranch ¶
GitDeleteBranch force-deletes branch name.
func (*Sandbox) GitPush ¶
GitPush pushes the current branch. Auth as in GitClone. Needs the egress lane; on the no-network lane it fails with a typed error pointing at Push.
func (*Sandbox) Hibernate ¶
Hibernate atomically snapshots the sandbox and stops its VM, freeing its memory; the next call that reaches the guest wakes it transparently with sessions, processes, and memory state intact. The TTL keeps running — a hibernated sandbox is still reaped at its deadline.
func (*Sandbox) Kill ¶
Kill signals a tracked process; sig 0 sends SIGKILL. Killing one that already exited is a no-op success — its OS pid may be recycled, so silkd never signals a reaped child.
func (*Sandbox) ListDir ¶
ListDir returns the entries of a directory (batched frames are concatenated).
func (*Sandbox) Logs ¶
func (s *Sandbox) Logs(ctx context.Context, pid uint32, stdout, stderr io.Writer) (code int32, exited bool, err error)
Logs replays a tracked process's ring-buffered output into the writers (nil discards). exited reports whether the process has ended; code is its exit code when it has.
func (*Sandbox) NewSession ¶
NewSession opens a persistent shell.
func (*Sandbox) Owner ¶
Owner returns the data-plane address of the node that owns the sandbox — on a cluster the node a redirected claim landed on.
func (*Sandbox) PreviewURL ¶
PreviewURL mints a shareable URL that serves the sandbox's guest HTTP port from a plain browser, valid for ttl (the node clamps it to the claim's lease). Requires the node to have preview configured.
func (*Sandbox) Promote ¶
Promote publishes the sandbox's current state as a template on its owning node: later claims for it (with this sandbox's network lane and size) clone from it, provision-on-demand. Re-promoting to the same name replaces the template. Like Fork, a hibernated sandbox is promoted from its memory image without waking. Templates are node-local — the returned handle is bound to the owning node. Delete and New without volumes reach that node; New with volumes may follow one placement redirect (name-based Client calls only see the connected node's templates).
func (*Sandbox) ProxyPort ¶
func (s *Sandbox) ProxyPort(ctx context.Context, localAddr string, port uint16) (net.Listener, error)
ProxyPort listens on localAddr (e.g. "127.0.0.1:0") and pipes every accepted connection to the sandbox port, so unmodified local tools reach the guest server. Closing the listener stops new connections; canceling ctx tears down the live ones.
func (*Sandbox) Ps ¶
Ps lists the guest's tracked processes — execs, spawns, and ptys — with state and exit codes.
func (*Sandbox) Push ¶
Push extracts a tar stream under dest in the sandbox — the no-network lane's way to move a project in. tarStream is a reader of tar bytes (e.g. from archive/tar); silkd runs `tar -x` under dest.
func (*Sandbox) Replace ¶
func (s *Sandbox) Replace(ctx context.Context, files []string, pattern, replacement string) ([]wire.Replaced, error)
Replace rewrites pattern (a regular expression) to replacement in each of files, returning one result per file with its replacement count.
func (*Sandbox) Run ¶
Run executes cmd in the sandbox, streaming stdio over one relayed silkd connection, and returns the exit code.
func (*Sandbox) Spawn ¶
Spawn starts cmd detached: it returns the guest pid as soon as the process starts, and the process keeps running with a bounded output ring readable later via Logs or Attach. cmd's Stdin/Stdout/Stderr are ignored; cmd.Session must be empty — the session exec path cannot detach.
func (*Sandbox) StartLsp ¶
StartLsp spawns the language server the flavor image provides for language (rooted at root), returning a handle. On the base image, which ships no language servers, this fails with silkd's typed not_found.
func (*Sandbox) Token ¶
Token is the per-sandbox bearer secret — persist it with ID to reattach later via Lookup (treat it like a credential).
type SandboxSummary ¶ added in v0.1.6
type SandboxSummary struct {
ID string `json:"id"`
Key PoolKey `json:"key"`
Deadline time.Time `json:"deadline"`
Hibernated bool `json:"hibernated"`
Archived bool `json:"archived,omitempty"`
FromCheckpoint string `json:"from_checkpoint,omitempty"`
Volumes []Volume `json:"volumes,omitempty"`
ClaimRef string `json:"claim_ref,omitempty"`
}
SandboxSummary is one live claim as the scoped index reports it; never a token or a host path.
type Session ¶
type Session struct {
ID string
// contains filtered or unexported fields
}
Session is a persistent shell in the sandbox: cwd, env, and shell state survive across Exec calls until Close.
type SessionOption ¶
type SessionOption func(*wire.SessionCreate)
SessionOption configures NewSession.
func WithSessionCwd ¶
func WithSessionCwd(dir string) SessionOption
WithSessionCwd sets the session's initial working directory.
func WithSessionEnv ¶
func WithSessionEnv(env map[string]string) SessionOption
WithSessionEnv sets the session's initial environment.
type Size ¶
type Size string
Size is a T-shirt resource tier; free-form CPU/memory would fragment the node's warm pools.
type Template ¶
Template is a promoted template bound to the node that holds it. Delete and New without volumes dial the owner directly, so they are usable the instant Promote returns; a volume claim may follow one placement redirect.
type Volume ¶ added in v0.1.5
type Volume struct {
Name string `json:"name"`
Mount string `json:"mount,omitempty"`
// Mode is "rw" for a writable mount; empty (or "ro") means read-only.
Mode string `json:"mode,omitempty"`
}
Volume requests one catalog entry at an optional guest mount path and mode.
type VolumeInfo ¶ added in v0.1.5
type VolumeInfo struct {
Name string `json:"name"`
DefaultMount string `json:"default_mount"`
SizeBytes int64 `json:"size_bytes"`
Available bool `json:"available"`
Nodes int `json:"nodes"`
Writable bool `json:"writable,omitempty"`
}
VolumeInfo describes one visible fleet catalog entry.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher is an open filesystem watch. Events yields events until the watch ends — by Close, ctx cancellation, or a watcher error on the sandbox side — after which Err reports the terminal error (nil after a clean Close).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
silkdtest
Package silkdtest fakes a silkd daemon for host-side tests: deterministic frame semantics over any listener, plus the hybrid-vsock muxer handshake for tests that dial a UDS the way sandboxd does.
|
Package silkdtest fakes a silkd daemon for host-side tests: deterministic frame semantics over any listener, plus the hybrid-vsock muxer handshake for tests that dial a UDS the way sandboxd does. |