Running Your Own Mostro Node
Mostro v0.20.2 — Community Guide · October 2026
1. What is Mostro and Why Should Your Community Run One?
Mostro is a peer-to-peer Bitcoin exchange that lets people buy and sell Bitcoin using local currencies (dollars, euros, pesos — any currency) without needing to provide identity documents (KYC). Think of it as a decentralized marketplace where buyers and sellers can trade directly.
It works using two technologies:
- Lightning Network — a fast, low-cost Bitcoin payment layer (think of it as Bitcoin's express lane for small, quick payments)
- Nostr — a censorship-resistant communication protocol (think of it as a messaging system that nobody can shut down)
Mostro acts as an escrow coordinator — it holds the seller's Bitcoin in a temporary "lock box" (called a hold invoice) until the buyer confirms they've sent the local currency payment. Mostro never actually controls anyone's funds; it just holds them briefly during the trade.
Why Would Your Community Want to Run a Mostro Node?
- Fee income — Every trade earns you the fee you set (the template ships 0; 0.6% is a common choice). At 0.6%, a community doing $10,000 in monthly trades earns ~$60/month in fees.
- P2P trading without KYC — Your community members can buy and sell Bitcoin without providing identity documents. Especially important in regions with unstable currencies or restrictive regulations.
- Disputes in your language — When a trade goes wrong, your community resolves it, in your language, understanding your local payment methods.
- Independence — No company can shut down your exchange. No government can pressure a single operator to close it.
- Customization — You choose which currencies to support, what payment methods to allow, and what fees to charge.
How Mostro Works (Simplified)
If something goes wrong (e.g., Bob says he paid but Alice didn't receive it), either party can open a dispute, and your community's assigned arbiters investigate and resolve it.
2. Prerequisites — What You Need Before Starting
2.1 A Server (VPS)
A VPS (Virtual Private Server) is a computer in a data center that runs 24/7. You'll rent one to host your Mostro node.
Minimum specs:
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 vCPUs (shared) | 2+ vCPUs |
| RAM | 2 GB | 4 GB |
| Storage | 60 GB SSD | 100 GB SSD |
| Bandwidth | 3 TB/month | 3+ TB/month |
| OS | Ubuntu 22.04+ LTS | Ubuntu 24.04 LTS |
Estimated monthly cost: $10–$24/month.
These figures are for Mostro alone. If LND runs on the same VPS, add at least 2 GB of RAM, and the disk its Bitcoin backend needs (see 2.2).
Popular VPS providers:
- Hostinger — from ~$7/month (promotional price; renewal may be higher) (KVM 2: 2 vCPU, 8GB RAM, 100GB NVMe, 8TB bandwidth) · Accepts Bitcoin
- Hetzner — €3.49-8/month (CX23 from €3.49, good value, EU-based)
- Digital Ocean — $24/month (4GB RAM, 2 CPUs, 80GB SSD) or $32/month (4GB RAM, 2 Intel CPUs, 120GB NVMe)
- OVH — ~$6-12/month
- Linode/Akamai — $12/month
- Lunanode — Accepts Bitcoin payments
Many VPS providers accept Bitcoin payments. Look for that option if you want to stay consistent with the Bitcoin ethos.
You need to be comfortable connecting to a server via SSH. If you've never done it, search for a tutorial on "Connect to a VPS via SSH" — it's simpler than it sounds.
2.2 A Lightning Network Node (LND)
Lightning Network is a "layer 2" system built on top of Bitcoin that enables fast, cheap payments. To run Mostro, you need an LND node (Lightning Network Daemon) — the specific Lightning software that Mostro works with.
Your options:
| Option | Difficulty | Cost | Notes |
|---|---|---|---|
| Use an existing LND node | Easy | Free (if you have one) | Best if someone already has one |
| Run LND on the same VPS | Hard | Bigger VPS + liquidity | 4GB+ RAM, and disk for the Bitcoin backend (see below) |
| Node-in-a-box solution | Medium | $200-600 + liquidity | Start9, Umbrel, RaspiBlitz |
| StartOS with Mostro package | Easiest | $300-600 + liquidity | Start9 has a one-click Mostro package |
| Use Voltage.cloud | Easy | From ~$20/month + liquidity | Voltage — Hosted LND with managed infrastructure |
Mostro specifically requires LND (not CLN/Core Lightning, not Eclair, not LDK). Make sure your Lightning node runs LND.
What you need from your LND node:
- The
tls.certfile (a security certificate) - A dedicated
mostro.macaroonfile (an authentication token with only the permissions Mostro needs, see below) - The gRPC address (typically
https://127.0.0.1:10009if on the same machine)
Generate a dedicated macaroon for Mostro. Do not give Mostro your admin.macaroon: it grants full control over your node and its funds. Bake a macaroon that carries only the permissions Mostro actually uses (read node info, list channels for the health report, create/settle/cancel hold invoices, send and track payments).
First pick a root key ID that is not already in use. Revoking a macaroon revokes every macaroon sharing its ID, so reusing one would take out unrelated credentials. ID 0 belongs to LND's own macaroons, so choose a free non-zero number and write it down:
lncli listmacaroonids
Then bake the macaroon with the ID you chose (7 in this example — substitute yours):
lncli bakemacaroon --root_key_id 7 \
--save_to /root/.lnd/data/chain/bitcoin/mainnet/mostro.macaroon \
info:read invoices:read invoices:write offchain:read offchain:write
This macaroon cannot open or close channels, move on-chain funds, or change your node's configuration. If it ever leaks, revoke it with lncli deletemacaroonid 7 — using the same ID you baked it with — and bake a new one.
If you can't write into LND's data directory (a container, a node-in-a-box, a hosted node), leave out --save_to: lncli then prints the macaroon in hex, and you turn it into a file wherever Mostro runs. lncli can also bake it from another machine, pointed at the node with its admin credentials:
lncli --rpcserver=NODE_ADDRESS:10009 --tlscertpath=tls.cert --macaroonpath=admin.macaroon \
bakemacaroon --root_key_id 7 info:read invoices:read invoices:write offchain:read offchain:write
# copy the hex it prints, then:
echo "PASTE_THE_HEX_HERE" | xxd -r -p > mostro.macaroon # apt install -y xxd if missing
Unlike Mostro, lncli checks that NODE_ADDRESS appears in the node's certificate. LND puts the machine's own addresses in it when it creates it, but not, for example, a VPN address added later. If the connection fails with a certificate error, run lncli on the node itself instead, or add the address with tlsextraip (or tlsextradomain) in lnd.conf, delete tls.cert and tls.key, and restart LND so it creates a new certificate; then copy the new tls.cert to Mostro too.
How Mostro connects to LND
Mostro talks to LND's gRPC port (10009 by default) and authenticates with tls.cert and the macaroon. Three things are worth knowing before you choose where LND runs:
- LND only listens on
localhostby default. A Mostro on the same machine reaches it athttps://127.0.0.1:10009; anything else (a Docker container, another server) needs the extra steps below. - Mostro pins the certificate. It accepts exactly the certificate in
tls.certand ignores the host name, so you don't needtlsextraiportlsextradomain. But if LND regenerates its certificate (it expires after about 14 months), copy the newtls.certto Mostro and restart it, or it stops at startup withLn node error. - No Tor for this link. Mostro cannot reach LND through Tor or a SOCKS proxy. A remote LND must be reachable over the network: use a VPN.
LND on the same VPS
Install LND following the official guide (docs.lightning.engineering), using its release binaries. LND needs a view of the Bitcoin chain, and that choice decides the size of your VPS:
| Backend | Disk | Trade-off |
|---|---|---|
bitcoind, full node | More than 700 GB | The most robust. Initial sync takes days. |
bitcoind, pruned | Tens of GB | Supported by LND and robust; the initial sync still takes days. |
neutrino | A few GB | No Bitcoin node and a fast start, but it depends on the peers it syncs from. A neutrino node that stalled for about 21 hours is why Mostro now watches LND's sync (5.4). If you use it, point it at Bitcoin nodes you trust with neutrino.connect and set up the health alerts. |
Then make LND reachable from Mostro. With the native install (Option C) nothing changes: use https://127.0.0.1:10009. With Docker (Options A and B) the container is a separate host, so pick one:
- Host networking (simplest). Run the container with
--network hostinstead of--add-host=host.docker.internal:host-gateway(in compose,network_mode: hostinstead ofextra_hosts) and setlnd_grpc_host = 'https://127.0.0.1:10009'. LND keeps listening onlocalhostonly. - Keep the bridge network and
host.docker.internal, as in Option A. Then LND has to listen beyondlocalhost— addrpclisten=0.0.0.0:10009tolnd.conf— and your firewall must keep port 10009 closed to the internet, for exampleufw allow from 172.16.0.0/12 to any port 10009 proto tcpfollowed byufw deny 10009/tcp. If UFW is not active yet, allow SSH first (ufw allow OpenSSH) and then runufw enable: inactive, it enforces none of these rules.
LND on another server or at home
Connect the two machines with a VPN such as WireGuard or Tailscale, make LND listen on its VPN address (rpclisten=VPN_IP:10009, plus rpclisten=localhost:10009 so lncli on the node keeps working: any rpclisten line replaces the default), and set lnd_grpc_host = 'https://VPN_IP:10009'. Copy tls.cert and the macaroon to the Mostro server. Opening port 10009 to the internet also works, but then the macaroon is the only thing between the world and your node: avoid it. A node at home must stay online 24/7: a power cut or a dropped connection leaves your users' trades waiting.
Node-in-a-box (Start9, Umbrel, RaspiBlitz)
These systems install and update LND for you, usually on a small computer at home. On StartOS the simplest path is the Mostro package itself, which connects to the LND service on the same box. Using the box's LND from a Mostro on a VPS is harder: these systems manage LND's configuration themselves, so you may not be able to add an rpclisten line, and they often expose gRPC only on the local network or over Tor, which Mostro cannot use. Check what your system allows before you plan on it. Each system shows LND's connection details in its own interface (on StartOS, under the LND service's properties); lncli usually runs inside the LND container — on Umbrel, for example, sudo docker exec -it lightning_lnd_1 lncli ... (check the name with sudo docker ps). Bake the macaroon in hex as shown above.
Voltage (hosted LND)
Voltage runs LND for you. Download tls.cert and the admin macaroon from the node's dashboard, use them only to bake Mostro's restricted macaroon from your own machine (see above), and point lnd_grpc_host at the gRPC address the dashboard shows, port 10009. You still provide and manage the liquidity.
LND settings that affect Mostro
invoices.holdexpirydelta(default 12): if you raise it, raiseescrow_deadline_margin_blocksso it stays at least 6 blocks above (4.9).max-cltv-expiry(default 2016): Mostro'spayment_cltv_limitmust not exceed it (4.9).- Keep LND's wallet seed and channel backup safe: see 5.6.
2.3 Lightning Liquidity
To facilitate trades, your Lightning node needs channels with Bitcoin in them. Think of Lightning channels as pre-funded payment tunnels. The Bitcoin inside these channels is your "liquidity".
How much do you need?
| Target trading volume | Suggested liquidity | Approximate BTC |
|---|---|---|
| Small community (few trades/day) | 1–5 million sats | 0.01–0.05 BTC |
| Medium community | 5–20 million sats | 0.05–0.20 BTC |
| Active community | 20–100 million sats | 0.20–1.0 BTC |
The Bitcoin in your Lightning channels is locked onchain but remains highly spendable via Lightning Network. Many services accept Lightning payments — from coffee shops to VPS providers — making your liquidity quite flexible for everyday use.
Start small, grow incrementally. Begin with enough for your community's initial needs and monitor feedback. When traders report orders failing due to insufficient capacity, that's your signal to add more. Listen to your community.
Getting liquidity:
- Open channels to well-connected nodes (use Lightning Network+ or Amboss to find good peers)
- You need outbound capacity (to pay buyers) and inbound capacity (to receive from sellers). Each trade first receives the seller's sats and then pays the buyer, so keep at least your
max_order_amount(4.3) of capacity in each direction - Getting inbound liquidity is usually harder — consider Lightning Loop, Magma, or channel swap services
2.4 Nostr Keys
Your Mostro node needs its own identity on the Nostr network — a cryptographic key pair with a public key (your node's address) and a private key (your secret).
Never reuse Nostr keys between Mostro instances. Each node needs its own unique identity.
Generating secure Nostr keys locally with rana:
# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Install rana - local Nostr key generator
cargo install rana
# Generate a new key pair (with 12-word seed phrase)
rana --generate 12
Rana will generate your private key (nsec), public key (npub), and a backup seed phrase. Keep everything safe! Note: running rana without arguments starts PoW mining (difficulty 10) which can take minutes — use --generate for instant generation. Never generate important keys using online services.
2.5 Technical Skill Level
| Task | Difficulty | Required knowledge |
|---|---|---|
| Rent a VPS | Easy | Credit card, basic web browsing |
| Connect via SSH | Easy | Follow instructions, type commands |
| Install Docker | Medium | Copy-paste commands, basic troubleshooting |
| Run Mostro (Docker) | Medium | Edit config files, understand paths |
| Run Mostro (native) | Hard | Linux administration, software compilation, systemd |
| Set up LND from scratch | Hard | Significant Linux and networking knowledge |
| Manage Lightning liquidity | Hard | Understanding Lightning channel economics |
💡 Our recommendation: If your community has someone comfortable with the Linux command line, they can handle the Docker installation. Native compilation requires system administration experience. Lightning node setup is the most complex part — consider asking someone experienced for help, or using a node-in-a-box solution.
3. Step-by-Step Setup
All installation options share the same first steps. Then choose your preferred option:
- Option A (Docker Hub): Fastest. No compiling, no cloning. Recommended for most.
- Option B (Docker Build): Build the image locally from the repository.
- Option C (Native Build): More control, better for experienced sysadmins.
All assume you already have: ✅ A VPS with Ubuntu · ✅ SSH access · ✅ A working LND node.
Common Steps (for all 3 options)
Step 1: Connect to your VPS
ssh root@YOUR_VPS_IP_ADDRESS
Step 2: Update the system
# Download the latest package information
apt update
# Install all available updates
apt upgrade -y
Step 3: Install Docker and Docker Compose
Docker is required for options A and B. If you're compiling manually (Option C), you can skip this step.
# Install Docker using the official script
curl -fsSL https://get.docker.com | sh
# Verify Docker is installed
docker --version
# Verify Docker Compose
docker compose version
Step 4: Install additional tools
apt install -y git make
✅ Common steps completed. Now choose your installation option:
Option A: Docker Hub (Fastest — Recommended)
Run Mostro directly from Docker Hub without cloning the repository or compiling. Perfect for VPS deployments.
Step 5: Create configuration directory
mkdir -p ~/mostro-config/lnd
Step 6: Get the configuration template
curl -sL https://raw.githubusercontent.com/MostroP2P/mostro/v0.20.2/settings.tpl.toml \
-o ~/mostro-config/settings.toml
Step 7: Copy LND credentials
cp /path/to/your/tls.cert ~/mostro-config/lnd/tls.cert
cp /path/to/your/mostro.macaroon ~/mostro-config/lnd/mostro.macaroon
If LND is on the same machine, typical paths are:
/root/.lnd/tls.cert/root/.lnd/data/chain/bitcoin/mainnet/mostro.macaroon
Step 8: Edit the configuration
nano ~/mostro-config/settings.toml
Required changes:
[lightning]
lnd_cert_file = '/config/lnd/tls.cert'
lnd_macaroon_file = '/config/lnd/mostro.macaroon'
lnd_grpc_host = 'https://host.docker.internal:10009' # If LND on same VPS
# Or use 'https://YOUR_LND_IP:10009' if LND on different server
[database]
url = "sqlite:///config/mostro.db" # mostrod always uses <config-dir>/mostro.db
[nostr]
nsec_privkey = 'YOUR_NSEC_KEY_HERE'
relays = ['wss://relay.mostro.network', 'wss://nos.lol']
[mostro]
fee = 0.006 # 0.6% fee per trade
max_order_amount = 1000000 # Maximum order in sats
min_payment_amount = 100 # Minimum order in sats
fiat_currencies_accepted = ['USD', 'EUR'] # Your currencies
Save: Ctrl+X, then Y, then Enter.
Step 9: Fix permissions
Avoid chmod 777. Use least privilege.
sudo chown -R 1000:1000 ~/mostro-config
chmod 700 ~/mostro-config
chmod 600 ~/mostro-config/settings.toml
chmod 600 ~/mostro-config/lnd/mostro.macaroon
Step 10: Run the container
If LND is on the same VPS (LND must accept the container's connection, or use host networking instead: see 2.2):
docker run -d --name mostro \
--restart unless-stopped \
--add-host=host.docker.internal:host-gateway \
-v ~/mostro-config:/config \
mostrop2p/mostro:v0.20.2
If LND is on a different server:
docker run -d --name mostro \
--restart unless-stopped \
-v ~/mostro-config:/config \
mostrop2p/mostro:v0.20.2
Step 11: Check the logs
docker logs -f mostro
Look for these messages:
Mostro pubkey: npub1... (hex ...)— Your node's identity. Write the npub down: it is what users and other tools needSettings correctly loaded!— Configuration is validTransport: nip44 (protocol v2, event kind 14)— Wire protocol in use (see 4.8)Recorded Lightning node identity <pubkey>— LND reached (first run only)Published health event: status ok, seq N— Repeated every minute. At least one relay accepted the node's health report (see 5.4)
There is no "connected to LND" or "connected to relay" message. Mostro contacts LND while starting up, so a daemon that gets past startup has reached LND. Failure is loud instead: it logs Ln node error and exits. After startup it polls LND every 15 seconds and logs LND did not answer GetInfo if it stops answering; outgoing payments then wait until it is back (5.4). For relays, the health-event line is your signal: No relay accepted the health event means none of them took it.
If you see Permission denied (os error 13), re-apply permissions: chown -R 1000:1000 ~/mostro-config and restart: docker restart mostro.
🎉 Congratulations! If you see successful connections in the logs, your Mostro node is running!
Alternative: Docker Compose
Instead of a long docker run command you can describe the container in a compose file. It runs the same image with the same settings, and updating becomes a one-line tag change. Create ~/mostro-docker/compose.yml:
mkdir -p ~/mostro-docker
nano ~/mostro-docker/compose.yml
services:
mostro:
image: mostrop2p/mostro:v0.20.2
container_name: mostro
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway" # only if LND runs on this VPS
volumes:
- ${HOME}/mostro-config:/config
Start it and follow the logs:
docker compose -f ~/mostro-docker/compose.yml up -d
docker compose -f ~/mostro-docker/compose.yml logs -f mostro
Pick one: docker run or compose, not both. To update either of them, see 5.5.
Always use a specific version tag (e.g., mostrop2p/mostro:v0.20.2) instead of :latest to control deployments.
Option B: Docker Build (Build image locally)
Step 5: Download Mostro
cd /opt
git clone https://github.com/MostroP2P/mostro.git
cd mostro
Step 6: Set up configuration files
cd docker
mkdir -p config
cp ../settings.tpl.toml config/settings.toml
Step 7: Edit the configuration file
nano config/settings.toml
Edit the same settings as Option A, Step 8, with one difference: make docker-build copies your macaroon to config/lnd/admin.macaroon, whatever its original name, so point Mostro there:
lnd_macaroon_file = '/config/lnd/admin.macaroon' # still your restricted mostro.macaroon, under another name
Unlike Option A, the repository's docker/compose.yml does not map host.docker.internal, so on Linux that name does not resolve inside the container. Add the mapping to the mostro service before building:
extra_hosts:
- "host.docker.internal:host-gateway"
Or point lnd_grpc_host at the host's LAN IP instead. Note that make docker-build also builds the StartOS image, which a VPS does not need — it only costs build time.
Step 8: Build the Docker image
cd ..
LND_CERT_FILE=/root/.lnd/tls.cert \
LND_MACAROON_FILE=/root/.lnd/data/chain/bitcoin/mainnet/mostro.macaroon \
make docker-build
Step 9: Start Mostro
# Start Mostro and the bundled relay. Plain `make docker-up` also
# starts the StartOS image, which you don't need on a VPS.
docker compose -f docker/compose.yml up -d mostro nostr-relay
# Check status
docker compose -f docker/compose.yml ps
# View logs
docker compose -f docker/compose.yml logs -f mostro
🎉 Congratulations! If you see successful connections, your Mostro node is running!
Option C: Native Build (For Technical Operators)
Step 5: Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source /root/.cargo/env
rustc --version
cargo --version
Do NOT install Rust via apt install rustc. Always use rustup. The system package is often outdated.
Step 6: Install build dependencies
apt install -y cmake build-essential libsqlite3-dev libssl-dev \
pkg-config git sqlite3 protobuf-compiler
Step 7: Download and compile Mostro
cd /opt
git clone https://github.com/MostroP2P/mostro.git
cd mostro
cargo build --release
If compilation fails due to low RAM, add swap space:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
Steps 8–10: Install, initialize, and clean
install target/release/mostrod /usr/local/bin
cargo clean # Saves 2+ GB of space
Steps 11–12: Create user and configure
adduser --disabled-login mostro
mkdir -p /opt/mostro
cp settings.tpl.toml /opt/mostro/settings.toml
nano /opt/mostro/settings.toml
Edit the same settings as Option A, Step 8.
Steps 13–15: Test, permissions, and systemd service
# Test run
/usr/local/bin/mostrod -d /opt/mostro
# Set permissions
chown -R mostro:mostro /opt/mostro
If you run mostrod with no settings.toml in the target directory and you are on a terminal, it offers a setup menu that can build the config for you and write the nsec to a .env file. Without a terminal — Docker, systemd, CI — it copies the template, prints where it put it, and exits so you can edit it.
Create the systemd service:
# /etc/systemd/system/mostro.service
[Unit]
Description=Mostro daemon
After=network.target
[Service]
Type=simple
User=mostro
WorkingDirectory=/home/mostro
ExecStart=/usr/local/bin/mostrod -d /opt/mostro
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable mostro.service
systemctl start mostro.service
systemctl status mostro.service
🎉 Congratulations! Your Mostro node is running as a system service.
4. Configuration In Depth
The settings.toml file controls everything about your Mostro node.
4.1 Nostr Keys — Your Node's Identity
[nostr]
nsec_privkey = 'YOUR_NSEC_KEY'
relays = [
'wss://relay.mostro.network',
'wss://nos.lol',
'wss://relay.nostr.band'
]
Which relays to use?
wss://relay.mostro.network— Mostro's own relay, recommendedwss://nos.lol— Reliable and well-connected relay- Add 3–5 relays for reliability. More relays = better availability but more bandwidth.
You can also run your own Nostr relay alongside Mostro. The Docker Build path (Option B) bundles one in its compose.yml; Option A and the native build do not.
Keeping the key out of settings.toml
Mostro also reads the key from the MOSTRO_NSEC_PRIVKEY environment variable. Precedence is: environment variable, then <config-dir>/.env, then settings.toml.
# ~/mostro-config/.env (chmod 600) — loaded automatically at startup
MOSTRO_NSEC_PRIVKEY=nsec1...
# Docker
docker run -e MOSTRO_NSEC_PRIVKEY=nsec1... ...
# systemd unit
Environment="MOSTRO_NSEC_PRIVKEY=nsec1..."
Leaving nsec_privkey in settings.toml still works. If you use the .env file, back it up as carefully as the config itself.
4.2 Fees — How You Earn Revenue
[mostro]
fee = 0.006
dev_fee_percentage = 0.30
Trading fee (fee): Percentage charged per trade, split between buyer and seller.
0.006= 0.6% (each party pays 0.3%)0.01= 1.0% (each party pays 0.5%)0= free (good for growing your user base)
Example: On a 100,000 sat trade with fee = 0.006: The buyer pays 300 sats, the seller pays 300 sats, your node earns 600 sats total.
Development fee (dev_fee_percentage): A percentage of your fee earnings that goes to Mostro development.
0.30= 30% (default) — out of 600 sats, 180 go to the development fund- Minimum: 10% (
0.10), Maximum: 100% (1.0) - Paid by your node from its earnings, not charged to users
- Sent automatically to the Lightning address
dev@pay.mostro.foundation; there is nothing to configure - Paid only when your LND runs on mainnet. On testnet, signet or regtest the amounts are still computed, but nothing is paid and the log says
dev fee payments are disabled - A failed payment is retried per order, from one minute up to one hour apart
- All payments publicly auditable via Nostr events (kind 8383)
Setting dev_fee_percentage below 0.10 will prevent Mostro from starting. This minimum ensures sustainable development funding.
4.3 Order Limits and Currencies
[mostro]
max_order_amount = 1000000
min_payment_amount = 100
max_orders_per_response = 10
fiat_currencies_accepted = ['USD', 'EUR', 'ARS', 'CUP']
max_order_amount: Largest trade in satoshis. Set based on your Lightning channel capacity.min_payment_amount: Minimum trade in satoshis. 1,000 or 10,000 is more practical than 100. Both limits are checked against the sats the order really moves, premium or discount included.max_orders_per_response: Maximum number of orders Mostro returns in a single query. If a user has more orders than this limit (e.g. when restoring their session from the mobile client), they will receive acant-do: too_many_requestserror and won't be able to recover their orders. If your users trade frequently, increase this value (e.g. 50 or 100). The default of 10 may not be enough.fiat_currencies_accepted: Use ISO 4217 codes, in UPPERCASE: the match is exact. Empty array[]accepts all currencies. Since v0.20.2 the list can also hold the stablecoinsUSDT,USDCandDOC, and bitcoin on other networks (BTC,LBTC,RBTC) — see 4.12.
4.4 Node Profile (Optional but Recommended)
[mostro]
name = "LatAm Mostro"
about = "P2P Bitcoin exchange for Latin America. Spanish support."
picture = "https://example.com/your-logo.png"
website = "https://your-community-site.com"
These set your Mostro's profile on Nostr (NIP-01 kind 0 metadata). Clients display this information so users know which Mostro they're trading on.
4.5 Timing and Expiration
[mostro]
expiration_hours = 24 # How long an untaken order stays in the book
max_expiration_days = 15 # Longest expiration a maker may ask for
expiration_seconds = 900 # How long a taken order may wait for the hold invoice
# or the buyer's invoice before it returns to the book (15 min)
[lightning]
hold_invoice_expiration_window = 300 # Time a taker has to pay the invoice or add a payout one (5 min)
hold_invoice_cltv_delta = 144 # Lifetime of the escrow, in blocks (~1 day)
Note that hold_invoice_expiration_window belongs to [lightning]: written under [mostro] it is silently ignored. It must be greater than 0, and it is also the expiry of a taker's bond invoice.
Once the seller's sats are locked, the buyer has roughly hold_invoice_cltv_delta - escrow_deadline_margin_blocks blocks to send the payment: 144 − 24 = 120 blocks, about 20 hours, with the defaults. When that runs out, Mostro cancels the trade if the buyer never marked the payment as sent, or opens a dispute if they did (see 4.9). Users see the countdown in the app.
4.6 Anti-Spam
[mostro]
pow = 0 # 0 = disabled; 10-20 = moderate. Start with 0.
A sharper gate is pow_first_contact. pow applies to every message, while pow_first_contact applies only to senders that are not part of an active trade and is checked before decryption. That keeps ongoing trades cheap while charging strangers real work:
[mostro]
pow = 0 # ongoing trades
pow_first_contact = 16 # new orders and takes from unknown keys
Clients must mine at least that much. mostro-cli users have to set POW (or -p) to your value, or their new orders and takes are dropped without an answer.
A stronger deterrent against people who take orders and walk away is the anti-abuse bond (see 4.11).
4.7 RPC Admin Interface (Optional)
[rpc]
enabled = false
listen_address = "127.0.0.1"
port = 50051
# auth_token = "a-long-random-string"
This gRPC interface is for operator tooling: grpcurl, and mostro-cli for maintenance mode (admsetmaintenance, admmaintenancestatus, admcancelpending). Mostrix does not use it — it works over Nostr, so you do not need RPC for dispute resolution.
Keep listen_address as "127.0.0.1" and never expose the port to the internet. Set auth_token whenever the port is reachable through anything other than the local machine, such as an SSH tunnel or a sidecar container: a forwarded connection arrives as loopback, so the bind address alone is not authorization. With a token set, every mutating call must carry the header authorization: Bearer <token>.
Besides maintenance mode, the RPC carries the reputation calls RevokeReputationImports and RebindReputationExport (see 4.13). Both are accepted from loopback only, and mostro-cli has no command for them yet: use grpcurl. mostro-cli sends MOSTRO_RPC_TOKEN only to a loopback address or over https://, never in clear text to a remote host.
4.8 Wire Protocol (Transport)
Since v0.19.0 a Mostro node speaks only protocol v2: signed kind-14 events with NIP-44 encrypted content. Protocol v1 (NIP-59 gift wraps, kind 1059) was removed, and there is nothing to choose any more. Leave the transport line out of settings.toml; the template no longer has it.
| Line in settings.toml | Result |
|---|---|
| none | Protocol v2. Recommended. |
transport = "nip44" | Protocol v2. Accepted and harmless. |
transport = "gift-wrap" | Mostro refuses to start, with an error that tells you to delete the line. |
Your node advertises protocol_version 2 in its kind 38385 info event. All current clients speak v2: the Mostro App, Mostrix 0.3.1 or later, mostro-cli 0.16.2 or later and Bosstro. A client that only speaks v1, such as an old Mostro Mobile build, gets no answer from your node: ask your users to update.
If your settings.toml still says transport = "gift-wrap", delete that line before you update. The only way to keep serving protocol-v1 clients is to stay on mostrod 0.18.x, which no longer receives fixes.
4.9 Lightning Safety Limits
These [lightning] settings bound how long your channels can stay locked and how many payouts can be unresolved at once. They all have defaults, so a config file from an older version usually still starts, but a fresh template ships them and they are worth knowing.
[lightning]
max_final_cltv_expiry_delta = 144
escrow_deadline_margin_blocks = 24
max_inflight_payouts = 100
max_inflight_payouts_per_destination = 10
payment_cltv_limit = 1008
allow_node_change = false
| Setting | What it protects |
|---|---|
max_final_cltv_expiry_delta | Rejects a payout invoice whose final CLTV would let the payee sit on your sats too long. 144 blocks (about a day) is the top of what real wallets ask for. Never set it to 0 — that rejects every invoice. |
escrow_deadline_margin_blocks | Safety margin before LND auto-cancels an accepted hold invoice. When the chain gets this close, Mostro cancels the trade or opens a dispute while the escrow still exists. Since v0.20.0 Mostro refuses to start unless it is at least 18 (LND's default invoices.holdexpirydelta of 12 plus 6 blocks of headroom) and below hold_invoice_cltv_delta. If you raised holdexpirydelta, keep this margin at least 6 blocks above it yourself. The value is published in your info event. |
max_inflight_payouts | Ceiling on unresolved payments across the node, so a payee who never settles cannot exhaust your HTLC slots. A gated payout is delayed, never dropped. |
max_inflight_payouts_per_destination | The same ceiling per destination pubkey, and the sharper of the two. |
payment_cltv_limit | Ceiling on the total timelock of a payout route. Must not exceed your LND's --max-cltv-expiry, and must sit at least 576 blocks above max_final_cltv_expiry_delta or honest payouts fail with "no route". |
allow_node_change | Boot guard for a changed Lightning node. Leave it false and see 5.8. |
Note also that max_routing_fee, in the [mostro] block, now defaults to 0.002 (0.2%).
4.10 Bitcoin Price Sources
Mostro needs a BTC/fiat rate to price orders. With no [price] block it uses a single source, Yadio, through the now-deprecated bitcoin_price_api_url. Adding the block gives you several sources, combined by median with an outlier guard, so one API going down or returning a bad number does not move your prices.
[price]
update_interval_seconds = 300
max_price_staleness_seconds = 1800
outlier_threshold_pct = 5.0 # discard a source this far from the median (needs 3+ sources)
provider_timeout_seconds = 10
provider_failure_threshold = 3 # failures before a source is put on cooldown
provider_failure_cooldown_seconds = 120
publish_to_nostr = true # publish aggregated rates as kind 30078
[price.providers.yadio]
enabled = true
url = "https://api.yadio.io"
[price.providers.coingecko]
enabled = true
url = "https://api.coingecko.com/api/v3"
# api_key = "CG-xxxx" # optional, raises rate limits
[price.providers.currency_api]
enabled = true
url = "https://currency-api.pages.dev/v1"
fallback_urls = ["https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1"]
except = ["CUP", "MLC"] # official rate only, don't mix with informal sources
[price.providers.blockchain]
enabled = true
url = "https://blockchain.info"
Each source takes only or except to scope which currencies it contributes, and fallback_urls for mirrors tried when the main URL fails. An enabled source missing a required secret fails at startup rather than silently producing no quotes.
You can take rates from Nostr instead of HTTP, published by Mostro nodes you trust. This reuses the relays already in [nostr], so it works wherever your node can already reach a relay. With several trusted nodes the freshest valid event wins.
[price.providers.nostr]
enabled = true
trusted_nodes = [
# hex pubkeys of Mostro nodes you trust to publish accurate rates
]
Operators serving Cuban pesos can add El Toque for informal-market CUP and MLC. It is opt-in, scoped to those two currencies, and needs a free token — an enabled El Toque without a token refuses to start.
Keep publish_to_nostr = true. Besides feeding other nodes, the published rates tell the Mostro App which currencies your node can price at market: a code missing from them is offered at a fixed price only. To price stablecoins and bitcoin on other networks, see 4.12.
As soon as settings.toml has a [price] or [price.providers.*] table, Mostro uses the multi-source engine and ignores bitcoin_price_api_url. Make sure at least one market source (Yadio, CoinGecko, Currency API, Blockchain.com…) is enabled, or your node has no fiat rate and refuses every market-price order. Watch the startup log for warnings that start with price:.
4.11 Other Optional Blocks
More blocks you may meet in a fresh template. None is required. Three of them get a section of their own: [price.providers.pegged] (4.12), [reputation_import] / [reputation_export] (4.13) and [health] (5.4). serbero_pubkey, in [mostro], is covered in 5.10.
Event retention. How long Mostro keeps each event kind before it expires. Omit the block entirely to accept the defaults.
[expiration]
order_days = 30 # order events (kind 38383)
rating_days = 90 # reputation history (kind 38384)
dispute_days = 90 # disputes, kept longer for auditing (kind 38386)
fee_audit_days = 365 # fee transparency (kind 8383)
dm_days = 30 # protocol-v2 direct messages (kind 14)
Anti-abuse bonds
[anti_abuse_bond] asks takers, makers or both to lock a small Lightning hold invoice next to the trade. Honest users get it back untouched; a solver can slash the bond of whoever acted in bad faith, and a share goes to the honest counterparty. Users see it as a "deposit" in the app, and the user docs explain it in their Anti-Abuse Bond chapter. All phases have shipped; it is disabled by default.
[anti_abuse_bond]
enabled = true
amount_pct = 0.01 # bond = max(1% of the order, base_amount_sats)
base_amount_sats = 1000
apply_to = "take" # "take" | "make" | "both"
slash_on_waiting_timeout = false # also slash whoever lets their step time out
slash_node_share_pct = 0.5 # share of a slashed bond the node keeps
payout_claim_window_days = 15 # time the winner has to claim their share
maker_bond_payment_timeout_seconds = 900
# bond_cltv_delta = 432 # see below
- A maker who has not paid the bond yet can cancel the order, and an unpaid order expires after
maker_bond_payment_timeout_seconds. - A maker bond stays locked while the order rests in the book, so an order rests there for at most
bond_cltv_delta - hold_invoice_cltv_delta - 144blocks (about 24 h with the defaults), whateverexpiration_hourssays. bond_cltv_deltamust be abovehold_invoice_cltv_delta + 144and at most 864. On v0.20.2 its default is a fixed 432, so if you raisedhold_invoice_cltv_deltato 288 or more, set it explicitly or Mostro refuses to start.- Solvers slash with the bond option when they settle or cancel (Mostrix, Bosstro, or
mostro-cli admsettle/admcancel --slashseller/--slashbuyer).
Read the project's docs/ANTI_ABUSE_BOND.md before enabling it on a live node, and announce it to your users first.
Payer history (experimental)
[payer_history] fights the triangulation scam, where a buyer pays from a third party's bank account. The buyer commits to a hash of the account they will pay from (declare-payer), the seller can compare it with the sender on their statement, and Mostro stores only an HMAC of it. As of v0.20.2 only the declaration is wired: the require_declaration gate and the history shown to sellers are not implemented yet, and no released client sends declare-payer. Leave it off for now.
# [payer_history]
# enabled = false
# require_declaration = false
Cashu escrow
[cashu] is an experimental mode that runs without LND and escrows trades in Cashu tokens. It is not usable for real trading yet — trade actions after the take are still rejected — and it cannot be combined with anti-abuse bonds. Since v0.19.3 the maker picks the mint per order from an allowlist:
# [cashu]
# enabled = false
# mint_urls = ["https://mint.example.com"] # empty [] = any mint with a public host
# escrow_locktime_days = 15
The old single mint_url key is rejected: a config that still has it does not load.
4.12 Stablecoins and Bitcoin on Other Networks
Since v0.20.2 a node can offer trades against six codes that are not fiat currencies: dollar stablecoins, and bitcoin on other networks. Lightning sats are always one side of the trade; the other side is paid on-chain, on Liquid, on Rootstock or on the stablecoin's network, directly between the two users.
| Code | What it is | Priced as | Order amount in |
|---|---|---|---|
USDT | Tether USD | 1 USD | whole coins |
USDC | USD Coin | 1 USD | whole coins |
DOC | Dollar on Chain (Rootstock) | 1 USD | whole coins |
BTC | Bitcoin on-chain | 1 sat = 1 sat | sats |
LBTC | L-BTC on Liquid | 1 sat = 1 sat | sats |
RBTC | RBTC on Rootstock | 1 sat = 1 sat | sats |
- Stablecoins are pegged 1:1 to the dollar, so a USDT order at market price is priced exactly like a USD order, premium included. The USD rate comes from your normal price sources (4.10); no extra API is called.
- BTC, LBTC and RBTC are quoted 1:1 in sats, so they need no price API at all. An order for 100,000 means 100,000 sats of the asset (0.001 RBTC). At market price the premium is the price of the swap: a sell order (the maker sells Lightning sats) for 100,000 sats of RBTC with a 1% premium locks 99,000 Lightning sats, and the seller receives 100,000 sats of RBTC.
- No other code is supported: DAI, EURC and the rest are refused at startup if you list them under
only.
Enabling it
You need mostrod v0.20.2 or later. Two settings work together: fiat_currencies_accepted says which codes orders may use, and the pegged provider says which of them have a market price.
[mostro]
# Keep your fiat currencies and add the new codes, in UPPERCASE
fiat_currencies_accepted = ['USD', 'EUR', 'USDT', 'USDC', 'DOC', 'LBTC', 'RBTC']
# A market price source for fiat (and therefore for the stablecoins).
# Required on v0.20.2 as soon as you add any [price.providers.*] table.
[price.providers.yadio]
enabled = true
url = "https://api.yadio.io"
[price.providers.pegged]
enabled = true
only = ["USDT", "USDC", "DOC", "LBTC", "RBTC"] # or except = ["BTC"]; without either, all six
Restart Mostro and check the log. If you see price: the pegged provider quotes ... but fiat_currencies_accepted does not list them, add the missing codes: a quoted code that is not accepted publishes a rate but refuses every order. An empty fiat_currencies_accepted = [] accepts every code.
Any [price.providers.*] table switches the node to the multi-source price engine and drops the legacy bitcoin_price_api_url. On v0.20.2 a pegged table with no market source next to it leaves the node without fiat rates: USD, EUR and USDT market orders are all refused. Always enable at least one market source, as in the example above. (The next release keeps the legacy Yadio source automatically in that case.) On v0.20.1 or older, don't add the block at all: the provider does not exist there.
Fixed price only. To accept a code but not price it at market, list it in fiat_currencies_accepted and leave it out of only. Users then have to set a fixed amount of sats. That is a sensible choice for on-chain BTC, where network fees make small market orders awkward.
What your users see
The Mostro App 2.1.2 and later shows these codes in the currency picker under Stablecoins and Bitcoin on other networks, but only those your node accepts. BTC, LBTC and RBTC amounts are entered in sats. The payment method names the network (USDT (Tron), USDC (Base), DOC (Rootstock), LBTC (Liquid)…), the seller shares a receiving address in the trade chat, and the buyer pastes the transaction ID there after paying. Mostro never checks the network or the address. mostro-cli and Mostrix can only create fixed-price orders in these codes for now.
Disputes need different evidence
Brief your solvers before you enable this. There is no bank statement: the evidence is the transaction ID pasted in the chat, which the solver checks in a block explorer for that network. For Liquid the buyer must share the unblinded link, because Liquid hides amounts. Transfers are irreversible, a payment on the wrong network can be lost, and the buyer pays the network fee: the seller must receive the full amount. The seller should release only after the transfer confirms. The window is the same as for fiat (about 120 blocks with the defaults, see 4.5), so a low-fee on-chain transfer can miss it.
There is no automatic depeg guard yet. If USDT or USDC drifts away from one dollar, remove it from only, or add it to except if you don't use only (keep it in fiat_currencies_accepted if you still want fixed-price orders) and restart.
4.13 Reputation Import and Export
Users can bring the reputation they earned elsewhere — on the Telegram bot @lnp2pBot or on another Mostro node — into yours, so a trader who moves does not start from zero. Three figures travel: the number of ratings received, the average rating, and the date of the first trade. They arrive as an attestation: a small Nostr event (kind 38388) signed by the source, addressed to the user's identity and valid for 7 days. Your node adds them to the user's own reputation, and counterparties see a single merged figure.
Both directions are opt-in and off by default. Import needs mostrod v0.20.0 or later; export, v0.20.1 or later. Users need the Mostro App in reputation mode: in full privacy mode there is no identity to carry a reputation.
Importing from lnp2pBot
lnp2pBot signs attestations with a dedicated key. Add it to your trust list:
[reputation_import]
enabled = true
max_lifetime_seconds = 604800 # longest attestation you accept (7 days; at most 30)
[[reputation_import.issuers]]
name = "lnp2pbot" # never rename an entry once it has imports
keys = ["000004ac730537f1cf7d91f4f584a498a530a0205d35e816ebbaf23aec61e459"]
# npub1qqqqftrnq5mlrnmaj860tp9ynzjnpgpqt567s9hthter4mrpu3vsrta3em
Restart Mostro. Your info event (kind 38385) now carries a reputation_import_issuers tag, which is how the app knows it can offer the import on your node. Several community nodes already trust this key.
The bot exports an account with at least 10 completed trades and 5 ratings received that is not banned. The user starts from the app (Settings → Network → Reputation → Import reputation → Get it from lnp2pBot), confirms in Telegram that the npub matches, and pastes back the message the bot sends.
Trusting another Mostro node
A node that exports announces its signing key in the reputation_issuer tag of its info event. Read it with the command in 5.4 (look for that tag), confirm it with the operator, and add an entry for it:
[[reputation_import.issuers]]
name = "mostro-example" # a stable local name of your choice
keys = ["<that node's reputation_issuer key, npub or hex>"]
- The
nameis what imports are deduplicated on. Never rename an entry that already has imports: Mostro refuses to start if a key moves to another name. - A key may belong to one entry only. To follow a key rotation, add the new key to the same entry and remove the old one once its attestations have expired.
- Trust only sources you trust: a large imported record outweighs local ratings.
A user can import once from each source on each node, and the import cannot be undone by the user. Figures are a snapshot: later ratings on the source do not follow.
Exporting from your node
Export lets your users carry the reputation they earned on your node to another one. Only ratings received on your node travel, never imported ones. The user needs 10 completed trades and 5 ratings received on your node, and must not be banned.
Attestations are signed with a dedicated key, never your node's key. Generate one and keep it next to the nsec, in the environment or in <config-dir>/.env:
# Docker Hub layout; use your own config directory otherwise
echo "MOSTRO_REPUTATION_ISSUER_SK=$(openssl rand -hex 32)" >> ~/mostro-config/.env
sudo chown 1000:1000 ~/mostro-config/.env
chmod 600 ~/mostro-config/.env
[reputation_export]
enabled = true
issuer_key_env = "MOSTRO_REPUTATION_ISSUER_SK"
lifetime_seconds = 604800 # 1 to 604800 (7 days)
Mostro refuses to start if the variable is missing or invalid, if it holds your node's own key, or if its public key is also in your [reputation_import] list. Once it runs, your info event carries the reputation_issuer tag: share that key with the operators who want to trust your node. Back the key up with your other secrets. If it leaks, anyone can mint attestations in your node's name until the other operators remove it and revoke the imports.
Revoking imports and rebinding exports
Two admin RPC calls (4.7) handle the rare cases. They are accepted from loopback only; add -H 'authorization: Bearer <token>' if you set auth_token. Run them from a checkout of the mostro repository, which holds proto/admin.proto.
A trusted issuer's key leaked. First remove the key from its entry and restart, then reverse every import signed with it (add "imported_after": <unix time> to limit it to recent ones):
grpcurl -plaintext -import-path proto -proto admin.proto \
-d '{"issuer_key": "<npub or hex>", "reason": "key leaked"}' \
127.0.0.1:50051 mostro.admin.v1.AdminService/RevokeReputationImports
Each import is reversed exactly, ratings received since then are kept, and users can import again with an attestation from the new key.
A user lost the identity their export is bound to. The first export binds the account to one identity; moving it normally needs an authorization signed with the old identity, which the app produces. If the old identity is gone, you can move it yourself after verifying the person:
grpcurl -plaintext -import-path proto -proto admin.proto \
-d '{"identity": "<old npub>", "new_identity": "<new npub>", "reason": "support ticket"}' \
127.0.0.1:50051 mostro.admin.v1.AdminService/RebindReputationExport
A user who restores the same 12 words keeps the same identity and needs none of this.
5. Operating Your Mostro Node
5.1 How Disputes Work
Disputes are your most important operational responsibility.
When do disputes happen?
- The buyer says they paid, the seller says they didn't receive it
- The seller refuses to release Bitcoin after receiving payment
- Either party becomes unresponsive
The dispute process:
- User opens a dispute — Either party clicks "Dispute" in their client
- Mostro flags the order — Status changes to "Dispute", funds remain locked
- Arbiter takes the case — An admin assigned to your node investigates
- Investigation — Communicates with both parties, requests evidence
- Resolution — Arbiter decides: release to buyer, or refund to seller. With anti-abuse bonds enabled (4.11), the arbiter can also slash the bond of whoever acted in bad faith.
The arbiter writes to each party on a private dispute chat, which the Mostro App shows under Chat → Disputes. Either party can also share the key of their trade chat with the arbiter, who then reads that conversation in Mostrix's Observer tab or in Bosstro. The parties can still finish the trade on their own: if the seller releases, or both agree to a cooperative cancel, Mostro closes the dispute by itself and tells the arbiter.
If you run Serbero (5.10), it alerts your arbiters the moment a dispute opens and can take the first steps with the parties. It never decides or moves funds.
Choose your arbiters carefully. They have the power to decide where locked funds go. Pick trusted, impartial community members. Having 2–3 arbiters is recommended.
Solver permission levels
A solver can be registered read-only or with full powers. Both levels can take a dispute and talk to the parties, but only a read-write solver can decide where the money goes.
| Registered as | Can do | Cannot do |
|---|---|---|
npub1...:read | Take a dispute, read it, message both parties | Settle or cancel the order |
npub1...:read-write | Everything, including settling and cancelling | — |
A bare npub1... with no suffix defaults to read-write, and so does registration over the RPC interface. Start a new arbiter on :read while they learn the process, then re-register them read-write once you trust their judgement. A read-write solver can take over a dispute that a read-only one (or Serbero) is holding.
Your arbiters can work from a terminal with Mostrix (5.2) or from an Android phone with Bosstro (5.11).
5.2 Mostrix — Your Admin Tool
Mostrix is a terminal (TUI) Mostro client with two modes: a user mode to trade, and an admin mode for dispute resolution — take disputes, chat with both parties, settle or cancel, slash bonds and add solvers. If you run a Mostro node, you need Mostrix. It talks to your node over Nostr only, never over the RPC interface.
Option A: Download Pre-built Binary (Recommended)
Download the latest release for your platform from GitHub Releases:
# Linux (x86_64)
wget https://github.com/MostroP2P/mostrix/releases/latest/download/mostrix-x86_64-unknown-linux-musl
# Linux (ARM64 / Raspberry Pi 4 and 5, 64-bit OS)
wget https://github.com/MostroP2P/mostrix/releases/latest/download/mostrix-aarch64-unknown-linux-musl
# Linux (ARMv7 / 32-bit Raspberry Pi OS)
wget https://github.com/MostroP2P/mostrix/releases/latest/download/mostrix-armv7-unknown-linux-gnueabi
# macOS (Apple Silicon / Intel)
curl -LO https://github.com/MostroP2P/mostrix/releases/latest/download/mostrix-aarch64-apple-darwin
curl -LO https://github.com/MostroP2P/mostrix/releases/latest/download/mostrix-x86_64-apple-darwin
# FreeBSD: mostrix-x86_64-unknown-freebsd
# Windows: download mostrix-x86_64-pc-windows-msvc.exe from the releases page
Verify the download before running it, as described just below.
Always verify the binary before running it. Import the maintainers' keys once:
curl https://raw.githubusercontent.com/MostroP2P/mostrix/main/keys/negrunch.asc | gpg --import
curl https://raw.githubusercontent.com/MostroP2P/mostrix/main/keys/arkanoider.asc | gpg --import
Signatures are detached files named manifest.txt.sig.<maintainer>. Recent releases carry only manifest.txt.sig.arkanoider (key 2E98 6CA1 C5E7 EA16 35CD 059C 4989 CC74 15A4 3AEC), so check the release page and download the ones it actually lists:
wget https://github.com/MostroP2P/mostrix/releases/latest/download/manifest.txt
wget https://github.com/MostroP2P/mostrix/releases/latest/download/manifest.txt.sig.arkanoider
# Verify each signature you downloaded
gpg --verify manifest.txt.sig.arkanoider manifest.txt
# Then check the binary's hash against the manifest
# (manifest lines carry an artifact-<target>/ prefix, stripped here)
sed 's# artifact-[^/]*/# #' manifest.txt | sha256sum -c --ignore-missing
One valid signature from a maintainer key you trust is enough. If a wget returns 404, that signature simply was not published for this release — do not treat a missing file as a verified one.
Only once a signature and the hash check out, make the binary executable and start it:
chmod +x mostrix-x86_64-unknown-linux-musl
./mostrix-x86_64-unknown-linux-musl
Option B: Build from Source
If you prefer to compile from source or need a platform not in the releases:
# Install dependencies (Ubuntu/Debian)
sudo apt install -y cmake build-essential pkg-config
# Install Rust (if not already installed; Mostrix needs 1.97 or newer)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Clone and build
git clone https://github.com/MostroP2P/mostrix.git
cd mostrix
cargo build --release
# Run
./target/release/mostrix
First Run & Configuration
On first run Mostrix creates ~/.mostrix/ with a database holding a new 12-word seed and a settings.toml, then shows a Backup New Keys popup with the 12 words. Write them down. A settings.toml placed next to the binary takes precedence (portable mode).
The generated config points at Mostro's default public node. Switch it to your own node: Settings → Select Mostro Instance, then paste your node's npub or hex, or edit mostro_pubkey in ~/.mostrix/settings.toml and restart.
For admin mode (dispute resolution), the relevant keys are:
# ~/.mostrix/settings.toml
mostro_pubkey = "YOUR_MOSTRO_PUBKEY" # hex or npub
nsec_privkey = "nsec1..." # your trader identity, derived from the seed — don't edit
admin_privkey = "nsec1..." # a registered solver's key — see below
relays = ["wss://relay.mostro.network"]
currencies_filter = [] # empty = show all currencies
user_mode = "admin" # or Settings → Switch Mode
# trusted_dm_senders = ["npub1..."] # Serbero's npub, to see its reports (5.10)
Mostro accepts dispute actions from its own key (the daemon's nsec_privkey) and from registered solver keys. Any other key is rejected. Only the daemon key can add solvers, so use it just for that and keep it on the server. For everyday dispute work, register a solver key — read-write to settle or cancel — and put that one in admin_privkey. Never reuse nsec_privkey.
ADMIN_NSEC=nsec1<daemon-key> MOSTRO_PUBKEY=<node-npub> \
RELAYS=wss://relay.mostro.network \
mostro-cli admaddsolver -n npub1solver...
Mostrix's Settings → Add Dispute Solver does the same thing, with Left/Right to choose Read or Read-Write (default Read-Write).
Admin mode in practice
- Disputes Pending: Enter takes a dispute. Ctrl+T takes one over from Serbero.
- Disputes Management: Tab cycles the BUYER, SELLER and SERBERO chats. Shift+F finalizes: pay the buyer, refund the seller, and slash a bond when the node has them.
- Observer: paste the trade-chat key a party shared to read their conversation.
- Settings → Link Watchdog: links the solver to mostro-watchdog (5.3), so they get a private Telegram ping when a party writes in a dispute they took.
- Ctrl+H shows context help. Quit from the Exit tab.
5.3 mostro-watchdog — Telegram Alerts for Disputes and Node Health
mostro-watchdog is a Telegram bot for your admin team. It watches your node over Nostr and posts to a private group or channel:
- Disputes: one message per dispute, sent when it opens and edited as it goes on (taken, Serbero's steps, takeover, resolution). Each edit gets a short reply so phones still ring.
- Node health: an alert when Mostro stops working, not only when it stops running: an info event gone silent, maintenance mode and, with Mostro v0.20.0 or later, LND out of sync or behind the chain, buyer payouts stuck or failing, HTLCs close to expiry and inactive channels. Only changes are posted: an alert, reminders while it lasts, and "resolved".
- Serbero's progress on each dispute, if you run Serbero (5.10).
- Private pings to solvers when a party writes to them in a dispute they took.
Option A: install script (recommended). It downloads the latest release, checks it against manifest.txt and installs to /usr/local/bin:
curl -fsSL https://raw.githubusercontent.com/MostroP2P/mostro-watchdog/main/install.sh | bash
# or into another directory:
curl -fsSL https://raw.githubusercontent.com/MostroP2P/mostro-watchdog/main/install.sh | bash -s -- --install-dir ~/.local/bin
Option B: manual binary download
# Linux x86_64 (Intel/AMD); use mostro-watchdog-linux-aarch64 on ARM64
curl -LO https://github.com/MostroP2P/mostro-watchdog/releases/latest/download/mostro-watchdog-linux-x86_64
curl -LO https://github.com/MostroP2P/mostro-watchdog/releases/latest/download/manifest.txt
sha256sum -c manifest.txt --ignore-missing
chmod +x mostro-watchdog-linux-x86_64
sudo mv mostro-watchdog-linux-x86_64 /usr/local/bin/mostro-watchdog
macOS and Windows binaries are on the releases page too.
Option C: build from source
sudo apt install -y cmake pkg-config libssl-dev
git clone https://github.com/MostroP2P/mostro-watchdog.git
cd mostro-watchdog
cargo build --release
sudo cp target/release/mostro-watchdog /usr/local/bin/
Configuration
The watchdog keeps its state (disputes.db) next to config.toml, so give it a directory of its own:
sudo useradd --system --home-dir /var/lib/mostro-watchdog --shell /usr/sbin/nologin mostro-watchdog
sudo install -d -o mostro-watchdog -g mostro-watchdog -m 700 /var/lib/mostro-watchdog
curl -LO https://raw.githubusercontent.com/MostroP2P/mostro-watchdog/main/config.example.toml
sudo install -o mostro-watchdog -g mostro-watchdog -m 600 config.example.toml /var/lib/mostro-watchdog/config.toml
sudo nano /var/lib/mostro-watchdog/config.toml
[mostro]
pubkey = "YOUR_MOSTRO_PUBKEY" # hex or npub
[nostr]
# Bootstrap relays: the watchdog reads your node's relay list (NIP-65)
# from them, then follows the relays your node advertises.
relays = ["wss://relay.mostro.network", "wss://nos.lol"]
[telegram]
bot_token = "YOUR_BOT_TOKEN" # from @BotFather
chat_id = -1001234567890 # your admin group or channel
[health]
info_silence_threshold = 900 # 3 × Mostro's publish_mostro_info_interval (300 by default)
mostro_health_enabled = true # needs Mostro v0.20.0+
event_alert_threshold = 0 # see below
To find chat_id, add the bot to the group, send a message there and open https://api.telegram.org/bot<TOKEN>/getUpdates.
The older "event silence" alert fires whenever no dispute event arrived for two hours, which on most nodes is just a quiet afternoon. The info and health checks detect a stopped node reliably, so 0 turns it off.
The watchdog's Nostr key
Health details, Serbero alerts, solver pings and the watchdog's own heartbeat need a key of its own, passed in the environment only:
sudo sh -c 'umask 077; echo "WATCHDOG_NOSTR_PRIVATE_KEY=$(openssl rand -hex 32)" > /etc/mostro-watchdog.env'
Back that file up. At startup the watchdog logs its public key. Put it in Mostro's settings.toml and restart Mostro, so your liquidity, payout failures and HTLC expiry are encrypted to the watchdog alone:
[health]
watchdog_pubkey = "WATCHDOG_PUBKEY" # hex or npub
This adds a public p tag to your health event that links your node to the watchdog's key. Without it, the watchdog still alerts on the public status.
Run it as a service
# /etc/systemd/system/mostro-watchdog.service
[Unit]
Description=mostro-watchdog
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=mostro-watchdog
WorkingDirectory=/var/lib/mostro-watchdog
EnvironmentFile=/etc/mostro-watchdog.env
ExecStart=/usr/local/bin/mostro-watchdog --config /var/lib/mostro-watchdog/config.toml
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now mostro-watchdog
journalctl -u mostro-watchdog -f
Optional: Serbero and solver alerts
Add an empty section for each one you want. Both use the key above:
[serbero] # Serbero's steps on each dispute's message
[solver_notifications] # private pings to solvers
For Serbero, also add the watchdog's hex public key to Serbero's config.toml as an observer — never as a solver — and restart Serbero. Observers get one line per step, never what the parties wrote. The watchdog finds Serbero on its own through your node's serbero_pubkey (5.10).
[[observers]]
pubkey = "WATCHDOG_PUBKEY_HEX"
For solver pings, each solver sends /link to the bot in a private chat and enters the code in Mostrix 0.3.6 or later, under Settings → Link Watchdog. The bot answers commands only in private chats: /version, /link, /unlink, /status.
A silent channel does not prove the watchdog is alive. You can run a second one on another machine, each with its own WATCHDOG_NOSTR_PRIVATE_KEY and the other's public key in [health].peer_watchdogs. Health alerts can also go to a chat of their own with [health].alerts_chat_id. The project's config.example.toml documents every option.
5.4 Monitoring and Node Health
Your node needs to be running 24/7.
# Native
systemctl status mostro.service
journalctl -u mostro -f
journalctl -u mostro | grep -E "(error|warn|connected)" --ignore-case
# Docker Hub (Option A)
docker ps --filter name=mostro
docker logs -f mostro
# Docker Build (Option B)
docker compose -f /opt/mostro/docker/compose.yml ps
docker compose -f /opt/mostro/docker/compose.yml logs -f mostro
Running is not the same as working
In October 2026 a production node's LND stopped following the chain for about 21 hours. Mostro kept running and kept accepting trades, but every buyer payout failed. A process check, a port check or an uptime robot would have shown green the whole time.
Since v0.20.0 Mostro checks this itself. Every minute it publishes a signed health event (kind 38389) with an overall status (ok, degraded or down), a state for each check (lnd_sync, payouts, relays, database) and LND's block height. It is on by default; this block only shows the defaults:
[health]
enabled = true # publish the health event (kind 38389)
interval = 60 # seconds between publications, 30 to 3600
watchdog_pubkey = "" # your watchdog's key: detailed metrics are encrypted to it (5.3)
lnd_unsynced_fail_after = 600 # must be greater than 60
auto_maintenance_on_lnd_unsynced = true
lnd_resynced_clear_after = 300
When LND stops being synced, Mostro protects your users on its own:
- After 60 seconds, outgoing payments (buyer payouts, dev fees, bond payouts) wait instead of failing. A waiting payout does not use up one of the buyer's payment attempts, and it goes out once LND recovers.
- After
lnd_unsynced_fail_afterseconds, Mostro enters maintenance mode with the reasonlnd_unsynced: no new orders or takes, while open trades can still finish. It leaves maintenance once LND has been synced forlnd_resynced_clear_afterseconds. Maintenance you turned on yourself (5.8) is never lifted automatically.
The log tells the story: LND reports synced_to_chain = false ...; outgoing payments wait after 60s, then maintenance mode ON (reason lnd_unsynced), and later LND is synced to chain again and maintenance mode OFF. Check LND with lncli getinfo (synced_to_chain, block_height). Set auto_maintenance_on_lnd_unsynced = false to opt out of the automatic maintenance.
Mostro only knows what its own LND tells it, and a node stuck on a stale chain does not always know it. That is why the two readers below compare its block height with public explorers:
- Private alerts: mostro-watchdog (5.3) tells your Telegram when something breaks and when it clears.
- Public status page: status.mostro.network shows the live state and 90-day uptime of every listed Mostro. Your users see it too.
The public status page
The page (Semáforo) reads the health events straight from the relays and verifies them in the visitor's browser, with no backend. Each node gets one colour:
| Colour | When |
|---|---|
| 🟢 Operational | Status ok, a fresh event, and LND less than 3 blocks behind the explorers |
| 🟡 Degraded | Status degraded, maintenance mode, or no independent block height to compare with |
| 🔴 Down | Status down, LND 3 or more blocks behind, or no valid event recently ("Not reporting") |
| ⚪ Not monitored | Listed, but not yet expected to publish |
Under each node, 90 daily bars and an uptime figure come from samples taken every 10 minutes.
Getting your node listed. Only curated nodes are shown, because anyone can sign an always-green event:
- Your node is listed as a community on this site.
- It runs Mostro v0.20.0 or later with the health event on, and publishes to at least one of
wss://relay.mostro.network,wss://mostro-p2p.techorwss://relay.shadowbip.com. - Open an issue or pull request on MostroP2P/semaforo to add it to
trusted.jsonwith its hex pubkey, name, region and network. Your avatar is copied from your Nostr profile picture.
Checking your node from the outside
Your node republishes an info event (kind 38385) describing itself: fees, currencies, protocol version, maintenance flag, bond settings, Serbero and reputation keys. Reading it and the health event from a relay is the quickest way to confirm the outside world sees what you think it does.
cargo install nostreq nostcat
# Info event
nostreq --kinds 38385 --limit 1 --authors YOUR_MOSTRO_PUBKEY_HEX \
| nostcat --stream wss://relay.mostro.network | jq
# Health event: status, checks, LND block height
nostreq --kinds 38389 --limit 1 --authors YOUR_MOSTRO_PUBKEY_HEX \
| nostcat --stream wss://relay.mostro.network | jq '.tags'
A host ping (UptimeRobot or similar) still tells you when the VPS itself is unreachable. It cannot tell you whether Mostro is paying buyers; the health event can.
5.5 Updating Mostro
Updating replaces the mostrod binary and nothing else: settings.toml and mostro.db stay where they are. Database migrations run on their own when the new version starts, so there is no extra step.
Before you update
- Read the release notes of the version you are moving to. Your
settings.tomlis never overwritten: compare it with the newsettings.tpl.toml. Most new features stay off until you add them, but a missing block takes its defaults, and some defaults are on (the health event of 5.4, for example). - Back up the database with the commands in 5.6. You need that backup to roll back.
Check these before you start the new version, or it may refuse to start:
- Delete any
transport = "gift-wrap"line (4.8). Users on old protocol-v1 clients have to update. escrow_deadline_margin_blocksmust be at least 18 and belowhold_invoice_cltv_delta(4.9).- Under
[cashu],mint_urlbecamemint_urls = [...](4.11). - With anti-abuse bonds on and
hold_invoice_cltv_deltaof 288 or more, setbond_cltv_deltaexplicitly (4.11). - Since v0.20.0 the health event is published by default, and Mostro may enter maintenance on its own when LND is out of sync (5.4).
Docker Hub (docker run)
export MOSTRO_TAG=v0.20.2
# Pull first: the running node stays up during the download
docker pull mostrop2p/mostro:$MOSTRO_TAG
docker stop mostro
docker rm mostro
docker run -d --name mostro \
--restart unless-stopped \
--add-host=host.docker.internal:host-gateway \
-v ~/mostro-config:/config \
mostrop2p/mostro:$MOSTRO_TAG
Use the same flags you installed with (drop --add-host if LND is on another server). If you don't remember them, check docker inspect mostro before removing the container.
docker restart is not an updatedocker restart starts the same container again, from the image it was created with. To run a new version the container has to be created again: docker rm + docker run, or docker compose up -d after changing the tag.
Docker Hub (Docker Compose)
export MOSTRO_TAG=v0.20.2
COMPOSE=~/mostro-docker/compose.yml
# Point the image line at the new tag, and check it
sed -i "s|image: mostrop2p/mostro:.*|image: mostrop2p/mostro:$MOSTRO_TAG|" $COMPOSE
grep image: $COMPOSE
docker compose -f $COMPOSE pull
# Recreates the container because the image changed
docker compose -f $COMPOSE up -d
Docker Build
cd /opt/mostro
git fetch --tags
git checkout v0.20.2
make docker-build
make docker-down
make docker-up
Native
cd /opt/mostro
git fetch --tags
git checkout v0.20.2
cargo build --release
install target/release/mostrod /usr/local/bin
cargo clean
systemctl restart mostro.service
Check the new version
# Docker Hub (docker run)
docker exec mostro mostrod --version
docker logs -f mostro
# Docker Hub (Docker Compose)
docker compose -f ~/mostro-docker/compose.yml exec mostro mostrod --version
docker compose -f ~/mostro-docker/compose.yml logs -f mostro
# Docker Build
docker compose -f /opt/mostro/docker/compose.yml exec mostro mostrod --version
docker compose -f /opt/mostro/docker/compose.yml logs -f mostro
# Native
mostrod --version
journalctl -u mostro -f
Look for the same startup messages as on the first run (Step 11 of Option A).
Rolling back
If the new version misbehaves, go back to the previous tag. The new version may already have migrated the database, and an older mostrod can refuse to start with it, so restore the backup you took before updating:
docker stop mostro
docker rm mostro
# replace YYYYMMDD with the date of the backup taken before updating
BACKUP=/root/mostro-backups/mostro.db.YYYYMMDD
cp "$BACKUP" ~/mostro-config/mostro.db
rm -f ~/mostro-config/mostro.db-wal ~/mostro-config/mostro.db-shm
chown 1000:1000 ~/mostro-config/mostro.db
# then start the previous tag: same docker run command,
# or put the old tag back in compose.yml and run docker compose up -d
Docker Build and native work the same way: check out the previous tag, rebuild, and restore the database before starting.
Updating Mostro is safe whenever you like. Pointing it at a different Lightning node is not: drain the escrow first, see 5.8.
5.6 Backups
Critical files to back up: settings.toml, the .env file if you keep your nsec there (see 4.1) or your reputation export key (4.13), and mostro.db (order history, reputation). If you enable payer history (4.11), keep the database backups apart from the nsec: the history is keyed by it.
SQLite runs in WAL mode, so recent writes live in mostro.db-wal until they are checkpointed. Copying only mostro.db while Mostro is running can produce a backup that is missing the newest trades. Use SQLite's own backup command, which is safe on a live database and writes a single consistent file.
# Manual backup — Docker Hub:
mkdir -p /root/mostro-backups
sqlite3 ~/mostro-config/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +%Y%m%d)'"
cp ~/mostro-config/settings.toml /root/mostro-backups/settings.toml.$(date +%Y%m%d)
cp ~/mostro-config/.env /root/mostro-backups/env.$(date +%Y%m%d) 2>/dev/null
# Manual backup — Docker Build (Option B):
sqlite3 /opt/mostro/docker/config/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +%Y%m%d)'"
cp /opt/mostro/docker/config/settings.toml /root/mostro-backups/settings.toml.$(date +%Y%m%d)
cp /opt/mostro/docker/config/.env /root/mostro-backups/env.$(date +%Y%m%d) 2>/dev/null
# Manual backup — Native:
sqlite3 /opt/mostro/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +%Y%m%d)'"
cp /opt/mostro/settings.toml /root/mostro-backups/settings.toml.$(date +%Y%m%d)
cp /opt/mostro/.env /root/mostro-backups/env.$(date +%Y%m%d) 2>/dev/null
Automated daily backup (add to crontab with crontab -e):
# Docker Hub (Option A):
0 3 * * * mkdir -p /root/mostro-backups && sqlite3 /root/mostro-config/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +\%Y\%m\%d)'" && cp /root/mostro-config/settings.toml /root/mostro-backups/settings.toml.$(date +\%Y\%m\%d); cp /root/mostro-config/.env /root/mostro-backups/env.$(date +\%Y\%m\%d) 2>/dev/null
# Docker Build (Option B):
0 3 * * * mkdir -p /root/mostro-backups && sqlite3 /opt/mostro/docker/config/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +\%Y\%m\%d)'" && cp /opt/mostro/docker/config/settings.toml /root/mostro-backups/settings.toml.$(date +\%Y\%m\%d); cp /opt/mostro/docker/config/.env /root/mostro-backups/env.$(date +\%Y\%m\%d) 2>/dev/null
# Native (Option C):
0 3 * * * mkdir -p /root/mostro-backups && sqlite3 /opt/mostro/mostro.db ".backup '/root/mostro-backups/mostro.db.$(date +\%Y\%m\%d)'" && cp /opt/mostro/settings.toml /root/mostro-backups/settings.toml.$(date +\%Y\%m\%d); cp /opt/mostro/.env /root/mostro-backups/env.$(date +\%Y\%m\%d) 2>/dev/null
Your nsec_privkey in settings.toml IS your node's identity. If you lose it, you lose your reputation and all users must reconnect to a new identity. Keep an offline copy.
If you run your own LND
Mostro's backups do not cover your Lightning node. The funds in your channels depend on two LND backups:
- The wallet seed (24 words, plus the passphrase if you set one), shown once when the wallet is created. Write it down offline.
- The static channel backup,
channel.backup(for example~/.lnd/data/chain/bitcoin/mainnet/channel.backup). LND rewrites it every time a channel opens or closes, so copy it off the machine regularly, for example in the same cron job as Mostro's backups. With the seed and this file you can close your channels and recover the funds on a new node.
Never back up or restore channel.db as a way to recover channels: an outdated copy makes your node broadcast old states, and the peers can take all the funds of those channels. Node-in-a-box systems and Voltage have their own backup features; turn them on. LND's documentation covers recovery in detail (recovery planning).
5.7 Reviewing Trade Activity
# Count all orders
sqlite3 /path/to/mostro.db "SELECT COUNT(*) FROM orders;"
# Recent successful trades
sqlite3 /path/to/mostro.db "SELECT id, fiat_code, fiat_amount, amount, fee, status, created_at FROM orders WHERE status = 'success' ORDER BY created_at DESC LIMIT 10;"
# Pending orders
sqlite3 /path/to/mostro.db "SELECT id, fiat_code, fiat_amount, status, created_at FROM orders WHERE status = 'pending';"
# Fee revenue: orders.fee stores each party's half, so the node's gross fee is fee*2 and the dev fee is deducted from it
sqlite3 /path/to/mostro.db "SELECT SUM(fee*2) AS gross_fees, SUM(dev_fee) AS dev_fees, SUM(fee*2 - COALESCE(dev_fee, 0)) AS net_fees FROM orders WHERE status = 'success';" 5.8 Maintenance Mode and Changing Your Lightning Node
Hold invoices, bonds and in-flight payouts belong to the Lightning node that created them. Pointing Mostro at a different node while any of that is open would strand those trades, so the daemon refuses to start when it sees a new LND identity with escrow still bound to the old one:
REFUSING TO START: Lightning node changed from ... but escrow is still bound to the old node
Maintenance mode is how you drain first. While it is on, new orders and takes are rejected and open trades keep working, so escrow can settle. It needs the RPC interface enabled (see 4.7), and the call is accepted from loopback only: run it on the node host, or through ssh -N -L 50051:127.0.0.1:50051 user@node.
Mostro can also enter maintenance on its own, with the reason lnd_unsynced, when LND falls out of sync (5.4). If you turn maintenance on while that is active, it becomes yours and the daemon will not lift it; you cannot use lnd_unsynced as your own reason.
- Announce the window to your users well ahead of time.
- Turn maintenance mode on:
mostro-cli admsetmaintenance -e true -r "LN node migration". - Poll
mostro-cli admmaintenancestatusuntil it reportsdrained = true. Pending orders expire on their own; to shorten the drain you can cancel one withmostro-cli admcancelpending -o <order-id>, which releases the maker's bond immediately. Announce that first, it is the user's order. Close long-running disputes as usual. - Keep the old node online the whole time. It still has to finish in-flight payouts.
- Stop Mostro and back up
mostro.db. - Point
[lightning]at the new node and leaveallow_node_change = false. - Start Mostro. It records the new pubkey. Turn maintenance mode off and place a test order.
- Only now decommission the old node.
Set it to true only for disaster recovery, when the old node is gone for good. It knowingly leaves the affected trades unresolved. Moving the same node to a new host is not a node change and needs none of this.
5.9 Operator Commands with mostro-cli
Mostrix is the comfortable way to work disputes, and mostro-cli covers the basics from a shell, plus a few operator commands Mostrix does not have. Dispute commands are signed with a Nostr key you pass as ADMIN_NSEC, which must be the daemon's own key or a registered solver's. The CLI reads no config file: set your node and relays in the environment, and put global flags before the subcommand.
# Dispute work (over Nostr)
export MOSTRO_PUBKEY=npub1... # your node
export RELAYS=wss://relay.mostro.network
export ADMIN_NSEC=nsec1...
mostro-cli listdisputes
mostro-cli admtakedispute -d <dispute-id> # prints buyer and seller pubkeys
mostro-cli getadmindm -s 120
mostro-cli admsenddm -p <party-pubkey> -m "message to a party"
mostro-cli admsettle -o <order-id> [--slashseller] [--slashbuyer] # release to the buyer
mostro-cli admcancel -o <order-id> [--slashseller] [--slashbuyer] # refund the seller
# Register an arbiter, optionally read-only
mostro-cli admaddsolver -n npub1...:read
The slash flags only matter with anti-abuse bonds (4.11). Note the spelling: --slashseller, not --slash-seller. admsenddm sends a direct message from the admin key; it is not the per-dispute chat that the Mostro App, Mostrix and Bosstro use, so prefer those clients for talking to the parties.
A separate group of commands goes over the admin gRPC instead of Nostr, so they need the RPC interface enabled (see 4.7) rather than ADMIN_NSEC. MOSTRO_RPC_URL defaults to http://127.0.0.1:50051, and MOSTRO_RPC_TOKEN is needed only if you set auth_token.
export MOSTRO_RPC_URL=http://127.0.0.1:50051
export MOSTRO_RPC_TOKEN=your-auth-token
mostro-cli admsetmaintenance -e true -r "reason"
mostro-cli admmaintenancestatus
mostro-cli admcancelpending -o <order-id>
admcancelpending is worth knowing outside a migration. It cancels an order that is still pending or waiting for a bond, notifies the maker and releases every bond on it at once. Use it for an order that is clearly abandoned or mispriced, and tell the maker first — it is their order, and this is not a dispute resolution.
mostro-cli has no commands yet for reputation import and export, payer history or the health event: use grpcurl for the reputation RPCs (4.13) and the commands in 5.4 for the health event. Install it with cargo install mostro-cli (Rust 1.97 or newer) or take a binary from its releases; run mostro-cli <command> --help for every flag.
5.10 Serbero — Dispute Assistant
Serbero is an optional assistant for your node's disputes. You run it next to Mostro, with a Nostr key of its own. It does two jobs:
- Notify (always on). Every solver you list gets a direct message within seconds of a new dispute, with reminders every 15 minutes until someone takes it.
- Mediate (opt-in, pilot). Serbero takes the dispute, asks the buyer whether they paid and the seller whether the money arrived, gives them time to finish the trade themselves, and otherwise hands the case to a human solver with a summary and the full transcript.
Serbero never moves funds and never decides a dispute. Mostro registers it as a read-only solver, so it cannot settle or cancel, and any read-write solver can take a dispute over from it at any time. If Serbero is down, Mostro and your solvers work exactly as before. Keep at least one human read-write solver.
Install
Each release has binaries for Linux (x86_64, aarch64) and macOS (Apple silicon), each with a SHA-256 file. The file names carry the version:
V=$(curl -fsSL https://api.github.com/repos/MostroP2P/serbero/releases/latest | grep -m1 '"tag_name"' | cut -d'"' -f4)
A=serbero-$V-x86_64-unknown-linux-gnu # aarch64-unknown-linux-gnu on ARM64
curl -LO https://github.com/MostroP2P/serbero/releases/download/$V/$A.tar.gz
curl -LO https://github.com/MostroP2P/serbero/releases/download/$V/$A.tar.gz.sha256
shasum -a 256 -c $A.tar.gz.sha256
tar -xzf $A.tar.gz && cd $A # serbero, config.sample.toml, deploy/
With Rust installed, cargo install serbero --locked works too. A Docker image (ghcr.io/mostrop2p/serbero) and a compose file are described in the project's README.
Configure
cp config.sample.toml config.toml
nano config.toml
[serbero]
private_key_env = "SERBERO_PRIVATE_KEY" # names the secret; the key never goes in this file
db_path = "serbero.db"
[mostro]
pubkey = "YOUR_MOSTRO_PUBKEY_HEX"
relays = ["wss://relay.mostro.network", "wss://nos.lol"] # relays your node publishes to
# One entry per human solver: the hex pubkey of the key their client uses
# (Mostrix: admin_privkey). Never list Serbero's own key.
[[solvers]]
pubkey = "SOLVER_PUBKEY_HEX"
permission = "write"
[notify]
renotify_after = "15m"
[mediation]
enabled = false
Run it as a service
The project ships a hardened systemd unit. Download it from main: the copy inside the v0.2.5 archive needs systemd 251 and fails to start on Ubuntu 22.04.
sudo useradd --system --home-dir /var/lib/serbero --shell /usr/sbin/nologin serbero
sudo install -m 755 serbero /usr/local/bin/serbero
sudo install -d -m 755 /etc/serbero
sudo install -m 644 config.toml /etc/serbero/config.toml
sudo install -d -m 700 /etc/serbero/credentials
sudo sh -c 'umask 077; openssl rand -hex 32 > /etc/serbero/credentials/serbero_private_key'
curl -fsSLO https://raw.githubusercontent.com/MostroP2P/serbero/main/deploy/serbero.service
sudo install -m 644 serbero.service /etc/systemd/system/serbero.service
sudo systemctl daemon-reload && sudo systemctl enable --now serbero
journalctl -u serbero -f # note the npub it logs at startup
Back up /etc/serbero/credentials/serbero_private_key offline. It is Serbero's identity: lose it and you must register a new npub.
Register it on your node
Mostro v0.19.1 or later registers Serbero for you. Add its npub to the [mostro] block of your settings.toml and restart Mostro:
[mostro]
serbero_pubkey = "npub1..." # Serbero's key, npub or hex
At startup Mostro registers that key as a read-only solver and announces it in its info event. That announcement is how the Mostro App labels Serbero's messages and how mostro-watchdog finds it. The log confirms it: Serbero npub1... configured: registered as a read-only solver.
A bare npub added with admaddsolver becomes read-write (5.1), and Mostro then refuses to start with REFUSING TO START: serbero_pubkey ... is a solver with write permission. It also refuses a key that belongs to a user who is not a solver, and your node's own key. Give Serbero a key of its own and let serbero_pubkey register it.
Your solvers read Serbero in Mostrix
Mostrix 0.3.5 or later shows Serbero's messages in a SERBERO pane for each dispute, but only from senders the solver trusts. In each solver's ~/.mostrix/settings.toml:
trusted_dm_senders = ["npub1..."] # Serbero's npub
The pubkey of the solver's admin_privkey must be exactly the one in a [[solvers]] entry, and Mostrix must read at least one of Serbero's relays. To take a case over, press Ctrl+T on a handoff; it needs read-write permission, and Serbero stops writing at once. Bosstro (5.11) does not show Serbero's summaries yet.
Turning on mediation (pilot)
Mediation ships off. When on, Serbero takes each new dispute and introduces itself to both parties as an automated assistant, in their language (English, Spanish or Portuguese), one short question at a time. Once both have answered, it gives them 30 minutes to settle the trade themselves, then hands the case to your solvers. It never tells anyone to release or cancel.
To read the replies, Serbero asks a judge model (TypeSafe's Jev) on your own paid account. The judge receives the trade roles, the amount, currency and payment method, and the message text — never public keys. Ask the provider for zero data retention, and tell your users that dispute messages are processed by an automated service. The README explains the API key and the threshold table; set enabled = true under [mediation] and restart, and the log says mediation ready or why not.
Party messages are kind 14 events: use more than one relay, and only relays that store them, or messages sent while Serbero is offline are lost. To post Serbero's progress to your team's Telegram, register mostro-watchdog as an observer (5.3). Back up serbero.db privately: it holds the dispute conversations.
5.11 Bosstro — Solving Disputes from a Phone
Bosstro is the Android app for dispute solvers. It brings the admin side of Mostrix to a phone: a solver sees your node's pending disputes, takes one, chats with the buyer and the seller (images and PDFs included), reads the parties' own chat when one of them shares its key and, with read-write permission, pays the buyer or refunds the seller, with the bond option when your node has bonds.
It is a tool for your solvers, not for trading, and it never holds your node's key. Registering solvers still happens from Mostrix or mostro-cli (5.2).
Install and verify
Android 7.0 or later. Download the APK from GitHub Releases: arm64-v8a for almost any phone, armeabi-v7a for an old 32-bit one. Check it against the release's checksum file:
sha256sum -c SHA256SUMS.txt --ignore-missing
The checksum only proves the download is complete. The signing certificate proves who built it, and Android refuses an update signed with any other key. Compare it with AppVerifier:
foundation.mostro.bosstro
63:92:27:84:C2:E3:2A:F4:B6:03:0F:5E:2A:18:1F:74:9B:F6:95:E5:2F:1C:A8:45:7E:98:CB:05:D2:9E:B3:4B
Add a solver
- The solver opens Bosstro, taps Create new account (or Log in with an existing solver nsec) and sends you the npub. That key must be used for nothing else — never the key of a trading app, or both roles are linked in public.
- You register it, read-only to start with (5.1):
ADMIN_NSEC=nsec1... mostro-cli admaddsolver -n npub1solver...:read - The solver picks your node in Bosstro: Mostro, a community from the list, or your own pubkey and relays. Bosstro learns from your node's answers whether the key is a solver and with which permission.
Bosstro speaks protocol v2 only (4.8). Version 0.1 does not show Serbero's handoff summaries and has no push notifications yet, so solvers working with Serbero still need Mostrix for those. A read-write solver can take a dispute over from Serbero in Bosstro.
6. Cost Breakdown
Monthly Operating Costs
| Item | Monthly Cost | Notes |
|---|---|---|
| VPS (server) | $10–24 | Depends on provider and specs |
| Domain name (optional) | $1–2 | For a website/identity |
| Lightning channel on-chain fees | Variable | Channel opening/closing |
| Monthly total | $11–26 | Excluding Lightning liquidity |
One-time / Capital Costs
| Item | Cost | Notes |
|---|---|---|
| Lightning liquidity | 0.01–1.0+ BTC | Locked in channels; recovered when closing |
| Node hardware (if self-hosting) | $0–600 | Free if using VPS; $300-600 for Start9/Umbrel |
| Setup time | 4–16 hours | Depending on experience level |
Revenue Potential
| Monthly Volume | Fee (0.6%) | Dev fee (30%) | Your net income |
|---|---|---|---|
| $1,000 | ~$6 | ~$1.80 | ~$4.20 |
| $10,000 | ~$60 | ~$18 | ~$42 |
| $50,000 | ~$300 | ~$90 | ~$210 |
| $100,000 | ~$600 | ~$180 | ~$420 |
Most new nodes take months to build trading volume. Don't expect immediate profitability. The real value often comes from providing a service to your community, with fees as a bonus.
Time Commitment
| Task | Frequency | Time |
|---|---|---|
| Monitoring (check logs, status) | Daily | 5–10 min |
| Dispute resolution | As needed | 15–60 min per dispute |
| Updates | Monthly | 15–30 min |
| Liquidity management | Weekly | 15–30 min |
| Estimated weekly total | 1–3 hours |
7. Frequently Asked Questions
Do I need to be a developer to run a Mostro node?
No, but you need to be comfortable with basic command line operations (typing commands, editing text files). The Docker path (Option A) is designed to be accessible.
Can I run Mostro on a Raspberry Pi?
Many people run LND on a Raspberry Pi 4 or 5 with an SSD, through Start9, Umbrel or RaspiBlitz, and the StartOS Mostro package can run next to it. For a node your community depends on, a VPS is more reliable than a home connection and power supply. A common middle ground is LND at home and Mostro on a VPS, linked over a VPN (see 2.2).
Can I use Core Lightning (CLN) instead of LND?
No. Mostro currently only supports LND because it depends on LND's specific hold invoice implementation. Support for other implementations may come in the future.
How do users connect to my Mostro?
Users need a Mostro client — the Mostro App for Android, iOS, desktop and web, or mostro-cli or Mostrix — and your Mostro's public key (npub). In the app they go to Settings → Network → Mostro Node → Add your own node and paste your npub; nodes listed on this site appear there already. The client communicates through Nostr relays. No direct connection needed.
Can my node still serve old (protocol v1) clients?
No. Since v0.19.0 Mostro speaks protocol v2 only (4.8). Users on an old client have to update to the Mostro App or a current Mostrix or mostro-cli.
Can my users trade USDT, Liquid or Rootstock instead of fiat?
Yes, since v0.20.2: USDT, USDC, DOC, on-chain BTC, LBTC and RBTC. You enable them one by one, and your solvers need to know how to check those transfers. See 4.12.
Can users bring their reputation from lnp2pBot or another Mostro?
Yes, if you trust the source. Turn on reputation import and add lnp2pBot's key, or the key of a Mostro node you trust (4.13). You can also let your users export the reputation they earned on your node.
Can I run multiple Mostro instances?
Yes, but each one needs its own Nostr key pair, LND node (or at least separate channels/liquidity), and configuration.
Is it legal?
It depends heavily on your jurisdiction. Mostro is software for peer-to-peer trading. In some jurisdictions, operating a P2P exchange may require licenses. Check local regulations and seek legal advice.
How much bandwidth does Mostro use?
Very little — mostly small Nostr events. A few GB per month is typical even with moderate volume.
What happens if my node goes offline?
Pending orders eventually expire. Active trades with locked funds continue when you come back online. If you're offline too long, users may lose trust. Since v0.18.3 there is also an escrow deadline: if you stay down long enough for the hold invoice to approach its CLTV horizon, LND cancels it and the seller is refunded automatically. If it is your LND that falls out of sync while Mostro keeps running, payouts wait instead of failing and, after 10 minutes, the node enters maintenance on its own until LND recovers (5.4).
Can I change my Nostr key later?
You can, but you'll lose your node's identity and reputation. Users will see it as a new Mostro. Treat your key as your brand identity.
Can I lose money running a Mostro node?
Yes, it's possible: Lightning channel funds could be at risk from bugs (rare); forced channel closures during high fee periods can be costly; VPS costs are ongoing.
Is Lightning liquidity "at risk"?
Your Lightning liquidity is yours. It's not at risk from Mostro itself — hold invoices are temporary locks. However, standard Lightning Network risks apply (forced closures, stuck channels, bugs).
When will I break even?
It depends on your costs and trading volume. With $20/month in costs and 0.6% fee, you need ~$5,000/month in trades to cover costs (before the development fee). Most communities take 3–6 months to build significant volume.
Can I move Mostro to a different Lightning node?
Yes, but not by editing the config and restarting. Escrow is bound to the node that created it, so you drain it in maintenance mode first and the daemon refuses to start if you skip that. Moving the same node to a new host is not a node change and needs nothing special. See 5.8.
8. Security Considerations
Mostro is in early-stage development. While the team works hard to ensure reliability, there may be undiscovered bugs — including security bugs that could result in loss of funds. The developers are not responsible for any money loss due to software bugs.
Mostro is open-source and its code is open for audits. We encourage communities to promote and fund independent security audits.
That said, the core escrow mechanism using Lightning hold invoices has been battle-tested since 2021, when @lnp2pBot first implemented this type of escrow. Thousands of trades have been completed successfully.
Keep Your Node's Key Out of Reach
Your nsec_privkey is your node's identity, and anyone holding it can impersonate your Mostro. Prefer supplying it through the MOSTRO_NSEC_PRIVKEY environment variable or a chmod 600 .env file over leaving it in settings.toml (see 4.1). Never put it on a laptop for dispute work either: register a separate solver key for that (see 5.2).
Operating Under Authoritarian Regimes
If you operate in a country with an authoritarian government, privacy is not optional — it's a security requirement.
- Run your Mostro node behind Tor and/or a VPN. This hides your server's IP from Nostr relays.
- If Tor/VPN is not possible (common in developing countries with slow internet), only publish events to relays you own or trust.
- Be very careful about which relays you use. In the future, governments could create Nostr relays specifically to collect IP addresses.
- Consider your Lightning node's privacy as well. Running LND behind Tor is possible and recommended in sensitive environments. Your info event publishes LND's pubkey and the addresses it advertises, so make LND advertise only Tor addresses.
- Know what your node publishes. The health event (5.4) is public: status, LND block height and network, Mostro version. Setting a
watchdog_pubkeyadds a tag that links your node to that key. Turn it off with[health] enabled = falseif that matters more than monitoring. - Proxy variables are not enough. Lightning-address lookups deliberately ignore
HTTP_PROXY/HTTPS_PROXY. To hide the server's IP, route the whole machine through Tor or a VPN.
The beauty of Mostro being decentralized is that even if one node is shut down, others keep running. But prevention is always better than recovery. Take privacy seriously from day one.
9. Troubleshooting
Mostro won't start
dev_fee_percentage (0.05) is below minimum (0.1)
Set dev_fee_percentage to at least 0.10 in settings.toml.
Configuration file or database not found
Make sure the -d flag points to the directory containing settings.toml. For Docker Hub: verify that ~/mostro-config/settings.toml exists.
Mostro exits at startup with Ln node error
- Verify LND is running:
lncli getinfo - Check that
lnd_grpc_hostmatches your LND address - Verify
tls.certandmostro.macaroonpaths are correct - Verify the macaroon carries the permissions from 2.2
- Docker + LND on the host: use
host.docker.internal. Option B also needs theextra_hostsmapping.
REFUSING TO START: Lightning node changed
Mostro is pointed at a different LND identity while escrow is still open on the old one. Reconnect the old node and drain it before switching. See 5.8.
transport = "gift-wrap" (protocol v1) is no longer supported
Delete the transport line from settings.toml. See 4.8.
escrow_deadline_margin_blocks (X) must be at least 18 ... or ... must be below hold_invoice_cltv_delta
Set the margin between 18 and hold_invoice_cltv_delta; the template's 24 with 144 works. See 4.9.
unknown field mint_url
In [cashu], rename it to mint_urls = ["..."]. See 4.11.
anti_abuse_bond.bond_cltv_delta ... must be above hold_invoice_cltv_delta + 144 or ... must be at most 864
Set bond_cltv_delta explicitly within those bounds. On v0.20.2, bonds plus a hold_invoice_cltv_delta of 288 or more hit this with the default. See 4.11.
REFUSING TO START: serbero_pubkey ...
Serbero's key is already a read-write solver, belongs to a user, or is your node's own key. Give Serbero a key of its own. See 5.10.
Reputation settings stop the start
Issuer names must be unique and every key valid and listed once; an issuer key must never move to another name once it has imports. With export on, MOSTRO_REPUTATION_ISSUER_SK must be set, valid, different from your node's key and not in your own import list. See 4.13.
Clients don't see my orders, or can't message my node
Your node speaks protocol v2 only. A user on an old, protocol-v1-only client gets no answer and must update (4.8). Also check that clients and your node share at least one relay.
Payouts fail with "no route"
Check payment_cltv_limit. It must sit at least 576 blocks above max_final_cltv_expiry_delta, and must not exceed your LND's --max-cltv-expiry. See 4.9.
Connection problems
Mostro starts but doesn't connect to relays
- Check relay URLs (must start with
wss://) - Make sure your VPS firewall allows outbound connections on port 443
- Try different relays — some may be temporarily down
Trade problems
A user gets "cant-do: too_many_requests" when restoring session
This happens when the user has more orders (historical + active) than the max_orders_per_response value in your configuration. The client tries to query all their orders at once and Mostro rejects it. This is not a ban or a temporary block — it will keep happening until you adjust the value.
# In settings.toml, increase the limit:
max_orders_per_response = 50 # default is 10, maximum 255
The value is stored as a single byte, so 255 is the ceiling. If a user has more orders than that, they need to prune their history rather than you raising the limit further.
Orders don't appear in clients
- Check relay connections in the logs
- Make sure clients use the same relays as your node
Payments failing
- Check liquidity:
lncli listchannels - Make sure you have enough outbound capacity
- Check the
max_routing_feesetting
Payouts are not going out, but there is no error
Check whether LND is synced: lncli getinfo. While it is not, Mostro holds payouts on purpose and resends them once LND recovers (5.4).
Users get "maintenance" refusals although I never enabled it
mostro-cli admmaintenancestatus shows the reason. lnd_unsynced means Mostro turned it on because LND fell out of sync; it lifts it by itself 5 minutes after LND is synced again (5.4).
Dev fees are never paid
On testnet, signet or regtest that is expected: dev fees are paid on mainnet only (4.2).
Market orders in a currency are refused
Your node has no rate for it. Check the price: warnings in the startup log: a [price] block with no enabled market source, or a stablecoin quoted but not listed in fiat_currencies_accepted, are the usual causes. See 4.10 and 4.12.
Database problems
Database locked errors
ps aux | grep mostrod
# If multiple processes, kill the extras:
kill <PID>
Getting help
- Check the logs first — most errors explain what went wrong
- Telegram (Developers): @mostro_dev
- Telegram (Community): @MostroP2P
- GitHub Issues: github.com/MostroP2P/mostro/issues
- DeepWiki: deepwiki.com/MostroP2P/mostro
When asking for help, always include: your Mostro version, relevant log output, and what you've already tried.
Appendix: Quick Reference
Important File Locations
| File | Docker Hub | Native |
|---|---|---|
| Configuration | ~/mostro-config/settings.toml | /opt/mostro/settings.toml |
| Database | ~/mostro-config/mostro.db | /opt/mostro/mostro.db |
| LND Cert | ~/mostro-config/lnd/tls.cert | Varies (check LND config) |
| LND Macaroon | ~/mostro-config/lnd/mostro.macaroon | Varies (check LND config) |
| Service | N/A | /etc/systemd/system/mostro.service |
| Logs | docker logs -f mostro | journalctl -u mostro |
Essential Commands
# Docker
docker logs -f mostro # View logs
docker restart mostro # Restart
docker stop mostro # Stop
# Native (systemd)
systemctl start mostro # Start
systemctl stop mostro # Stop
systemctl restart mostro # Restart
systemctl status mostro # View status
journalctl -u mostro -f # View logs
# Database
sqlite3 mostro.db "SELECT COUNT(*) FROM orders;" # Total orders
sqlite3 mostro.db "SELECT COUNT(*) FROM orders WHERE status='success';" # Successful trades
sqlite3 mostro.db "SELECT SUM(fee*2 - COALESCE(dev_fee, 0)) FROM orders WHERE status='success';" # Net fees kept by the node
Recommended Configuration for New Nodes
[mostro]
fee = 0.006
max_order_amount = 500000
min_payment_amount = 1000
expiration_hours = 24
expiration_seconds = 900
pow = 0
dev_fee_percentage = 0.30
fiat_currencies_accepted = ['USD'] # Change to your local currency
[nostr]
relays = [
'wss://relay.mostro.network',
'wss://nos.lol',
'wss://relay.nostr.band'
] This guide is maintained by the Mostro community. Found an error or want to improve it?
Contributions are welcome at github.com/MostroP2P/community
Last updated: October 2026 · Mostro v0.20.2