# Prior art and design decision

Reviewed 2026-09-08. This is a transport implementation, not a compatible clone
of any project below. Sources are primary project documentation.

## Blockchain Commons UR / bc-ur

[UR specification](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md)
[Developer overview](https://developer.blockchaincommons.com/ur/)

UR wraps typed CBOR as a URI using Bytewords. Multipart UR starts with pure
fragments, then produces pseudorandom XOR combinations. Readers can accumulate
an unordered stream; extra combinations can recover missed original fragments.
The specification explicitly permits the initial fixed sequence to be repeated.
Therefore printing does NOT itself make UR impossible, nor does it require a
fountain encoder to emit infinite frames. Full UR decoding does need the
multipart/CBOR/Bytewords machinery. The current specification reserves the
undifferentiated `bytes` type for testing/validation, so production KPOP use
should choose a properly specified type instead of assuming `ur:bytes` is enough.

**Decision for this deliverable:** do not adopt UR wholesale. The requested
explicit index-0 manifest and finite human-numbered set do not directly map to
UR's normal wire format. Two vendored optical libraries plus a small independent
core satisfy the stated dependency budget. A proper UR implementation, type
selection and interoperability vectors would expand that scope substantially.

For a future KPOP feature where receiving from existing UR wallets is an actual
requirement, use a maintained UR implementation, define the application type and
test peer compatibility. Sharing a transport alone does not mean a wallet will
understand arbitrary KPOP backup text. Keep that as an explicit UR mode, with a
separate decoder, rather than labelling KBQR1 as UR-compatible.

## divan / txqr

[Repository](https://github.com/divan/txqr)
[Original experiment and measurements](https://divan.dev/posts/animatedqr/)

The original txqr design used a numbered frame prefix and looping animation.
The author later moved to fountain coding. His timing experiments demonstrate
that chunk size, camera performance, symbol density and missed frames interact;
a fast best-case transfer may still have many timeouts. The reported device
results are historical, not universal settings for modern phones.

KBQR follows the simple numbered-transfer idea but adds a version, set identity,
whole-payload checksum and manifest. It cannot exchange txqr frames. Start at
250 ms (4 fps) with play/pause/manual frame selection. If scans stall, increase
the interval to 400–750 ms or enlarge the displayed code. A fixed stream can be
joined mid-loop but requires every unique frame eventually; it lacks fountain
coding's recovery advantage on a lossy channel.

## za3k / qr-backup

[Repository and paper-backup workflow](https://github.com/za3k/qr-backup)

qr-backup produces printable QR PDFs and restores using cameras or scanners.
Its documentation emphasizes printed restore instructions, conservative defaults,
and optional redundancy to recover missing pages or codes. It is a Linux command
line workflow with features including compression and optional password protection.

KBQR adopts the paper usability lessons: visible ids and frame captions, a label,
checksum and date on each sheet, black-on-white codes and self-contained print
exports. It does not implement qr-backup's protocol, compression, encryption or
cross-code redundancy. Here, **every code, including index 0, must survive**.
Printing duplicate complete sets is possible; this is not erasure coding.

## Why plain indexing here

For a small fixed paper set, exact missing numbers are useful: a user can locate
and rescan frame 17. No fragment equations or extra transport dependencies are
needed. The cost is real: one unrecoverable missing frame prevents restoration,
and repeated screen loops can be inefficient. Fountain coding is a worthwhile
future animation option, but it does not meet this brief's finite manifest-led
format without additional design. KBQR1 deliberately chooses clarity and the
specified dependency limit over existing-wallet transport interoperability.

## Print and timing defaults

Default chunk: 160 original UTF-8 bytes. Base64url expands each full data chunk
to 214 ASCII characters; the repeated checksum/header adds about 94 characters
for the sample. That overhead buys standalone identification and corruption
checks. Many small symbols also cost more paper and more required scans.

Use Q correction initially. H adds redundancy but also density at the same
physical size; it is not automatically easier to scan. M can be useful on a
clean screen. See [DENSO WAVE's correction guidance](https://www.qrcode.com/en/about/error_correction.html).
The level percentages concern codewords, not an arbitrary fraction of the
printed area. Large finder-pattern damage can defeat detection.

All codes retain a four-module quiet zone per
[DENSO WAVE's sizing guidance](https://www.qrcode.com/en/howto/code.html).
At 65 mm overall width the default sample data code has generous module spacing;
TESTING.md gives measured dimensions and comparisons with 320/640-byte chunks.
SVG print output keeps edges independent of screen pixel density. Defaults are
engineering starting points supported by synthetic decoding, not certified
inkjet settings. Test with the intended printer, paper, lighting and camera.

## Why QR rather than Aztec or Data Matrix

The specified vendored encoder plus jsQR provide a small, established browser QR
path. Aztec and Data Matrix would need other encoders/decoders, defeating this
package's two-library constraint. Their potential density/quiet-zone trade-offs
can be explored later. `qr-adapter.js` isolates matrix generation, rendering and
image decoding from wire framing. A new symbology should carry the same KBQR1
strings, or explicitly version the protocol if its transport representation
changes. No alternate symbology support is claimed today.
