Skip to content
XTXP

Addressing and discovery

draft
xtxp://{host}/{path}@{ref}

Used by native desktop clients, terminal interfaces and automated nodes exchanging XTXP streams. Whether it names the WebSocket binding directly or resolves to it through a muppet disco is still open.

https://{host}/{path}@{ref}

A muppet that hosts a session serves it to ordinary browsers itself, through its server-side muppet. This is all the MVP needs.

https://xtxp.link/{host}/{path}@{ref}

sketch · a future milestone

xtxp.link doesn’t host or serve documents. It is a muppet disco: it finds the muppets hosting the sessions behind an address, and only those the asking key may see, then hands the client on to them. Anyone can run a disco; xtxp.link is one.

{host} can be a muppet hosting native documents, or an ordinary website. There is no separate proxy mode. A website is simply a document whose upstream peer is the origin site, and everything else about it is branching.

The @{ref} suffix says which version you mean:

Ref Meaning
@origin The website’s authoritative version, as served by its origin server: the upstream peer’s session. Websites only, read-only.
@main The shared collaborative session. The default for native documents.
@{session} A named session on this host: a proposed edit, an undo fork, someone’s overlay. A session on another host is addressed through that host: xtxp.link/{other-host}/{path}@{session}.
@{snapshot} An exact point in history: a short hash of a snapshot. Syntax open.

Examples:

xtxp.link/en.wikipedia.org/wiki/Browser@origin Wikipedia's page, unedited
xtxp.link/en.wikipedia.org/wiki/Browser@notes an edited session of it
xtxp.link/my-laptop.lan/drawing@sketch a fork, living on a laptop
xtxp.link/xtxp.example.org/drawing@main a native document
xtxp.link/medium.com/@campey/post@origin "@campey" stays in the path

Prior art: the @ reads like npm’s package@version, and origin follows both git’s authoritative remote and HTTP’s origin server (prior art).

  1. The last @ starts the ref. Every other @ belongs to the path, so origin paths like Medium’s /@user need no escaping.
  2. Generated links always carry a ref. That includes every link it rewrites inside a page, so the last @ is never ambiguous.
  3. Bare URLs (typed or pasted from elsewhere):
    • with no @ at all, redirect to the default ref (@origin for websites, @main for native documents)
    • with an @ whose text doesn’t resolve to a ref, as in medium.com/@campey, treat it as part of the path and redirect to …/@campey@origin
  4. A first path segment without a dot is a keyword, reserved for the disco’s own pages (such as /d/ for diagrams). Hostnames always contain a dot, so the two never collide.

@origin means unedited. Any other ref means a session that may contain other people’s edits, and the page frame says so, with links to @origin and to the original site. The ref travels with every shared link, so a reader can tell what they’re looking at before they click. That supports the overlay safety requirements.

Sequence: a client asks the xtxp.link disco for an address, and the disco finds the muppet hosting the session and redirects there; with a WebSocket upgrade the host opens the XTXP stream, with Accept text/html it returns rendered HTML with edit forms and a link to the native client. The ref after the last @ picks @origin, @main, another session or a snapshot.Sequence: a client asks the xtxp.link disco for an address, and the disco finds the muppet hosting the session and redirects there; with a WebSocket upgrade the host opens the XTXP stream, with Accept text/html it returns rendered HTML with edit forms and a link to the native client. The ref after the last @ picks @origin, @main, another session or a snapshot.
Select the diagram to view it full screenPlantUML source
Request Response
WebSocket upgrade The live XTXP stream of Yrs updates, for muppets and automated nodes (The Wire).
Accept: text/html The current state rendered as HTML, with forms that POST edits. Served by the host’s server-side muppet.
A person who wants their native client A plain link to xtxp://{host}/{path}@{ref}.
Sequence: the browser POSTs a form to the hosting instance, which hands it to its server muppet, which makes CRDT ops and sends them to the host; the host fans them out to Inkscape; the browser is redirected and GETs the new HTML; later meta-refresh polls get 304 Not Modified unless someone edited.Sequence: the browser POSTs a form to the hosting instance, which hands it to its server muppet, which makes CRDT ops and sends them to the host; the host fans them out to Inkscape; the browser is redirected and GETs the new HTML; later meta-refresh polls get 304 Not Modified unless someone edited.
Select the diagram to view it full screenPlantUML source

decided for M2 (decision 23)

  • At heart, every edit is a form POST. The page around it can be as rich as HTML and CSS allow.
  • The forms: select an element (a link per element), edit an attribute, recolour, move (x and y fields, or nudge buttons), delete, and add a basic shape.
  • Every POST carries the node ID it acts on and the snapshot hash the page was rendered from, so the host can tell when someone edited a stale page.
  • Every form carries a CSRF token.
to revisit

What a document may contain, scripts included, isn’t the protocol’s business. Hosts serving user documents still face real risks: scripts in user SVG, and pages that run on the host’s own origin. Sanitising, a strict CSP and a separate origin for user content are the usual answers, to revisit before hosting other people’s documents in earnest.

  • Snapshots: @{snapshot} names a document at an exact point in history, so a historical state can be shared exactly. How the snapshot hash is written in a URL is open.
  • Discovery: a muppet disco resolves an address to the muppet currently hosting the session.
  • Short links: short, low-density URLs that make easily scanned QR codes, for bridging paper and screens.
open
  • Patch documents can reuse application/xml-patch+xml (RFC 7351).
  • Whether the XTXP stream needs a registered media type at all, now that it travels over a WebSocket upgrade rather than HTTP content negotiation.