Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 partFor members
Someone founding or stewarding a community: charter, votes, disputesFor organizers
Someone running the station computer that the community’s phones talk toFor operators
Someone who wants the exact commands, the glossary, or the design recordReference

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 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:

  1. Create a wallet and get your rrn1… address. Install the app.
  2. Join a community by pairing with its station in person. Join your community.
  3. Pay and get paid in Commons, with a settlement window that protects both sides. Pay and get paid.
  4. Vouch for people you know, and build your own standing through trade and vouches. Vouching and standing.
  5. 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.
  6. 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.
  7. Offer and find things in the marketplace, agree a price, and pay for what you agreed. The marketplace.
  8. Contest a payment that went wrong, respond when someone contests yours, and serve on a jury when drawn. Contest a payment.
  9. 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.
  10. 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

  1. Get the file onto your phone. Email, a USB cable, or a download link all work.
  2. 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.
  3. 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

  1. 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).
  2. Unlock your wallet. Your phone shows an 8-character code.
  3. The operator lists the pending requests on the station. Yours shows up with the same style of code and your rrn1… address.
  4. 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

  1. 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.
  2. Confirm. The receiver sees the proposal and confirms it. Nothing moves yet. Confirming starts the settlement window.
  3. 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 sizeTierWindow
SmallTier 124 hours
LargerTier 248 hours
50 Commons and upTier 3Not 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.

  1. 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.
  2. Confirm it’s you. Enter your passphrase. The app needs the key unlocked to split it.
  3. 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.
  4. 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”.
  5. Split. Tap Split my key into N pieces. This takes a moment and happens entirely on the device.
  6. 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.
  7. 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.
  8. 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.

  1. Be there when they split. They will show you one QR code.
  2. 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.
  3. 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.”
  4. 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.
  5. 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.

  1. Open Settings → Shards you hold → Help someone recover.
  2. 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.
  3. 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.
  4. Enter your passphrase. Your phone opens your sealed piece and re-seals it to this one ceremony.
  5. 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.

  1. 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.
  2. On the new phone, at the welcome screen, tap Recover an existing identity, then From my recovery circle.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

  1. Settings → Export wallet. Enter your passphrase.
  2. 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.”
  3. 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.
  4. 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

  1. 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.
  2. Rebuild on a new phone from your circle or your export, as above, then re-pair. Your identity carries over intact.
  3. 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 submit on 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):

RuleDefault
Most one certificate can reserve10 Commons
Live certificates one member may hold4
How long a certificate can be spent against7 days
How late a spend against it may arrive and still count14 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

SurfaceWhat goes thereExample
GoodsPhysical things, countedEggs by the dozen, a spare bike wheel
ServicesTime and skill, one-off or recurringAn afternoon of fencing, weekly childcare
CommonsCommunity-pooled resources at low or no costThe 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 toCommand
See your address, pairing, and how many records are pendingrrn wallet status (never unlocks the key)
Pay someonerrn wallet pay rrn1<receiver> 3.50 --memo "eggs"
Confirm a payment addressed to yourrn wallet confirm <tx-id>
Vote on a proposalrrn wallet vote <proposal-id> yes (or no, abstain)
Contest a confirmed paymentrrn wallet dispute <tx-id> --reason "…"
Vouch for someonerrn wallet vouch rrn1<address> --statement "…" --stake <amount>
Your transactions, live from the stationrrn wallet transactions
Your local outbox and what happened to each recordrrn 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

  1. Found the community. Choose founders, write the Charter’s principles and rights, and run the signing ceremony. Start a community.
  2. 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.
  3. Run decisions in the open. Proposals, co-signing, voting, statutes. How decisions get made.
  4. Let disputes work. Understand the jury, escalation, and appeal so you can explain them and stay out of the way. Disputes.
  5. Know the emergency lever cold, because it is the sharpest capture lever in the system. Emergencies.
  6. 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

  1. Propose. Any member authors a proposal: a title, a body, and a kind (an ordinary statute, a Charter amendment, or an emergency measure).
  2. 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.
  3. Vote. Established members cast ballots during a deliberation window, about a week for ordinary proposals.
  4. 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

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:

RuleValue
Default duration72 hours
Longest single declaration7 days
Longest chain of renewals14 days, each renewal needing the full two thirds again
Cooldown after a chain ends14 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 wallet on 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:

