# RoboSats: a peer-to-peer bitcoin exchange built on hold invoices

> An AGPL-licensed Django and React application where a coordinator holds the lightning hold invoices that make a peer-to-peer fiat trade safe, reachable over Tor, on the web, on Android and on the desktop. The interesting parts are the bond mechanism and the coordinator trust model, and the fact that the clearnet address is labelled unsafe in the project's own README.

**RoboSats/robosats** — A simple and private bitcoin exchange

- Repository: https://github.com/RoboSats/robosats
- Website: https://learn.robosats.org
- Stars: 1,037 · Forks: 240
- Language: Python
- License: AGPL-3.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/robosats-robosats

## What the coordinator actually does

RoboSats is a simple and private way to exchange bitcoin for national currencies. It simplifies the peer-to-peer user experience and uses lightning hold invoices to minimize custody and trust requirements. The deterministically generated avatars help users stick to best privacy practices.

The trade flow is described in eleven numbered steps, and reading it tells you the whole protocol.

Alice generates an avatar, AdequateAlice01, using her private random token, and stores the token safely so she can recover that same avatar in future. She makes a new order and locks a small hold invoice to publish it, which is the maker bond. Bob wants to sell satoshis, sees the order in the book and takes it. Bob scans a small hold invoice as his taker bond, and at that point the contract is final.

Then Bob posts the traded satoshis with a hold invoice, while Alice submits her payout invoice. On a private chat Bob tells Alice how to send him fiat. Alice pays Bob, and then they confirm that the fiat has been sent and received. Bob's trade hold invoice is charged and the satoshis go to Alice. And because both parties complied with the rules, the bonds return automatically.

The last line is the failure case: the bonds would be charged, and lost, in the case of a unilateral cancellation or a cheating party, in a lost dispute.

So the escrow is not a company holding the money. It is a pair of lightning hold invoices, and the incentive to settle honestly is that the money is provably in both directions and lost by whoever breaks the rules. A hold invoice is a payment that cannot be claimed until the holder gives up the preimage, and that property is what makes the trade safe without a trusted third party.

## The federation model, and why the clearnet address is labelled unsafe

RoboSats is not a single service, it is a set of coordinators, and the README is explicit about how you reach them.

The main Tor URL is given, to be opened with Tor Browser. There are coordinator Tor URLs, hosted by coordinators, in a discussion thread on the repository. And there is a clearnet URL at unsafe.robosats.org, which is labelled not recommended.

The warning is spelled out in italics. You should always use Tor Browser and the .onion address for best privacy. The clearnet URL redirects to a third party Tor2web service, your privacy cannot be guaranteed to be respected, and you should use clearnet only to check around the app and never for trading.

That is the honest framing of what a Tor2web gateway is. It fetches the page for you, so the gateway operator can see the request and the response, and for an exchange that is the whole point of the product. The gateway makes the app browsable for a first look and unusable for the thing it exists for.

The federation aspect is what stops one coordinator being a single point of failure, and the `federation.md` file at the repository root is the document that describes it. The coordinator is the party that runs the backend and holds the hold invoices, and clients hosted by coordinators are listed separately, so a user can pick a coordinator rather than being assigned one.

The trade-off is that you have to trust the coordinator for the duration of the trade. The coordinator holds the funds in escrow and it is the party that would have to resolve a dispute, and a coordinator you have no relationship with is a stranger. The badges for the SimpleX, Matrix and Nostr chat channels exist because the private chat is where the fiat leg of the trade happens, outside the application.

## The application stack, and the pinned dependency list

The requirements file is the clearest description of what the backend is.

Django 5.2.18 is the framework, with djangorestframework 3.18.3 for the API and django-model-utils, django-redis and django-cors-headers alongside it. Channels 4.3.2 with channels-redis handles the websocket side, and celery 5.6.3 with django-celery-beat and django-celery-results handles the asynchronous work and the periodic tasks. daphne 4.2.3 and gunicorn 26.2.0 are the servers, with psycopg2 for PostgreSQL and redis 8.1.0 for the broker and the cache.

