A decentralized file system built for Reticulum & based on IPFS (InterPlanetary File System).
Share files over LoRa radio, Wi-Fi, local networks or the internet without relying on a an authoritative server for file hosting or peer lookups.
Access any file on RNFS by it's 'CID'.
Example: Moby Dick book .txt file: bagaibqabciqfweclrlsfdkqgkhtmbavijftqfkiogrprcntnew5jalakjsykq4q
No centralized servers
Contents
Install · Sample files (with CIDs) · Getting started · How rnfs adapts IPFS · Settings · Commands · Building apps on rnfs · Keeping content off your node · Example use cases · Running several nodes · Known limitations · specification.md (the protocol) · design.md (the code)
RNFS provides a number of benefits compared to other 'torrenting for RNS' or file hosting projects.
📃 Share File With A Single Line (CID) - To fetch a file, all someone needs it's a 'CID'. For example: Moby Dick book .txt file - bagaibqabciqfweclrlsfdkqgkhtmbavijftqfkiogrprcntnew5jalakjsykq4q
- 🔖 Names (links that can change) - A CID is permanent, but a name can point to a new version. Others can follow a name to keep the latest version pinned, and keepers keep it resolvable while you're offline.
- ⚡ Multi-source, resumable downloads - Pulls pieces from several seeders at once, preferring nearby, fast and long-standing nodes, and picks up where it left off after an interruption.
- 🌐 Global Namespace - CIDs are deterministic hashes of the actual files. No tracker is required like in traditional torrents
- 🗺️ Distributed Hash Table (Kademlia) - The Kademlia algorithm is used to build a distributed dictionary of who has which files. This means you only need to know the CID of a file to download it. Because the global 'phone book' is decentralized, there is zero reliance on trackers, or for the torrent file to be up to date as long as at least one other node has pinned (is seeding) the file.
- 📃 Nodes automatically split up lookup data between themselves - Lookup 'Kademlia' for more in depth information, however all nodes on the RNFS network will take responsibility for remembering a part of the overall 'address book' of nodes and a list of which files they host. Assignment of the information is automatic.
- 🌱 Automatically find seeders - No file of known seeders is needed, RNFS will automatically ask it's peers "who has this file". Kademlia is very efficient at doing this. In a 1 million node network, this would only take 6 hops to find a seeder.
- GUI Application (Planned) - User friendly interface for rnfs coming soon
- Standard Install (Command line + HTML gateway) - See the Quick Start or Getting started sections to install this project and get started with RNFS.
- Host a NomadNet Gateway - See RNFS NomadNet Gateway Project
- Project Gutenberg NomadNet Mirror - Visit
78d812594409fc12489f3d392974bec9:/page/index.muon NomadNet. This is essentially a site that indexes a mirror of Project Gutenberg (libary of over 70k public domain books). It serves files through a gateway rather than RNFS but also provides CIDs. - Public RNFS NomadNet Gateway - Access file on RNFS via the nomadnet gateway at
14c5fc21e231343073ff9e64a26d6f5d:/page/index.mu
This section covers the basics to get you using rnfs.
Warning
If you don't already have Reticulum running on your machine, you'll need to configure some interfaces to connect to the backbone: see Connecting to the network.
Different package options available at latest release
Windows:
rnfs-0.1.6-windows-x64.exe, saved as rnfs.exe
somewhere on your PATH (how).
Linux:
One liner for install script:
curl -fsSL https://raw.githubusercontent.com/afit21/rnfs/main/install.sh | sh
To install as a systemctl service, add the service flag:
curl -fsSL https://raw.githubusercontent.com/afit21/rnfs/main/install.sh | sh -s -- --service
Or installers for (Debian & Ubuntu). Flatpak
macOS:
pipx install https://github.com/afit21/rnfs/releases/download/v0.1.6/rnfs-0.1.6-py3-none-any.whl (details).
rnfs daemonfor example one of the sample files:
rnfs get bagaibqabciqkq6rth6jhr2vkh7c2ip7p6bsmdxmslbia47rjbmwpcz6wwu6veya -o manifesto.pdfOr open it in your browser: http://127.0.0.1:8789/rnfs/bagaibqabciqkq6rth6jhr2vkh7c2ip7p6bsmdxmslbia47rjbmwpcz6wwu6veya?filename=manifesto.pdf
rnfs add holiday.jpg # prints its CID: give that to anyone who should have the fileThat's all you need for the basics. Getting started goes a step further (folders, links that can change, sharing in the background), and Commands lists everything.
rnfs runs on Linux, macOS and Windows. Every package includes Reticulum and LXMF and gives you one rnfs
command. Your data in ~/.rnfs is never touched by an update or uninstall.
| System | Download |
|---|---|
| Windows (64-bit) | rnfs-0.1.6-windows-x64.exe (how) |
| Debian 12+, Ubuntu 24.04+, Raspberry Pi OS (64-bit) | rnfs_0.1.6_amd64.deb or rnfs_0.1.6_arm64.deb (how) |
| Other Linux (x86_64) | rnfs-0.1.6-x86_64.flatpak (how), or the install script |
| macOS, or any system with Python 3.11+ | rnfs-0.1.6-py3-none-any.whl (how) |
These links are for 0.1.6; for a newer version, take the same files from the
latest release, which also has the source archive
(rnfs-0.1.6.tar.gz) and SHA256SUMS (checking a download).
The exe is a single file with Python, Reticulum and LXMF inside. In PowerShell, from your download folder:
$bin = "$env:USERPROFILE\.local\bin"
New-Item -ItemType Directory -Force $bin | Out-Null
Move-Item .\rnfs-0.1.6-windows-x64.exe "$bin\rnfs.exe" -Force
Unblock-File "$bin\rnfs.exe"
[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";$bin", "User")
# open a new terminal, then:
rnfs daemon- The exe isn't code-signed: if SmartScreen warns you, choose "More info", then "Run anyway".
- Update: stop the daemon and replace
rnfs.exe. Uninstall: delete it. - Start at logon: download contrib/install-windows-task.ps1 and run
powershell -ExecutionPolicy Bypass -File install-windows-task.ps1(-Uninstallremoves it). It logs to%USERPROFILE%\.rnfs\daemon.log. - Windows has no execute bit, so a directory with executable scripts gets a different CID than on Linux or macOS. Windows support is the newest, so please report problems.
Pick amd64 or arm64 to match dpkg --print-architecture. Ubuntu 22.04 has Python 3.10, which is too old;
use the install script there.
curl -LO https://github.com/afit21/rnfs/releases/download/v0.1.6/rnfs_0.1.6_amd64.deb
sudo apt install ./rnfs_0.1.6_amd64.deb
systemctl --user enable --now rnfs # run it as a service; logs: journalctl --user -u rnfs -f
loginctl enable-linger $USER # keep it running while you're logged out- Update: install the newer
.debthe same way, thensystemctl --user restart rnfs. - Uninstall:
systemctl --user disable --now rnfs && sudo apt remove rnfs.
curl -LO https://github.com/afit21/rnfs/releases/download/v0.1.6/rnfs-0.1.6-x86_64.flatpak
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --user rnfs-0.1.6-x86_64.flatpak
alias rnfs='flatpak run io.github.afit21.rnfs' # add to ~/.bashrc
rnfs daemonThe app can use the network, ~/.rnfs and ~/.reticulum, and your Downloads folder: not the rest of your home
folder, nor any device. Grant more with flatpak override --user --device=all io.github.afit21.rnfs (RNode and
other serial interfaces) or flatpak override --user --filesystem=<folder> io.github.afit21.rnfs (another
folder, or ~/.config/reticulum if your Reticulum config is there); --reset takes them back. Until 0.1.6 the app had your whole home folder and every
device: after updating, grant the ones you used again. Update by installing the newer .flatpak; uninstall
with flatpak uninstall --user io.github.afit21.rnfs.
brew install pipx && pipx ensurepath # then open a new terminal
pipx install https://github.com/afit21/rnfs/releases/download/v0.1.6/rnfs-0.1.6-py3-none-any.whl
rnfs daemon- Update:
pipx install --force <the newer .whl URL>. Uninstall:pipx uninstall rnfs. - Start at login:
curl -fsSL -o ~/Library/LaunchAgents/io.github.afit21.rnfs.plist \ https://raw.githubusercontent.com/afit21/rnfs/main/contrib/io.github.afit21.rnfs.plist launchctl load -w ~/Library/LaunchAgents/io.github.afit21.rnfs.plist
- pipx (and plain
pip install) resolves rnfs's dependencies unpinned, at the newest Reticulum, LXMF and cryptography rnfs allows, not at the tested versions and hashes inpackaging/requirements.lockthat the other packages and the install script use.
For any Linux with Python 3.11+ and venv (sudo apt install python3 python3-venv on Debian/Ubuntu). It
installs the latest release into ~/.local/share/rnfs/venv, with the dependency versions and hashes pinned in
the release, and checks the download against SHA256SUMS and, if the GitHub CLI is installed, its signed
provenance for that release's tag. If gh is installed but not logged in, it stops: run gh auth login, or set
RNFS_REQUIRE_ATTESTATION=0 to check the checksum only. Without gh it warns (RNFS_REQUIRE_ATTESTATION=1 makes
it stop): curl | sh without gh trusts whoever can write the release's files, since SHA256SUMS comes from
the same place.
curl -fsSL https://raw.githubusercontent.com/afit21/rnfs/main/install.sh | sh # install or update
curl -fsSL https://raw.githubusercontent.com/afit21/rnfs/main/install.sh | sh -s -- --service # also run it as a systemd user service
curl -fsSL https://raw.githubusercontent.com/afit21/rnfs/main/install.sh | sh -s -- --uninstall # remove it (keeps ~/.rnfs)- Running it again updates rnfs and restarts the service if you installed it.
--maininstalls the main branch instead (no checksum to check; give it on every update).--system, as root, installs for all users in/opt/rnfsand/usr/local/bin/rnfs.--python PATHpicks the Python;--forcereplaces anotherrnfscommand (e.g. pipx's);RNFS_SOURCE=<dir, archive, .whl or URL>installs something other than the latest release (nothing checks it; a plainhttp://URL gets a warning).- With the service,
loginctl enable-linger $USERkeeps it running while you're logged out. More sandboxing (ProtectSystem,PrivateTmp) needs unprivileged user namespaces, so it is an optional drop-in, for this unit or the.deb's: seecontrib/rnfs.service.d/hardening.conf.
Each release's SHA256SUMS lists every file's checksum. With it next to your download:
sha256sum -c SHA256SUMS --ignore-missing # Linux; macOS: shasum -a 256 <file>
gh attestation verify rnfs_0.1.6_amd64.deb --repo afit21/rnfsA matching checksum shows the file is whole, not who made it. The signed build provenance, checked with the
GitHub CLI (2.49 or newer, logged in), shows it was built by this repository's
release workflow. On Windows, Get-FileHash .\rnfs-0.1.6-windows-x64.exe gives the checksum.
git clone https://github.com/afit21/rnfs && cd rnfs
pip install --require-hashes -r packaging/requirements.lock # the tested Reticulum and LXMF (optional)
pip install -e .
rnfs daemon
python3 -m unittest discover tests # the testspipx install git+https://github.com/afit21/rnfs installs the main branch without a checkout. The release
packages are built by .github/workflows/release.yml; the scripts it uses are in packaging/. To work on rnfs
itself (the tests and their options, lint, the locks, adding a schema step or an API route, releasing), see
CONTRIBUTING.md.
rnfs uses Reticulum's own config directory (/etc/reticulum, ~/.config/reticulum or ~/.reticulum;
%USERPROFILE%\.reticulum on Windows), and attaches to rnsd if it is running. Your network interfaces
(LAN, TCP, RNode/LoRa…) are set up in its config file: see the
Reticulum manual. daemon --rnsconfig DIR uses another.
Everything else is in the rnfs directory (--dir, default ~/.rnfs/), which only you can read:
| Path | Contents |
|---|---|
rnfs.toml |
your settings |
storage/identity, storage/lxmf_identity |
the node's identity (your node ID comes from it) and your LXMF identity |
blocks/ |
stored blocks |
state/rnfs.db |
pins, records, jobs, names' sequences, contacts, the LXMF inbox and outbox, deny lists (SQLite; an older version's JSON files are imported once and kept as *.migrated) |
names/ |
name keys: back them up with rnfs name export |
files/ |
the shared folder: anything put here is added, pinned and shared |
api_token, daemon.lock |
the running daemon's API token and lock |
debug.log |
with debug_log on, this run's debug log (debug.log.1: the previous run's) |
Keep files/ writable only by you. rnfs skips links and files with several hard links in it, so copy files in
rather than linking them. rnfs refuses an identity or rnfs.toml owned by another account.
Some files & folders that I host as examples for everyone to download. Fetch them with a running daemon that is connected to a network (Getting started):
Moby Dick book .txt file - bagaibqabciqfweclrlsfdkqgkhtmbavijftqfkiogrprcntnew5jalakjsykq4q
The Communist Manifesto - bagaibqabciqkq6rth6jhr2vkh7c2ip7p6bsmdxmslbia47rjbmwpcz6wwu6veya (PDF, 715 KB)
Original Doge Photo - bafkreigqte4wbglswc74766w445b67scla7kd2wvrqtp6t7a2ofw32znny
MeshChatv2.4.0 (folder) - bagaibqabciqaiphoe7qtscjuqprvwpr2ifr3hgnvuk5rgeh53mpk4ntapwutr3a
Nasa Apolo 11 'One Small Step' audio mp3 - bagaibqabciqbe75h7g7s4zqozhwrbxbica6krk5iggn7itaoh6t2fh7lnhqpjeq
Reticulum github zip taken on 11/10/2026 - bagaibqabciqd6s6yrwpp6eemv27sa7ud4l3r7rjcyu5oh4fgziopqa432htdxma
Apline Linux 3.24.2 ISOs (folder) - bagaibqabciqh5e4vsvwazopdfypoiflpfjvbfqionjko7cvxsmzmumsdoppzixy
SCP-106 Original Image - bagaibqabciqospcwo756sqjp7yl3h2vukd3ijkvffy4nyh2umljg4yluwp2ce6a
rockyou.txt.zip - bagaibqabciqp26agunewwmavivqgwv3c53xvnxdn7vpz4rdknjlfai72zszhdza
Fetch one with rnfs get <cid> -o <output file name>, or open it in your browser through the local gateway
(?filename= sets the type and the download name):
rnfs get bagaibqabciqospcwo756sqjp7yl3h2vukd3ijkvffy4nyh2umljg4yluwp2ce6a -o scp-106.jpghttp://127.0.0.1:8789/rnfs/bagaibqabciqkq6rth6jhr2vkh7c2ip7p6bsmdxmslbia47rjbmwpcz6wwu6veya?filename=ManifestoOftheCommunistParty.pdf
rnfs daemon runs your node; leave it running (or install it as a service), since every other command
talks to it. Your node needs Reticulum interfaces that reach other nodes:
- Local network: Reticulum's default
AutoInterfacefinds nodes on the same LAN or Wi-Fi with no setup. - Internet: add an interface to a public transport node to your Reticulum
config; see reticulum.network/connect.html for the public backbone. - Radio: add an
RNodeInterfacefor LoRa (Reticulum manual).
Your node then joins the DHT through the project's built-in bootstrap nodes and the rnfs nodes it hears
announcing. rnfs status shows your node ID and known_nodes; rnfs peers lists the nodes it knows. To also
join through a node you choose, run rnfs daemon --bootstrap <NODE_ID> (or set bootstrap_nodes);
default_bootstrap = false stops it contacting the built-in ones.
rnfs get <cid> -o out.pdf # a progress bar; Ctrl+C cancels, and a later get resumes
rnfs get <cid>/docs/ --background # leave it running in the daemon: rnfs jobs
rnfs ls <cid> # list a directory; rnfs cat <cid> writes to stdout
rnfs get <cid> --from <node ID> # ask a node you know has it, without the DHTIn a browser, http://127.0.0.1:8789/rnfs/<cid>/ shows a directory's listing or its index.html, so a folder
with a website in it works as a site.
rnfs add report.pdf # prints its CID; give that to others
rnfs add photos/ # a whole directory
rnfs add report.pdf --wrap # in a directory, so the link keeps its name: <cid>/report.pdfadd stores and pins the file and tells the DHT you have it. Files dropped into ~/.rnfs/files/ are
shared automatically. rnfs pin rm <cid> stops sharing, and rnfs gc frees space used by downloads you
didn't pin. By default your node also shares what you download while it's cached, so others can see what you
fetched; provide_strategy = "pinned" shares only what you pin.
A CID always means the same content. A name is a fixed ID you can point at new CIDs:
rnfs name create site # prints the name (32 hex characters)
rnfs name publish site <cid> # again later for a new version
rnfs contacts add site <name> # a nickname, so others can type "site"
rnfs get site/index.html # whatever the name points at now
rnfs name follow <name> # keep the latest version pinnedIn a browser: http://127.0.0.1:8789/name/<name>/. Back up the key with rnfs name export site -o site.key:
only it can update the name.
rnfs follows IPFS where its design helps, uses Reticulum in place of libp2p, and drops what only makes sense on IP networks. It is not compatible with IPFS. The full comparison is in specification.md, Appendix A.
| IPFS | rnfs |
|---|---|
| CIDs | Same idea: a raw block or one rnfs DAG codec, SHA-256, base32. A file of up to 64 KiB has the same CID as in IPFS |
| UnixFS | 64 KiB chunks (optionally FastCDC) in balanced trees, sorted directory indexes. The execute bit is kept; mtimes and symlinks aren't |
| PeerID, transports, NAT traversal | A node ID is a Reticulum destination hash; Reticulum routes and encrypts |
| Kademlia DHT | For provider records only, K = 10, server and client modes, Sybil resistance from seniority |
| Bitswap | Multi-source, resumable, verified /blocks requests over Reticulum, sized to each link's measured rate |
| IPNS | Signed records in Reticulum announces, re-served by keepers so a name outlives its owner's announces |
| Pinning, remote pinning, GC | The same, with pin sources (user, folder, name, remote, follow, LXMF) |
| CAR files, HTTP gateway | export / import archives; a local gateway with sandboxed pages and trustless ?format=raw / archive |
| MFS | dir set / dir rm make edited copies; a name is the mutable root |
| Pubsub, DNSLink | Dropped (floods are unaffordable on radio; Reticulum has no DNS). Use name follow and LXMF links |
| Private networks | Reticulum interface access codes, plus rnfs private mode (members only) |
Settings go in ~/.rnfs/rnfs.toml; command-line options override them. Times are in seconds, sizes in bytes,
rates in bits/s, and an unknown setting is an error. These are the ones you're most likely to change; every
setting and its default is in specification.md, section 12.
# ~/.rnfs/rnfs.toml
dht_mode = "client" # on phones, laptops and battery nodes
max_storage = 21474836480 # 20 GiB
provide_strategy = "pinned" # don't share your downloads
upload_budget = 5_000_000 # upload at most ~5 MB per hour
no_serve_interfaces = ["RNode"] # don't upload over the LoRa radio
quiet_hours = "22:00-06:00"
lxmf_accept_from = ["<a friend's LXMF address>"]| Setting | Default | What it does |
|---|---|---|
dht_mode |
"server" |
"client" only asks the DHT, for nodes often offline or short of bandwidth |
bootstrap_nodes, default_bootstrap |
[], true |
Node IDs to join through first; whether to also use the built-in ones |
slow |
"auto" |
A slow node uses one provider at a time and fewer parallel queries; auto when every interface is under slow_bitrate (100 kbit/s) |
announce_interval |
21600 (6 h) |
How often the node and its names announce; keep it ≥ 1 h |
provide_strategy |
"roots" |
"roots" shares pins and every complete download while it's cached (even a cat or stat), so anyone can learn what you downloaded; "pinned" shares only pins |
max_storage |
10 GiB | Cached downloads are evicted above this; pins never are. The disk keeps max(256 MiB, 2%) free |
max_download_size |
none | Refuse fetches larger than this |
max_jobs |
2; 1 on slow nodes | Transfers running at once |
upload_budget |
none | Bytes per hour your node uploads in total |
serve_interfaces, no_serve_interfaces |
all, none | Reticulum interfaces to upload on, or never upload on (names or prefixes) |
quiet_hours |
off | Local times with no uploads, e.g. "22:00-06:00" |
remote_pin_identities, remote_pin_quota |
[], 1 GiB |
Identities allowed to ask this node to pin (pin remote add), and the storage each may use |
deny_lists |
[] |
Deny lists to apply |
keep_max_names |
100 |
Names you keep or follow for others |
follow_quota |
1 GiB | Storage each followed name's target may use; a larger one isn't fetched and the previous one stays pinned (up to twice this while it changes). 0 stops fetching followed names |
lxmf_accept_from |
everyone | Only keep LXMF messages from these addresses (recommended if you only expect links from people you know) |
lxmf_auto_fetch_from, lxmf_auto_fetch_quota |
[], 1 GiB |
Senders whose links are fetched and pinned automatically, and the storage each may use |
lxmf_propagation_node |
none | Propagation node for offline delivery (send --propagate) and periodic downloads |
lxmf_send_from_hint |
true |
Links you send carry your node ID, so recipients can fetch from you; this links your LXMF address to your node |
api_port, gateway_port |
8788, 8789 |
Local API and gateway ports (127.0.0.1 only) |
gateway_scripts |
true |
Let gateway pages run scripts |
debug_log |
false |
Write ~/.rnfs/debug.log (readable by you only) for diagnosing problems (daemon --debug-log) |
verbosity |
0 |
Extra logging: 1 per-query detail, 2 RNS debug output (daemon -v, -vv) |
Private mode. private = true with a members list (identity hashes, from each
node's rnfs status) makes the node link to, serve and keep records for members only. A name's announce still
reaches everyone, so outsiders can see which CID it points at and when it changes (not fetch it), and
rnfs name publish warns of this in private mode.
LXMF senders you've named (in lxmf_accept_from, lxmf_auto_fetch_from, or as an LXMF contact) are trusted.
Anyone can make new LXMF addresses for free, so messages from everyone else only push out each other's, within
fixed limits; set lxmf_accept_from if strangers' messages aren't wanted.
Every command except daemon talks to the running daemon, so run them as the daemon's user. --dir DIR and
--api-port N pick another daemon; rnfs <command> --help shows every option. A target is
<cid>[/path] or <contact>/path; where a node ID is expected, a node contact's nick works too.
| Command | Description |
|---|---|
rnfs daemon |
Run a node with its API and gateway (--bootstrap, --rnsconfig, --private, --debug-log, -v…) |
rnfs status / peers / stats |
Node ID and identity hash, storage, jobs, LXMF address / known nodes / traffic |
rnfs ping NODE_ID / providers CID |
Round-trip time to a node / nodes providing a CID |
rnfs add PATH|- |
Add a file, directory or stdin, pin it and print its CID (options below) |
rnfs get TARGET [-o OUT] [--from NODE_ID] [--background] |
Fetch and write out a file or directory |
rnfs cat TARGET [--offset N] [--length N] |
Write a file, or part of one, to stdout |
rnfs ls TARGET / stat TARGET |
List a directory / size and how much is stored locally |
rnfs dir set TARGET NAME CID / dir rm TARGET/NAME |
Make a copy of a directory with an entry added, replaced or removed |
rnfs export TARGET -o FILE / import FILE |
Write or import an archive (USB sticks, sneakernet) |
rnfs pin add TARGET / pin rm CID / pin ls |
Fetch and pin / unpin / list pins, their sources and where they came from |
rnfs pin remote add|rm NODE_ID CID |
Ask another node to pin or unpin (it must list you in remote_pin_identities) |
rnfs gc / verify |
Evict cached downloads / re-check every stored block |
rnfs deny add|rm ENTRY… / deny ls |
Edit your deny list / show it and the lists in use |
rnfs jobs [list|show|wait|cancel|clear] [ID] |
Background transfers: progress, rate, time left |
rnfs name create|list|publish|resolve|rm |
Your names and what they point at |
rnfs name export|import ALIAS |
Back up or restore a name's private key |
rnfs name keep|unkeep|kept NAME |
Store and serve someone's name record, so it resolves while they're offline |
rnfs name follow|unfollow|following NAME |
Keep the latest version a name points at pinned |
rnfs contacts add NICK HEX / rm / ls |
Nicknames for names, node IDs and LXMF addresses |
rnfs lxmf address / lxmf sync |
Your LXMF address / download waiting messages now |
rnfs send ADDRESS TARGET [-m TEXT] [--propagate] |
Send an rnfs: link over LXMF |
rnfs inbox / inbox get N / inbox rm N / outbox |
Received links (each line starts with the sender's address and whether it is untrusted, trusted or a contact; the display name the sender chose comes last, quoted), fetching them (a name link whose name can't be resolved needs --use-sent-cid), sent messages |
add options: --wrap keeps the file's name in the link; --only-hash prints the CID without storing;
--no-pin keeps it as evictable cache; --chunker fastcdc lets an edited version share most blocks;
--to RECIPIENT… (after the path) seals a file so only those node IDs, LXMF addresses or contacts can read it,
and --to-self adds you.
get without -o uses the target's name only if it is a safe file name (printable, not starting with .),
otherwise its CID, and writes under a temporary name until the transfer is complete. It decrypts sealed files
you can open; --raw gives the stored bytes, which is what scripts that show content to others should use.
Gateway (http://127.0.0.1:8789): /rnfs/<cid>[/path] and /name/<name>[/path] serve files and
directories; ?filename= sets the type and download name; ?format=raw and ?format=archive return data you
can check against the CID. Pages are sandboxed and can't read the gateway's responses, and sealed files are
never decrypted there.
Python programs use rnfs the way they use RNS, through one of two classes with the same methods, objects and errors:
rnfs.Noderuns a node inside your program, beside its own Reticulum, with its own storage and identity.rnfs.Clientuses the user's running daemon (their content, pins and names). It never loads Reticulum, so it starts fast.
The API is provisional until rnfs 0.2 (rnfs.API_STATUS).
import RNS, rnfs
RNS.Reticulum() # your program's Reticulum, as with any RNS app
with rnfs.Node("~/.myapp/rnfs") as node: # or: rnfs.Client(), for the daemon (no RNS.Reticulum())
added = node.add("photos/") # rnfs.Added: .cid, .size, .warnings
for entry in node.ls(added.cid): # rnfs.Entry(name, kind, size, cid)
print(entry.name, entry.size)
with node.open(f"{added.cid}/cat.jpg") as f: # seekable: a read after a seek fetches only what it covers
data = f.read(4096)
site = node.names.create("site")
site.publish(added.cid)
node.names.watch(site, lambda record: print("now", record.cid))
node.jobs.get(f"rnfs:name/{site.hex}", "/tmp/copy").wait()- An embedded node is a DHT client with LXMF off unless you pass
settings={"dht_mode": "server", ...}. It never stops your Reticulum. Never point it at the daemon's~/.rnfs/: a directory is used by one node at a time. - Callbacks run on one event thread; keep them short.
examples/rnfs_share.pyis a runnable example using either class; design.md's "Public API" section is the reference. Other languages can use the local API's JSON endpoints (listed inrnfs/api.py).
A deny list keeps chosen content off a public gateway or provider node. Entries are CIDs or file hashes
(sha256:<hex>, as sha256sum prints it):
rnfs deny add bafkreidhtnhwyqkmbhxvp2mrery66f4kpdsfbxew3qrfqrbtwbs6zb5zce
rnfs deny add sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08To use a list someone maintains (a text file, one entry per line, # for comments), add its CID, or
name/<name>/path to follow its updates, to deny_lists in rnfs.toml.
A denied CID is never fetched, stored or served, whether asked for directly or found inside other content (the gateway answers 451). Cached copies are deleted and remote pins released; your own pins are kept but not served, and the log names them so you can remove them. A file hash is only known once a file is downloaded, so it stops the file being handed out or added, not its blocks being fetched: deny the CID for that (the first time rnfs refuses a file by its hash, the log names the command). While there are file hashes, a file that repeats its content more than 16 times beyond 64 MiB can't be read or imported (451): it can't be checked cheaply.
Community library over LoRa. A librarian publishes a directory of documents under a name and updates it
with dir set and name publish. Library nodes rnfs name follow it, so each always holds the latest
edition, and readers rnfs get library/maps/region.pdf.
Sharing with a friend over LXMF. rnfs send <their LXMF address> <cid>/report.pdf -m "as promised".
Sideband, NomadNet and MeshChat show the link as text; their rnfs node lists it in rnfs inbox and fetches it
with rnfs inbox get 1, asking your node first. Add friends to lxmf_auto_fetch_from to fetch their links
automatically.
Private group. rnfs add minutes.pdf --to <alice's node ID> bob --to-self seals
a file so only those people can read it, while any node can carry it. For a fully closed group, run every
node with private = true and the same members.
Offline sneakernet. rnfs export <cid> -o bundle.rnfs on a connected node, rnfs import bundle.rnfs on
the offline one: it's verified and pinned.
Mirroring a publisher. rnfs name follow <their name> keeps their latest content pinned and their name
resolvable through your node while they're offline.
Field kit with an airtime budget. A battery node on a shared LoRa channel:
dht_mode = "client", max_jobs = 1, upload_budget = 2_000_000, quiet_hours = "22:00-06:00" and
no_serve_interfaces = ["RNode"]. Interface rules need rnfs to run Reticulum itself: attached to a shared
rnsd, every link looks like it arrives on the local interface.
A website on the mesh. rnfs add mysite/, then rnfs name create site && rnfs name publish site <cid>.
Visitors open http://127.0.0.1:8789/name/<name>/ on their own node, and each name publish updates it.
NomadNet pages can't open rnfs: links, so show the link as text: rnfs:name/<name>/index.html.
Give each daemon its own --dir, --api-port, --gateway-port and Reticulum config (--rnsconfig). The
first node's Reticulum config has a TCPServerInterface with enable_transport = Yes; the others have a
TCPClientInterface to it and share_instance = No, and start with --bootstrap <first node ID>:
rnfs --dir /tmp/a --api-port 9001 daemon --rnsconfig /tmp/a --gateway-port 9101 --bootstrap <hub id>
rnfs --dir /tmp/a --api-port 9001 add somefile
rnfs --dir /tmp/b --api-port 9002 get <cid> -o out- A name resolves only while its owner, a transport node that remembers its announce, or a keeper is reachable, and can be updated about once an hour.
- Sybil attacks can make a CID hard to find (
get --frombypasses the DHT), but data can never be forged. - Sealed files hide content and file name, not size or recipients, and don't prove who sealed them.
getof a directory leaves sealed files inside it encrypted. - Under the default
provide_strategy = "roots", anyone can learn what your node downloaded. - LXMF messages over 256 kB are refused, unsent messages are lost if the daemon restarts, and offline delivery
needs
lxmf_propagation_node. A message rnfs can't keep (a stranger's during a flood) is lost, though the sender's app shows it as delivered.
