Files
c-relay/docs/event_based_config_implementation_plan.md

13 KiB

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

// 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

// 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

// 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

// 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

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

// 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

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

// 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

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.