transport

package
v1.47.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package transport supplies the connection backends a quil client can use to reach a daemon: the local Unix socket, or an SSH channel to another host.

Index

Constants

View Source
const DefaultRemoteCommand = "quil --stdio"

DefaultRemoteCommand is what ssh runs on the far side. It is `quil`, not `quild`: the daemon-ensure logic (startDaemon, waitForDaemonReady, findDaemonBinary) lives in the TUI binary, and release archives ship both binaries together so quil is present wherever quild is.

View Source
const ExitSSHOwnFailure = 255

ExitSSHOwnFailure is the status ssh reserves for its own failures — auth, host key, DNS, refused connect. The remote shell's codes pass through untouched (127, 126, and whatever the command returned), so this value is what separates "ssh could not connect" from "the far side had something to say". Same signal remoteinstall reads from the other direction.

Variables

This section is empty.

Functions

func Local

func Local(socketPath string) func(context.Context) (net.Conn, error)

Local returns a dialer for a daemon listening on a Unix socket on this machine — the transport quil has always used.

func RunSSH added in v1.44.0

func RunSSH(ctx context.Context, dest string, opts SSHOptions, command string,
	stdin io.Reader, stdout, stderr io.Writer) (int, error)

RunSSH executes one command on a remote host and returns its exit status.

It shares sshArgs with SSH(), so every hardening option, the ConnectTimeout and the destination check apply identically. That sharing is the point: a second ssh call site assembling its own argument vector would silently drop the guard against a destination like "-oProxyCommand=...", which executes a command on THIS machine before any network traffic happens.

Unlike SSH() this is synchronous and reaps the child itself — callers here want a finished command and its status, not a live connection.

A non-zero exit is returned as a status with a nil error: for this caller "the remote command failed" is data, not an exception. Only a failure to run ssh at all is an error, and the status is then -1.

func SSH

func SSH(dest string, opts SSHOptions) func(context.Context) (net.Conn, error)

SSH returns a dialer that reaches a daemon on another host by running the remote command over ssh and speaking the IPC protocol across its stdio.

No network port is opened on the remote host: the daemon keeps listening only on its local Unix socket, and the remote command proxies to it.

Types

type LinkFailure added in v1.45.1

type LinkFailure int

LinkFailure says whether a failed dial is worth retrying.

const (
	// LinkFailureTransient is the default and the safe answer: retry.
	LinkFailureTransient LinkFailure = iota
	// LinkFailurePermanent means further identical attempts cannot succeed.
	LinkFailurePermanent
)

func ClassifyLinkFailure added in v1.45.1

func ClassifyLinkFailure(stderr string, established bool, exitCode int) LinkFailure

ClassifyLinkFailure reports whether a failed link is worth retrying.

stderr alone is NOT sufficient evidence and must never be used alone, which is why this takes all three signals rather than exposing the string match. ssh multiplexes the REMOTE command's fd 2 onto its own stderr, so the text includes whatever the far side's rc files printed — and "permission denied" is one of the most common strings any Unix shell emits. Trusting it alone lets an unreadable path in someone's ~/.bashrc park the session, and lets a compromised remote do it deliberately.

Two independent gates make the text attributable to ssh itself:

  • established — bytes the pump read, which is STDOUT only. Any byte proves the remote command ran, which proves ssh authenticated, which means an auth or host-key marker cannot be ssh's own.
  • exitCode — 255 is ssh's own failure. If the remote command ran and exited, its status passes through instead. And when ssh is still alive and Close has to kill it, the status is the kill rather than 255 — so a transient drop fails this gate too, which is the safe direction.

The asymmetry is deliberate and load-bearing: anything unproven is TRANSIENT. Mis-parking a session that would have healed costs the user their session; retrying one that will not costs authentication attempts, which the backoff decay already bounds.

func (LinkFailure) String added in v1.45.1

func (f LinkFailure) String() string

type LinkStatus

