Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 11:28
· 19 commits to main since this release
v0.7.0
df5c1f6

Ready for codes that work without the internet. A pairing code may now carry a
short suffix after its key: where the other device can be reached on the local network.
Nothing makes such a code yet. This release teaches every place that reads a code to
accept one, so that when a later version starts printing them, the version you have
still pairs with it, over the internet as before.

Send text, not just files. "Send text" on the first screen opens a box to type or
paste into, and pasting text anywhere on that screen opens the same box with it filled
in. The pasted text isn't sent until you press Send. You both check the same four symbols as for a
file, and the other device accepts it the same way. Instead of saving it, the
receiving page then shows the text with Copy and Save as file. A message can be
up to 64 KB. Nothing about the protocol changed: the text travels as a small
message.txt, so the command line and older versions receive it as that file.

The command line is now a single native binary. npx qrdrop, npm i -g qrdrop,
brew install stan-ely/tap/qrdrop and scoop install stan-ely/qrdrop all install the
same commands, flags and output as before, now built in Rust. It is one self-contained
file, about 11 MB on Windows, with nothing else to install: the Homebrew formula and the
Scoop manifest no longer pull in Node, and the binary for each platform is also attached
to the GitHub Release. Through npm, the binary arrives as a package matched to your
platform (qrdrop-linux-x64, qrdrop-darwin-arm64, and so on). On a platform with no
binary, or with optional dependencies turned off, the qrdrop command runs the
JavaScript version exactly as it always has. QRDROP_CLI=js picks the JavaScript one on
purpose. Both speak the same protocol, and transfers between them were tested in every
pairing. One visible difference: an unknown option is a usage error that exits 2, where
the JavaScript version printed a stack trace and exited 1.

For code using the package directly, nothing changes. qrdrop, qrdrop/web and
qrdrop/node export what they did.

Several times faster between two native devices on one network. When both ends run
the command line or the qrdrop app, and one of them can accept a connection without the
operating system asking its person first, the file itself now moves over a direct
connection on the local network instead of WebRTC. Measured between two copies of the
command line, 256 MB went at 33–42 MB/s against 17 MB/s. The file is encrypted exactly
as before, the connection is offered only after the four symbols are confirmed, and
nothing about pairing changes. Linux listens; Windows and macOS never do, because that is
where a firewall dialog would appear, but they still connect out to a device that
listens, such as a phone running the app or a Linux machine. Two Windows machines stay on
WebRTC. --no-lan turns it off. An older version on the other end ignores the offer, and
the transfer goes over WebRTC as it always has.

For code using the package directly: sendFile takes an optional onAccept, run after
the receiver accepts and before the first chunk; createReceiver takes an optional
onPeerLan; and a paired room has attachLan, sendOverLan and onClose. The
negotiation is createLanLink in src/transport/lan.js, and its onCarrying tells a
receiver when the first frame arrives over the direct connection.

Three to ten times faster across the internet between two command lines. When both
ends run the native command line, the file now moves over a direct QUIC connection
instead of WebRTC, wherever the two can reach each other directly (one network, or two
home routers a connection can be punched through, as WebRTC does), and the sender prints
Using a direct QUIC connection. Measured on an emulated link, 64 MB went at 51 MB/s
against 13 at 50 ms, and at 32 against 8.5 at 100 ms; with 0.1% loss behind a
100 Mbit/s bottleneck it was 9 against 0.9 at 50 ms. As with the local-network path, the
connection is offered only after the four symbols are confirmed, each end accepts only
the certificate the other named over the encrypted session, and the file is encrypted
inside it exactly as before. When the connection cannot be made the sender waits three
seconds at most and carries on over WebRTC; a browser, the JavaScript command line or the
app on the other end never answers the offer, and costs no wait at all. QRDROP_QUIC=0
turns it off, and so does --no-lan.

WebRTC between two native ends is faster too. The native command line now asks for
4 MB socket buffers, without which its bursts overran the operating system's default and
the connection spent its time recovering from loss: 25 MB/s against 8.4 on a fast link,
and 8.5 against 1.2 at 100 ms. A browser on the other end was already the limit, and
still is.

Dependencies. node-datachannel moves from 0.33.0 to 0.33.4. It was pinned because
0.33.0 was the last version to ship every platform's prebuilt binary, and 0.33.4 ships
them all again, adding Windows on ARM. The Trystero packages go to 0.25.4, a patch with
no API change. Both interop suites and the two-browser suite pass on the new versions.

One signalling relay replaced. wss://relay.mostr.pub now answers every connection
with a permanent redirect to relay.ditto.pub, and a WebSocket does not follow redirects,
so it had stopped carrying any pairing. The list names wss://relay.ditto.pub in its place,
which paired two peers on every attempt, and the app's content security policy follows.

Pairing works again after the network has been away for a while. If every signalling
relay was unreachable for about two minutes, the signalling library stopped trying them for
good, and every pairing after that timed out until the page was reloaded or the command
restarted. It happened when the operating system cut off a page in the background, which
Android does to a backgrounded browser or app within seconds, and a browser's "offline"
signal never fired for it. Relays that drop now wait and reconnect as soon as a pairing
needs them. Measured with every relay refused for two and a half minutes: the next pairing
timed out after 60 seconds before this change, and now pairs in about three.

The progress bar redraws at most ten times a second. It redrew the whole component after
every 16 KB frame, which on a phone was a measurable share of the time spent per frame; see
the app changelog for the numbers. The final 100% is always drawn.

Beam's loss numbers are measured, and one was wrong. Since 0.2.0 the docs, the README
and this changelog have said a numbered loop needs 8.3×N frames at 10% loss (10.7×N at
30%). That is what chunks picked at random cost, not an in-order loop like qrbeam's, which
measures 3.89×N at 10% and 7.02×N at 30%. The fountain code's own figures move slightly
too, to 1.58×N and 2.06×N as the mean of 20 seeds. node scripts/bench-beam.mjs now
produces every one of these numbers, including losses that arrive in runs and the decoder
overhead the systematic prefix costs (about 1.35 distinct frames per block, against 1.13
for a pure LT code). The codec itself is unchanged.