sshx

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 17 Imported by: 0

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

View Source
const Magic = cb01Magic

Magic is the CB01 frame magic, exported so the decoder/tests can assert it.

Variables

View Source
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).

View Source
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

func ChannelByte(channel string) byte

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

func ChannelString(b byte) string

ChannelString maps a CB01 channel byte back to its wire channel string.

func EncodeCB01

func EncodeCB01(w io.Writer, channel byte, items []CB01Item) error

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 KindByte

func KindByte(kind string) byte

KindByte maps an ingest kind string to its CB01 kind byte.

func KindString

func KindString(b byte) string

KindString maps a CB01 kind byte back to its ingest kind string.

Types

type CB01Item

type CB01Item struct {
	Kind    byte
	Name    string
	Uti     string
	Mime    string
	Payload []byte
}

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

func NewClient() (Client, error)

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

func ResolveTarget(spec string) (Target, error)

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).

Jump to

Keyboard shortcuts

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