v0.2.2 - Working on config setup
This commit is contained in:
493
docs/file_config_design.md
Normal file
493
docs/file_config_design.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user