JoinMarket Orderbook Watcher
A clean, performant, and secure orderbook watcher for JoinMarket that aggregates offers from multiple directory nodes via Tor.
Features
- Tor Integration: Connects to directory nodes via Tor for privacy
- Multi-Directory Aggregation: Fetches and combines orderbooks from multiple directory nodes
- Web Interface: Clean, modern UI with real-time updates
- Advanced Filtering: Filter by offer type, directory node, and counterparty
- Directory Statistics: See offer counts per directory node
- Mempool.space Integration: Validates fidelity bonds using mempool.space API
- Docker Support: Easy deployment with Docker Compose
Architecture
The orderbook watcher follows the clean architecture principles of this repository:
- jmcore/models.py: Core data models (Offer, FidelityBond, OrderBook)
- jmcore/network.py: Tor connection support
- jmcore/mempool_api.py: Mempool.space API client
- orderbook_watcher/: Application-specific code
- directory_client.py: Connects to directory nodes
- aggregator.py: Aggregates orderbooks from multiple nodes
- server.py: HTTP server for API and static files
Quick Start
Using Docker Compose (Recommended)
-
Copy the environment file:
cd orderbook_watcher cp .env.example .env -
Edit
.envand configure your directory nodes:DIRECTORY_NODES=nakamotourflxwjnjpnrk7yc2nhkf6r62ed4gdfxmmn5f4saw5q5qoyd.onion:5222 -
Start the services:
docker-compose up -d -
Access the web interface at http://localhost:8000
Note: The
tor/conf/torrcfile must be manually created with the following content:SocksPort 0.0.0.0:9050 ControlPort 0.0.0.0:9051 CookieAuthentication 1 DataDirectory /var/lib/tor Log notice stdout
Manual Installation
See Installation for general installation instructions and Tor setup.
For orderbook watcher (manual installation):
-
Install dependencies:
cd joinmarket-ng source jmvenv/bin/activate # If you used install.sh # OR create venv: python3 -m venv jmvenv && source jmvenv/bin/activate cd jmcore pip install -e . cd ../orderbook_watcher pip install -r requirements.txt -
Make sure Tor is running on port 9050 (see Installation - Tor Setup)
-
Set environment variables:
export NETWORK__NETWORK=mainnet export NETWORK__DIRECTORY_SERVERS='["node1.onion:5222", "node2.onion:5222"]' export TOR__SOCKS_HOST=127.0.0.1 export TOR__SOCKS_PORT=9050 export ORDERBOOK_WATCHER__MEMPOOL_API_URL=https://mempool.sgn.space/api export ORDERBOOK_WATCHER__HTTP_HOST=0.0.0.0 export ORDERBOOK_WATCHER__HTTP_PORT=8000 -
Run the watcher:
python -m orderbook_watcher.main
Configuration
All configuration is done via environment variables or config file (~/.joinmarket-ng/config.toml):
| Variable | Description | Default |
|---|---|---|
NETWORK__NETWORK |
Bitcoin network (mainnet/testnet/signet/regtest) | mainnet |
NETWORK__DIRECTORY_SERVERS |
JSON array of directory servers (e.g., ["host1:port1", "host2:port2"]) |
(required) |
TOR__SOCKS_HOST |
Tor SOCKS proxy host | 127.0.0.1 |
TOR__SOCKS_PORT |
Tor SOCKS proxy port | 9050 |
ORDERBOOK_WATCHER__MEMPOOL_API_URL |
Mempool.space API base URL | http://mempopwcaqoi7z5xj5zplfdwk5bgzyl3hemx725d4a3agado6xtk3kqd.onion/api |
ORDERBOOK_WATCHER__MEMPOOL_WEB_URL |
Base URL for transaction links (optional) | https://mempool.sgn.space |
ORDERBOOK_WATCHER__MEMPOOL_WEB_ONION_URL |
Onion base URL for transaction links (optional) | http://mempopwcaqoi7z5xj5zplfdwk5bgzyl3hemx725d4a3agado6xtk3kqd.onion |
ORDERBOOK_WATCHER__HTTP_HOST |
HTTP server bind address | 0.0.0.0 |
ORDERBOOK_WATCHER__HTTP_PORT |
HTTP server port | 8000 |
ORDERBOOK_WATCHER__UPDATE_INTERVAL |
Orderbook update interval in seconds | 60 |
LOGGING__LEVEL |
Logging level (TRACE/DEBUG/INFO/WARNING/ERROR) | INFO |
NETWORK__MAX_MESSAGE_SIZE |
Maximum message size in bytes | 2097152 |
NETWORK__CONNECTION_TIMEOUT |
Connection timeout in seconds | 30.0 |
Exposing as a Tor Hidden Service
You can expose the orderbook watcher as a Tor hidden service using the existing Tor container.
- Update your
tor/conf/torrcfile:
SocksPort 0.0.0.0:9050
ControlPort 0.0.0.0:9051
CookieAuthentication 1
DataDirectory /var/lib/tor
SafeLogging 0
Log notice stdout
# hidden service
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServiceVersion 3
HiddenServicePort 80 orderbook_watcher:8000
-
Restart the Tor container:
docker-compose restart tor -
Get your onion address:
cat tor/data/hidden_service/hostname -
(Optional) Configure onion-friendly links: If you want the web interface to use onion links for Mempool.space when visited via Tor, add this to your
.env:MEMPOOL_WEB_ONION_URL=http://mempopwcaqoi7z5xj5zplfdwk5bgzyl3hemx725d4a3agado6xtk3kqd.onion
API Endpoints
GET /
Web interface for viewing the orderbook
GET /orderbook.json
Returns the aggregated orderbook in JSON format:
{
"timestamp": "2025-11-16T12:00:00.000000",
"offers": [
{
"counterparty": "J5maker",
"oid": 0,
"ordertype": "sw0reloffer",
"minsize": 100000,
"maxsize": 10000000,
"txfee": 1000,
"cjfee": "0.0002",
"fidelity_bond_value": 5000000,
"directory_node": "node1.onion:5222"
}
],
"fidelitybonds": [...],
"directory_nodes": ["node1.onion:5222", "node2.onion:5222"],
"directory_stats": {
"node1.onion:5222": {"offer_count": 10},
"node2.onion:5222": {"offer_count": 8}
}
}
GET /health
Health check endpoint