ghostline

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 28 Imported by: 0

README

ghostline

ghostline is a local-first terminal session runtime for Go. It owns real pseudo-terminals, keeps child processes independent from clients, maintains a Ghostty-compatible terminal model, and exposes resumable raw output.

The package provides terminal mechanics. It is not a terminal UI, a remote shell protocol, a cross-host migration system, or a reboot-persistent process manager.

v1 capabilities

  • In-process sessions through Hub, or same-host process separation through Server and Client.
  • One concrete *Session API for local and daemon-owned sessions.
  • Argv-first process startup. Shell evaluation is explicit through Shell.
  • Context-aware input, resize, wait, status, metadata, replay, output, and lifecycle operations.
  • Immutable output segments addressed by opaque, comparable Cursor values.
  • Bounded OutputReader streams with cancellation and natural backpressure.
  • Atomic checkpoints that pair a VT replay with the first raw output byte not represented by that replay.
  • Atomic VT state captures that pair a complete opaque emulator state with the first raw output byte not represented by that state.
  • Same-version daemon upgrades that adopt live PTY file descriptors, VT state, and output generations, with ANSI or blank-screen recovery when native state is not portable.

v1 is intentionally incompatible with every v0 public API and wire method. The final v0.x daemon exposes a separate, explicit handoff contract for Warren-coordinated upgrades; it is not a mixed-protocol compatibility mode. See the v1 migration note.

Requirements and platform status

  • Go 1.25 or newer.
  • macOS 13 or newer on amd64 or arm64, with CGo enabled.
  • Linux with glibc 2.31 or newer on amd64 or arm64, with CGo enabled.

Both supported families statically embed libghostty-vt; applications do not need to install or deploy a Ghostty dynamic library. FreeBSD is compile-checked with CGo disabled but does not have a working VT runtime. Windows is not supported because ghostline requires Unix PTYs, Unix sockets, file-descriptor transfer, and process-group signals.

The bundled libraries are built from pinned Ghostty commit 88f57ee66eeaad4da77b414b245f7b6693348985. See the third-party artifact manifest for build commands, targets, checksums, and license information.

For high-density daemon use, set a realistic VTScrollbackMaxBytes budget and an explicit ServerMaxClientConnections limit. The default connection limit is 1,024. Each live Output, Replay, Checkpoint, or AtomicState stream holds one server socket connection; the daemon is not a multiplexed stream transport. Clients can read the configured limit from Client.VersionInfo.

Local sessions

hub, err := ghostline.New(ghostline.Options{
	OutputDir:   "/var/lib/my-app/ghostline",
	DefaultSize: ghostline.Size{Columns: 120, Rows: 36},
})
if err != nil {
	return err
}
defer hub.Close()

session, err := hub.Start(ctx, ghostline.SessionOptions{
	Name: "build",
	Process: ghostline.ProcessSpec{
		Path:        "go",
		Args:        []string{"test", "./..."},
		Directory:   "/path/to/worktree",
		Environment: []string{"CI=1"},
	},
})
if err != nil {
	return err
}

output, err := session.Output(ctx, ghostline.Cursor{})
if err != nil {
	return err
}
defer output.Close()

_, err = io.Copy(os.Stdout, output)

A zero ProcessSpec starts $SHELL, falling back to sh. Use ghostline.Shell("make test && make lint") only when shell parsing is wanted. Environment overrides matching inherited variables. ghostline supplies TERM=xterm-256color when the resulting environment has no non-empty TERM.

Output and checkpoints

Output(ctx, cursor) returns raw PTY bytes in order. A zero cursor starts at the earliest retained generation. Each Read returns at most the size of the caller's buffer; daemon reads request at most 64 KiB per round trip. Closing the reader or canceling its context unblocks a pending read.

Cursor fields are deliberately private. Store Cursor.String() or use its text/JSON marshaling methods. ParseCursor accepts the stable v1 text form. After retention removes a generation, opening a new reader at one of its cursors returns ErrCursorExpired. A reader that already pinned the segment may finish draining it.

For a lossless window switch or client reattach:

  1. Stop and wait for the current output-reading goroutine.
  2. Call Checkpoint(ctx).
  3. Open a new reader at checkpoint.Cursor.
  4. Write checkpoint.Replay to the destination.
  5. Start reading from the new reader.

The checkpoint lock orders the VT replay and cursor atomically. Starting the new reader only after writing the replay prevents raw bytes from appearing before the reconstructed screen.

When the destination can install ghostline's bundled VT state format, use AtomicState to avoid replaying the entire screen as input:

state, err := session.AtomicState(ctx)
if err != nil {
	return err
}
if state.Format != ghostline.AtomicStateFormat {
	return fmt.Errorf("unsupported terminal state format %q", state.Format)
}
// Install state.Payload as one opaque unit, then open Output at state.Cursor.
output, err := session.Output(ctx, state.Cursor)

AtomicState is not a VT byte replay. Its payload is the raw GHOSTSNP CRC-protected binary snapshot record stream that includes emulator details such as scrollback, cursor, modes, and parser continuation state. The READY record terminates a renderable prefix; older history pages follow it and FINISH terminates the complete snapshot. A compatible consumer may decode READY first and prepend history pages incrementally, or decode the complete payload in one operation. The payload and cursor are captured under one session boundary; consumers must install the renderable state before consuming output from the returned cursor.

Output retention

The core exposes mechanism, not retention policy:

boundary, err := session.RotateOutput(ctx)
if err != nil {
	return err
}
// Archive or account for completed generations in application policy.
if err := session.PruneOutput(ctx, boundary); err != nil {
	return err
}

Rotation completes the active generation and creates a new one. Pruning only accepts a generation-boundary cursor returned by rotation. The core does not compress segments, choose archive counts, or prune automatically. VT scrollback and raw output retention are independent; see scrollback and output retention.

Daemon client

Run the server:

go run ./cmd/ghostline serve --socket /tmp/ghostline.sock

Use NewClient when the caller or a service manager owns that process:

client := ghostline.NewClient("/tmp/ghostline.sock")
if err := client.Check(ctx); err != nil {
	return err
}

Use the explicitly managed constructor when the client should spawn a missing server and lazily restore service after a transport failure:

client, err := ghostline.ConnectManaged(ctx, ghostline.ManagedClientOptions{
	Socket: "/tmp/ghostline.sock",
	Spawn:  []string{"ghostline", "serve", "--socket", "{socket}"},
})
if err != nil {
	return err
}
defer client.Close()

ConnectManaged keeps at most the latest 64 KiB of startup diagnostics. Read-only operations and Start may bootstrap and retry once after a transport failure. Input, resize, termination, deletion, rotation, and pruning are never retried because repeating them can change meaning. Client.Close stops only a server spawned by that client.

Hub and Client expose matching Start, Get, and List methods. Daemon output, replay, and checkpoints travel over the socket; clients never open server-side output files.

Lifecycle and durability

  • Wait reports *ExitError. Canceling its context stops waiting, not the child.
  • Status distinguishes process state from transport failure.
  • Terminate ends the process tree but retains the session record and output.
  • Delete ends the process tree and removes both the session record and its output storage. Archive required output before deletion.
  • Hub.Close terminates all local sessions. Server.Shutdown does the same for daemon-owned sessions.
  • The working directory is read from the OSC 7 report a shell integration emits.
  • Foreground process name and command line are opt-in through ProbeForeground.

Sessions survive client detach and a successful same-version rolling daemon upgrade. They do not survive daemon crashes that lose PTY masters, host reboots, or cross-machine moves. Raw output is written to files, but those files cannot resurrect a dead process or reconstruct an unretained VT state.

Rolling daemon upgrades

Server.Adopt and ghostline serve --adopt-from transfer a complete batch from an existing server's <socket>.admin endpoint. Both sides must advertise exactly ProtocolVersion == "1.0.0"; protocol mismatch is rejected before any session is prepared. A successful batch transfers native VT state, live PTY file descriptors, output directories, and the active generation. There is no implicit v0 replay bridge in this native path. The final v0.8 compatibility daemon can be handed off separately through docs/v0-compat-bridge.md; that path rebuilds a fresh v1 VT/output generation and never reuses a v0 byte offset as a cursor.

See RFC 0002 for ordering and failure semantics. The real-binary handoff and crash-window rehearsal is documented in docs/integration-testing.md.

Protocol and security

The daemon uses the v1 bounded JSON-envelope protocol over mode 0600 Unix sockets. Binary input/output is an exact-length raw payload after the JSON header; it is not JSON/base64. Headers and payloads are limited to 1 MiB, and raw output, Replay, and Checkpoint replay use 64 KiB pull-stream chunks. See RFC 0004 for framing, IDs, state, and extension rules. Use errors.Is(err, ErrFrameTooLarge) for a frame limit violation.

The public and admin sockets assume trusted same-user, same-host callers. They have no remote authentication or encryption. Do not expose them through a TCP proxy or to another trust domain.

minimux example

The independent module in examples/minimux demonstrates multiple windows, background terminal-query responses, output readers, and checkpoint-safe switching:

cd examples/minimux
go run .
go run . -- htop

Keys are Ctrl-B c (create), Ctrl-B n/p (switch), Ctrl-B x (delete), and Ctrl-B q (quit).

libghostty-vt

The repository includes headers, a macOS 13+ universal static archive, and Linux glibc 2.31+ static archives for amd64 and arm64. The package selects and links the matching archive through CGo. Builds with CGo disabled, or builds on an unsupported OS or architecture, compile with the unavailable backend; Hub.Check and Start then return ErrUnavailable.

Design documents

Documentation

Overview

Package ghostline provides a local-first terminal session runtime for Go.

A Hub owns pseudo-terminals and child processes in the embedding process. A Server and Client provide the same concrete Session contract over a same-host Unix socket. Session identity accessors are cached; every operation that can perform process, storage, or network I/O accepts a context and returns an error.

Raw PTY output is stored in immutable generations plus one active segment. Output returns a bounded reader positioned by an opaque Cursor. Checkpoint atomically pairs a terminal replay with the cursor of the first raw byte not represented by that replay. AtomicState provides the same boundary for a complete, versioned VT emulator state when the consumer can install the advertised opaque format. Callers own reader cancellation, goroutines, archive format, and retention policy.

Sessions survive client detach and successful same-version daemon adoption. They do not survive daemon crashes that lose PTY ownership, host reboot, or cross-machine migration.

Index

Constants

View Source
const (
	// CapabilityRawPayload indicates that envelopes may be followed by an
	// exact-length unencoded payload.
	CapabilityRawPayload = "raw-payload-v1"
	// CapabilityStreams indicates support for the v1 pull-stream state machine.
	CapabilityStreams = "pull-stream-v1"
	// CapabilityAtomicState indicates support for the atomic native VT state
	// stream. Its payload format is advertised separately by blob open results.
	CapabilityAtomicState = "atomic-state-v1"
)
View Source
const AtomicStateFormat = "ghostty-vt-snapshot-v1"

AtomicStateFormat identifies the opaque payload encoding returned by Session.AtomicState. The format is tied to ghostline's bundled VT implementation and is deliberately separate from the RPC wire version. Consumers must treat Payload as opaque and reject formats they do not understand.

View Source
const DefaultServerMaxClientConnections = 1024

DefaultServerMaxClientConnections is the maximum number of active client socket connections accepted by a Server when Options.ServerMaxClientConnections is zero. It leaves room for hundreds of long-lived output streams and their concurrent control calls on a trusted same-host socket.

View Source
const DefaultVTScrollbackMaxBytes uint64 = 2 << 20

DefaultVTScrollbackMaxBytes is the default logical scrollback budget for each embedded VT terminal. libghostty stores history in page-sized units, so the physical allocation can be somewhat larger than this value.

View Source
const ProtocolVersion = "1.0.0"

ProtocolVersion identifies the RPC protocol spoken by the server. Clients use it to detect an outdated server process during upgrades instead of failing on unknown methods.

View Source
const V0HandoffProtocolVersion = "ghostline-v0-to-v1-1"

V0HandoffProtocolVersion identifies the final v0 compatibility contract accepted by the v1 migration consumer. It is separate from ProtocolVersion so v1 never treats a v0 source as a native same-version daemon.

Variables

View Source
var (
	// ErrUnavailable indicates that libghostty-vt cannot be used by this build.
	ErrUnavailable = errors.New("ghostline: libghostty-vt unavailable")
	// ErrClosed indicates that the hub is closed.
	ErrClosed = errors.New("ghostline: hub closed")
	// ErrSessionExists indicates that the name is already taken.
	ErrSessionExists = errors.New("ghostline: session already exists")
	// ErrSessionNotFound indicates that no session has the requested name.
	ErrSessionNotFound = errors.New("ghostline: session not found")
	// ErrSessionClosed indicates that a session handle is no longer usable.
	ErrSessionClosed = errors.New("ghostline: session closed")
	// ErrInvalidSessionName indicates that a name cannot identify session
	// storage safely.
	ErrInvalidSessionName = errors.New("ghostline: invalid session name")
	// ErrInvalidSignal indicates that Signal received nil, zero, or a signal
	// value that is not backed by syscall.Signal.
	ErrInvalidSignal = errors.New("ghostline: invalid process signal")
	// ErrInvalidCursor indicates a malformed cursor or a position beyond the
	// output currently available in its generation.
	ErrInvalidCursor = errors.New("ghostline: invalid output cursor")
	// ErrCursorExpired indicates that retention pruned the cursor's generation.
	ErrCursorExpired = errors.New("ghostline: output cursor expired")
	// ErrFrameTooLarge indicates that one RPC frame exceeded the protocol
	// limit. Large protocol payloads must use a chunked stream instead.
	ErrFrameTooLarge = errors.New("ghostline: RPC frame too large")
	// ErrProtocolMismatch indicates that peers use incompatible wire framing.
	ErrProtocolMismatch = errors.New("ghostline: RPC protocol mismatch")
)

Functions

func TagVersion added in v0.6.3

func TagVersion() string

TagVersion returns the Ghostline module version embedded in the running binary. Development builds and local replacements intentionally report an empty value because they do not carry a release tag.

Types

type AdoptReport added in v1.1.1

type AdoptReport struct {
	Adopted int
	Skipped map[string]string
}

AdoptReport contains per-session details for an adoption that could not recover the complete source inventory. A non-empty Skipped map means the destination committed no sessions and the source remains authoritative.

type AtomicState added in v1.1.0

type AtomicState struct {
	// Format identifies Payload's serialization format.
	Format string
	// Payload is the opaque serialized VT state.
	Payload []byte
	// Cursor is the first raw output byte not represented by Payload.
	Cursor Cursor
}

AtomicState is a complete terminal emulator state paired with the first raw output byte not represented by that state. It is suitable for an atomic reattach when the consumer can install the advertised Ghostty VT snapshot format.

Payload is not a VT replay stream. It is an opaque serialized state stream whose interpretation is selected by Format. The v1 format is the native Ghostty snapshot record stream and may contain scrollback, cursor, modes, parser continuation state, READY, and FINISH records. The payload and cursor are captured while the session output lock is held, so opening Output at Cursor after installing Payload cannot duplicate or omit bytes at the boundary.

type Checkpoint added in v0.2.0

type Checkpoint struct {
	// Replay is a full VT replay of the visible grid and scrollback.
	Replay []byte
	// Cursor is the first raw output byte not covered by Replay.
	Cursor Cursor
}

Checkpoint is an atomic screen replay and raw output position.

type Client added in v0.2.0

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

Client proxies Hub operations to a Server over a Unix socket.

func ConnectManaged added in v1.0.0

func ConnectManaged(ctx context.Context, options ManagedClientOptions) (*Client, error)

ConnectManaged returns a client, spawning the server when the socket is missing. The returned client owns the spawned process; Close stops it. This lifecycle behavior is intentionally separate from plain NewClient.

func NewClient added in v0.2.0

func NewClient(socketPath string) *Client

NewClient returns a client for the server at socketPath.

func (*Client) Check added in v0.2.0

func (c *Client) Check(ctx context.Context) error

Check verifies that the server socket accepts connections.

func (*Client) Close added in v0.3.0

func (c *Client) Close() error

Close stops the server that this client spawned. Clients that connected to an existing server have nothing to stop.

func (*Client) Ensure added in v0.3.0

func (c *Client) Ensure(ctx context.Context) error

Ensure starts the server if it is missing and waits until it is ready.

func (*Client) Get added in v1.0.0

func (c *Client) Get(ctx context.Context, name string) (*Session, error)

Get returns a daemon-owned session handle or ErrSessionNotFound.

func (*Client) List added in v0.2.0

func (c *Client) List(ctx context.Context) ([]*Session, error)

List returns daemon-owned sessions in the server's stable order.

func (*Client) PID added in v0.3.4

func (c *Client) PID() int

PID returns the process ID of the server spawned by this client, or zero when the client attached to an existing server.

func (*Client) Socket added in v0.2.0

func (c *Client) Socket() string

Socket returns the server socket path.

func (*Client) Start added in v0.2.0

func (c *Client) Start(ctx context.Context, options SessionOptions) (*Session, error)

Start creates a session on the server and returns its remote handle.

func (*Client) Version added in v0.3.3

func (c *Client) Version(ctx context.Context) (string, error)

Version returns the server's RPC protocol version. Use VersionInfo when the release tag is also needed.

func (*Client) VersionInfo added in v0.6.3

func (c *Client) VersionInfo(ctx context.Context) (VersionInfo, error)

VersionInfo returns the server's RPC protocol version and release tag.

type ColorQueryCallback added in v0.6.2

type ColorQueryCallback func(ColorQueryKind) (color string, ok bool)

ColorQueryCallback supplies a color for an OSC 10 or OSC 11 query.

The callback should return a six-digit RGB value with an optional leading '#'. It returns false when the requested color is not available. A callback is optional; without one, unknown colors receive no reply. Feed invokes the callback synchronously without holding the responder's internal lock. The callback may re-enter the responder. Concurrent Feed calls may invoke it concurrently.

type ColorQueryKind added in v0.6.2

type ColorQueryKind uint8

ColorQueryKind identifies the terminal color requested by an OSC query.

const (
	// ColorQueryForeground is the default text color (OSC 10).
	ColorQueryForeground ColorQueryKind = 10
	// ColorQueryBackground is the default background color (OSC 11).
	ColorQueryBackground ColorQueryKind = 11
)

type Cursor added in v1.0.0

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

Cursor identifies a position in a session's output log. Its representation is intentionally opaque; cursors may be compared, stored as text, and passed back to Output, but their fields are not independently meaningful. The zero Cursor asks Output to start at the earliest retained byte.

func ParseCursor added in v1.0.0

func ParseCursor(value string) (Cursor, error)

ParseCursor parses the stable text representation produced by Cursor.String.

func (Cursor) MarshalText added in v1.0.0

func (c Cursor) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Cursor) String added in v1.0.0

func (c Cursor) String() string

String returns the stable text form of c. The zero Cursor is encoded as an empty string.

func (*Cursor) UnmarshalText added in v1.0.0

func (c *Cursor) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

type ExitError added in v0.3.0

type ExitError struct {
	// Code is the process exit status, or -1 when the process was signaled.
	Code int
	// Signal names the terminating signal when the process was signaled.
	Signal string
	// Unknown is true when a migrated child exited after its original parent
	// had already gone away. In that case the new server can observe the PTY
	// closing, but the operating system cannot provide the original wait
	// status to a different parent process.
	Unknown bool
}

ExitError describes a terminated child process.

func (*ExitError) Error added in v0.3.0

func (e *ExitError) Error() string

type Hub added in v0.2.0

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

Hub owns local pseudo-terminal sessions.

func New added in v0.2.0

func New(options Options) (*Hub, error)

New constructs a session hub.

func (*Hub) Check added in v0.2.0

func (h *Hub) Check(ctx context.Context) error

Check verifies that libghostty-vt can create a terminal.

func (*Hub) Close added in v0.2.0

func (h *Hub) Close() error

Close terminates every session and prevents further Start calls.

func (*Hub) Get added in v1.0.0

func (h *Hub) Get(ctx context.Context, name string) (*Session, error)

Get returns a handle for a known session name.

func (*Hub) List added in v0.2.0

func (h *Hub) List(ctx context.Context) ([]*Session, error)

List returns all known sessions ordered by creation time and name.

func (*Hub) Start added in v0.2.0

func (h *Hub) Start(ctx context.Context, options SessionOptions) (*Session, error)

Start creates and starts a session.

type ManagedClientOptions added in v1.0.0

type ManagedClientOptions struct {
	// Socket is the Unix socket path the server listens on.
	Socket string
	// Spawn is the command used to start the server when the socket is
	// missing. Arguments may contain {socket}, replaced by Socket. Empty uses
	// ["ghostline", "serve", "--socket", socket].
	Spawn []string
	// Env overrides the spawned server's environment.
	Env []string
	// ReadyTimeout bounds how long ConnectManaged waits for the socket. Zero
	// uses 5s.
	ReadyTimeout time.Duration
	// Log receives serialized writes from the spawned server's stdout and
	// stderr. Empty discards them after retaining bounded diagnostics.
	Log io.Writer
}

ManagedClientOptions configures how ConnectManaged starts a missing server. Use NewClient when process lifecycle is owned by the caller or a service manager.

type Options added in v0.2.0

type Options struct {
	// OutputDir stores segmented session output. An empty value uses
	// $HOME/.ghostline/output.
	OutputDir string
	// DefaultSize is used when SessionOptions.Size is zero. The default is
	// 120 columns by 36 rows.
	DefaultSize Size
	// DefaultTerm is used for pty children whose environment has no
	// non-empty TERM. An empty value defaults to xterm-256color.
	DefaultTerm string
	// VTScrollbackMaxBytes is the default logical scrollback budget for new
	// sessions. Zero uses DefaultVTScrollbackMaxBytes.
	VTScrollbackMaxBytes uint64
	// ProbeForeground enables OS-level foreground process metadata (name and
	// command line). Disabled by default; Session.Metadata reports an empty
	// process and command line without spawning any OS probes. The OSC 7
	// working directory is parsed from the output stream regardless of this
	// setting.
	ProbeForeground bool
	// ServerMaxClientConnections limits concurrently active client socket
	// connections when these options are passed to NewServer. A connection is
	// active for the life of an Output, Replay, or Checkpoint stream. Zero uses
	// DefaultServerMaxClientConnections. Hub ignores this field.
	ServerMaxClientConnections int
}

Options configures a Hub. Zero values select documented defaults.

type OutputReader added in v1.0.0

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

OutputReader streams raw PTY output from a cursor. Read applies natural backpressure: it returns at most len(p) bytes and does not buffer the rest in memory. Close unblocks a pending Read. Cursor returns the next unread byte.

func (*OutputReader) Close added in v1.0.0

func (r *OutputReader) Close() error

Close implements io.Closer. It is safe to call more than once.

func (*OutputReader) Cursor added in v1.0.0

func (r *OutputReader) Cursor() Cursor

Cursor returns the next raw output position Read will deliver.

func (*OutputReader) Read added in v1.0.0

func (r *OutputReader) Read(p []byte) (int, error)

Read implements io.Reader.

type ProcessSpec added in v1.0.0

type ProcessSpec struct {
	// Path is the executable path. Empty starts the user's shell when
	// ShellCommand and Args are also empty.
	Path string
	// Args are passed directly to Path without shell evaluation.
	Args []string
	// Directory is the child process working directory. Empty inherits the
	// parent process working directory.
	Directory string
	// Environment overrides inherited variables using KEY=VALUE entries.
	Environment []string
	// ShellCommand is evaluated by "sh -lc" and cannot be combined with Path
	// or Args.
	ShellCommand string
}

ProcessSpec describes the process started inside a session. Path and Args are the primary, shell-free form. ShellCommand is explicit opt-in shell evaluation and cannot be combined with Path or Args. A zero ProcessSpec starts $SHELL, falling back to sh.

func Shell added in v1.0.0

func Shell(command string) ProcessSpec

Shell returns a process specification evaluated by "sh -lc".

type ProtocolLimits added in v1.0.0

type ProtocolLimits struct {
	MaxHeaderBytes  int `json:"maxHeaderBytes"`
	MaxPayloadBytes int `json:"maxPayloadBytes"`
	MaxChunkBytes   int `json:"maxChunkBytes"`
}

ProtocolLimits are the framing limits advertised by VersionInfo.

type QueryResponder

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

QueryResponder answers terminal capability queries while a session has no attached terminal client. TUIs such as Codex send DA/DSR/OSC/kitty keyboard queries at startup. A raw PTY has nobody to answer until a client attaches, so the application may downgrade itself (for example disabling colors). Replies are written back into the PTY as input, never into output. Feed and Resize are safe for concurrent use. The responder starts no goroutines.

func NewQueryResponder

func NewQueryResponder() *QueryResponder

NewQueryResponder returns a responder initialized to a 120x36 terminal.

func NewQueryResponderWithColorQuery added in v0.6.2

func NewQueryResponderWithColorQuery(callback ColorQueryCallback) *QueryResponder

NewQueryResponderWithColorQuery returns a responder that uses callback to answer OSC 10 and OSC 11 color queries.

func (*QueryResponder) Feed

func (r *QueryResponder) Feed(data []byte) [][]byte

