v0.3.0 - Complete deployment documentation and examples - Added comprehensive deployment guide, automated deployment scripts, nginx SSL proxy setup, backup automation, and monitoring tools. Includes VPS deployment, cloud platform guides, and practical examples for production deployment of event-based configuration system.

This commit is contained in:
Your Name
2025-09-06 20:19:12 -04:00
parent a02c1204ce
commit c1de1bb480
39 changed files with 6109 additions and 2452 deletions

View File

@@ -1,280 +0,0 @@
# Database Configuration Schema Design
## Overview
This document outlines the database configuration schema additions for the C Nostr Relay startup config file system. The design follows the Ginxsom admin system approach with signed Nostr events and database storage.
## Schema Version Update
- Current Version: 2
- Target Version: 3
- Update: Add server configuration management tables
## Core Configuration Tables
### 1. `server_config` Table
```sql
-- Server configuration table - core configuration storage
CREATE TABLE server_config (
key TEXT PRIMARY KEY, -- Configuration key (unique identifier)
value TEXT NOT NULL, -- Configuration value (stored as string)
description TEXT, -- Human-readable description
config_type TEXT DEFAULT 'user' CHECK (config_type IN ('system', 'user', 'runtime')),
data_type TEXT DEFAULT 'string' CHECK (data_type IN ('string', 'integer', 'boolean', 'json')),
validation_rules TEXT, -- JSON validation rules (optional)
is_sensitive INTEGER DEFAULT 0, -- 1 if value should be masked in logs
requires_restart INTEGER DEFAULT 0, -- 1 if change requires server restart
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
```
**Configuration Types:**
- `system`: Core system settings (admin keys, security)
- `user`: User-configurable settings (relay info, features)
- `runtime`: Dynamic runtime values (statistics, cache)
**Data Types:**
- `string`: Text values
- `integer`: Numeric values
- `boolean`: True/false values (stored as "true"/"false")
- `json`: JSON object/array values
### 2. `config_history` Table
```sql
-- Configuration change history table
CREATE TABLE config_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
config_key TEXT NOT NULL, -- Key that was changed
old_value TEXT, -- Previous value (NULL for new keys)
new_value TEXT NOT NULL, -- New value
changed_by TEXT DEFAULT 'system', -- Who made the change (system/admin/user)
change_reason TEXT, -- Optional reason for change
changed_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
FOREIGN KEY (config_key) REFERENCES server_config(key)
);
```
### 3. `config_validation_log` Table
```sql
-- Configuration validation errors log
CREATE TABLE config_validation_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
config_key TEXT NOT NULL,
attempted_value TEXT,
validation_error TEXT NOT NULL,
error_source TEXT DEFAULT 'validation', -- validation/parsing/constraint
attempted_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
```
### 4. Configuration File Cache Table
```sql
-- Cache for file-based configuration events
CREATE TABLE config_file_cache (
file_path TEXT PRIMARY KEY, -- Full path to config file
file_hash TEXT NOT NULL, -- SHA256 hash of file content
event_id TEXT, -- Nostr event ID from file
event_pubkey TEXT, -- Admin pubkey that signed event
loaded_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
validation_status TEXT CHECK (validation_status IN ('valid', 'invalid', 'unverified')),
validation_error TEXT -- Error details if invalid
);
```
## Indexes and Performance
```sql
-- Performance indexes for configuration tables
CREATE INDEX idx_server_config_type ON server_config(config_type);
CREATE INDEX idx_server_config_updated ON server_config(updated_at DESC);
CREATE INDEX idx_config_history_key ON config_history(config_key);
CREATE INDEX idx_config_history_time ON config_history(changed_at DESC);
CREATE INDEX idx_config_validation_key ON config_validation_log(config_key);
CREATE INDEX idx_config_validation_time ON config_validation_log(attempted_at DESC);
```
## Triggers
### Update Timestamp Trigger
```sql
-- Trigger to update timestamp on configuration changes
CREATE TRIGGER update_config_timestamp
AFTER UPDATE ON server_config
BEGIN
UPDATE server_config SET updated_at = strftime('%s', 'now') WHERE key = NEW.key;
END;
```
### Configuration History Trigger
```sql
-- Trigger to log configuration changes to history
CREATE TRIGGER log_config_changes
AFTER UPDATE ON server_config
WHEN OLD.value != NEW.value
BEGIN
INSERT INTO config_history (config_key, old_value, new_value, changed_by, change_reason)
VALUES (NEW.key, OLD.value, NEW.value, 'system', 'configuration update');
END;
```
## Default Configuration Values
### Core System Settings
```sql
INSERT OR IGNORE INTO server_config (key, value, description, config_type, data_type, requires_restart) VALUES
-- Administrative settings
('admin_pubkey', '', 'Authorized admin public key (hex)', 'system', 'string', 1),
('admin_enabled', 'false', 'Enable admin interface', 'system', 'boolean', 1),
-- Server core settings
('relay_port', '8888', 'WebSocket server port', 'user', 'integer', 1),
('database_path', 'db/c_nostr_relay.db', 'SQLite database file path', 'user', 'string', 1),
('max_connections', '100', 'Maximum concurrent connections', 'user', 'integer', 1),
-- NIP-11 Relay Information
('relay_name', 'C Nostr Relay', 'Relay name for NIP-11', 'user', 'string', 0),
('relay_description', 'High-performance C Nostr relay with SQLite storage', 'Relay description', 'user', 'string', 0),
('relay_contact', '', 'Contact information', 'user', 'string', 0),
('relay_pubkey', '', 'Relay public key', 'user', 'string', 0),
('relay_software', 'https://git.laantungir.net/laantungir/c-relay.git', 'Software URL', 'user', 'string', 0),
('relay_version', '0.2.0', 'Software version', 'user', 'string', 0),
-- NIP-13 Proof of Work
('pow_enabled', 'true', 'Enable NIP-13 Proof of Work validation', 'user', 'boolean', 0),
('pow_min_difficulty', '0', 'Minimum PoW difficulty required', 'user', 'integer', 0),
('pow_mode', 'basic', 'PoW validation mode (basic/full/strict)', 'user', 'string', 0),
-- NIP-40 Expiration Timestamp
('expiration_enabled', 'true', 'Enable NIP-40 expiration handling', 'user', 'boolean', 0),
('expiration_strict', 'true', 'Reject expired events on submission', 'user', 'boolean', 0),
('expiration_filter', 'true', 'Filter expired events from responses', 'user', 'boolean', 0),
('expiration_grace_period', '300', 'Grace period for clock skew (seconds)', 'user', 'integer', 0),
-- Subscription limits
('max_subscriptions_per_client', '20', 'Max subscriptions per client', 'user', 'integer', 0),
('max_total_subscriptions', '5000', 'Max total concurrent subscriptions', 'user', 'integer', 0),
('subscription_id_max_length', '64', 'Maximum subscription ID length', 'user', 'integer', 0),
-- Event processing limits
('max_event_tags', '100', 'Maximum tags per event', 'user', 'integer', 0),
('max_content_length', '8196', 'Maximum content length', 'user', 'integer', 0),
('max_message_length', '16384', 'Maximum message length', 'user', 'integer', 0),
-- Performance settings
('default_limit', '500', 'Default query limit', 'user', 'integer', 0),
('max_limit', '5000', 'Maximum query limit', 'user', 'integer', 0);
```
### Runtime Statistics
```sql
INSERT OR IGNORE INTO server_config (key, value, description, config_type, data_type) VALUES
-- Runtime statistics (updated by server)
('server_start_time', '0', 'Server startup timestamp', 'runtime', 'integer'),
('total_events_processed', '0', 'Total events processed', 'runtime', 'integer'),
('total_subscriptions_created', '0', 'Total subscriptions created', 'runtime', 'integer'),
('current_connections', '0', 'Current active connections', 'runtime', 'integer'),
('database_size_bytes', '0', 'Database file size in bytes', 'runtime', 'integer');
```
## Configuration Views
### Active Configuration View
```sql
CREATE VIEW active_config AS
SELECT
key,
value,
description,
config_type,
data_type,
requires_restart,
updated_at
FROM server_config
WHERE config_type IN ('system', 'user')
ORDER BY config_type, key;
```
### Runtime Statistics View
```sql
CREATE VIEW runtime_stats AS
SELECT
key,
value,
description,
updated_at
FROM server_config
WHERE config_type = 'runtime'
ORDER BY key;
```
### Configuration Change Summary
```sql
CREATE VIEW recent_config_changes AS
SELECT
ch.config_key,
sc.description,
ch.old_value,
ch.new_value,
ch.changed_by,
ch.change_reason,
ch.changed_at
FROM config_history ch
JOIN server_config sc ON ch.config_key = sc.key
ORDER BY ch.changed_at DESC
LIMIT 50;
```
## Validation Rules Format
Configuration validation rules are stored as JSON strings in the `validation_rules` column:
```json
{
"type": "integer",
"min": 1,
"max": 65535,
"required": true
}
```
```json
{
"type": "string",
"pattern": "^[0-9a-fA-F]{64}$",
"required": false,
"description": "64-character hex string"
}
```
```json
{
"type": "boolean",
"required": true
}
```
## Migration Strategy
1. **Phase 1**: Add configuration tables to existing schema
2. **Phase 2**: Populate with current hardcoded values
3. **Phase 3**: Update application code to read from database
4. **Phase 4**: Add file-based configuration loading
5. **Phase 5**: Remove hardcoded defaults and environment variable fallbacks
## Integration Points
- **Startup**: Load configuration from file → database → apply to application
- **Runtime**: Read configuration values from database cache
- **Updates**: Write changes to database → optionally update file
- **Validation**: Validate all configuration changes before applying
- **History**: Track all configuration changes for audit purposes

