Phase 0 proof of concept: move a GNU Taler offline-payment payload (~3 kB) from a phone screen to a laptop webcam, byte-identical, with no network on either side, using fountain-coded animated QR codes. Pre-grant demo for the NGI TALER open call.
Status: verified on real hardware — a phone screen to laptop webcam transfer
completed byte-identical (hash OK). Detailed metrics (time, throughput,
distance) from that run are not yet recorded; see docs/results.md.
General-purpose animated-QR transfer is already solved — libcimbar, TXQR, and Blockchain Commons UR all move data screen-to-camera. We build a dedicated channel instead because this use case has different requirements: a small signed payload (2–7 kB) rather than large files, sunlight-readable operation with a monochrome fallback for outdoor/market use, and a lean implementation with no heavy computer-vision dependency so it can live in embedded reader firmware. The most important reason — binding each optical frame to a secure element's attestation, which a generic transfer library doesn't offer — is part of the funded work below, not yet implemented here; this PoC proves the transport itself works.
This repo is the transport-layer proof of concept for a larger proposal, OpenTOP (Taler Offline Purse): a secure-element-backed offline purse for GNU Taler that prevents double-spending in hardware rather than merely detecting it after the fact, submitted to the NGI TALER open call. This demo shows the optical transport works end to end; it deliberately does not address double-spend prevention or the secure element — that is the core of the funded work, not this PoC.
npm install
npm run devOpen the printed localhost URL; the index page links to the encoder and
decoder pages. getUserMedia (camera access, for the decoder) requires
localhost or HTTPS, which npm run dev provides.
- Encoder: load a payload (file picker, or the sample button for
samples/payload-3kb.bin), click Start — an animated, fountain-coded QR renders on the canvas. - Decoder: click Start to request camera access, point it at the animating QR. Once all fragments are received it reconstructs the payload and reports a SHA-256 hash (optionally compared against an expected hash — there's a button to fill it in from the sample). After a successful run it also shows transfer time, throughput, and fountain-code overhead, with buttons to export that run as JSON or all runs so far as CSV.
npm run build # static build: index + decoder
npm run build:encoder # single self-contained encoder.html for offline phone usedist-encoder/encoder.html is meant to be copied onto a phone and opened
locally — no server, no network. Verified to load and run with all network
requests blocked, and confirmed working end to end on real hardware.
node tools/measure/synthetic.ts --coins 1,4,8,15 --denom rsa
node tools/measure/synthetic.ts --coins 1,4,8,15 --denom csRegenerates docs/payload-sizes.md and samples/payload-3kb.bin.
- Payload size table:
docs/payload-sizes.md - Measured transfer runs:
docs/results.md
hardware/ has 3D-printable enclosure files (STL/STEP) and a BOM for an
optical reader device — see hardware/README.md. This
is a separate, not-yet-integrated track from the phone-to-webcam software
demo above.
AGPLv3 — see LICENSE.