type LinkStatus interface {
	// Established reports whether the far side has ever delivered a byte.
	//
	// This, not LinkErr, is the load-bearing test. LinkErr only becomes
	// non-nil once ssh has actually DIED, which covers the fast failures
	// (NXDOMAIN, immediate auth reject, "command not found") but not the slow
	// ones: against a firewalled port or a dead IP, ssh is still happily
	// waiting on TCP while the caller's much shorter handshake deadline
	// expires — so a liveness test keyed on "has it died yet" reports a
	// perfectly healthy link that has never carried a single byte.
	//
	// "Has anything arrived" has neither problem. It is monotonic, it cannot
	// race a still-connecting child, and it is exact by construction: a peer
	// that answered badly has produced bytes by definition, and one that never
	// connected has produced none.
	Established() bool

	// LinkErr reports why the transport is dead, or nil while it is alive —
	// including while it is merely still connecting. Use it for the
	// EXPLANATION once Established() has established there is a problem, never
	// as the test for whether one exists.
	LinkErr() error

	// ExitCode reports the child's exit status, or -1 if it has not been
	// reaped.
	//
	// For an ssh child this separates a failure on the far side from ssh's own:
	// 255 is ssh itself (auth, host key, DNS, refused connect), while the
	// remote shell's codes pass through untouched — 127 for a command it could
	// not find, 126 for one it found and could not execute. That distinction is
	// what makes "quil is not installed over there" actionable rather than just
	// another unreachable host.
	//
	// Read it AFTER Close, which is what reaps the child and makes the status
	// final. This is the mirror image of LinkErr, which must be read BEFORE
	// Close because Close can clear it.
	ExitCode() int
}

LinkStatus is implemented by connections carried over a child process. It exists so a caller can tell "the transport never came up" apart from "the peer answered, badly" — a distinction the net.Conn interface cannot express, and one that matters because both arrive at the same call site as a failed read.

type SSHOptions

type SSHOptions struct {
	// SSHPath overrides the ssh binary. Empty means "ssh" from PATH.
	SSHPath string

	// RemoteCommand overrides what runs on the far side. Empty means
	// DefaultRemoteCommand. A non-interactive login shell often lacks
	// ~/.local/bin, which is where scripts/install.sh puts quil, so an
	// absolute path is sometimes required.
	RemoteCommand string

	// ConnectTimeout bounds the TCP handshake. Zero means
	// defaultConnectTimeout. Values below one second are rounded up to one:
	// ssh's ConnectTimeout is expressed in whole seconds, and a sub-second
	// value would truncate to 0, which OpenSSH reads as "no timeout" — the
	// exact opposite of what a caller passing a small value intends.
	ConnectTimeout time.Duration

	// Batch suppresses every interactive prompt. False for the first dial,
	// which happens before the TUI takes the terminal and must be able to
	// prompt for a host-key fingerprint or a key passphrase. True for
	// reconnects, which happen under raw mode where a prompt would garble or
	// deadlock the display — on Windows especially, since ssh reads CONIN$
	// directly rather than stdin.
	Batch bool

	// StderrSink receives a sanitized copy of the child's stderr on a batch
	// dial. Nil discards it, which is the pre-reconnect behaviour.
	//
	// Only meaningful with Batch. A non-batch dial's stderr goes to the terminal
	// and moves to the log later through RedirectStderr instead — a seam that is
	// a no-op here, because there is no switchWriter on this path, which is why
	// post-reconnect diagnostics needed their own route.
	StderrSink io.Writer
}

SSHOptions tunes the ssh invocation.

type StderrRedirector added in v1.44.1

type StderrRedirector interface {
	// RedirectStderr sends any further child diagnostics to w. Pass nil to
	// discard them entirely.
	RedirectStderr(w io.Writer)
}

StderrRedirector is implemented by transports holding a child process whose stderr is attached to the operator's terminal. cmd/quil asserts for it after dialling, the same way it asserts for LinkStatus.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL