libghostty
Loading...
Searching...
No Matches
Terminal Snapshot

Detailed Description

Encode and restore the complete state of a terminal via a binary format.

A snapshot is an ordered, authenticated record stream. Its READY checkpoint contains enough state to render and resume the terminal, including any unfinished VT parser input. Older scrollback pages follow READY and the FINISH checkpoint authenticates the complete snapshot.

End-of-file before an operation's required READY or FINISH checkpoint is malformed, truncated snapshot data and returns GHOSTTY_INVALID_VALUE. GHOSTTY_IO_ERROR is reserved for a reader callback that returns false.

Examples

The complete working example is available in example/c-vt-snapshot.

Encode a terminal and its unfinished VT continuation

GhosttyTerminal source = NULL;
// A wide, shallow screen fills backing pages quickly enough to leave older
// PAGE records after READY for the incremental decoder to demonstrate.
result = ghostty_terminal_new(NULL, &source, 215, 2);
assert(result == GHOSTTY_SUCCESS);
// Snapshot encoding requires continuation tracking to be enabled before
// feeding input. The limit bounds unfinished VT sequence retention.
const size_t continuation_limit = 1024;
source,
&continuation_limit);
assert(result == GHOSTTY_SUCCESS);
// Keep enough scrollback to demonstrate incremental history restoration.
assert(result == GHOSTTY_SUCCESS);
const char *line = "snapshot history line\r\n";
for (size_t i = 0; i < 1000; i++) {
source, (const uint8_t *)line, strlen(line));
}
// Leave an SGR sequence unfinished so its continuation is snapshotted too.
const char *unfinished = "\x1b[31";
source, (const uint8_t *)unfinished, strlen(unfinished));
uint8_t *snapshot = NULL;
size_t snapshot_len = 0;
source, NULL, &snapshot, &snapshot_len);
assert(result == GHOSTTY_SUCCESS);
printf("encoded %zu snapshot bytes\n", snapshot_len);

Restore a complete snapshot in one call

GhosttySnapshotDecoder full_decoder = NULL;
NULL, &full_decoder, snapshot, snapshot_len);
assert(result == GHOSTTY_SUCCESS);
GhosttyTerminal full_terminal = NULL;
result = ghostty_snapshot_decoder_decode(full_decoder, &full_terminal);
assert(result == GHOSTTY_SUCCESS);
ghostty_terminal_free(full_terminal);

Adapt a byte source to GhosttyReader

typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} BufferReader;
// GhosttyReader callbacks are synchronous. A successful zero-byte read is
// permanent EOF; returning false would report an I/O error.
static bool buffer_read(void *userdata,
uint8_t *buffer,
size_t capacity,
size_t *out_read) {
BufferReader *reader = userdata;
size_t remaining = reader->len - reader->offset;
size_t count = remaining < capacity ? remaining : capacity;
// Deliberately return short reads to demonstrate that the decoder retries.
if (count > 64) count = 64;
memcpy(buffer, reader->data + reader->offset, count);
reader->offset += count;
*out_read = count;
return true;
}

Restore READY first, then incrementally prepend history

