File transfer¶
How files move on and off these machines without the cloud: LocalSend for phone ↔ computer, and rsync over SSH for computer ↔ computer. Everything is local-network only, opened temporarily on untrusted networks (and auto-closed), and installs from a single recipe.
Install¶
just file-transfer # tools + firewall + ~/Downloads/Transfers + docs
just file-transfer-harden # receiving machines only: inbound SSH key-only + LLMNR off
file-transfer is idempotent and machine-agnostic — it opens 53317 on
whatever firewall zone holds the active interfaces, and renders a per-host
~/Downloads/Transfers/README.md. The helper scripts and the niri menu entries
arrive with just stow.
Why file-transfer-harden is a separate step
It disables SSH password login (key-only). Run it only on machines that
will receive rsync, and make sure you have a key installed — or are sitting
at the machine — first. sshd is left disabled; ssh-here starts it on demand.
LocalSend — phone ↔ computer¶
LocalSend discovers devices over UDP 53317 and
transfers over TCP 53317.
- Home network: permanently allowed on the
homezone — your phone just finds the machine. - Untrusted network: open it for the session from the niri power menu (Mod+Escape):
| Action | Menu entry | Command |
|---|---|---|
| Open + launch | 📤 LocalSend share (temp) | localsend-here on |
| Close now | 🔒 LocalSend close | localsend-here off |
The opening is runtime-only with a 30-minute timeout, so it reverts on its own (and a reboot wipes it).
rsync over SSH — computer ↔ computer¶
Direction decides what you need:
- This machine → remote (you push out): nothing to set up —
rsync -av ~/stuff/ user@remote:/dest/. - Remote → this machine (something connects in): inbound SSH is key-only and
every transfer key is locked to
~/Downloads/Transfers.
Authorize a sender's key (one-time)¶
| Option | Effect |
|---|---|
| (default) | upload-only (-wo -no-del) into ~/Downloads/Transfers |
--ro |
download-only |
--dir NAME |
lock to ~/NAME instead |
The key is installed with a forced command:
restrict strips PTY and forwarding; rrsync (see man rrsync) refuses
anything but rsync and jails it to that folder. So the key can do nothing but
drop files there — no shell, no other paths — even if it leaks.
Receive a transfer¶
- Open: menu 🔑 SSH/rsync in (temp) or
ssh-here on— startssshd, opens22for 30 minutes. - Sender:
rsync -av ./files/ <host>:(the path is relative to the inbox folder). - Close: menu 🔒 SSH/rsync close or
ssh-here off.
How the temporary opening works¶
localsend-here and ssh-here share one pattern:
- Find the zone of the default-route interface — the network you're actually on.
- Add the port to that zone at runtime only, with
--timeout=30m. - Never
--permanent, so it's gone on reboot/reload and can't silently persist.
That's why a conference-Wi-Fi opening is safe: scoped to the live network, and self-closing.
Security model¶
- Inbound SSH is public-key only, no root —
/etc/ssh/sshd_config.d/10-hardening.conf. sshdis disabled; started on demand byssh-here, stopped onoff.- Transfer keys are rsync-only, upload-only, no-delete, single-folder.
- LLMNR is disabled (
file-transfer-harden) — SMB discovery (mDNS + NetBIOS) is unaffected. - Every temporary firewall hole is runtime-only with a 30-minute expiry.
Where it lives¶
| Thing | Path |
|---|---|
| Helper scripts | ~/.local/bin/{localsend-here, ssh-here, add-rsync-key} |
| System setup | ~/.local/bin/{setup-file-transfer, harden-file-transfer-ssh} |
| Menu entries | niri power menu (menu-power) |
| Doc templates | ~/.local/share/file-transfer/*.md → rendered into ~/Downloads/Transfers/ |
| Packages | file-transfer category in setup/packages.yaml |
| Recipes | just file-transfer, just file-transfer-harden |