Skip to content
XTXP

The Wire

decided in shape, draft in detail.

The Wire half of XTXP defines how operations get from one node to another. It is a small sync protocol that doesn’t depend on any one transport, plus a few bindings that carry it.

Very little. Xmesh operations can be applied in any order, and applying one twice does no harm. So a transport only has to deliver operations eventually, at least once, in any order. It doesn’t need ordering guarantees, exactly-once delivery or a durable message queue.

Recovering from a dropped connection is part of XTXP itself, not something borrowed from the transport:

Sequence: a peer edits offline, reconnects, sends its state vector, the host replies with exactly the operations the peer is missing and the peer uploads the operations the host is missing; both end at the same state vector.Sequence: a peer edits offline, reconnects, sends its state vector, the host replies with exactly the operations the peer is missing and the peer uploads the operations the host is missing; both end at the same state vector.
Select the diagram to view it full screenPlantUML source
draft

The sync messages follow the Yjs sync protocol. XTXP adds its own messages for identity and access alongside them.

draft · enough to build M1 on; needs further work (decision 21)

  • Peers exchange Yrs binary updates, in Yrs’s v1 encoding, inside XTXP frames. v1 is Yrs’s default, and what the Yjs sync protocol and y-sweet speak. The more compact v2 encoding is a later optimisation.
  • XML Patch never travels between peers. It’s how edits come in from a UI or go out to a tool (see The Math).
  • HTTP is for browsers. A client that wants the live stream asks for a WebSocket upgrade on the same address.
Message Purpose Stored?
hello / challenge / prove Authenticate a connection by public key. The proof signs the host, session and nonce under a domain tag. See Identity and access. No
bind Signed: “public key P owns client ID 7 on this session.” Yes
sync-1 “Here is my state vector.” No
sync-2 “Here are the operations you’re missing.” Yes, as operations
update New operations, as they happen. Yes
grant / revoke Signed changes to who may read or write a session. Yes
awareness Cursors, selections, presence, tied to the authenticated connection. Fire-and-forget, overwritten, never archived. No

Cursors and presence travel separately from operations. A 60 Hz cursor stream should never be archived, wrapped in anything heavy, or replayed to a late joiner.

Binding Used by Notes
WebSocket, binary frames Wasm muppets in the browser, the Inkscape fork, CLI peers The primary binding, chosen for raw speed. Hosts serve it directly.
HTTP forms and redirects Browsers with no JavaScript, through a server-side muppet See Addressing.
XMPP, through a bridge Mobile and chat clients such as Conversations, and federation A bridge in front of a host, not the core transport.
Sequence: two muppets connected to a host over WebSocket make concurrent edits; the host checks each client's grant, records the operations and fans them out; both peers integrate and reach the same state vector and hash.Sequence: two muppets connected to a host over WebSocket make concurrent edits; the host checks each client's grant, records the operations and fans them out; both peers integrate and reach the same state vector and hash.
Select the diagram to view it full screenPlantUML source

XMPP is good at things a browser tab isn’t: holding messages while a phone sleeps, waking it with a push, and fitting into chat apps people already use. The bridge keeps those strengths for the clients that need them:

  • XEP-0198 stream management and XEP-0313 message archive for flaky mobile links
  • XEP-0352 client state and XEP-0357 push, to save battery
  • chat interop: canvas comments mirrored as room messages, and replies turned back into comments (open question 7)
  • OMEMO end-to-end encryption for rooms that want it

Behind the bridge, everything is ordinary XTXP to a host. Inkboard’s old problem of XML getting damaged inside XMPP stanzas is now one bridge’s concern, not the core wire format’s (see prior art).

Every binding runs over TLS. A session can also be end-to-end encrypted, so that its host only sees ciphertext. The host can still admit operations, because client bindings and grants are metadata it can check. It can’t compact the session into a full state or render previews, though, so those move to the clients. A server-side muppet can still take part as one of the user’s own devices.

  • How XTXP’s own messages (identity, grants) are framed alongside the Yjs sync messages.
  • The wire format as a whole: decision 21 is a starting point to build on, not the final word.
  • Whether xtxp:// names the WebSocket binding directly, or resolves to it through a muppet disco.
  • How the XMPP bridge is built: for example as a component plugged into an existing XMPP server, speaking XTXP to a host.