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:
- ✅ Rename
src/config.ctosrc/config.c.old- DONE - ✅ Rename
src/config.htosrc/config.h.old- DONE - ✅ Create new empty
src/config.candsrc/config.h- DONE - ✅ 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.candsrc/config.hfiles - ✅ Rename old files to
src/config.c.oldandsrc/config.h.old - ✅ Modify
init_database()to use relay pubkey for database naming - ✅ Use
nostr_core_libfunctions 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.hfor default configuration values - ✅ Add functions to store/retrieve kind 33334 events from events table
- ✅ Use
nostr_core_libfunctions 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-pathhandling 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
- ✅ Implement database naming with relay pubkey
- ✅ Add key generation functions using
nostr_core_lib - ✅ Create configuration event storage/retrieval functions
- ✅ Test basic event creation and storage
Step 2: Event Processing Integration ✅ MOSTLY COMPLETED
- ✅ Add kind 33334 event detection to event processing loop
- ✅ Implement configuration event validation
- ⚠️ Create configuration application handlers (basic access implemented, runtime handlers pending)
- ⏳ Test real-time configuration updates (infrastructure ready)
Step 3: First-Time Startup ✅ COMPLETED
- ✅ Implement first-time startup detection
- ✅ Add automatic key generation and database creation
- ✅ Create default configuration event generation
- ✅ Test complete first-time startup flow
Step 4: Legacy Removal ⚠️ MOSTLY COMPLETED
- ✅ Remove command line argument parsing
- ✅ Remove configuration file system
- ⏳ Remove legacy database tables (pending)
- ✅ Update all references to use event-based config
Step 5: Testing and Validation ⚠️ PARTIALLY COMPLETED
- ✅ Test complete startup flow (first time and existing)
- ⏳ Test configuration updates via events (infrastructure ready)
- ⚠️ Test error handling and recovery (basic error handling implemented)
- ⏳ Performance testing and optimization (pending)
Migration Strategy
For Existing Installations
Since the new system uses a completely different approach:
- No Automatic Migration: The new system starts fresh
- Manual Migration: Users can manually copy configuration values
- Documentation: Provide clear migration instructions
- Coexistence: Old and new systems use different database names
Migration Steps for Users
- Stop existing relay
- Note current configuration values
- Start new relay (generates keys and new database)
- Create kind 33334 event with desired configuration using admin private key
- 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
- Admin Private Key: Never stored, only printed once
- Event Validation: All configuration events must be signed by admin
- Database Security: Relay database contains relay private key
- Key Generation: Use
nostr_core_libfor cryptographically secure generation
Files to Modify
Major Changes
src/main.c- Startup logic, event processing, argument removalsrc/config.c- Complete rewrite for event-based configurationsrc/config.h- Update function signatures and structuressrc/sql_schema.h- Remove config tables
Minor Changes
Makefile- Remove any config file generationsystemd/- 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
- ✅ Zero Command Line Arguments: Relay starts with just
./c-relay - ✅ Automatic First-Time Setup: Generates keys and database automatically
- ⚠️ Real-Time Configuration: Infrastructure ready, handlers need completion
- ✅ Single Database File: All configuration and data in one
.nrdbfile - ⚠️ Admin Control: Event processing implemented, signature validation ready
- ⚠️ Clean Codebase: Most legacy code removed, database tables cleanup pending
Risk Mitigation
- Backup Strategy: Document manual backup procedures for relay database
- Key Loss Recovery: Document recovery procedures if admin key is lost
- Testing Coverage: Comprehensive test suite before deployment
- Rollback Plan: Keep old version available during transition period
- 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.