The lightning and cryptography dependencies are the interesting group. drf-spectacular with its sidecar generates the OpenAPI schema, and secp256k1 and ring 0.10.1 handle the elliptic curve and cryptographic primitives, with python-gnupg for the GPG operations the bond mechanism needs, base91 and pygeohash for the encoding work, and nostr-sdk 0.45.1 for the Nostr integration the chat badges point at.

Two dependencies suggest features the README does not spell out. shapely 2.1.2 is a geometry library, and requests with the socks extra is how the backend reaches the Tor network. numpy 2.5.2 and Pillow 12.3.0 are there for avatar generation and image handling.

The pinning is complete, with exact versions on nearly every line, which is the right choice for a security-sensitive application.

The Python tooling is configured in `pyproject.toml`, with ruff excluding the migrations and the generated gRPC protobuf files, and coverage omitting the same generated files plus the tests and the management script. The `api/lightning` path is the gRPC interface to the Lightning node, and the `*pb2*` pattern in the exclusions is the generated code from the protobuf definitions, which is why it is excluded from linting and coverage.

## The development stack, all of it inside the Tor namespace

The `docker-compose.yml` is the development environment, and one detail in it explains the whole deployment model.

The Redis service, the backend, the nodeapp and the clean-orders and follow-invoices services all set `network_mode: service:tor`. They share the network namespace of the Tor container, so that the application's outbound traffic goes through Tor and there is no route around it. The backend also has `depends_on` for bitcoind, lnd and redis, and mounts the LND and CLN node directories, so the Lightning backends are local to the stack.

The services themselves map onto the protocol. The backend is the Django application, run with `python3 -u manage.py runserver`. The frontend is the npm development server. The nodeapp is the Umbrel and Citadel app, with its own nginx configuration and its own coordinators directory, and it is the piece that would become a one-click install. There are background services for cleaning orders and for following invoices, both of which run the Django management command. And there is a jekyll service that builds the documentation.

So a local development environment is a full stack: a bitcoind, two Lightning node implementations, Redis, a Django backend, a React front end, and the node packaging, all sharing one network namespace so that nothing leaks onto the clearnet.

That last part is the point. A privacy application that has a route to the internet that bypasses its own proxy is a privacy application with a hole in it, and the compose file closes it by construction rather than by configuration. The `TOR_PROXY_IP` and `TOR_PROXY_PORT` environment variables on the nodeapp confirm the same design at the application level.

## The clients, and how the avatars work

RoboSats is available on the web through the Tor address, on Android and on the desktop.

The Android build is distributed through F-Droid as the `com.robosats` package, through Obtainium for tracking GitHub releases, and through the Zap Store. The desktop build is on GitHub releases. The README shows badges for each, and the Obtainium and Zap Store routes are the ones that give you automatic updates rather than a file you download once.

The avatar system is the detail that explains the privacy model. The trade flow starts with the user generating an avatar from a private random token, and the avatar name is deterministic in the sense that the same token always produces the same name, AdequateAlice01 in the example. The token is stored by the user in case they need to recover the avatar in future.

So a user has a consistent pseudonym across trades without the exchange ever needing a username, an email address or a password. The token is the identity, the avatar is the presentation of it, and the exchange stores the pseudonym rather than a credential. A user who loses the token loses the avatar, and a user who wants to be someone new just generates a new token.

That design is what makes the peer-to-peer model work without an account system, and it is why the README says the avatars help users stick to best privacy practices rather than presenting it as a cosmetic feature.

There is also a testnet mode, reached from the Settings page by switching to Testnet, which is how you can try the full trade flow including the bonds without moving real bitcoin.

## The documentation, and the inspiration

The project takes documentation seriously enough to have a dedicated site, learn.robosats.org, with tutorials and guides in several languages.

There are written guides in English, Spanish, German, French and Russian, and video guides in those five plus Portuguese and Polish. The video guides are the more useful of the two for a first-time user, because the trade flow is a sequence of eleven steps across a web interface, a wallet and a chat, and reading about it is less effective than watching it.

The site is also where the tutorials and the more detailed documentation live, and the README's Learn section points there rather than duplicating the content.

The Inspiration section is short and worth quoting. The concept of a simple custody-minimized lightning exchange with hold invoices is inspired by P2PLNBOT by grunch.

