protocol

package
v0.0.0-...-9d22358 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 10 Imported by: 0

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

View Source
const (
	// SecretLen is the size of the session secret S.
	SecretLen = 32
	// KeyLen is the size of each derived AES-256 key.
	KeyLen = 32
)
View Source
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.

View Source
const (
	ControlReadOnly byte = 0
	ControlGranted  byte = 1
	ControlTaken    byte = 2
)

Control states carried by KindControl.

Variables

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

func DecodeExit(b []byte) (int32, error)

DecodeExit parses an EXIT payload.

func DecodeHello

func DecodeHello(b []byte) (baseline uint64, cols, rows uint16, err error)

DecodeHello parses a HELLO payload.

func DecodeRelayViewerFrame

func DecodeRelayViewerFrame(b []byte) (viewerID string, sealedFrame []byte, err error)

DecodeRelayViewerFrame unwraps EncodeRelayViewerFrame.

func DecodeResize

func DecodeResize(b []byte) (cols, rows uint16, err error)

DecodeResize parses a RESIZE payload.

func DecodeViewerPayload

func DecodeViewerPayload(b []byte) (viewerID string, payload []byte, err error)

DecodeViewerPayload unwraps EncodeViewerPayload.

func EncodeExit

func EncodeExit(code int32) []byte

EncodeExit builds an EXIT payload.

func EncodeHello

func EncodeHello(baseline uint64, cols, rows uint16) []byte

EncodeHello builds a HELLO payload: the input-seq baseline the viewer must start at, plus the current terminal size.

func EncodeRelayViewerFrame

func EncodeRelayViewerFrame(viewerID string, sealedFrame []byte) []byte

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

func EncodeResize(cols, rows uint16) []byte

EncodeResize builds a RESIZE payload.

func EncodeViewerPayload

func EncodeViewerPayload(viewerID string, payload []byte) []byte

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

func NewCipher(key, aad []byte) (*Cipher, error)

NewCipher builds a Cipher from a 32-byte key and the additional authenticated data.

func (*Cipher) Open

func (c *Cipher) Open(frame []byte) (seq uint64, kind byte, payload []byte, err error)

Open authenticates and decrypts a wire frame, returning (seq, kind, payload). A frame that fails authentication (tampered, wrong key, wrong session) returns ErrOpen.

func (*Cipher) Seal

func (c *Cipher) Seal(seq uint64, kind byte, payload []byte) ([]byte, error)

Seal produces a wire frame (nonce ‖ ciphertext) for the message (seq, kind, payload). The nonce is fresh random bytes, so reconnects can never reuse a nonce.

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

func DeriveKeys(secret []byte, id, passphrase string) (Keys, error)

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.

Jump to

Keyboard shortcuts

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