[{"data":1,"prerenderedAt":195},["ShallowReactive",2],{"navigation_docs":3,"landing":59},[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":61,"body":62,"description":61,"extension":187,"meta":188,"navigation":137,"path":189,"seo":190,"stem":193,"__hash__":194},"landing\u002Findex.md","",{"type":63,"value":64,"toc":185},"minimark",[65,158,165,171,176,181],[66,67,71,75,78,81,97],"u-page-hero",{"className":68,"orientation":70},[69],"grid-surface","horizontal",[72,73,74],"template",{"v-slot:headline":61},"Free · ISC · Rust sender · Flutter receiver",[72,76,77],{"v-slot:title":61},"The QR codes are the wire.",[72,79,80],{"v-slot:description":61},"Porter moves a file between two machines that share no network. A terminal\ndisplays it as a slideshow of QR codes, a phone camera reads them back, and\nfountain coding means the receiver never has to catch any particular frame.",[72,82,83,89],{"v-slot:links":61},[84,85,88],"u-button",{"size":86,"to":16,"trailing-icon":87},"xl","i-lucide-arrow-right","Get started",[84,90,96],{"size":86,"to":91,"color":92,"icon":93,"target":94,"variant":95},"https:\u002F\u002Fgithub.com\u002FTerrorSquad\u002Fporter","neutral","i-simple-icons-github","_blank","subtle","View on GitHub",[98,99,103],"pre",{"className":100,"code":101,"language":102,"meta":61,"style":61},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","cd rust-sender\ncargo build --release\n\n.\u002Ftarget\u002Frelease\u002Fporter-sender myfile.pdf --fountain\n# Point the receiver app at the terminal. That's the whole protocol.\n","bash",[104,105,106,119,132,139,151],"code",{"__ignoreMap":61},[107,108,111,115],"span",{"class":109,"line":110},"line",1,[107,112,114],{"class":113},"s2Zo4","cd",[107,116,118],{"class":117},"sfazB"," rust-sender\n",[107,120,122,126,129],{"class":109,"line":121},2,[107,123,125],{"class":124},"sBMFI","cargo",[107,127,128],{"class":117}," build",[107,130,131],{"class":117}," --release\n",[107,133,135],{"class":109,"line":134},3,[107,136,138],{"emptyLinePlaceholder":137},true,"\n",[107,140,142,145,148],{"class":109,"line":141},4,[107,143,144],{"class":124},".\u002Ftarget\u002Frelease\u002Fporter-sender",[107,146,147],{"class":117}," myfile.pdf",[107,149,150],{"class":117}," --fountain\n",[107,152,154],{"class":109,"line":153},5,[107,155,157],{"class":156},"sHwdD","# Point the receiver app at the terminal. That's the whole protocol.\n",[159,160],"u-page-section",{"description":161,"title":162,":features":163,":links":164},"The receiver cannot ask for a frame again. A dropped frame — bad angle, motion blur, a reflection at exactly the wrong moment — is simply gone, and every interesting decision in Porter follows from that.","A link with no back-channel","[{\"title\":\"Sequential\",\"description\":\"Frame n is byte range n. Miss one and you wait for the sender to loop all the way back to that index.\",\"icon\":\"i-lucide-list-ordered\"},{\"title\":\"Fountain\",\"description\":\"Each frame XORs a random subset of blocks, picked from its sequence number by a PRNG both sides run. Any sufficient pile rebuilds the file.\",\"icon\":\"i-lucide-waves\"},{\"title\":\"So nothing is special\",\"description\":\"No individual frame matters, which means a persistently missed one costs nothing at all.\",\"icon\":\"i-lucide-shuffle\"}]","[{\"label\":\"How a transfer actually runs\",\"to\":\"\u002Fhow-it-works\",\"variant\":\"link\",\"trailingIcon\":\"i-lucide-arrow-right\"}]",[159,166],{"description":167,"title":168,":features":169,":links":170},"Porter is an air-gapped tool, so the sending machine is often not the one you keep a runtime on. The sender compiles to a single static binary — no Node.js, no Python, nothing to install alongside it.","One binary on the sending machine","[{\"title\":\"porter-sender \u003Cfile>\",\"description\":\"A ratatui TUI — QR grid, sidebar, scrubbing and gap-fill. Runs on the offline machine.\",\"icon\":\"i-lucide-monitor-play\"},{\"title\":\"Receiver app\",\"description\":\"Flutter, on Android and macOS. Scans, decodes, verifies, and resumes from disk if it is killed.\",\"icon\":\"i-lucide-smartphone\"},{\"title\":\"porter-sender serve\",\"description\":\"An HTTP receiver on axum, for when a network turns out to exist after all.\",\"icon\":\"i-lucide-server\"},{\"title\":\"porter-sender join\",\"description\":\"Reassembles the .partaa and .partab files a receiver wrote, with a SHA-256 check.\",\"icon\":\"i-lucide-combine\"}]","[{\"label\":\"Every flag and subcommand\",\"to\":\"\u002Fdocs\u002Freference\u002Fcommands\",\"variant\":\"link\",\"trailingIcon\":\"i-lucide-arrow-right\"}]",[159,172],{"description":173,"title":174,":features":175},"The security story is short, which is the point. No account, no server, no telemetry, and by default no trace left on disk.","What Porter does not do","[{\"title\":\"No network path\",\"description\":\"The sender opens no sockets on the QR path. The transport is a camera pointed at a screen, so there is nothing to intercept in transit.\",\"icon\":\"i-lucide-wifi-off\"},{\"title\":\"No disk trace by default\",\"description\":\"Slideshow position persists only under --resume. Without the flag, nothing is written.\",\"icon\":\"i-lucide-file-x\"},{\"title\":\"No telemetry\",\"description\":\"No analytics, no crash reporting, no phone-home. Both binaries do exactly what the flags say.\",\"icon\":\"i-lucide-eye-off\"},{\"title\":\"Verified on arrival\",\"description\":\"Fountain transfers always carry a CHECKSUM frame. The receiver checks SHA-256 before saving.\",\"icon\":\"i-lucide-shield-check\"},{\"title\":\"Resumable receiver\",\"description\":\"Chunks land on disk as they decode, so a killed app rehydrates from the files rather than re-scanning.\",\"icon\":\"i-lucide-rotate-ccw\"},{\"title\":\"Decisions on the record\",\"description\":\"The worker isolate, fountain coding, disk hydration and the Rust move are all written up as ADRs.\",\"icon\":\"i-lucide-file-text\"}]",[159,177],{"description":178,"title":179,":links":180},"QR is a slow, lossy, line-of-sight link, and no amount of coding changes that. A 109 MB file is roughly 26,000 chunks — a real transfer that has been run end to end, and also a long time holding a phone at a screen. Porter is for the case where two machines genuinely cannot talk - no NIC, an isolated network, a device you will not plug anything into.","Honest limits, and an ISC licence","[{\"label\":\"Read the docs\",\"to\":\"\u002Fdocs\u002Fgetting-started\u002Fintroduction\",\"size\":\"xl\",\"trailingIcon\":\"i-lucide-arrow-right\"},{\"label\":\"View on GitHub\",\"to\":\"https:\u002F\u002Fgithub.com\u002FTerrorSquad\u002Fporter\",\"target\":\"_blank\",\"size\":\"xl\",\"color\":\"neutral\",\"variant\":\"subtle\",\"icon\":\"i-simple-icons-github\"}]",[182,183,184],"style",{},"html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":61,"searchDepth":121,"depth":121,"links":186},[],"md",{},"\u002F",{"title":191,"description":192},"Air-gapped file transfer over QR codes","Move a file between two machines with no network between them. A terminal renders it as a QR slideshow, a phone scans it back, and fountain coding means no frame has to be caught twice.","index","RPQ127_7dStXs3kiCaC52B-CzSDihEeN9io5GbxodW8",1786360985616]