Compare commits

...

1 Commits

Author SHA1 Message Date
Your Name
74a4dc2533 v0.3.2 - Implement -p/--port CLI option for first-time startup port override
- Add cli_options_t structure for extensible command line options
- Implement port override in create_default_config_event()
- Update main() with robust CLI parsing and validation
- Add comprehensive help text documenting first-time only behavior
- Ensure CLI options only affect initial configuration event creation
- Maintain event-based configuration architecture for ongoing operation
- Include comprehensive error handling and input validation
- Add documentation in CLI_PORT_OVERRIDE_IMPLEMENTATION.md

Tested: First-time startup uses CLI port, subsequent startups use database config
2025-09-07 06:54:56 -04:00
7 changed files with 216 additions and 11 deletions

View File

@@ -0,0 +1,145 @@
# CLI Port Override Implementation
## Overview
This document describes the implementation of the `-p <port>` 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 <port>` and `--port <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 <port>` 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 <port>` 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.

View File

@@ -1 +1 @@
1177520
1187428

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
#define DEFAULT_CONFIG_EVENT_H
#include <cjson/cJSON.h>
#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 */

View File

@@ -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: <relay_pubkey>.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();