Welcome
Railroad Network is a way for a community to run its own economy, identity, and decision-making on hardware it owns, and to keep doing so when the internet, the grid, or the institutions around it stop working.
Members trade in a shared credit called the Common. Nobody issues it and nobody can print more of it: when you pay a neighbour, your balance goes down and theirs goes up, and the community as a whole always sums to zero. Who you are is a key on your own phone, backed by people who vouch for you, not by an account on someone’s server. Rules are written in a Charter the founders sign, changed by votes, and enforced by juries drawn by lot. When the network is gone, payments travel by radio, by text message, or on a printed sheet in someone’s pocket, and settle when they arrive. (Today that last part works from the command-line wallet; the phone app still needs the station in reach. Status and roadmap keeps the honest list.)
The name is a nod to the Underground Railroad: a network of trusted people, under hostile conditions, with no central authority, whose real strength was its human trust architecture. This software tries to encode those principles.
Do not use this with real value. Railroad Network is research-stage software. The cryptography has had an internal review but no independent professional audit. Run a pilot with play stakes, not livelihoods. See Security and audits for exactly where things stand.
Who these pages are for
This site is written for people, not for the code. Pick the door that fits:
| You are… | Go to |
|---|---|
| Someone who wants to use the app, pay people, and take part | For members |
| Someone founding or stewarding a community: charter, votes, disputes | For organizers |
| Someone running the station computer that the community’s phones talk to | For operators |
| Someone who wants the exact commands, the glossary, or the design record | Reference |
Not sure? Read Start here.
Where the software lives
The code is public and dual-licensed (Apache-2.0 or MIT):
station: the Rust daemon and command-line client, and the engineering record (design overview, decision records, threat model, audits).mobile: the Android app.
When this site and a decision record in station disagree, the decision
record is right and this site has a bug. Tell us.
Start here
Ten minutes of orientation. After this, the section for your role stands on its own.
The shape of a community
A Railroad Network community is small and concrete: one station and the phones that can reach it.
- The station is an ordinary computer, often a Raspberry Pi, that one person keeps running. It holds the community’s ledger: the single, signed, tamper-evident history of every payment, vouch, vote, and dispute. It is the only thing that writes that history.
- Each member holds their own key on their own phone (or on a laptop, with the command-line wallet). The station cannot spend, vote, or speak for a member. It can only record what members have signed.
- The organizers are the founders and whoever the community’s charter says makes decisions. In the software they are just members with a role in governance, not administrators with special powers.
There is no company in the middle, no cloud, and today no connection between communities. Federation between communities is planned, but it comes after making a single community resilient, which is where the project is now.
The five ideas
The Common. The unit of account. Balances start at zero and can go negative; that is how mutual credit works. A floor stops anyone from signing themselves too far into debt. More.
Vouching and standing. New members are vouched for by existing ones. Trade and vouches build a standing score that the software computes from the history, using one formula that no community can tune. Enough standing makes you an established member: someone who can vote and sit on a jury. More.
Settlement windows. A payment does not move balances the moment it is confirmed. It waits a day or two, during which either side can contest it. Then it settles. More.
The Charter and the log. The founders sign a charter. Every rule since is a proposal, co-signed and voted on, recorded in the same history as payments. Disputes go to a jury of three drawn by lot. More.
Offline is normal mode, running late. The station is the only thing that writes the history, but a member does not need to reach it to sign. Signed records travel later over whatever works and settle on arrival. Today that is what the command-line wallet does; the phone app still needs the station in reach to send. More.
What to read next
- Members: Install the app, then Join your community. No Android phone? Using a computer instead of a phone.
- Organizers: Start a community.
- Operators: Run a station.
- Everyone: the Glossary when a word is new.
What you can do
As a member you hold your own key, on your own phone, and the community’s station records what you sign. Nobody can move your balance, cast your vote, or speak in your name without that key.
Here is what the app lets you do today, in the order you will meet it:
- Create a wallet and get your
rrn1…address. Install the app. - Join a community by pairing with its station in person. Join your community.
- Pay and get paid in Commons, with a settlement window that protects both sides. Pay and get paid.
- Vouch for people you know, and build your own standing through trade and vouches. Vouching and standing.
- Back up your identity with a recovery circle and an export, so a lost phone need not be a lost identity. Back up and recover your keys.
- Know what happens when the network is down, and what a headroom certificate is for. Today the phone app waits for the station; the command-line wallet keeps signing. When the network is down.
- Offer and find things in the marketplace, agree a price, and pay for what you agreed. The marketplace.
- Contest a payment that went wrong, respond when someone contests yours, and serve on a jury when drawn. Contest a payment.
- Take part in governance: read the Charter, co-sign and vote on proposals. See the organizers’ section for how it works: How decisions get made.
- Use a laptop instead of a phone if you have no Android phone, with the same identity model from a terminal. Using a computer instead of a phone.
Do not use this with real value. This is pilot software. Trade with play stakes until an independent security audit says otherwise. See Security and audits.
Two things that are different here
Your balance can be negative, and that is fine. There is no bank issuing Commons. When you are paid, your balance rises; when you pay, it falls. A newcomer with nothing to sell but time simply starts by going a little negative and works it back. There is a floor (20 Commons in debt by default) below which the software will not let you sign, so nobody can dig a hole the community cannot absorb.
Payments take a day or two to become final. Confirming a payment starts a clock. While it runs, either side can contest. When it stops, balances move. This is on purpose: it is how the community catches mistakes and fraud without a central authority. Do not expect instant finality, and do not worry when a confirmed payment shows as pending.
What is a Common?
A Common is the unit everyone in a Railroad Network community trades in. It is the same unit in every community, so that when communities eventually connect, a Common earned in one place means something in another.
It is mutual credit, not money
Nobody issues Commons. There is no treasury, no mint, and no way to create them out of nothing. A Common comes into existence only when one member pays another:
- Ana fixes Ben’s bicycle. Ben pays Ana 5 Commons.
- Ben’s balance goes from 0 to −5. Ana’s goes from 0 to +5.
- Add up every balance in the community and you get zero. Always.
A positive balance means the community owes you. A negative balance means you owe the community, in the form of goods or services someone will eventually buy from you. Both are normal. A community where everyone is at zero is a community where nobody is trading.
This is an old idea. Local Exchange Trading Systems have run this way in hundreds of communities since the 1980s. The software adds a tamper-evident history, a fraud-resistant way to confirm real-world trades, and the ability to keep going with no internet.
The debt floor
Because anyone can go negative, there has to be a limit. The debt floor is the lowest balance you may sign yourself down to. The default is −20 Commons. A payment that would take you below it is refused by the software before it ever reaches anyone.
The floor counts what has settled and what you have already signed but which has not settled yet, so you cannot get around it by sending several payments at once. It applies when you propose a payment and when you confirm a payment request someone sent you.
A community can set its own floor in the station’s configuration. The point is not to punish debt; it is to bound how much any one person can take out without giving back.
Fractions
Amounts are shown in Commons, with up to two decimal places (2.50 Commons). Under the hood the software only ever counts whole centicommons, one hundredth of a Common, so there is no rounding and no floating-point error in anything that gets signed.
What a Common is worth
Whatever the community decides, by trading. There is no exchange rate to any national currency and the software sets no prices. Communities tend to settle on a rough anchor in conversation (“about an hour of ordinary work”) and let the marketplace do the rest.
Install the app
There is no app store listing. Your organizer or operator hands you a signed installer file for Android, and you install it directly. iPhones are not supported for pilots today.
What you need
- An Android phone from roughly 2017 or later (64-bit).
- The file
app-release.apk, from whoever runs your community. - To be on the same Wi-Fi as the community’s station when you join. After that, you only need to reach it now and then to sync.
Install
- Get the file onto your phone. Email, a USB cable, or a download link all work.
- Allow the install. Open the file. Android will ask whether to allow “install unknown apps” for whichever app you opened it from (Files, Chrome, and so on). Allow it, then tap Install.
- Open the app. It walks you through creating your wallet: choose a
passphrase, optionally turn on fingerprint or face unlock, and the app
generates your address, a long string that starts with
rrn1.
Your key is created on the phone and never leaves it. Nobody, including the station, can act as you.
Updating
When you are handed a newer app-release.apk, install it over the top. Your
wallet and history are preserved.
Do not uninstall first. Uninstalling erases the wallet on the phone. You can get your identity back from a wallet export or your recovery circle, but only if you set one of those up beforehand.
If the app stops syncing in the background
Some phones, Motorola in particular, cut off a backgrounded app’s network access until you exempt it from battery optimization. If notifications or background sync stop working:
Settings → Apps → Railroad Network → Battery → allow Unrestricted.
The wording varies by phone. Your operator has a per-vendor checklist if that does not fix it.
Using a computer instead
A member with a laptop and no Android phone can hold their own key with the command-line wallet instead. It is the same identity model, driven from a terminal, and unlike the app today it can sign while the station is out of reach. See Using a computer instead of a phone.
Join your community
Joining is a short in-person ceremony between you and the community’s operator. It proves to your phone that it is talking to the real station, and to the station that your phone is welcome. Do it face to face.
Before you start
- You have installed the app and created a wallet.
- Your phone is on the same Wi-Fi as the station.
- The operator is next to you, at the station’s console.
The pairing ceremony
- On your phone, tap Join your community. The station appears in the list by name. If it does not, tap Add by address and type the address and port the operator gives you (the port is usually 7500).
- Unlock your wallet. Your phone shows an 8-character code.
- The operator lists the pending requests on the station. Yours shows up
with the same style of code and your
rrn1…address. - Together, compare the codes. Read yours aloud. The operator reads theirs. If they match, the operator confirms your address and you confirm on your phone.
That is it. Your phone now syncs with the station, receives updates, and can transact.
If the codes do not match, stop. Something on the network answered in the station’s place. Do not confirm. The operator will want to find out what before pairing anyone else.
The code comparison is the security of this step. Everything your phone and the station say to each other afterwards is individually encrypted and signed end to end, so it does not matter that the Wi-Fi is shared. What matters is that the first handshake was with the right machine.
Right after joining
- Set up social recovery as soon as there are a few members you trust. The app nudges you from the Home screen (“Protect your account”), and export your wallet too. See Back up and recover your keys.
- Get vouched for. Ask someone who knows you to vouch. Until you have some standing, larger payments and governance are closed to you. See Vouching and standing.
- Read the Charter. Community → Governance shows the founding document everyone signed up to.
If you see a banner about bootstrap grace, the community is young and has fewer than three established members. During grace, the founders act as the electorate and jury pool so the community can function. It ends by itself as members build standing.
Pay and get paid
A payment in Railroad Network is a conversation between two people, witnessed by the station, with a pause built in before it becomes final.
The three steps
- Propose. You sign a payment to someone’s address for an amount. The software checks it would not take you below the debt floor and sends it to the station.
- Confirm. The receiver sees the proposal and confirms it. Nothing moves yet. Confirming starts the settlement window.
- Settle. When the window closes with no dispute, the station records the settlement and both balances move. The payment is now final.
A proposal that is never confirmed simply expires. Either party can also cancel before settlement.
You can also request a payment: you sign a request, the payer confirms it. The debt-floor check then runs against the payer at confirmation time.
The settlement window
| Payment size | Tier | Window |
|---|---|---|
| Small | Tier 1 | 24 hours |
| Larger | Tier 2 | 48 hours |
| 50 Commons and up | Tier 3 | Not available yet: the payment is refused, not shrunk |
The window is the community’s chance to catch a mistake or a lie. During it, either party can contest the payment, which freezes settlement until a jury rules. If nobody contests, it settles.
Larger (Tier 2) payments ask more of the person confirming: they need enough standing to stake on it, and an upheld dispute costs them some of that standing. This is what makes confirming a real-world trade you did not actually receive a bad idea.
Payments at or above 50 Commons would need evidence the software cannot yet weigh, so for now they are refused outright. Split a large trade into smaller payments over time, or wait for a later phase.
What “pending” means
Your balance screen shows two numbers: what has settled, and what is pending (confirmed but still in its window, or signed but not yet delivered to the station). Pending amounts already count against your debt floor, so you cannot spend the same headroom twice.
In the app, a payment that cannot reach the station is not sent at all: the app tells you and you try again when connected. The command-line wallet keeps a signed payment pending until it reaches the station, over Wi-Fi later or on paper. See When the network is down.
Good habits
- Confirm only what you actually received. Confirming is the moment you put your standing behind the claim that the trade happened.
- Contest inside the window. After settlement the payment is final; the only remedy is a new payment the other way.
- Link marketplace payments to the listing. Paying for an agreed inquiry from the marketplace records what the payment was for, which helps if it is ever disputed.
Vouching and standing
There is no sign-up form and no identity check. You become a real member of a community the way you would in a village: someone who is already known says they know you.
Vouching
A vouch is a signed statement from one member that another member’s key belongs to a real, known person. It is recorded in the community’s history like everything else.
Vouching is not free. When you vouch for someone you put a little of your own standing behind them. If they turn out to be a fraud, or a second key for a person who already has one, it costs you. So vouch for people you actually know, and expect the same of others.
To vouch: find the person in the community list, check the address on their phone matches the one you see, and tap Vouch. Vouching needs the station reachable; it is one of the few things you cannot queue up offline.
Standing
Standing is the score the software computes for every member from the history: vouches received, trades completed, disputes lost, and how recently. Two things about it are unusual:
- It is never stored, only computed. Any station can replay the history and arrive at the same number. There is no field an administrator can edit.
- The formula is the same everywhere. No community can tune it. This is a deliberate choice so that standing means the same thing across communities when they eventually connect.
Your Standing screen shows your composite score and what feeds it. Standing decays slowly if you stop taking part, so it reflects who you are now, not who you were.
Established members
Once your standing crosses a threshold (a composite score of 2.0) and your identity is anchored by vouches, you are an established member. Established members are the community’s electorate: they vote on proposals, sit in the jury pool for disputes, and can be recovery holders for the station.
A community with fewer than three established members is in bootstrap grace: the founders stand in as the electorate so the community can govern itself from day one. The app shows a banner while grace is on. It ends by itself.
Limits on new members
To make it expensive to flood a community with fake people, the software rate limits how quickly a member can vouch and how fast a new identity can gain standing. If a vouch is refused with a velocity message, wait and try later; it is not personal.
Back up and recover your keys
Your identity is a key on your phone. Your balance, your standing, your history, and your vote all hang off it. There is no “forgot password” link, because there is no company holding a copy. This page walks through, step by step, what the app gives you instead: a passphrase, a recovery circle of friends who each hold a sealed piece of your key, and an export you can carry to another device.
Both backups work end to end. A recovery circle rebuilds your key on a new phone or laptop from your holders; an export restores it from a file you kept. Set up the circle in your first weeks and do the export too. Neither can be set up after the phone is gone.
What you are protecting
Two things, and they are different:
- Your key. Generated on your phone when you created the wallet. It is your identity. Lose every copy and the identity is gone.
- Your passphrase. Encrypts the key on the phone. Fingerprint or face unlock sits on top of it as a convenience; you still need the passphrase after a restart, to export, and to help a friend recover.
A backup here means a second way to get the key back. Nothing on this page backs up the passphrase. Write it down, on paper, and keep it somewhere that is not your phone.
The idea behind the circle
The app splits your key into pieces, called shards, and hands one to each of several people you trust. The scheme is called Shamir’s secret sharing, and it has one property worth understanding: any three shards, brought together, rebuild the key exactly, and any two reveal nothing at all. Not a little. Nothing. Two holders colluding learn as much about your key as two strangers, because of the mathematics, not because of a server or a rule.
Some facts about how the app does it:
- The split happens on your phone, offline. Nothing is uploaded.
- The threshold is fixed at 3. You choose between 3 and 7 holders; the app recommends 5.
- Each shard is sealed to its holder before it leaves your phone. A photo of the QR code is useless to anyone but that holder, and even that holder has only one piece.
- Holders cannot act alone, cannot see your key, and cannot combine pieces without you asking them to.
The project wrote its own implementation of the scheme rather than depend on a library. The reasoning is in ADR-0004.
Walkthrough: set up your recovery circle
Do this in the first week or two, once there are a few members you trust. It takes fifteen minutes and needs your holders in the room.
Before you start. Pick your holders. Each must already have the app and a wallet, and each needs to be physically present, one at a time. See Choosing holders below.
- Open the setup. On the Home screen the app nudges you with Protect your account and a Set up recovery button. Later, it is under Settings → Social recovery.
- Confirm it’s you. Enter your passphrase. The app needs the key unlocked to split it.
- Read the intro. “Your circle holds the key” explains the three promises: any 3 can bring you back, no single one can, and nothing is uploaded. Tap Choose my circle.
- Choose your circle. Add each holder by scanning the address QR from their app (Settings → Your address) or pasting it. You can give each one a nickname; it stays on your phone. The app refuses your own address and duplicates, and needs at least 3. The counter reads “N chosen · any 3 can restore”.
- Split. Tap Split my key into N pieces. This takes a moment and happens entirely on the device.
- Hand out the shards. The app shows one holder’s QR at a time. For
each holder, in person:
- The holder opens Settings → Shards you hold and scans your screen.
- Their app checks the shard is addressed to them and reports “You’re now holding a recovery piece.”
- You tap Scanned, then Next holder.
- Finish. Once at least 3 are marked scanned, Finish unlocks. If some holders are not present you can tap Finish — distribute the rest later and come back; the remaining shards stay on your phone until delivered.
- Done. “Your circle has you” shows the shape, for example “5-holder circle · 3-of-5 to restore”.
Tap Scanned only when the holder’s phone actually confirmed it. The app takes your word for it. A holder you marked but who never received the shard is a hole in your circle you will not discover until you need it.
Choosing holders
- People who will still be around. A circle of five is a bet that at least three of them will be reachable, with their phones, when you need them. Choose across households and friend groups, not one family.
- People who keep their phones. A holder who wipes or loses their phone loses your shard with it. Ask holders to tell you if that happens, and re-run setup.
- Not all founders, not all one clique. The circle is a social graph that lives on your phone. It is not secret, but it should not be a single point of pressure either.
- Five is a good number. Three of five tolerates two absences. Seven is the maximum and rarely needed.
Changing your circle
Settings → Social recovery → Update my circle runs the same steps again with a new list. Your identity and address do not change. Every old shard stops working the moment you split again, so re-hand-out to everyone, not just the new people.
Walkthrough: hold a shard for someone
You may be asked to be in a friend’s circle. It costs you nothing until the day they need you.
- Be there when they split. They will show you one QR code.
- Open Settings → Shards you hold and scan it. The app accepts only a real recovery shard, and only one addressed to you. A plain address or any other QR is rejected with an explanation.
- The app confirms: “You’re now holding a recovery piece. If your friend ever needs it, any 3 of their N holders can bring them back.”
- Keep your phone and your wallet. The shard lives inside your wallet’s secure storage. If you change phones or reset, it is gone; tell your friend so they can re-run setup.
- Do not forget a shard (the “Forget” action on that screen) unless the owner has told you they re-split and yours is dead.
You cannot read the shard, and nobody can make you hand it over without your passphrase. Holding one for someone is safe.
Walkthrough: help someone recover
When someone whose shard you hold runs a recovery ceremony, they will ask you to contribute your piece. The ceremony runs on the requester’s own device: a member’s new phone or laptop, or the station console for the station’s key or its encrypted volume. Whichever it is, it shows a request QR and a ceremony fingerprint.
- Open Settings → Shards you hold → Help someone recover.
- Scan the request the station is showing. The app checks that you actually hold a piece for the identity the request names; if not, it says so and stops.
- Confirm. The app shows the address being recovered and the ceremony fingerprint. Read the fingerprint aloud and check it matches the one on the requester’s screen. The request itself carries no proof of who is asking; the matching fingerprint, between people who know each other, is that proof. A mismatch means someone else is running a ceremony with this address: stop.
- Enter your passphrase. Your phone opens your sealed piece and re-seals it to this one ceremony.
- Show this to your friend. The app displays a response QR. The person running the ceremony scans it. The response is useless to anyone else and for any other ceremony, so if you truly cannot attend, it is acceptable to relay the response over a chat, provided you have confirmed the fingerprint with the requester by voice first.
Walkthrough: rebuild your key on a new phone
Your phone is gone. You have a new one, the app installed, and at least three of your holders reachable, with their phones. Reconstruction happens entirely on your new device; the station never sees your key and cannot help or hinder.
- Tell the operator first if the old phone was stolen rather than lost. They unpair it so a thief cannot keep syncing under your name. Do this before step 2.
- On the new phone, at the welcome screen, tap Recover an existing identity, then From my recovery circle.
- Type your address, the
rrn1…string. Read it off your credential card, a friend’s contact list, or the community list on someone else’s phone. The app checks it is an address. - The app mints a fresh ceremony and shows a request QR and, under it, a short ceremony fingerprint in large type. Every holder must see this same fingerprint.
- Each holder, in person: opens Settings → Shards you hold → Help someone recover, scans your request, and their phone shows the address being recovered and the same fingerprint. They read the fingerprint aloud and you confirm it matches yours. If it does not, stop: someone else is running a ceremony with your address. They enter their passphrase and their phone shows a response QR.
- Scan each response. The app counts pieces gathered. A response from a different ceremony or a stale circle is rejected with an explanation. If you have a piece from everyone and it still will not rebuild, one holder has a shard from an older split; start over with a fresh request.
- When three responses are in, your key is rebuilt. Choose a new passphrase, optionally set up fingerprint or face unlock, and re-pair with the station as on your first day. Your address, balance, standing, and history are exactly as they were.
Leaving the screen part way discards the gathered pieces, and your holders would have to scan a fresh request; finish in one sitting.
Why the fingerprint. A recovery request carries no proof of who is asking. The fingerprint, read aloud between people who know each other, is that proof. A holder who skips it can be tricked into helping a stranger rebuild your key. The design is in ADR-0016.
On a laptop instead: rrn wallet recover --station <station> --address <yours>
runs the same ceremony from a terminal, printing the request QR and the
fingerprint and reading the pasted responses. See
Using a computer instead of a phone.
The station’s own key is protected the same way, with members as holders; the operator’s side is Backups and key recovery.
Walkthrough: export your wallet
The export is your encrypted key, in the same sealed format the wallet keeps on disk. It is useless without your passphrase, and with your passphrase it is you.
- Settings → Export wallet. Enter your passphrase.
- The app shows a block of text and a Copy to clipboard button. The banner says it exactly: “Anyone with this file and your passphrase is you.”
- Move it directly to where it will live, then clear your clipboard and delete any copies along the way. Good homes: a password manager entry, an encrypted drive, a file on a laptop with full-disk encryption. A bad home: a chat thread, an email to yourself, a cloud drive that syncs to every device you own.
- Keep the passphrase with it, but not in the same place. The export without the passphrase is a brick. The two together are your identity.
Repeat the export after you change your passphrase; an old export answers only to the old one.
Restoring from an export
On a new phone: at the welcome screen tap Recover an existing identity, then From an exported wallet, paste the exported text, enter the export’s passphrase, then choose a new device passphrase and re-pair.
On a laptop, into the command-line wallet:
# 1. Turn the exported text back into the wallet file.
base64 -d exported.txt > member.rrnwallet
# 2. Restore it, pinned to your community's station. You will set a
# passphrase for this copy and be asked for the export's passphrase.
rrn wallet init --station rrn1<your-station-address> --restore member.rrnwallet
# 3. Re-anchor before signing anything. The restored wallet refuses to
# sign until it has synced once from the station's network.
rrn wallet sync
The re-anchor step is not optional. A restored wallet does not know how far its own history got, and signing before it finds out would look to the station like a forked identity, which costs your whole standing. The app does the same on its first sync after a restore.
Rehearse this once, on a laptop, while your phone still works. A backup you have never restored is a hope, not a plan.
If your phone is lost or stolen
- Tell the operator. They unpair the old phone so it can no longer sync. The key on it is still encrypted under your passphrase; a thief needs that to use it.
- Rebuild on a new phone from your circle or your export, as above, then re-pair. Your identity carries over intact.
- Do not create a fresh wallet on the new phone hoping to merge later; you cannot. A fresh wallet is a new identity starting from zero.
Housekeeping that matters
- Updates: install a newer app over the old one. Never uninstall first; that erases the wallet on the phone.
- Factory reset in Settings erases the wallet and every shard you hold for others. It is for handing the phone on, not for troubleshooting.
- Change passphrase is in Settings. Re-export afterwards.
If you use the command-line wallet
Back up the whole wallet directory (~/.railroad/wallet by default), not
just the key file. The outbox and its position live beside the key, and a
backup of the key alone loses the chain. Full-disk encryption on the laptop
is your responsibility. After restoring from a backup, reach the station once
and run rrn wallet sync before you sign again.
The command-line wallet can rebuild from a circle (rrn wallet recover)
but cannot yet set one up; you split your key from the phone app. That
surface is deferred in
ADR-0028.
When the network is down
Railroad Network was designed for the day the Wi-Fi, the internet, or the power is gone. The rule is simple: keep signing. A member’s device does not need the station to sign a payment. It needs the station to settle one, and that can happen later.
Where this stands today. The design below is fully built on the station side and in the command-line wallet, and it is what the project’s 72-hour outage simulation exercises. The phone app does not do it yet: when the app cannot reach the station it refuses to send, confirm, vote, or contest, tells you so, and you try again when connected. Nothing is queued on the phone. The Rust core behind the app already has the outbox, certificate, and bundle functions; the screens are not written. Until they are, a phone member’s offline options are to wait, or to trade on paper through a member with a laptop. The rest of this page describes the mechanism as it exists, so you know what a receipt or a certificate is when you meet one.
What actually happens
When a device cannot reach the station, anything it signs goes into an outbox. It is still a real, signed record; it is just undelivered. When the station is reachable again, by any route, the outbox drains, the station checks each record, and sends back a receipt saying whether it was admitted. Settlement windows start counting from arrival, not from when the record was signed, so a delayed record can never shortcut the window in which it can be contested.
Routes a record can take:
- Wi-Fi, later. Walk back into range and the wallet delivers it by itself
(
rrn wallet submiton a laptop). - Paper. Export the outbox as QR codes, print or photograph them, and a courier carries the sheet to the station’s courier desk. Receipts come back the same way.
- Radio. Where the community has set up LoRa radios, a bundle hops over the air with no infrastructure. See Radio and text message.
- Text message. Designed and tested, but the physical gateway is not built. Do not count on it today.
A courier needs no trust. Every record is signed by its author and re-checked by the station. A courier can lose or delay what they carry. They cannot forge or alter it. Sending the same record twice is always safe: the station recognises it and never admits a payment twice.
A receipt is proof. The absence of one is not proof of anything. Keep re-sending until the receipt comes back.
Before the storm: headroom certificates
Here is the one problem offline cannot solve by itself. When you pay someone offline, they cannot see your balance, so they cannot know whether the payment will clear the debt floor when it finally arrives.
A headroom certificate fixes that. While you are still connected, you ask the station to reserve part of your credit headroom, say 10 Commons. The station signs a certificate saying so. Later, offline, you pay against it: the receiver checks the certificate is genuine, is yours, has room, and has not expired. They can hand over the goods knowing the credit was already set aside. The command-line wallet requests, holds, and spends against certificates and writes a spend voucher the receiver can verify without the station; the operator can also print a certificate as a wallet card.
The trade is that reserved headroom is idle headroom. A 10-Common certificate is 10 Commons you cannot spend online until it expires or you return it. So the habit is: reserve before a market day, a storm warning, or a trip up the valley; return what you did not use when you are back.
Defaults a community can change (see Configuration):
| Rule | Default |
|---|---|
| Most one certificate can reserve | 10 Commons |
| Live certificates one member may hold | 4 |
| How long a certificate can be spent against | 7 days |
| How late a spend against it may arrive and still count | 14 days |
The one thing you must not do
A certificate holder could try to spend the same certificate twice with two different people, each of whom can only see the spends the payer chooses to show. The cap on the certificate bounds what the community can lose. When both records reach the station, the second is refused and the double-spend is recorded as provable equivocation: your standing drops to nothing, you can issue no new certificates, and a jury case opens.
This is the one fraud the system cannot prevent offline, only price. The price is your whole standing. The rules are locked in ADR-0021 and ADR-0025.
What the station does meanwhile
Nothing settles, no window runs, and no vote closes while the station is unreachable; it is the one thing that writes the community’s history, by design (ADR-0020). Members’ outboxes preserve everything signed. When the station comes back, whether from a power cut or from a restored backup, the outboxes replay and the history continues. How a community rehearses all this is Running an outage drill.
The marketplace
The marketplace is where the economy becomes daily life: what people have, what people need, and the conversation that turns one into the other. It is built into the app and the command line, and like everything else it is a set of signed records in the community’s history.
Three surfaces
| Surface | What goes there | Example |
|---|---|---|
| Goods | Physical things, counted | Eggs by the dozen, a spare bike wheel |
| Services | Time and skill, one-off or recurring | An afternoon of fencing, weekly childcare |
| Commons | Community-pooled resources at low or no cost | The tool library, the shared hall |
A Commons listing may carry a negative price: a subsidy, where the community pays members to take part. Nothing else can.
Every listing has a category from a fixed list (agriculture, construction, education, food, medical, tools, transportation, other) so that needs can be matched to offers without guessing at words.
Offering something
A listing is a signed offer: a surface, a category, a title, a description, a price in Commons, availability, and optionally an expiry and requirements. Requirements let you say that only members above a certain standing, or only members of this community, may take it up.
After publishing you can change the price, description, availability, and expiry. You cannot change the surface, category, title, or requirements; those are what the listing is. Close a listing when it no longer applies.
A listing’s track record is not something you write. The count of completed trades against it is computed from settled payments that reference it, so nobody can claim a history they do not have.
Looking for something
You can browse the listings by surface, category, free text, price cap, and the provider’s minimum standing. Or you can post a need: a category, a quantity, the most you will pay, and how long the need stands. The station then shows you the matches, the listings that answer it, and keeps matching as new listings appear.
Agreeing a price
An inquiry is the conversation between a buyer and a listing’s provider. The buyer opens it, accepting the listed price or making an offer, with a message. Either side replies, with a counter-offer or without. The provider closes it as agreed, at a price, or declined.
The inquiry is the place to sort out details: when, where, how many. Nothing moves until it is agreed.
Paying for what you agreed
Once an inquiry is agreed, the buyer settles it. That signs a payment at the agreed price, linked to the listing, which the provider then confirms like any other payment. It settles after the usual window and appears in both members’ history naming the listing it paid for. See Pay and get paid.
Only the buyer can settle an inquiry, and settling it twice returns the same payment rather than paying twice.
Recurring services
For a service that repeats, such as weekly deliveries or a standing patrol, the buyer signs a contract from an agreed inquiry instead of a one-off payment. The contract records the terms; charges against it are payments linked to the contract as they fall due. Either party can end a contract early.
Rules of the road
- Describe honestly. A listing is signed by you and lives in the history forever. So does a dispute over it.
- Link the payment. Settling from the inquiry, rather than paying the provider directly, records what the payment was for. That is what a jury sees if it is ever contested.
- Standing requirements cut both ways. You can require a minimum standing of buyers; providers with little standing will find fewer takers.
- Not visible across communities yet. Listings are local until federation arrives.
From the command line
Members using the command-line wallet
and operators at the station console have the same marketplace under rrn:
list, browse, need, matches, inquire, inquiry-reply,
inquiry-close, settle-inquiry, and contract. The
command reference has every option.
The data model is locked in ADR-0010.
Contest a payment
Something went wrong with a trade: the goods never came, the work was not done, the amount is not what you agreed, or you never made the payment at all. If the payment is still in its settlement window, you can contest it. This page is the member’s side; how the jury itself works is under Disputes.
When you can
Only while the payment is confirmed but not yet settled: inside the 24-hour window for a Tier 1 payment or the 48-hour window for Tier 2. After settlement the payment is final, and the only remedy is a new payment the other way, agreed between you.
Either party can contest: the payer or the receiver.
How
In the app, open the payment from your transaction history and choose
Contest. From the command-line wallet it is rrn wallet dispute. You
write a short reason. If you have evidence held somewhere else, such as
photos or a message thread, you can attach its fingerprint so it can be
checked later.
Contesting freezes that one payment. Nothing else about your account changes.
If someone contests yours
You are notified in the app and may file a response. You do not have to. The jury sees the reason, your response if any, and the payment itself. Keep your response factual; it is signed by you and lives in the history.
What happens next
The software draws three jurors by lot from the established members, weighted by standing and never including either party. Each rules to uphold the dispute or reject it. Majority wins:
- Upheld. The payment is voided and balances do not move. The member who confirmed it loses some standing, because confirming was their claim that the trade happened.
- Rejected. The payment settles as normal.
- Nobody rules in time. The dispute lapses and the payment settles. Every path fails toward the status quo, so a dispute can never freeze a payment forever.
If you disagree with the ruling you can appeal to the whole electorate of established members, which suspends the ruling while they vote. If no jury can be seated at all, a party can escalate straight to the electorate. Both run on bounded windows and lapse the same way.
If you are drawn as a juror
You may be drawn for a dispute you have nothing to do with. The app tells you, shows you the payment, the reason, and any response, and asks for your verdict before the window closes. Rule on what you can see. There is no obligation to investigate, and no penalty for how you rule, but a panel that does not reach a majority in time lets the dispute lapse, so do not sit on it.
Good habits
- Look at pending payments each day while the community is new. The window is short by design.
- Contest, then talk. Contesting costs nothing and preserves your position. Most disputes get resolved by the two people before the jury does anything, and a rejected or lapsed dispute has no consequence for the person who raised it.
- Confirm carefully in the first place. Confirming is the moment you put your standing behind the trade. Nearly every upheld dispute traces back to a confirmation that should not have happened.
Using a computer instead of a phone
Not everyone has an Android phone. A member with a laptop or desktop can hold
their own key with the command-line wallet, rrn wallet, and take part in
the community from a terminal. It is the same identity model as the phone: the
key is created on your machine and never leaves it, the station records what
you sign, and nobody can act as you without it.
Today this is also the most capable member device when the network is down. The command-line wallet can sign payments with no station in reach, keep them in an outbox, print them as QR sheets for a courier, and spend against a headroom certificate. The phone app cannot do any of that yet: it needs the station reachable to send or confirm a payment. See When the network is down.
Who this is for
- A member without a smartphone, or with an iPhone (which the app does not support for pilots).
- A member who wants a second, offline-capable identity on a laptop. Note that this is a separate identity from a phone wallet; one key must live on exactly one device (below).
- Anyone who wants to rehearse the paper path before the storm.
You need to be comfortable typing commands and reading their output. Every
command is short, and rrn wallet <command> --help explains each one.
Install
rrn is the same program the operator uses at the station console. Either the
operator hands you a copy of the binary, or you build it yourself from the
station repo with the Rust
toolchain from rustup.rs:
git clone https://github.com/railroad-network/station.git
cd station
cargo build --release -p rrn-cli
sudo cp target/release/rrn /usr/local/bin/
Linux and macOS are supported.
Two settings before you start
The wallet takes its passphrase from the environment or from a hidden prompt. It never accepts it on the command line, where it would land in your shell history.
export RRN_WALLET_PASSPHRASE='a passphrase you choose' # or leave unset and be prompted
export RRN_WALLET_HOME=~/.railroad/wallet # the default; set it to keep the wallet elsewhere
The wallet home directory holds your encrypted key file (member.rrnwallet),
a small database with your outbox and its cursors (wallet.db), and the
station pin. Treat the whole directory as your identity.
Create the wallet, pinned to the station
Ask the operator for the station’s rrn1… address, in person. The wallet
pins that address, and from then on every station-signed thing it accepts
(receipts, certificates, the pairing reply) is checked against the pin. That
hand-off is this path’s security boundary, exactly as the pairing-code
comparison is for a phone. Read it off the operator’s screen; do not accept it
over email.
rrn wallet init --station rrn1<the-station-address>
This prints your own new rrn1… address. Write it down; you will read it to
people who want to pay you or vouch for you.
Pair, then sync
Pairing is the same short ceremony a phone goes through, over the same sealed channel. Be on the station’s network and next to the operator:
rrn wallet pair --url 192.168.4.1:7500 # the station's address and port, from the operator
The wallet prints an 8-character code. The operator sees a code beside your address in their list of pending requests. Compare them aloud. If they match, the operator confirms your address and the wallet is paired. If they do not match, stop: something on the network answered in the station’s place.
Then:
rrn wallet sync
Sync pulls your nonce, the position of your outbox as the station knows it, your balance, and any delivery receipts waiting for you. Run it whenever you are back in range. It is mandatory after any restore or recovery (see below), because a wallet that does not know how far its own history got would sign a record the station reads as a forked identity.
Everyday use
| You want to | Command |
|---|---|
| See your address, pairing, and how many records are pending | rrn wallet status (never unlocks the key) |
| Pay someone | rrn wallet pay rrn1<receiver> 3.50 --memo "eggs" |
| Confirm a payment addressed to you | rrn wallet confirm <tx-id> |
| Vote on a proposal | rrn wallet vote <proposal-id> yes (or no, abstain) |
| Contest a confirmed payment | rrn wallet dispute <tx-id> --reason "…" |
| Vouch for someone | rrn wallet vouch rrn1<address> --statement "…" --stake <amount> |
| Your transactions, live from the station | rrn wallet transactions |
| Your local outbox and what happened to each record | rrn wallet show (add --all for history) |
Every signing command chains the record into your outbox. Nothing reaches the station until it is submitted or carried there, so the normal online rhythm is: sign, then
rrn wallet submit
which bundles everything pending, sends it over the channel, and applies the
station’s receipts in one go. transactions and sync read live, so they
need the station reachable. Vouching is online only: it is one of the few
things you cannot queue up offline.
Amounts are written in Commons (3, 3.5, 3.50). The debt floor, settlement
windows, and tiers work exactly as they do for a phone; see
Pay and get paid.
The offline loop
When the station is out of reach, sign anyway. A record signed today and delivered next week is admitted next week; its settlement window starts when it arrives.
# Sign. --carrier slow gives the record a two-week validity window instead of
# the normal 24 hours, so it survives a slow delivery.
rrn wallet pay rrn1<receiver> 4.00 --memo "firewood" --carrier slow
# Export what is pending, either as printable QR sheets ...
rrn wallet export qr --out ~/to-carry/
# -> chunk_NN_of_MM.png, a captioned sheet.pdf, and bundle.txt (the raw lines)
# ... or as one raw file for a radio or USB stick.
rrn wallet export bundle --out ~/to-carry/
# -> payload.bundle, for the operator's `rrn dtn push --bundle`
Hand the sheet or the file to a courier: anyone walking to the station. They need no trust. Every record is signed by you and re-checked by the station; a courier can lose or delay it, never alter it. Re-exporting and sending the same records twice is always safe.
At the station, the operator ingests the sheet at the courier desk and prints your delivery receipts for the return trip. When they reach you:
rrn wallet receipts apply --in ~/carried-back/receipts.txt
Each receipt says whether the record was admitted, was already known, or was
refused and why. Until a receipt arrives, the record stays pending in
rrn wallet show. A receipt is proof; the absence of one is not proof of
anything, so keep re-sending until it comes back.
If you come back into Wi-Fi range before the courier does, just run
rrn wallet submit: the station recognizes anything it already has.
Headroom certificates
A receiver who cannot reach the station cannot see your balance, so they cannot know an offline payment will clear your debt floor. A headroom certificate reserves part of your credit while you are still connected, so a later offline payment against it is accepted on arrival. The idea is explained in When the network is down.
rrn wallet cert request 10 # reserve 10 Commons of headroom (needs the station)
rrn wallet cert list # what you hold, with caps and expiries
rrn wallet pay rrn1<receiver> 6.00 --cert <cert-id-or-prefix> --carrier slow \
--voucher-out ~/to-carry/ # also writes rrnspend: lines the receiver can verify offline
rrn wallet cert return <cert-id> # give back what you did not use (a chained record, carried like any other)
rrn wallet cert import --in card.txt # load a certificate from an rrncert: line, for instance a printed card
Reserved headroom is idle headroom: a 10-Common certificate is 10 Commons you cannot spend online until it expires or you return it. Reserve before a market day or a trip; return the remainder when you are back.
Never spend one certificate twice. The station will refuse the second spend when both arrive and record the double-spend as provable equivocation. Your standing drops to nothing and a jury case opens. This is the one fraud the system cannot prevent offline, only price.
Back up the whole directory
Back up the entire wallet home directory, not just the key file. The outbox and its position live beside the key, and a backup of the key alone loses the chain. Full-disk encryption on the laptop is your responsibility: the key file is encrypted under your passphrase, but the decrypted key is in memory while a command runs.
To restore on a new machine:
rrn wallet init --station rrn1<the-station-address> --restore member.rrnwallet
rrn wallet sync # required before the wallet will sign anything
A wallet exported from the phone app restores the same way; see Back up and recover your keys.
One key, one device. Do not copy a wallet directory to a second machine and sign from both. Two records at the same outbox position are equivocation, with the same consequence as a double-spent certificate, and the software cannot stop a determined member from doing it to themselves.
Recover a lost key from your circle
If you set up a recovery circle on your phone and then lost the phone, you can rebuild the key on a laptop. Gather at least three of your holders in person:
rrn wallet recover --station rrn1<the-station-address> --address rrn1<your-old-address>
The command prints a request QR and a short ceremony fingerprint. Read the fingerprint aloud; each holder must see the same one on their screen before they answer, which is what stops someone with a stolen request from tricking your holders. Each holder scans the request in the app under Shards you hold → Help someone recover and shows you a response QR; paste each response line into the waiting command. When enough are in, the key is rebuilt on your machine. Nothing touches the station.
The recovered wallet is treated as restored: it refuses to sign until one
rrn wallet sync re-anchors it. The full member-side story is in
Back up and recover your keys.
What the command-line wallet does not do yet
- No recovery circle of its own. The wallet can rebuild a key from a circle that was set up on a phone, but it cannot yet split its own key and hand out shards. Until it can, keep the directory backup current; that is this wallet’s only self-made safety net. The decision record that scoped the wallet lists this as follow-on work.
- No marketplace, and no proposal authoring. The member verbs are pay, confirm, vote, dispute, and vouch. Listings, inquiries, contracts, and authoring or co-signing proposals are in the app, or in the operator’s console commands for the station’s own wallet.
- No jury duty. If you are drawn as a juror, there is no wallet command to rule with yet.
- One device per key, as above.
The wallet is specified by
ADR-0028,
which extends the “the member device holds the keys” rule of
ADR-0006
from phones to computers. The
rrn command reference lists every
option.
What an organizer does
An organizer is a founder or steward of a community: the person, or the handful of people, who get it started and keep its decision-making healthy. In the software there is no organizer role and no admin button. What organizers have is a founder’s signature on the Charter, standing as established members, and the trust of the room.
That is deliberate. Railroad Network is built on the assumption that the most dangerous failure of a community is capture: a small group turning the rules to their own ends. Every governance mechanism is designed so that doing that is slow, visible, and reversible.
Your work, roughly in order
- Found the community. Choose founders, write the Charter’s principles and rights, and run the signing ceremony. Start a community.
- Get the first members established. Vouch for people you know and trade with them, so the community grows out of bootstrap grace and governs itself.
- Run decisions in the open. Proposals, co-signing, voting, statutes. How decisions get made.
- Let disputes work. Understand the jury, escalation, and appeal so you can explain them and stay out of the way. Disputes.
- Know the emergency lever cold, because it is the sharpest capture lever in the system. Emergencies.
- Practise the outage. Once a season, run the community with the network off. Running an outage drill.
Organizer and operator
Often the same person in a small pilot, but they are different jobs. The operator keeps the station computer running, backed up, and paired. The organizer keeps the community deciding well. This section is the second job. For the first, see For operators.
Do not use this with real value. Run your pilot with play stakes. See Security and audits.
Start a community
A community exists once its Charter is ratified: the founding document that names the community, states its principles and guaranteed rights, and lists its founders. Until then, phones can pair and look around, but governance and disputes have nothing to stand on.
This page is the organizer’s half. The operator’s half, standing up the station and pairing phones, is Run a station.
Choose your founders
Founders matter beyond ceremony. Standing is earned through vouches and trade, which takes time. To avoid a dead zone where nobody can vote or sit on a jury, a young community runs in bootstrap grace: while fewer than three members are established, the electorate is the founders plus whoever is established. Your founders are the functioning government of the early community.
Three to five trusted people is a good shape for a pilot of around twenty. Fewer, and one absence stalls everything. More, and the ceremony drags.
Write the Charter
The Charter has three kinds of content:
- A community id. A short, stable name like
maple-street-commons. - Principles. What the community is for. “Mutual aid before profit.” “Decisions in the open.”
- Rights. What no vote can take away from a member. “Any member may call a vote.” “Any member may contest a transaction.”
Keep it short. A Charter is hard to change on purpose: amendments chain to the version they replace, need a higher bar than ordinary rules, and are frozen entirely during an emergency. Put durable values here and leave details to statutes.
Run the signing ceremony
There are two ways to found. Use the second.
Solo bootstrap. One command at the station makes the station’s own wallet the sole founder. Quick, and fine for a technical trial. But it makes the operator the whole grace-period electorate, which is not a founding story a real community should tell.
The founding ceremony. Founders keep their keys on their own phones and
sign there. The operator opens the ceremony from the station, declaring the
community id, principles, rights, and each founder’s rrn1… address. Each
founder then opens the app, Community → Governance, sees “Sign the founding
charter”, reads it, and taps Sign. The Charter publishes by itself once
three quarters of the declared founders (rounded up) have signed.
Read the Charter aloud before anyone signs. It must say exactly what was declared; a founder who finds a typo should refuse and the ceremony is reopened.
The exact commands are in the operator’s runbook under Found the community.
The first weeks
- Vouch for everyone you actually know, and encourage members to vouch for each other. Vouches plus trade are what lift people to established, which is what ends bootstrap grace.
- Trade something real early. A community with an empty ledger has no standing anywhere. Even play-stake trades build the history that governance depends on.
- Set up social recovery, both for members and for the station. The operator arms the station’s recovery with a few trusted members as holders. Founders are natural candidates.
- Read the phase warnings. This is pilot software with no independent audit. Say so in your first meeting.
How decisions get made
Everything a community decides is a signed record in the same history as its payments. There is no separate admin database and nothing an operator can change by hand. This page is the plain-language version of the governance design; the authoritative rules are in the decision records linked at the bottom.
The layers
The Charter. The founding document: principles, rights, founders. Changing it is a formal amendment that chains to the version it replaces, so treaty partners in a future federation can verify they are still dealing with the same community. Amendments need a higher bar than ordinary rules.
Statutes. The community’s ordinary laws, passed through the normal proposal process. A statute cannot contradict the Charter.
Precedent. Jury rulings in disputes build up a record of how the rules are read in practice.
Who decides
The electorate is the established members: those whose computed standing is high enough and whose identity is anchored by vouches. Standing is derived from the history by one formula that no community can tune, so nobody can be minted into the electorate by fiat.
Bootstrap grace
A brand-new community has no established members. Rather than leave it unable to govern, the software runs a grace: while fewer than three members are established, the electorate is the founders plus whoever is established. The app shows a banner while grace is on. It ends by itself the moment a third member becomes established, and it never comes back.
The life of a proposal
- Propose. Any member authors a proposal: a title, a body, and a kind (an ordinary statute, a Charter amendment, or an emergency measure).
- Co-sign. Other members endorse it. A proposal needs enough co-signers before it goes to a vote; this filters noise without letting anyone veto.
- Vote. Established members cast ballots during a deliberation window, about a week for ordinary proposals.
- Tally and enact. When the window closes, the station tallies the votes from the history. A passed statute is in force; a passed amendment becomes the new Charter.
Every step is a signed record. Anyone with the history can recompute the result, and a station cannot fake a tally it did not derive.
Which clock counts
Phones sign proposals and ballots with their own clocks, which may be wrong or lying. The station therefore anchors every window on the admission clock: the time the station admitted the record, not the time the author claimed. A ballot that arrives after the window closed does not count, no matter what time is written on it.
What organizers should watch for
- Quorum by attrition. If members drift away, the electorate shrinks and a few people can pass anything. Keep vouching and trading so the established pool stays broad.
- The emergency lever. It exists to decide faster in a crisis and does nothing else, but it is the sharpest tool in the box. Read Emergencies before you ever need it.
- Charter creep. If you find yourselves amending the Charter often, the content belongs in statutes.
Where the rules are written down
- ADR-0012: the Charter and how it changes.
- ADR-0015: bootstrap grace.
- ADR-0009: the one standing formula.
- ADR-0022: the admission clock.
Disputes
A payment can be contested while it is confirmed but not yet settled. This page explains what happens next, so organizers can explain it to members and then stay out of the way. The jury is designed to work without anyone running it.
Raising a dispute
Either party to a confirmed payment can contest it during its settlement window, from the app or the command line. Contesting freezes settlement of that one payment; nothing else is affected. The disputer writes a short reason and may attach a fingerprint of evidence held elsewhere.
The other party may file a response. Neither is required to.
The jury
The software draws three jurors by lot from the established members, weighted by standing, excluding the parties. The draw is deterministic from the history, so anyone can verify that the panel is the one the rules produce and not one somebody picked.
Jurors are notified in the app. Each casts a verdict: uphold the dispute (the payment is voided) or reject it (the payment stands). Majority rules.
Every path fails open
There is a bounded window for the jury to rule. If it closes without a majority, the dispute lapses and the confirmed payment stands. Nobody can stall a payment forever by contesting it, and nobody can block a ruling by refusing to serve. The default outcome is always the status quo.
What an upheld dispute does
- The pending transfer is voided. Balances do not move.
- The member who confirmed the payment takes a hit to their standing. This is the point of the settlement window and the reason confirming is a serious act: it is where the “we both say this trade happened” claim gets tested.
Escalation and appeal
Two paths lead from the jury to the whole electorate, both optional:
- Escalation, when a panel cannot be seated (too few eligible members, for instance). A party asks the electorate to decide directly.
- Appeal, when a party disagrees with the jury’s ruling. It suspends enactment of the ruling while the electorate votes.
Both use the same bounded windows and fail open the same way.
Equivocation cases
One kind of case is opened by the station rather than a member: provable equivocation, when two conflicting records signed by the same member reach the station, for instance a headroom certificate spent twice offline. The member’s standing drops to nothing immediately, and a jury case opens with the evidence attached. These cases default to lapsing if the jury does not act, like every other.
What organizers should do
- Explain the window. Most bad outcomes are a member who noticed a problem after settlement, when it was final.
- Do not lobby jurors. The whole design is that the panel is random and the reasoning is theirs.
- Read the rulings. Precedent is how a community learns what its own rules mean.
The authoritative rules are ADR-0014 and ADR-0025.
Emergencies
In a real crisis a week-long deliberation window is too slow. Emergency governance lets the community decide faster, for a narrow class of temporary measures, and deliberately does nothing else. It is the sharpest capture lever in the system. Know exactly what it does before you touch it.
Declaring one
An emergency does not exist because someone says so. A member signs a declaration with a reason and a requested duration, and it activates only once co-signatures from distinct members of the electorate reach two thirds of the electorate (the author’s own counts). One person, or a bare majority, compresses nothing.
A declaration that never reaches the threshold expires after seven days.
What it changes
Exactly one thing: the deliberation window for emergency-kind proposals admitted while the declaration is active, compressed to 24 hours. That is the floor; a Charter cannot set it lower. Ordinary proposals keep their normal window.
Emergency-kind proposals also have a higher approval bar than ordinary ones, and that bar does not drop.
What it freezes and pins
- The Charter is frozen. No amendment and no replacement founding charter can be admitted or enacted while the emergency holds. The whole constitution, not one door.
- The electorate is pinned at the moment of activation. Nobody can be minted into it, or lose standing out of it, mid-crisis.
What it never touches
Settlement windows, dispute windows, the debt floor, headroom certificates, standing. A flood does not authorize economic restructuring.
How it ends
By itself. The defaults:
| Rule | Value |
|---|---|
| Default duration | 72 hours |
| Longest single declaration | 7 days |
| Longest chain of renewals | 14 days, each renewal needing the full two thirds again |
| Cooldown after a chain ends | 14 days |
Every measure passed under an emergency carries an enforced expiry. If the community wants it to last, it goes through the ordinary process afterwards. The community can also lift an emergency early with the same two-thirds threshold.
Afterwards
The station produces an emergency report: who declared, who co-signed, and every measure passed under it. Read it together when it is over. The design relies on that review being possible, not on it being compulsory, so make it a habit.
What organizers should do
- Rehearse once, in a drill. The first time you declare an emergency should not be during one.
- Name the reason honestly in the declaration. It is permanent.
- Watch the renewals. Two renewals back to back is a pattern, not a crisis.
The authoritative rules are ADR-0023 and ADR-0027.
Running an outage drill
The software’s version of a 72-hour outage runs in seconds, in a test that stands up a real station and about twenty simulated members, cuts the network, trades over couriers, paper, and a lossy radio model for three simulated days, and checks that everything reconciles. The community’s version takes a half-day and real people, and it is the only way to learn where your community actually breaks: which holders cannot be found, which courier gets lost, which merchant will not take an offline payment. Run it before you need it. This page is for the person facilitating.
What the phones can and cannot do today. The runbook this drill comes from assumes members sign offline on their phones. The phone app cannot do that yet: it needs the station reachable to send or confirm a payment, and it has no outbox, no certificates, and no paper export. The only member device that does all of that is the command-line wallet on a laptop. So during the cut, offline trade happens on laptops and on paper at the courier desk, and phone members play the part of the people who were out of range and come back when the network does. That is still most of the drill, and it is an honest picture of a pilot today.
Why drill
The mechanisms that carry a community through an outage are all things people do: reserve a certificate before the storm, carry a sheet to the steward, confirm a fingerprint out loud, keep re-sending until the receipt comes back. None of them is exercised by ordinary trading on Wi-Fi. A drill is the only time they get practised, and the debrief is where the community writes down what surprised it.
What you need
- The station on a UPS or battery, backed up that morning (
station backup). - A printer and a phone with any QR-scanner app, at the courier desk.
- At least one or two members with
rrn walleton a laptop, set up and paired before the day. Two is better: they can pay each other. - Optionally a LoRa pair, if the community has brought radios up.
- Paper and pens for the log sheet.
- About 8 to 20 members and four hours.
- Play stakes. This is pilot software.
If you shorten the settlement window for the drill so settlements land the same afternoon, do it in the station’s configuration beforehand and put it back afterwards; see Configuration.
Roles
Assign before the day:
| Role | Does |
|---|---|
| Facilitator | Runs the clock, calls the phases, keeps the log sheet. |
| Steward | At the station console the whole time: ingest, export receipts, status. Usually the operator. |
| Two couriers | One on foot; one “slow”, who deliberately delays and reorders what they carry. |
| Laptop members | One or two people with the command-line wallet. They are the offline economy. |
| A merchant and a customer or two | Trade for real, with real goods. Lunch works. |
| A holder quorum | If the station runs the encrypted profile, the key-holders who unlock it. |
| One adversary | Briefed privately; see below. |
| Everyone else | Phone members who transact while online, vote, watch the banners, and try to break things. |
Timeline
| Time | Phase | What happens |
|---|---|---|
| Day before | Prepare | Laptop members reserve a headroom certificate (rrn wallet cert request) while online. The steward prints credential cards for anyone who wants one and a certificate card for the station’s own wallet. The facilitator briefs the adversary. |
| 0:00 | Normal | Fifteen minutes of ordinary trade on Wi-Fi, phones included. The steward notes a few balances and the length of the log. |
| 0:15 | Cut | Turn off the Wi-Fi access point. Announce it. Phones now show offline; their members note what they would have done and stop. |
| 0:15 to 2:15 | Outage | Laptop members trade offline: a certificate-backed payment to the merchant, a plain payment or two, a confirmation. They export to QR sheets. Couriers carry sheets to the steward, who ingests them and hands back receipt sheets. The slow courier holds one sheet back deliberately. One laptop “dies” (close the lid) after signing. If you have radios, push at least one bundle over LoRa. |
| 1:15 | Emergency (optional) | Declare a drill emergency from the console and co-sign it in person with the electorate present; pass one temporary measure through the compressed window. Watch the banner appear on phones when they reconnect. See Emergencies. |
| 2:15 | Power loss (encrypted profile only) | Pull the station’s power. Convene the holders and run the unlock ceremony; read the fingerprint aloud. Time it. See Encrypted at rest. |
| 2:45 | Reconnect | Wi-Fi back on. Phones sync and their members make the payments they were holding. Laptop members run rrn wallet submit. The slow courier finally delivers. |
| 3:00 | Reconcile | The steward reads every receipt outcome aloud: admitted, known, or refused and why. The adversary reveals what they tried. |
| 3:30 | Settle and debrief | Wait out the settlement window if you shortened it, or read the pending list. Debrief. |
The adversary’s brief
Choose two or three. The software should catch every one; the drill is to see whether the people notice.
- Spend one certificate twice, to two different receivers. Expect the second refused as an overspend when both arrive, and the member’s standing gone. Use a throwaway identity, or accept the consequence.
- Hand a courier a sheet with one page missing. Expect the rest to land and the author to re-send the missing record later.
- Scan the same sheet twice. Expect the same receipt and nothing double-counted.
- Edit one character of a QR payload line before ingest. Expect it refused with a bad-signature reason.
- Try to co-sign the emergency declaration from an identity that is not in the electorate. Expect it refused.
The log sheet
Record, per event: the time, who, what carrier, and the receipt outcome. Afterwards verify with the steward:
- Every value is conserved. Balances across members still sum to zero. The software test checks exactly this; you check it on paper.
- No record was silently lost. Everything on the log sheet has a receipt or a known re-send.
- Exactly the planted double-spend was flagged, and nothing honest was.
- No settlement happened before its window elapsed from arrival at the station, not from when the record was signed.
- The unlock ceremony completed with the holders you actually had, in the time you actually took.
What you are really testing
Not the software. You are testing whether everyone knows how to reserve a certificate; whether the couriers can find the steward; whether the holders answer the phone; whether the merchant trusts an offline payment from a laptop they cannot check against the station; whether phone members understand that “offline” means “wait”, not “lost”. Write down what surprised you and fix the people-side before the storm.
The debrief
Sit down together while it is fresh:
- Read the receipt outcomes and the emergency report aloud.
- For every refusal, ask whether the person understood why at the time.
- For every delay, ask who was waiting and what they did in the meantime.
- Decide one thing to change: a second courier, a printed “how to reserve” card, a UPS, a holder who lives closer.
- Put the settlement window and any other drill settings back, take a fresh backup, and schedule the next drill. Once a season is about right.
The software’s own simulation, what it verifies, and what it deliberately leaves to a drill like this one are described in the 72-hour outage simulation. The steward’s fuller checklist, with the exact console commands, is Part 6 of the operator runbook.
What an operator does
The operator keeps the community’s station running. The station is an ordinary computer, often a Raspberry Pi, that holds the community’s ledger and is the one thing that writes to it. Phones talk to it over the local network. If it is down, members keep signing but nothing settles until it is back.
The job in one sentence: keep it running, keep it backed up, and keep the pairing ceremony honest.
What you need
| Thing | Details |
|---|---|
| A machine | Any always-on Linux or macOS box: a Raspberry Pi 4 or 5 with a 64-bit OS and 4 GB or more of RAM, a spare laptop, a mini-PC. |
| A network | A Wi-Fi network all members’ phones can join. Give the station a fixed address on it if you can. |
| The software | Built from source, once. Ten to thirty minutes. |
| Two safe places | For the passphrase and backups. A fireproof folder at home plus a sealed envelope with a trusted member. |
The pages
- Run a station: build, initialize, configure, run, survive reboots.
- Pair phones and wallets: the in-person ceremony that is the actual security boundary.
- Backups and key recovery: the fifteen minutes that save the community.
- Encrypted at rest: the seizure-resistant profile, Linux only, and the boot ceremony it costs.
- The courier desk: ingesting paper, printing receipts and cards.
- Radio and text message: the Reticulum sidecar, LoRa bring-up, and where SMS stops today.
- Keeping phones in sync: the per-vendor battery traps and the ten-minute drill.
- Troubleshooting: symptom, cause, fix.
The exact values every setting takes are in
Configuration. The source runbooks these
pages are drawn from live in the station repo and go into more detail:
Setting up a Railroad Network community,
Keeping phones in sync when the app is closed,
and Bringing up a LoRa radio link.
Honest warnings
- Research-stage software. No independent audit. Play stakes only.
- One station, one network. No federation and no relaying over the internet yet. Your community is one station plus the phones that can reach it.
- Phone traffic is plain HTTP, on purpose. Every message is encrypted and signed end to end, so the transport needs no TLS. The security ceremony that matters is the pairing-code comparison. Take it seriously.
- Android only, sideloaded. You hand members a signed installer file.
- The phone app is online-only for signing. Members with the app need the station reachable to send, confirm, vote, or contest. The offline outbox, certificates, and paper export ship today in the command-line wallet; plan your outage drill around laptops and paper.
Run a station
This page gets a station from nothing to serving phones. It follows the
operator runbook in the station repo, which has more detail on every step.
1. Build the software
Install the Rust toolchain from rustup.rs, then:
git clone https://github.com/railroad-network/station.git
cd station
cargo build --release -p rrn-station -p rrn-cli
This produces two programs in target/release/:
station, the daemon. Holds the wallet and ledger, talks to phones. This is the thing that runs forever.rrn, the command-line client. Your admin tool. It talks to the running daemon over a local socket and only works while the daemon is up.
Put both on your PATH, for example with
sudo cp target/release/{station,rrn} /usr/local/bin/.
2. Initialize
station init
You are prompted twice for a wallet passphrase. Choose it carefully. It protects the station’s identity key and encrypts every backup, and you type it each time the station starts.
Write the passphrase down, on paper, in two places. A passphrase that exists only in one person’s head has already been lost twice in this project’s own history. Key recovery builds a net for a lost passphrase, but only if you set it up first.
init creates the data directory, ~/.railroad/station by default, and
prints the station’s rrn1… address. That address is the station’s
identity.
| File | What it is |
|---|---|
wallet.rrnwallet | The identity key, encrypted under your passphrase. Irreplaceable. |
station.db | The community ledger. Irreplaceable. |
paired_mobiles.json | Which phones are paired. Losing it means re-pairing everyone. |
config.toml | Settings. |
station.sock, marketplace_index/ | Runtime scratch, rebuilt automatically. Never back these up. |
3. Configure
config.toml in the data directory. The defaults suit a single-community
pilot. The section you may want to touch is [mobile]:
[network]
listen = "127.0.0.1:7400" # station-to-station port: loopback-only is correct today
role = "writer" # this station owns the community's log (the default)
[mobile]
listen = "0.0.0.0:7500" # where phones connect; 7500 is what the app expects
advertise = true # announce the station on the LAN so phones find it by name
# name = "Railroad Station — Maple Street" # optional friendly name shown on phones
Two practical notes:
- Firewall. Allow inbound TCP 7500 from your LAN. Port 7400 should stay unreachable from other machines.
- Advertising.
advertise = truemakes the station appear in the app’s “Join your community” list. If your network blocks mDNS, members type the station’s address and port by hand instead.
Your station is a writer: it owns the log and there is exactly one per community. A writer has no peers. You can optionally run a second station as a read-only replica for off-site audit; it can never write, so it can never fork the community. The runbook covers it.
4. Run
station run
It prompts for the passphrase and serves until stopped. Check it is alive from the same machine:
rrn whoami # the station's rrn1… address
rrn history # the ledger log, empty at first
Surviving reboots
station run also reads the passphrase from the RRN_PASSPHRASE environment
variable. Never write the passphrase on a command line or in a script; it
lands in shell history. On Linux, a minimal systemd unit reads it from a
root-only file:
# /etc/systemd/system/rrn-station.service
[Unit]
Description=Railroad Network station
After=network-online.target
Wants=network-online.target
[Service]
User=railroad
EnvironmentFile=/etc/railroad/station.env # RRN_PASSPHRASE=…; chmod 600, owned by root
ExecStart=/usr/local/bin/station run
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now rrn-station
Keeping the passphrase on disk readable only by root is an accepted pilot-grade trade so the station comes back by itself after a power cut. If your community does not accept it, start the station by hand after each reboot.
On macOS, fetch it from the keychain at the moment of use:
security add-generic-password -a "$USER" -s rrn-station-passphrase -w # once
RRN_PASSPHRASE=$(security find-generic-password -s rrn-station-passphrase -w) station run
Next
Pair phones and wallets, then found the community with your organizers, then, the same day, Backups and key recovery.
Pair phones and wallets
Pairing is a short in-person ceremony between a member and you. It proves to the phone that it is talking to the real station, and to the station that this phone is welcome. The code comparison is the actual security boundary of the whole system, so do it face to face, reading the code aloud.
A phone
On the phone. After creating a wallet, the member taps Join your community. The station appears by name, or they tap Add by address and type the station’s IP and port 7500. They unlock; the phone shows an 8-character code.
On the station:
station pair-mobile
lists the pending requests, each with a code and the phone’s rrn1… address.
Together. Compare the code on the phone with the one the station printed. If, and only if, they match:
station pair-mobile <the-phone's-rrn1-address>
and the member confirms on their phone. The phone now syncs, receives push updates, and can transact.
If the codes do not match, refuse. Something on the network answered in the station’s place. Find out what before pairing anyone.
Housekeeping over the community’s life:
station list-mobiles # who is paired
station unpair <rrn1-addr> # revoke a lost or departed member's phone
A computer instead of a phone
A member with a laptop and no Android phone can hold their own key with the
built-in rrn wallet. Same identity model, driven from a terminal, and unlike
the app today it works offline. The member’s side of this is
Using a computer instead of a phone.
They learn the station’s address from you, in person. The wallet pins that address, and every station-signed thing it later accepts is checked against the pin. That hand-off is this path’s security boundary, exactly as the code comparison is for a phone. Read the address to them; do not email it.
On the member’s laptop:
export RRN_WALLET_PASSPHRASE='something the member chooses' # never on a command line
rrn wallet init --station rrn1<your-station-address> # prints their new rrn1… address
rrn wallet pair --url 192.168.4.1:7500 # shows an 8-character code
Confirm the pair exactly as for a phone: compare the code the wallet prints
with what station pair-mobile lists, then confirm their address. The member
then runs rrn wallet sync to pull their nonce, balance, and receipts.
Two things the member must understand:
- Back up the whole wallet directory (
~/.railroad/walletby default), not just the key file. The outbox and its cursors live beside the key. - Full-disk encryption is their responsibility. The wallet encrypts its key file, but the decrypted key is in memory while a command runs.
After restoring from a backup they must reach the station once and sync before signing again, so their chain cannot fork.
After pairing
Nudge every member through two things in the first week: the recovery circle once there are a few members to hand shards to, and Export wallet (Settings). Either one rebuilds a member’s key on a new phone; see Back up and recover your keys. A lost phone without either is a lost identity, and helping members avoid that is operator work too. Then walk the phone through Keeping phones in sync before the member leaves.
Backups and key recovery
The station directory holds the only copy of your community’s history and the key that is its identity. Three failures will eventually visit any long-running community: the machine dies, the passphrase is lost, or both at once. Each has a prepared exit, but only if you prepare it before the failure. Both preparations together take about fifteen minutes. Do them the day you found the community.
Backups
station backup
Safe to run while the station is serving. It verifies your passphrase, then
writes a single encrypted archive, station-backup-<timestamp>.rrnbak,
bundling the wallet, the ledger, the paired-phones list, and the config. Use
--out to choose where.
Because the archive is encrypted it is safe to copy anywhere: a USB stick in a drawer, another machine, a cloud drive. What is not safe is keeping the only copy on the station itself.
A pilot-grade routine:
-
Weekly, and before any software upgrade, run a backup and copy it off the machine.
-
Keep the last three or four, not just the newest.
-
Rehearse the restore once, early, into a scratch directory, so you know it works and the passphrase does too:
station restore station-backup-<timestamp>.rrnbak --data-dir /tmp/restore-drill rm -rf /tmp/restore-drill
To restore for real after losing a machine: build the software on the new machine, then
station restore <archive>
station run
Restore refuses to overwrite an existing station unless you add --force. It
restores everything, pairings included, so members’ phones simply resume. The
ledger resumes from the snapshot: anything transacted after your last backup
is gone. That is why the routine is weekly.
Key recovery
A backup you cannot decrypt is a paperweight, and the archive is rightly encrypted under the passphrase. So the station also supports social key recovery: its key is split into shards sealed to trusted members’ phones. A threshold of them, cooperating in person, can reconstruct the key, which also unlocks any backup archive, passphrase or no passphrase.
Arm it as soon as you have a few paired members you trust:
station recovery setup \
--threshold 3 \
--holder <alice-rrn1-address> \
--holder <bob-rrn1-address> \
--holder <carol-rrn1-address> \
--holder <dan-rrn1-address> \
--holder <erin-rrn1-address>
3 of 5 is the sweet spot for a small community: no single holder or pair can act alone, and losing one or two does not sink you. Holders must be paired members. Pick people unlikely to leave together.
The command prints a QR code per holder. Each holder scans theirs, in person, in the app under Settings → Shards you hold. The shard sits on their phone doing nothing, possibly for years. If a holder leaves, re-run setup with a new roster; a re-run re-splits the key and invalidates every earlier shard.
station recovery status # the current roster
station recovery show-shard <addr> # re-display one holder's QR
Use it the day the passphrase is gone. Gather a threshold of holders in one room:
station recovery restore # passphrase lost, data dir intact
station recovery restore --from-backup <archive> # machine AND passphrase lost
The command prints a request QR. Each holder opens Shards you hold →
Help someone recover, scans it, checks the station address shown is really
yours, unlocks with their own passphrase, and their phone shows a response
QR. Scan each response and paste the rrnrecover-resp:… lines into the
waiting command. With enough responses in, you choose a new passphrase and
the station is yours again. Take a fresh backup immediately: the old
archives still answer only to the old key wrapping.
The disaster table
| What happened | What saves you | Prepared by |
|---|---|---|
| Station machine dies | station restore <archive> on a new machine | Backups |
| Passphrase lost, machine fine | station recovery restore | Key recovery |
| Machine dies and passphrase lost | station recovery restore --from-backup <archive> | Both |
| A member loses their phone | They rebuild their key on a new device from their recovery circle (Recover an existing identity in the app, or rrn wallet recover on a laptop), or restore their wallet export | Each member, in-app |
| A member’s phone is stolen | station unpair <addr> first, then the member recovers on a new phone and re-pairs | Nothing to prepare |
The first three rows protect the community. The last two protect a member, which is why nudging everyone through the recovery circle and the export in Back up and recover your keys is operator work too. Order matters when a phone was stolen: the thief holds the same key until you unpair it, so unpair first, then let the member recover. The member’s reconstruction runs on their own device and never touches the station; every holder confirms the ceremony fingerprint out loud before contributing.
Seizure resistance
For communities under surveillance pressure there is an optional encrypted at-rest profile on Linux: the wallet and ledger live inside an encrypted volume whose key is itself split among member-held shards, and the station cannot boot without a quorum of holders present. It is one-way, needs practice, and changes what a backup contains. See Encrypted at rest.
Encrypted at rest
By default a station keeps its database in the clear. Only the station’s own
key is encrypted, under the passphrase you typed at station init. A
powered-off station that is seized or imaged therefore hands over the whole
community: every balance, every memo, and who vouched for whom.
For communities that face that threat there is an optional encrypted at-rest profile. The ledger, the station’s wallet, the paired-phones list, the search index, and the radio identity all live inside an encrypted container whose key is held by members, not stored on the machine. Powered off, the station is an encrypted brick: no key on disk, no list of who holds the pieces.
This page is the operator’s walkthrough. The design and its trade-offs are locked in ADR-0024.
What it protects, and what it does not
Protected: a station that is off, or whose storage is copied while off. The container has no key slot at all. The key is rebuilt, each time, from shards held by a quorum of members, and it lives only in kernel memory while the station runs.
Not protected: a station seized while running. The volume is mounted and the key is in memory. Live imaging or a cold-boot attack can recover it. The defenses there are physical: custody of the machine, a tamper-evident enclosure, and a fast rebuild on new hardware from your backups. The profile does not claim to defend a running node.
Also not protected: anything the host leaks around the container. Swap, crash dumps, and logs written to a plaintext partition can carry pieces of the key or of the ledger. The hardening steps below are not optional.
The trade you are choosing
Every loss of power makes the station a locked brick until enough key holders gather to run an unlock ceremony. A blackout, a tripped breaker, an unplugged cable, a reboot for an update: each one needs a ceremony. Where holders share a building that is minutes. Where they are dispersed it can mean a scheduled meetup, and days of downtime. During that time members keep signing, but nothing settles.
So:
- A UPS is close to mandatory. It turns brownouts and blips, the common cause of downtime, into non-events, and shuts the station down cleanly when the battery runs low.
- There is no operator-passphrase shortcut. A passphrase one person knows is exactly what coercion extracts. The member-held quorum exists to remove that single seizable human. If you want a single-operator unlock, you want the plaintext profile. Use it honestly.
- Linux only. The profile uses the kernel’s own disk encryption. A station configured for it on macOS refuses to start rather than quietly serving plaintext.
Before you turn it on
Harden the host. The encryption is only as good as the machine around it.
# Disable swap (or use encrypted swap) so key bytes and database pages never page out:
sudo swapoff -a # and remove the swap entry from /etc/fstab
# Suppress core dumps so a crash cannot spill plaintext to disk:
echo 'kernel.core_pattern=|/bin/false' | sudo tee /etc/sysctl.d/50-no-cores.conf
# Keep logs off any plaintext partition (journald to volatile storage is fine).
Give the station user a scoped privilege. Provisioning and unlocking drive
the kernel’s disk tools through sudo without a password. The daemon itself
stays unprivileged. If the station runs as a dedicated user, grant that user
exactly those tools and nothing else:
# /etc/sudoers.d/rrn-station (edit with: visudo -f), for user "rrn":
rrn ALL=(root) NOPASSWD: /usr/sbin/cryptsetup, /usr/sbin/losetup, /bin/mount, \
/bin/umount, /sbin/mkfs.ext4, /bin/chown
Warning. Granting
mount,chown, andcryptsetupto a user is close to root on that host. That is the price of a scripted, testable key ceremony. A community that wants a smaller privileged surface can run the ceremonies as root, interactively, instead of through a service account.
Take a backup. The migration is one-way. See Backups and key recovery.
Turn it on: the one-way migration
You need each key holder’s rrn1… address. Holders read it from their own
app under Settings → Your address. Three of five is the usual shape, for the
same reasons as station key recovery: no single holder or pair can act alone,
and losing one or two does not sink you.
station backup --out ~/before-encrypt.rrnbak # keep this somewhere safe
station encrypt-in-place \
--holder rrn1<alice> --holder rrn1<bob> --holder rrn1<carol> \
--holder rrn1<dave> --holder rrn1<erin> \
--threshold 3
The command provisions the container, moves the wallet and ledger inside,
splits the volume key three-of-five, and prints one QR code per holder. The
volume is left unlocked so you can station run straight away.
Have each holder scan their QR in person, in the app under Settings →
Shards you hold. Their phone stores it as an ordinary recovery shard. It
reads as “a shard for rrn1…”, naming the volume key’s address rather than
the station’s. That is expected.
The holder set is deliberately not written to the configuration file. A
seized card must not carry a list of whom to pressure. The authoritative record
of who holds a shard lives inside the container and is readable only once it is
unlocked, with station vmk status.
Then destroy the old media. The migration securely erases the plaintext files it moved, but secure erase is unreliable on SD cards and other flash: old blocks can survive wear levelling. For a real threat model, physically destroy the card the station ran on before the migration and start the encrypted station on a fresh one.
Every boot from now on: the unlock ceremony
After any power loss the station will not start until a quorum of holders help.
station status # "state volume: LOCKED (not mounted)"
station unlock # prints a request QR and a short console fingerprint
station run # once the volume is mounted, the daemon starts normally
The ceremony, step by step:
station unlockshows a request QR and a console fingerprint, two groups of five letters such asB523J-DY6LH.- Read the fingerprint aloud to each holder, or send it over a channel you both trust.
- Each holder opens Settings → Shards you hold → Help someone recover, scans the request, and compares the fingerprint their phone shows with the one you read. Only if they match do they enter their passphrase and show you the response QR.
- Scan each response and paste the
rrnrecover-resp:…lines into the waiting command. When enough are in, the volume mounts.
The fingerprint is the whole safety check. Before the volume is unlocked the station has no key of its own, so the request cannot be signed. Someone who copied the machine and ran their own ceremony would show a different fingerprint, and the holders would catch it. If a holder’s fingerprint does not match yours, stop. Someone else is running a ceremony against your holders. The algorithm is pinned in the boot ceremony spec so the app and the station always agree.
Tip. Holders can answer remotely if they must. A response is sealed to this one ceremony and is useless to anyone else. What cannot be skipped is the fingerprint comparison, which is the only thing telling a remote holder that the request really came from you.
Running under systemd
Under the encrypted profile station run exits until the volume is unlocked.
A unit with Restart=always crash-loops after every reboot until someone runs
the ceremony. That is expected: unlock is a deliberate human step. Use
Restart=on-failure, as the sample unit in Run a station
does, and either start the service after the ceremony or leave it enabled and
accept the restart backoff until the holders have gathered.
Rotating holders
When a relationship changes, re-split the key to a new set. The volume must be unlocked first.
station vmk status # current holders, K of N
station vmk refresh --holder … --holder … --threshold 3
A refresh gives every holder a brand-new shard, and old and new shards cannot be mixed: a leftover old shard is useless next to the new ones. It does not change the underlying volume key, though. A full quorum of the former holders, acting together, could still rebuild it.
To lock former holders out completely, rotate onto a new container. The migration command refuses to run on a station that is already encrypted, so a rotation is a fresh migration:
station unlock && station backup --out rotate.rrnbak # while still unlocked
station restore rotate.rrnbak --data-dir /path/to/fresh # a fresh, plaintext dir
station --data-dir /path/to/fresh encrypt-in-place \
--holder … --holder … --threshold 3 # new holders, new key
cp <old-boot-dir>/config.toml /path/to/fresh/ # backups omit the boot config
Move the fresh directory into place and, again, physically destroy the old media: the restore step rewrote the plaintext ledger onto it.
Backups under this profile
station backup still works while the station is serving, and the archive
still covers everything inside the container: ledger, wallet, pairings. It does
not cover the unencrypted boot directory’s config.toml, which holds your
listen addresses, timers, and radio settings. Keep a copy of config.toml
alongside your backups, or expect to re-enter that configuration when you
restore onto fresh hardware. The keys are documented in the
configuration reference.
Recovering an encrypted station onto new hardware is a manual sequence:
station restore <archive> to get the ledger and wallet back, then
station encrypt-in-place to provision a new container and re-arm the volume
key to your holders, then the unlock ceremony.
The drills
Two things only work if practised, and neither failure is cryptographic. The failure is three holders who cannot be found, or who have never scanned a request before.
| Drill | How often | Command |
|---|---|---|
| The unlock ceremony, with the real holders | After arming, after every holder change, at least twice a year | station unlock |
| The community-continues path: restore from backup on fresh storage | Quarterly, and before any upgrade | scripts/drill-seizure-recovery.sh --profile plaintext |
| The brick property: a closed container leaks nothing | After arming and after every re-key | scripts/drill-seizure-recovery.sh --profile encrypted |
| The UPS | Monthly: pull the mains, confirm a clean shutdown on low battery | none |
The encrypted drill plants a marker inside the volume, closes it, and proves
the marker appears nowhere in the container bytes or the boot directory, that
the holder set is absent from the boot directory, and that the container has
zero key slots. The plaintext drill runs on any machine and rehearses the
restore path end to end. Both scripts ship in the station repo.
Practising the ceremony is part of the community’s outage drill.
The courier desk
When a member cannot reach the station by any network, a payment can still travel on paper. The member signs a record on their own device, turns it into QR codes, and someone carries the printed sheet to the station. The station scans it in, checks it, and hands back a signed receipt for the return trip. This page is the operator’s side of that loop: the desk where sheets arrive and receipts go out.
Who this serves today. The phone app cannot yet export its outbox or accept a payment offline: it needs the station in reach to send or confirm. The plumbing for it exists in the app’s Rust core, but the screens do not. The members whose records a courier carries are therefore those using the command-line wallet, which signs offline, prints its own outbox, and applies receipts. The tools on this page work the same way for either once the app catches up.
What a courier carries
A courier is anyone who physically moves records between a cut-off member and the station: a neighbour walking to the community hall, the person who drives to town, a bicycle. There are three kinds of thing in their pocket.
- A bundle, going to the station. One or more members’ signed records: payments, confirmations, ballots, disputes. On paper it is a sheet of numbered QR codes. On a USB stick it is a single file.
- Delivery receipts, going back to each author. The station’s signed answer, per record: admitted, already known, or refused and why.
- Cards, printed once and carried by their owner: a member’s address as a credential card, or a headroom certificate as a wallet card.
A courier needs no trust. Every record is signed by its author and the station re-checks every signature. A courier can lose, delay, or duplicate what they carry. They cannot forge or alter it. A sheet holds no secret: losing one is a delay, never a loss of funds. This is the whole design of the offline path, locked in ADR-0020.
What you need at the desk
- A printer. Any monochrome laser is plenty.
- A QR scanner app, on any phone, or a webcam scanning tool. The
rrncommand line does not read camera images: a headless station has no camera. Scanning produces text, one QR payload string per line, saved to a file. That file is what you feed the station.
A payload string starts with a short prefix that says what it is: rrnp: for
one chunk of a multi-part sheet, rrncert: for a certificate, rrnspend:
for an offline spend voucher, and a bare rrn1… for an address. The formats
are pinned in the
QR payload spec.
A sheet arrives
Scan every QR on the sheet into a text file, one line each, in any order. Then:
# Look before you ingest. Works with the daemon stopped.
rrn paper show --in scanned.txt
# Ingest: reassemble, submit to the station, print each record's outcome,
# and write the receipts for the return trip.
rrn paper ingest --in scanned.txt --out carryback/
show classifies every payload, verifies every signature it can, and reports
which chunks are missing from an incomplete group. Use it when a sheet looks
damaged or a courier is unsure what they were handed.
ingest prints one line per carried record with its outcome:
| Outcome | Meaning |
|---|---|
| admitted | New, valid, now on the log. The settlement window starts from this moment. |
| known | The station had it already, from an earlier sheet or another route. Nothing double-counted. |
| refused | Rejected, with a reason. The record never touched the log. |
Re-ingesting is always safe. Scan the same sheet twice, or receive the same bundle from two couriers, and the station recognises it and returns the same receipt. Nothing is admitted twice.
Every sheet’s QR codes are labelled with the payload’s short id and their
index, 3 of 7, so a dropped page can be spotted by eye. Chunks of two
different payloads mixed together are refused as a group rather than combined
into nonsense, and a clean re-scan rebuilds the set.
Reading a refusal
The commonest reasons, and what to tell the courier to tell the member:
| Reason | What it means | What to do |
|---|---|---|
nonce-gap | An earlier record from this member has not arrived yet. Records from one device form a chain and land in order. | Wait. The earlier one is on its way, or was lost and needs re-sending. Re-ingest this sheet afterwards. |
debt-floor | The payment would have taken the payer below the debt floor, and no headroom certificate backed it. | The payer needs a certificate before going offline next time. See below. |
bad-signature | The bytes do not match the signature: a scan error, a corrupted line, or tampering. | Re-scan. If it persists, the sheet is not what the member printed. |
cert-expired, cert-overspent, cert-unknown, cert-wrong-member | A certificate-backed spend failed one of its checks. | The receiver should have caught most of these when they accepted the payment. An overspend is recorded as equivocation, which costs the payer their whole standing. |
read-replica | You are at a replica, not the community’s writer. | Carry the sheet to the writer station. A replica admits nothing. |
Receipts go back
The station holds a signed receipt for every record it has processed until the author’s own device confirms it has seen it. Sending them home:
# Receipts for a member who is about to walk home:
rrn paper export-receipts --author rrn1<member> --out receipts/
# Or the whole pending queue, for a courier doing the rounds:
rrn paper export-receipts --out receipts/
Exporting never marks a receipt delivered. Only the author’s device does that, when it applies the receipt. So it is safe to export the same receipts twice, and if a receipt sheet goes missing, run the export again.
Tip. Tell members the rule plainly: a receipt is proof; the absence of a receipt is proof of nothing. A member whose device has not seen a receipt should keep re-sending. Re-sending is always safe.
Cards
Two things are printed once and kept:
# A member's credential card: their address as a QR, with a name on it.
rrn paper credential --address rrn1<member> --name "Jordan" --out cards/
# A headroom certificate as a wallet card, for the station's own wallet.
rrn paper cert --request 10 --out cards/ # reserve 10 Commons and print it
rrn paper cert --cert-id <hex> --out cards/ # print an existing live one
A credential card is just the member’s address. It is how a courier or a merchant scans a payee without typing a long string, and how a member reads their own address off paper when rebuilding a lost key.
A headroom certificate is the station’s signed reservation of part of a
member’s credit headroom, made while connected so that a later offline payment
against it is accepted on arrival. The card carries the certificate as a
single QR with the member, the cap, and the expiry printed beneath. The
station’s own wallet requests one with rrn cert request; a member on the
command-line wallet requests theirs with rrn wallet cert request and can
import a printed one with rrn wallet cert import. The rules are in
When the network is down and locked
in ADR-0021.
What is in an output directory
Every --out directory gets the same three things:
| File | What it is |
|---|---|
chunk_NN_of_MM.png | One PNG per QR code, numbered. |
sheet.pdf | Every QR on captioned pages: payload id, index, and count under each one. Print this. |
*.txt | The raw payload strings, one per line. The no-printer path: copy this file to a USB stick instead, or read it back with rrn paper render. |
rrn paper render --in some.txt --out sheets/ turns any saved text file back
into a printable sheet. It is the primitive the other commands use.
Limits worth knowing
- A paper payload is at most 64 chunks. In practice that is around a hundred small records per sheet set. A larger bundle is electronic-only: a USB stick or a radio.
- A payment signed for a slow carrier should carry a long validity window. The
command-line wallet’s
--carrier slowsets two weeks instead of one day, so the record survives the trip. - A certificate-backed spend can arrive up to fourteen days after the certificate expires and still be admitted, so a courier who is late does not strand the receiver.
The routine
At a market day or during an outage, the desk runs like this:
- Couriers arrive with sheets.
show, theningest, then read the outcomes aloud to the courier. - Before each courier leaves,
export-receiptsfor the members they are heading back toward, and print the sheet. - Members who reach Wi-Fi again pick up their receipts themselves; the paper ones are for those who will not.
- Keep the
carryback/directories. They are proof of what left the desk.
The end-to-end walkthrough, with real binaries and a simulated phone, is the
demo-phase-2-paper.sh script in the station repo. The same loop with a
laptop member is demo-phase-2-wallet.sh.
Radio and text message
Between “phones on the station’s Wi-Fi” and “a printed sheet in a pocket” sit two electronic carriers. LoRa radio moves signed records kilometres with no infrastructure at all. Text message could move them anywhere a phone has signal. One is built and bench-proven; the other stops at a software seam. This page says which is which and how to bring the radio up.
Both are dumb carriers. They see only signed, already-public records. They are never the identity, the integrity, or the encryption boundary: a record that crosses a radio is checked at the station exactly as if a courier had walked it in. That is locked in ADR-0013.
What each one leaks
Content is community-public either way. What a carrier adds is metadata: a radio has a location and a traffic pattern; a phone number has an owner and a carrier that logs who texted whom, and when. A community under surveillance pressure should route sensitive traffic on paper, which has no electronic trace, and treat radio and SMS as conveniences for reach.
LoRa radio
The station does not speak radio itself. It supervises Reticulum, a mesh networking stack that drives LoRa boards running the RNode firmware, and carries bundles over it. Adding a radio is a firmware and configuration change, not a code change.
Status
| Piece | State |
|---|---|
| Reticulum sidecar, supervised by the station | Built. Off by default. |
| Bundle push and receipt over the radio | Built. Two radios verified over the air on the bench on 2026-09-11. |
| Airtime budget, money-first pacing | Built. |
| Field acceptance at real range | Pending: a human sign-off with the checklist in the bring-up guide. |
The full procedure, with board compatibility, flashing, the regional
compliance table, airtime presets, and the acceptance checklist, is the
LoRa radio bring-up guide
in the station repo. This page is the map; that guide is the territory.
Hardware
You need two radios to test a link: one at the station, one at a peer. A board must carry a Semtech LoRa transceiver, be on the RNode firmware’s supported list (LilyGO T-Beam and T3, Heltec LoRa32, RAK4631-class boards are common choices), and match your regional band on both ends. The definitive check takes five minutes and no code:
pipx install "rns==1.5.2" "lxmf==1.1.1" # Reticulum, pinned to the version the station manages
rnodeconf --autoinstall # detects the board; offers firmware only if supported
A board that rnodeconf does not recognise is not an RNode target. Test with a
known-good board instead.
The sidecar
The station runs the Reticulum daemon as a managed child: version-pinned, restarted with backoff, killed cleanly on shutdown. Its loss is a connectivity event, never a reason the station exits. Two things before you enable it:
- Install it yourself. Reticulum is not bundled. The station refuses to
manage a version other than the one it was validated against, any
1.5.x. - Know what you are installing. Reticulum is under its own licence, permissive but with two use restrictions, and not an OSI-approved one. The station never links it; by enabling the sidecar you choose to run that separate program. If that is a problem for your community, leave it off. Nothing else depends on it.
Then, in the station’s config.toml:
[sidecar]
enabled = true # off by default
[lora]
# An absolute path: the station resolves it against its working directory,
# which is "/" under systemd.
adapter_script = "/opt/railroad/station/scripts/reticulum/lxmf_adapter.py"
[lora.rnode]
port = "/dev/ttyACM0" # the radio's serial port
frequency_hz = 867200000 # NO default: must be legal for your region
tx_power_dbm = 7 # NO default: start low, respect your region's cap
# bandwidth_hz = 125000 # optional, defaults shown; must match on every node
# spreading_factor = 8
# coding_rate = 5
On first run the station writes a Reticulum configuration under the data
directory with the matching radio interface, and never overwrites your edits
afterwards. If [lora.rnode] is absent, the generated configuration carries a
commented example and a pointer to the guide instead. The station never
transmits on a frequency or power that nobody chose.
Warning. Choosing a frequency, power, and duty cycle that are legal where you operate is your responsibility, and it varies by country. Antenna gain counts toward the radiated-power cap. The bring-up guide has a starting table by region and links to the regulators; confirm before keying up, and when in doubt transmit at the lowest power that carries the link.
Tip. If the station already ran once before you added
[lora.rnode], the generated Reticulum config is on disk without a radio and is not regenerated. Delete it (the station rewrites it on the next start) or add the interface stanza by hand.
The airtime budget
LoRa is slow, and duty-cycle rules make sustained throughput slower still:
single-digit bytes per second in the conservative European setting. The
station paces to a budget of raw_bytes_per_sec × duty_cycle_percent / 100
and sends money before governance before bulk, so a payment never waits
behind a marketplace listing. The defaults are the conservative setting.
After a link test, measure your real on-air rate and set the budget to it;
the guide has presets per region. Every key is in the
configuration reference.
Watching traffic cross
rrn status # the connectivity block: sidecar disabled / running / degraded / restarting
rrn dtn status # every tracked push: peer, records, state, attempts, and the receipt's outcome
A push goes queued, then delivered once the peer’s signed receipt
correlates back, or abandoned after a week of retries. Nothing is dropped
silently. To send by hand:
rrn dtn bind --destination <this station's Reticulum hex> # publish your own reachability
rrn dtn push --peer <peer hex> --bundle payload.bundle # returns "queued"; watch status
A member on the command-line wallet produces payload.bundle with
rrn wallet export bundle, having signed with --carrier slow so the record
survives a slow link. The receiving member gets their receipt the normal way:
a sync when they next reach the writer’s network, or a receipt sheet from the
courier desk. A push’s status line shows only a summary,
not the receipt bytes.
The scripted acceptance run
scripts/field-test-lora.sh in the station repo drives one signed bundle
across the radio and confirms the receipt returns, printing pass or fail per
stage. Start a station on each of two machines yourself, then run the
receiver role first (it prints the destination hex the sender needs) and the
sender role second. --dry-run rehearses every stage with no hardware and is
what continuous integration runs.
Two rnsd instances cannot share one host, so this is two machines, not one.
Text message
SMS is designed as a carrier for already-signed records, never as a way
to command the station. A phone with cellular text but no data encodes its
outbox into text chunks, texts them to the station’s number, and the station
texts the signed receipt back. The feature-phone model, a person texting
PAY 5 TO ALICE with the station holding their keys, is out of scope by
decision: keys stay with members
(ADR-0006).
Status
| Piece | State |
|---|---|
| Wire codec: records as text chunks that survive a carrier’s mangling | Built and tested. |
| Sender registry: a member binds their own number with a signed record | Built. |
| Per-sender rate cap, money-first outbound pacing | Built. |
| Station relay, tested against a mock gateway | Built. |
| The physical modem gateway | Not built. |
| The app composing texts | Not built. |
Because there is no gateway, SMS cannot be switched on today. The [sms]
section of the configuration exists and is off by default; an enabled section
without a modem backend is supervised but idle. When the gateway lands it
will be a local USB GSM modem, not a cloud texting service, so that it works
with no account and no internet, the same posture as the radio.
What is built is enough to know how it will behave:
- The sender registry is spam control, not security. A sender number is forgeable; the signatures inside the texts are the boundary. The default policy processes texts only from numbers a member has bound to their identity. The rate cap is sixty inbound texts per sender per hour.
- There is no acknowledgement protocol in SMS. Reliability is re-send driven, exactly like paper: a member keeps texting their outbox until the receipt comes back, and the station re-ingests idempotently.
- The carrier sees everything: both numbers, the timing, and the bytes. The bytes are public signed records; the numbers and timing are the leak.
The format is pinned in the SMS carrier spec.
Where each carrier fits
| Situation | Reach for |
|---|---|
| Members on the community Wi-Fi | Nothing extra: phones and wallets sync directly. |
| A member across the valley, no internet either end | Radio, if both ends have one. Otherwise paper. |
| Sensitive traffic, surveillance pressure | Paper. No electronic trace. |
| Cellular signal but no data | Paper today. SMS once the gateway exists. |
| Volume too large for a sheet | A bundle on a USB stick, or the radio. |
All of them end at the same front door. Whatever the carrier, the station checks every signature, every nonce, and every window as if the member had been standing in front of it.
Keeping phones in sync
A member’s phone is only as useful as its last sync. With the app open it holds a live connection to the station and everything is instant. The moment it goes to the background, and more so once Android kills the process, the phone is at the mercy of the operating system’s power management. On many phones the factory settings quietly starve the app: no notifications, stale balances, members who miss vouches, payment confirmations, votes, and dispute deadlines.
This page is for the operator onboarding members. It explains what the app does in the background, the two in-app switches and one system setting every phone needs, the vendor-specific traps, and how to verify a phone in ten minutes. Do it at pairing time: it takes two minutes with the member present and saves diagnosing a silent phone a week later.
How background sync actually works
The app has three modes. Knowing which one a phone is in tells you what is normal. Half of all “background sync is broken” reports are correct behaviour with unset expectations.
Foreground. App open and unlocked. A live subscription to the station; new events arrive within seconds. Nothing on this page affects this.
Backgrounded. App not on screen, process still alive. The app asks the system to wake it periodically, at minimum every fifteen minutes, and the system is free to space the wakes out further for an app it considers unimportant. Each wake runs one short pass: connect, pull whatever queued since the phone’s last sync, raise a notification for each event the member has asked about, sleep.
Killed or rebooted. Android eventually kills every backgrounded process, and a reboot kills everything. The app registers a task that lets the system wake it even then, without it appearing on screen. But a freshly woken process has no unlocked wallet, and a sync request must be signed. So killed-app sync only works if the member has opted in to background sync, which provisions a special signing credential for the task.
Two things follow:
- Background sync is a polling cadence, not a push channel. “Within fifteen to thirty minutes” is healthy. “Instantly, in a pocket” was never on offer. Opening the app always syncs at once.
- Every layer below can silently veto the wakes. The rest of this page is about finding and disarming those vetoes.
The per-phone setup
Three things, in order, on every member’s phone.
1. Allow notifications. In the app, Settings → Notifications: enable Local notifications and check the Notify me about list matches what the member wants. On first run Android shows its own permission prompt; it must be accepted. A declined prompt is fixed later under system Settings → Apps → Railroad Network → Notifications.
2. Turn on “Sync while the app is closed.” Same screen, under Background sync. This is the switch that makes killed-app and after-reboot sync possible at all. Flipping it does two things:
- The app provisions a background signing credential: the wallet is re-encrypted under a random device-held secret so the background task can sign sync requests without a passphrase. The trade is real and deliberate: while the device is unlocked, the app process can sign without the member’s passphrase. It is opt-in and device-bound, and a member who declines simply gets no sync while the app is closed. That is a legitimate choice.
- The app immediately shows the system battery-exemption dialog. The member should accept it. On many phones (Motorola, verified first-hand in this project) the battery optimiser does not just slow a backgrounded app; it cuts its network access entirely. The wake fires, the pass runs, and the station is simply unreachable.
3. Clear the vendor’s own app-killer. Stock Android’s exemption is necessary but on several brands not sufficient. Find the brand in the table below.
If the dialog in step 2 was dismissed, the manual path is system Settings → Apps → Railroad Network → Battery → Unrestricted. The wording varies: “Don’t optimize”, “No restrictions”, “Allow background activity”.
Per-vendor traps
Stock Android already throttles twice: deep sleep when the phone sits still, and rarer wakes for rarely used apps. The exemption above handles those. On top of that, most vendors ship their own battery manager that kills or freezes apps by its own rules and ignores the stock exemption. Those produce the “phone went quiet three days in” reports.
| Brand | Trap | What to set |
|---|---|---|
| Motorola | The battery optimiser cuts background network on the LAN. | The step-2 dialog is usually enough: Battery → Unrestricted. |
| Google Pixel | Closest to stock; standby buckets still apply. | The step-2 exemption is enough. |
| Samsung | “Sleeping apps” and “deep sleeping apps” lists, plus put unused apps to sleep. Apps freeze after a few days of light use. | Settings → Battery → Background usage limits: remove the app from Sleeping and Deep sleeping, add it to Never sleeping apps; turn off Put unused apps to sleep. |
| Xiaomi, Redmi, POCO (MIUI) | The most aggressive: a separate Autostart permission, a per-app battery saver, and swipe-away kills the app by default. | Security app → Permissions → Autostart: on. Settings → Battery → App battery saver → Railroad Network → No restrictions. In Recents, drag the app down to lock it. |
| Huawei, Honor (EMUI) | “App launch” auto-manages apps and kills background tasks. | Settings → Battery → App launch → Railroad Network → Manage manually, enable all three: Auto-launch, Secondary launch, Run in background. |
| OnePlus, Oppo, Realme, Vivo | Battery optimisation plus a separate autostart or “quick freeze” layer. | Battery → Don’t optimize; Settings → Apps → Autostart (or “Startup manager”) → allow; disable any “sleep standby” or “quick freeze” entry for the app. |
Two gotchas on every brand:
- Force-stop kills everything. Settings → Apps → Force stop, and on some vendors swiping the app out of Recents, puts the app in a state where Android delivers no background wakes until the member opens it again. A member troubleshooting by force-stopping is disabling the thing they are testing. On the swipe-happy brands, use the Recents lock instead.
- The trap resets. OS updates, vendor “battery usage reviews”, and the periodic “this app runs in the background” nudge can re-enable optimisation. If a previously fine phone goes quiet, re-walk the setup before suspecting anything else.
Vendors rename these screens constantly. The community-maintained dontkillmyapp.com has per-brand, per-version walkthroughs with screenshots when the wording above has drifted.
The network is part of “background”
A wake that fires on schedule still syncs nothing if the phone cannot reach the station at that moment.
- The station is on the local network only. Away from the community’s Wi-Fi there is nothing to sync; the phone catches up when it is back. This is by design. Do not chase it as a bug.
- Wi-Fi sleep. Some phones drop Wi-Fi minutes into deep sleep and lean on mobile data, which cannot reach the station. Symptom: quiet overnight on a shelf, instantly current when picked up. Look for a “keep Wi-Fi on during sleep” setting; some brands bundle it into the battery manager.
- Guest or isolated Wi-Fi. Client isolation blocks phone-to-station traffic entirely, in the foreground too, so it shows up at pairing time already. Members must be on the same real network as the station.
The ten-minute drill
Run it once per phone at onboarding, and again whenever someone reports silence. It separates a misconfigured phone from normal cadence in one pass.
-
Setup check. All three steps above done; the in-app Notifications screen shows background sync enabled.
-
Background the app with Home. Do not force-stop it, and do not swipe it away on MIUI-family phones. Screen off, phone on the community Wi-Fi.
-
Queue an event from the station. Anything addressed to that member works; a vouch is the classic:
rrn vouch <member-rrn1-address> --statement "background sync drill" -
Wait up to thirty minutes: one wake at the fifteen-minute floor plus system slack. A notification arriving means the backgrounded path works.
-
The killed-app path, once per phone model. Reboot the phone, do not open the app, queue another event, wait again. A notification on a locked phone never opened since boot proves the whole chain.
-
Log the result. Keep a roster note per phone: brand and model, date verified, which settings it needed beyond stock. The next phone of the same model onboards in a minute.
If step 4 or 5 fails, work the vendor table and the network checks for that brand, then re-run. For a phone that still fails, have the member open the app. A backlog of missed notifications appearing at once means wakes are being suppressed: keep digging in the vendor’s battery manager. Nothing appearing even in the foreground means it is not a background problem at all: check pairing and Wi-Fi in Troubleshooting.
Setting member expectations
Tell members plainly, once, at onboarding:
- Notifications while the app is closed arrive in batches, up to half an hour behind. That is the platform, not a fault. For anything time-critical, such as a dispute window closing, open the app.
- Off the community Wi-Fi you are offline. The app catches up when you are back in range.
- Do not force-stop the app or swipe it away to save battery. It costs almost nothing, and killing it turns off your notifications until you next open it.
- If your phone ever asks about the app “running in the background” or “using battery”, answer Allow or Keep. The optimisation it offers is what breaks your notifications.
iPhones, for completeness
The pilot fleet is Android. On iOS the same sync code runs under Background App Refresh: no vendor app-killer zoo and no background network firewall, but the system grants wakes purely opportunistically, with no fifteen-minute floor, no boot wake, and no exemption to request. Cadence is noticeably lazier and nothing here can improve it. Foreground behaviour is identical.
The one-glance checklist, per phone
- Notification permission accepted; Local notifications on
- Sync while the app is closed on, and the member understood the trade
- Battery → Unrestricted (the in-app dialog accepted)
- Vendor trap cleared: Samsung sleep lists, MIUI autostart and lock, EMUI app launch, ColorOS autostart
- Phone on the real community Wi-Fi, not a guest network
- Drill passed: backgrounded notification
- Drill passed once per model: post-reboot notification
- Roster note written
Troubleshooting
Symptom first, then the likely cause, then the fix. Grouped by where the problem shows up. The three commands that answer most questions from the station’s console:
rrn whoami # the station is up and this is its address
rrn status # connectivity: role, peers, the phone listener, pending queues, the sidecar
rrn history # the log; the last entries tell you what actually landed
rrn status reports the station’s role (writer or replica), whether the
phone listener bound and is advertised, how many delivery receipts are waiting
for a courier, and the radio sidecar’s state: disabled, running with its
version, degraded with a reason, or restarting.
Pairing and discovery
| Symptom | Likely cause | Fix |
|---|---|---|
| The phone’s Join screen finds no station | Phone on a guest or other Wi-Fi. | Same real network as the station. Guest networks isolate clients. |
The network blocks mDNS, or advertise = false. | On the phone, Add by address with the station’s IP and port 7500. | |
| The station is down. | rrn whoami on the station machine. Start it. | |
| Add by address fails too | Firewall on the station machine. | Allow inbound TCP 7500 from the LAN. Port 7400 stays closed. |
| Wrong IP. | Check the router, or ip addr on the station. | |
| The listener did not bind. | rrn status shows the mobile listener as unbound. Another process holds port 7500, or [mobile] listen is wrong. | |
| Pairing codes do not match | Something else answered in the station’s place. | Do not pair. Identify the machine that owns that IP before pairing anyone. |
| The app says the station “couldn’t be verified” | Same as above: the endpoint cannot prove it holds the station key. | Refuse. |
station pair-mobile lists nothing | The phone has not sent its request yet, or sent it to a different machine. | Have the member tap Join again while you watch. |
| A wallet on a laptop refuses a receipt or a pairing reply | The address pinned at rrn wallet init is not this station’s. | Compare rrn whoami with what the member typed. A wrong pin means a fresh init. |
Sync and notifications
| Symptom | Likely cause | Fix |
|---|---|---|
| No notifications while the app is closed | Battery optimisation, or Sync while the app is closed is off. | The three-step setup in Keeping phones in sync. |
| Phone quiet for days, then a flood when opened | The vendor’s app-killer is suppressing wakes. | The per-vendor table on that page. |
| Phone quiet overnight on a shelf, current when picked up | Wi-Fi sleeps and the phone falls back to mobile data. | A “keep Wi-Fi on during sleep” setting. |
| A previously fine phone goes quiet | An OS update or a “battery review” reset the exemption. | Re-walk the setup. |
| A Connecting… pill on cold start | Normal: the app is re-establishing its subscription. | Wait a second. |
| Offline that persists on the right Wi-Fi | The station is unreachable from the phone. | Work the pairing table above. |
| The member force-stopped the app “to fix it” | Force-stop disables all background wakes until the app is opened. | Open the app. Tell them not to. |
| Nothing appears even in the foreground | Not a background problem at all. | Pairing and Wi-Fi, above. |
Payments and refusals
| Symptom | Likely cause | Fix |
|---|---|---|
| A payment is refused with a debt-floor message | It would take the payer below the floor, counting what they have already signed. | Wait for something to settle, or the payer sells something. The floor is a community setting, not a personal one. |
| A payment of 50 Commons or more is refused | Tier 3 is not served. Refused, never shrunk. | Split it into smaller payments over time. |
| A confirmed payment sits as pending for a day or two | The settlement window. Normal. | Nothing. Tier 1 settles after 24 hours, Tier 2 after 48. |
| A payment never settles | Someone contested it. | rrn dispute list. Every path resolves or lapses on its own. |
| A vouch is refused with a velocity message | The rate limit on how fast anyone can vouch or gain standing. | Wait. It is not personal. |
A paper or bundle record is refused nonce-gap | An earlier record from that device has not arrived. Records land in order. | Wait for it, or have the member re-send. Re-ingest afterwards. |
Refused bad-signature | A scan error, a corrupted line, or tampering. | Re-scan. If it persists the sheet is not what the member printed. |
Refused outbox-fork | The member’s device signed two different records at the same position, usually a restored wallet that did not sync first. | The member’s standing is gone by rule. Prevent it: a restored wallet must rrn wallet sync before signing. |
Refused cert-overspent | The same certificate was spent past its cap. | Recorded as equivocation. Costs the payer their whole standing and opens a jury case. |
Refused read-replica | This station is a replica, not the writer. | Carry the record to the writer. A replica admits nothing. |
Refused tier-unsupported | A carried record asked for a tier the station does not serve. | Same as the 50-Common case. |
Every refusal names its reason, and every admitted record earns a signed receipt. See The courier desk for the full table.
Station and daemon
| Symptom | Likely cause | Fix |
|---|---|---|
station run rejects the passphrase | It wants the wallet passphrase from station init, not the machine login. | Lost it? Key recovery, today. |
| The daemon refuses to start with peers configured | A writer never pulls, so a writer with a non-empty [peers] list refuses to run. | Remove the peers, or set role = "replica" if this really is a read-only copy. |
rrn says it cannot connect | The daemon is not running, or --socket points elsewhere. | Start the daemon; check the data directory. |
| Balances read zero on a replica | Expected: a replica cannot re-derive station-signed records under its own key. It is a copy of the chain for audit, not a balance oracle. | Read balances on the writer. |
| Shutdown hangs when a peer is configured by hostname | Name resolution on a dead resolver cannot be cancelled. | Configure peers by IP. |
| Sweeps seem late (settlement, contract charges, enactments) | The background timers run on coarse intervals: thirty seconds for settlement, minutes to an hour for the rest. | Normal. See the timers in the configuration reference. |
| Nothing settles, no window runs | The station is the one writer. While it is down, nothing moves. | Bring it back. Members’ signed records are preserved in their outboxes. |
| A member’s app crashed | Every error is recorded on the phone and survives restarts. | Ask for Settings → Advanced → Diagnostics, which has a copy button. The crash screen offers the same. |
Offline carriers
| Symptom | Likely cause | Fix |
|---|---|---|
| A sheet will not reassemble | A chunk is missing, or two payloads’ sheets are mixed. | rrn paper show --in scanned.txt names the missing indexes. Re-scan the whole set. |
| The same sheet ingested twice | Nothing wrong. | The station returns the same receipts; nothing double-counts. |
| A member never got their receipt | The receipt is waiting at the station. | rrn paper export-receipts --author <addr>; exporting twice is safe. |
rrn dtn status shows a push abandoned | Retried for a week with no receipt back. | The peer was unreachable that whole time. Re-push, or send the bundle on paper. |
The sidecar reads degraded | Usually a version mismatch: only 1.5.x is accepted. | rnsd --version; reinstall pinned. The station keeps running without it. |
| The station runs but no radio interface appears | [lora.rnode] absent, or the generated Reticulum config predates it. | Set the section; delete the generated config so it is rewritten. See Radio and text message. |
| No serial port, or permission denied | The user is not in the serial group. | sudo usermod -aG dialout $USER, log out and in. |
| Two radios see no path to each other | Frequency, bandwidth, or spreading factor differ. | They must match exactly on every node. |
SMS does nothing though [sms] is enabled | There is no modem gateway yet. | Expected. SMS cannot be switched on today. |
The encrypted profile
| Symptom | Likely cause | Fix |
|---|---|---|
station run exits right after a reboot | The volume is locked. | station status, then station unlock with a quorum of holders. |
| A systemd unit crash-loops after a reboot | Same. Restart=always retries before anyone can run the ceremony. | Use Restart=on-failure and start the unit after the ceremony. Expected behaviour. |
| A holder’s fingerprint does not match the console | Someone else is running a ceremony against your holders, or the holder scanned an old request. | Stop. Start a fresh station unlock and compare again. |
| A holder’s phone says it is not holding a piece for this | They scanned a shard for a different key, or never scanned one. | station vmk status shows the real holder set. Re-issue with station vmk refresh. |
encrypt-in-place refuses to run | The station is already encrypted. | A rotation is a fresh migration; see Encrypted at rest. |
| The station starts in the encrypted profile on macOS | It will not: the profile refuses rather than serving plaintext. | Linux only. |
| A restored station lost its listen address and timers | The archive does not include the boot directory’s config.toml. | Keep a copy with your backups. |
When you are stuck
rrn status --format jsonandrrn historygive the station’s own account.- The member’s Diagnostics screen gives the phone’s.
- The
stationrepo’s runbook, Setting up a Railroad Network community, has the long form of every procedure on this site. - If it looks like a bug, open an issue in the
stationormobilerepo with both accounts attached. A security problem goes to the security policy instead, never a public issue.
About the reference
The reference section is the exact, terse layer under the rest of this site: what every command does, what every word means, and where the design decisions are written down.
- Glossary: the project’s vocabulary, one paragraph each.
- rrn command reference and station command reference: the built-in help for every command, generated from the code.
- Configuration: every key in
config.tomlwith its default, the environment variables, and the data-directory layout. - Architecture decision records: the locked-decision record, with links.
- Wire specifications: QR payloads, bundles, SMS, the boot ceremony.
What is authoritative
This site explains. The station repo decides. In order of authority:
- The ADRs in
station/docs/adr/. Append-only; a changed decision gets a new ADR that supersedes the old one. - The wire specs in
station/docs/spec/. - The design overview in
station/docs/design/, which predates parts of the implementation and says so where it conflicts with an ADR. - This site.
If a page here disagrees with an ADR, the page has a bug. Tell us.
Glossary
Admission clock. The station’s own clock at the moment it admits a record to the log. Every time-bounded rule (settlement windows, voting windows, dispute windows) is anchored on it, never on the time a phone wrote into the record, because phones’ clocks may be wrong or lying.
Bootstrap grace. The state of a community with fewer than three established members. During grace the electorate and jury pool are the founders plus whoever is established. Ends by itself and never returns.
Bundle. One or more members’ signed records packaged for carriage to the station over a route other than direct Wi-Fi: another phone, a radio, a text message, or paper.
Centicommon. One hundredth of a Common. Every amount in the software is a whole number of centicommons; there are no fractions or floats anywhere in a signed record.
Ceremony fingerprint. A short code derived from a recovery or boot request, shown on the requester’s screen and on every holder’s screen. Read aloud and matched between people before a holder contributes, because the request itself carries no proof of who is asking.
Charter. A community’s founding document: its id, principles, guaranteed rights, and founders, signed by the founders. Amendments chain to the version they replace.
Common. The mutual-credit unit every community trades in. Created only by one member paying another; the community’s balances always sum to zero.
Community. The social entity: one station plus the members whose devices pair with it. In protocol documents this word is preferred over “station” for the entity, to keep it distinct from the software.
Courier. Anyone who physically carries records between a cut-off member and the station. Needs no trust: every record is signed by its author and re-checked by the station.
Debt floor. The lowest balance a member may sign themselves down to. The default is 20 Commons in debt. Counts settled balance plus pending signed debits.
Dispute. A signed record contesting a confirmed payment inside its settlement window. Freezes settlement of that payment until a jury rules or the dispute lapses.
Emergency. A supermajority-declared state that compresses the voting window for emergency-kind proposals to 24 hours, freezes the Charter, pins the electorate, and changes nothing else. Self-expiring.
Equivocation. Two conflicting records signed by the same member, such as a headroom certificate spent twice offline. Provable from the log; costs the member their entire standing and opens a jury case.
Established member. A member whose computed standing is at least 2.0 and whose identity is anchored by vouches. The electorate and the jury pool.
Fails open. The property that every dispute and governance path, if nobody acts in time, resolves to the confirmed status quo rather than stalling. Nothing can be frozen forever by inaction.
Federation. Connection between communities: shared reputation, treaties, cross-community trade. Planned for Phase 3; not built.
Headroom certificate. A station-signed reservation of part of a member’s credit headroom, requested while connected, so that a later offline payment against it is accepted on arrival. Capped, time-limited, returnable.
Log. The hash-chained, signed, append-only record of everything that has happened in a community. The single source of truth: balances, standing, tallies, and every other view are derived from it by replay.
Operator. The person who runs the station computer.
Organizer. A founder or steward of a community. Not a software role.
Outbox. The chained queue of records a member’s device has signed but which have not yet been delivered to the station. Today the command-line wallet keeps one; the phone app does not.
Pairing. The in-person ceremony by which a member’s device and the station establish that they are talking to each other, by comparing an 8-character code aloud. The security boundary of the transport.
Proposal. A signed motion to the electorate: an ordinary statute, a Charter amendment, or an emergency measure. Co-signed, then voted on, then tallied from the log.
Receipt. The station’s signed answer to a delivered record, saying whether it was admitted, was already known, or was refused and why.
Recovery circle. The 3 to 7 members who each hold one sealed shard of a member’s key; any three rebuild it on a new device.
Replica. An optional second station that pulls the writer’s log for audit or off-site backup. Admits nothing, so it can never fork the community. Not a failover.
Sealed envelope. The per-message encryption and signature between a member’s device and the station, so the plain-HTTP transport needs no TLS and the pairing-code comparison is the only trust step.
Settlement window. The delay between a payment being confirmed and the balances moving: 24 hours for Tier 1, 48 hours for Tier 2. Doubles as the window in which it can be contested.
Shamir’s secret sharing. The scheme behind every shard on this site: a secret is split into N pieces such that any K rebuild it exactly and any K−1 reveal nothing, by the mathematics rather than by policy. The project uses its own implementation (ADR-0004). Member circles use K = 3; the station’s key and its encrypted volume use an operator-chosen threshold.
Shard. One piece of a key split with Shamir’s secret sharing, sealed to the holder it is handed to. A threshold of shards rebuilds the key; fewer reveal nothing. Used for member circles, station key recovery, and the encrypted at-rest volume.
Sortition. Selection by lot. Juries are three members drawn by a standing-weighted, deterministic, log-derivable draw.
Standing. The reputation score computed for every member from the log by one formula that no community can tune. Never stored authoritatively; every copy is a cache.
Station. The software: the daemon that holds a community’s wallet and log, runs settlement, and serves the phones and the command-line client. Also, loosely, the computer it runs on.
Statute. An ordinary community rule, passed by the normal proposal process. May not contradict the Charter.
Tier. A payment’s size class, which sets its settlement window and what the confirmer must stake. Tier 1 and Tier 2 are served today; Tier 3 (50 Commons and up) is refused rather than shrunk.
Volume master key. The key of the encrypted at-rest container, split among member holders and never written to disk; rebuilt at every boot by the unlock ceremony.
Vouch. A signed attestation by one member that another member’s key belongs to a real, known person. Carries a stake; feeds standing and identity anchoring.
Writer. The one station per community that owns and appends to the log.
rrn command reference
rrn is the command-line client. It is three things in one binary: the operator’s console for a running station, the verify-only paper courier tools, and the self-custody member wallet for people who use a computer instead of a phone.
Generated page. This is the
--helptext of every command, captured from a build of thestationrepo byscripts/gen-reference.sh. Edit the help strings there, not this file.
rrn
The Railroad Network rrn CLI: the operator console (a thin client over the station's Unix socket), the verify-only paper courier tools, and the self-custody member wallet (ADR-0028).
Usage: rrn [OPTIONS] <COMMAND>
Commands:
init Explain how to bootstrap a station (init runs in the daemon, not here)
whoami Print this station's own address
status Show the station's connectivity / offline posture: per-peer reachability, the mobile listener state, and pending DTN queue depths
balance Show a balance (defaults to your own)
pay Propose a payment to another identity
confirm Confirm a proposed payment addressed to you
history Print recent log history
transactions Show settled and pending transactions, naming the listing each marketplace payment settled
vouch Vouch for another identity
backup Export a social-recovery package sealed to a set of holders
recover Reconstruct an identity from a recovery package and decrypted shards
list Publish a listing offering something to the marketplace
browse Browse what is on offer
show-listing Show one listing in full
my-listings Show the listings you have published, in any state
edit-listing Edit one of your own listings. Only price, description, availability, and expiry can change; surface, category, title, and requirements are fixed at publication. Each field left off is kept as it was
close-listing Take one of your own listings off offer
need Announce something you are looking for
matches Show the listings answering your needs
inquire Open an inquiry against a listing
show-inquiry Show one inquiry thread in full
inquiries Show the inquiries you are a party to
inquiry-reply Reply in an inquiry, optionally with a counter-offer
inquiry-close Close an inquiry: agree on a price, or decline
settle-inquiry Pay for an inquiry the provider has agreed. Signs a listing-linked payment at the agreed price, which the provider then confirms with `rrn confirm`. Only the inquiry's buyer may settle it; a second run returns the existing payment rather than paying twice
contract Sign up to a recurring service, from an inquiry the provider has agreed
contracts Show the service contracts you are a party to
show-contract Show one service contract in full
terminate-contract End one of your service contracts early
governance Community governance: the Charter, proposals, voting, and statutes (ADR-0012)
dispute Disputes: contest a confirmed payment, respond, rule as a juror, resolve (ADR-0014)
cert Offline spending certificates: reserve debt-floor headroom before a partition, and list your outstanding certificates (ADR-0021)
paper Paper fallback: export payloads to printable QR sheets and ingest scanned QR text (ADR-0020 §3 / ADR-0021 §4). The CLI consumes scanned QR *text* — one payload string per line, from any scanner app — it does not read camera images. A member exporting their *own* signed records prints them with `rrn wallet export` (ADR-0028); these tools are the courier's
dtn Delay-tolerant networking: originate an outbound bundle push to a peer over the Reticulum transport, inspect tracked pushes, and publish this station's own reachability binding (ADR-0013, ADR-0020)
wallet The self-custody member wallet (ADR-0028): hold your own key on a computer with no phone, sign payments offline into a durable outbox, carry them on paper or a DTN bundle, and pair/sync over the sealed channel when online
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
rrn init
Explain how to bootstrap a station (init runs in the daemon, not here)
Usage: rrn init [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn whoami
Print this station's own address
Usage: rrn whoami [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn status
Show the station's connectivity / offline posture: per-peer reachability, the mobile listener state, and pending DTN queue depths
Usage: rrn status [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn balance
Show a balance (defaults to your own)
Usage: rrn balance [OPTIONS] [ADDRESS]
Arguments:
[ADDRESS]
The `rrn1…` address to query; omitted means your own
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn pay
Propose a payment to another identity
Usage: rrn pay [OPTIONS] <RECEIVER> <AMOUNT>
Arguments:
<RECEIVER>
The receiver's `rrn1…` address
<AMOUNT>
Amount in Commons, e.g. `3`, `3.5`, or `3.50`
Options:
--memo <MEMO>
Optional memo recorded in the signed proposal
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn confirm
Confirm a proposed payment addressed to you
Usage: rrn confirm [OPTIONS] <TX_ID>
Arguments:
<TX_ID>
The hex transaction id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn history
Print recent log history
Usage: rrn history [OPTIONS]
Options:
--limit <LIMIT>
Maximum number of (most-recent-first) entries
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn transactions
Show settled and pending transactions, naming the listing each marketplace payment settled
Usage: rrn transactions [OPTIONS] [ADDRESS]
Arguments:
[ADDRESS]
The `rrn1…` address to view; omitted means your own
Options:
--limit <LIMIT>
Maximum number of (most-recent-first) rows
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn vouch
Vouch for another identity
Usage: rrn vouch [OPTIONS] <SUBJECT>
Arguments:
<SUBJECT>
The `rrn1…` address being vouched for
Options:
--socket <SOCKET>
Path to the station's Unix socket
--statement <STATEMENT>
A free-text statement
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--stake <STAKE>
Reputation to stake, in points, e.g. `1.50`
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn backup
Export a social-recovery package sealed to a set of holders
Usage: rrn backup [OPTIONS] --threshold <THRESHOLD> --output <OUTPUT> <HOLDERS>...
Arguments:
<HOLDERS>...
Holder `rrn1…` addresses (two or more)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--threshold <THRESHOLD>
`K` — how many shards are required to reconstruct
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--output <OUTPUT>
Where to write the recovery package
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn recover
Reconstruct an identity from a recovery package and decrypted shards
Usage: rrn recover [OPTIONS] --package <PACKAGE>
Options:
--package <PACKAGE>
Path to a `.rrnrecovery` package
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--shards <SHARDS>
Comma-separated decrypted raw-shard files
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn list
Publish a listing offering something to the marketplace
Usage: rrn list [OPTIONS] --price <PRICE> --category <CATEGORY> <SURFACE> <TITLE>
Arguments:
<SURFACE>
The surface to publish on
[possible values: goods, services, commons]
<TITLE>
Short name for the offer
Options:
--price <PRICE>
Price in Commons, e.g. `3`, `3.5`, or `3.50`. Only a `commons` listing may be negative (a subsidy), written as `-3.50`
--socket <SOCKET>
Path to the station's Unix socket
--category <CATEGORY>
Category from the controlled vocabulary
[possible values: agriculture, construction, education, food, medical, other, tools, transportation]
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--negotiable
Invite offers on the price
--description <DESCRIPTION>
Longer prose describing the offer
--capacity <CAPACITY>
Units available (`goods` only)
--next-slot <NEXT_SLOT>
Next open slot, as a date or unix seconds (`services` only)
--min-reputation <MIN_REPUTATION>
Minimum reputation an inquirer must hold, in points e.g. `1.50`
--community-only
Deal only with members of your own community
--oracle-tier <ORACLE_TIER>
Oracle tier to claim; defaults to the price-based suggestion
--expires <EXPIRES>
Take the listing off offer after this date (or unix seconds)
--every <EVERY>
Make this a recurring service: how often a period falls due (`services` only)
[possible values: daily, weekly, monthly]
--periods <PERIODS>
How many periods a recurring commitment runs for (required with `--every`)
--notice <NOTICE>
Days of notice to end a recurring contract early
--penalty <PENALTY>
Early-termination penalty in Commons, charged to whoever ends a recurring contract before its natural end
-h, --help
Print help (see a summary with '-h')
rrn browse
Browse what is on offer
Usage: rrn browse [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--text <TEXT>
Free-text query over titles and descriptions
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--surface <SURFACE>
Only this surface
[possible values: goods, services, commons]
--category <CATEGORY>
Only this category
[possible values: agriculture, construction, education, food, medical, other, tools, transportation]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--max-price <MAX_PRICE>
Only listings at or below this price, in Commons. May be negative, to look only at Commons-surface subsidies
--min-reputation <MIN_REPUTATION>
Only providers at or above this reputation, in points
--limit <LIMIT>
Page size
--offset <OFFSET>
Ranked hits to skip
-h, --help
Print help (see a summary with '-h')
rrn show-listing
Show one listing in full
Usage: rrn show-listing [OPTIONS] <LISTING_ID>
Arguments:
<LISTING_ID>
The hex listing id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn my-listings
Show the listings you have published, in any state
Usage: rrn my-listings [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn edit-listing
Edit one of your own listings. Only price, description, availability, and expiry can change; surface, category, title, and requirements are fixed at publication. Each field left off is kept as it was
Usage: rrn edit-listing [OPTIONS] <LISTING_ID>
Arguments:
<LISTING_ID>
The hex listing id
Options:
--price <PRICE>
New price in Commons, e.g. `3`, `3.5`, or `-3.50` (Commons subsidy)
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--negotiable <NEGOTIABLE>
Invite offers (`--negotiable true`) or fix the price (`--negotiable false`). Omitted leaves the pricing model
[possible values: true, false]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--description <DESCRIPTION>
New description
--capacity <CAPACITY>
New units available (`goods` only)
--next-slot <NEXT_SLOT>
New next open slot, as a date or unix seconds (`services` only)
--expires <EXPIRES>
New expiry, as a date or unix seconds
--clear-expiry
Remove the expiry so the listing stands until closed. Wins over `--expires` if both are given
-h, --help
Print help (see a summary with '-h')
rrn close-listing
Take one of your own listings off offer
Usage: rrn close-listing [OPTIONS] <LISTING_ID>
Arguments:
<LISTING_ID>
The hex listing id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn need
Announce something you are looking for
Usage: rrn need [OPTIONS] --valid-until <VALID_UNTIL> <CATEGORY> <QUANTITY>
Arguments:
<CATEGORY>
Category from the controlled vocabulary
[possible values: agriculture, construction, education, food, medical, other, tools, transportation]
<QUANTITY>
How many units you want
Options:
--max-price <MAX_PRICE>
The most you will pay, in Commons. May be negative, to seek only Commons-surface work that pays
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--valid-until <VALID_UNTIL>
Valid through this date (`YYYY-MM-DD`), a `+<N>d` offset, or unix seconds
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn matches
Show the listings answering your needs
Usage: rrn matches [OPTIONS] [SEQ]
Arguments:
[SEQ]
The log seq of one need (from `rrn need`); omitted means all of them
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn inquire
Open an inquiry against a listing
Usage: rrn inquire [OPTIONS] <LISTING_ID>
Arguments:
<LISTING_ID>
The hex listing id
Options:
--offer <OFFER>
Your opening offer, in Commons; omitted accepts the listed price
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--message <MESSAGE>
An opening message
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn show-inquiry
Show one inquiry thread in full
Usage: rrn show-inquiry [OPTIONS] <INQUIRY_ID>
Arguments:
<INQUIRY_ID>
The hex inquiry id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn inquiries
Show the inquiries you are a party to
Usage: rrn inquiries [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn inquiry-reply
Reply in an inquiry, optionally with a counter-offer
Usage: rrn inquiry-reply [OPTIONS] <INQUIRY_ID>
Arguments:
<INQUIRY_ID>
The hex inquiry id
Options:
--offer <OFFER>
A counter-offer, in Commons
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--message <MESSAGE>
The message body
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn inquiry-close
Close an inquiry: agree on a price, or decline
Usage: rrn inquiry-close [OPTIONS] --outcome <OUTCOME> <INQUIRY_ID>
Arguments:
<INQUIRY_ID>
The hex inquiry id
Options:
--outcome <OUTCOME>
How it ends
[possible values: agreed, declined]
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--price <PRICE>
The agreed price, in Commons (for `--outcome agreed`); omitted takes the listed price
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn settle-inquiry
Pay for an inquiry the provider has agreed. Signs a listing-linked payment at the agreed price, which the provider then confirms with `rrn confirm`. Only the inquiry's buyer may settle it; a second run returns the existing payment rather than paying twice
Usage: rrn settle-inquiry [OPTIONS] <INQUIRY_ID>
Arguments:
<INQUIRY_ID>
The hex id of the agreed inquiry
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn contract
Sign up to a recurring service, from an inquiry the provider has agreed
Usage: rrn contract [OPTIONS] <INQUIRY_ID>
Arguments:
<INQUIRY_ID>
The hex id of the agreed inquiry
Options:
--metric <METRICS>
A free-form note to record on the contract, as `key=value`; repeatable. No logic reads these; they are not part of the terms
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn contracts
Show the service contracts you are a party to
Usage: rrn contracts [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn show-contract
Show one service contract in full
Usage: rrn show-contract [OPTIONS] <CONTRACT_ID>
Arguments:
<CONTRACT_ID>
The hex contract id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn terminate-contract
End one of your service contracts early
Usage: rrn terminate-contract [OPTIONS] <CONTRACT_ID>
Arguments:
<CONTRACT_ID>
The hex contract id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance
Community governance: the Charter, proposals, voting, and statutes (ADR-0012)
Usage: rrn governance [OPTIONS] <COMMAND>
Commands:
charter-init Publish the community's genesis Charter. With no `--founder-key`, the station wallet is the sole founder (the one-command solo bootstrap); pass a founder key file per founder for a multi-founder genesis
charter-begin Open a distributed founding ceremony: declare the founders **by address** so a founder who holds their key on a phone can sign later, on-device, without ever handing over the secret. The Charter publishes automatically once `ceil(founders × 0.75)` have signed
charter-status Show the founding ceremony's progress: who has signed, the threshold, and the Charter body (`body_hex`) a station founder signs with `charter-sign`
charter-sign Sign a shared Charter body with this station's wallet, printing the `(pubkey, signature)` a station founder hands back to the coordinator
charter-add-signature Add a founder's collected signature to the pending Charter (a station founder's `charter-sign` output), publishing it if the threshold is met
charter Show the community's current (effective) Charter
list List proposals with their phase and vote so far
show Show one proposal in full
propose Author a proposal (signed by the station wallet)
cosign Endorse a proposal, carrying it toward the co-sign threshold
vote Cast a ballot on a published proposal
statutes List the statutes in force
emergency-declare Declare an emergency (ADR-0023): compresses the emergency-proposal window once a supermajority of the electorate co-signs. Signed by the station wallet, whose signature counts toward the threshold
emergency-cosign Co-sign an emergency declaration (or a lapse), toward its supermajority
emergency-lapse Raise a lapse to end an active emergency early; it takes effect once its own co-signatures reach the supermajority. Prints the lapse hash to co-sign
emergency-status Show the active emergency (if any) and the full derived timeline
emergency-report The post-emergency report: every activation, its co-signers, and the measures passed under it (ADR-0023 §6)
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance charter-init
Publish the community's genesis Charter. With no `--founder-key`, the station wallet is the sole founder (the one-command solo bootstrap); pass a founder key file per founder for a multi-founder genesis
Usage: rrn governance charter-init [OPTIONS] --community-id <COMMUNITY_ID>
Options:
--community-id <COMMUNITY_ID>
A stable identifier for the community
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--principle <PRINCIPLES>
A founding principle; repeatable
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--right <RIGHTS>
A guaranteed right; repeatable
--founder-key <FOUNDER_KEYS>
A file holding a founder's hex-encoded 32-byte secret key; repeatable. Omit entirely to sign with the station wallet as the sole founder
-h, --help
Print help (see a summary with '-h')
rrn governance charter-begin
Open a distributed founding ceremony: declare the founders **by address** so a founder who holds their key on a phone can sign later, on-device, without ever handing over the secret. The Charter publishes automatically once `ceil(founders × 0.75)` have signed
Usage: rrn governance charter-begin [OPTIONS] --community-id <COMMUNITY_ID>
Options:
--community-id <COMMUNITY_ID>
A stable identifier for the community
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--principle <PRINCIPLES>
A founding principle; repeatable
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--right <RIGHTS>
A guaranteed right; repeatable
--founder <FOUNDERS>
A founder's bech32 `rrn1…` address; repeatable. Include the station's own address to have it co-sign at once
-h, --help
Print help (see a summary with '-h')
rrn governance charter-status
Show the founding ceremony's progress: who has signed, the threshold, and the Charter body (`body_hex`) a station founder signs with `charter-sign`
Usage: rrn governance charter-status [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance charter-sign
Sign a shared Charter body with this station's wallet, printing the `(pubkey, signature)` a station founder hands back to the coordinator
Usage: rrn governance charter-sign [OPTIONS] --body <BODY_HEX>
Options:
--body <BODY_HEX>
The Charter body's canonical bytes, hex (the coordinator's `body_hex`)
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance charter-add-signature
Add a founder's collected signature to the pending Charter (a station founder's `charter-sign` output), publishing it if the threshold is met
Usage: rrn governance charter-add-signature [OPTIONS] --pubkey <PUBKEY_HEX> --signature <SIGNATURE_HEX>
Options:
--pubkey <PUBKEY_HEX>
The founder's public key, hex-encoded
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--signature <SIGNATURE_HEX>
Their signature over the Charter body, hex-encoded
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance charter
Show the community's current (effective) Charter
Usage: rrn governance charter [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance list
List proposals with their phase and vote so far
Usage: rrn governance list [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance show
Show one proposal in full
Usage: rrn governance show [OPTIONS] <PROPOSAL_ID>
Arguments:
<PROPOSAL_ID>
The hex proposal id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance propose
Author a proposal (signed by the station wallet)
Usage: rrn governance propose [OPTIONS] <TITLE> <BODY>
Arguments:
<TITLE>
The short title
<BODY>
The full body, markdown allowed
Options:
--kind <KIND>
Kind: `statute` (default), `administrative_rule`, or `emergency`
[default: statute]
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--scope <SCOPE>
The scope, required for `administrative_rule`
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--expires-at <EXPIRES_AT>
Unix seconds an `emergency` measure expires
-h, --help
Print help (see a summary with '-h')
rrn governance cosign
Endorse a proposal, carrying it toward the co-sign threshold
Usage: rrn governance cosign [OPTIONS] <PROPOSAL_ID>
Arguments:
<PROPOSAL_ID>
The hex proposal id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance vote
Cast a ballot on a published proposal
Usage: rrn governance vote [OPTIONS] <PROPOSAL_ID> <CHOICE>
Arguments:
<PROPOSAL_ID>
The hex proposal id
<CHOICE>
`yes`, `no`, or `abstain`
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance statutes
List the statutes in force
Usage: rrn governance statutes [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance emergency-declare
Declare an emergency (ADR-0023): compresses the emergency-proposal window once a supermajority of the electorate co-signs. Signed by the station wallet, whose signature counts toward the threshold
Usage: rrn governance emergency-declare [OPTIONS] <REASON> <SCOPE>
Arguments:
<REASON>
The crisis, human-readable (testimony/display only)
<SCOPE>
The declared emergency domain (testimony/display only)
Options:
--duration-secs <DURATION_SECS>
Requested lifetime in seconds (clamped to [24h, 7d]; default 72h)
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--previous <PREVIOUS>
The hex hash of the declaration this renews, if any (advisory)
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance emergency-cosign
Co-sign an emergency declaration (or a lapse), toward its supermajority
Usage: rrn governance emergency-cosign [OPTIONS] <DECLARATION_HASH>
Arguments:
<DECLARATION_HASH>
The hex declaration hash (for a lapse, the lapse's own hash)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance emergency-lapse
Raise a lapse to end an active emergency early; it takes effect once its own co-signatures reach the supermajority. Prints the lapse hash to co-sign
Usage: rrn governance emergency-lapse [OPTIONS] <DECLARATION_HASH>
Arguments:
<DECLARATION_HASH>
The hex hash of the declaration whose emergency to end
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance emergency-status
Show the active emergency (if any) and the full derived timeline
Usage: rrn governance emergency-status [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn governance emergency-report
The post-emergency report: every activation, its co-signers, and the measures passed under it (ADR-0023 §6)
Usage: rrn governance emergency-report [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute
Disputes: contest a confirmed payment, respond, rule as a juror, resolve (ADR-0014)
Usage: rrn dispute [OPTIONS] <COMMAND>
Commands:
list List the disputes currently frozen, with their live jury tally
show Show one dispute in full: grievance, responses, and the seated jury
raise Contest a confirmed transaction, freezing its settlement (station-signed)
respond File the station wallet's side of a live dispute (station-signed)
rule Cast the station wallet's juror verdict (must hold a live seat)
resolve Enact terminal outcomes and lapse expired disputes. With no `tx_id`, sweeps them all; with one, resolves just that dispute
escalate Escalate to the electorate because the jury cannot seat a panel (station-signed; the wallet must be a party). ADR-0014 §5
appeal Appeal a jury ruling to the electorate (station-signed; the wallet must be a party), suspending the ruling's enactment. ADR-0014 §5
vote Cast the station wallet's ballot in an open escalation (must be an eligible, non-party established member)
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute list
List the disputes currently frozen, with their live jury tally
Usage: rrn dispute list [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute show
Show one dispute in full: grievance, responses, and the seated jury
Usage: rrn dispute show [OPTIONS] <TX_ID>
Arguments:
<TX_ID>
The hex id of the disputed transaction
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute raise
Contest a confirmed transaction, freezing its settlement (station-signed)
Usage: rrn dispute raise [OPTIONS] <TX_ID> <REASON>
Arguments:
<TX_ID>
The hex id of the confirmed transaction to contest
<REASON>
A bounded statement of the grievance
Options:
--evidence <EVIDENCE>
Optional hex content hash of out-of-band evidence
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute respond
File the station wallet's side of a live dispute (station-signed)
Usage: rrn dispute respond [OPTIONS] <TX_ID> <STATEMENT>
Arguments:
<TX_ID>
The hex id of the disputed transaction
<STATEMENT>
A bounded statement of your side
Options:
--evidence <EVIDENCE>
Optional hex content hash of out-of-band evidence
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute rule
Cast the station wallet's juror verdict (must hold a live seat)
Usage: rrn dispute rule [OPTIONS] <TX_ID> <RULING>
Arguments:
<TX_ID>
The hex id of the disputed transaction
<RULING>
`uphold` (void the transfer) or `reject` (let it settle)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute resolve
Enact terminal outcomes and lapse expired disputes. With no `tx_id`, sweeps them all; with one, resolves just that dispute
Usage: rrn dispute resolve [OPTIONS] [TX_ID]
Arguments:
[TX_ID]
The hex id of a single disputed transaction to resolve
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute escalate
Escalate to the electorate because the jury cannot seat a panel (station-signed; the wallet must be a party). ADR-0014 §5
Usage: rrn dispute escalate [OPTIONS] <TX_ID>
Arguments:
<TX_ID>
The hex id of the disputed transaction
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute appeal
Appeal a jury ruling to the electorate (station-signed; the wallet must be a party), suspending the ruling's enactment. ADR-0014 §5
Usage: rrn dispute appeal [OPTIONS] <TX_ID>
Arguments:
<TX_ID>
The hex id of the disputed transaction
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dispute vote
Cast the station wallet's ballot in an open escalation (must be an eligible, non-party established member)
Usage: rrn dispute vote [OPTIONS] <TX_ID> <RULING>
Arguments:
<TX_ID>
The hex id of the escalated transaction
<RULING>
`uphold` (void the transfer) or `reject` (let it settle)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn cert
Offline spending certificates: reserve debt-floor headroom before a partition, and list your outstanding certificates (ADR-0021)
Usage: rrn cert [OPTIONS] <COMMAND>
Commands:
request Reserve a headroom certificate for your own wallet, ahead of going offline. Its cap is held against your debt-floor headroom until it expires or you return it
list List outstanding certificates (defaults to your own)
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn cert request
Reserve a headroom certificate for your own wallet, ahead of going offline. Its cap is held against your debt-floor headroom until it expires or you return it
Usage: rrn cert request [OPTIONS] <CAP>
Arguments:
<CAP>
Cap to reserve, in Commons, e.g. `10` or `2.50`
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn cert list
List outstanding certificates (defaults to your own)
Usage: rrn cert list [OPTIONS] [ADDRESS]
Arguments:
[ADDRESS]
The `rrn1…` address to query; omitted means your own
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn paper
Paper fallback: export payloads to printable QR sheets and ingest scanned QR text (ADR-0020 §3 / ADR-0021 §4). The CLI consumes scanned QR *text* — one payload string per line, from any scanner app — it does not read camera images. A member exporting their *own* signed records prints them with `rrn wallet export` (ADR-0028); these tools are the courier's
Usage: rrn paper [OPTIONS] <COMMAND>
Commands:
show Classify and pretty-print any paper payload(s) without ingesting them — the courier's inspection path. Verifies every signature it can and reports missing chunks for incomplete multi-part groups. Needs no station
ingest Reassemble scanned QR text, submit carried bundles to the station, and print each record's outcome. Re-ingesting the same input is idempotent
export-receipts Fetch pending delivery receipts and render them for a courier to carry back to their authors (the carry-back leg). Fetching never confirms delivery — only the author's own device does
cert Print a headroom-certificate wallet card: an existing certificate (by id) or one requested now, as an `rrncert:` QR with member, cap, and expiry
credential Print a member credential card: the member's bare address QR (qr-payloads §1 — no new format) with an optional name
render Render already-encoded QR-text lines to a printable sheet: one `chunk_NN_of_MM.png` per line plus a captioned `sheet.pdf`. The shared print primitive (what `export-receipts` uses internally)
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn paper show
Classify and pretty-print any paper payload(s) without ingesting them — the courier's inspection path. Verifies every signature it can and reports missing chunks for incomplete multi-part groups. Needs no station
Usage: rrn paper show [OPTIONS] --in <INPUT>
Options:
--in <INPUT>
Files of QR-text lines (one payload string per line). Repeatable
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn paper ingest
Reassemble scanned QR text, submit carried bundles to the station, and print each record's outcome. Re-ingesting the same input is idempotent
Usage: rrn paper ingest [OPTIONS] --in <INPUT>
Options:
--in <INPUT>
Files of QR-text lines (one payload string per line). Repeatable
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--out <OUT>
Write the station's delivery receipts here (as `receipts.txt` + QR sheet) for the carry-back leg
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn paper export-receipts
Fetch pending delivery receipts and render them for a courier to carry back to their authors (the carry-back leg). Fetching never confirms delivery — only the author's own device does
Usage: rrn paper export-receipts [OPTIONS] --out <OUT>
Options:
--out <OUT>
Where to write `receipts.txt` and the QR sheet
--socket <SOCKET>
Path to the station's Unix socket
--author <AUTHORS>
Restrict to receipts for these `rrn1…` authors. Repeatable; omit for the whole pending queue
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--since <SINCE>
Only receipts for records admitted at/after this admission-clock time
-h, --help
Print help (see a summary with '-h')
rrn paper cert
Print a headroom-certificate wallet card: an existing certificate (by id) or one requested now, as an `rrncert:` QR with member, cap, and expiry
Usage: rrn paper cert [OPTIONS] --out <OUT>
Options:
--out <OUT>
Where to write the card (PDF + PNG)
--socket <SOCKET>
Path to the station's Unix socket
--cert-id <CERT_ID>
Export an existing certificate by its hex content id
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--request <REQUEST>
Reserve a new certificate for this cap (Commons, e.g. `10`) and export it
-h, --help
Print help (see a summary with '-h')
rrn paper credential
Print a member credential card: the member's bare address QR (qr-payloads §1 — no new format) with an optional name
Usage: rrn paper credential [OPTIONS] --out <OUT> --address <ADDRESS>
Options:
--out <OUT>
Where to write the card (PDF + PNG)
--socket <SOCKET>
Path to the station's Unix socket
--address <ADDRESS>
The member's `rrn1…` address
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--name <NAME>
A human name to caption the card with
-h, --help
Print help (see a summary with '-h')
rrn paper render
Render already-encoded QR-text lines to a printable sheet: one `chunk_NN_of_MM.png` per line plus a captioned `sheet.pdf`. The shared print primitive (what `export-receipts` uses internally)
Usage: rrn paper render [OPTIONS] --in <INPUT> --out <OUT>
Options:
--in <INPUT>
A file of QR-text lines to render
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--out <OUT>
Where to write the PNGs and `sheet.pdf`
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dtn
Delay-tolerant networking: originate an outbound bundle push to a peer over the Reticulum transport, inspect tracked pushes, and publish this station's own reachability binding (ADR-0013, ADR-0020)
Usage: rrn dtn [OPTIONS] <COMMAND>
Commands:
push Push a bundle file to a peer over the DTN transport. Returns a queued acknowledgement — delivery is confirmed later by the peer's receipt (watch `rrn dtn status`)
status List tracked outbound pushes: peer, records, state, attempts, and the correlated receipt's outcome summary
bind Publish this station's own transport binding at a Reticulum destination hex, and print the signed binding envelope to carry to peers
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dtn push
Push a bundle file to a peer over the DTN transport. Returns a queued acknowledgement — delivery is confirmed later by the peer's receipt (watch `rrn dtn status`)
Usage: rrn dtn push [OPTIONS] --peer <PEER> --bundle <BUNDLE>
Options:
--peer <PEER>
The peer: an `rrn1…` address (resolved via the binding directory) or a bare Reticulum destination hex
--socket <SOCKET>
Path to the station's Unix socket
--bundle <BUNDLE>
Path to a bundle file (encoded bytes), e.g. one produced by the paper tooling
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dtn status
List tracked outbound pushes: peer, records, state, attempts, and the correlated receipt's outcome summary
Usage: rrn dtn status [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn dtn bind
Publish this station's own transport binding at a Reticulum destination hex, and print the signed binding envelope to carry to peers
Usage: rrn dtn bind [OPTIONS] --destination <DESTINATION>
Options:
--destination <DESTINATION>
The Reticulum destination hex this station is reachable at
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet
The self-custody member wallet (ADR-0028): hold your own key on a computer with no phone, sign payments offline into a durable outbox, carry them on paper or a DTN bundle, and pair/sync over the sealed channel when online
Usage: rrn wallet [OPTIONS] <COMMAND>
Commands:
init Create a new member wallet (or restore a backed-up one) pinned to a station
recover Rebuild a lost key from your recovery circle, into a fresh wallet home
pair Pair with the station over the sealed channel; prints the SAS for the operator to confirm
sync Re-anchor and sync: nonce, outbox head, balance, and delivery receipts
status Show local wallet state — never unlocks the key
pay Sign a payment: chained into the outbox, pending until submitted/carried
confirm Confirm a proposed payment addressed to you
vote Cast a governance ballot
dispute Contest a confirmed payment
vouch Vouch for another member (online only — needs the station reachable)
cert Headroom certificates (ADR-0021): request, import, list, return
export Export pending outbox entries as QR sheets or a raw bundle (offline path)
submit Submit pending entries online: bundle → channel → apply receipts
receipts Delivery receipts
show Show local outbox rows and their dispositions
transactions Your transactions, read live from the station over the channel
help Print this message or the help of the given subcommand(s)
Options:
--home <HOME>
The wallet home directory (else `$RRN_WALLET_HOME`, else `$HOME/.railroad/wallet`)
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet init
Create a new member wallet (or restore a backed-up one) pinned to a station
Usage: rrn wallet init [OPTIONS] --station <STATION>
Options:
--socket <SOCKET>
Path to the station's Unix socket
--station <STATION>
The station's `rrn1…` address to pin (learned from the operator, in person — the pin is the security boundary)
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--restore <RESTORE>
Restore from a backed-up `member.rrnwallet` instead of a fresh key. The restored wallet refuses signing until one `sync` re-anchors it
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet recover
Rebuild a lost key from your recovery circle, into a fresh wallet home.
Runs the reconstruction ceremony on *this* device: prints a request QR and a fingerprint for your holders to confirm, gathers their responses, and rebuilds your key locally — nothing touches the station (ADR-0006). The recovered wallet is treated as restored: it refuses signing until one `sync` re-anchors it (ADR-0028).
Usage: rrn wallet recover [OPTIONS] --station <STATION>
Options:
--socket <SOCKET>
Path to the station's Unix socket
--station <STATION>
The station's `rrn1…` address to pin (as for `init`)
--address <ADDRESS>
The `rrn1…` address being recovered. Prompted for if omitted (read it off your old credential card or a friend's contact list)
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--force
Overwrite an existing wallet home
-h, --help
Print help (see a summary with '-h')
rrn wallet pair
Pair with the station over the sealed channel; prints the SAS for the operator to confirm
Usage: rrn wallet pair [OPTIONS] --url <URL>
Options:
--socket <SOCKET>
Path to the station's Unix socket
--url <URL>
The station's mobile listener, `host:port`
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet sync
Re-anchor and sync: nonce, outbox head, balance, and delivery receipts
Usage: rrn wallet sync [OPTIONS]
Options:
--no-receipts
Skip fetching and applying delivery receipts
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet status
Show local wallet state — never unlocks the key
Usage: rrn wallet status [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet pay
Sign a payment: chained into the outbox, pending until submitted/carried
Usage: rrn wallet pay [OPTIONS] <RECEIVER> <AMOUNT>
Arguments:
<RECEIVER>
The receiver's `rrn1…` address
<AMOUNT>
Amount in Commons, e.g. `3`, `3.5`, or `3.50`
Options:
--memo <MEMO>
Optional memo recorded in the signed proposal
--socket <SOCKET>
Path to the station's Unix socket
--cert <CERT>
Spend against a held headroom certificate (id hex or prefix)
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
--expires-in <EXPIRES_IN>
Override the proposal's validity window, in seconds
--carrier <CARRIER>
The carrier this spend is bound for (sets the default expiry)
Possible values:
- fast: Online submit or a same-day courier: the normal 24h proposal TTL
- slow: Paper / LoRa / SMS: a two-week window so the record survives delivery
[default: fast]
--voucher-out <VOUCHER_OUT>
Also write `rrnspend:` voucher lines for offline verification
-h, --help
Print help (see a summary with '-h')
rrn wallet confirm
Confirm a proposed payment addressed to you
Usage: rrn wallet confirm [OPTIONS] <TX_ID>
Arguments:
<TX_ID>
The hex transaction id
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet vote
Cast a governance ballot
Usage: rrn wallet vote [OPTIONS] <PROPOSAL_ID> <CHOICE>
Arguments:
<PROPOSAL_ID>
The proposal id, hex
<CHOICE>
yes | no | abstain
[possible values: yes, no, abstain]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet dispute
Contest a confirmed payment
Usage: rrn wallet dispute [OPTIONS] --reason <REASON> <TX_ID>
Arguments:
<TX_ID>
The hex transaction id
Options:
--reason <REASON>
A bounded free-text statement of the grievance
--socket <SOCKET>
Path to the station's Unix socket
--evidence-hash <EVIDENCE_HASH>
Optional content hash of out-of-band evidence (hex)
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet vouch
Vouch for another member (online only — needs the station reachable)
Usage: rrn wallet vouch [OPTIONS] --statement <STATEMENT> --stake <STAKE> <ADDRESS>
Arguments:
<ADDRESS>
The `rrn1…` address to vouch for
Options:
--socket <SOCKET>
Path to the station's Unix socket
--statement <STATEMENT>
The attestation statement
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--stake <STAKE>
The reputation stake, in Commons
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet cert
Headroom certificates (ADR-0021): request, import, list, return
Usage: rrn wallet cert [OPTIONS] <COMMAND>
Commands:
request Request a headroom certificate (online round-trip)
import Import a certificate from an `rrncert:` line or raw envelope file
list List certificates the wallet holds
return Return an outstanding certificate early (chained record)
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet export
Export pending outbox entries as QR sheets or a raw bundle (offline path).
The encoding is a positional argument (`qr` or `bundle`): the global `--format json|text` already owns `--format`, so this cannot reuse it.
Usage: rrn wallet export [OPTIONS] --out <OUT> <ENCODING>
Arguments:
<ENCODING>
`qr` (printable sheets) or `bundle` (raw `payload.bundle`)
Possible values:
- qr: Printable QR sheets (`rrnp:` chunks + PNG + PDF), for `rrn paper ingest`
- bundle: A single `payload.bundle` of raw bytes, for `rrn dtn push --bundle`
Options:
--out <OUT>
The output directory
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--max-entries <MAX_ENTRIES>
Cap the number of pending entries carried
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet submit
Submit pending entries online: bundle → channel → apply receipts
Usage: rrn wallet submit [OPTIONS]
Options:
--max-entries <MAX_ENTRIES>
Cap the number of pending entries submitted in one bundle
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet receipts
Delivery receipts
Usage: rrn wallet receipts [OPTIONS] <COMMAND>
Commands:
apply Apply station-signed receipts from scanned QR text or raw bytes
help Print this message or the help of the given subcommand(s)
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet show
Show local outbox rows and their dispositions
Usage: rrn wallet show [OPTIONS]
Options:
--pending
Only still-pending rows
--socket <SOCKET>
Path to the station's Unix socket
--all
Every row, acked and pending (the default shows pending + recent)
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
rrn wallet transactions
Your transactions, read live from the station over the channel
Usage: rrn wallet transactions [OPTIONS]
Options:
--socket <SOCKET>
Path to the station's Unix socket
--format <FORMAT>
Output format
Possible values:
- text: Terse, human/grep-friendly lines
- json: One-line JSON for piping
[default: text]
--color <COLOR>
Colorize text output. Off unless asked for
Possible values:
- never: No escape codes, ever
- always: Colorize, whatever stdout is attached to
[default: never]
-h, --help
Print help (see a summary with '-h')
station command reference
station is the daemon: it holds the community’s wallet and log, runs settlement, talks to peers, and serves the rrn client over a Unix socket. Most of its subcommands are one-time or occasional operator tasks; station run is the long-running one.
Generated page. This is the
--helptext of every command, captured from a build of thestationrepo byscripts/gen-reference.sh. Edit the help strings there, not this file.
station
The Railroad Network station daemon: holds the wallet and log, runs settlement, gossips with peers, and serves the rrn CLI over a Unix socket.
Usage: station [OPTIONS] [COMMAND]
Commands:
init Bootstrap a new station: generate an identity and initialize storage
run Run the daemon (default)
peers Inspect the static peer configuration
pair-mobile Confirm a mobile's pairing request (ADR-0008). With no address, lists the pending requests and their confirmation codes; pass an address to confirm it after comparing the code with the mobile's screen in person
list-mobiles List the mobiles currently paired with this station
unpair Revoke a mobile's pairing by its bech32 address
backup Write an encrypted backup of this station (wallet + ledger + config)
restore Restore a station from an encrypted backup archive into the data dir
recovery Manage key recovery: split the station key across trusted holders so a threshold of them can restore it after a lost passphrase (ADR-0016)
status Report the at-rest storage profile, and (encrypted profile) whether the state volume is currently unlocked. Works with the daemon stopped
encrypt-in-place Migrate this plaintext station to the encrypted at-rest profile: provision a member-keyed LUKS container, move the wallet and ledger inside, and split the Volume Master Key across holders (ADR-0024). Linux only. One-way — keep a backup first, and destroy the old media afterward (secure erase is unreliable on flash/SD)
unlock Run the boot ceremony and unlock (mount) the encrypted state volume, so the daemon can then be started (ADR-0024). Linux only. Prints a request QR and a console fingerprint for holders to confirm, then reads their responses
vmk Manage the Volume Master Key custody (encrypted profile, ADR-0024)
help Print this message or the help of the given subcommand(s)
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
-V, --version Print version
station init
Bootstrap a new station: generate an identity and initialize storage
Usage: station init [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station run
Run the daemon (default)
Usage: station run [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station peers
Inspect the static peer configuration
Usage: station peers [OPTIONS] <COMMAND>
Commands:
list Print the configured peers
help Print this message or the help of the given subcommand(s)
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station peers list
Print the configured peers
Usage: station peers list [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station pair-mobile
Confirm a mobile's pairing request (ADR-0008). With no address, lists the pending requests and their confirmation codes; pass an address to confirm it after comparing the code with the mobile's screen in person
Usage: station pair-mobile [OPTIONS] [ADDRESS]
Arguments:
[ADDRESS] The bech32 address of the pending mobile to confirm
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station list-mobiles
List the mobiles currently paired with this station
Usage: station list-mobiles [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station unpair
Revoke a mobile's pairing by its bech32 address
Usage: station unpair [OPTIONS] <ADDRESS>
Arguments:
<ADDRESS> The mobile's bech32 address
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station backup
Write an encrypted backup of this station (wallet + ledger + config).
Safe to run while the station is running: the ledger is captured as a consistent live snapshot. Prompts for the wallet passphrase, which both protects the archive and is verified before anything is written.
Usage: station backup [OPTIONS]
Options:
--data-dir <DATA_DIR>
Data directory (wallet, database, socket, config)
--out <OUT>
Where to write the archive. Defaults to a timestamped file `station-backup-<unix>.rrnbak` in the current directory
-h, --help
Print help (see a summary with '-h')
station restore
Restore a station from an encrypted backup archive into the data dir.
Refuses to overwrite a data dir that already holds a station unless `--force` is given. Restore into a stopped station (or a fresh dir).
Usage: station restore [OPTIONS] <ARCHIVE>
Arguments:
<ARCHIVE>
The backup archive to restore
Options:
--data-dir <DATA_DIR>
Data directory (wallet, database, socket, config)
--force
Overwrite even if the data dir already holds a wallet or database
-h, --help
Print help (see a summary with '-h')
station recovery
Manage key recovery: split the station key across trusted holders so a threshold of them can restore it after a lost passphrase (ADR-0016)
Usage: station recovery [OPTIONS] <COMMAND>
Commands:
setup Arm recovery: split the station key across holders and print a shard QR for each. Prompts for the wallet passphrase. Re-running re-splits and invalidates shards handed out before
status Show the current recovery configuration
show-shard Re-display one holder's shard QR (for redelivery)
restore Reconstruct the station key from a threshold of holders after a lost passphrase. Prints a request QR for holders to scan, then reads their responses. In place by default (data dir intact); pass a backup archive to rebuild on a fresh machine
help Print this message or the help of the given subcommand(s)
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station recovery setup
Arm recovery: split the station key across holders and print a shard QR for each. Prompts for the wallet passphrase. Re-running re-splits and invalidates shards handed out before
Usage: station recovery setup [OPTIONS] --holder <ADDRESS> --threshold <THRESHOLD>
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
--holder <ADDRESS> A holder's `rrn1…` address. Repeat once per holder (N total)
--threshold <THRESHOLD> K — how many holders must cooperate to recover (2 ≤ K ≤ N)
-h, --help Print help
station recovery status
Show the current recovery configuration
Usage: station recovery status [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station recovery show-shard
Re-display one holder's shard QR (for redelivery)
Usage: station recovery show-shard [OPTIONS] <ADDRESS>
Arguments:
<ADDRESS> The holder's `rrn1…` address
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station recovery restore
Reconstruct the station key from a threshold of holders after a lost passphrase. Prints a request QR for holders to scan, then reads their responses. In place by default (data dir intact); pass a backup archive to rebuild on a fresh machine
Usage: station recovery restore [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
--from-backup <ARCHIVE> Rebuild from this backup archive (total-loss recovery). Omit to re-key an intact data dir whose passphrase was lost
--force Overwrite even if the data dir already holds a station
-h, --help Print help
station status
Report the at-rest storage profile, and (encrypted profile) whether the state volume is currently unlocked. Works with the daemon stopped
Usage: station status [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station encrypt-in-place
Migrate this plaintext station to the encrypted at-rest profile: provision a member-keyed LUKS container, move the wallet and ledger inside, and split the Volume Master Key across holders (ADR-0024). Linux only. One-way — keep a backup first, and destroy the old media afterward (secure erase is unreliable on flash/SD)
Usage: station encrypt-in-place [OPTIONS] --holder <ADDRESS>
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
--holder <ADDRESS> A VMK holder's `rrn1…` address. Repeat once per holder (N total)
--threshold <THRESHOLD> K — how many holders must cooperate at each boot ceremony (2 ≤ K ≤ N). Defaults to the configured `[storage.encrypted] threshold` (3 if unset)
-h, --help Print help
station unlock
Run the boot ceremony and unlock (mount) the encrypted state volume, so the daemon can then be started (ADR-0024). Linux only. Prints a request QR and a console fingerprint for holders to confirm, then reads their responses
Usage: station unlock [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station vmk
Manage the Volume Master Key custody (encrypted profile, ADR-0024)
Usage: station vmk [OPTIONS] <COMMAND>
Commands:
status Show the VMK descriptor (address, K/N) and, when the volume is mounted, the current holder set
refresh Re-split the VMK to a new holder set. Old and new shards cannot be combined, but a full quorum of the *old* holders can still reconstruct the same key — to truly revoke, rotate the VMK (re-migrate). Runs a boot ceremony (the volume must already be unlocked) and prints the new shard QRs
help Print this message or the help of the given subcommand(s)
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station vmk status
Show the VMK descriptor (address, K/N) and, when the volume is mounted, the current holder set
Usage: station vmk status [OPTIONS]
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
-h, --help Print help
station vmk refresh
Re-split the VMK to a new holder set. Old and new shards cannot be combined, but a full quorum of the *old* holders can still reconstruct the same key — to truly revoke, rotate the VMK (re-migrate). Runs a boot ceremony (the volume must already be unlocked) and prints the new shard QRs
Usage: station vmk refresh [OPTIONS] --holder <ADDRESS>
Options:
--data-dir <DATA_DIR> Data directory (wallet, database, socket, config)
--holder <ADDRESS> The new holder set: an `rrn1…` address, repeated once per holder
--threshold <THRESHOLD> The new threshold K (2 ≤ K ≤ N). Defaults to the current configured threshold when omitted
-h, --help Print help
Configuration
Every setting a station reads lives in one file, config.toml, in its data
directory. This page lists every key with its default and what it means.
Hand-written from the source. This page mirrors the
[StationConfig]structs incrates/rrn-station/src/config.rsof thestationrepo, which are the authority. It is not generated. If a key here and the code disagree, the code is right and this page needs an edit; re-check it whenever a change to that file lands.
Where it lives and how it is created
The data directory is ~/.railroad/station unless you pass --data-dir to
station. station init creates the directory and writes a default
config.toml: a writer with no peers, listening for other stations on a
loopback port chosen at random between 7400 and 7499, and every other section
at its default. Every section except [network] may be omitted entirely and
takes its defaults.
A malformed file is an error with a line number. It is never silently replaced. The station reads the file once at start; edit it, then restart.
Values are TOML: strings in quotes, numbers bare, lists in square brackets. Amounts are in centicommons, whole numbers, one hundredth of a Common. Durations are in seconds.
Two complete examples
A community’s station, the normal case. It owns the log and never pulls from anyone, so it has no peers:
[network]
listen = "127.0.0.1:7400" # station-to-station port; loopback is correct for a lone writer
role = "writer" # the default
[mobile]
listen = "0.0.0.0:7500" # where phones and command-line wallets connect
advertise = true # announce on the LAN so the app finds it by name
# name = "Railroad Station — Maple Street"
An optional read-only replica of that writer, for audit or an off-site copy. It pulls the writer’s log and admits nothing:
[peers]
list = ["192.168.4.1:7400"] # the writer's [network] listen address
[network]
listen = "127.0.0.1:7401"
role = "replica"
A writer with a non-empty [peers] list refuses to start and names the
fix. A replica with an empty list is valid but useless, and the station warns.
The rule is
ADR-0020:
one log, one writer.
[network]
Where this station accepts connections from other stations, and its role. This is a different port and protocol from the one phones use.
| Key | Default | Meaning |
|---|---|---|
listen | "127.0.0.1:74xx" (random on init) | host:port for inbound station-to-station connections. Required. A writer still serves this port so a replica can copy from it. |
role | "writer" | "writer" owns the community’s log, admits records at its own front door, and never pulls. "replica" pulls the writer’s log and refuses every write: the console, the phone channel, and courier ingest all answer that this station is a read-replica, and the sweeps that would append records do not run. A replica is not a failover. |
[peers]
| Key | Default | Meaning |
|---|---|---|
list | [] | host:port of each station this one pulls the log from. Must be empty on a writer. Configure peers by IP address, not hostname: a dead resolver can stall shutdown. |
[mobile]
How paired phones and command-line wallets reach this station.
| Key | Default | Meaning |
|---|---|---|
listen | "0.0.0.0:7500" | host:port of the member-facing listener. Must be reachable from the LAN, so it defaults to all interfaces. 7500 is what the app expects. |
name | (derived) | The name advertised on the local network. When omitted it is derived from the station’s own address, so it is stable across restarts and distinct between stations. |
advertise | true | Whether to announce on the LAN at all. false runs dark: members must type the station’s address and port by hand. |
subscribe_hold_secs | 30 | How long a phone’s long-poll is held open before an empty heartbeat is returned. |
[settlement]
The delay between a payment being confirmed and balances moving, per tier. The rule is ADR-0011.
| Key | Default | Meaning |
|---|---|---|
window_seconds | (unset) | A uniform override for every tier, in seconds. Meant for demos and drills, where a few seconds lets settlements land the same afternoon. When set it wins over the two keys below. Leave it unset in real use. |
tier1_window_seconds | 86400 (24 hours) | The settlement window for a Tier 1 payment. |
tier2_window_seconds | 172800 (48 hours) | The settlement window for a Tier 2 payment. |
[credit]
How far into debt a member may sign themselves, and the headroom certificates that reserve credit ahead of an outage. The rules are ADR-0018 and ADR-0021.
| Key | Default | Meaning |
|---|---|---|
debt_floor_centi | -2000 (−20 Commons) | The lowest projected balance a member may commit to. Must be 0 or negative. Lowering it is a community decision, not a personal one; a governance-set floor is later work, so today it is the operator’s knob. |
cert_validity_seconds | 604800 (7 days) | How long a newly issued certificate stays valid. Must be greater than 0. |
cert_max_cap_centi | 1000 (10 Commons) | The most one certificate may reserve. Must be greater than 0 and less than 5,000 (the Tier 3 floor of 50 Commons): certificates are for everyday trade, not exceptional transfers. A cap larger than the debt floor’s magnitude only warns, since a member at zero balance could never fill it. |
cert_delivery_grace_seconds | 1209600 (14 days) | How long after a certificate expires a spend against it may still arrive and be admitted, to allow for slow carriers. Must be 0 or more. |
cert_max_outstanding | 4 | How many live certificates one member may hold at once. Must be at least 1. |
[timers]
How often each background sweep fires. All in seconds. The defaults suit a pilot; a demo shortens the first two.
| Key | Default | Meaning |
|---|---|---|
sweep_interval_secs | 30 | The settlement sweep: settles every confirmed payment whose window has elapsed. |
gossip_interval_secs | 5 | How often a replica pulls from its peers. |
reputation_refresh_interval_secs | 3600 (1 hour) | Recomputes the cached standing of every known member from the log. The cache is only a cache. |
listing_expiry_interval_secs | 300 (5 minutes) | Writes down marketplace listings whose expiry has passed. They are already unbuyable the moment they expire. |
inquiry_expiry_interval_secs | 3600 (1 hour) | Closes marketplace inquiries idle for seven days. |
contract_charge_interval_secs | 300 (5 minutes) | Bills every period a recurring service contract has due. Re-sweeping a period is harmless. |
governance_implementation_interval_secs | 3600 (1 hour) | Puts passed proposals whose implementation delay has run into force. |
dispute_resolution_interval_secs | 3600 (1 hour) | Enacts jury majorities and lapses disputes whose window closed unresolved. |
dtn_prune_interval_secs | 3600 (1 hour) | Removes delivery-receipt tracking past its retention (see [dtn]). |
On a replica the sweeps that would append to the log do not run.
[dtn]
Delay-tolerant delivery: the receipts the station signs for records that arrive by courier, paper, radio, or text.
| Key | Default | Meaning |
|---|---|---|
receipt_retention_secs | 2592000 (30 days) | How long a receipt the author has already picked up is kept. A receipt nobody has collected yet is kept four times as long, since a courier may take weeks to carry it home. |
[sidecar]
The supervised Reticulum daemon, rnsd, which carries traffic over mesh and
LoRa radio. Off by default. Reticulum is strictly a carrier and never the
security boundary, per
ADR-0013
and ADR-0026.
You install rnsd yourself; see Radio and text message.
| Key | Default | Meaning |
|---|---|---|
enabled | false | Whether to run the sidecar at all. |
rnsd_path | "rnsd" | Path to, or name of, the rnsd binary. |
config_dir | <data dir>/reticulum | Where the generated Reticulum configuration lives. Generated once and never overwritten, so your edits survive. |
pinned_version | "1.5" | The rnsd version this station is validated against, as a dotted prefix: "1.5" accepts any 1.5.x; "1.5.2" only that release. A mismatch runs the station without the sidecar. |
allow_version_drift | false | Development escape hatch: run a mismatched rnsd anyway, with a loud warning. |
restart_backoff_secs | 5 | Base delay before restarting a sidecar that exited. Doubles per consecutive failure, capped at 300; resets once it has stayed up. |
tcp_listen | (unset) | A host:port for rnsd to accept inbound Reticulum links on. Omitted means no listener is generated. |
tcp_peers | [] | host:port targets rnsd dials out to, one interface each. |
A crashed or degraded sidecar never takes the station down. rrn status
shows its state.
[lora]
The airtime budget the radio transport paces to, and the adapter that drives it. Radio throughput is small and duty-cycle rules cut it further; the station sends money before governance before bulk.
| Key | Default | Meaning |
|---|---|---|
raw_bytes_per_sec | 250.0 | Raw carrier throughput before the duty cycle. |
duty_cycle_percent | 1.0 | Duty cycle as a percentage; 1.0 is 1%, the conservative EU-868 ceiling. A laxer region may raise it. Sustained throughput is raw × duty ÷ 100. |
burst_bytes | 500 | The largest catch-up burst after idle, and the largest single frame the budget passes. Must be at least frame_bytes. |
frame_bytes | 480 | The largest carrier frame the framing layer targets. |
adapter_script | (unset) | Path to the LXMF adapter script (scripts/reticulum/lxmf_adapter.py in the station repo). Set this and enable [sidecar] and delay-tolerant traffic flows over Reticulum. Unset, the sidecar is supervised but carries nothing. |
adapter_python | "python3" | The interpreter that has the rns and lxmf packages. |
push_rescan_secs | 3600 (1 hour) | How often undelivered outbound pushes are re-sent. |
push_ttl_secs | 604800 (7 days) | How long an undelivered push is retried before being marked abandoned. Never silently dropped; rrn dtn status shows it. |
[lora.rnode]
The physical RNode LoRa radio. Absent by default: a station never transmits on a frequency or power nobody chose. Spectrum compliance is the operator’s responsibility per region, and every node on one network must share frequency, bandwidth, and spreading factor. See Radio and text message.
| Key | Default | Meaning |
|---|---|---|
port | (required) | The radio’s serial port, such as /dev/ttyACM0 or /dev/ttyUSB0. |
frequency_hz | none | Centre frequency in hertz. Must be legal where you are and identical on every node. |
tx_power_dbm | none | Transmit power in dBm. Start low; antenna gain counts toward your region’s cap. |
bandwidth_hz | 125000 | Channel bandwidth. |
spreading_factor | 8 | 7 to 12. Higher reaches further and sends less. |
coding_rate | 5 | The denominator of the 4/x coding rate; 5 to 8. |
[sms]
Text messages as a carrier for records a member’s phone already signed. Off by default, and the modem gateway that would carry the texts is not built: enabling this section today leaves the carrier supervised but idle. The format is in the wire specifications.
| Key | Default | Meaning |
|---|---|---|
enabled | false | Whether the SMS carrier is enabled at all. |
station_msisdn | (unset) | The station’s own phone number, the one members text. Required once a gateway exists. |
max_parts_per_message | 4 | The most concatenated parts one text may span. Must be at least 1. |
allowed_senders | "paired" | "paired" processes only numbers a member has bound to their identity; "open" processes any number. Both still check every signature; this is spam control, not security. |
max_inbound_per_hour | 60 | The most texts one sender may send per hour before the rest are dropped. |
[storage]
The at-rest profile. Chosen when the station is provisioned, not a runtime toggle. The encrypted profile is ADR-0024; see Encrypted at rest.
| Key | Default | Meaning |
|---|---|---|
at_rest | "plaintext" | "plaintext" is a flat, unencrypted data directory with only the wallet key encrypted. "encrypted" keeps everything sensitive inside a member-keyed LUKS2 container unlocked by a boot ceremony. Linux only: on any other host the encrypted profile refuses to start rather than serve plaintext. |
[storage.encrypted]
Read only when at_rest = "encrypted". Written for you by
station encrypt-in-place.
| Key | Default | Meaning |
|---|---|---|
container_path | <boot dir>/state.img | The LUKS2 container file. |
state_dir | <boot dir>/state | Where the unlocked container is mounted. |
threshold | 3 | How many holders must cooperate at the boot ceremony. At least 2, and no more than the number of holders. |
The holder set is deliberately not stored here. A list of holders on the
unencrypted boot directory would hand whoever seized the machine a list of
people to pressure. The authoritative record lives inside the container and is
readable, once unlocked, with station vmk status.
Environment variables
| Variable | Read by | Meaning |
|---|---|---|
RRN_PASSPHRASE | station | The station’s wallet passphrase, for init, run, and the other daemon commands. When unset you are prompted. Never put it on a command line; a root-only environment file for a service manager is the accepted trade. |
RRN_NEW_PASSPHRASE | station | The new passphrase chosen at the end of a key-recovery ceremony. Prompted for when unset. |
RRN_LOG | station | The log filter, in tracing syntax. Default info,tantivy=warn. |
RRN_WALLET_PASSPHRASE | rrn wallet | The member wallet’s passphrase. Deliberately a different variable from the station’s, so one shell cannot leak the other. Prompted for when unset. |
RRN_WALLET_HOME | rrn wallet | The wallet directory. --home on the command line wins over it; the default is ~/.railroad/wallet. |
The rrn console finds the station’s socket at
~/.railroad/station/station.sock; pass --socket for a station elsewhere.
The data directory
Plaintext profile. One flat directory, ~/.railroad/station by default:
| File | What it is | Back it up? |
|---|---|---|
wallet.rrnwallet | The station’s identity key, encrypted under the passphrase. | Yes. Irreplaceable. |
station.db | The community’s log and everything derived from it. | Yes. Irreplaceable. |
paired_mobiles.json | Which phones and wallets are paired. | Yes. Losing it means re-pairing everyone. |
config.toml | This page. | Yes. |
recovery.rrnrecovery | The station’s key-recovery package, once armed. | Yes. |
station.sock | The console socket. | No. Runtime scratch. |
marketplace_index/ | The search index. | No. Rebuilt from the log. |
reticulum/ | The generated Reticulum configuration and adapter identity, when the sidecar is on. | Optional. |
station backup bundles the right ones into a single encrypted archive.
Encrypted profile. The directory you pass as --data-dir becomes the
unencrypted boot directory, holding only config.toml, station.sock,
the container state.img, and a tiny vmk.descriptor that records the
threshold and nothing about who the holders are. Everything else above lives
inside the container, mounted at state/ once unlocked, alongside
vmk.rrnrecovery, the full holder record. Backups cover what is inside the
container but not the boot directory’s config.toml, so keep a copy of that
file with your archives.
Architecture decision records
Every locked design decision in Railroad Network is written down as an
Architecture Decision Record (ADR) in the station repo. ADRs are
append-only: a changed decision gets a new ADR that supersedes the old one.
When a page on this site and an ADR disagree, the ADR wins.
Phase numbering. ADRs written before 2026-08-25 use the old phase numbering, where “Phase 2” meant federation. ADR-0017 resequenced the plan: Phase 2 is now single-community resilience and Phase 3 is federation. ADRs 0001 to 0016 keep the old numbering as written.
The Status column is the ADR’s own status line. Proposed on a decision that has shipped means the maintainer has not yet formally ratified it; the code follows it regardless.
| ADR | Decision | Status |
|---|---|---|
| 0001 | Rust workspace and dual license | Accepted |
| 0002 | Canonical serialization via deterministic CBOR (dcbor) | Accepted |
| 0003 | Human-readable address format: bech32m with HRP rrn | Accepted |
| 0004 | Own Shamir’s Secret Sharing implementation over GF(256) | Accepted |
| 0005 | The station signs settlement and cancellation records | Accepted |
| 0006 | The mobile client holds the keys; the station is a local backend | Accepted |
| 0007 | uniffi-rs generates the mobile bindings to our Rust crypto | Accepted |
| 0008 | The mobile↔station envelope is the security boundary; the transport is a dumb carrier | Accepted |
| 0009 | One reputation formula runs on every station and no community can tune it | Accepted |
| 0010 | A listing is a signed record on the log; the search index is a view that can be thrown away | Accepted |
| 0011 | The Phase-1 oracle ladder: two serviceable tiers, a blocked ceiling, and a derived reputation stake | Accepted |
| 0012 | The Charter: a self-bootstrapping constitutional document, and how a community changes it | Accepted |
| 0013 | Federation and collapse-mode transport is pluggable; Reticulum is the adopted backend, run as an external sidecar | Accepted |
| 0014 | Phase-1 dispute resolution: a sortition jury with a governance backstop, and the Tier-2 stake that finally bites | Proposed |
| 0015 | Bootstrapping the electorate: a governance and dispute grace so a young community can actually govern | Proposed |
| 0016 | Station backup and key recovery: an encrypted archive whose key survives a lost passphrase | Proposed |
| 0017 | Single-community resilience comes before federation | Accepted |
| 0018 | A debt floor bounds how far a member can sign themselves into debt | Proposed |
| 0019 | A freshness bound on confirmed_at protects the dispute window | Accepted (superseded for delay-tolerant sync by ADR-0022) |
| 0020 | The community log keeps one writer; resilience is delay-tolerant submission, not multi-writer merge | Accepted |
| 0021 | Escrowed offline spending certificates bound the debt floor under partition | Accepted |
| 0022 | The admission clock: the station’s clock at admission is the only window-bearing clock | Accepted |
| 0023 | Emergency governance: deciding faster in a crisis without building a coup lever | Accepted |
| 0024 | Station at-rest encryption: a member-keyed encrypted volume unlocked by a boot ceremony | Accepted |
| 0025 | Equivocation cases are a distinct jury case kind with a Lapsed default and identity-anchored sortition | Accepted |
| 0026 | The Reticulum sidecar is ratified: pinned rnsd 1.5, driven from the station, native Rust deferred | Accepted |
| 0027 | Emergency declaration activation is a single first-crossing event, and a part-signed declaration expires | Accepted |
| 0028 | A self-custody CLI member wallet: the non-mobile member device, a sealed-channel client with an offline outbox | Accepted |
Generated page. Built from
docs/adr/in thestationrepo byscripts/gen-reference.sh.
Wire specifications
The byte-level formats, for anyone building a client, a scanner app, or a
carrier. They live in the station repo and are the authority.
| Spec | What it defines |
|---|---|
| QR payloads | The rrnp:, rrncert:, and rrnspend: text formats printed as QR codes for the paper path, including chunking. |
| DTN bundles | The delay-tolerant bundle a courier, radio, or phone carries to the station, and the receipt that comes back. |
| SMS carrier | Records as text-message chunks, the sender registry, rate caps, and what is reserved for the not-yet-built modem gateway. |
| VMK boot ceremony | The request and response formats of the encrypted-at-rest unlock ceremony. |
Two rules every format follows:
- Anything signed is canonical CBOR. The signature covers the deterministic encoding of the payload, never the envelope it travels in, so the same record is byte-identical on every platform.
- Carriers are dumb. Radio, SMS, paper, and the phone transport see only signed, already-public records. None of them is an identity, integrity, or encryption boundary.
The mobile app verifies its encodings against fixtures in the station repo,
so a new signed record kind always lands in both.
Status and roadmap
Last updated 2026-09-22.
Railroad Network is built in phases, each meant to be useful on its own.
| Phase | Scope | State |
|---|---|---|
| 0 | Crypto core, signed log, identity, ledger, daemon and CLI | Done |
| 1 | Mobile app, vouching, standing, marketplace, oracle tiers 1 and 2, governance, disputes, pilot readiness | Done; the 90-day community pilot has not yet been run |
| 2 | Single-community resilience: offline-first, delay-tolerant sync, headroom certificates, paper, LoRa radio, SMS codec, emergency governance, encrypted at rest, the command-line wallet | Done on simulation evidence, closed 2026-09-13; two hardware sign-offs pending |
| 3 | Federation between communities | Not started |
Phase numbering. Documents written before 2026-08-25 call federation “Phase 2”. A decision record (ADR-0017) moved single-community resilience ahead of federation, so resilience became Phase 2 and federation Phase 3. Older decision records keep the old numbering as written.
What works today
- One community: one writer station and the phones and laptops that pair with it, plus an optional read-only replica for audit.
- Payments with settlement windows and a debt floor. Tiers 1 and 2.
- Vouching, computed standing, established members, bootstrap grace.
- A marketplace: listings, needs, inquiries, recurring contracts.
- Charter founding ceremony, proposals, co-signing, voting, statutes.
- Jury disputes by sortition, escalation, appeal, equivocation cases.
- Emergency governance with a two-thirds declaration and hard expiry.
- Every window and electorate anchored on the station’s admission clock, so a device’s clock cannot move a deadline.
- Offline signing into a durable outbox, headroom certificates, delivery by courier, paper, or LoRa radio, and signed receipts, from the command-line wallet (see the next section for the phone).
- Encrypted station backups, station key recovery held by members, member key recovery from a recovery circle on a new phone or laptop, and an optional encrypted at-rest profile unlocked by a member quorum.
- A self-custody command-line wallet for members without a phone.
- A 72-hour outage simulation that runs in seconds and checks conservation, the debt floor, forks, and reproducibility.
What does not work yet
- The phone app cannot sign while the station is out of reach. Sending, confirming, voting, and contesting from the app need the station reachable; the app says so and you retry later. The offline outbox, headroom certificates, and paper export described on When the network is down exist in the app’s Rust core and ship today in the command-line wallet; the phone screens are not written.
- No federation. Communities cannot see or trade with each other.
- No Tier 3 or higher. Payments of 50 Commons and up are refused.
- No SMS gateway. The codec, sender registry, and relay are built and tested against a mock; the physical modem side is not, so text message cannot be switched on.
- LoRa field sign-off pending. The radios are bench-verified over the air; the field-acceptance run at real range has not been recorded.
- No app store, no iOS. Android, sideloaded.
- No per-member rate limiting on any surface, accepted at pilot scale behind the pairing gate.
- No independent audit. See Security and audits.
The pilot
What remains of Phase 1 is running the real thing: a 90-day pilot with a community of around twenty people, play stakes, one station. The guides on this site were written for it. If you run one, the project wants to hear about it: see Contributing.
Security and audits
Do not use this with real value. Not yet.
Where things stand
An internal, AI-assisted security review of both the station and the mobile app was completed in August 2026. It found no High-severity issues, with a handful of Medium and Low findings concentrated at the pairing and recovery ceremony surfaces and at platform exposure on the phone (clipboard, screen capture, keychain accessibility). The full reports, with each finding’s failure scenario and remediation order, are public:
That review was a code review performed by an AI model operated by the maintainer. It is not a penetration test and not an attestation by a professional security firm. It was meant to raise the floor, not to clear the stack for production. An independent professional audit remains warranted before any deployment where real people depend on this software, and it has not happened yet.
The threat model
The project keeps a living, STRIDE-organized threat model that grows with each component and states plainly what is not mitigated:
Some limitations worth knowing as a user:
- A compromised phone or station OS is out of scope. The software defends the wire and the disk; it cannot defend against a machine that is already someone else’s.
- Radio and SMS leak metadata. They carry only signed, public records, but a radio has a location and a phone number has an owner. Communities under surveillance pressure should prefer paper for sensitive traffic.
- Offline double-spending cannot be prevented, only priced. See When the network is down.
- Nothing rate-limits a paired member. Every input is size-bounded, but no surface limits how often one identity may call it. At pilot scale the pairing gate is the accepted bound.
- The encrypted at-rest profile defends a powered-off station only. A station seized while running has its keys in memory. See Encrypted at rest.
- A read replica is a copy of the history, not a second source of balances. Its derived views are empty by design, loudly.
Reporting a vulnerability
Please do not open a public issue for a security problem. See the
security policy
in the station repo for how to reach the maintainers privately.
Contributing
Railroad Network is open source under Apache-2.0 or MIT, at your option.
Fixing these docs
Every page has an edit link in its top bar that opens the Markdown on GitHub.
Small fixes can be made there directly. For anything bigger, clone
railroad-network.github.io
and see About these docs for how the site is built and
the writing conventions.
If a page here disagrees with a decision record in the station repo, the
page is wrong. Please say so.
Contributing to the software
station: the Rust daemon and command-line client. Read its CONTRIBUTING.md and code of conduct.mobile: the Android app. Its CONTRIBUTING.md.
Both repos are built by a single maintainer ahead of an independent audit, so unsolicited code changes are not being merged yet; open an issue to discuss a change first. Design changes go through an Architecture Decision Record before code. The ADR index shows the form.
Running a pilot
The most useful contribution right now is a real pilot: twenty people, one station, play stakes, ninety days, and honest notes about what confused people. The organizer and operator sections are the runbook.
About these docs
This site is an mdBook book in the
railroad-network.github.io
repo. Markdown in src/, one sidebar in src/SUMMARY.md, deployed to GitHub
Pages on every push to main.
Building locally
cargo install mdbook --locked # once
mdbook serve --open # live preview
The book/ directory that mdbook build produces is a self-contained static
site. It works from a USB stick, or served by a station on the local network,
with no internet. Communities are encouraged to keep a copy.
Generated pages
The command references and the ADR index are generated from a station
checkout, not written by hand. After a CLI change lands there:
scripts/gen-reference.sh # expects ../station; STATION=/path overrides
Commit the result.
How the site is organized
Four sections by audience, not by component:
- For members: tutorials and how-tos for people using the app.
- For organizers: how founding, governance, disputes, and emergencies work, in plain language.
- For operators: runbooks for the station computer.
- Reference: exact commands, the glossary, and pointers to the decision records.
Every page in the sidebar is written. If a future page is planned before it
is written, add it to SUMMARY.md as a draft entry, [Title](), and the
sidebar shows it greyed out with no link; replace the empty parentheses with
the real path when the file exists.
Two pages are hand-maintained mirrors of the code rather than generated:
Configuration follows the station’s config
source, and the operator pages follow the runbooks in the station repo.
When a runbook changes, the page that draws on it changes in the same pull
request or the next.
Writing conventions
- Plain language. Say “the community’s history”, then introduce “the log”. Every term of art appears in the Glossary.
- Explain, then link to the record. A governance or ledger rule gets a human explanation here and a link to the ADR that locks it. Never restate numbers the ADR owns without linking it.
- The ADR wins. If you find a conflict, fix the page.
- Callouts are blockquotes starting with a bold lead:
> **Warning.**,> **Tip.**,> **Generated page.** - Amounts in Commons in prose, with the centicommon rule mentioned once where it matters.
- No ticket numbers, no milestone codes. Cite the ADR or describe the behavior. Tickets are ephemeral; the docs must stand on their own.
- The warning stays. “Do not use with real value” appears on the welcome page and each section’s overview until an independent audit says otherwise.
- Say what ships, not what is designed. Where the phone app lags the station (today: offline signing, certificates, paper export), the page says so in a callout and points to the command-line wallet. The status page is the one list of gaps; keep it current.