RoleDoes
FacilitatorRuns the clock, calls the phases, keeps the log sheet.
StewardAt the station console the whole time: ingest, export receipts, status. Usually the operator.
Two couriersOne on foot; one “slow”, who deliberately delays and reorders what they carry.
Laptop membersOne or two people with the command-line wallet. They are the offline economy.
A merchant and a customer or twoTrade for real, with real goods. Lunch works.
A holder quorumIf the station runs the encrypted profile, the key-holders who unlock it.
One adversaryBriefed privately; see below.
Everyone elsePhone members who transact while online, vote, watch the banners, and try to break things.

Timeline

TimePhaseWhat happens
Day beforePrepareLaptop 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:00NormalFifteen minutes of ordinary trade on Wi-Fi, phones included. The steward notes a few balances and the length of the log.
0:15CutTurn 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:15OutageLaptop 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:15Emergency (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:15Power 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:45ReconnectWi-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:00ReconcileThe steward reads every receipt outcome aloud: admitted, known, or refused and why. The adversary reveals what they tried.
3:30Settle and debriefWait 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.

  1. 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.
  2. Hand a courier a sheet with one page missing. Expect the rest to land and the author to re-send the missing record later.
  3. Scan the same sheet twice. Expect the same receipt and nothing double-counted.
  4. Edit one character of a QR payload line before ingest. Expect it refused with a bad-signature reason.
  5. 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

ThingDetails
A machineAny 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 networkA Wi-Fi network all members’ phones can join. Give the station a fixed address on it if you can.
The softwareBuilt from source, once. Ten to thirty minutes.
Two safe placesFor the passphrase and backups. A fireproof folder at home plus a sealed envelope with a trusted member.

The pages

  1. Run a station: build, initialize, configure, run, survive reboots.
  2. Pair phones and wallets: the in-person ceremony that is the actual security boundary.
  3. Backups and key recovery: the fifteen minutes that save the community.
  4. Encrypted at rest: the seizure-resistant profile, Linux only, and the boot ceremony it costs.
  5. The courier desk: ingesting paper, printing receipts and cards.
  6. Radio and text message: the Reticulum sidecar, LoRa bring-up, and where SMS stops today.
  7. Keeping phones in sync: the per-vendor battery traps and the ten-minute drill.
  8. 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.

FileWhat it is
wallet.rrnwalletThe identity key, encrypted under your passphrase. Irreplaceable.
station.dbThe community ledger. Irreplaceable.
paired_mobiles.jsonWhich phones are paired. Losing it means re-pairing everyone.
config.tomlSettings.
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 = true makes 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/wallet by 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 happenedWhat saves youPrepared by
Station machine diesstation restore <archive> on a new machineBackups
Passphrase lost, machine finestation recovery restoreKey recovery
Machine dies and passphrase loststation recovery restore --from-backup <archive>Both
A member loses their phoneThey 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 exportEach member, in-app
A member’s phone is stolenstation unpair <addr> first, then the member recovers on a new phone and re-pairsNothing 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, and cryptsetup to 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:

  1. station unlock shows a request QR and a console fingerprint, two groups of five letters such as B523J-DY6LH.
  2. Read the fingerprint aloud to each holder, or send it over a channel you both trust.
  3. 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.
  4. 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.

DrillHow oftenCommand
The unlock ceremony, with the real holdersAfter arming, after every holder change, at least twice a yearstation unlock
The community-continues path: restore from backup on fresh storageQuarterly, and before any upgradescripts/drill-seizure-recovery.sh --profile plaintext
The brick property: a closed container leaks nothingAfter arming and after every re-keyscripts/drill-seizure-recovery.sh --profile encrypted
The UPSMonthly: pull the mains, confirm a clean shutdown on low batterynone

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 rrn command 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:

OutcomeMeaning
admittedNew, valid, now on the log. The settlement window starts from this moment.
knownThe station had it already, from an earlier sheet or another route. Nothing double-counted.
refusedRejected, 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:

ReasonWhat it meansWhat to do
nonce-gapAn 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-floorThe 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-signatureThe 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-memberA 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-replicaYou 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:

FileWhat it is
chunk_NN_of_MM.pngOne PNG per QR code, numbered.
sheet.pdfEvery QR on captioned pages: payload id, index, and count under each one. Print this.
*.txtThe 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 slow sets 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:

  1. Couriers arrive with sheets. show, then ingest, then read the outcomes aloud to the courier.
  2. Before each courier leaves, export-receipts for the members they are heading back toward, and print the sheet.
  3. Members who reach Wi-Fi again pick up their receipts themselves; the paper ones are for those who will not.
  4. 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

PieceState
Reticulum sidecar, supervised by the stationBuilt. Off by default.
Bundle push and receipt over the radioBuilt. Two radios verified over the air on the bench on 2026-09-11.
Airtime budget, money-first pacingBuilt.
Field acceptance at real rangePending: 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

PieceState
Wire codec: records as text chunks that survive a carrier’s manglingBuilt and tested.
Sender registry: a member binds their own number with a signed recordBuilt.
Per-sender rate cap, money-first outbound pacingBuilt.
Station relay, tested against a mock gatewayBuilt.
The physical modem gatewayNot built.
The app composing textsNot 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

SituationReach for
Members on the community Wi-FiNothing extra: phones and wallets sync directly.
A member across the valley, no internet either endRadio, if both ends have one. Otherwise paper.
Sensitive traffic, surveillance pressurePaper. No electronic trace.
Cellular signal but no dataPaper today. SMS once the gateway exists.
Volume too large for a sheetA 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.

BrandTrapWhat to set
MotorolaThe battery optimiser cuts background network on the LAN.The step-2 dialog is usually enough: Battery → Unrestricted.
Google PixelClosest 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, VivoBattery 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.

  1. Setup check. All three steps above done; the in-app Notifications screen shows background sync enabled.

  2. 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.

  3. 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"
    
  4. Wait up to thirty minutes: one wake at the fifteen-minute floor plus system slack. A notification arriving means the backgrounded path works.

  5. 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.

  6. 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

SymptomLikely causeFix
The phone’s Join screen finds no stationPhone 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 tooFirewall 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 matchSomething 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 nothingThe 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 replyThe 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.

See Pair phones and wallets.

Sync and notifications

SymptomLikely causeFix
No notifications while the app is closedBattery 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 openedThe vendor’s app-killer is suppressing wakes.The per-vendor table on that page.
Phone quiet overnight on a shelf, current when picked upWi-Fi sleeps and the phone falls back to mobile data.A “keep Wi-Fi on during sleep” setting.
A previously fine phone goes quietAn OS update or a “battery review” reset the exemption.Re-walk the setup.
A Connecting… pill on cold startNormal: the app is re-establishing its subscription.Wait a second.
Offline that persists on the right Wi-FiThe 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 foregroundNot a background problem at all.Pairing and Wi-Fi, above.

Payments and refusals

SymptomLikely causeFix
A payment is refused with a debt-floor messageIt 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 refusedTier 3 is not served. Refused, never shrunk.Split it into smaller payments over time.
A confirmed payment sits as pending for a day or twoThe settlement window. Normal.Nothing. Tier 1 settles after 24 hours, Tier 2 after 48.
A payment never settlesSomeone contested it.rrn dispute list. Every path resolves or lapses on its own.
A vouch is refused with a velocity messageThe rate limit on how fast anyone can vouch or gain standing.Wait. It is not personal.
A paper or bundle record is refused nonce-gapAn 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-signatureA scan error, a corrupted line, or tampering.Re-scan. If it persists the sheet is not what the member printed.
Refused outbox-forkThe 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-overspentThe same certificate was spent past its cap.Recorded as equivocation. Costs the payer their whole standing and opens a jury case.
Refused read-replicaThis station is a replica, not the writer.Carry the record to the writer. A replica admits nothing.
Refused tier-unsupportedA 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

SymptomLikely causeFix
station run rejects the passphraseIt wants the wallet passphrase from station init, not the machine login.Lost it? Key recovery, today.
The daemon refuses to start with peers configuredA 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 connectThe daemon is not running, or --socket points elsewhere.Start the daemon; check the data directory.
Balances read zero on a replicaExpected: 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 hostnameName 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 runsThe 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 crashedEvery 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

SymptomLikely causeFix
A sheet will not reassembleA 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 twiceNothing wrong.The station returns the same receipts; nothing double-counts.
A member never got their receiptThe receipt is waiting at the station.rrn paper export-receipts --author <addr>; exporting twice is safe.
rrn dtn status shows a push abandonedRetried 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 degradedUsually 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 deniedThe user is not in the serial group.sudo usermod -aG dialout $USER, log out and in.
Two radios see no path to each otherFrequency, bandwidth, or spreading factor differ.They must match exactly on every node.
SMS does nothing though [sms] is enabledThere is no modem gateway yet.Expected. SMS cannot be switched on today.

The encrypted profile

SymptomLikely causeFix
station run exits right after a rebootThe volume is locked.station status, then station unlock with a quorum of holders.
A systemd unit crash-loops after a rebootSame. 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 consoleSomeone 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 thisThey 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 runThe station is already encrypted.A rotation is a fresh migration; see Encrypted at rest.
The station starts in the encrypted profile on macOSIt will not: the profile refuses rather than serving plaintext.Linux only.
A restored station lost its listen address and timersThe archive does not include the boot directory’s config.toml.Keep a copy with your backups.

When you are stuck

  1. rrn status --format json and rrn history give the station’s own account.
  2. The member’s Diagnostics screen gives the phone’s.
  3. The station repo’s runbook, Setting up a Railroad Network community, has the long form of every procedure on this site.
  4. If it looks like a bug, open an issue in the station or mobile repo 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.

What is authoritative

This site explains. The station repo decides. In order of authority:

  1. The ADRs in station/docs/adr/. Append-only; a changed decision gets a new ADR that supersedes the old one.
  2. The wire specs in station/docs/spec/.
  3. The design overview in station/docs/design/, which predates parts of the implementation and says so where it conflicts with an ADR.
  4. 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 --help text of every command, captured from a build of the station repo by scripts/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 --help text of every command, captured from a build of the station repo by scripts/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 in crates/rrn-station/src/config.rs of the station repo, 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.

KeyDefaultMeaning
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]

