Documentation
¶
Overview ¶
Package protocol is the relay wire + crypto contract, shared by the runner and (mirrored in JS) the browser viewer. See PROTOCOL.md — it is the source of truth.
All terminal IO is end-to-end encrypted with AES-256-GCM under keys derived from a 32-byte session secret S that the relay never sees. Every primitive here is Go standard library, so the runner needs no third-party crypto.
Index ¶
- Constants
- Variables
- func DecodeExit(b []byte) (int32, error)
- func DecodeHello(b []byte) (baseline uint64, cols, rows uint16, err error)
- func DecodeRelayViewerFrame(b []byte) (viewerID string, sealedFrame []byte, err error)
- func DecodeResize(b []byte) (cols, rows uint16, err error)
- func DecodeViewerPayload(b []byte) (viewerID string, payload []byte, err error)
- func EncodeExit(code int32) []byte
- func EncodeHello(baseline uint64, cols, rows uint16) []byte
- func EncodeRelayViewerFrame(viewerID string, sealedFrame []byte) []byte
- func EncodeResize(cols, rows uint16) []byte
- func EncodeViewerPayload(viewerID string, payload []byte) []byte
- type Cipher
- type Keys
Constants ¶
const ( // SecretLen is the size of the session secret S. SecretLen = 32 // KeyLen is the size of each derived AES-256 key. KeyLen = 32 )
const ( KindHello byte = 0x01 // runner→viewer: baseline seq + initial size KindOutput byte = 0x02 // runner→viewer: raw PTY output KindExit byte = 0x03 // runner→viewer: command exit code KindControl byte = 0x04 // runner→viewer: control state (read-only / granted / taken) KindInput byte = 0x10 // viewer→runner: raw keystrokes KindResize byte = 0x11 // viewer→runner: terminal size KindCtrlReq byte = 0x12 // viewer→runner: request control KindCtrlRel byte = 0x13 // viewer→runner: release control )
Message kinds. Low byte space is runner→viewer, high is viewer→runner; see PROTOCOL.md. The numbers are part of the contract and must not change.
const ( ControlReadOnly byte = 0 ControlGranted byte = 1 ControlTaken byte = 2 )
Control states carried by KindControl.
Variables ¶
var ( ErrFrameShort = errors.New("protocol: frame too short") ErrOpen = errors.New("protocol: open failed") )
Errors returned when opening a frame. They are deliberately coarse: a peer (or the relay) must not learn why a frame failed to open.
Functions ¶
func DecodeHello ¶
DecodeHello parses a HELLO payload.
func DecodeRelayViewerFrame ¶
DecodeRelayViewerFrame unwraps EncodeRelayViewerFrame.
func DecodeResize ¶
DecodeResize parses a RESIZE payload.
func DecodeViewerPayload ¶
DecodeViewerPayload unwraps EncodeViewerPayload.
func EncodeHello ¶
EncodeHello builds a HELLO payload: the input-seq baseline the viewer must start at, plus the current terminal size.
func EncodeRelayViewerFrame ¶
EncodeRelayViewerFrame labels an opaque encrypted viewer→runner frame with the relay-assigned viewer id. It carries metadata only; terminal bytes remain inside the sealed frame.
func EncodeResize ¶
EncodeResize builds a RESIZE payload.
func EncodeViewerPayload ¶
EncodeViewerPayload binds a viewer id to an encrypted viewer→runner payload. The relay cannot read this wrapper, but the runner can compare it with the relay's plaintext source label to reject re-labeled frames.
Types ¶
type Cipher ¶
type Cipher struct {
// contains filtered or unexported fields
}
Cipher seals and opens frames for one direction (one key). aad binds frames to the session (it is the session id), preventing cross-session replay.
func NewCipher ¶
NewCipher builds a Cipher from a 32-byte key and the additional authenticated data.
type Keys ¶
type Keys struct {
R2V []byte // seals runner→viewer
V2R []byte // seals viewer→runner
Fingerprint string
}
Keys are the directional AEAD keys plus a short fingerprint, all derived from the session secret (and an optional passphrase). The fingerprint is shown at both ends so a human can confirm they are looking at the same session.
func DeriveKeys ¶
DeriveKeys turns the session secret (and optional passphrase) into directional keys. id is the session id, used as the HKDF salt so keys are bound to the session. When a passphrase is set it is stretched with PBKDF2 and folded into the key material, so knowing the link alone (the secret) is not enough to decrypt.