WoT Relay is a high-performance Nostr relay that saves all the notes from people you follow and their extended network (Web of Trust). Built on the Khatru framework with production-ready features including health monitoring, structured logging, and anonymous access via Tor.
- Web of Trust: Automatically discovers and archives notes from your extended network
- Performance Optimized: Multi-threaded processing, batch operations, and memory monitoring
- Production Ready: Health checks, structured logging, metrics, and graceful shutdown
- Anonymous Access: Built-in Tor proxy for privacy-preserving access
- Docker Support: Complete containerization with production deployment
- Auto-healing: Automatic container health monitoring and restart
- Configurable: Extensive environment variable configuration
Don't want to run the relay, just want to connect to some? Here are some available relays:
- wss://wot.utxo.one
- wss://nostrelites.org
- wss://wot.nostr.party
- wss://wot.sovbit.host
- wss://wot.girino.org
- wss://relay.lnau.net
- wss://wot.siamstr.com
- wss://relay.lexingtonbitcoin.org
- wss://wot.azzamo.net
- wss://wot.swarmstr.com
- wss://zap.watch
- wss://satsage.xyz
- wss://wons.calva.dev
- wss://wot.zacoos.com
- wss://wot.shaving.kiwi
- wss://wot.tealeaf.dev
- wss://wot.nostr.net
- wss://relay.goodmorningbitcoin.com
- wss://wot.sudocarlos.com
- Prerequisites
- Quick Start
- Configuration
- Running the Relay
- Monitoring
- Troubleshooting
- Architecture
- License
- Docker and Docker Compose
- At least 2GB RAM and 1 CPU core
- Domain name pointing to your server (for SSL)
- Go 1.21+: Download from golang.org
- Build Essentials: On Linux, install with
sudo apt install build-essential
# Clone the repository
git clone https://github.com/bitvora/wot-relay.git
cd wot-relay
# Copy environment template
cp example.env .env
# Edit .env with your configuration
nano .env
# Start with Docker Compose
docker compose up --build -d
# Production deployment (includes Tor proxy)
docker-compose -f docker-compose.prod.yml up -d
# With Nginx reverse proxy
docker-compose -f docker-compose.prod.yml --profile with-nginx up -d
Production Features:
- Multi-stage optimized Docker build
- Auto-healing container monitoring
- Anonymous access via Tor proxy
- Health checks and structured logging
- Resource limits and restart policies
Tor Hidden Service
# Start with Tor hidden service
docker compose -f docker-compose.tor.yml up --build -d
# Find your onion address
cat tor/data/relay/hostname
git clone https://github.com/bitvora/wot-relay.git
cd wot-relay
# Copy the example environment file
cp example.env .env
# Edit with your settings
nano .env
See Configuration section for details.
# Build with version information
go build -ldflags "-X main.version=$(git describe --tags --always)"
./wot-relay
The relay will be available at http://localhost:3334
All configuration is done via environment variables in the .env
file.
# Relay Information (REQUIRED)
RELAY_NAME="YourRelayName"
RELAY_PUBKEY="your_64_character_hex_pubkey" # Convert npub to hex at https://nostrcheck.me/converter/
RELAY_DESCRIPTION="Your relay description"
RELAY_URL="wss://your-domain.com"
RELAY_CONTACT="your_contact_pubkey"
# Paths
DB_PATH="/app/db/relay.db" # Database location
INDEX_PATH="/app/templates/index.html" # Web interface
STATIC_PATH="/app/templates/static" # Static assets
# Web of Trust Settings
REFRESH_INTERVAL_HOURS=3 # How often to refresh WoT (default: 3 hours)
MINIMUM_FOLLOWERS=1 # Minimum followers to be included in WoT
WOT_DEPTH=2 # Network depth (1 or 2, default: 2)
# Worker Configuration
# WORKER_COUNT=auto # Default: 2 per CPU core (auto-detected)
WORKER_MULTIPLIER=50 # Multiplier for worker count
# Network Configuration
SEED_RELAYS=wss://relay.primal.net,wss://relay.damus.io,wss://nos.lol
IGNORE_FOLLOWS_LIST= # Comma-separated pubkeys to ignore
# Archiving Settings
ARCHIVAL_SYNC=TRUE # Archive notes from WoT (recommended: TRUE)
ARCHIVE_REACTIONS=FALSE # Archive reactions (not recommended unless needed)
ARCHIVE_MAX_DAYS=15 # Maximum age of notes to archive (0 = all)
MAX_AGE_DAYS=0 # Maximum age for stored events (0 = keep all)
PROFILE_MAX_AGE_DAYS=30 # How old profiles can be before refresh
# Logging
LOG_LEVEL=INFO # DEBUG, INFO, WARN, ERROR
Control the maximum complexity of queries allowed (all optional, default: 50):
# Database Query Limits
QUERY_IDS_LIMIT=50 # Max event IDs in a filter
QUERY_AUTHORS_LIMIT=50 # Max authors (pubkeys) in a filter
QUERY_KINDS_LIMIT=50 # Max event kinds in a filter
QUERY_TAGS_LIMIT=50 # Max total tag values across all tags
Recommendations:
- Default (50): Good for most relays
- High-performance: 100/100/50/100
- Resource-constrained: 25/25/25/25
- Open data: 200/200/50/100
Detects and blocks automated scrapers (disabled by default):
ENABLE_ANTI_SYNC_BOTS_FILTER=FALSE # TRUE to enable (requires auth + WoT for bulk queries)
When enabled:
- Large queries require authentication
- Only WoT members can run bulk queries
- Helps prevent data scraping
Restrict access to moderation/report events (disabled by default):
ENABLE_KIND1984_FILTER=FALSE # TRUE to enable (requires auth + WoT)
When enabled:
- Kind 1984 queries require authentication
- Only WoT members can query reports
- Blocks expensive queries with both authors and tags
Performance monitoring layer (disabled by default for maximum speed):
ENABLE_PROFILING=FALSE # TRUE to enable detailed performance metrics
When enabled:
- Tracks operation statistics (calls, duration, averages)
- Semaphore-based concurrency control
- Slow query detection and warnings
- Exposed via
/stats
endpoint
When disabled (default):
- Maximum performance (no profiling overhead)
- Direct database access
- Lower memory usage
- Still provides connection stats
Recommendation: Enable during initial deployment for monitoring, then disable for production.
AUTOHEAL_INTERVAL=5 # Health check interval (seconds)
AUTOHEAL_START_PERIOD=0 # Grace period before checks start
AUTOHEAL_DEFAULT_STOP_TIMEOUT=10 # Container stop timeout
For production environments, use the optimized Docker setup:
# Standard production (includes Tor)
docker-compose -f docker-compose.prod.yml up -d
# With Nginx SSL termination
docker-compose -f docker-compose.prod.yml --profile with-nginx up -d
# View logs
docker-compose -f docker-compose.prod.yml logs -f
# Restart services
docker-compose -f docker-compose.prod.yml restart
# Stop services
docker-compose -f docker-compose.prod.yml down
To run the relay as a system service:
sudo nano /etc/systemd/system/wot-relay.service
[Unit]
Description=WoT Relay Service
After=network.target
[Service]
ExecStart=/home/ubuntu/wot-relay/wot-relay
WorkingDirectory=/home/ubuntu/wot-relay
Restart=always
MemoryLimit=2G
[Install]
WantedBy=multi-user.target
# Reload systemd
sudo systemctl daemon-reload
# Start the service
sudo systemctl start wot-relay
# Enable on boot
sudo systemctl enable wot-relay
# Check status
sudo systemctl status wot-relay
# View logs
sudo journalctl -u wot-relay -f
If you encounter database permission issues:
sudo chmod -R 777 /path/to/db
Serve the relay over HTTP/HTTPS with Nginx:
sudo nano /etc/nginx/sites-available/wot-relay
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3334;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# Create symbolic link
sudo ln -s /etc/nginx/sites-available/wot-relay /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginx
Add HTTPS support with Let's Encrypt:
# Install Certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
# Generate certificate
sudo certbot --nginx -d yourdomain.com
# Test automatic renewal
sudo certbot renew --dry-run
Certbot will automatically configure Nginx for HTTPS.
Tor Hidden Service
Run as a Tor hidden service for anonymous access:
# Start with Docker
docker compose -f docker-compose.tor.yml up --build -d
# Find your onion address
cat tor/data/relay/hostname
Your relay will be accessible at http://[your-onion-address].onion
curl http://localhost:3334/health
Response:
{
"status": "healthy",
"timestamp": 1691234567,
"uptime": 3600.5
}
curl http://localhost:3334/stats
Response includes:
{
"relay": {
"name": "WoT Relay",
"pubkey": "abc123...",
"description": "Web of Trust Relay",
"version": "1.1.0"
},
"uptime": {
"start_time": "2023-08-04T10:00:00Z",
"duration": 3600.5
},
"network": {
"size": 15000,
"last_wot_refresh": "2023-08-04T11:00:00Z",
"last_profile_refresh": "2023-08-04T11:05:00Z",
"last_archiving": "2023-08-04T11:10:00Z"
},
"events": {
"total": 50000,
"trusted": 45000,
"untrusted": 5000,
"processing_queue": 0
},
"system": {
"goroutines": 150,
"memory_mb": 256,
"gc_runs": 10,
"active_connections": 25
},
"database": {
"profiling_enabled": false,
"connections": {
"open_connections": 5,
"in_use": 2,
"idle": 3
}
},
"errors": {
"count": 0,
"last_error": null,
"last_error_msg": ""
}
}
Logs are structured JSON with configurable levels (DEBUG, INFO, WARN, ERROR):
# View logs (systemd)
sudo journalctl -u wot-relay -f
# View logs (Docker)
docker-compose -f docker-compose.prod.yml logs -f wot-relay
# View logs (manual)
./wot-relay | tee relay.log
- Check logs for errors
- Verify
.env
configuration - Check resource usage:
docker stats
- Ensure database path is writable
- Increase
REFRESH_INTERVAL_HOURS
- Reduce
WOT_DEPTH
(try 1 instead of 2) - Set
ARCHIVE_REACTIONS=FALSE
- Check for memory leaks in logs
- Check
/stats
endpoint for metrics - Monitor query times (enable profiling temporarily)
- Reduce
QUERY_AUTHORS_LIMIT
andQUERY_TAGS_LIMIT
- Check network connectivity to seed relays
- Optimize database:
PRAGMA optimize;
- Check disk space:
df -h
- Verify database permissions
- Check database integrity:
sqlite3 db/relay.db "PRAGMA integrity_check;"
- Consider running
VACUUM
to reclaim space
# Clean build
rm -rf wot-relay
go clean -cache
go build -ldflags "-X main.version=$(git describe --tags --always)"
- Verify port 3334 is accessible
- Check firewall rules:
sudo ufw status
- Test WebSocket connection:
wscat -c ws://localhost:3334
- Review Nginx logs if using reverse proxy
- Main Relay: Nostr protocol implementation using Khatru framework
- Database: SQLite with custom indexes and optimizations (see
pkg/sqlite3/
) - Profiling Layer: Optional performance monitoring (see
pkg/profiling/
) - Logger: Structured logging system (see
pkg/logger/
) - Worker Pool: Multi-threaded event processing
- Health Monitor: Container orchestration support
- WAL Mode: Better concurrency
- Custom Indexes: Optimized for WoT queries
- Query Limits: Prevent expensive queries
- Connection Pooling: Efficient connection management
- Periodic Maintenance: Automatic ANALYZE and optimization
For detailed documentation on internal packages:
- pkg/logger/ - Structured logging
- pkg/sqlite3/ - SQLite backend wrapper
- pkg/profiling/ - Performance profiling
GET /
- Relay information pageGET /health
- Health check endpointGET /stats
- Detailed metrics and statisticsWS /
- Nostr WebSocket endpoint (NIP-01)
- Firewall: Only expose ports 80 and 443; do not expose port 3334 to the public internet
- SSL: Always use HTTPS in production
- Updates: Regularly update Docker images and dependencies
- Monitoring: Set up alerts for health check failures
- Backups: Regular automated database backups
- Access Control: Restrict monitoring endpoints if needed
- Rate Limiting: Built-in rate limiters protect against abuse
# Docker
docker-compose -f docker-compose.prod.yml exec wot-relay tar -czf /tmp/backup.tar.gz /app/data
docker cp $(docker-compose -f docker-compose.prod.yml ps -q wot-relay):/tmp/backup.tar.gz ./backup-$(date +%Y%m%d).tar.gz
# Manual
tar -czf backup-$(date +%Y%m%d).tar.gz db/
# Stop services
docker-compose -f docker-compose.prod.yml down
# Restore data
tar -xzf backup-YYYYMMDD.tar.gz
mv data ./data
# Start services
docker-compose -f docker-compose.prod.yml up -d
# Use automated deployment script
./deploy.sh
# Or manually
git pull
docker-compose -f docker-compose.prod.yml down
docker-compose -f docker-compose.prod.yml up -d --build
# Connect to database
sqlite3 db/relay.db
# Run optimization
PRAGMA optimize;
ANALYZE;
# Check integrity
PRAGMA integrity_check;
# Reclaim space (may take time)
VACUUM;
This project is licensed under the MIT License.
- Issues: GitHub Issues
- Documentation: Check this README and package docs
- Stats: Monitor
/health
and/stats
endpoints - Logs: Check structured logs for detailed information