KeyDefaultMeaning
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.

KeyDefaultMeaning
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.
advertisetrueWhether to announce on the LAN at all. false runs dark: members must type the station’s address and port by hand.
subscribe_hold_secs30How 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.

KeyDefaultMeaning
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_seconds86400 (24 hours)The settlement window for a Tier 1 payment.
tier2_window_seconds172800 (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.

KeyDefaultMeaning
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_seconds604800 (7 days)How long a newly issued certificate stays valid. Must be greater than 0.
cert_max_cap_centi1000 (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_seconds1209600 (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_outstanding4How 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.

KeyDefaultMeaning
sweep_interval_secs30The settlement sweep: settles every confirmed payment whose window has elapsed.
gossip_interval_secs5How often a replica pulls from its peers.
reputation_refresh_interval_secs3600 (1 hour)Recomputes the cached standing of every known member from the log. The cache is only a cache.
listing_expiry_interval_secs300 (5 minutes)Writes down marketplace listings whose expiry has passed. They are already unbuyable the moment they expire.
inquiry_expiry_interval_secs3600 (1 hour)Closes marketplace inquiries idle for seven days.
contract_charge_interval_secs300 (5 minutes)Bills every period a recurring service contract has due. Re-sweeping a period is harmless.
governance_implementation_interval_secs3600 (1 hour)Puts passed proposals whose implementation delay has run into force.
dispute_resolution_interval_secs3600 (1 hour)Enacts jury majorities and lapses disputes whose window closed unresolved.
dtn_prune_interval_secs3600 (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.

KeyDefaultMeaning
receipt_retention_secs2592000 (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.

KeyDefaultMeaning
enabledfalseWhether to run the sidecar at all.
rnsd_path"rnsd"Path to, or name of, the rnsd binary.
config_dir<data dir>/reticulumWhere 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_driftfalseDevelopment escape hatch: run a mismatched rnsd anyway, with a loud warning.
restart_backoff_secs5Base 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.

KeyDefaultMeaning
raw_bytes_per_sec250.0Raw carrier throughput before the duty cycle.
duty_cycle_percent1.0Duty 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_bytes500The largest catch-up burst after idle, and the largest single frame the budget passes. Must be at least frame_bytes.
frame_bytes480The 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_secs3600 (1 hour)How often undelivered outbound pushes are re-sent.
push_ttl_secs604800 (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.

KeyDefaultMeaning
port(required)The radio’s serial port, such as /dev/ttyACM0 or /dev/ttyUSB0.
frequency_hznoneCentre frequency in hertz. Must be legal where you are and identical on every node.
tx_power_dbmnoneTransmit power in dBm. Start low; antenna gain counts toward your region’s cap.
bandwidth_hz125000Channel bandwidth.
spreading_factor87 to 12. Higher reaches further and sends less.
coding_rate5The 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.

KeyDefaultMeaning
enabledfalseWhether 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_message4The 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_hour60The 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.

KeyDefaultMeaning
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.

KeyDefaultMeaning
container_path<boot dir>/state.imgThe LUKS2 container file.
state_dir<boot dir>/stateWhere the unlocked container is mounted.
threshold3How 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

VariableRead byMeaning
RRN_PASSPHRASEstationThe 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_PASSPHRASEstationThe new passphrase chosen at the end of a key-recovery ceremony. Prompted for when unset.
RRN_LOGstationThe log filter, in tracing syntax. Default info,tantivy=warn.
RRN_WALLET_PASSPHRASErrn walletThe 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_HOMErrn walletThe 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:

FileWhat it isBack it up?
wallet.rrnwalletThe station’s identity key, encrypted under the passphrase.Yes. Irreplaceable.
station.dbThe community’s log and everything derived from it.Yes. Irreplaceable.
paired_mobiles.jsonWhich phones and wallets are paired.Yes. Losing it means re-pairing everyone.
config.tomlThis page.Yes.
recovery.rrnrecoveryThe station’s key-recovery package, once armed.Yes.
station.sockThe 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.

ADRDecisionStatus
0001Rust workspace and dual licenseAccepted
0002Canonical serialization via deterministic CBOR (dcbor)Accepted
0003Human-readable address format: bech32m with HRP rrnAccepted
0004Own Shamir’s Secret Sharing implementation over GF(256)Accepted
0005The station signs settlement and cancellation recordsAccepted
0006The mobile client holds the keys; the station is a local backendAccepted
0007uniffi-rs generates the mobile bindings to our Rust cryptoAccepted
0008The mobile↔station envelope is the security boundary; the transport is a dumb carrierAccepted
0009One reputation formula runs on every station and no community can tune itAccepted
0010A listing is a signed record on the log; the search index is a view that can be thrown awayAccepted
0011The Phase-1 oracle ladder: two serviceable tiers, a blocked ceiling, and a derived reputation stakeAccepted
0012The Charter: a self-bootstrapping constitutional document, and how a community changes itAccepted
0013Federation and collapse-mode transport is pluggable; Reticulum is the adopted backend, run as an external sidecarAccepted
0014Phase-1 dispute resolution: a sortition jury with a governance backstop, and the Tier-2 stake that finally bitesProposed
0015Bootstrapping the electorate: a governance and dispute grace so a young community can actually governProposed
0016Station backup and key recovery: an encrypted archive whose key survives a lost passphraseProposed
0017Single-community resilience comes before federationAccepted
0018A debt floor bounds how far a member can sign themselves into debtProposed
0019A freshness bound on confirmed_at protects the dispute windowAccepted (superseded for delay-tolerant sync by ADR-0022)
0020The community log keeps one writer; resilience is delay-tolerant submission, not multi-writer mergeAccepted
0021Escrowed offline spending certificates bound the debt floor under partitionAccepted
0022The admission clock: the station’s clock at admission is the only window-bearing clockAccepted
0023Emergency governance: deciding faster in a crisis without building a coup leverAccepted
0024Station at-rest encryption: a member-keyed encrypted volume unlocked by a boot ceremonyAccepted
0025Equivocation cases are a distinct jury case kind with a Lapsed default and identity-anchored sortitionAccepted
0026The Reticulum sidecar is ratified: pinned rnsd 1.5, driven from the station, native Rust deferredAccepted
0027Emergency declaration activation is a single first-crossing event, and a part-signed declaration expiresAccepted
0028A self-custody CLI member wallet: the non-mobile member device, a sealed-channel client with an offline outboxAccepted

Generated page. Built from docs/adr/ in the station repo by scripts/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.

SpecWhat it defines
QR payloadsThe rrnp:, rrncert:, and rrnspend: text formats printed as QR codes for the paper path, including chunking.
DTN bundlesThe delay-tolerant bundle a courier, radio, or phone carries to the station, and the receipt that comes back.
SMS carrierRecords as text-message chunks, the sender registry, rate caps, and what is reserved for the not-yet-built modem gateway.
VMK boot ceremonyThe 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.

PhaseScopeState
0Crypto core, signed log, identity, ledger, daemon and CLIDone
1Mobile app, vouching, standing, marketplace, oracle tiers 1 and 2, governance, disputes, pilot readinessDone; the 90-day community pilot has not yet been run
2Single-community resilience: offline-first, delay-tolerant sync, headroom certificates, paper, LoRa radio, SMS codec, emergency governance, encrypted at rest, the command-line walletDone on simulation evidence, closed 2026-09-13; two hardware sign-offs pending
3Federation between communitiesNot 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

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.