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:
@@ -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
421
docs/configuration_guide.md
Normal 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.
|
||||
94
docs/default_config_event_template.md
Normal file
94
docs/default_config_event_template.md
Normal 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
600
docs/deployment_guide.md
Normal 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.
|
||||
358
docs/event_based_config_implementation_plan.md
Normal file
358
docs/event_based_config_implementation_plan.md
Normal 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.
|
||||
@@ -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.
|
||||
128
docs/startup_config_analysis.md
Normal file
128
docs/startup_config_analysis.md
Normal 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.
|
||||
22
docs/startup_config_design.md
Normal file
22
docs/startup_config_design.md
Normal 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
507
docs/user_guide.md
Normal 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.
|
||||
Reference in New Issue
Block a user