diff --git a/CLI_PORT_OVERRIDE_IMPLEMENTATION.md b/CLI_PORT_OVERRIDE_IMPLEMENTATION.md new file mode 100644 index 0000000..9fb76ca --- /dev/null +++ b/CLI_PORT_OVERRIDE_IMPLEMENTATION.md @@ -0,0 +1,145 @@ +# CLI Port Override Implementation + +## Overview + +This document describes the implementation of the `-p ` command line option for the C Nostr Relay, which allows overriding the default relay port during first-time startup only. + +## Design Principles + +1. **First-time startup only**: Command line options only affect the initial configuration event creation +2. **Event-based persistence**: After first startup, all configuration is managed through database events +3. **Proper encapsulation**: All configuration logic is contained within `config.c` +4. **Extensible design**: The CLI options structure can easily accommodate future command line options + +## Implementation Details + +### Files Modified + +#### `src/config.h` +- Added `cli_options_t` structure to encapsulate command line options +- Updated `first_time_startup_sequence()` function signature + +#### `src/config.c` +- Updated `first_time_startup_sequence()` to accept CLI options parameter +- Updated `create_default_config_event()` to accept CLI options parameter +- Implemented port override logic in DEFAULT_CONFIG_VALUES array processing + +#### `src/default_config_event.h` +- Updated function signature for `create_default_config_event()` +- Added proper header include for `cli_options_t` definition + +#### `src/main.c` +- Added command line parsing for `-p ` and `--port ` +- Updated help text to document the new option +- Added proper error handling for invalid port values +- Updated function call to pass CLI options to configuration system + +### CLI Options Structure + +```c +typedef struct { + int port_override; // -1 = not set, >0 = port value + // Future CLI options can be added here +} cli_options_t; +``` + +### Command Line Usage + +```bash +# First-time startup with port override +./c_relay_x86 -p 9090 +./c_relay_x86 --port 9090 + +# Show help (includes new option) +./c_relay_x86 --help + +# Show version +./c_relay_x86 --version +``` + +### Error Handling + +The implementation includes robust error handling for: +- Missing port argument: `./c_relay_x86 -p` +- Invalid port format: `./c_relay_x86 -p invalid_port` +- Out-of-range ports: `./c_relay_x86 -p 0` or `./c_relay_x86 -p 99999` + +## Behavior Verification + +### First-Time Startup +When no database exists: +1. Command line is parsed and `-p ` is processed +2. CLI options are passed to `first_time_startup_sequence()` +3. Port override is applied in `create_default_config_event()` +4. Configuration event is created with overridden port value +5. Relay starts on the specified port +6. Port setting is persisted in database for future startups + +### Subsequent Startups +When database already exists: +1. Command line is still parsed (for consistency) +2. Existing relay path is taken +3. Configuration is loaded from database events +4. CLI options are ignored +5. Relay starts on port from database configuration + +## Testing Results + +### Test 1: First-time startup with port override +```bash +./c_relay_x86 -p 9090 +``` +**Result**: ✅ Relay starts on port 9090, configuration stored in database + +### Test 2: Subsequent startup ignores CLI options +```bash +./c_relay_x86 -p 7777 +``` +**Result**: ✅ Relay starts on port 9090 (from database), ignores `-p 7777` + +### Test 3: Error handling +```bash +./c_relay_x86 -p invalid_port +./c_relay_x86 -p +``` +**Result**: ✅ Proper error messages and help text displayed + +### Test 4: Help text +```bash +./c_relay_x86 --help +``` +**Result**: ✅ Displays updated help with `-p, --port PORT` option + +## Database Verification + +The port setting is correctly stored in the database: +```sql +SELECT json_extract(tags, '$') FROM events WHERE kind = 33334; +``` +Shows the overridden port value in the configuration event tags. + +## Future Extensions + +The `cli_options_t` structure is designed to be easily extended: + +```c +typedef struct { + int port_override; // -1 = not set, >0 = port value + char* description_override; // Future: relay description override + int max_connections_override; // Future: connection limit override + // Add more options as needed +} cli_options_t; +``` + +## Key Design Benefits + +1. **Separation of Concerns**: Main function handles CLI parsing, config system handles application +2. **First-time Only**: Prevents confusion about configuration precedence +3. **Event-based Architecture**: Maintains consistency with the relay's event-based configuration system +4. **Extensible**: Easy to add new command line options in the future +5. **Robust**: Comprehensive error handling and validation +6. **Documented**: Clear help text explains behavior to users + +## Summary + +The `-p ` command line option implementation successfully provides a way to override the default relay port during first-time startup while maintaining the event-based configuration architecture for ongoing operation. The implementation is robust, well-tested, and ready for production use. \ No newline at end of file diff --git a/fd8000ad015bc2e5e606a67c586cd8718a260a375890b5670b33e632f1d300b9.db b/fd8000ad015bc2e5e606a67c586cd8718a260a375890b5670b33e632f1d300b9.db new file mode 100644 index 0000000..889c240 Binary files /dev/null and b/fd8000ad015bc2e5e606a67c586cd8718a260a375890b5670b33e632f1d300b9.db differ diff --git a/relay.pid b/relay.pid index eda1436..78d8b0e 100644 --- a/relay.pid +++ b/relay.pid @@ -1 +1 @@ -1177520 +1187428 diff --git a/src/config.c b/src/config.c index 4651982..7a14f84 100644 --- a/src/config.c +++ b/src/config.c @@ -443,7 +443,8 @@ int generate_random_private_key_bytes(unsigned char* privkey_bytes) { cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes, const char* relay_privkey_hex, - const char* relay_pubkey_hex) { + const char* relay_pubkey_hex, + const cli_options_t* cli_options) { if (!admin_privkey_bytes || !relay_privkey_hex || !relay_pubkey_hex) { log_error("Invalid parameters for creating default config event"); return NULL; @@ -475,11 +476,28 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes, cJSON_AddItemToArray(relay_privkey_tag, cJSON_CreateString(relay_privkey_hex)); cJSON_AddItemToArray(tags, relay_privkey_tag); - // Add all default configuration values + // Add all default configuration values with command line overrides for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) { cJSON* tag = cJSON_CreateArray(); cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].key)); - cJSON_AddItemToArray(tag, cJSON_CreateString(DEFAULT_CONFIG_VALUES[i].value)); + + // Check for command line overrides + const char* value = DEFAULT_CONFIG_VALUES[i].value; + if (cli_options) { + // Override relay_port if specified on command line + if (cli_options->port_override > 0 && strcmp(DEFAULT_CONFIG_VALUES[i].key, "relay_port") == 0) { + char port_str[16]; + snprintf(port_str, sizeof(port_str), "%d", cli_options->port_override); + cJSON_AddItemToArray(tag, cJSON_CreateString(port_str)); + log_info("Using command line port override in configuration event"); + printf(" Port: %d (overriding default %s)\n", cli_options->port_override, DEFAULT_CONFIG_VALUES[i].value); + } else { + cJSON_AddItemToArray(tag, cJSON_CreateString(value)); + } + } else { + cJSON_AddItemToArray(tag, cJSON_CreateString(value)); + } + cJSON_AddItemToArray(tags, tag); } @@ -516,7 +534,7 @@ cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes, // IMPLEMENTED FUNCTIONS // ================================ -int first_time_startup_sequence(void) { +int first_time_startup_sequence(const cli_options_t* cli_options) { log_info("Starting first-time startup sequence..."); // 1. Generate admin keypair using /dev/urandom + nostr_core_lib @@ -566,7 +584,7 @@ int first_time_startup_sequence(void) { } // 5. Create initial configuration event using defaults - cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey); + cJSON* config_event = create_default_config_event(admin_privkey_bytes, relay_privkey, relay_pubkey, cli_options); if (!config_event) { log_error("Failed to create default configuration event"); return -1; diff --git a/src/config.h b/src/config.h index ffa528b..c35373e 100644 --- a/src/config.h +++ b/src/config.h @@ -32,6 +32,12 @@ typedef struct { char config_file_path[512]; // Temporary for compatibility } config_manager_t; +// Command line options structure for first-time startup +typedef struct { + int port_override; // -1 = not set, >0 = port value + // Future CLI options can be added here +} cli_options_t; + // Global configuration manager extern config_manager_t g_config_manager; @@ -62,7 +68,7 @@ int get_config_bool(const char* key, int default_value); // First-time startup functions int is_first_time_startup(void); -int first_time_startup_sequence(void); +int first_time_startup_sequence(const cli_options_t* cli_options); int startup_existing_relay(const char* relay_pubkey); // Configuration application functions diff --git a/src/default_config_event.h b/src/default_config_event.h index 6ca9afd..995f61b 100644 --- a/src/default_config_event.h +++ b/src/default_config_event.h @@ -2,6 +2,7 @@ #define DEFAULT_CONFIG_EVENT_H #include +#include "config.h" // For cli_options_t definition /* * Default Configuration Event Template @@ -61,8 +62,9 @@ static const struct { #define DEFAULT_CONFIG_COUNT (sizeof(DEFAULT_CONFIG_VALUES) / sizeof(DEFAULT_CONFIG_VALUES[0])) // Function to create default configuration event -cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes, +cJSON* create_default_config_event(const unsigned char* admin_privkey_bytes, const char* relay_privkey_hex, - const char* relay_pubkey_hex); + const char* relay_pubkey_hex, + const cli_options_t* cli_options); #endif /* DEFAULT_CONFIG_EVENT_H */ \ No newline at end of file diff --git a/src/main.c b/src/main.c index d3b3e41..28d70cd 100644 --- a/src/main.c +++ b/src/main.c @@ -3132,14 +3132,19 @@ void print_usage(const char* program_name) { printf("Options:\n"); printf(" -h, --help Show this help message\n"); printf(" -v, --version Show version information\n"); + printf(" -p, --port PORT Override relay port (first-time startup only)\n"); printf("\n"); printf("Configuration:\n"); printf(" This relay uses event-based configuration stored in the database.\n"); printf(" On first startup, keys are automatically generated and printed once.\n"); + printf(" Command line options like --port only apply during first-time setup.\n"); + printf(" After initial setup, all configuration is managed via database events.\n"); printf(" Database file: .db (created automatically)\n"); printf("\n"); printf("Examples:\n"); printf(" %s # Start relay (auto-configure on first run)\n", program_name); + printf(" %s -p 8080 # First-time setup with port 8080\n", program_name); + printf(" %s --port 9000 # First-time setup with port 9000\n", program_name); printf(" %s --help # Show this help\n", program_name); printf(" %s --version # Show version info\n", program_name); printf("\n"); @@ -3154,7 +3159,12 @@ void print_version() { } int main(int argc, char* argv[]) { - // Parse minimal command line arguments (no configuration overrides) + // Initialize CLI options structure + cli_options_t cli_options = { + .port_override = -1 // -1 = not set + }; + + // Parse command line arguments for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(argv[0]); @@ -3162,6 +3172,30 @@ int main(int argc, char* argv[]) { } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) { print_version(); return 0; + } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) { + // Port override option + if (i + 1 >= argc) { + log_error("Port option requires a value. Use --help for usage information."); + print_usage(argv[0]); + return 1; + } + + // Parse port number + char* endptr; + long port = strtol(argv[i + 1], &endptr, 10); + + if (endptr == argv[i + 1] || *endptr != '\0' || port < 1 || port > 65535) { + log_error("Invalid port number. Port must be between 1 and 65535."); + print_usage(argv[0]); + return 1; + } + + cli_options.port_override = (int)port; + i++; // Skip the port argument + + char port_msg[128]; + snprintf(port_msg, sizeof(port_msg), "Port override specified: %d", cli_options.port_override); + log_info(port_msg); } else { log_error("Unknown argument. Use --help for usage information."); print_usage(argv[0]); @@ -3194,7 +3228,7 @@ int main(int argc, char* argv[]) { } // Run first-time startup sequence (generates keys, creates database, etc.) - if (first_time_startup_sequence() != 0) { + if (first_time_startup_sequence(&cli_options) != 0) { log_error("Failed to complete first-time startup sequence"); cleanup_configuration_system(); nostr_cleanup();