[{"data":1,"prerenderedAt":531},["ShallowReactive",2],{"navigation_docs":3,"-docs-reference-wire-format":59,"-docs-reference-wire-format-surround":526},[4,25,40],{"title":5,"path":6,"stem":7,"children":8,"page":24},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[9,14,19],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F1.getting-started\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Using Porter","\u002Fdocs\u002Fgetting-started\u002Fusing-porter","docs\u002F1.getting-started\u002F3.using-porter","i-lucide-terminal",false,{"title":26,"path":27,"stem":28,"children":29,"page":24},"Architecture","\u002Fdocs\u002Farchitecture","docs\u002F2.architecture",[30,35],{"title":31,"path":32,"stem":33,"icon":34},"Overview","\u002Fdocs\u002Farchitecture\u002Foverview","docs\u002F2.architecture\u002F1.overview","i-lucide-layers",{"title":36,"path":37,"stem":38,"icon":39},"Receiver internals","\u002Fdocs\u002Farchitecture\u002Freceiver","docs\u002F2.architecture\u002F2.receiver","i-lucide-smartphone",{"title":41,"path":42,"stem":43,"children":44,"page":24},"Reference","\u002Fdocs\u002Freference","docs\u002F3.reference",[45,49,54],{"title":46,"path":47,"stem":48,"icon":23},"Commands","\u002Fdocs\u002Freference\u002Fcommands","docs\u002F3.reference\u002F1.commands",{"title":50,"path":51,"stem":52,"icon":53},"Wire format","\u002Fdocs\u002Freference\u002Fwire-format","docs\u002F3.reference\u002F2.wire-format","i-lucide-binary",{"title":55,"path":56,"stem":57,"icon":58},"Decisions","\u002Fdocs\u002Freference\u002Fdecisions","docs\u002F3.reference\u002F3.decisions","i-lucide-file-text",{"id":60,"title":50,"body":61,"description":519,"extension":520,"links":521,"meta":522,"navigation":523,"path":51,"seo":524,"stem":52,"__hash__":525},"docs\u002Fdocs\u002F3.reference\u002F2.wire-format.md",{"type":62,"value":63,"toc":501},"minimark",[64,68,116,119,124,131,136,146,203,207,213,250,259,263,269,280,284,287,293,313,317,327,331,337,344,348,351,357,360,403,407,413,428,432,441,445,452,465,469,477,483,498],[65,66,67],"p",{},"This is the one document two independent implementations must agree on.",[69,70,71,84],"table",{},[72,73,74],"thead",{},[75,76,77,81],"tr",{},[78,79,80],"th",{},"Implementation",[78,82,83],{},"Files",[85,86,87,103],"tbody",{},[75,88,89,93],{},[90,91,92],"td",{},"Rust sender",[90,94,95,99,100],{},[96,97,98],"code",{},"rust-sender\u002Fsrc\u002Fchunker.rs",", ",[96,101,102],{},"src\u002Ffountain.rs",[75,104,105,108],{},[90,106,107],{},"Flutter receiver",[90,109,110,99,113],{},[96,111,112],{},"flutter\u002Flib\u002Fservices\u002Fchunk_parser.dart",[96,114,115],{},"fountain_codec.dart",[65,117,118],{},"If this document and the code disagree, the code is wrong — but fix both.",[120,121,123],"h2",{"id":122},"frame-types","Frame types",[65,125,126,127,130],{},"Every frame is a single QR code whose payload is a UTF-8 string with\n",[96,128,129],{},"|","-separated fields. Three shapes exist.",[132,133,135],"h3",{"id":134},"sequential-data-frame","Sequential data frame",[137,138,144],"pre",{"className":139,"code":141,"language":142,"meta":143},[140],"language-text","index|total|mode|id|payload\n","text","",[96,145,141],{"__ignoreMap":143},[147,148,149,156,166,184,190],"ul",{},[150,151,152,155],"li",{},[96,153,154],{},"index"," — 1-based frame number.",[150,157,158,161,162,165],{},[96,159,160],{},"total"," — total data frames, excluding the ",[96,163,164],{},"CHECKSUM"," frame.",[150,167,168,171,172,175,176,179,180,183],{},[96,169,170],{},"mode"," — ",[96,173,174],{},"T"," plain text, ",[96,177,178],{},"B"," base64 binary, ",[96,181,182],{},"C"," gzip+base64.",[150,185,186,189],{},[96,187,188],{},"id"," — 2-character transfer id.",[150,191,192,195,196,202],{},[96,193,194],{},"payload"," — the chunk body. ",[197,198,199,200],"strong",{},"May itself contain ",[96,201,129],{},", so parsers must split\non the first four separators only.",[132,204,206],{"id":205},"fountain-data-frame","Fountain data frame",[137,208,211],{"className":209,"code":210,"language":142,"meta":143},[140],"F|seq|K|fileSize|id|payload\n",[96,212,210],{"__ignoreMap":143},[147,214,215,225,231,237,241],{},[150,216,217,220,221,224],{},[96,218,219],{},"seq"," — 0-based symbol sequence number; determines ",[96,222,223],{},"(degree, indices)",".",[150,226,227,230],{},[96,228,229],{},"K"," — number of source blocks.",[150,232,233,236],{},[96,234,235],{},"fileSize"," — original length in bytes. The last source block is zero-padded\nand the receiver trims the assembled output to this.",[150,238,239,189],{},[96,240,188],{},[150,242,243,245,246,249],{},[96,244,194],{}," — base64 of exactly ",[96,247,248],{},"blockSize"," bytes.",[65,251,252,254,255,258],{},[96,253,248],{}," is ",[197,256,257],{},"not transmitted",". The receiver infers it from the decoded\npayload length, since every symbol is exactly one block.",[132,260,262],{"id":261},"checksum-frame","Checksum frame",[137,264,267],{"className":265,"code":266,"language":142,"meta":143},[140],"CHECKSUM|T|id|sha256\n",[96,268,266],{"__ignoreMap":143},[65,270,271,272,276,277,224],{},"Lowercase hex digest of the ",[273,274,275],"em",{},"original"," file. Fountain always sends this frame;\nsequential sends it only with ",[96,278,279],{},"--verify",[120,281,283],{"id":282},"transfer-id","Transfer id",[65,285,286],{},"Two characters derived from the first two bytes of the file's SHA-256:",[137,288,291],{"className":289,"code":290,"language":142,"meta":143},[140],"value = (digest[0] \u003C\u003C 8) | digest[1]\nid    = ALPHABET[(value >> 6) & 0x3f] + ALPHABET[value & 0x3f]\nALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\"\n",[96,292,290],{"__ignoreMap":143},[294,295,296,299,300,302,303,305,306,309,310,312],"warning",{},[197,297,298],{},"The id identifies content, not a session."," The same file sent twice gets the\nsame id — and re-sending at a different QR version yields the same id with a\ndifferent ",[96,301,229],{}," and ",[96,304,248],{},", producing two mutually undecodable streams. A\nreceiver must key a fountain transfer on ",[96,307,308],{},"(id, K, blockSize)",", never on ",[96,311,188],{},"\nalone. Mixing them corrupts the output.",[120,314,316],{"id":315},"symbol-derivation","Symbol derivation",[65,318,319,320,322,323,326],{},"Sender and receiver independently derive each symbol's source-block indices from\n",[96,321,219],{},". This must stay ",[197,324,325],{},"bit-identical","; any drift silently breaks decoding.",[132,328,330],{"id":329},"prng-xorshift32","PRNG — xorshift32",[137,332,335],{"className":333,"code":334,"language":142,"meta":143},[140],"state = seq XOR 0x9e3779b9      \u002F\u002F if 0, use 0x9e3779b9\nnext():\n  state ^= state \u003C\u003C 13\n  state ^= state >> 17\n  state ^= state \u003C\u003C 5\n  return state                   \u002F\u002F unsigned 32-bit, wrapping\n",[96,336,334],{"__ignoreMap":143},[65,338,339,340,343],{},"Dart must mask to 32 bits explicitly; Rust's ",[96,341,342],{},"u32"," wraps natively.",[132,345,347],{"id":346},"degree-table","Degree table",[65,349,350],{},"Integer weights only, so the same table and the same draw-by-modulo produce\nidentical degrees everywhere.",[137,352,355],{"className":353,"code":354,"language":142,"meta":143},[140],"if K \u003C= 2:  every symbol has degree 1\n\nweights[1] = 1\nweights[i] = floor(K \u002F (i * (i - 1)))     for i = 2..K\n\nS = max(2, floor(sqrt(K)))\nweights[i] += max(1, floor(S \u002F i))        for i = 1..S-1\nweights[S] += S\n\ncum_weights[i] = sum(weights[1..i])\ntotal          = cum_weights[K]\n",[96,356,354],{"__ignoreMap":143},[65,358,359],{},"Two things that look like bugs and are not:",[147,361,362,386],{},[150,363,364,254,367,370,371,374,375,378,379,382,383,385],{},[96,365,366],{},"weights[i]",[197,368,369],{},"not"," floored to 1. It reaches 0 once ",[96,372,373],{},"i*(i-1) > K"," (around\n",[96,376,377],{},"i > sqrt(K)","), which is what caps the maximum degree near ",[96,380,381],{},"sqrt(K)",".\nFlooring to 1 would give every high degree equal weight and make large-",[96,384,229],{},"\ntransfers effectively undecodable.",[150,387,388,391,392,395,396,398,399,402],{},[96,389,390],{},"i * (i - 1)"," ",[197,393,394],{},"must be computed in 64-bit",". It overflows ",[96,397,342],{}," once\n",[96,400,401],{},"i > ~65536",", which a large file reaches. This was a real bug: it wrapped to a\nbogus divisor and corrupted the distribution.",[132,404,406],{"id":405},"drawing-indices","Drawing indices",[137,408,411],{"className":409,"code":410,"language":142,"meta":143},[140],"r      = next() mod total\ndegree = smallest d >= 1 with cum_weights[d] > r\nindices = {}\nwhile |indices| \u003C degree and |indices| \u003C K:\n    indices.add((next() mod K) + 1)      \u002F\u002F 1-based\nreturn sorted(indices)\n",[96,412,410],{"__ignoreMap":143},[65,414,415,416,419,420,423,424,427],{},"The symbol's ",[273,417,418],{},"effective"," degree is ",[96,421,422],{},"|indices|",", which can be less than ",[96,425,426],{},"degree","\nif the PRNG repeats an index.",[132,429,431],{"id":430},"symbol-value","Symbol value",[65,433,434,435,438,439,224],{},"XOR of the source blocks at ",[96,436,437],{},"indices",", each zero-padded to ",[96,440,248],{},[120,442,444],{"id":443},"redundancy","Redundancy",[65,446,447,448,451],{},"The sender emits ",[96,449,450],{},"N = max(K + 20, ceil(K * 3))"," symbols, then the checksum\nframe. The 3× factor is empirical: enough for full peeling recovery from the\ncomplete pool, with margin for scan loss.",[65,453,454,455,458,459,461,462,464],{},"A receiver needs materially ",[197,456,457],{},"more than K"," distinct symbols before peeling\ncompletes — measured 1.33×–1.89× ",[96,460,229],{}," across K=50..70,965. Progress UI should\nscale against ~2×",[96,463,229],{},", or it reads as ~99% complete with a third of the scanning\nleft.",[120,466,468],{"id":467},"sizing","Sizing",[65,470,471,472,302,474,476],{},"Not part of the wire format — a receiver never needs it — but it explains why\n",[96,473,229],{},[96,475,248],{}," change between runs of the same file.",[137,478,481],{"className":479,"code":480,"language":142,"meta":143},[140],"version   = clamp(((rows - buffer) * 2 - 17 - 4) \u002F 4, 1, 40)\ncapacity  = byte-mode capacity for (version, ecc)\nblockSize = floor((capacity - headerReserve) * 0.75)     \u002F\u002F fountain\nK         = ceil(fileSize \u002F blockSize)\n",[96,482,480],{"__ignoreMap":143},[65,484,485,486,489,490,497],{},"Because ",[96,487,488],{},"version"," depends on terminal height, ",[197,491,492,493,302,495],{},"resizing mid-transfer changes\n",[96,494,229],{},[96,496,248],{}," and forks the stream. Senders recompute layout on resize;\nreceivers should detect the change rather than merge the two.",[65,499,500],{},"The Rust sender computes the header reserve exactly and forces a single\nByte-mode QR segment, because the crate's \"optimal\" segmentation can charge more\nbits than the byte-mode capacity tables assume — a payload under the table limit\ncould still be rejected.",{"title":143,"searchDepth":502,"depth":502,"links":503},2,[504,510,511,517,518],{"id":122,"depth":502,"text":123,"children":505},[506,508,509],{"id":134,"depth":507,"text":135},3,{"id":205,"depth":507,"text":206},{"id":261,"depth":507,"text":262},{"id":282,"depth":502,"text":283},{"id":315,"depth":502,"text":316,"children":512},[513,514,515,516],{"id":329,"depth":507,"text":330},{"id":346,"depth":507,"text":347},{"id":405,"depth":507,"text":406},{"id":430,"depth":507,"text":431},{"id":443,"depth":502,"text":444},{"id":467,"depth":502,"text":468},"Normative description of what a Porter QR frame contains.","md",null,{},{"icon":53},{"title":50,"description":519},"pWm1rmiCXM6bzqr6mZpcqXjiaaiDKH7Omu-qXIa0n_s",[527,529],{"title":46,"path":47,"stem":48,"description":528,"icon":23,"children":-1},"Every flag, subcommand and keybinding the sender accepts.",{"title":55,"path":56,"stem":57,"description":530,"icon":58,"children":-1},"The architecture decision records, and where to find the full text.",1786321021821]