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

476 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Configuration Management Guide
Comprehensive guide for managing the C Nostr Relay's event-based configuration system.
## Table of Contents
- [Overview](#overview)
- [Configuration Events](#configuration-events)
- [Parameter Reference](#parameter-reference)
- [Configuration Examples](#configuration-examples)
- [Security Considerations](#security-considerations)
- [Troubleshooting](#troubleshooting)
## Overview
The C Nostr Relay uses a revolutionary **event-based configuration system** where all settings are stored as kind 33334 Nostr events in the database. This provides several advantages:
### Benefits
- **Real-time updates**: Configuration changes applied instantly without restart
- **Cryptographic security**: All changes must be cryptographically signed by admin
- **Audit trail**: Complete history of all configuration changes
- **Version control**: Each configuration change is timestamped and signed
- **Zero files**: No configuration files to manage, backup, or version control
### How It Works
1. **Admin keypair**: Generated on first startup, used to sign configuration events
2. **Configuration events**: Kind 33334 Nostr events with relay settings in tags
3. **Real-time processing**: New configuration events processed via WebSocket
4. **Immediate application**: Changes applied to running system without restart
## Configuration Events
### Event Structure
Configuration events follow the standard Nostr event format with kind 33334:
```json
{
"id": "event_id_computed_from_content",
"kind": 33334,
"pubkey": "admin_public_key_hex",
"created_at": 1699123456,
"content": "C Nostr Relay Configuration",
"tags": [
["d", "relay_public_key_hex"],
["relay_description", "My Nostr Relay"],
["max_subscriptions_per_client", "25"],
["pow_min_difficulty", "16"]
],
"sig": "signature_computed_with_admin_private_key"
}
```
### Required Tags
- **`d` tag**: Must contain the relay's public key (identifies which relay this config is for)
### Event Properties
- **Kind**: Must be exactly `33334`
- **Content**: Should be descriptive (e.g., "C Nostr Relay Configuration")
- **Pubkey**: Must be the admin public key generated at first startup
- **Signature**: Must be valid signature from admin private key
## Parameter Reference
### Basic Relay Information
#### `relay_description`
- **Description**: Human-readable relay description (shown in NIP-11)
- **Default**: `"C Nostr Relay"`
- **Format**: String, max 512 characters
- **Example**: `"My awesome Nostr relay for the community"`
#### `relay_contact`
- **Description**: Admin contact information (email, npub, etc.)
- **Default**: `""` (empty)
- **Format**: String, max 256 characters
- **Example**: `"admin@example.com"` or `"npub1..."`
#### `relay_software`
- **Description**: Software identifier for NIP-11
- **Default**: `"c-relay-pg"`
- **Format**: String, max 64 characters
- **Example**: `"c-relay-pg v1.0.0"`
#### `relay_version`
- **Description**: Software version string
- **Default**: Auto-detected from build
- **Format**: Semantic version string
- **Example**: `"1.0.0"`
### Client Connection Limits
#### `max_subscriptions_per_client`
- **Description**: Maximum subscriptions allowed per WebSocket connection
- **Default**: `"25"`
- **Range**: `1` to `100`
- **Impact**: Prevents individual clients from overwhelming the relay
- **Example**: `"50"` (allows up to 50 subscriptions per client)
#### `max_total_subscriptions`
- **Description**: Maximum total subscriptions across all clients
- **Default**: `"5000"`
- **Range**: `100` to `50000`
- **Impact**: Global limit to protect server resources
- **Example**: `"10000"` (allows up to 10,000 total subscriptions)
### Message and Event Limits
#### `max_message_length`
- **Description**: Maximum WebSocket message size in bytes
- **Default**: `"65536"` (64KB)
- **Range**: `1024` to `1048576` (1MB)
- **Impact**: Prevents large messages from consuming resources
- **Example**: `"131072"` (128KB)
#### `max_event_tags`
- **Description**: Maximum number of tags allowed per event
- **Default**: `"2000"`
- **Range**: `10` to `10000`
- **Impact**: Prevents events with excessive tags
- **Example**: `"5000"`
#### `max_content_length`
- **Description**: Maximum event content length in bytes
- **Default**: `"65536"` (64KB)
- **Range**: `1` to `1048576` (1MB)
- **Impact**: Limits event content size
- **Example**: `"131072"` (128KB for longer content)
### Proof of Work (NIP-13)
#### `pow_min_difficulty`
- **Description**: Minimum proof-of-work difficulty required for events
- **Default**: `"0"` (no PoW required)
- **Range**: `0` to `40`
- **Impact**: Higher values require more computational work from clients
- **Example**: `"20"` (requires significant PoW)
#### `pow_mode`
- **Description**: How proof-of-work is handled
- **Default**: `"optional"`
- **Values**:
- `"disabled"`: PoW completely ignored
- `"optional"`: PoW verified if present but not required
- `"required"`: All events must meet minimum difficulty
- **Example**: `"required"` (enforce PoW for all events)
### Event Expiration (NIP-40)
#### `nip40_expiration_enabled`
- **Description**: Enable NIP-40 expiration timestamp support
- **Default**: `"true"`
- **Values**: `"true"` or `"false"`
- **Impact**: When enabled, processes expiration tags and removes expired events
- **Example**: `"false"` (disable expiration processing)
#### `nip40_expiration_strict`
- **Description**: Strict mode for expiration handling
- **Default**: `"false"`
- **Values**: `"true"` or `"false"`
- **Impact**: In strict mode, expired events are immediately rejected
- **Example**: `"true"` (reject expired events immediately)
#### `nip40_expiration_filter`
- **Description**: Filter expired events from query results
- **Default**: `"true"`
- **Values**: `"true"` or `"false"`
- **Impact**: When enabled, expired events are filtered from responses
- **Example**: `"false"` (include expired events in results)
#### `nip40_expiration_grace_period`
- **Description**: Grace period in seconds before expiration takes effect
- **Default**: `"300"` (5 minutes)
- **Range**: `0` to `86400` (24 hours)
- **Impact**: Allows some flexibility in expiration timing
- **Example**: `"600"` (10 minute grace period)
### UDP Nostr Ingress (No-Handshake Event Reception)
The relay can receive Nostr events as single UDP datagrams — no TCP connection,
no WebSocket handshake, no TLS negotiation. The event carries its own Schnorr
signature, which replaces the handshake. This provides censorship resistance:
a passive observer cannot distinguish a Nostr event from random UDP noise, and
active probing cannot detect the relay (invalid events are silently dropped).
#### `udp_ingress_enabled`
- **Description**: Enable the UDP event listener
- **Default**: `"false"`
- **Values**: `"true"` or `"false"`
- **Impact**: When enabled, the relay binds a UDP socket and accepts Nostr events
as single datagrams. Events are validated through the same pipeline as WebSocket
events (signature, structure, expiration, PoW) and broadcast to subscribers.
- **Note**: This is a dynamic config key — the listener starts/stops without
restart when the value changes (via `udp_ingress_tick()` in the main loop).
#### `udp_ingress_port`
- **Description**: UDP port for the event listener
- **Default**: `"443"`
- **Range**: `1` to `65535`
- **Impact**: Ports below 1024 require `CAP_NET_BIND_SERVICE` capability (see
deployment guide). TCP and UDP port namespaces are separate — UDP 443 can
coexist with an HTTPS server on TCP 443.
- **Recommendation**: Use port 443 to blend with QUIC/HTTP3 traffic for maximum
censorship resistance. Use port 8888 for local testing.
#### `udp_ingress_bind_addr`
- **Description**: IP address to bind the UDP socket to
- **Default**: `"0.0.0.0"`
- **Format**: IPv4 address string
- **Example**: `"127.0.0.1"` (listen only on localhost)
#### `udp_ingress_max_datagram_size`
- **Description**: Maximum allowed UDP datagram size in bytes
- **Default**: `"1472"`
- **Range**: `512` to `65535`
- **Impact**: 1472 bytes is the single-packet guarantee (1500 Ethernet MTU − 20
IP header − 8 UDP header). Datagrams larger than this are silently dropped.
This ensures the event fits in a single unfragmented IP packet, which is the
foundation of the censorship-resistance property.
### NIP-59 Gift Wrap Timestamp Configuration
#### `nip59_timestamp_max_delay_sec`
- **Description**: Controls timestamp randomization for NIP-59 gift wraps
- **Default**: `"0"` (no randomization)
- **Range**: `0` to `604800` (7 days)
- **Impact**: Affects compatibility with other Nostr clients for direct messaging
- **Values**:
- `"0"`: No randomization (maximum compatibility)
- `"1-604800"`: Random timestamp between now and N seconds ago
- **Example**: `"172800"` (2 days randomization for privacy)
## Configuration Examples
### Basic Relay Setup
```json
{
"kind": 33334,
"content": "Basic Relay Configuration",
"tags": [
["d", "relay_pubkey_here"],
["relay_description", "Community Nostr Relay"],
["relay_contact", "admin@community-relay.com"],
["max_subscriptions_per_client", "30"],
["max_total_subscriptions", "8000"]
]
}
```
### High-Security Relay
```json
{
"kind": 33334,
"content": "High Security Configuration",
"tags": [
["d", "relay_pubkey_here"],
["relay_description", "High-Security Nostr Relay"],
["pow_min_difficulty", "24"],
["pow_mode", "required"],
["max_subscriptions_per_client", "10"],
["max_total_subscriptions", "1000"],
["max_message_length", "32768"],
["nip40_expiration_strict", "true"]
]
}
```
### Public Community Relay
```json
{
"kind": 33334,
"content": "Public Community Relay Configuration",
"tags": [
["d", "relay_pubkey_here"],
["relay_description", "Open Community Relay - Welcome Everyone!"],
["relay_contact", "community@relay.example"],
["max_subscriptions_per_client", "50"],
["max_total_subscriptions", "25000"],
["max_content_length", "131072"],
["pow_mode", "optional"],
["pow_min_difficulty", "8"],
["nip40_expiration_enabled", "true"],
["nip40_expiration_grace_period", "900"]
]
}
```
### Private/Corporate Relay
```json
{
"kind": 33334,
"content": "Corporate Internal Relay",
"tags": [
["d", "relay_pubkey_here"],
["relay_description", "Corporate Internal Communications"],
["relay_contact", "it-admin@company.com"],
["max_subscriptions_per_client", "20"],
["max_total_subscriptions", "2000"],
["max_message_length", "262144"],
["nip40_expiration_enabled", "false"],
["pow_mode", "disabled"]
]
}
```
## Security Considerations
### Admin Key Management
#### Secure Storage
```bash
# Store admin private key securely
echo "ADMIN_PRIVKEY=your_admin_private_key_here" > .env
chmod 600 .env
# Or use a password manager
# Never store in version control
echo ".env" >> .gitignore
```
#### Key Rotation
Currently, admin key rotation requires:
1. Stopping the relay
2. Removing the database (loses all events)
3. Restarting (generates new keys)
Future versions will support admin key rotation while preserving events.
### Event Validation
The relay performs comprehensive validation on configuration events:
#### Cryptographic Validation
- **Signature verification**: Uses `nostr_verify_event_signature()`
- **Event structure**: Validates JSON structure with `nostr_validate_event_structure()`
- **Admin authorization**: Ensures events are signed by the authorized admin pubkey
#### Content Validation
- **Parameter bounds checking**: Validates numeric ranges
- **String length limits**: Enforces maximum lengths
- **Enum validation**: Validates allowed values for mode parameters
### Network Security
#### Access Control
```bash
# Limit access with firewall
sudo ufw allow from 192.168.1.0/24 to any port 8888
# Or use specific IPs
sudo ufw allow from 203.0.113.10 to any port 8888
```
#### TLS/SSL Termination
```nginx
# nginx configuration for HTTPS termination
server {
listen 443 ssl;
server_name relay.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
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;
}
}
```
## Troubleshooting
### Configuration Not Applied
#### Check Event Signature
```javascript
// Verify event signature with nostrtool or similar
const event = { /* your configuration event */ };
const isValid = nostrTools.verifySignature(event);
```
#### Verify Admin Pubkey
```bash
# Check current admin pubkey in database
sqlite3 relay.nrdb "SELECT DISTINCT pubkey FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1;"
# Compare with expected admin pubkey from first startup
grep "Admin Public Key" relay.log
```
#### Check Event Structure
```bash
# View the exact event stored in database
sqlite3 relay.nrdb "SELECT json_pretty(json_object(
'kind', kind,
'pubkey', pubkey,
'created_at', created_at,
'content', content,
'tags', json(tags),
'sig', sig
)) FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1;"
```
### Configuration Validation Errors
#### Invalid Parameter Values
```bash
# Check relay logs for validation errors
journalctl -u c-relay-pg | grep "Configuration.*invalid\|Invalid.*configuration"
# Common issues:
# - Numeric values outside valid ranges
# - Invalid enum values (e.g., pow_mode)
# - String values exceeding length limits
```
#### Missing Required Tags
```bash
# Ensure 'd' tag is present with relay pubkey
sqlite3 relay.nrdb "SELECT tags FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1;" | grep '"d"'
```
### Performance Impact
#### Monitor Configuration Changes
```bash
# Track configuration update frequency
sqlite3 relay.nrdb "SELECT datetime(created_at, 'unixepoch') as date,
COUNT(*) as config_updates
FROM events WHERE kind = 33334
GROUP BY date(created_at, 'unixepoch')
ORDER BY date DESC;"
```
#### Resource Usage After Changes
```bash
# Monitor system resources after configuration updates
top -p $(pgrep c_relay_pg)
# Check for memory leaks
ps aux | grep c_relay_pg | awk '{print $6}' # RSS memory
```
### Emergency Recovery
#### Reset to Default Configuration
If configuration becomes corrupted or causes issues:
```bash
# Create emergency configuration event
nostrtool event \
--kind 33334 \
--content "Emergency Reset Configuration" \
--tag d YOUR_RELAY_PUBKEY \
--tag max_subscriptions_per_client 25 \
--tag max_total_subscriptions 5000 \
--tag pow_mode optional \
--tag pow_min_difficulty 0 \
--private-key YOUR_ADMIN_PRIVKEY \
| nostrtool send ws://localhost:8888
```
#### Database Recovery
```bash
# If database is corrupted, backup and recreate
cp relay.nrdb relay.nrdb.backup
rm relay.nrdb*
./build/c_relay_pg_x86 # Creates fresh database with new keys
```
---
This configuration guide covers all aspects of managing the C Nostr Relay's event-based configuration system. The system provides unprecedented flexibility and security for Nostr relay administration while maintaining simplicity and real-time responsiveness.