Help us shape the future of Go! Take the 2026 Go Developer Survey

ws

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package ws implements the daemon's binary WebSocket protocol for PTY input, output, resizing, state updates, and bounded reconnect replay. The public wire contract is documented in docs/protocol.md.

Index

Constants

View Source
const (
	MsgInput                 byte = 0x00 // C→S: raw PTY input bytes
	MsgOutput                byte = 0x00 // S→C: raw PTY output bytes
	MsgResize                byte = 0x01 // C→S: JSON {"cols":N,"rows":N}
	MsgTitle                 byte = 0x01 // S→C: UTF-8 OSC window title
	MsgReplayStart           byte = 0x02 // S→C: replay bracket start (uncompressed)
	MsgReplayEnd             byte = 0x03 // S→C: replay end + delta anchor [u64 epoch BE][u64 totalWritten BE]
	MsgSessionsUpdate        byte = 0x04 // S→C: JSON live sessions list
	MsgNotify                byte = 0x05 // S→C: JSON done-for-you alert
	MsgUnifiedUpdate         byte = 0x06 // S→C: JSON unified sessions list
	MsgAuth                  byte = 0x07 // C→S: raw bearer token bytes (first frame only)
	MsgClaudeState           byte = 0x08 // S→C: JSON per-tab attention state
	MsgDeltaRequest          byte = 0x09 // C→S: [u64 epoch BE][u64 resumeOffset BE]
	MsgDeltaStart            byte = 0x0A // S→C: delta bracket start
	MsgDeltaEnd              byte = 0x0B // S→C: delta end + new anchor [u64 epoch BE][u64 totalWritten BE]
	MsgClearTerminal         byte = 0x0C // S→C: clear screen+scrollback (agent /clear)
	MsgReplayStartCompressed byte = 0x0D // S→C: replay bracket start (raw-deflate)
	// 0x0E/0x0F are reserved-deferred for the attachment-lease feature — avoid.
	MsgControlState byte = 0x10 // S→C: JSON {"hasDriver":bool,"cols":N,"rows":N} — current PTY driver (Option D "tap to drive")

)

MSG_* frame-type bytes. The first byte of every WS binary frame is one of these; the rest is the type-specific payload. Values are wire contracts and must not be renumbered.

Note 0x00 and 0x01 are reused for input/output and resize/title respectively — direction (C→S vs S→C) disambiguates, never both on the same side.

Variables

This section is empty.

Functions

func AuthGate

func AuthGate(ctx context.Context, c *websocket.Conn) error

AuthGate reads one frame from c and verifies it is MsgAuth + the correct bearer. On success returns nil — the caller then drops all read timeouts and arms replay + the I/O loop. On failure closes c and returns a descriptive error for the daemon log (the close reason sent to the peer is uniform "unauthorized" regardless of failure mode).

The passed context is the ACCEPT-time request context; AuthGate wraps it in a 5 s timeout for the auth read only.

func DecodeDeltaRequest

func DecodeDeltaRequest(payload []byte) (epoch, resumeOffset uint64, ok bool)

DecodeDeltaRequest parses the payload of a MsgDeltaRequest frame (the 16 bytes AFTER the type byte). Returns ok=false if the payload is not exactly 16 bytes — iOS always sends exactly epoch+offset, so any other length is a protocol error and the handler falls back to full replay.

func DecodeResize

func DecodeResize(payload []byte) (rows, cols uint16, ok bool)

DecodeResize parses the JSON payload of a MsgResize frame. Returns ok=false on short/long/malformed JSON. The handler ignores a bad resize rather than dropping the connection — a malformed resize must not kill an otherwise healthy session.

func DeflateRaw

func DeflateRaw(src []byte) []byte

DeflateRaw compresses src into a fresh byte slice using raw deflate. Returns nil when:

  • src is below the compression threshold (caller sends uncompressed), OR
  • compression didn't shrink src (incompressible data — caller sends uncompressed so iOS's flate path isn't taken at all).

The returned slice is safe to retain; the pool's Writer is reset before next use.

func EncodeControlState

func EncodeControlState(hasDriver bool, rows, cols uint16) []byte

EncodeControlState builds the MsgControlState[JSON] frame. The JSON payload is the stable control-state payload consumed by compatible clients.

func EncodeDeltaEnd

func EncodeDeltaEnd(epoch, totalWritten uint64) []byte

EncodeDeltaEnd builds the MsgDeltaEnd[epoch BE][offset BE] frame. Same layout as replay-end; distinct type byte so iOS knows the bytes it just received were a delta, not a full snapshot.

func EncodeFrame

func EncodeFrame(t byte, payload []byte) []byte

EncodeFrame returns a single binary frame: [type byte][payload]. payload may be nil for type-only frames (replay/delta start).

func EncodeReplayEnd

func EncodeReplayEnd(epoch, totalWritten uint64) []byte

EncodeReplayEnd builds the MsgReplayEnd[epoch BE][offset BE] frame. iOS uses these two values as its delta anchor for the next reconnect.

func InflateRaw

func InflateRaw(src []byte) ([]byte, error)

InflateRaw is the inverse of deflateRaw. Production never decompresses its own output, but tests use it to verify round-trip parity with what iOS will decompress, and a future debug tool could call it to inspect a captured compressed replay. Exported for those callers.

Types

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler is the WebSocket endpoint. One instance serves all /ws/{id} requests; per-connection state lives in the ServeHTTP goroutine + the reader/writer/pinger children it spawns.

func NewHandler

func NewHandler(mgr *pty.Manager, epoch uint64, log *slog.Logger) *Handler

NewHandler builds the WS handler. epoch is the daemon-start epoch used in replay/delta end-frames; iOS anchors against it to detect daemon restarts.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP upgrades to WS, authenticates, and runs the per-connection loop. The HTTP layer does NOT auth-gate this route (it's bare on the mux like /api/health) — auth happens in the WS first frame so the bearer never appears in URL/proxy logs and the upgrade response itself leaks nothing.

Jump to

Keyboard shortcuts

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