Feed scans output bytes for complete terminal queries and returns the replies to write back into the PTY. Queries split across chunks are buffered until complete or until they prove not to be queries. Color callbacks run synchronously after parsing and outside the internal lock.

func (*QueryResponder) Resize

func (r *QueryResponder) Resize(columns, rows int)

Resize updates the window size reported in XTWINOPS replies.

type Server added in v0.2.0

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

Server owns PTY sessions in a standalone process so clients can restart without ending any session. The wire protocol uses bounded JSON envelopes with optional exact-length raw payloads over a Unix socket.

func NewServer added in v0.2.0

func NewServer(options Options) (*Server, error)

NewServer constructs a server with its own hub.

func (*Server) Adopt added in v0.3.4

func (s *Server) Adopt(ctx context.Context, adminSocket string) (int, error)

Adopt migrates sessions into this server. Call it before Serve so the target never accepts a client while its session map is being rebuilt.

func (*Server) AdoptWithReport added in v1.1.1

func (s *Server) AdoptWithReport(ctx context.Context, adminSocket string) (AdoptReport, error)

AdoptWithReport migrates sessions and reports per-session skips separately so control-plane callers can surface failures that could not be recovered. Native snapshot errors are handled as lossy recovery and do not appear in Skipped when the PTY and a target terminal can still be created.

func (*Server) Close added in v0.2.0

func (s *Server) Close() error

Close stops accepting connections. Sessions keep running.

func (*Server) Serve added in v0.2.0

func (s *Server) Serve(ctx context.Context, socketPath string) error

Serve listens on socketPath and handles requests until ctx is canceled or the listener fails. The socket is created with mode 0600.

func (*Server) Shutdown added in v0.3.0

func (s *Server) Shutdown(ctx context.Context) error

Shutdown stops accepting connections and terminates every session.

type Session added in v0.2.0

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

Session is a concrete handle to one local or daemon-owned terminal session. Immutable identity is cached in the handle. Every method that can perform process, storage, or network I/O accepts a context and returns an error.

func (*Session) AtomicState added in v1.1.0

func (s *Session) AtomicState(ctx context.Context) (AtomicState, error)

AtomicState captures the terminal's complete emulator state and output cursor at one synchronization boundary. Payload is an opaque, versioned VT state envelope. Consumers must install it as one unit before applying raw output beginning at Cursor; they must not interleave that output with state installation.

func (*Session) Checkpoint added in v0.2.0

func (s *Session) Checkpoint(ctx context.Context) (Checkpoint, error)

Checkpoint atomically captures a replay and its output position.

func (*Session) CreatedAt added in v0.3.0

func (s *Session) CreatedAt() time.Time

CreatedAt returns when the child process started without performing I/O.

func (*Session) Delete added in v1.0.0

func (s *Session) Delete(ctx context.Context) error

Delete ends the process tree and removes the session record and output storage. Callers that need retained output must archive it before Delete.

func (*Session) Info added in v1.0.0

func (s *Session) Info() SessionInfo

Info returns the session's immutable identity without performing I/O.

func (*Session) Metadata added in v1.0.0

func (s *Session) Metadata(ctx context.Context) (SessionMetadata, error)

Metadata reports presentation metadata for the session. The working directory is the path the program reported through OSC 7 and is always collected from the parsed output stream. The foreground process name and command line require Options.ProbeForeground; both are best-effort and empty when probing is disabled or the foreground process cannot be resolved.

func (*Session) Name added in v0.2.0

func (s *Session) Name() string

Name returns the session's unique name without performing I/O.

func (*Session) Output added in v1.0.0

func (s *Session) Output(ctx context.Context, from Cursor) (*OutputReader, error)

Output streams raw PTY output beginning at from. The zero Cursor starts at the earliest retained byte. The caller must close the returned reader.

func (*Session) OutputCursor added in v1.0.0

func (s *Session) OutputCursor(ctx context.Context) (Cursor, error)

OutputCursor returns the current end of retained raw output without capturing a VT replay. Bytes may be appended immediately after it returns. Use Checkpoint when the cursor must be atomically paired with a replay.

func (*Session) PruneOutput added in v1.0.0

func (s *Session) PruneOutput(ctx context.Context, before Cursor) error

PruneOutput removes immutable generations strictly before before. before must be a generation-boundary cursor returned by RotateOutput.

