Documentation
¶
Overview ¶
Package sshx is the Tier-0 in-process SSH transport (PLAN §5.1, §5.4): a golang.org/x/crypto/ssh client with hand-rolled ~/.ssh/config resolution, knownhosts host-key policy (accept-new TOFU only under setup; never under data verbs), and ssh-agent discovery. The "reuse your SSH auth" promise is NOT free — these are protocol libraries; we own the config resolution, host-key policy, and agent discovery. BANNED: ssh.InsecureIgnoreHostKey, any non-knownhosts HostKeyCallback, any --insecure flag (CI grep-gated).
Index ¶
- Constants
- Variables
- func ChannelByte(channel string) byte
- func ChannelString(b byte) string
- func EncodeCB01(w io.Writer, channel byte, items []CB01Item) error
- func EncodeCB01Streaming(w io.Writer, channel byte, items []CB01ItemReader) error
- func KindByte(kind string) byte
- func KindString(b byte) string
- type CB01Item
- type CB01ItemReader
- type Client
- type Session
- type Target
Constants ¶
const Magic = cb01Magic
Magic is the CB01 frame magic, exported so the decoder/tests can assert it.
Variables ¶
var ( // ErrHostKeyUnknown is an unknown host under a data verb (never prompt; fail // closed with exit 6 and a hint to run `clipbeam setup user@host`, PLAN §5.4). ErrHostKeyUnknown = errors.New("clipbeam: host key unknown (run clipbeam setup)") // ErrHostKeyMismatch is a changed host key — ALWAYS hard-fail, exit 6 (§5.4). ErrHostKeyMismatch = errors.New("clipbeam: host key mismatch") // ErrHostKeyCAUnsupported is a CA-signed host key (@cert-authority) — a // documented v1 NON-GOAL with a specific error (PLAN §5.4 R14). ErrHostKeyCAUnsupported = errors.New("clipbeam: host certificate (cert-authority) unsupported") // ErrProxyJumpUnsupported is a resolved ProxyJump/Include — a documented v1 // NON-GOAL with a specific message, never an opaque handshake failure (§5.4). ErrProxyJumpUnsupported = errors.New("clipbeam: ProxyJump/Include unsupported in clipbeam v1") // ErrDialFailed is any SSH-dial failure (maps to exit 6, PLAN §8.3). ErrDialFailed = errors.New("clipbeam: ssh dial failed") )
Sentinel transport errors mapped into the unified exit-code table (PLAN §8.3).
var ErrBadFrame = errors.New("clipbeam: bad CB01 frame")
ErrBadFrame is returned for a malformed CB01 frame (bad magic, truncated, count<1, an over-long string prefix, or a short payload read).
Functions ¶
func ChannelByte ¶
ChannelByte collapses a wire channel string ("" | "clipboard" | "agent") to its CB01 channel byte (the nil/"clipboard" distinction collapses to 0, PLAN §5.1).
func ChannelString ¶
ChannelString maps a CB01 channel byte back to its wire channel string.
func EncodeCB01 ¶
EncodeCB01 writes a CB01 frame to w. channel is the collapsed channel byte; items carry their metadata + raw (buffered) payload. The sender builds this and streams it to the remote `clipbeam ingest` stdin (PLAN §5.1).
func EncodeCB01Streaming ¶
func EncodeCB01Streaming(w io.Writer, channel byte, items []CB01ItemReader) error
EncodeCB01Streaming writes a CB01 frame to w using per-item streamed payloads. Each item's payload is copied via io.CopyN with its declared PayloadSize as the length-prefix, so a large file/image item is never held whole in RAM on the send side (PLAN §3.7). The caller must set PayloadSize to the exact byte count the Payload reader will yield.
func KindString ¶
KindString maps a CB01 kind byte back to its ingest kind string.
Types ¶
type CB01Item ¶
CB01Item is one item in a CB01 frame, carrying full metadata so agent-channel + filename fidelity survive the daemonless path (PLAN §5.1). The buffered Payload form is used by the sender's metadata-light text/agent frames; large file/image items use EncodeCB01Streaming so a 50 MB item is never held whole in RAM (PLAN §3.7).
type CB01ItemReader ¶
type CB01ItemReader struct {
Kind byte
Name string
Uti string
Mime string
// Payload is a bounded reader over exactly the item's payload length; consume it
// fully (or Close it) before relying on the next item. For a non-last item it is a
// temp-file spill that auto-removes on full read or Close.
Payload io.Reader
// PayloadSize is the exact payload byte count (the wire length-prefix).
PayloadSize int64
}
CB01ItemReader exposes one decoded CB01 item's metadata plus a bounded Reader over its payload (streamed straight into Ingest, PLAN §5.1). On encode the caller sets PayloadSize + Payload; on decode the decoder fills them and Payload is bounded.
func DecodeCB01 ¶
func DecodeCB01(r io.Reader) (channel byte, items []CB01ItemReader, err error)
DecodeCB01 reads a CB01 frame from r and returns the channel byte plus a slice of per-item readers feeding Ingest directly (PLAN §5.1, §3.7). Each item's metadata (kind/name/uti/mime) is populated up front; its payload is exposed as a bounded io.Reader. The returned readers MUST be consumed strictly in order.
The wire interleaves metadata and payload per item (metadata of item N+1 follows payload N), so to populate every item's metadata before the caller reads any payload the decoder reads each item in order: the LAST item's payload is a direct bounded reader over r (zero copy — the overwhelmingly common count==1 path streams the whole payload straight from r to disk), while each EARLIER item's payload is spilled to a bounded temp file via io.CopyN (32 KiB window) so its bytes survive past the point where item N+1's metadata is read. A payload is therefore NEVER read whole into a single in-memory []byte; bounded memory holds (PLAN §3.7). Temp spills auto-remove on close/full-read.
type Client ¶
type Client interface {
// Dial opens an SSH connection to the resolved target. It returns
// ErrHostKeyUnknown/ErrHostKeyMismatch/ErrHostKeyCAUnsupported/
// ErrProxyJumpUnsupported/ErrDialFailed as appropriate (PLAN §5.4/§8.3).
Dial(t Target) (Session, error)
// Close tears down the underlying connection.
Close() error
}
Client is the in-process SSH client boundary consumed by send/setup verbs. Implementations dial with a knownhosts-backed HostKeyCallback (never nil, never InsecureIgnoreHostKey, PLAN §5.4) and authenticate via ssh-agent then IdentityFiles.
func NewClient ¶
NewClient constructs the in-process SSH client. It resolves auth methods in order (ssh-agent via $SSH_AUTH_SOCK, then IdentityFiles) and installs the knownhosts-backed HostKeyCallback (PLAN §5.4). It returns an error only if the host-key database files cannot be opened (an empty/missing known_hosts is fine — every host is then unknown, failing closed under data verbs).
type Session ¶
type Session interface {
// Run execs remoteCommand, writes stdin fully, and returns the captured stdout
// (the saved path(s), one per line) + stderr. A non-zero remote exit is an error.
Run(remoteCommand string, stdin []byte) (stdout []byte, stderr []byte, err error)
// Close ends the session.
Close() error
}
Session is one exec channel on a dialed connection. The daemonless-exec push runs the ABSOLUTE remote `clipbeam ingest` (a bare name frequently fails — minimal non-login PATH, PLAN §5.1) and streams a CB01 frame to its stdin, relaying the remote stdout (the saved abs path(s)).
type Target ¶
type Target struct {
User string
Host string
Port int
IdentityFiles []string // from ssh_config IdentityFile(s), else default keys
ConfigAlias string // the ~/.ssh/config Host name, if any
// AcceptNewHostKey records an unknown host non-interactively (setup /
// --accept-new-hostkey / StrictHostKeyChecking=accept-new only, PLAN §5.4). Data
// verbs leave this false and fail closed on an unknown host.
AcceptNewHostKey bool
}
Target is a resolved SSH destination (after ~/.ssh/config alias expansion).
func ResolveTarget ¶
ResolveTarget expands a user@host[:port] spec (or a ~/.ssh/config alias) into a Target via kevinburke/ssh_config (HostName/User/Port/IdentityFile). It detects a resolved ProxyJump/Include and returns ErrProxyJumpUnsupported (PLAN §5.4).