Wireless file transfer for retro handhelds.
retsend (retro + sending) is a Rust LocalSend
client for retro handhelds: send and receive files with your phone
or PC over Wi-Fi, no cable or SSH. Compatible with the official LocalSend apps.
It targets PortMaster-compatible Linux handhelds (Knulli, muOS, ROCKNIX — Anbernic/TrimUI class devices, gamepad-only, no compositor) and runs on regular desktop Linux too.
Getting files onto a handheld usually means pulling the SD card or setting up SSH/SMB. Phones and desktops already have LocalSend — this is the missing end: a client built for a gamepad and screen, moving files both ways.
- Discovery — a live radar of nearby devices (LocalSend protocol v2.1).
- Receive — accept/decline dialog with countdown, speed/ETA, and cancel from either side; quick-save mode auto-accepts.
- Send — gamepad file browser with multi-select and per-file progress.
- Save routes — sort received files into folders by extension, e.g. ROMs straight into each console's folder.
- Encryption — the protocol's HTTPS mode, on by default; works with the official app's default settings both ways.
- Settings on device — alias, save folder, and port, applied live.
- Headless —
retsend --receiveruns with no screen, for SSH and scripting. - Small and simple — no async runtime, a minimal HTTP server, system SDL2.
Grab retsend-portmaster.zip from
Releases and unpack it
into your ports folder (e.g. /roms/ports/).
System SDL2 is the only native dependency. On Debian/Ubuntu:
sudo apt-get install -y build-essential pkg-config libsdl2-dev
cargo runFiles passed as arguments (cargo run -- file1 file2) come pre-selected in the
send browser.
Two instances on one machine discover each other (multicast loopback) — handy for trying both sides of a transfer without a second device:
RETSEND_DATA_DIR=/tmp/ls-a cargo run &
RETSEND_DATA_DIR=/tmp/ls-b cargo runTests are headless (no SDL, no network setup needed):
cargo test| Pad | Keyboard | Action |
|---|---|---|
| D-pad / stick | Arrows | Navigate |
| A | Enter | Send to device · select file · accept · type |
| B | Esc | Back · decline · cancel · erase (keyboard) |
| Start | F1 | Confirm send · OK (keyboard) |
| Select | Tab / F5 | Refresh radar · switch roots · layer (keyboard) |
| L1 / R1 | PgUp / PgDn | Switch tabs · page the file browser |
config.toml lives in the data dir (created with defaults on first run) and
everything in it is also editable from the Settings screen, except:
[network] https = false— fall back to the protocol's plain-http mode[network] announce_interval_secs— multicast announce cadence[transfer] browser_roots— extra mount points for the file browser[transfer] history_limit— max transfers kept in the History tab (default 200)
Received files land in save_dir by default. [transfer.routes] overrides
that per file extension — handy for dropping ROMs straight into each console's
folder. Edit it on the device from Settings → Save routes (type the extension,
pick the folder), or in the config:
[transfer.routes]
gbc = "gb" # relative → <save_dir>/gb
gba = "/roms/gba" # absolute → used as-is
png = "/roms/screenshots"Extensions match case-insensitively, folders are created on demand, and
anything without a matching route still lands in save_dir.
Environment variables override paths and control logging at launch:
RETSEND_DATA_DIR, RETSEND_CONFIG, RETSEND_SAVE_DIR, RETSEND_SCALE,
RETSEND_GLES=0|1, RETSEND_LOG_LEVEL, RETSEND_LOG_FILE,
RETSEND_PANIC_FILE.