BufferReader reader_state = {
.data = snapshot,
.len = snapshot_len,
.offset = 0,
};
GhosttyReader reader = {
.read = buffer_read,
.userdata = &reader_state,
};
GhosttySnapshotDecoder incremental_decoder = NULL;
NULL, &incremental_decoder, reader);
assert(result == GHOSTTY_SUCCESS);
// READY authenticates and returns a renderable terminal before old history.
GhosttyTerminal incremental_terminal = NULL;
incremental_decoder, &incremental_terminal);
assert(result == GHOSTTY_SUCCESS);
uint64_t history_rows = 0;
incremental_decoder,
&history_rows);
assert(result == GHOSTTY_SUCCESS);
printf("snapshot advertises %llu primary history rows\n",
(unsigned long long)history_rows);
size_t page_count = 0;
while ((result = ghostty_snapshot_decoder_next(incremental_decoder)) ==
size_t rows = 0;
uint32_t remaining = 0;
const GhosttySnapshotDecoderData keys[] = {
};
void *values[] = {&screen, &rows, &remaining};
size_t written = 0;
incremental_decoder,
sizeof(keys) / sizeof(keys[0]),
keys,
values,
&written);
assert(result == GHOSTTY_SUCCESS);
assert(written == sizeof(keys) / sizeof(keys[0]));
printf("restored %zu rows to screen %d (%u pages remain)\n",
rows, (int)screen, remaining);
page_count++;
}
// NO_VALUE means FINISH authenticated successfully and is idempotent.
assert(result == GHOSTTY_NO_VALUE);
assert(page_count > 0);
assert(ghostty_snapshot_decoder_next(incremental_decoder) ==
ghostty_snapshot_decoder_free(incremental_decoder);
ghostty_terminal_free(incremental_terminal);

Format

Every integer is unsigned and little-endian. The stream begins with this fixed ten-byte envelope:

byte 0 8 10
+---------------+--------+
| "GHOSTSNP" | version|
| 8-byte magic | u16 |
+---------------+--------+

The envelope is followed by independently checksummed records. A record's CRC32C covers its encoded tag and payload length followed by its payload; it does not cover the CRC field itself.

byte 0 2 6 10 10 + payload_len
+-------+-------------+-----------+----------------+
| tag | payload_len | CRC32C | payload |
| u16 | u32 | u32 | payload_len B |
+-------+-------------+-----------+----------------+
\____________________/ \______________/
CRC prefix CRC suffix

Record groups occur in this strict order. SCREEN and HISTORY groups contain one entry for each screen declared by TERMINAL. Each manifest is followed by the number of PAGE records it declares. Active SCREEN pages make the terminal renderable; HISTORY pages are older scrollback ordered newest to oldest so an incremental decoder can prepend them as they arrive.

+---------------- TERMINAL ----------------+
| terminal-wide state and screen count |
+----------------- SCREEN -----------------+ repeated per screen
| active-screen manifest |
+------------------ PAGE ------------------+ repeated per manifest
| active screen rows |
+------------- CONTINUATION ---------------+
| unfinished VT/UTF-8 input, or ground |
+------------------ READY -----------------+
| BLAKE3-256 of every preceding byte | ready() returns here
+----------------- HISTORY ----------------+ repeated per screen
| scrollback manifest |
+------------------ PAGE ------------------+ next() consumes one page
| older screen rows |
+------------------ FINISH ----------------+
| BLAKE3-256 of every preceding byte | next() returns NO_VALUE
+------------------------------------------+
| trailing transport bytes (not consumed) |
+------------------------------------------+

READY authenticates the renderable prefix through CONTINUATION. FINISH authenticates READY and every history record as well as the earlier prefix. Thus record CRC32C detects local corruption while the BLAKE3 checkpoints also bind the ordering and completeness of the record stream.

Snapshot format version 1 is a work in progress and does not yet carry a binary-compatibility guarantee.

See also
Snapshot format and Zig codec documentation

Typedefs

typedef struct GhosttySnapshotDecoderImpl * GhosttySnapshotDecoder

Enumerations

enum  GhosttySnapshotDecoderOption { GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_CONTINUATION_BYTES = 0 , GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE }
enum  GhosttySnapshotDecoderData {
  GHOSTTY_SNAPSHOT_DECODER_DATA_INVALID = 0 , GHOSTTY_SNAPSHOT_DECODER_DATA_MAX_CONTINUATION_BYTES = 1 , GHOSTTY_SNAPSHOT_DECODER_DATA_SOURCE_OFFSET = 2 , GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY = 3 ,
  GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_ALTERNATE = 4 , GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_SCREEN = 5 , GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS = 6 , GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_REMAINING = 7 ,
  GHOSTTY_SNAPSHOT_DECODER_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE
}

Functions

GHOSTTY_API GhosttyResult ghostty_snapshot_encode (GhosttyTerminal terminal, GhosttyWriter writer)
GHOSTTY_API GhosttyResult ghostty_snapshot_encode_buf (GhosttyTerminal terminal, uint8_t *buf, size_t buf_len, size_t *out_written)
GHOSTTY_API GhosttyResult ghostty_snapshot_encode_alloc (GhosttyTerminal terminal, const GhosttyAllocator *allocator, uint8_t **out_ptr, size_t *out_len)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new (const GhosttyAllocator *allocator, GhosttySnapshotDecoder *decoder, GhosttyReader reader)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new_buf (const GhosttyAllocator *allocator, GhosttySnapshotDecoder *decoder, const uint8_t *ptr, size_t len)
GHOSTTY_API void ghostty_snapshot_decoder_free (GhosttySnapshotDecoder decoder)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_set (GhosttySnapshotDecoder decoder, GhosttySnapshotDecoderOption option, const void *value)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_ready (GhosttySnapshotDecoder decoder, GhosttyTerminal *terminal)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_next (GhosttySnapshotDecoder decoder)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_decode (GhosttySnapshotDecoder decoder, GhosttyTerminal *terminal)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get (GhosttySnapshotDecoder decoder, GhosttySnapshotDecoderData data, void *out)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get_multi (GhosttySnapshotDecoder decoder, size_t count, const GhosttySnapshotDecoderData *keys, void **values, size_t *out_written)

Typedef Documentation

◆ GhosttySnapshotDecoder

typedef struct GhosttySnapshotDecoderImpl* GhosttySnapshotDecoder

Opaque handle to an incremental terminal snapshot decoder.

Definition at line 106 of file types.h.

Enumeration Type Documentation

◆ GhosttySnapshotDecoderData

Queryable snapshot decoder data.

Each variant documents the output pointer type expected by ghostty_snapshot_decoder_get().

Enumerator
GHOSTTY_SNAPSHOT_DECODER_DATA_INVALID 

Invalid data type. Never results in data extraction.

GHOSTTY_SNAPSHOT_DECODER_DATA_MAX_CONTINUATION_BYTES 

Current maximum accepted continuation size.

This value is available in every non-failed decoder state.

Output type: size_t *

GHOSTTY_SNAPSHOT_DECODER_DATA_SOURCE_OFFSET 

Number of snapshot source bytes consumed so far.

At FINISH this identifies the first byte after the snapshot. Trailing bytes are not consumed. This value is unavailable after a decoding error, because the decoder can no longer guarantee its source position.

Output type: size_t *

GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY 

Advisory complete logical history extent for the primary screen.

The value counts rows before the active area, including any resident overlap carried before READY. It becomes available after READY validates.

Output type: uint64_t *

GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_ALTERNATE 

Advisory complete logical history extent for the alternate screen.

The value has the same semantics and lifetime as GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY. Querying it returns GHOSTTY_NO_VALUE when the snapshot does not declare an alternate screen.

Output type: uint64_t *

GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_SCREEN 

Screen associated with the most recently decoded history page.

This value is available only after ghostty_snapshot_decoder_next() returns GHOSTTY_SUCCESS. A later call to next replaces it or clears it when FINISH is reached or an error occurs.

Output type: GhosttyTerminalScreen *

GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS 

Rows prepended by the most recently decoded history page.

Zero means the page was consumed and authenticated but could not be applied to the live terminal.

Output type: size_t *

GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_REMAINING 

Page records remaining in the same screen's HISTORY sequence.

This is not a count of all pages remaining in the snapshot.

Output type: uint32_t *

Definition at line 150 of file snapshot.h.

◆ GhosttySnapshotDecoderOption

Configurable snapshot decoder options.

Options may only be changed before decoding starts. Calling ghostty_snapshot_decoder_set() after ghostty_snapshot_decoder_ready() or ghostty_snapshot_decoder_decode() returns GHOSTTY_INVALID_VALUE.

Enumerator
GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_CONTINUATION_BYTES 

Largest non-ground continuation the decoder will accept.

A value of zero accepts only snapshots whose VT parser is in the ground state. The decoder default matches the largest built-in APC protocol buffer limit, currently 65 MiB.

This is an input validation limit only. It does not configure continuation tracking on a terminal returned by the decoder.

Input type: size_t *

Definition at line 126 of file snapshot.h.

Function Documentation

◆ ghostty_snapshot_decoder_decode()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_decode ( GhosttySnapshotDecoder decoder,
GhosttyTerminal * terminal )

Decode and authenticate one complete snapshot.

This is the one-shot form of READY followed by all history pages through FINISH. It may only be called before decoding starts. Bytes following FINISH are left unread. On success terminal receives a caller-owned terminal with its persistent VT stream restored. Continuation tracking on the returned terminal is disabled and GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES returns zero. terminal is set to NULL on every error. A decoding, I/O, or allocation error after input consumption begins poisons the decoder, after which it must be freed. An invalid argument or lifecycle error detected before the operation consumes input does not poison it.

Parameters
decoderDecoder handle (must not be NULL)
[out]TerminalPointer to receive the terminal (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, or an error code on failure

◆ ghostty_snapshot_decoder_free()

GHOSTTY_API void ghostty_snapshot_decoder_free ( GhosttySnapshotDecoder decoder)

Free a snapshot decoder.

This does not release the caller's ownership of a terminal returned by ready or decode. Abandoning an incremental decode leaves that terminal usable with whatever history had already been restored.

Parameters
decoderDecoder to free (may be NULL)

◆ ghostty_snapshot_decoder_get()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get ( GhosttySnapshotDecoder decoder,
GhosttySnapshotDecoderData data,
void * out )

Get typed data from a snapshot decoder.

The output pointer must have the type documented by data. A phase-dependent value that is not currently available returns GHOSTTY_NO_VALUE.

Parameters
decoderDecoder handle (must not be NULL)
dataData kind to query
[out]outPointer to receive the value (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the requested data is unavailable, or another error code on failure

◆ ghostty_snapshot_decoder_get_multi()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get_multi ( GhosttySnapshotDecoder decoder,
size_t count,
const GhosttySnapshotDecoderData * keys,
void ** values,
size_t * out_written )

Get multiple snapshot decoder data fields in a single call.

Each keys element selects a data kind and the corresponding values element points to storage of the documented output type. Processing stops at the first error. On success out_written is set to count; on error it is set to the number of values written before the failing key. Invalid array arguments report zero values written.

Parameters
decoderDecoder handle (must not be NULL)
countNumber of key/value pairs
keysArray of data kinds to query
valuesArray of output pointers corresponding to keys
[out]out_writtenNumber of successfully written values (may be NULL)
Returns
GHOSTTY_SUCCESS if every query succeeds, or the first error

◆ ghostty_snapshot_decoder_new()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new ( const GhosttyAllocator * allocator,
GhosttySnapshotDecoder * decoder,
GhosttyReader reader )

Create a snapshot decoder that reads from a caller-provided reader.

The decoder stores a copy of reader. Its read callback must not be NULL, and both the callback and its caller-owned context must remain valid until FINISH is reached or the decoder is freed. Reads are synchronous and occur only during ready, next, or decode calls. A zero-byte successful read is permanent end-of-file, not temporary starvation; nonblocking sources must wait outside the decoder or block in their callback. The read callback must not call APIs, including ghostty_snapshot_decoder_free(), on the decoder that owns it. Returning false reports GHOSTTY_IO_ERROR; returning true with zero bytes before a required checkpoint reports truncated snapshot data as GHOSTTY_INVALID_VALUE.

Parameters
Memory ManagementAllocator for decoder and decoded terminal state, or NULL for the default allocator
decoderPointer to receive the decoder handle (must not be NULL)
readerSnapshot source reader
Returns
GHOSTTY_SUCCESS on success, or an error code on failure

◆ ghostty_snapshot_decoder_new_buf()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new_buf ( const GhosttyAllocator * allocator,
GhosttySnapshotDecoder * decoder,
const uint8_t * ptr,
size_t len )

Create a snapshot decoder over a borrowed byte buffer.

The bytes are not copied. ptr must remain valid and immutable until FINISH is reached or the decoder is freed. Bytes after FINISH are not consumed; query GHOSTTY_SNAPSHOT_DECODER_DATA_SOURCE_OFFSET to locate them.

Parameters
Memory ManagementAllocator for decoder and decoded terminal state, or NULL for the default allocator
decoderPointer to receive the decoder handle (must not be NULL)
ptrSnapshot source bytes
lenNumber of source bytes
Returns
GHOSTTY_SUCCESS on success, or an error code on failure

◆ ghostty_snapshot_decoder_next()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_next ( GhosttySnapshotDecoder decoder)

Decode one history page into the terminal returned by READY.

Each GHOSTTY_SUCCESS consumes and authenticates one PAGE record. Query the GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_* values before calling next again. GHOSTTY_NO_VALUE means FINISH was validated; repeated calls after FINISH also return GHOSTTY_NO_VALUE.

The terminal may be rendered, resized, and fed live PTY input between calls. If a history page can no longer be applied safely, it is still consumed and authenticated and progress reports zero rows. The decoder applies history to the caller-owned terminal produced by its READY operation.

A decoding error invalidates the decoder's source position. The terminal remains caller-owned and usable with its already-restored history, but only ghostty_snapshot_decoder_free() may subsequently be called on the decoder.

Parameters
decoderDecoder handle (must not be NULL)
Returns
GHOSTTY_SUCCESS for one page, GHOSTTY_NO_VALUE after FINISH, or an error code on failure

◆ ghostty_snapshot_decoder_ready()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_ready ( GhosttySnapshotDecoder decoder,
GhosttyTerminal * terminal )

Decode and authenticate the renderable snapshot prefix through READY.

On success, terminal receives a caller-owned terminal with its persistent VT stream already restored from the snapshot continuation. The terminal is immediately usable for rendering and live input. Older scrollback remains to be restored with ghostty_snapshot_decoder_next().

The restored parser state may be unfinished, but terminal continuation tracking is disabled; GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES returns zero. The decoder's continuation option is an input limit, not terminal runtime policy.

The caller must keep the returned terminal alive until FINISH validates or the decoder is freed. The decoder borrows this terminal handle while it restores history; ghostty_snapshot_decoder_next() uses it automatically.

This operation may only be called once and only before decoding starts. terminal is set to NULL on every error. A decoding, I/O, or allocation error after input consumption begins poisons the decoder, after which it must be freed. An invalid argument or lifecycle error detected before the operation consumes input does not poison it.

Parameters
decoderDecoder handle (must not be NULL)
[out]TerminalPointer to receive the terminal (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, or an error code on failure

◆ ghostty_snapshot_decoder_set()

GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_set ( GhosttySnapshotDecoder decoder,
GhosttySnapshotDecoderOption option,
const void * value )

Set a snapshot decoder option.

The value pointer must have the type documented by option. Options may only be changed before decoding starts.

Parameters
decoderDecoder handle (must not be NULL)
optionOption to change
valuePointer to the option value (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if decoding has started or an argument is invalid, or another error code on failure

◆ ghostty_snapshot_encode()

GHOSTTY_API GhosttyResult ghostty_snapshot_encode ( GhosttyTerminal terminal,
GhosttyWriter writer )

Encode a complete terminal snapshot to a writer.

The terminal's persistent VT stream supplies the continuation bytes needed to reconstruct unfinished parser state. The caller must prevent concurrent writes or other terminal mutation for the duration of this call. The writer callback must not call terminal APIs with the same terminal handle. A terminal can be encoded with tracking disabled when its VT parser and UTF-8 decoder are both at ground. If either is unfinished, tracking must have been enabled before the input that produced that state was written; otherwise this returns GHOSTTY_INVALID_VALUE.

Encoding begins at the writer's current position. If an error occurs, the writer may contain a partial snapshot without a valid FINISH checkpoint. Calls to the writer are synchronous; this function does not flush or make the caller's destination durable.

Parameters
TerminalTerminal to encode (must not be NULL)
writerDestination writer whose write callback must not be NULL
Returns
GHOSTTY_SUCCESS on success, GHOSTTY_IO_ERROR if the writer rejects output, GHOSTTY_LIMIT_EXCEEDED if output accounting overflows, or another error code on failure

◆ ghostty_snapshot_encode_alloc()

GHOSTTY_API GhosttyResult ghostty_snapshot_encode_alloc ( GhosttyTerminal terminal,
const GhosttyAllocator * allocator,
uint8_t ** out_ptr,
size_t * out_len )

Encode a complete terminal snapshot to an allocated buffer.

The returned buffer is allocated with allocator, or the default allocator when allocator is NULL. The caller must release it with ghostty_free(), passing the same allocator used here.

A terminal can be encoded with tracking disabled when its VT parser and UTF-8 decoder are both at ground. If either is unfinished, tracking must have been enabled before the input that produced that state was written; otherwise this returns GHOSTTY_INVALID_VALUE.

Parameters
TerminalTerminal to encode (must not be NULL)
Memory ManagementAllocator for the output, or NULL for the default allocator
[out]out_ptrAllocated snapshot bytes (must not be NULL)
[out]out_lenNumber of allocated snapshot bytes (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, or an error code on failure

◆ ghostty_snapshot_encode_buf()

GHOSTTY_API GhosttyResult ghostty_snapshot_encode_buf ( GhosttyTerminal terminal,
uint8_t * buf,
size_t buf_len,
size_t * out_written )

Encode a complete terminal snapshot to a caller-provided buffer.

Pass NULL for buf with buf_len zero to query the required size. If the buffer is too small, this returns GHOSTTY_OUT_OF_SPACE and stores the required capacity in out_written. A non-NULL undersized buffer may contain a partial snapshot prefix. On success, out_written receives the number of bytes encoded.

A terminal can be encoded with tracking disabled when its VT parser and UTF-8 decoder are both at ground. If either is unfinished, tracking must have been enabled before the input that produced that state was written; otherwise this returns GHOSTTY_INVALID_VALUE.

Parameters
TerminalTerminal to encode (must not be NULL)
bufDestination buffer, or NULL when buf_len is zero
buf_lenDestination buffer capacity in bytes
[out]out_writtenBytes written, or required capacity on GHOSTTY_OUT_OF_SPACE (must not be NULL)
Returns
GHOSTTY_SUCCESS on success, or an error code on failure