func (*Session) Replay added in v1.0.0

func (s *Session) Replay(ctx context.Context) ([]byte, error)

Replay renders the visible grid and scrollback as terminal bytes.

func (*Session) Resize added in v0.3.0

func (s *Session) Resize(ctx context.Context, size Size) error

Resize updates the real PTY and the emulated grid.

func (*Session) RotateOutput added in v1.0.0

func (s *Session) RotateOutput(ctx context.Context) (Cursor, error)

RotateOutput completes the active output segment and returns the boundary cursor at the beginning of the new generation.

func (*Session) Signal added in v1.0.0

func (s *Session) Signal(ctx context.Context, signal os.Signal) error

Signal sends signal to the session's process group. signal must be a non-zero syscall.Signal, such as os.Interrupt or syscall.SIGTERM.

func (*Session) Size added in v1.0.0

func (s *Session) Size(ctx context.Context) (Size, error)

Size returns the current PTY grid size.

func (*Session) Status added in v0.3.0

func (s *Session) Status(ctx context.Context) (Status, error)

Status reports whether the session is running and, when stopped, why. A fatal session runtime or storage failure is returned as an error.

func (*Session) Terminate added in v1.0.0

func (s *Session) Terminate(ctx context.Context) error

Terminate ends the process tree but keeps the session record and output.

func (*Session) Wait added in v0.3.0

func (s *Session) Wait(ctx context.Context) error

Wait waits for the child and returns its exit error. A fatal output storage error is joined with the exit error. Canceling ctx stops waiting but does not terminate the child.

func (*Session) WriteInput added in v1.0.0

func (s *Session) WriteInput(ctx context.Context, data []byte) error

WriteInput writes bytes to the PTY verbatim.

type SessionInfo added in v1.0.0

type SessionInfo struct {
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"createdAt"`
}

SessionInfo is immutable session identity returned by List.

type SessionMetadata added in v0.5.0

type SessionMetadata struct {
	// Process is the foreground process name. It is empty when foreground
	// probing is disabled or the process cannot be resolved.
	Process string `json:"process,omitempty"`
	// CommandLine is the foreground process command line including arguments.
	// It is empty when foreground probing is disabled or the process cannot be
	// resolved.
	CommandLine string `json:"commandLine,omitempty"`
	// Directory is the working directory the program reported through OSC 7.
	// It is empty until a valid report is parsed.
	Directory string `json:"directory,omitempty"`
}

SessionMetadata is presentation metadata for one session. It is not lifecycle state, and every field is best-effort.

type SessionOptions added in v0.2.0

type SessionOptions struct {
	// Name identifies the session and its output storage. It must be a single,
	// non-empty path component.
	Name string
	// Process describes the child process. Its zero value starts the user's
	// shell without evaluating a command string.
	Process ProcessSpec
	// Size is the initial grid size. A zero value uses the hub's default.
	Size Size
	// VTScrollbackMaxBytes overrides the Hub default for this session. Zero
	// inherits the Hub setting.
	VTScrollbackMaxBytes uint64
}

SessionOptions configures one session.

type Size added in v0.2.0

type Size struct {
	// Columns is the number of character cells per line.
	Columns int
	// Rows is the number of lines in the grid.
	Rows int
}

Size is a terminal grid size in cells.

type Status added in v0.3.0

type Status struct {
	// Alive is true while the child process is running.
	Alive bool `json:"alive"`
	// Exit describes the termination when Alive is false.
	Exit *ExitError `json:"exit,omitempty"`
}

Status describes whether a session is running and, when stopped, why.

type VersionInfo added in v0.6.3

type VersionInfo struct {
	// ProtocolVersion is the server's RPC protocol identifier.
	ProtocolVersion string
	// TagVersion is the server module's release tag, or empty for a
	// development build or local replacement.
	TagVersion string
	// Capabilities contains stable feature names understood by the server.
	// Clients must ignore names they do not recognize.
	Capabilities []string
	// Limits contains the server's enforced wire framing limits.
	Limits ProtocolLimits
	// MaxClientConnections is the maximum number of active client sockets the
	// daemon accepts. Long-lived streams count against this limit.
	MaxClientConnections int
}

VersionInfo describes the protocol and release tag reported by a v1 server.

Directories

Path Synopsis
cmd
ghostline command

Jump to

Keyboard shortcuts

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