Print (animated) QR codes straight in your terminal
$ qr "Hello"
or
$ echo -n "Hello" | qr
Usage: qr [OPTIONS] STRING
or: cat FILE | qr [OPTIONS]
Options:
-m QR mode [na8k] (n = number, a = alphabet, 8 = 8-bit, k = Kanji)
-v QR version [1-40] (the default is 10 for -a)
-e QR EC level [lmqh] or [1-4]
-l use two characters per block
-c compact mode
-b border width [1-4] (the default is 1)
-i invert colors
-p force colorless output
-u add a UTF-8 BOM to the encoded data
-a animate data [bbqr|bbqr-hex|bbqr-base32|bbqr-zlib|ur|numbered|txqr]
-t data type [auto|psbt|tx|json|cbor|text|binary] (for -a)
-f frame rate [1-30] (frames per second, the default is 4)
-n number of frames to output (for -a)
-r print frame contents instead of QR codes (for -a)
-h print help info and exit
-V print version info and exit
Data that is too large for a single QR Code can be split into a series of smaller ones that play as an animation, which phones and hardware wallets scan one frame after another. This is how air-gapped Bitcoin wallets pass around PSBTs and transactions, but works for any file.
$ qr -a bbqr < transaction.psbt
In a terminal the frames loop in place until you press Ctrl+C. When the
output is not a terminal, every frame is printed once, one after another.
With -r the content of each frame is printed instead of QR Codes, each
followed by a newline, which is handy for piping into other tools (TXQR
frames are binary and may contain newlines themselves).
There are several formats, and the one to use is whichever the receiving wallet or app can scan:
| Format | -a |
Fountain codes | Typical scanners |
|---|---|---|---|
| BBQr | bbqr |
no | COLDCARD Q, Sparrow Wallet, Nunchuk |
| UR | ur |
yes | Sparrow Wallet, Keystone, Passport, SeedSigner, Jade |
| pMofN | numbered |
no | Specter Desktop, Specter DIY, Krux |
| TXQR | txqr |
yes | txqr-reader |
Without fountain codes every frame has to be caught at least once, so a missed frame means waiting for the loop to come around again. With fountain codes any frames will do, as long as there are enough of them.
-vsets the QR version of every frame (10 by default, 57Γ57 modules). Lower versions are easier to scan but need more frames.-fsets the frame rate (4 frames per second by default).-nsets how many frames to output. The animation then stops by itself instead of looping, and when printing frames (rather than playing them) the sequence repeats, or for fountain codes keeps generating new frames.-e,-c,-l,-iand-bwork the same way as for single QR Codes.-ttells what kind of data it is. By default PSBTs are detected automatically, whether they are binary, base64 (as printed by Bitcoin Core and most wallets) or hex. Use-t txfor a transaction, which can be binary or hex. Anything else is sent as text if it's valid UTF-8, and as binary data otherwise.
BBQr (Better Bitcoin QR) by Coinkite.
Every frame starts with an 8-character header (B$, the encoding, the file
type, the number of frames and the frame number), so frames can be scanned in
any order, but every one of them is needed.
$ qr -a bbqr < transaction.psbt
$ bitcoin-cli walletprocesspsbt "$psbt" | jq -r .psbt | qr -a bbqr
$ qr -a bbqr -t tx 0200000001...
bbqrcompresses the data with zlib when that makes it smaller, and uses base32 otherwise. This is what COLDCARD Q recommends.bbqr-zlib,bbqr-base32andbbqr-hexforce one encoding.
Scan with: COLDCARD Q, Sparrow Wallet, Nunchuk, Fully Noded, Krux, and other wallets compatible with COLDCARD Q.
To check the frames on a computer, use Coinkite's reference decoder:
$ pip install bbqr
$ qr -a bbqr -r < transaction.psbt | bbqr decode
Uniform Resources
by Blockchain Commons. Frames look like UR:CRYPTO-PSBT/7-12/LPAS..., and
data that fits into a single frame is shown as a plain UR:CRYPTO-PSBT/....
$ qr -a ur < transaction.psbt
After the first round of frames (one per piece of data), UR keeps generating new frames that mix several pieces together using fountain codes, so the animation never repeats: a scanner that misses some frames catches up from any later ones, instead of waiting for the whole loop to come around.
PSBTs are sent as crypto-psbt, which is what most wallets expect.
Everything else is sent as bytes.
Scan with: Sparrow Wallet, Keystone, Foundation Passport, SeedSigner, Blockstream Jade, Krux and other wallets listed by Blockchain Commons.
The simplest scheme, used by Specter: the data is split into text parts that
start with p1of3 , p2of3 and so on. PSBTs are sent in base64,
transactions in hex, text as is, and other binary data in base64.
$ qr -a numbered < transaction.psbt
Every part is needed, in any order. Krux accepts up to 99 parts.
Scan with: Specter Desktop, Specter DIY and Krux.
TXQR by Ivan Daniluk, a research project
on transferring files over animated QR codes with fountain codes. Frames
start with <block>/<chunk size>/<total size>|, followed by the data of
several pieces XORed together.
$ qr -a txqr < notes.txt
The first round of frames is identical to what TXQR's own encoder makes, and like UR, it then keeps generating new frames instead of repeating. Data is sent as text, the same way as for numbered parts. Since frames carry raw bytes, plain ASCII text is the safest choice.
Scan with: txqr-reader (iOS).
QR Codes often carry secrets (keys, seeds, passwords, PSBTs), so qr makes
sure that what it was given doesn't stay behind in memory:
- All memory it uses is zeroed before being freed or resized, including
while switching animation frames, and so is the part of the stack it used.
This covers the QR Code encoder (it's part of
qr) and zlib's working memory too. - The data never goes through the C library's I/O buffers.
- When the data is given as an argument, it's erased from the command line, so it doesn't show up in the list of running processes.
- Core dumps are disabled, and on Linux other processes can't attach to
qr. - Ctrl+C (and termination, hangup or a closed pipe) still wipes everything on the way out, and animations are erased from the screen when they stop.
Some things are beyond its reach:
- Nothing can be cleaned up after
kill -9, a crash or a power loss, and memory may have been written to swap or a hibernation file meanwhile. - Your shell may keep arguments in its history: pass secrets through STDIN
instead, e.g.
qr < secret.txt. - Static QR Codes stay on the screen and in the terminal's scrollback until cleared.
# make install
or
$ sudo PREFIX=/usr/local make install
Every release comes with standalone binaries:
qr-linux-x86_64.tar.gzandqr-linux-arm64.tar.gzfor Linux, each containing a statically linkedqrthat runs on any distributionqr.exefor Windows (x86_64)
They have zlib built in.
$ make
Build dependencies
$ docker build -t y2z/qr .
$ sudo install -b utils/qr.sh /usr/local/bin/qr
$ pkg install qr
or
$ cd /usr/ports/textproc/qr/
$ make install clean
# make uninstall
$ make test
The tests are a plain shell script, so they can also be run one by one,
e.g. sh tests.sh 10 36 runs tests 10 and 36. A failed test leaves what
it did in tests.tmp/<number>.
Test dependencies
- zbar
$ apt-get install zbar-tools$ brew install zbar - imagemagick
$ apt-get install imagemagick$ brew install imagemagick - FreeMono font
$ apt-get install fonts-freefont-ttf$ brew tap homebrew/cask-fonts $ brew install --cask font-freefont - Python 3
$ apt-get install python3Nothing to do: it comes with the Xcode Command Line Tools
On Windows, in an MSYS2 UCRT64 shell:
$ pacman -S diffutils mingw-w64-ucrt-x86_64-imagemagick mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-zbar
$ make test CC=gcc FONT="$(cygpath -m "$PWD/FreeMono.ttf")"
with FreeMono.ttf taken from GNU FreeFont.
QR Code is a registered trademark of DENSO WAVE INCORPORATED in Japan and other countries.
Zero-Clause BSD (0BSD): use, copy, modify and distribute this software for any purpose, with or without fee, no attribution required. This software is distributed without any warranty.
