Skip to main content

Crate qualia_transport

Crate qualia_transport 

Source
Expand description

Transport channels for Qualia2FA.

This crate does one thing: it hands a byte string out and takes a byte string back. The protocol itself — whose payload arrives first, how conflicts are reported, how replays are blocked — lives in qualia2fa-core, and this crate knows nothing about it. The interface between the two is the core library’s Transport trait, and the dependency direction is “this crate ← core library”: any project that needs to “put bytes into a QR code” or “find a peer on the LAN” can use this crate as is, without accepting Qualia2FA’s vault format or account model.

§What is here

ModuleContents
qrQR capacity model, encoding, rendering and (optional) image decoding
envelopeBinary payload ↔ text a QR code can carry
frameAnimated QR: a payload that does not fit is split into several codes, which the scanning side reassembles
imageThe pixel form passed between rendering and scanning
lanLAN: multicast lobby (discovery), connection info and (optional) QUIC data plane

§Payloads are carried as text

QR byte mode can carry arbitrary bytes, but then only this crate could read the code back: platform cameras (Android ML Kit, iOS AVFoundation) return strings, a clipboard holds text, and a human copying a code reads text. Payloads therefore go into a text envelope and are encoded in the QR alphanumeric mode — about 10% of the capacity, in exchange for every scanner returning the same string. See the module documentation of envelope.

The frame index and the frame count are part of that envelope from the start (Q2FA1:2/3:), so carrying a payload in several codes needs no additional wire format. Splitting and reassembly live in frame, and the layer that turns a split payload into images lives in qr::QrProfile::encode_payload_frames.

§QR code and LAN

The two channels do not replace each other:

  • The QR code has very little bandwidth, is one-shot and requires both ends to be present, but it needs no network infrastructure and no permissions (Android 17’s ACCESS_LOCAL_NETWORK and the iOS multicast entitlement do not apply to it).
  • The LAN has ample bandwidth and can be reused, but it requires both ends in the same broadcast domain, and multicast is disabled on many corporate WiFi networks.

The combination is: which route carries discovery is decided on the spot, the payload itself takes whichever route has enough bandwidth. The core library’s SharePlan makes that decision; this crate provides both routes.

The two halves meet where the LAN route needs the QR channel: when multicast is unavailable, a connection record travels out of band in a QR code. That conversion lives in lan — lan::LanAddress::to_qr_symbols renders a record into codes through the same capacity and framing path as any other payload, lan::LanAddressScanner collects them again — so a caller does not assemble it from qr and frame on its own.

§Not implemented

The per-platform LAN permission flows (Android 17’s ACCESS_LOCAL_NETWORK, iOS NSLocalNetworkUsageDescription and the multicast entitlement) belong to the platform integration layer and are out of scope here. The LAN path’s “no internet” property currently goes as far as “no relay is built, no DNS/pkarr registration, HTTPS probing disabled”; packet-capture verification has not been done, so nothing beyond that may be claimed publicly (see the iroh-lan section of the README).

§Purity

qr, envelope, frame, image and lan::wire are pure logic: no file system, no network, no clock, no environment variables; every input is a parameter. Hence #![forbid(unsafe_code)] holds, and no input may cause a panic — the only permitted way to fail is returning an Error.

The same contract covers panics inside dependencies, at the cost of one convergence point: every call into the image decoder (rqrr) from qr::scan is wrapped in catch_unwind, which turns an upstream assertion failure into Error::QrDecoderPanicked. That is containment, not a fix (the fix belongs upstream), and it relies on unwinding: on a platform side built with panic = "abort", this layer stops working.

lan::lobby is the only module that touches I/O (a UDP socket and one background thread), and the decisions inside it are kept to a minimum: all the reasoning lives in lan::wire, so properties such as “three devices converge on one view” can be verified in a single process, without a network.

§Supported toolchains

The default build supports Rust 1.89 (the manifest’s rust-version). Enabling the default-off iroh-lan feature raises that floor to 1.91, the rust-version iroh 1.3.0 declares; Cargo has one rust-version per package and no per-feature equivalent, so that field states the default build’s floor and the feature’s floor is documented in the manifest and in the lan::transport module (which exists only with that feature).

Re-exports§

pub use error::Error;
pub use error::Result;

Modules§

envelope
The text envelope a QR code carries: a byte string becomes text that fits a QR code, and back.
error
Error types of this crate.
frame
Dynamic QR codes: a payload that does not fit into one code is split across several codes, which the scanning side collects and reassembles one by one.
image
Grayscale images: the form in which pixels are passed between rendering and scanning.
lan
LAN channel (QWBP): find peers on the same network segment, then move bytes to them.
qr
QR codes: capacity model, encoding, and rendering.