Report: M1-y-sweet
Written 19 October 2026, when M1-y-sweet was finished.
M1-y-sweet is the first working XTXP code. Two people, each in a terminal, edit the same SVG drawing through a stock, unmodified y-sweet v0.9.1, and each sees the other’s changes arrive live. It is the first half of the y-sweet spike (decision 25), and it is command line only. Browsers and Inkscape come with M2-y-sweet.

Two muppets, one blue and one green, on the same drawing through one y-sweet, whose log runs behind them. Both show the same snapshot and document hashes in their headers, and the same value for width.
What was built
Section titled “What was built”All of it is in xtxp-rs, and scripts/try-it.sh runs it on a Mac or Linux machine.
| Piece | What it does |
|---|---|
| Xmesh | The CRDT engine: XML documents on Yrs 0.28’s XML types, canonical form v0, snapshot hashes, and node IDs that follow a node while its path shifts. |
xtxp-wire |
The peer side of the Yjs sync protocol as y-sweet speaks it, as a pure state machine. |
xtxp-muppet |
A local muppet: a terminal UI for browsing and editing a live document, and one-shot commands (init, edit, show, replay) for scripts and tests. It reconnects with backoff and a fresh client ID each time. |
| Chaos proxy | Sits between muppets and y-sweet, and delays and duplicates frames and drops connections. |
| Vectors, fuzzer and probes | 14 hand-written test vectors, a fuzzer that checks 500 seeds, and a probe that replays every vector on y-sweet’s older Yrs 0.19.2. |
In the terminal UI, a person selects a node and changes it: they edit attributes and text, add, remove or nudge numeric attributes with the arrow keys, insert children, and delete or move nodes. Edits are aimed at the node itself, so they still land where intended when someone else’s edit has moved it.
What it showed
Section titled “What it showed”- Live editing works through a stock y-sweet. End-to-end tests run three muppets at once, as separate processes, through the chaos proxy against a real y-sweet. They converge, and a late joiner loads the whole document in one update. Edits made while disconnected survive reconnects with fresh client IDs. A y-sweet restart loses nothing.
- Real hands find what tests don’t. A first try in two terminals worked, and it raised small questions that turned into changes the same day: how to add an attribute or a node, quitting with Ctrl-C, and nudging numbers with the arrow keys.
- y-sweet’s acknowledgement only means “received”. y-sweet echoes a message to say it has an update, but an update it can’t integrate yet is echoed like any other. Two of the bugs below were invisible to every peer. The tests caught them only by comparing what a late joiner gets.
Findings
Section titled “Findings”Testing against a real y-sweet found four problems. Each has a test that reproduces it, and the upstream reports are tracked in xtxp-link/xtxp-rs#4.
- A use-after-free in Yrs 0.28 (upstream). An update that lists the same blocks twice, applied to a document that integrated past a gap, makes Yrs free memory it still uses, and the next read crashes. It brought the muppet down in about 1 run in 25. Xmesh now merges each incoming update on its own before applying it, which drops the repeats. This is the most important report, because it is memory unsafety reached through Yrs’s safe API.
- y-sweet sends updates that list blocks twice (upstream). y-sweet’s Yrs 0.19.2 produced exactly the repeated update that sets off the first finding.
- Client IDs above 32 bits are mangled (upstream). Yrs 0.19.2 reads client IDs as 32-bit, so a larger ID becomes a different client inside y-sweet. Xmesh now uses 32-bit client IDs, as Yjs does.
- Lost edits from before the first sync (ours, fixed). An update applied before what it builds on had arrived was left out when the muppet copied its document on reconnect. y-sweet held only the starting drawing, while the muppet reported success.
Part 1 had already found two Yrs 0.28 bugs and that Yrs 0.19.2 loses a delete arriving before its insert. They are listed in the part 1 design. The part 2 design has these four in detail.
What y-sweet can’t do
Section titled “What y-sweet can’t do”These are recorded as gaps, not built:
- Admission by session, key grants and the signed handshake. y-sweet admits per connection, with bearer tokens, and starts syncing before any handshake.
- Auth of any kind in this spike. It runs with none, so it is for one machine or a trusted network. Token auth is the next step.
- Knowing an update was integrated. Its acknowledgement can’t tell “integrated” from “stuck waiting”, as above.
- A current Yrs. Its Yrs 0.19.2 is behind fixes that XTXP relies on: three of the findings above come from that version gap.
What’s next
Section titled “What’s next”- M2-y-sweet: someone watching or editing the same drawing from a browser, which brings in y-sweet’s token auth.
- Then the decision this spike exists for: whether the full M1 and M2 keep y-sweet as the host, or build XTXP’s own host borrowing from it. These findings, especially the version gap and the acknowledgement, are the evidence.
To try it yourself, follow “Try it” in the xtxp-rs README.