ws

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 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