Files
c-relay-pg/docs/deployment_guide.md
T

681 lines
18 KiB
Markdown

# Deployment Guide - C Nostr Relay
Complete deployment guide for the C Nostr Relay with event-based configuration system across different environments and platforms.
## Table of Contents
- [Deployment Overview](#deployment-overview)
- [Production Deployment](#production-deployment)
- [Cloud Deployments](#cloud-deployments)
- [Container Deployment](#container-deployment)
- [Reverse Proxy Setup](#reverse-proxy-setup)
- [Monitoring Setup](#monitoring-setup)
- [Security Hardening](#security-hardening)
- [Backup and Recovery](#backup-and-recovery)
## Deployment Overview
The C Nostr Relay's event-based configuration system simplifies deployment:
### Key Deployment Benefits
- **Zero Configuration**: No config files to manage or transfer
- **Self-Contained**: Single binary + auto-generated database
- **Portable**: Database contains all relay state and configuration
- **Secure**: Admin keys generated locally, never transmitted
- **Scalable**: Efficient SQLite backend with WAL mode
### Deployment Requirements
- **CPU**: 1 vCPU minimum, 2+ recommended
- **RAM**: 512MB minimum, 2GB+ recommended
- **Storage**: 100MB for binary + database growth (varies by usage)
- **Network**: Port 8888 (configurable via events)
- **OS**: Linux (recommended), macOS, Windows (WSL)
## Production Deployment
### Server Preparation
#### System Updates
```bash
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# CentOS/RHEL
sudo yum update -y
# Install required packages
sudo apt install -y build-essential git sqlite3 libsqlite3-dev \
libwebsockets-dev libssl-dev libsecp256k1-dev libcurl4-openssl-dev \
zlib1g-dev systemd
```
#### User and Directory Setup
```bash
# Create dedicated system user
sudo useradd --system --home-dir /opt/c-relay-pg --shell /bin/false c-relay-pg
# Create application directory
sudo mkdir -p /opt/c-relay-pg
sudo chown c-relay-pg:c-relay-pg /opt/c-relay-pg
```
### Build and Installation
#### Automated Installation (Recommended)
```bash
# Clone repository
git clone https://github.com/your-org/c-relay-pg.git
cd c-relay-pg
git submodule update --init --recursive
# Build
make clean && make
# Install as systemd service
sudo systemd/install-service.sh
```
#### Manual Installation
```bash
# Build relay
make clean && make
# Install binary
sudo cp build/c_relay_pg_x86 /opt/c-relay-pg/
sudo chown c-relay-pg:c-relay-pg /opt/c-relay-pg/c_relay_pg_x86
sudo chmod +x /opt/c-relay-pg/c_relay_pg_x86
# Install systemd service
sudo cp systemd/c-relay-pg.service /etc/systemd/system/
sudo systemctl daemon-reload
```
### UDP Nostr Ingress (Privileged Port Binding)
If UDP ingress is enabled on a port below 1024 (e.g., the default port 443),
the relay needs the `CAP_NET_BIND_SERVICE` capability to bind the port as a
non-root user. Add this to the `[Service]` section of the systemd unit file:
```ini
AmbientCapabilities=CAP_NET_BIND_SERVICE
```
This grants the `c-relay-pg` service user permission to bind privileged ports
without running as root. The capability is automatically inherited by the
forked relay process and does not require any changes to the binary.
**Note**: TCP and UDP port namespaces are separate. The UDP listener on port
443 coexists with an HTTPS server (nginx, caddy) on TCP port 443 without
conflict. This is the same mechanism used by QUIC/HTTP3.
### Service Management
#### Start and Enable Service
```bash
# Start the service
sudo systemctl start c-relay-pg
# Enable auto-start on boot
sudo systemctl enable c-relay-pg
# Check status
sudo systemctl status c-relay-pg
```
#### Capture Admin Keys (CRITICAL)
```bash
# View startup logs to get admin keys
sudo journalctl -u c-relay-pg --since "5 minutes ago" | grep -A 10 "IMPORTANT: SAVE THIS ADMIN PRIVATE KEY"
# Or check the full log
sudo journalctl -u c-relay-pg --no-pager | grep "Admin Private Key"
```
⚠️ **CRITICAL**: Save the admin private key immediately - it's only shown once and is needed for all configuration updates!
### Firewall Configuration
#### UFW (Ubuntu)
```bash
# Allow relay WebSocket port
sudo ufw allow 8888/tcp
# Allow UDP Nostr ingress (if enabled)
sudo ufw allow 443/udp
# Allow SSH (ensure you don't lock yourself out)
sudo ufw allow 22/tcp
# Enable firewall
sudo ufw enable
```
#### iptables
```bash
# Allow relay WebSocket port
sudo iptables -A INPUT -p tcp --dport 8888 -j ACCEPT
# Allow UDP Nostr ingress (if enabled)
sudo iptables -A INPUT -p udp --dport 443 -j ACCEPT
# Save rules (Ubuntu/Debian)
sudo iptables-save > /etc/iptables/rules.v4
```
## Cloud Deployments
### AWS EC2
#### Instance Setup
```bash
# Launch Ubuntu 22.04 LTS instance (t3.micro or larger)
# Security Group: Allow port 8888 from 0.0.0.0/0 (or restricted IPs)
# Connect via SSH
ssh -i your-key.pem ubuntu@your-instance-ip
# Use the simple deployment script
git clone https://github.com/your-org/c-relay-pg.git
cd c-relay-pg
sudo examples/deployment/simple-vps/deploy.sh
```
#### Elastic IP (Recommended)
```bash
# Associate Elastic IP to ensure consistent public IP
# Configure DNS A record to point to Elastic IP
```
#### EBS Volume for Data
```bash
# Attach EBS volume for persistent storage
sudo mkfs.ext4 /dev/xvdf
sudo mkdir /data
sudo mount /dev/xvdf /data
sudo chown c-relay-pg:c-relay-pg /data
# Update systemd service to use /data
sudo sed -i 's/WorkingDirectory=\/opt\/c-relay-pg/WorkingDirectory=\/data/' /etc/systemd/system/c-relay-pg.service
sudo systemctl daemon-reload
```
### Google Cloud Platform
#### Compute Engine Setup
```bash
# Create VM instance (e2-micro or larger)
gcloud compute instances create c-relay-pg-instance \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--machine-type=e2-micro \
--tags=nostr-relay
# Configure firewall
gcloud compute firewall-rules create allow-nostr-relay \
--allow tcp:8888 \
--source-ranges 0.0.0.0/0 \
--target-tags nostr-relay
# SSH and deploy
gcloud compute ssh c-relay-pg-instance
git clone https://github.com/your-org/c-relay-pg.git
cd c-relay-pg
sudo examples/deployment/simple-vps/deploy.sh
```
#### Persistent Disk
```bash
# Create and attach persistent disk
gcloud compute disks create relay-data --size=50GB
gcloud compute instances attach-disk c-relay-pg-instance --disk=relay-data
# Format and mount
sudo mkfs.ext4 /dev/sdb
sudo mkdir /data
sudo mount /dev/sdb /data
sudo chown c-relay-pg:c-relay-pg /data
```
### DigitalOcean
#### Droplet Creation
```bash
# Create Ubuntu 22.04 droplet (Basic plan, $6/month minimum)
# Enable monitoring and backups
# SSH into droplet
ssh root@your-droplet-ip
# Deploy relay
git clone https://github.com/your-org/c-relay-pg.git
cd c-relay-pg
examples/deployment/simple-vps/deploy.sh
```
#### Block Storage
```bash
# Attach block storage volume
# Format and mount as /data
sudo mkfs.ext4 /dev/sda
sudo mkdir /data
sudo mount /dev/sda /data
echo '/dev/sda /data ext4 defaults,nofail,discard 0 2' >> /etc/fstab
```
## Automated Deployment Examples
The `examples/deployment/` directory contains ready-to-use scripts:
### Simple VPS Deployment
```bash
# Clone repository and run automated deployment
git clone https://github.com/your-org/c-relay-pg.git
cd c-relay-pg
sudo examples/deployment/simple-vps/deploy.sh
```
### SSL Proxy Setup
```bash
# Set up nginx reverse proxy with SSL
sudo examples/deployment/nginx-proxy/setup-ssl-proxy.sh \
-d relay.example.com -e admin@example.com
```
### Monitoring Setup
```bash
# Set up continuous monitoring
sudo examples/deployment/monitoring/monitor-relay.sh \
-c -i 60 -e admin@example.com
```
### Backup Setup
```bash
# Set up automated backups
sudo examples/deployment/backup/backup-relay.sh \
-s my-backup-bucket -e admin@example.com
```
## Reverse Proxy Setup
### Nginx Configuration
#### Basic WebSocket Proxy
```nginx
# /etc/nginx/sites-available/nostr-relay
server {
listen 80;
server_name relay.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
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;
# WebSocket timeouts
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
```
#### HTTPS with Let's Encrypt
```bash
# Install certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d relay.yourdomain.com
# Auto-renewal (crontab)
echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -
```
#### Enhanced HTTPS Configuration
```nginx
server {
listen 443 ssl http2;
server_name relay.yourdomain.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/relay.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/relay.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
# Rate limiting (optional)
limit_req_zone $remote_addr zone=relay:10m rate=10r/s;
limit_req zone=relay burst=20 nodelay;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
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;
# WebSocket timeouts
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# Buffer settings
proxy_buffering off;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name relay.yourdomain.com;
return 301 https://$server_name$request_uri;
}
```
#### PHP Admin UI: Public Stats vs Authenticated Admin
The relay ships with a PHP admin UI (in [`admin/`](../admin/)) that talks
directly to PostgreSQL. It is served by nginx + php-fpm and is split into two
URL paths with different access controls:
| URL | Auth | Purpose |
|----------------------------------|---------------|----------------------------------------------------|
| `https://<domain>/relay/` | None (public) | Public landing page: relay info + event-rate chart |
| `https://<domain>/relay/api/chart.php?range=day` | None (public) | Read-only ASCII chart endpoint (aggregate COUNT) |
| `https://<domain>/relay/admin/` | HTTP Basic Auth | Full admin UI: config, auth rules, IP bans, DMs, SQL query |
**Why Basic Auth is required for `/relay/admin/`:** the PHP endpoints have no
server-side authentication of their own. The `nostr_login_lite` modal in the
admin UI is a **client-side only** gate (it only toggles UI visibility in the
browser); it does not send a signed event or session token to the backend.
Without Basic Auth, anyone could hit `api/query.php` to run SELECT queries
against your database, `api/config.php` to change relay config, `api/auth.php`
to edit blacklist/whitelist rules, or `api/dm.php` to read DMs.
Only [`admin/api/chart.php`](../admin/api/chart.php) is safe to expose
publicly — it runs a fixed aggregate `COUNT(*)` query with the only user input
being the `range` selector, which is validated against a whitelist
(`hour`/`day`/`month`/`year`).
A complete, ready-to-use nginx config including this split is in
[`examples/deployment/nginx-proxy/nginx.conf`](../examples/deployment/nginx-proxy/nginx.conf).
The relevant blocks:
```nginx
# Public landing page + chart (no auth)
location /relay/ {
alias /opt/c-relay-pg/admin/public/;
index index.php;
location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; ... }
}
location ^~ /relay/api/chart.php { alias /opt/c-relay-pg/admin/api/chart.php; ... }
location ^~ /relay/assets/ { alias /opt/c-relay-pg/admin/assets/; }
# Authenticated admin UI (Basic Auth)
location /relay/admin/ {
alias /opt/c-relay-pg/admin/;
auth_basic "Relay Admin";
auth_basic_user_file /opt/c-relay-pg/admin/.htpasswd;
location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; ... }
location ^~ /relay/admin/lib/ { deny all; } # DB credentials
location ^~ /relay/admin/public/ { deny all; } # served via /relay/
}
```
Create the Basic Auth credentials on the server:
```bash
sudo htpasswd -c /opt/c-relay-pg/admin/.htpasswd <username>
sudo systemctl reload nginx
```
### Apache Configuration
#### WebSocket Proxy with mod_proxy_wstunnel
```apache
# Enable required modules
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo a2enmod ssl
# /etc/apache2/sites-available/nostr-relay.conf
<VirtualHost *:443>
ServerName relay.yourdomain.com
# SSL configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/relay.yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/relay.yourdomain.com/privkey.pem
# WebSocket proxy
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / ws://127.0.0.1:8888/
ProxyPassReverse / ws://127.0.0.1:8888/
# Fallback for HTTP requests
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:8888/$1" [P,L]
# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
</VirtualHost>
<VirtualHost *:80>
ServerName relay.yourdomain.com
Redirect permanent / https://relay.yourdomain.com/
</VirtualHost>
```
## Monitoring Setup
### System Monitoring
#### Basic Monitoring Script
```bash
#!/bin/bash
# /usr/local/bin/relay-monitor.sh
LOG_FILE="/var/log/relay-monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
# Check if relay is running
if ! pgrep -f "c_relay_pg_x86" > /dev/null; then
echo "[$DATE] ERROR: Relay process not running" >> $LOG_FILE
systemctl restart c-relay-pg
fi
# Check port availability
if ! netstat -tln | grep -q ":8888"; then
echo "[$DATE] ERROR: Port 8888 not listening" >> $LOG_FILE
fi
# Check database file
RELAY_DB=$(find /opt/c-relay-pg -name "*.nrdb" | head -1)
if [[ -n "$RELAY_DB" ]]; then
DB_SIZE=$(du -h "$RELAY_DB" | cut -f1)
echo "[$DATE] INFO: Database size: $DB_SIZE" >> $LOG_FILE
fi
# Check memory usage
MEM_USAGE=$(ps aux | grep c_relay_pg_x86 | grep -v grep | awk '{print $6}')
if [[ -n "$MEM_USAGE" ]]; then
echo "[$DATE] INFO: Memory usage: ${MEM_USAGE}KB" >> $LOG_FILE
fi
```
#### Cron Job Setup
```bash
# Add to crontab
echo "*/5 * * * * /usr/local/bin/relay-monitor.sh" | sudo crontab -
# Make script executable
sudo chmod +x /usr/local/bin/relay-monitor.sh
```
### Log Aggregation
#### Centralized Logging with rsyslog
```bash
# /etc/rsyslog.d/50-c-relay-pg.conf
if $programname == 'c-relay-pg' then /var/log/c-relay-pg.log
& stop
```
### External Monitoring
#### Prometheus Integration
```yaml
# /etc/prometheus/prometheus.yml
scrape_configs:
- job_name: 'c-relay-pg'
static_configs:
- targets: ['localhost:8888']
metrics_path: '/metrics' # If implemented
scrape_interval: 30s
```
## Security Hardening
### System Hardening
#### Service User Restrictions
```bash
# Restrict service user
sudo usermod -s /bin/false c-relay-pg
sudo usermod -d /opt/c-relay-pg c-relay-pg
# Set proper permissions
sudo chmod 700 /opt/c-relay-pg
sudo chown -R c-relay-pg:c-relay-pg /opt/c-relay-pg
```
#### File System Restrictions
```bash
# Mount data directory with appropriate options
echo "/dev/sdb /opt/c-relay-pg ext4 defaults,noexec,nosuid,nodev 0 2" >> /etc/fstab
```
### Network Security
#### Fail2Ban Configuration
```ini
# /etc/fail2ban/jail.d/c-relay-pg.conf
[c-relay-pg-dos]
enabled = true
port = 8888
filter = c-relay-pg-dos
logpath = /var/log/c-relay-pg.log
maxretry = 10
findtime = 60
bantime = 300
```
#### DDoS Protection
```bash
# iptables rate limiting
sudo iptables -A INPUT -p tcp --dport 8888 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8888 -j DROP
```
### Database Security
#### Encryption at Rest
```bash
# Use encrypted filesystem
sudo cryptsetup luksFormat /dev/sdb
sudo cryptsetup luksOpen /dev/sdb relay-data
sudo mkfs.ext4 /dev/mapper/relay-data
```
## Backup and Recovery
### Automated Backup
#### Database Backup Script
```bash
#!/bin/bash
# /usr/local/bin/backup-relay.sh
BACKUP_DIR="/backup/c-relay-pg"
DATE=$(date +%Y%m%d_%H%M%S)
RELAY_DB=$(find /opt/c-relay-pg -name "*.nrdb" | head -1)
mkdir -p "$BACKUP_DIR"
if [[ -n "$RELAY_DB" ]]; then
# SQLite backup
sqlite3 "$RELAY_DB" ".backup $BACKUP_DIR/relay_backup_$DATE.nrdb"
# Compress backup
gzip "$BACKUP_DIR/relay_backup_$DATE.nrdb"
# Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -name "relay_backup_*.nrdb.gz" -mtime +30 -delete
echo "Backup completed: relay_backup_$DATE.nrdb.gz"
else
echo "No relay database found!"
exit 1
fi
```
#### Cron Schedule
```bash
# Daily backup at 2 AM
echo "0 2 * * * /usr/local/bin/backup-relay.sh" | sudo crontab -
```
### Cloud Backup
#### AWS S3 Sync
```bash
# Install AWS CLI
sudo apt install -y awscli
# Configure AWS credentials
aws configure
# Sync backups to S3
aws s3 sync /backup/c-relay-pg/ s3://your-backup-bucket/c-relay-pg/ --delete
```
### Disaster Recovery
#### Recovery Procedures
```bash
# 1. Restore from backup
gunzip backup/relay_backup_20231201_020000.nrdb.gz
cp backup/relay_backup_20231201_020000.nrdb /opt/c-relay-pg/
# 2. Fix permissions
sudo chown c-relay-pg:c-relay-pg /opt/c-relay-pg/*.nrdb
# 3. Restart service
sudo systemctl restart c-relay-pg
# 4. Verify recovery
sudo journalctl -u c-relay-pg --since "1 minute ago"
```
---
This deployment guide provides comprehensive coverage for deploying the C Nostr Relay across various environments while taking full advantage of the event-based configuration system's simplicity and security features.