421
docs/configuration_guide.md Normal file
View File

@@ -0,0 +1,421 @@
# 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"`
- **Format**: String, max 64 characters
- **Example**: `"c-relay 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)
## 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 | 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)
# Check for memory leaks
ps aux | grep c_relay | 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_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.

View File

@@ -0,0 +1,94 @@
# Default Configuration Event Template
This document contains the template for the `src/default_config_event.h` file that will be created during implementation.
## File: `src/default_config_event.h`
```c
#ifndef DEFAULT_CONFIG_EVENT_H
#define DEFAULT_CONFIG_EVENT_H
/*
* Default Configuration Event Template
*
* This header contains the default configuration values for the C Nostr Relay.
* These values are used to create the initial kind 33334 configuration event
* during first-time startup.
*
* IMPORTANT: These values should never be accessed directly by other parts
* of the program. They are only used during initial configuration event creation.
*/
// Default configuration key-value pairs
static const struct {
const char* key;
const char* value;
} DEFAULT_CONFIG_VALUES[] = {
// Authentication
{"auth_enabled", "false"},
// Server Core Settings
{"relay_port", "8888"},
{"max_connections", "100"},
// NIP-11 Relay Information (relay keys will be populated at runtime)
{"relay_description", "High-performance C Nostr relay with SQLite storage"},
{"relay_contact", ""},
{"relay_software", "https://git.laantungir.net/laantungir/c-relay.git"},
{"relay_version", "v1.0.0"},
// NIP-13 Proof of Work (pow_min_difficulty = 0 means PoW disabled)
{"pow_min_difficulty", "0"},
{"pow_mode", "basic"},
// NIP-40 Expiration Timestamp
{"nip40_expiration_enabled", "true"},
{"nip40_expiration_strict", "true"},
{"nip40_expiration_filter", "true"},
{"nip40_expiration_grace_period", "300"},
// Subscription Limits
{"max_subscriptions_per_client", "25"},
{"max_total_subscriptions", "5000"},
{"max_filters_per_subscription", "10"},
// Event Processing Limits
{"max_event_tags", "100"},
{"max_content_length", "8196"},
{"max_message_length", "16384"},
// Performance Settings
{"default_limit", "500"},
{"max_limit", "5000"}
};
// Number of default configuration values
#define DEFAULT_CONFIG_COUNT (sizeof(DEFAULT_CONFIG_VALUES) / sizeof(DEFAULT_CONFIG_VALUES[0]))
// Function to create default configuration event
cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes,
const char* relay_privkey_hex,
const char* relay_pubkey_hex);
#endif /* DEFAULT_CONFIG_EVENT_H */
```
## Usage Notes
1. **Isolation**: These default values are completely isolated from the rest of the program
2. **Single Access Point**: Only accessed during `create_default_config_event()`
3. **Runtime Keys**: Relay keys are added at runtime, not stored as defaults
4. **No Direct Access**: Other parts of the program should never include this header directly
5. **Clean Separation**: Keeps default configuration separate from configuration logic
## Function Implementation
The `create_default_config_event()` function will:
1. Create a new cJSON event object with kind 33334
2. Add all default configuration values as tags
3. Add runtime-generated relay keys as tags
4. Use `nostr_core_lib` to sign the event with admin private key
5. Return the complete signed event ready for database storage
This approach ensures clean separation between default values and the configuration system logic.

600
docs/deployment_guide.md Normal file
View File

@@ -0,0 +1,600 @@
# 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 --shell /bin/false c-relay
# Create application directory
sudo mkdir -p /opt/c-relay
sudo chown c-relay:c-relay /opt/c-relay
```
### Build and Installation
#### Automated Installation (Recommended)
```bash
# Clone repository
git clone https://github.com/your-org/c-relay.git
cd c-relay
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_x86 /opt/c-relay/
sudo chown c-relay:c-relay /opt/c-relay/c_relay_x86
sudo chmod +x /opt/c-relay/c_relay_x86
# Install systemd service
sudo cp systemd/c-relay.service /etc/systemd/system/
sudo systemctl daemon-reload
```
### Service Management
#### Start and Enable Service
```bash
# Start the service
sudo systemctl start c-relay
# Enable auto-start on boot
sudo systemctl enable c-relay
# Check status
sudo systemctl status c-relay
```
#### Capture Admin Keys (CRITICAL)
```bash
# View startup logs to get admin keys
sudo journalctl -u c-relay --since "5 minutes ago" | grep -A 10 "IMPORTANT: SAVE THIS ADMIN PRIVATE KEY"
# Or check the full log
sudo journalctl -u c-relay --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 port
sudo ufw allow 8888/tcp
# Allow SSH (ensure you don't lock yourself out)
sudo ufw allow 22/tcp
# Enable firewall
sudo ufw enable
```
#### iptables
```bash
# Allow relay port
sudo iptables -A INPUT -p tcp --dport 8888 -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.git
cd c-relay
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:c-relay /data
# Update systemd service to use /data
sudo sed -i 's/WorkingDirectory=\/opt\/c-relay/WorkingDirectory=\/data/' /etc/systemd/system/c-relay.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-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-instance
git clone https://github.com/your-org/c-relay.git
cd c-relay
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-instance --disk=relay-data
# Format and mount
sudo mkfs.ext4 /dev/sdb
sudo mkdir /data
sudo mount /dev/sdb /data
sudo chown c-relay:c-relay /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.git
cd c-relay
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.git
cd c-relay
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;
}
```
### 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_x86" > /dev/null; then
echo "[$DATE] ERROR: Relay process not running" >> $LOG_FILE
systemctl restart c-relay
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 -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_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.conf
if $programname == 'c-relay' then /var/log/c-relay.log
& stop
```
### External Monitoring
#### Prometheus Integration
```yaml
# /etc/prometheus/prometheus.yml
scrape_configs:
- job_name: 'c-relay'
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
sudo usermod -d /opt/c-relay c-relay
# Set proper permissions
sudo chmod 700 /opt/c-relay
sudo chown -R c-relay:c-relay /opt/c-relay
```
#### File System Restrictions
```bash
# Mount data directory with appropriate options
echo "/dev/sdb /opt/c-relay ext4 defaults,noexec,nosuid,nodev 0 2" >> /etc/fstab
```
### Network Security
#### Fail2Ban Configuration
```ini
# /etc/fail2ban/jail.d/c-relay.conf
[c-relay-dos]
enabled = true
port = 8888
filter = c-relay-dos
logpath = /var/log/c-relay.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"
DATE=$(date +%Y%m%d_%H%M%S)
RELAY_DB=$(find /opt/c-relay -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/ s3://your-backup-bucket/c-relay/ --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/
# 2. Fix permissions
sudo chown c-relay:c-relay /opt/c-relay/*.nrdb
# 3. Restart service
sudo systemctl restart c-relay
# 4. Verify recovery
sudo journalctl -u c-relay --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.

View File

@@ -0,0 +1,358 @@
# Event-Based Configuration System Implementation Plan
## Overview
This document provides a detailed implementation plan for transitioning the C Nostr Relay from command line arguments and file-based configuration to a pure event-based configuration system using kind 33334 Nostr events stored directly in the database.
## Implementation Phases
### Phase 0: File Structure Preparation ✅ COMPLETED
#### 0.1 Backup and Prepare Files ✅ COMPLETED
**Actions:**
1. ✅ Rename `src/config.c` to `src/config.c.old` - DONE
2. ✅ Rename `src/config.h` to `src/config.h.old` - DONE
3. ✅ Create new empty `src/config.c` and `src/config.h` - DONE
4. ✅ Create new `src/default_config_event.h` - DONE
### Phase 1: Database Schema and Core Infrastructure ✅ COMPLETED
#### 1.1 Update Database Naming System ✅ COMPLETED
**File:** `src/main.c`, new `src/config.c`, new `src/config.h`
```c
// New functions implemented: ✅
char* get_database_name_from_relay_pubkey(const char* relay_pubkey);
int create_database_with_relay_pubkey(const char* relay_pubkey);
```
**Changes Completed:**
- ✅ Create completely new `src/config.c` and `src/config.h` files
- ✅ Rename old files to `src/config.c.old` and `src/config.h.old`
- ✅ Modify `init_database()` to use relay pubkey for database naming
- ✅ Use `nostr_core_lib` functions for all keypair generation
- ✅ Database path: `./<relay_pubkey>.nrdb`
- ✅ Remove all database path command line argument handling
#### 1.2 Configuration Event Storage ✅ COMPLETED
**File:** new `src/config.c`, new `src/default_config_event.h`
```c
// Configuration functions implemented: ✅
int store_config_event_in_database(const cJSON* event);
cJSON* load_config_event_from_database(const char* relay_pubkey);
```
**Changes Completed:**
- ✅ Create new `src/default_config_event.h` for default configuration values
- ✅ Add functions to store/retrieve kind 33334 events from events table
- ✅ Use `nostr_core_lib` functions for all event validation
- ✅ Clean separation: default config values isolated in header file
- ✅ Remove existing config table dependencies
### Phase 2: Event Processing Integration ✅ COMPLETED
#### 2.1 Real-time Configuration Processing ✅ COMPLETED
**File:** `src/main.c` (event processing functions)
**Integration Points:** ✅ IMPLEMENTED
```c
// In existing event processing loop: ✅ IMPLEMENTED
// Added kind 33334 event detection in main event loop
if (kind_num == 33334) {
if (handle_configuration_event(event, error_message, sizeof(error_message)) == 0) {
// Configuration event processed successfully
}
}
// Configuration event processing implemented: ✅
int process_configuration_event(const cJSON* event);
int handle_configuration_event(cJSON* event, char* error_message, size_t error_size);
```
#### 2.2 Configuration Application System ⚠️ PARTIALLY COMPLETED
**File:** `src/config.c`
**Status:** Configuration access functions implemented, field handlers need completion
```c
// Configuration access implemented: ✅
const char* get_config_value(const char* key);
int get_config_int(const char* key, int default_value);
int get_config_bool(const char* key, int default_value);
// Field handlers need implementation: ⏳ IN PROGRESS
// Need to implement specific apply functions for runtime changes
```
### Phase 3: First-Time Startup System ✅ COMPLETED
#### 3.1 Key Generation and Initial Setup ✅ COMPLETED
**File:** new `src/config.c`, `src/default_config_event.h`
**Status:** ✅ FULLY IMPLEMENTED with secure /dev/urandom + nostr_core_lib validation
```c
int first_time_startup_sequence() {
// 1. Generate admin keypair using nostr_core_lib
unsigned char admin_privkey_bytes[32];
char admin_privkey[65], admin_pubkey[65];
if (nostr_generate_private_key(admin_privkey_bytes) != 0) {
return -1;
}
nostr_bytes_to_hex(admin_privkey_bytes, 32, admin_privkey);
unsigned char admin_pubkey_bytes[32];
if (nostr_ec_public_key_from_private_key(admin_privkey_bytes, admin_pubkey_bytes) != 0) {
return -1;
}
nostr_bytes_to_hex(admin_pubkey_bytes, 32, admin_pubkey);
// 2. Generate relay keypair using nostr_core_lib
unsigned char relay_privkey_bytes[32];
char relay_privkey[65], relay_pubkey[65];
if (nostr_generate_private_key(relay_privkey_bytes) != 0) {
return -1;
}
nostr_bytes_to_hex(relay_privkey_bytes, 32, relay_privkey);
unsigned char relay_pubkey_bytes[32];
if (nostr_ec_public_key_from_private_key(relay_privkey_bytes, relay_pubkey_bytes) != 0) {
return -1;
}
nostr_bytes_to_hex(relay_pubkey_bytes, 32, relay_pubkey);
// 3. Create database with relay pubkey name
if (create_database_with_relay_pubkey(relay_pubkey) != 0) {
return -1;
}
// 4. Create initial configuration event using defaults from header
cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey);
// 5. Store configuration event in database
store_config_event_in_database(config_event);
// 6. Print admin private key for user to save
printf("=== SAVE THIS ADMIN PRIVATE KEY ===\n");
printf("Admin Private Key: %s\n", admin_privkey);
printf("===================================\n");
return 0;
}
```
#### 3.2 Database Detection Logic ✅ COMPLETED
**File:** `src/main.c`
**Status:** ✅ FULLY IMPLEMENTED
```c
// Implemented functions: ✅
char** find_existing_nrdb_files(void);
char* extract_pubkey_from_filename(const char* filename);
int is_first_time_startup(void);
int first_time_startup_sequence(void);
int startup_existing_relay(const char* relay_pubkey);
```
### Phase 4: Legacy System Removal ✅ PARTIALLY COMPLETED
#### 4.1 Remove Command Line Arguments ✅ COMPLETED
**File:** `src/main.c`
**Status:** ✅ COMPLETED
- ✅ All argument parsing logic removed except --help and --version
-`--port`, `--config-dir`, `--config-file`, `--database-path` handling removed
- ✅ Environment variable override systems removed
- ✅ Clean help and version functions implemented
#### 4.2 Remove Configuration File System ✅ COMPLETED
**File:** `src/config.c`
**Status:** ✅ COMPLETED - New file created from scratch
- ✅ All legacy file-based configuration functions removed
- ✅ XDG configuration directory logic removed
- ✅ Pure event-based system implemented
#### 4.3 Remove Legacy Database Tables ⏳ PENDING
**File:** `src/sql_schema.h`
**Status:** ⏳ NEEDS COMPLETION
```sql
-- Still need to remove these tables:
DROP TABLE IF EXISTS config;
DROP TABLE IF EXISTS config_history;
DROP TABLE IF EXISTS config_file_cache;
DROP VIEW IF EXISTS active_config;
```
### Phase 5: Configuration Management
#### 5.1 Configuration Field Mapping
**File:** `src/config.c`
```c
// Map configuration tags to current system
static const config_field_handler_t config_handlers[] = {
{"auth_enabled", 0, apply_auth_enabled},
{"relay_port", 1, apply_relay_port}, // requires restart
{"max_connections", 0, apply_max_connections},
{"relay_description", 0, apply_relay_description},
{"relay_contact", 0, apply_relay_contact},
{"relay_pubkey", 1, apply_relay_pubkey}, // requires restart
{"relay_privkey", 1, apply_relay_privkey}, // requires restart
{"pow_min_difficulty", 0, apply_pow_difficulty},
{"nip40_expiration_enabled", 0, apply_expiration_enabled},
{"max_subscriptions_per_client", 0, apply_max_subscriptions},
{"max_event_tags", 0, apply_max_event_tags},
{"max_content_length", 0, apply_max_content_length},
{"default_limit", 0, apply_default_limit},
{"max_limit", 0, apply_max_limit},
// ... etc
};
```
#### 5.2 Startup Configuration Loading
**File:** `src/main.c`
```c
int startup_existing_relay(const char* relay_pubkey) {
// 1. Open database
if (init_database_with_pubkey(relay_pubkey) != 0) {
return -1;
}
// 2. Load configuration event from database
cJSON* config_event = load_config_event_from_database(relay_pubkey);
if (!config_event) {
log_error("No configuration event found in database");
return -1;
}
// 3. Apply all configuration from event
if (apply_configuration_from_event(config_event) != 0) {
return -1;
}
// 4. Continue with normal startup
return start_relay_services();
}
```
## Implementation Order - PROGRESS STATUS
### Step 1: Core Infrastructure ✅ COMPLETED
1. ✅ Implement database naming with relay pubkey
2. ✅ Add key generation functions using `nostr_core_lib`
3. ✅ Create configuration event storage/retrieval functions
4. ✅ Test basic event creation and storage
### Step 2: Event Processing Integration ✅ MOSTLY COMPLETED
1. ✅ Add kind 33334 event detection to event processing loop
2. ✅ Implement configuration event validation
3. ⚠️ Create configuration application handlers (basic access implemented, runtime handlers pending)
4. ⏳ Test real-time configuration updates (infrastructure ready)
### Step 3: First-Time Startup ✅ COMPLETED
1. ✅ Implement first-time startup detection
2. ✅ Add automatic key generation and database creation
3. ✅ Create default configuration event generation
4. ✅ Test complete first-time startup flow
### Step 4: Legacy Removal ⚠️ MOSTLY COMPLETED
1. ✅ Remove command line argument parsing
2. ✅ Remove configuration file system
3. ⏳ Remove legacy database tables (pending)
4. ✅ Update all references to use event-based config
### Step 5: Testing and Validation ⚠️ PARTIALLY COMPLETED
1. ✅ Test complete startup flow (first time and existing)
2. ⏳ Test configuration updates via events (infrastructure ready)
3. ⚠️ Test error handling and recovery (basic error handling implemented)
4. ⏳ Performance testing and optimization (pending)
## Migration Strategy
### For Existing Installations
Since the new system uses a completely different approach:
1. **No Automatic Migration**: The new system starts fresh
2. **Manual Migration**: Users can manually copy configuration values
3. **Documentation**: Provide clear migration instructions
4. **Coexistence**: Old and new systems use different database names
### Migration Steps for Users
1. Stop existing relay
2. Note current configuration values
3. Start new relay (generates keys and new database)
4. Create kind 33334 event with desired configuration using admin private key
5. Send event to relay to update configuration
## Testing Requirements
### Unit Tests
- Key generation functions
- Configuration event creation and validation
- Database naming logic
- Configuration application handlers
### Integration Tests
- Complete first-time startup flow
- Configuration update via events
- Error handling scenarios
- Database operations
### Performance Tests
- Startup time comparison
- Configuration update response time
- Memory usage analysis
## Security Considerations
1. **Admin Private Key**: Never stored, only printed once
2. **Event Validation**: All configuration events must be signed by admin
3. **Database Security**: Relay database contains relay private key
4. **Key Generation**: Use `nostr_core_lib` for cryptographically secure generation
## Files to Modify
### Major Changes
- `src/main.c` - Startup logic, event processing, argument removal
- `src/config.c` - Complete rewrite for event-based configuration
- `src/config.h` - Update function signatures and structures
- `src/sql_schema.h` - Remove config tables
### Minor Changes
- `Makefile` - Remove any config file generation
- `systemd/` - Update service files if needed
- Documentation updates
## Backwards Compatibility
**Breaking Changes:**
- Command line arguments removed (except --help, --version)
- Configuration files no longer used
- Database naming scheme changed
- Configuration table removed
**Migration Required:** This is a breaking change that requires manual migration for existing installations.
## Success Criteria - CURRENT STATUS
1.**Zero Command Line Arguments**: Relay starts with just `./c-relay`
2.**Automatic First-Time Setup**: Generates keys and database automatically
3. ⚠️ **Real-Time Configuration**: Infrastructure ready, handlers need completion
4.**Single Database File**: All configuration and data in one `.nrdb` file
5. ⚠️ **Admin Control**: Event processing implemented, signature validation ready
6. ⚠️ **Clean Codebase**: Most legacy code removed, database tables cleanup pending
## Risk Mitigation
1. **Backup Strategy**: Document manual backup procedures for relay database
2. **Key Loss Recovery**: Document recovery procedures if admin key is lost
3. **Testing Coverage**: Comprehensive test suite before deployment
4. **Rollback Plan**: Keep old version available during transition period
5. **Documentation**: Comprehensive user and developer documentation
This implementation plan provides a clear path from the current system to the new event-based configuration architecture while maintaining security and reliability.

View File

@@ -1,493 +0,0 @@
# File-Based Configuration Architecture Design
## Overview
This document outlines the XDG-compliant file-based configuration system for the C Nostr Relay, following the Ginxsom admin system approach using signed Nostr events.
## XDG Base Directory Specification Compliance
### File Location Strategy
**Primary Location:**
```
$XDG_CONFIG_HOME/c-relay/c_relay_config_event.json
```
**Fallback Location:**
```
$HOME/.config/c-relay/c_relay_config_event.json
```
**System-wide Fallback:**
```
/etc/c-relay/c_relay_config_event.json
```
### Directory Structure
```
$XDG_CONFIG_HOME/c-relay/
├── c_relay_config_event.json # Main configuration file
├── backup/ # Configuration backups
│ ├── c_relay_config_event.json.bak
│ └── c_relay_config_event.20241205.json
└── validation/ # Validation logs
└── config_validation.log
```
## Configuration File Format
### Signed Nostr Event Structure
The configuration file contains a signed Nostr event (kind 33334) with relay configuration:
```json
{
"kind": 33334,
"created_at": 1704067200,
"tags": [
["relay_name", "C Nostr Relay"],
["relay_description", "High-performance C Nostr relay with SQLite storage"],
["relay_port", "8888"],
["database_path", "db/c_nostr_relay.db"],
["admin_pubkey", ""],
["admin_enabled", "false"],
["pow_enabled", "true"],
["pow_min_difficulty", "0"],
["pow_mode", "basic"],
["expiration_enabled", "true"],
["expiration_strict", "true"],
["expiration_filter", "true"],
["expiration_grace_period", "300"],
["max_subscriptions_per_client", "20"],
["max_total_subscriptions", "5000"],
["max_connections", "100"],
["relay_contact", ""],
["relay_pubkey", ""],
["relay_software", "https://git.laantungir.net/laantungir/c-relay.git"],
["relay_version", "0.2.0"],
["max_event_tags", "100"],
["max_content_length", "8196"],
["max_message_length", "16384"],
["default_limit", "500"],
["max_limit", "5000"]
],
"content": "C Nostr Relay configuration event",
"pubkey": "admin_public_key_hex_64_chars",
"id": "computed_event_id_hex_64_chars",
"sig": "computed_signature_hex_128_chars"
}
```
### Event Kind Definition
**Kind 33334**: C Nostr Relay Configuration Event
- Parameterized replaceable event
- Must be signed by authorized admin pubkey
- Contains relay configuration as tags
- Validation required on load
## Configuration Loading Architecture
### Loading Priority Chain
1. **Command Line Arguments** (highest priority)
2. **File-based Configuration** (signed Nostr event)
3. **Database Configuration** (persistent storage)
4. **Environment Variables** (compatibility mode)
5. **Hardcoded Defaults** (fallback)
### Loading Process Flow
```mermaid
flowchart TD
A[Server Startup] --> B[Get Config File Path]
B --> C{File Exists?}
C -->|No| D[Check Database Config]
C -->|Yes| E[Load & Parse JSON]
E --> F[Validate Event Structure]
F --> G{Valid Event?}
G -->|No| H[Log Error & Use Database]
G -->|Yes| I[Verify Event Signature]
I --> J{Signature Valid?}
J -->|No| K[Log Error & Use Database]
J -->|Yes| L[Extract Configuration Tags]
L --> M[Apply to Database]
M --> N[Apply to Application]
D --> O[Load from Database]
H --> O
K --> O
O --> P[Apply Environment Variable Overrides]
P --> Q[Apply Command Line Overrides]
Q --> N
N --> R[Server Ready]
```
## C Implementation Architecture
### Core Data Structures
```c
// Configuration file management
typedef struct {
char file_path[512];
char file_hash[65]; // SHA256 hash
time_t last_modified;
time_t last_loaded;
int validation_status; // 0=valid, 1=invalid, 2=unverified
char validation_error[256];
} config_file_info_t;
// Configuration event structure
typedef struct {
char event_id[65];
char pubkey[65];
char signature[129];
long created_at;
int kind;
cJSON* tags;
char* content;
} config_event_t;
// Configuration management context
typedef struct {
config_file_info_t file_info;
config_event_t event;
int loaded_from_file;
int loaded_from_database;
char admin_pubkey[65];
time_t load_timestamp;
} config_context_t;
```
### Core Function Signatures
```c
// XDG path resolution
int get_config_file_path(char* path, size_t path_size);
int create_config_directories(const char* config_path);
// File operations
int load_config_from_file(const char* config_path, config_context_t* ctx);
int save_config_to_file(const char* config_path, const config_event_t* event);
int backup_config_file(const char* config_path);
// Event validation
int validate_config_event_structure(const cJSON* event);
int verify_config_event_signature(const config_event_t* event, const char* admin_pubkey);
int validate_config_tag_values(const cJSON* tags);
// Configuration extraction and application
int extract_config_from_tags(const cJSON* tags, config_context_t* ctx);
int apply_config_to_database(const config_context_t* ctx);
int apply_config_to_globals(const config_context_t* ctx);
// File monitoring and updates
int monitor_config_file_changes(const char* config_path);
int reload_config_on_change(config_context_t* ctx);
// Error handling and logging
int log_config_validation_error(const char* config_key, const char* error);
int log_config_load_event(const config_context_t* ctx, const char* source);
```
## Configuration Validation Rules
### Event Structure Validation
1. **Required Fields**: `kind`, `created_at`, `tags`, `content`, `pubkey`, `id`, `sig`
2. **Kind Validation**: Must be exactly 33334
3. **Timestamp Validation**: Must be reasonable (not too old, not future)
4. **Tags Format**: Array of string arrays `[["key", "value"], ...]`
5. **Signature Verification**: Must be signed by authorized admin pubkey
### Configuration Value Validation
```c
typedef struct {
char* key;
char* data_type; // "string", "integer", "boolean", "json"
char* validation_rule; // JSON validation rule
int required;
char* default_value;
} config_validation_rule_t;
static config_validation_rule_t validation_rules[] = {
{"relay_port", "integer", "{\"min\": 1, \"max\": 65535}", 1, "8888"},
{"pow_min_difficulty", "integer", "{\"min\": 0, \"max\": 64}", 1, "0"},
{"expiration_grace_period", "integer", "{\"min\": 0, \"max\": 86400}", 1, "300"},
{"admin_pubkey", "string", "{\"pattern\": \"^[0-9a-fA-F]{64}$\"}", 0, ""},
{"pow_enabled", "boolean", "{}", 1, "true"},
// ... more rules
};
```
### Security Validation
1. **Admin Pubkey Verification**: Only configured admin pubkeys can create config events
2. **Event ID Verification**: Event ID must match computed hash
3. **Signature Verification**: Signature must be valid for the event and pubkey
4. **Timestamp Validation**: Prevent replay attacks with old events
5. **File Permission Checks**: Config files should have appropriate permissions
## File Management Features
### Configuration File Operations
**File Creation:**
- Generate initial configuration file with default values
- Sign with admin private key
- Set appropriate file permissions (600 - owner read/write only)
**File Updates:**
- Create backup of existing file
- Validate new configuration
- Atomic file replacement (write to temp, then rename)
- Update file metadata cache
**File Monitoring:**
- Watch for file system changes using inotify (Linux)
- Reload configuration automatically when file changes
- Validate changes before applying
- Log all configuration reload events
### Backup and Recovery
**Automatic Backups:**
```
$XDG_CONFIG_HOME/c-relay/backup/
├── c_relay_config_event.json.bak # Last working config
├── c_relay_config_event.20241205-143022.json # Timestamped backups
└── c_relay_config_event.20241204-091530.json
```
**Recovery Process:**
1. Detect corrupted or invalid config file
2. Attempt to load from `.bak` backup
3. If backup fails, generate default configuration
4. Log recovery actions for audit
## Integration with Database Schema
### File-Database Synchronization
**On File Load:**
1. Parse and validate file-based configuration
2. Extract configuration values from event tags
3. Update database `server_config` table
4. Record file metadata in `config_file_cache` table
5. Log configuration changes in `config_history` table
**Configuration Priority Resolution:**
```c
char* get_config_value(const char* key, const char* default_value) {
// Priority: CLI args > File config > DB config > Env vars > Default
char* value = NULL;
// 1. Check command line overrides (if implemented)
value = get_cli_override(key);
if (value) return value;
// 2. Check database (updated from file)
value = get_database_config(key);
if (value) return value;
// 3. Check environment variables (compatibility)
value = get_env_config(key);
if (value) return value;
// 4. Return default
return strdup(default_value);
}
```
## Error Handling and Recovery
### Validation Error Handling
```c
typedef enum {
CONFIG_ERROR_NONE = 0,
CONFIG_ERROR_FILE_NOT_FOUND = 1,
CONFIG_ERROR_PARSE_FAILED = 2,
CONFIG_ERROR_INVALID_STRUCTURE = 3,
CONFIG_ERROR_SIGNATURE_INVALID = 4,
CONFIG_ERROR_UNAUTHORIZED = 5,
CONFIG_ERROR_VALUE_INVALID = 6,
CONFIG_ERROR_IO_ERROR = 7
} config_error_t;
typedef struct {
config_error_t error_code;
char error_message[256];
char config_key[64];
char invalid_value[128];
time_t error_timestamp;
} config_error_info_t;
```
### Graceful Degradation
**File Load Failure:**
1. Log detailed error information
2. Fall back to database configuration
3. Continue operation with last known good config
4. Set service status to "degraded" mode
**Validation Failure:**
1. Log validation errors with specific details
2. Skip invalid configuration items
3. Use default values for failed items
4. Continue with partial configuration
**Permission Errors:**
1. Log permission issues
2. Attempt to use fallback locations
3. Generate temporary config if needed
4. Alert administrator via logs
## Configuration Update Process
### Safe Configuration Updates
**Atomic Update Process:**
1. Create backup of current configuration
2. Write new configuration to temporary file
3. Validate new configuration completely
4. If valid, rename temporary file to active config
5. Update database with new values
6. Apply changes to running server
7. Log successful update
**Rollback Process:**
1. Detect invalid configuration at startup
2. Restore from backup file
3. Log rollback event
4. Continue with previous working configuration
### Hot Reload Support
**File Change Detection:**
```c
int monitor_config_file_changes(const char* config_path) {
// Use inotify on Linux to watch file changes
int inotify_fd = inotify_init();
int watch_fd = inotify_add_watch(inotify_fd, config_path, IN_MODIFY | IN_MOVED_TO);
// Monitor in separate thread
// On change: validate -> apply -> log
return 0;
}
```
**Runtime Configuration Updates:**
- Reload configuration on file change
- Apply non-restart-required changes immediately
- Queue restart-required changes for next restart
- Notify operators of configuration changes
## Security Considerations
### Access Control
**File Permissions:**
- Config files: 600 (owner read/write only)
- Directories: 700 (owner access only)
- Backup files: 600 (owner read/write only)
**Admin Key Management:**
- Admin private keys never stored in config files
- Only admin pubkeys stored for verification
- Support for multiple admin pubkeys
- Key rotation support
### Signature Validation
**Event Signature Verification:**
```c
int verify_config_event_signature(const config_event_t* event, const char* admin_pubkey) {
// 1. Reconstruct event for signing (without id and sig)
// 2. Compute event ID and verify against stored ID
// 3. Verify signature using admin pubkey
// 4. Check admin pubkey authorization
return NOSTR_SUCCESS;
}
```
**Anti-Replay Protection:**
- Configuration events must be newer than current
- Event timestamps validated against reasonable bounds
- Configuration history prevents replay attacks
## Implementation Phases
### Phase 1: Basic File Support
- XDG path resolution
- File loading and parsing
- Basic validation
- Database integration
### Phase 2: Security Features
- Event signature verification
- Admin pubkey management
- File permission checks
- Error handling
### Phase 3: Advanced Features
- Hot reload support
- Automatic backups
- Configuration utilities
- Interactive setup
### Phase 4: Monitoring & Management
- Configuration change monitoring
- Advanced validation rules
- Configuration audit logging
- Management utilities
## Configuration Generation Utilities
### Interactive Setup Script
```bash
#!/bin/bash
# scripts/setup_config.sh - Interactive configuration setup
create_initial_config() {
echo "=== C Nostr Relay Initial Configuration ==="
# Collect basic information
read -p "Relay name [C Nostr Relay]: " relay_name
read -p "Admin public key (hex): " admin_pubkey
read -p "Server port [8888]: " server_port
# Generate signed configuration event
./scripts/generate_config.sh \
--admin-key "$admin_pubkey" \
--relay-name "${relay_name:-C Nostr Relay}" \
--port "${server_port:-8888}" \
--output "$XDG_CONFIG_HOME/c-relay/c_relay_config_event.json"
}
```
### Configuration Validation Utility
```bash
#!/bin/bash
# scripts/validate_config.sh - Validate configuration file
validate_config_file() {
local config_file="$1"
# Check file exists and is readable
# Validate JSON structure
# Verify event signature
# Check configuration values
# Report validation results
}
```
This comprehensive file-based configuration design provides a robust, secure, and maintainable system that follows industry standards while integrating seamlessly with the existing C Nostr Relay architecture.

View File

@@ -0,0 +1,128 @@
# Startup Configuration Design Analysis
## Review of startup_config_design.md
### Key Design Principles Identified
1. **Zero Command Line Arguments**: Complete elimination of CLI arguments for true "quick start"
2. **Event-Based Configuration**: Configuration stored as Nostr event (kind 33334) in events table
3. **Self-Contained Database**: Database named after relay pubkey (`<pubkey>.nrdb`)
4. **First-Time Setup**: Automatic key generation and initial configuration creation
5. **Configuration Consistency**: Always read from event, never from hardcoded defaults
### Implementation Gaps and Specifications Needed
#### 1. Key Generation Process
**Specification:**
```
First Startup Key Generation:
1. Generate all keys on first startup (admin private/public, relay private/public)
2. Use nostr_core_lib for key generation entropy
3. Keys are encoded in hex format
4. Print admin private key to stdout for user to save (never stored)
5. Store admin public key, relay private key, and relay public key in configuration event
6. Admin can later change the 33334 event to alter stored keys
```
#### 2. Database Naming and Location
**Specification:**
```
Database Naming:
1. Database is named using relay pubkey: ./<relay_pubkey>.nrdb
2. Database path structure: ./<relay_pubkey>.nrdb
3. If database creation fails, program quits (can't run without database)
4. c_nostr_relay.db should never exist in new system
```
#### 3. Configuration Event Structure (Kind 33334)
**Specification:**
```
Event Structure:
- Kind: 33334 (parameterized replaceable event)
- Event validation: Use nostr_core_lib to validate event
- Event content field: "C Nostr Relay Configuration" (descriptive text)
- Configuration update mechanism: TBD
- Complete tag structure provided in configuration section below
```
#### 4. Configuration Change Monitoring
**Configuration Monitoring System:**
```
Every event that is received is checked to see if it is a kind 33334 event from the admin pubkey.
If so, it is processed as a configuration update.
```
#### 5. Error Handling and Recovery
**Specification:**
```
Error Recovery Priority:
1. Try to load latest valid config event
2. Generate new default configuration event if none exists
3. Exit with error if all recovery attempts fail
Note: There is only ever one configuration event (parameterized replaceable event),
so no fallback to previous versions.
```
### Design Clarifications
**Key Management:**
- Admin private key is never stored, only printed once at first startup
- Single admin system (no multi-admin support)
- No key rotation support
**Configuration Management:**
- No configuration versioning/timestamping
- No automatic backup of configuration events
- Configuration events are not broadcastable to other relays
- Future: Auth system to restrict admin access to configuration events
---
## Complete Current Configuration Structure
Based on analysis of [`src/config.c`](src/config.c:753-795), here is the complete current configuration structure that will be converted to event tags:
### Complete Event Structure Example
```json
{
"kind": 33334,
"created_at": 1725661483,
"tags": [
["d", "<relay_pubkey>"],
["auth_enabled", "false"],
["relay_port", "8888"],
["max_connections", "100"],
["relay_description", "High-performance C Nostr relay with SQLite storage"],
["relay_contact", ""],
["relay_pubkey", "<relay_public_key>"],
["relay_privkey", "<relay_private_key>"],
["relay_software", "https://git.laantungir.net/laantungir/c-relay.git"],
["relay_version", "v1.0.0"],
["pow_min_difficulty", "0"],
["pow_mode", "basic"],
["nip40_expiration_enabled", "true"],
["nip40_expiration_strict", "true"],
["nip40_expiration_filter", "true"],
["nip40_expiration_grace_period", "300"],
["max_subscriptions_per_client", "25"],
["max_total_subscriptions", "5000"],
["max_filters_per_subscription", "10"],
["max_event_tags", "100"],
["max_content_length", "8196"],
["max_message_length", "16384"],
["default_limit", "500"],
["max_limit", "5000"]
],
"content": "C Nostr Relay Configuration",
"pubkey": "<admin_public_key>",
"id": "<computed_event_id>",
"sig": "<event_signature>"
}
```
**Note:** The `admin_pubkey` tag is omitted as it's redundant with the event's `pubkey` field.

View File

@@ -0,0 +1,22 @@
# Startup and configuration for c_nostr_relay
No command line variables. Quick start.
## First time startup
When the program first starts, it generates a new private and public keys for the program, and for the admin. In the command line it prints out the private key for the admin. It creates a database in the same directory as the application. It names the database after the pubkey of the database <pubkey>.nrdb (This stands for nostr relay db)
Internally, it creates a valid nostr event using the generated admin private key, and saves it to the events table in the db. That nostr configuration event is a type 33334 event, with a d tag that equals the database public key d=<db pubkey>.
The event is populated from internal default values. Then the configuration setup is run by reading the event from the database events table.
Important, the constant values are ALWAYS read and set from the 33334 event in the events table, they are NEVER read from the stored default values. This is important for consistancy.
The config section of the program keeps track of the admin file, and if it ever changes, it does what is needed to implement the change.
## Later startups
The program looks for the database with the name c_nostr_relay.db in the same directory as the program. If it doesn't find it, it assumes a first time startup. If it does find it, it loads the database, and the config section reads the config event and proceedes from there.
## Changing database location?
Changing the location of the databases can be done by creating a sym-link to the new location of the database.

507
docs/user_guide.md Normal file
View File

@@ -0,0 +1,507 @@
# C Nostr Relay - User Guide
Complete guide for deploying, configuring, and managing the C Nostr Relay with event-based configuration system.
## Table of Contents
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Configuration Management](#configuration-management)
- [Administration](#administration)
- [Monitoring](#monitoring)
- [Troubleshooting](#troubleshooting)
- [Advanced Usage](#advanced-usage)
## Quick Start
### 1. Build and Start
```bash
# Clone and build
git clone <repository-url>
cd c-relay
git submodule update --init --recursive
make
# Start relay (zero configuration needed)
./build/c_relay_x86
```
### 2. First Startup - Save Keys
The relay will display admin keys on first startup:
```
=================================================================
IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY!
=================================================================
Admin Private Key: a018ecc259ff296ef7aaca6cdccbc52cf28104ac7a1f14c27b0b8232e5025ddc
Admin Public Key: 68394d08ab87f936a42ff2deb15a84fbdfbe0996ee0eb20cda064aae673285d1
=================================================================
```
⚠️ **CRITICAL**: Save the admin private key - it's needed for configuration updates and only shown once!
### 3. Connect Clients
Your relay is now available at:
- **WebSocket**: `ws://localhost:8888`
- **NIP-11 Info**: `http://localhost:8888`
## Installation
### System Requirements
- **Operating System**: Linux, macOS, or Windows (WSL)
- **RAM**: Minimum 512MB, recommended 2GB+
- **Disk**: 100MB for binary + database storage (grows with events)
- **Network**: Port 8888 (configurable via events)
### Dependencies
Install required libraries:
**Ubuntu/Debian:**
```bash
sudo apt update
sudo apt install build-essential git sqlite3 libsqlite3-dev libwebsockets-dev libssl-dev libsecp256k1-dev libcurl4-openssl-dev zlib1g-dev
```
**CentOS/RHEL:**
```bash
sudo yum install gcc git sqlite-devel libwebsockets-devel openssl-devel libsecp256k1-devel libcurl-devel zlib-devel
```
**macOS (Homebrew):**
```bash
brew install git sqlite libwebsockets openssl libsecp256k1 curl zlib
```
### Building from Source
```bash
# Clone repository
git clone <repository-url>
cd c-relay
# Initialize submodules
git submodule update --init --recursive
# Build
make clean && make
# Verify build
ls -la build/c_relay_x86
```
### Production Deployment
#### SystemD Service (Recommended)
```bash
# Install as system service
sudo systemd/install-service.sh
# Start service
sudo systemctl start c-relay
# Enable auto-start
sudo systemctl enable c-relay
# Check status
sudo systemctl status c-relay
```
#### Manual Deployment
```bash
# Create dedicated user
sudo useradd --system --home-dir /opt/c-relay --shell /bin/false c-relay
# Install binary
sudo mkdir -p /opt/c-relay
sudo cp build/c_relay_x86 /opt/c-relay/
sudo chown -R c-relay:c-relay /opt/c-relay
# Run as service user
sudo -u c-relay /opt/c-relay/c_relay_x86
```
## Configuration Management
### Event-Based Configuration System
Unlike traditional relays that use config files, this relay stores all configuration as **kind 33334 Nostr events** in the database. This provides:
- **Real-time updates**: Changes applied instantly without restart
- **Cryptographic security**: All config changes must be signed by admin
- **Audit trail**: Complete history of configuration changes
- **No file management**: No config files to manage or version control
### First-Time Configuration
On first startup, the relay:
1. **Generates keypairs**: Creates cryptographically secure admin and relay keys
2. **Creates database**: `<relay_pubkey>.nrdb` file with optimized schema
3. **Stores default config**: Creates initial kind 33334 event with sensible defaults
4. **Displays admin key**: Shows admin private key once for you to save
### Updating Configuration
To change relay configuration, create and send a signed kind 33334 event:
#### Using nostrtool (recommended)
```bash
# Install nostrtool
npm install -g nostrtool
# Update relay description
nostrtool event \
--kind 33334 \
--content "C Nostr Relay Configuration" \
--tag d <relay_pubkey> \
--tag relay_description "My Production Relay" \
--tag max_subscriptions_per_client 50 \
--private-key <admin_private_key> \
| nostrtool send ws://localhost:8888
```
#### Manual Event Creation
```json
{
"kind": 33334,
"content": "C Nostr Relay Configuration",
"tags": [
["d", "<relay_pubkey>"],
["relay_description", "My Production Relay"],
["max_subscriptions_per_client", "50"],
["pow_min_difficulty", "20"]
],
"created_at": 1699123456,
"pubkey": "<admin_pubkey>",
"id": "<computed_event_id>",
"sig": "<signature>"
}
```
Send this to your relay via WebSocket, and changes are applied immediately.
### Configuration Parameters
#### Basic Settings
| Parameter | Description | Default | Example |
|-----------|-------------|---------|---------|
| `relay_description` | Relay description for NIP-11 | "C Nostr Relay" | "My awesome relay" |
| `relay_contact` | Admin contact information | "" | "admin@example.com" |
| `relay_software` | Software identifier | "c-relay" | "c-relay v1.0" |
#### Client Limits
| Parameter | Description | Default | Range |
|-----------|-------------|---------|-------|
| `max_subscriptions_per_client` | Max subscriptions per client | "25" | 1-100 |
| `max_total_subscriptions` | Total relay subscription limit | "5000" | 100-50000 |
| `max_message_length` | Maximum message size (bytes) | "65536" | 1024-1048576 |
| `max_event_tags` | Maximum tags per event | "2000" | 10-10000 |
| `max_content_length` | Maximum event content length | "65536" | 1-1048576 |
#### Proof of Work (NIP-13)
| Parameter | Description | Default | Options |
|-----------|-------------|---------|---------|
| `pow_min_difficulty` | Minimum PoW difficulty | "0" | 0-40 |
| `pow_mode` | PoW validation mode | "optional" | "disabled", "optional", "required" |
#### Event Expiration (NIP-40)
| Parameter | Description | Default | Options |
|-----------|-------------|---------|---------|
| `nip40_expiration_enabled` | Enable expiration handling | "true" | "true", "false" |
| `nip40_expiration_strict` | Strict expiration mode | "false" | "true", "false" |
| `nip40_expiration_filter` | Filter expired events | "true" | "true", "false" |
| `nip40_expiration_grace_period` | Grace period (seconds) | "300" | 0-86400 |
## Administration
### Viewing Current Configuration
```bash
# Find your database
ls -la *.nrdb
# View configuration events
sqlite3 <relay_pubkey>.nrdb "SELECT created_at, tags FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1;"
# View all configuration history
sqlite3 <relay_pubkey>.nrdb "SELECT datetime(created_at, 'unixepoch') as date, tags FROM events WHERE kind = 33334 ORDER BY created_at DESC;"
```
### Admin Key Management
#### Backup Admin Keys
```bash
# Create secure backup
echo "Admin Private Key: <your_admin_key>" > admin_keys_backup_$(date +%Y%m%d).txt
chmod 600 admin_keys_backup_*.txt
# Store in secure location (password manager, encrypted drive, etc.)
```
#### Key Recovery
If you lose your admin private key:
1. **Stop the relay**: `pkill c_relay` or `sudo systemctl stop c-relay`
2. **Backup events**: `cp <relay_pubkey>.nrdb backup_$(date +%Y%m%d).nrdb`
3. **Remove database**: `rm <relay_pubkey>.nrdb*`
4. **Restart relay**: This creates new database with new keys
5. **⚠️ Note**: All stored events and configuration history will be lost
### Security Best Practices
#### Admin Key Security
- **Never share** the admin private key
- **Store securely** in password manager or encrypted storage
- **Backup safely** to multiple secure locations
- **Monitor** configuration changes in logs
#### Network Security
```bash
# Restrict access with firewall
sudo ufw allow 8888/tcp
# Use reverse proxy for HTTPS (recommended)
# Configure nginx/apache to proxy to ws://localhost:8888
```
#### Database Security
```bash
# Secure database file permissions
chmod 600 <relay_pubkey>.nrdb
chown c-relay:c-relay <relay_pubkey>.nrdb
# Regular backups
cp <relay_pubkey>.nrdb backup/relay_backup_$(date +%Y%m%d_%H%M%S).nrdb
```
## Monitoring
### Service Status
```bash
# Check if relay is running
ps aux | grep c_relay
# SystemD status
sudo systemctl status c-relay
# Network connections
netstat -tln | grep 8888
sudo ss -tlpn | grep 8888
```
### Log Monitoring
```bash
# Real-time logs (systemd)
sudo journalctl -u c-relay -f
# Recent logs
sudo journalctl -u c-relay --since "1 hour ago"
# Error logs only
sudo journalctl -u c-relay -p err
# Configuration changes
sudo journalctl -u c-relay | grep "Configuration updated via kind 33334"
```
### Database Analytics
```bash
# Connect to database
sqlite3 <relay_pubkey>.nrdb
# Event statistics
SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;
# Recent activity
SELECT datetime(created_at, 'unixepoch') as date, kind, LENGTH(content) as content_size
FROM events
ORDER BY created_at DESC
LIMIT 10;
# Subscription analytics (if logging enabled)
SELECT * FROM subscription_analytics ORDER BY date DESC LIMIT 7;
# Configuration changes
SELECT datetime(created_at, 'unixepoch') as date, tags
FROM configuration_events
ORDER BY created_at DESC;
```
### Performance Monitoring
```bash
# Database size
du -sh <relay_pubkey>.nrdb*
# Memory usage
ps aux | grep c_relay | awk '{print $6}' # RSS memory in KB
# Connection count (approximate)
netstat -an | grep :8888 | grep ESTABLISHED | wc -l
# System resources
top -p $(pgrep c_relay)
```
## Troubleshooting
### Common Issues
#### Relay Won't Start
```bash
# Check port availability
netstat -tln | grep 8888
# If port in use, find process: sudo lsof -i :8888
# Check binary permissions
ls -la build/c_relay_x86
chmod +x build/c_relay_x86
# Check dependencies
ldd build/c_relay_x86
```
#### Configuration Not Updating
1. **Verify signature**: Ensure event is properly signed with admin private key
2. **Check admin pubkey**: Must match the pubkey from first startup
3. **Validate event structure**: Use `nostrtool validate` or similar
4. **Check logs**: Look for validation errors in relay logs
5. **Test WebSocket**: Ensure WebSocket connection is active
```bash
# Test WebSocket connection
wscat -c ws://localhost:8888
# Send test message
{"id":"test","method":"REQ","params":["test",{}]}
```
#### Database Issues
```bash
# Check database integrity
sqlite3 <relay_pubkey>.nrdb "PRAGMA integrity_check;"
# Check schema version
sqlite3 <relay_pubkey>.nrdb "SELECT * FROM schema_info WHERE key = 'version';"
# View database size and stats
sqlite3 <relay_pubkey>.nrdb "PRAGMA page_size; PRAGMA page_count;"
```
#### Performance Issues
```bash
# Analyze slow queries (if any)
sqlite3 <relay_pubkey>.nrdb "PRAGMA compile_options;"
# Check database optimization
sqlite3 <relay_pubkey>.nrdb "PRAGMA optimize;"
# Monitor system resources
iostat 1 5 # I/O statistics
free -h # Memory usage
```
### Recovery Procedures
#### Corrupted Database Recovery
```bash
# Attempt repair
sqlite3 <relay_pubkey>.nrdb ".recover" > recovered.sql
sqlite3 recovered.nrdb < recovered.sql
# If repair fails, start fresh (loses all events)
mv <relay_pubkey>.nrdb <relay_pubkey>.nrdb.corrupted
./build/c_relay_x86 # Creates new database
```
#### Lost Configuration Recovery
If configuration is lost but database is intact:
1. **Find old config**: `sqlite3 <relay_pubkey>.nrdb "SELECT * FROM configuration_events;"`
2. **Create new config event**: Use last known good configuration
3. **Sign and send**: Update with current timestamp and new signature
#### Emergency Restart
```bash
# Quick restart with clean state
sudo systemctl stop c-relay
mv <relay_pubkey>.nrdb <relay_pubkey>.nrdb.backup
sudo systemctl start c-relay
# Check logs for new admin keys
sudo journalctl -u c-relay --since "5 minutes ago" | grep "Admin Private Key"
```
## Advanced Usage
### Custom Event Handlers
The relay supports custom handling for different event types. Configuration changes trigger:
- **Subscription Manager Updates**: When client limits change
- **PoW System Reinitialization**: When PoW settings change
- **Expiration System Updates**: When NIP-40 settings change
- **Relay Info Updates**: When NIP-11 information changes
### API Integration
```javascript
// Connect and send configuration update
const ws = new WebSocket('ws://localhost:8888');
ws.on('open', function() {
const configEvent = {
kind: 33334,
content: "Updated configuration",
tags: [
["d", relayPubkey],
["relay_description", "Updated via API"]
],
created_at: Math.floor(Date.now() / 1000),
pubkey: adminPubkey,
// ... add id and sig
};
ws.send(JSON.stringify(["EVENT", configEvent]));
});
```
### Backup Strategies
#### Automated Backup
```bash
#!/bin/bash
# backup-relay.sh
DATE=$(date +%Y%m%d_%H%M%S)
DB_FILE=$(ls *.nrdb | head -1)
BACKUP_DIR="/backup/c-relay"
mkdir -p $BACKUP_DIR
cp $DB_FILE $BACKUP_DIR/relay_backup_$DATE.nrdb
gzip $BACKUP_DIR/relay_backup_$DATE.nrdb
# Cleanup old backups (keep 30 days)
find $BACKUP_DIR -name "relay_backup_*.nrdb.gz" -mtime +30 -delete
```
#### Configuration Export
```bash
# Export configuration events
sqlite3 <relay_pubkey>.nrdb "SELECT json_object(
'kind', kind,
'content', content,
'tags', json(tags),
'created_at', created_at,
'pubkey', pubkey,
'sig', sig
) FROM events WHERE kind = 33334 ORDER BY created_at;" > config_backup.json
```
### Migration Between Servers
```bash
# Source server
tar czf relay_migration.tar.gz *.nrdb* relay.log
# Target server
tar xzf relay_migration.tar.gz
./build/c_relay_x86 # Will detect existing database and continue
```
---
This user guide provides comprehensive coverage of the C Nostr Relay's event-based configuration system. For additional technical details, see the developer documentation in the `docs/` directory.