How it works
The handshake, the four channels, and the wire that carries a file.
Two browsers cannot call each other
They have no address anybody can dial. So before any byte moves, they must agree on what codecs they support, what network paths exist between them, and what keys to use. That is the SDP offer and answer, plus ICE candidates, and it is about 2 KB.
Something has to carry those 2 KB. Sparsh has three ways and they are all the same interface:
| Transport | When | Rendezvous |
|---|---|---|
| Nostr relays | the default | eight public relays, ephemeral events, nothing stored |
| WebSocket | a signalling server is configured | your own box, and it adds trickle ICE, TURN credentials and device discovery |
| Offline QR | no internet at all | two QR scans |
After the handshake the relays are irrelevant. The session survives every one of them going down.
Four data channels
| Label | Carries |
|---|---|
file-transfer | binary chunks, size negotiated per connection |
clipboard-sync | text |
control | file headers, progress, cancels, the round-trip probe |
mcp | MCP itself, opened on demand |
All four are DTLS encrypted. There is no point at which the bytes are in the clear in transit.
The fourth opens after the connection is up, in band, with no renegotiation. It carries newline-delimited JSON-RPC, which is exactly the framing a local MCP server speaks over a pipe, so both daemons are bridges and neither end knows Sparsh is in the middle.
The wire
Small files take a single ordered channel. Files over 8 MiB take the striped wire: several channels at once, each frame carrying its transfer id, its index and a SHA-256 of its payload, with a have-bitmap on the receiver so an interrupted transfer resumes rather than restarts.
A frame that does not match all three is dropped, not written.
Latency is the number
| What | Measured |
|---|---|
| round trip on a warm channel, median of 30 | 0.21 ms |
| the same, p95 | 0.54 ms |
| cold connect, process start to first byte | 0.59 s |
| warm first byte, second file on the same session | 0.7 ms |
Cold start is roughly seven hundred times the warm one. Everything an agent waits
for is in the setup, not the transfer, which is the entire argument for
sparsh serve and why a resident process was built before any faster wire.
The rule that follows: a change that raises throughput while raising the round trip is a regression here, and it is rejected on that basis.
See Performance for the instruments.