That naming matters for a reader evaluating the design, because it says the bond mechanism was not invented for this project. There was a prior implementation of the same idea, and RoboSats is a different thing built on the same concept, with the differentiator being the coordinator federation, the automatic avatars and the client applications rather than the underlying protocol.

The repository has a `CONTRIBUTING.md` and a separate `CONTRIBUTING_DOCS.md`, so the documentation has its own contribution process, and a `development/` directory with the developer documentation linked from the README. There is a `CHANGELOG` in the form of `release_notes.md`, a `version.json`, and a `devfund_pubkey.json` for development funding.

## Where RoboSats is the wrong choice

The first wrong case is a user who needs a guaranteed settlement. Every trade ends with the two parties confirming out of band that the fiat moved, and the bond mechanism incentivises honesty rather than enforcing it. If the buyer simply does not pay, the seller loses the satoshis and the bond, and the dispute resolution depends on the coordinator. That is a different proposition from an exchange that guarantees your fiat side.

The second is a user who cannot use Tor or a coordinator they trust. The clearnet address exists for a first look and the README says never to trade on it, so a user on a restricted network without Tor is out of options. The coordinators are community-run, which means availability is uneven and the operator of one is a stranger with your escrow.

The third is a business that needs records. There is no account, no identity and no invoice, and the trade history lives between the two parties. That is the point of the design, and it is disqualifying for a company that has to reconcile payments.

The fourth is a user who wants to be sure the software is production-hardened. The releases are alpha versions, v0.8.7-alpha and before, and the project is explicit that it is not a mainstream exchange.

The fifth is a developer who wants to read a small codebase. The repository is a Django application, a React front end, a node packaging layer, an Android app, a desktop app, a chat integration with three different networks, and a Lightning integration over gRPC, and the pieces are coupled through the coordinator protocol rather than through a clean API. That is the price of a federated peer-to-peer exchange, and it is a price worth paying only if the federation is the requirement.

## Conclusion

RoboSats suits a user who wants to buy or sell bitcoin without handing custody to a central exchange and without a KYC process, and who is willing to run a coordinator or use one run by the community. It does not suit a user who needs a guaranteed fiat settlement, since the final step of every trade is a private chat where both parties confirm the money moved. Before using it, read the federation model, decide whether you trust the coordinator you connect to, and if you run one, read the deployment documentation, because the coordinator is the party holding your funds in escrow during the trade.

## FAQ

### How does RoboSats protect a peer-to-peer trade?

With lightning hold invoices used as bonds. The maker locks a small hold invoice to publish an order, the taker locks one to take it, and both bonds return automatically if both parties comply. In a unilateral cancellation or a cheat, the bonds are charged and lost.

### Why does the RoboSats README call the clearnet address unsafe?

Because the clearnet URL redirects to a third party Tor2web service, so that operator can see the requests, and the README says your privacy cannot be guaranteed to be respected. It says to use clearnet only to look around the app and never for trading.

### What is the coordinator in RoboSats?

The service that hosts the clients and holds the lightning hold invoices during a trade. The README lists coordinator Tor URLs for clients hosted by coordinators, so a user chooses a coordinator rather than being assigned one, and the federation model is described in the repository's federation.md file.

### How do the RoboSats avatars work?

A user generates an avatar from a private random token, and the same token always produces the same avatar name, so the pseudonym is consistent across trades without an account. The user stores the token to be able to recover the avatar later.

### Which clients does RoboSats ship?

A web client reached through the Tor address, an Android app distributed through F-Droid, Obtainium and the Zap Store, and a desktop build on GitHub releases. There is also a testnet mode in the Settings page for trying the flow without real bitcoin.

## Sources

- [License: AGPL-3.0](https://github.com/RoboSats/robosats/blob/main/LICENSE)
- [Project website](https://learn.robosats.org)
- [README](https://github.com/RoboSats/robosats/blob/main/README.md)
- [Releases](https://github.com/RoboSats/robosats/releases)
- [RoboSats/robosats on GitHub](https://github.com/RoboSats/robosats)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/robosats-robosats
