Experimental mirror wire contract
All integers in binary packets are unsigned big-endian. A packet is length:u32,
then kind:u8, then payload. Length includes kind, excludes the four-byte prefix,
and must be 1…8,388,608. Unknown kinds, malformed UTF-8, missing required fields and
invalid payloads close the connection. This revision has no compatibility negotiation.
| Kind | Payload |
|---|---|
| 0 | UTF-8 JSON control message |
| 1 | subscription UUID:16 raw bytes, sequence:u64, columns:u32, rows:u32, raw VT bytes |
| 2 | subscription UUID:16 raw bytes, sequence:u64, columns:u32, rows:u32, truncated:u8, UTF-8 text |
Sequence starts at 1. VT grid dimensions are 1…1000. Text dimensions allow zero for an unspecified test/source grid; production Ghostty sources supply actual sizes. Truncated is exactly 0 or 1. Empty text is a replacement. JSON frame/textFrame cases are rejected: frames must use their binary encoding. No Base64 frame content is sent.
Control JSON follows Swift Codable’s associated-value enum shape: no-payload list
is {"list":{}}; a subscribe is {"subscribe":{"_0":{"paneID":"UUID", "representation":"text-v1","intent":"ifFree"}}}. The enum and payload structs in
MirrorProtocol.swift define required fields. Both repositories verify identical
fixed-byte vectors, independent of encode/decode round-trip tests.
Controls: challenge, authenticate, pair, paired, authenticated; list, panes; subscribe, subscribed, acknowledge, input, refresh; history, historyPage; command, commandResult, commandReceipt; failure, ended, ping, pong. There is no private Agent-state or private submission control. Command envelopes preserve the public CLI JSON and UUID request ID. See the feature documentation for command scope.
The launch-profile capability permits Profile-backed background tab creation.
launch-shell additionally permits a background create tab with no launch
field. Neither capability permits arbitrary initial Shell input. Mac clients gate
Shell creation on launch-shell; existing Profile requests remain unchanged.
The remote list command result adds optional data.worktrees, using the same
worktree fields as data.items[].worktree, to include known worktrees without
terminal panes. Clients without that field can still use worktrees from items.
Authentication and lifecycle#
TLS uses ECDHE-PSK with ChaCha20-Poly1305. Enrollment uses identity pair and the
normalized temporary code. Runtime connections use the device UUID identity and
32 random secret bytes. No plain-PSK legacy suite is enabled.
TLS PSK membership alone is not treated as a device identity. Host sends its stable
hostID and a fresh 32-byte nonce. Device proves its secret using HMAC-SHA256 over
UTF-8 device:HOST_UUID:DEVICE_UUID: followed by nonce bytes. Enrollment proves the
code over pair:HOST_UUID:DEVICE_NAME: plus nonce. UUID strings use the Foundation
uppercase canonical spelling. Proofs are Base64 Data fields in control JSON.
Different purpose, Host, identity or nonce cannot reuse a proof.
Before proof validation only authentication/enrollment and connection heartbeats are accepted. Enrollment persists Host-generated device ID/key before replying; the window is consumed before another peer can enroll. Client saves before closing the enrollment connection and reconnecting with its device credential. Host names are display metadata, never identity. Authentication has a five-second deadline. Failure accounting and pending TLS pools are bounded. No secret is logged.
A subscribed connection has exactly one lease. Every input, ACK, history request, refresh and targeted command must match it. Takeover revokes the previous connection. Delayed commands recheck authorization immediately before delivery. A command result is not a terminal/frame acknowledgement. One unacknowledged frame per subscriber bounds backpressure; an ACK must exactly match its outstanding sequence.
History ID/offset refer to one frozen snapshot, with fixed total and capture time. Pages cannot overlap, skip or exceed the total byte budget. Heartbeats run every two seconds; eight seconds without inbound traffic ends an established connection.