493 lines
14 KiB
Markdown
493 lines
14 KiB
Markdown
# 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://github.com/laantungir/c-relay"],
|
|
["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. |