# 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.