JoinMarket Directory Server
Relay server for peer discovery and message routing in the JoinMarket network.
Features
- Peer Discovery: Register and discover active peers
- Message Routing: Forward public broadcasts and private messages
- Connection Management: Handle peer connections and disconnections
- Handshake Protocol: Verify peer compatibility and network
- High Performance: Async I/O with optimized message handling
- Observability: Structured logging with loguru
- Tor Hidden Service: Run behind Tor for privacy (via separate container)
Running
Docker Compose quick start (Recommended)
Use the directory_server/docker-compose.yml deployment for production-style operation
behind a Tor hidden service. The compose stack isolates the directory server on an internal
network with no direct internet access; only the Tor container bridges both internal and
external networks.
- Go to the
directory_serverdirectory. - Run
docker compose up(ordocker compose up -dfor non-interactive run)
Retrieve your .onion address:
cat tor/data/hostname
Docker Notes
Network Architecture
Internet <--> [tor container] <-- internal network --> [directory_server container]
directory_serverlives onjoinmarket_directory_internal(bridge, internal) -- no direct internet access, no information leakage.torbridgesjoinmarket_directory_internalandjoinmarket_directory_external, exposing the directory server only as a Tor hidden service.
Debug Image
A debug variant with pdbpp and memray pre-installed is available:
docker pull ghcr.io/joinmarket-ng/joinmarket-ng/directory-server:main-debug
Profile memory with memray:
docker run -it --rm \
-v $(pwd)/memray-output:/app/memray-output \
ghcr.io/joinmarket-ng/joinmarket-ng/directory-server:main-debug \
memray run -o /app/memray-output/profile.bin -m directory_server.main
To attach memray to a running container, add cap_add: [SYS_PTRACE] to the
service in docker-compose.yml, then:
docker exec -it joinmarket_directory_server \
python -m memray attach 1 --verbose
Installation without Docker
See Installation for general setup. For local development:
cd joinmarket-ng
source jmvenv/bin/activate # If you used install.sh
# OR create venv: python3 -m venv jmvenv && source jmvenv/bin/activate
# Install jmcore first
cd jmcore
pip install -e .
# Install directory server
cd ../directory_server
pip install -e .
# Development
pip install -e ".[dev]"
Configuration
Create a .env file or set environment variables:
# Network
NETWORK=mainnet # mainnet, testnet, signet, regtest
HOST=127.0.0.1
PORT=5222
# Server
MAX_PEERS=10000
MESSAGE_RATE_LIMIT=100
LOG_LEVEL=INFO
Heartbeat liveness settings (section [directory_server] in config.toml):
heartbeat_sweep_interval(default60.0)heartbeat_idle_threshold(default600.0)heartbeat_hard_evict(default1500.0)heartbeat_pong_wait(default30.0)
Behavior summary:
- Idle peers are probed with PING/PONG when they advertise
"ping": truein handshake features - Legacy/non-ping makers receive
!orderbookas a compatibility liveness probe - Peers idle beyond hard eviction threshold are disconnected
Optional
Vanity Onion Address
Generate a vanity .onion address using
mkp224o before first start.
The tool creates one output directory per matching address and runs until killed.
Five-character prefixes are fast; six characters can take hours.
docker run --rm -it --network none -v $PWD:/keys \
ghcr.io/cathugger/mkp224o:master -d /keys desired_prefix
Copy the generated keys into the Tor data directory:
mv desired_prefix*.onion/hs_ed25519_public_key \
desired_prefix*.onion/hs_ed25519_secret_key \
desired_prefix*.onion/hostname \
tor/data/
chown -R 1000:1000 tor/data/
If the stack is already running, restart Tor to pick up the new keys:
docker compose restart tor
cat tor/data/hostname
Health Check & Monitoring
The directory server provides comprehensive health check and monitoring capabilities.
Health Check Endpoint
An HTTP server runs on port 8080 (configurable via HEALTH_CHECK_HOST and HEALTH_CHECK_PORT) providing:
GET /health - Basic health check
curl http://localhost:8080/health
# {"status": "healthy"}
GET /status - Detailed server statistics
curl http://localhost:8080/status
# {
# "network": "mainnet",
# "uptime_seconds": 3600,
# "server_status": "running",
# "max_peers": 1000,
# "stats": {
# "total_peers": 150,
# "connected_peers": 150,
# "passive_peers": 45,
# "active_peers": 105
# },
# "connected_peers": {
# "total": 150,
# "nicks": ["maker1", "taker1", ...]
# },
# "passive_peers": {
# "total": 45,
# "nicks": ["taker1", "taker2", ...]
# },
# "active_peers": {
# "total": 105,
# "nicks": ["maker1", "maker2", ...]
# },
# "active_connections": 150
# }
CLI Tool
Use jm-directory-ctl to query server status:
# Check server health
jm-directory-ctl health
# Get detailed status (human-readable)
jm-directory-ctl status
# Get status as JSON
jm-directory-ctl status --json
# Query remote server
jm-directory-ctl status --host 192.168.1.10 --port 8080
Signal-based Status Logging
Send SIGUSR1 signal to trigger detailed status logging to the server logs:
# Docker
docker kill -s SIGUSR1 joinmarket_directory_server
# Local process
kill -USR1 $(pgrep jm-directory-server)
This will log comprehensive status including: - Network type and uptime - Connected peers count and list - Passive peers (orderbook watchers/takers - NOT-SERVING-ONION) - Active peers (makers - serving onion address) - Active connections
Docker Health Check
The Docker image includes automatic health checks using the CLI command:
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD ["jm-directory-ctl", "health"]
Check container health status:
docker ps # Shows (healthy) or (unhealthy)
docker inspect joinmarket_directory_server | grep -A 10 Health
Protocol and Security Context
- Message envelope types and handshake flow: Technical Protocol Notes
- Directory role in peer discovery and routing: Technical Protocol Notes
Command Reference
jm-directory-ctl --help
usage: jm-directory-ctl [-h] [--host HOST] [--port PORT]
[--log-level LOG_LEVEL]
{status,health} ...
JoinMarket Directory Server CLI
positional arguments:
{status,health} Available commands
status Get server status
health Check server health
options:
-h, --help show this help message and exit
--host HOST Health check server host (default: 127.0.0.1)
--port PORT Health check server port (default: 8080)
--log-level, -l LOG_LEVEL
Log level (default: INFO)
jm-directory-ctl status --help
usage: jm-directory-ctl status [-h] [--json]
options:
-h, --help show this help message and exit
--json Output as JSON
jm-directory-ctl health --help
usage: jm-directory-ctl health [-h] [--json]
options:
-h, --help show this help message and exit
--json Output as JSON