# How it works
> The handshake, the four channels, and the wire that carries a file.
> Source: https://jvoltci.github.io/sparsh/docs/how-it-works/

## 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](/performance) for the instruments.
