protocol

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package protocol defines the NDJSON wire format spoken between the pupptyeer daemon and its clients over a unix socket.

One JSON object per message; clients emit them newline-delimited. Raw PTY bytes ride in the base64 `data` field. A single Message shape is used for every message; the `type` field discriminates. The design goal is cross-language ergonomics (Go, TS, Python clients) over raw throughput. See PROTOCOL.md for the canonical wire contract.

Index

Constants

View Source
const (
	TypeNewSession   = "new_session"
	TypeListSessions = "list_sessions"
	TypeAttach       = "attach"
	TypeDetach       = "detach"
	TypeWritePane    = "write_pane"
	TypeCapturePane  = "capture_pane"
	TypeResize       = "resize"
	TypeKill         = "kill"
	TypeGC           = "gc"
)

Message types (client → server).

View Source
const (
	TypeOK            = "ok"
	TypeError         = "error"
	TypeSessions      = "sessions"
	TypeAttached      = "attached"
	TypeOutput        = "output"
	TypeScrollbackEnd = "scrollback_end"
	TypeCapture       = "capture"
	TypeExit          = "exit"
	TypeSessionClosed = "session_closed"
	TypeReaped        = "reaped"
)

Message types (server → client).

Variables

This section is empty.

Functions

func DecodeData

func DecodeData(s string) ([]byte, error)

DecodeData decodes the `data` field back to raw bytes.

func EncodeData

func EncodeData(b []byte) string

EncodeData base64-encodes raw bytes for the `data` field.

func Marshal

func Marshal(m Message) ([]byte, error)

Marshal serialises a message to a single NDJSON line (with trailing \n).

Types

type Cursor added in v0.2.0

type Cursor struct {
	Row     int  `json:"row"`
	Col     int  `json:"col"`
	Visible bool `json:"visible"`
}

Cursor is the cursor position in a rendered capture. Row/Col are 0-based; Col may equal the grid width (a pending-wrap cursor). Visible reflects the terminal's cursor-visibility (DECTCEM) state.

type Decoder

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

Decoder reads newline-delimited messages from r. It uses a large buffer because base64 output lines can be tens of KiB.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a Decoder reading from r.

func (*Decoder) Decode

func (d *Decoder) Decode() (Message, error)

Decode reads the next message. Returns io.EOF at clean end of stream.

type Encoder

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

Encoder writes newline-delimited messages to w.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder returns an Encoder writing to w.

func (*Encoder) Encode

func (e *Encoder) Encode(m Message) error

Encode writes one message as an NDJSON line.

type Message

type Message struct {
	Type    string `json:"type"`
	ID      int    `json:"id,omitempty"`
	Session string `json:"session,omitempty"`

	// namespace scopes a session: identity is (namespace, id). Carried by
	// new_session and every session-addressed verb, and echoed on the
	// server->client events (output/exit/capture/...) so a client can tell
	// apart sessions whose ids collide across namespaces. Empty means the
	// "default" namespace - so old clients that send none operate entirely
	// there (backward compatible). See PROTOCOL.md.
	Namespace string `json:"namespace,omitempty"`

	// All, on list_sessions/gc, selects every namespace instead of just the
	// one in Namespace. An explicit flag, deliberately not a "*" sentinel.
	All bool `json:"all,omitempty"`

	// new_session
	Command string            `json:"command,omitempty"`
	Args    []string          `json:"args,omitempty"`
	Cwd     string            `json:"cwd,omitempty"`
	Env     map[string]string `json:"env,omitempty"`
	Cols    int               `json:"cols,omitempty"`
	Rows    int               `json:"rows,omitempty"`
	Raw     bool              `json:"raw,omitempty"` // new_session: don't run a terminal emulator for this session (no rendered capture; lower CPU/latency)

	// new_session, caller-supplied id. RequestedID, if set, becomes the
	// session's id instead of a daemon-generated UUID. With GetOrCreate, an
	// alive session already holding RequestedID is returned as-is (no new
	// process); without it, a clash is an error.
	RequestedID string `json:"requested_id,omitempty"`
	GetOrCreate bool   `json:"get_or_create,omitempty"`

	// gc
	MaxIdleSeconds int `json:"max_idle_seconds,omitempty"`

	// data-bearing (output / write_pane / capture)
	Data string `json:"data,omitempty"` // base64 of raw PTY bytes
	Text string `json:"text,omitempty"` // convenience: raw UTF-8 input for write_pane

	// capture_pane options
	Render    bool `json:"render,omitempty"`     // return the rendered grid instead of raw bytes
	SettleMs  int  `json:"settle_ms,omitempty"`  // hold reply until PTY quiet for this long
	TimeoutMs int  `json:"timeout_ms,omitempty"` // cap on settle wait; <=0 uses the default

	// rendered capture response (Render == true). Cols/Rows above carry the
	// grid dimensions; Lines/Cursor/AltScreen describe the visible screen.
	Lines     []string `json:"lines,omitempty"`
	Cursor    *Cursor  `json:"cursor,omitempty"`
	AltScreen bool     `json:"alt_screen,omitempty"`

	// responses / events
	Message  string        `json:"message,omitempty"`   // error text
	ExitCode *int          `json:"exit_code,omitempty"` // pointer so 0 is preserved
	Sessions []SessionInfo `json:"sessions,omitempty"`
}

Message is the single wire shape. Fields are omitempty so a given message only carries what its type needs. Clients read the fields relevant to the type they received.

type SessionInfo

type SessionInfo struct {
	ID           string   `json:"id"`
	Namespace    string   `json:"namespace"`
	Command      string   `json:"command"`
	Args         []string `json:"args,omitempty"`
	Cwd          string   `json:"cwd,omitempty"`
	Cols         int      `json:"cols"`
	Rows         int      `json:"rows"`
	Created      string   `json:"created"`
	LastActivity string   `json:"last_activity"`
	Attached     int      `json:"attached"`
	Alive        bool     `json:"alive"`
	Raw          bool     `json:"raw,omitempty"` // session was created with raw:true (no terminal emulator)
}

SessionInfo is the metadata returned by list_sessions (and the reaped list from gc). LastActivity is the RFC3339 time of the most recent PTY input or output; gc ages sessions by it.

Jump to

Keyboard shortcuts

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