From f3d6afead1d19868123b3b41e4aea8e56adbcd82 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Sep 2025 08:49:09 -0400 Subject: [PATCH] v0.3.5 - nip42 implemented --- .roo/architect/AGENTS.md | 298 +++++ 40.md | 59 + 42.md | 109 ++ AGENTS.md | 142 ++ Makefile | 2 +- README.md | 320 +---- ...7bfb6b4197013249f3b822f2cdd1f51591cf1e6.db | 0 db/README.md | 1 - docs/NIP-42_Authentication.md | 295 +++++ get_settings.sh | 19 + make_and_restart_relay.sh | 47 +- relay.pid | 2 +- src/config.c | 228 +++- src/config.h | 8 +- src/default_config_event.h | 6 + src/main.c | 704 +++++++--- src/request_validator.c | 1174 +++++++++++++++++ src/sql_schema.h | 27 +- test_event.json | 1 + test_key.txt | 1 + tests/40_nip_test.sh | 108 +- tests/42_nip_test.sh | 477 +++++++ tests/malformed_expiration_test.sh | 116 ++ tests/nip42_test.log | 93 ++ 24 files changed, 3705 insertions(+), 532 deletions(-) create mode 100644 .roo/architect/AGENTS.md create mode 100644 40.md create mode 100644 42.md create mode 100644 AGENTS.md delete mode 100644 a3aa7add56a3d4f742c904c2b7bfb6b4197013249f3b822f2cdd1f51591cf1e6.db delete mode 100644 db/README.md create mode 100644 docs/NIP-42_Authentication.md create mode 100755 get_settings.sh create mode 100644 src/request_validator.c create mode 100644 test_event.json create mode 100644 test_key.txt create mode 100755 tests/42_nip_test.sh create mode 100755 tests/malformed_expiration_test.sh create mode 100644 tests/nip42_test.log diff --git a/.roo/architect/AGENTS.md b/.roo/architect/AGENTS.md new file mode 100644 index 0000000..ff414de --- /dev/null +++ b/.roo/architect/AGENTS.md @@ -0,0 +1,298 @@ + +# AGENTS.md - AI Agent Integration Guide for Architect Mode + +**Project-Specific Information for AI Agents Working with C-Relay in Architect Mode** + +## Critical Architecture Understanding + +### System Architecture Overview +C-Relay implements a **unique event-based configuration architecture** that fundamentally differs from traditional Nostr relays: + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ WebSocket │ │ Configuration │ │ Database │ +│ + HTTP │◄──►│ Event System │◄──►│ (SQLite) │ +│ (Port 8888) │ │ (Kind 33334) │ │ Schema v4 │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ nostr_core_lib │ │ Admin Key │ │ Event Storage │ +│ (Crypto/Sigs) │ │ Management │ │ + Subscriptions │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ +``` + +### Core Architectural Principles + +#### 1. Event-Driven Configuration +**Design Philosophy**: Configuration as cryptographically signed events rather than files +- **Benefits**: Auditability, remote management, tamper-evidence +- **Trade-offs**: Complexity in configuration changes, admin key management burden +- **Implementation**: Kind 33334 events stored in same database as relay events + +#### 2. Identity-Based Database Naming +**Design Philosophy**: Database file named by relay's generated public key +- **Benefits**: Prevents database conflicts, enables multi-relay deployments +- **Trade-offs**: Cannot predict database filename, complicates backup strategies +- **Implementation**: `.db` created in build/ directory + +#### 3. Single-Binary Deployment +**Design Philosophy**: All functionality embedded in one executable +- **Benefits**: Simple deployment, no external dependencies to manage +- **Trade-offs**: Larger binary size, harder to modularize +- **Implementation**: SQL schema embedded as header file, nostr_core_lib as submodule + +#### 4. Dual-Protocol Support +**Design Philosophy**: WebSocket (Nostr) and HTTP (NIP-11) on same port +- **Benefits**: Simplified port management, reduced infrastructure complexity +- **Trade-offs**: Protocol detection overhead, libwebsockets dependency +- **Implementation**: Request routing based on HTTP headers and upgrade requests + +## Architectural Decision Analysis + +### Configuration System Design +**Traditional Approach vs C-Relay:** +``` +Traditional: C-Relay: +config.json → kind 33334 events +ENV variables → cryptographically signed tags +File watching → database polling/restart +``` + +**Implications for Extensions:** +- Configuration changes require event signing capabilities +- No hot-reloading without architectural changes +- Admin key loss = complete database reset required + +### Database Architecture Decisions +**Schema Design Philosophy:** +- **Event Tags as JSON**: Separate table with JSON column instead of normalized relations +- **Application-Level Filtering**: NIP-40 expiration handled in C, not SQL +- **Embedded Schema**: Version 4 schema compiled into binary + +**Scaling Considerations:** +- SQLite suitable for small-to-medium relays (< 10k concurrent connections) +- Single-writer limitation of SQLite affects write-heavy workloads +- JSON tag storage optimizes for read performance over write normalization + +### Memory Management Architecture +**Thread Safety Model:** +- Global subscription manager with mutex protection +- Per-client subscription limits enforced in memory +- WebSocket connection state managed by libwebsockets + +**Resource Management:** +- JSON objects use reference counting (jansson library) +- String duplication pattern for configuration values +- Automatic cleanup on client disconnect + +## Architectural Extension Points + +### Adding New Configuration Options +**Required Changes:** +1. Update [`default_config_event.h`](src/default_config_event.h) template +2. Add parsing logic in [`config.c`](src/config.c) `load_config_from_database()` +3. Add global config struct field in [`config.h`](src/config.h) +4. Update documentation in [`docs/configuration_guide.md`](docs/configuration_guide.md) + +### Adding New NIP Support +**Integration Pattern:** +1. Event validation in [`request_validator.c`](src/request_validator.c) +2. Protocol handling in [`main.c`](src/main.c) WebSocket callback +3. Database storage considerations in schema +4. Add test in `tests/` directory + +### Scaling Architecture +**Current Limitations:** +- Single process, no horizontal scaling +- SQLite single-writer bottleneck +- Memory-based subscription management + +**Potential Extensions:** +- Redis for subscription state sharing +- PostgreSQL for better concurrent write performance +- Load balancer for read scaling with multiple instances + +## Deployment Architecture Patterns + +### Development Deployment +``` +Developer Machine: +├── ./make_and_restart_relay.sh +├── build/c_relay_x86 +├── build/.db +└── relay.log +``` + +### Production SystemD Deployment +``` +/opt/c-relay/: +├── c_relay_x86 +├── .db +├── systemd service (c-relay.service) +└── c-relay user isolation +``` + +### Container Deployment Architecture +``` +Container: +├── Multi-stage build (deps + binary) +├── Volume mount for database persistence +├── Health checks via NIP-11 endpoint +└── Signal handling for graceful shutdown +``` + +### Reverse Proxy Architecture +``` +Internet → Nginx/HAProxy → C-Relay + ├── WebSocket upgrade handling + ├── SSL termination + └── Rate limiting +``` + +## Security Architecture Considerations + +### Key Management Design +**Admin Key Security Model:** +- Generated once, displayed once, never stored +- Required for all configuration changes +- Loss requires complete database reset + +**Relay Identity Model:** +- Separate keypair for relay identity +- Public key used for database naming +- Private key never exposed to clients + +### Event Validation Pipeline +``` +WebSocket Input → JSON Parse → Schema Validate → Signature Verify → Store + ↓ ↓ ↓ + reject reject reject success +``` + +### Attack Surface Analysis +**Network Attack Vectors:** +- WebSocket connection flooding (mitigated by libwebsockets limits) +- JSON parsing attacks (handled by jansson library bounds checking) +- SQLite injection (prevented by prepared statements) + +**Configuration Attack Vectors:** +- Admin key compromise (complete relay control) +- Event signature forgery (prevented by nostr_core_lib validation) +- Replay attacks (event timestamp validation required) + +## Non-Obvious Architectural Considerations + +### Database Evolution Strategy +**Current Limitations:** +- Schema changes require database recreation +- No migration system for configuration events +- Version 4 schema embedded in binary + +**Future Architecture Needs:** +- Schema versioning and migration system +- Backward compatibility for configuration events +- Database backup/restore procedures + +### Configuration Event Lifecycle +**Event Flow:** +``` +Admin Signs Event → WebSocket Submit → Validate → Store → Restart Required + ↓ ↓ ↓ + Signature Check Database Config Reload +``` + +**Architectural Implications:** +- No hot configuration reloading +- Configuration changes require planned downtime +- Event ordering matters for multiple simultaneous changes + +### Cross-Architecture Deployment +**Build System Architecture:** +- Auto-detection of host architecture +- Cross-compilation support for ARM64 +- Architecture-specific binary outputs + +**Deployment Implications:** +- Binary must match target architecture +- Dependencies must be available for target architecture +- Debug tooling architecture-specific + +### Performance Architecture Characteristics +**Bottlenecks:** +1. **SQLite Write Performance**: Single writer limitation +2. **JSON Parsing**: Per-event parsing overhead +3. **Signature Validation**: Cryptographic operations per event +4. **Memory Management**: JSON object lifecycle management + +**Optimization Points:** +- Prepared statement reuse +- Connection pooling for concurrent reads +- Event batching for bulk operations +- Subscription indexing strategies + +### Integration Architecture Patterns +**Monitoring Integration:** +- NIP-11 endpoint for health checks +- Log file monitoring for operational metrics +- Database query monitoring for performance +- Process monitoring for resource usage + +**Backup Architecture:** +- Database file backup (SQLite file copy) +- Configuration event export/import +- Admin key secure storage (external to relay) + +### Future Extension Architectures +**Multi-Relay Coordination:** +- Database sharding by event kind +- Cross-relay event synchronization +- Distributed configuration management + +**Plugin Architecture Possibilities:** +- Event processing pipeline hooks +- Custom validation plugins +- External authentication providers + +**Scaling Architecture Options:** +- Read replicas with PostgreSQL migration +- Event stream processing with message queues +- Microservice decomposition (auth, storage, validation) + +## Architectural Anti-Patterns to Avoid + +1. **Configuration File Addition**: Breaks event-based config paradigm +2. **Direct Database Modification**: Bypasses signature validation +3. **Hard-Coded Ports**: Conflicts with auto-fallback system +4. **Schema Modifications**: Requires database recreation +5. **Admin Key Storage**: Violates security model +6. **Blocking Operations**: Interferes with WebSocket event loop +7. **Memory Leaks**: JSON objects must be properly reference counted +8. **Thread Unsafe Operations**: Global state requires proper synchronization + +## Architecture Decision Records (Implicit) + +### Decision: Event-Based Configuration +**Context**: Traditional config files vs. cryptographic auditability +**Decision**: Store configuration as signed Nostr events +**Consequences**: Complex configuration changes, enhanced security, remote management capability + +### Decision: SQLite Database +**Context**: Database choice for relay storage +**Decision**: Embedded SQLite with JSON tag storage +**Consequences**: Simple deployment, single-writer limitation, application-level filtering + +### Decision: Single Binary Deployment +**Context**: Dependency management vs. deployment simplicity +**Decision**: Embed all dependencies and schema in binary +**Consequences**: Larger binary, simple deployment, version coupling + +### Decision: Dual Protocol Support +**Context**: WebSocket for Nostr, HTTP for NIP-11 +**Decision**: Same port serves both protocols +**Consequences**: Simplified deployment, protocol detection overhead, libwebsockets dependency + +These architectural decisions form the foundation of C-Relay's unique approach to Nostr relay implementation and should be carefully considered when planning extensions or modifications. +** + +[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.] \ No newline at end of file diff --git a/40.md b/40.md new file mode 100644 index 0000000..909747f --- /dev/null +++ b/40.md @@ -0,0 +1,59 @@ +NIP-40 +====== + +Expiration Timestamp +-------------------- + +`draft` `optional` + +The `expiration` tag enables users to specify a unix timestamp at which the message SHOULD be considered expired (by relays and clients) and SHOULD be deleted by relays. + +#### Spec + +``` +tag: expiration +values: + - [UNIX timestamp in seconds]: required +``` + +#### Example + +```json +{ + "pubkey": "", + "created_at": 1000000000, + "kind": 1, + "tags": [ + ["expiration", "1600000000"] + ], + "content": "This message will expire at the specified timestamp and be deleted by relays.\n", + "id": "" +} +``` + +Note: The timestamp should be in the same format as the created_at timestamp and should be interpreted as the time at which the message should be deleted by relays. + +Client Behavior +--------------- + +Clients SHOULD use the `supported_nips` field to learn if a relay supports this NIP. Clients SHOULD NOT send expiration events to relays that do not support this NIP. + +Clients SHOULD ignore events that have expired. + +Relay Behavior +-------------- + +Relays MAY NOT delete expired messages immediately on expiration and MAY persist them indefinitely. +Relays SHOULD NOT send expired events to clients, even if they are stored. +Relays SHOULD drop any events that are published to them if they are expired. +An expiration timestamp does not affect storage of ephemeral events. + +Suggested Use Cases +------------------- + +* Temporary announcements - This tag can be used to make temporary announcements. For example, an event organizer could use this tag to post announcements about an upcoming event. +* Limited-time offers - This tag can be used by businesses to make limited-time offers that expire after a certain amount of time. For example, a business could use this tag to make a special offer that is only available for a limited time. + +#### Warning +The events could be downloaded by third parties as they are publicly accessible all the time on the relays. +So don't consider expiring messages as a security feature for your conversations or other uses. diff --git a/42.md b/42.md new file mode 100644 index 0000000..24f7f5d --- /dev/null +++ b/42.md @@ -0,0 +1,109 @@ +NIP-42 +====== + +Authentication of clients to relays +----------------------------------- + +`draft` `optional` + +This NIP defines a way for clients to authenticate to relays by signing an ephemeral event. + +## Motivation + +A relay may want to require clients to authenticate to access restricted resources. For example, + + - A relay may request payment or other forms of whitelisting to publish events -- this can naïvely be achieved by limiting publication to events signed by the whitelisted key, but with this NIP they may choose to accept any events as long as they are published from an authenticated user; + - A relay may limit access to `kind: 4` DMs to only the parties involved in the chat exchange, and for that it may require authentication before clients can query for that kind. + - A relay may limit subscriptions of any kind to paying users or users whitelisted through any other means, and require authentication. + +## Definitions + +### New client-relay protocol messages + +This NIP defines a new message, `AUTH`, which relays CAN send when they support authentication and clients can send to relays when they want to authenticate. When sent by relays the message has the following form: + +``` +["AUTH", ] +``` + +And, when sent by clients, the following form: + +``` +["AUTH", ] +``` + +Clients MAY provide signed events from multiple pubkeys in a sequence of `AUTH` messages. Relays MUST treat all pubkeys as authenticated accordingly. + +`AUTH` messages sent by clients MUST be answered with an `OK` message, like any `EVENT` message. + +### Canonical authentication event + +The signed event is an ephemeral event not meant to be published or queried, it must be of `kind: 22242` and it should have at least two tags, one for the relay URL and one for the challenge string as received from the relay. Relays MUST exclude `kind: 22242` events from being broadcasted to any client. `created_at` should be the current time. Example: + +```jsonc +{ + "kind": 22242, + "tags": [ + ["relay", "wss://relay.example.com/"], + ["challenge", "challengestringhere"] + ], + // other fields... +} +``` + +### `OK` and `CLOSED` machine-readable prefixes + +This NIP defines two new prefixes that can be used in `OK` (in response to event writes by clients) and `CLOSED` (in response to rejected subscriptions by clients): + +- `"auth-required: "` - for when a client has not performed `AUTH` and the relay requires that to fulfill the query or write the event. +- `"restricted: "` - for when a client has already performed `AUTH` but the key used to perform it is still not allowed by the relay or is exceeding its authorization. + +## Protocol flow + +At any moment the relay may send an `AUTH` message to the client containing a challenge. The challenge is valid for the duration of the connection or until another challenge is sent by the relay. The client MAY decide to send its `AUTH` event at any point and the authenticated session is valid afterwards for the duration of the connection. + +### `auth-required` in response to a `REQ` message + +Given that a relay is likely to require clients to perform authentication only for certain jobs, like answering a `REQ` or accepting an `EVENT` write, these are some expected common flows: + +``` +relay: ["AUTH", ""] +client: ["REQ", "sub_1", {"kinds": [4]}] +relay: ["CLOSED", "sub_1", "auth-required: we can't serve DMs to unauthenticated users"] +client: ["AUTH", {"id": "abcdef...", ...}] +client: ["AUTH", {"id": "abcde2...", ...}] +relay: ["OK", "abcdef...", true, ""] +relay: ["OK", "abcde2...", true, ""] +client: ["REQ", "sub_1", {"kinds": [4]}] +relay: ["EVENT", "sub_1", {...}] +relay: ["EVENT", "sub_1", {...}] +relay: ["EVENT", "sub_1", {...}] +relay: ["EVENT", "sub_1", {...}] +... +``` + +In this case, the `AUTH` message from the relay could be sent right as the client connects or it can be sent immediately before the `CLOSED` is sent. The only requirement is that _the client must have a stored challenge associated with that relay_ so it can act upon that in response to the `auth-required` `CLOSED` message. + +### `auth-required` in response to an `EVENT` message + +The same flow is valid for when a client wants to write an `EVENT` to the relay, except now the relay sends back an `OK` message instead of a `CLOSED` message: + +``` +relay: ["AUTH", ""] +client: ["EVENT", {"id": "012345...", ...}] +relay: ["OK", "012345...", false, "auth-required: we only accept events from registered users"] +client: ["AUTH", {"id": "abcdef...", ...}] +relay: ["OK", "abcdef...", true, ""] +client: ["EVENT", {"id": "012345...", ...}] +relay: ["OK", "012345...", true, ""] +``` + +## Signed Event Verification + +To verify `AUTH` messages, relays must ensure: + + - that the `kind` is `22242`; + - that the event `created_at` is close (e.g. within ~10 minutes) of the current time; + - that the `"challenge"` tag matches the challenge sent before; + - that the `"relay"` tag matches the relay URL: + - URL normalization techniques can be applied. For most cases just checking if the domain name is correct should be enough. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fcd4dcb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,142 @@ +# AGENTS.md - AI Agent Integration Guide + +**Project-Specific Information for AI Agents Working with C-Relay** + +## Critical Build Commands + +### Primary Build Command +```bash +./make_and_restart_relay.sh +``` +**Never use `make` directly.** The project requires the custom restart script which: +- Handles database preservation/cleanup based on flags +- Manages architecture-specific binary detection (x86/ARM64) +- Performs automatic process cleanup and port management +- Starts relay in background with proper logging + +### Architecture-Specific Binary Outputs +- **x86_64**: `./build/c_relay_x86` +- **ARM64**: `./build/c_relay_arm64` +- **Other**: `./build/c_relay_$(ARCH)` + +### Database File Naming Convention +- **Format**: `.db` (NOT `.nrdb` as shown in docs) +- **Location**: Created in `build/` directory during execution +- **Cleanup**: Use `--preserve-database` flag to retain between builds + +## Critical Integration Issues + +### Event-Based Configuration System +- **No traditional config files** - all configuration stored as kind 33334 Nostr events +- Admin private key shown **only once** on first startup +- Configuration changes require cryptographically signed events +- Database path determined by generated relay pubkey + +### First-Time Startup Sequence +1. Relay generates admin keypair and relay keypair +2. Creates database file with relay pubkey as filename +3. Stores default configuration as kind 33334 event +4. **CRITICAL**: Admin private key displayed once and never stored on disk + +### Port Management +- Default port 8888 with automatic fallback (8889, 8890, etc.) +- Script performs port availability checking before libwebsockets binding +- Process cleanup includes force-killing processes on port 8888 + +### Database Schema Dependencies +- Uses embedded SQL schema (`sql_schema.h`) +- Schema version 4 with JSON tag storage +- **Critical**: Event expiration filtering done at application level, not SQL level + +### Configuration Event Structure +```json +{ + "kind": 33334, + "content": "C Nostr Relay Configuration", + "tags": [ + ["d", ""], + ["relay_description", "value"], + ["max_subscriptions_per_client", "25"], + ["pow_min_difficulty", "16"] + ] +} +``` + +### Process Management +```bash +# Kill existing relay processes +pkill -f "c_relay_" + +# Check running processes +ps aux | grep c_relay_ + +# Force kill port binding +fuser -k 8888/tcp +``` + +### Cross-Compilation Specifics +- ARM64 requires explicit dependency installation: `make install-arm64-deps` +- Uses `aarch64-linux-gnu-gcc` with specific library paths +- PKG_CONFIG_PATH must be set for ARM64: `/usr/lib/aarch64-linux-gnu/pkgconfig` + +### Testing Integration +- Tests expect relay running on default port +- Use `tests/quick_error_tests.sh` for validation +- Event configuration tests: `tests/event_config_tests.sh` + +### SystemD Integration Considerations +- Service runs as `c-relay` user in `/opt/c-relay` +- Database files created in WorkingDirectory automatically +- No environment variables needed (event-based config) +- Resource limits: 65536 file descriptors, 4096 processes + +### Development vs Production Differences +- Development: `make_and_restart_relay.sh` (default database cleanup) +- Production: `make_and_restart_relay.sh --preserve-database` +- Debug build requires manual gdb attachment to architecture-specific binary + +### Critical File Dependencies +- `nostr_core_lib/` submodule must be initialized and built first +- Version header auto-generated from git tags: `src/version.h` +- Schema embedded in binary from `src/sql_schema.h` + +### WebSocket Protocol Specifics +- Supports both WebSocket (Nostr protocol) and HTTP (NIP-11) +- NIP-11 requires `Accept: application/nostr+json` header +- CORS headers automatically added for NIP-11 compliance + +### Memory Management Notes +- Persistent subscription system with thread-safe global manager +- Per-session subscription limits enforced +- Event filtering done at C level, not SQL level for NIP-40 expiration + +### Configuration Override Behavior +- CLI port override only affects first-time startup +- After database creation, all config comes from events +- Database path cannot be changed after initialization + +## Non-Obvious Pitfalls + +1. **Database Lock Issues**: Script handles SQLite locking by killing existing processes first +2. **Port Race Conditions**: Pre-check + libwebsockets binding can still fail due to timing +3. **Key Loss**: Admin private key loss requires complete database deletion and restart +4. **Architecture Detection**: Build system auto-detects but cross-compilation requires manual setup +5. **Event Storage**: Ephemeral events (kind 20000-29999) accepted but not stored +6. **Signature Validation**: All events validated with `nostr_verify_event_signature()` from nostr_core_lib + +## Quick Debugging Commands +```bash +# Check relay status +ps aux | grep c_relay_ && netstat -tln | grep 8888 + +# View logs +tail -f relay.log + +# Test WebSocket connection +wscat -c ws://localhost:8888 + +# Test NIP-11 endpoint +curl -H "Accept: application/nostr+json" http://localhost:8888 + +# Find database files +find . -name "*.db" -type f \ No newline at end of file diff --git a/Makefile b/Makefile index e5b79f9..6a883c6 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ LIBS = -lsqlite3 -lwebsockets -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k BUILD_DIR = build # Source files -MAIN_SRC = src/main.c src/config.c +MAIN_SRC = src/main.c src/config.c src/request_validator.c NOSTR_CORE_LIB = nostr_core_lib/libnostr_core_x64.a # Architecture detection diff --git a/README.md b/README.md index c728d6a..d519b12 100644 --- a/README.md +++ b/README.md @@ -2,265 +2,6 @@ A high-performance Nostr relay implemented in C with SQLite backend, featuring a revolutionary **zero-configuration** approach using event-based configuration management. -## 🌟 Key Features - -- **🔧 Zero Configuration**: No config files or command line arguments needed -- **🔑 Event-Based Config**: All settings stored as kind 33334 Nostr events -- **🚀 Real-Time Updates**: Configuration changes applied instantly via WebSocket -- **🛡️ Cryptographic Security**: Configuration events cryptographically signed and validated -- **📊 SQLite Backend**: High-performance event storage with optimized schema -- **🔄 Auto Key Generation**: Secure admin and relay keypairs generated on first startup -- **💾 Database Per Relay**: Each relay instance uses `.nrdb` database naming - -## 🚀 Quick Start - -### 1. Build the Relay -```bash -git clone -cd c-relay -git submodule update --init --recursive -make -``` - -### 2. Start the Relay -```bash -./build/c_relay_x86 -``` - -**That's it!** No configuration files, no command line arguments needed. - -### 3. Save Your Admin Keys (IMPORTANT!) -On first startup, the relay will display: - -``` -================================================================= -IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY! -================================================================= -Admin Private Key: f8491814ea288260dad2ab52c09b3b037e75e83e8b24feb9bdc328423922be44 -Admin Public Key: 07fc2cdd8bdc0c60eefcc9e37e67fef88206bc84fadb894c283b006554ac687b - -Relay Private Key: a1b2c3d4e5f6... -Relay Public Key: 1a2b3c4d5e6f... - -Database: dc9a93fd0ffba7041f6df0602e5021913a42fcaf6dbf40f43ecdc011177b4d94.nrdb -================================================================= -``` - -⚠️ **Save the admin private key securely** - it's needed to update relay configuration and is only displayed once! - -## 📋 System Requirements - -- **OS**: Linux, macOS, or Windows (WSL) -- **Dependencies**: - - SQLite 3 - - libwebsockets - - OpenSSL/LibreSSL - - libsecp256k1 - - libcurl - - zlib - -## 🏗️ Event-Based Configuration System - -### How It Works - -Traditional Nostr relays require configuration files, environment variables, or command line arguments. This relay uses a **revolutionary approach**: - -1. **First-Time Startup**: Generates cryptographically secure admin and relay keypairs -2. **Database Creation**: Creates `.nrdb` database file -3. **Default Configuration**: Creates initial kind 33334 configuration event with sensible defaults -4. **Real-Time Updates**: Administrators send new kind 33334 events to update configuration -5. **Instant Application**: Changes are applied immediately without restart - -### Configuration Updates - -To update relay configuration, send a signed kind 33334 event: - -```json -{ - "kind": 33334, - "content": "C Nostr Relay Configuration", - "tags": [ - ["d", ""], - ["relay_description", "My awesome Nostr relay"], - ["max_subscriptions_per_client", "25"], - ["pow_min_difficulty", "16"], - ["nip40_expiration_enabled", "true"] - ], - "created_at": 1234567890, - "pubkey": "", - "id": "...", - "sig": "..." -} -``` - -Send this event to your relay via WebSocket, and changes are applied instantly. - -### Configurable Parameters - -| Parameter | Description | Default | -|-----------|-------------|---------| -| `relay_description` | Relay description (NIP-11) | "C Nostr Relay" | -| `relay_contact` | Admin contact info | "" | -| `max_subscriptions_per_client` | Max subscriptions per client | "25" | -| `max_total_subscriptions` | Total subscription limit | "5000" | -| `pow_min_difficulty` | NIP-13 PoW difficulty | "0" | -| `pow_mode` | PoW validation mode | "optional" | -| `nip40_expiration_enabled` | Enable NIP-40 expiration | "true" | -| `nip40_expiration_strict` | Strict expiration mode | "false" | -| `max_message_length` | Max message size | "65536" | -| `max_event_tags` | Max tags per event | "2000" | -| `max_content_length` | Max content length | "65536" | - -## 🔧 Deployment - -### Manual Installation -```bash -# Build the relay -make - -# Run directly -./build/c_relay_x86 -``` - -### SystemD Service (Recommended) -```bash -# Install as system service -sudo systemd/install-service.sh - -# Start the service -sudo systemctl start c-relay - -# Enable auto-start on boot -sudo systemctl enable c-relay - -# View logs -sudo journalctl -u c-relay -f -``` - -See [`systemd/README.md`](systemd/README.md) for detailed deployment documentation. - -### Docker (Coming Soon) -Docker support is planned for future releases. - -## 📊 Database Schema - -The relay uses an optimized SQLite schema (version 4) with these key features: - -- **Event-based storage**: All Nostr events in single `events` table -- **JSON tags support**: Native JSON storage for event tags -- **Performance optimized**: Multiple indexes for fast queries -- **Subscription logging**: Optional detailed subscription analytics -- **Auto-cleanup**: Automatic ephemeral event cleanup -- **Replaceable events**: Proper handling of replaceable/addressable events - -## 🛡️ Security Features - -- **Cryptographic validation**: All configuration events cryptographically verified -- **Admin-only config**: Only authorized admin pubkey can update configuration -- **Signature verification**: Uses `nostr_verify_event_signature()` for validation -- **Event structure validation**: Complete event structure validation -- **Secure key generation**: Uses `/dev/urandom` for cryptographically secure keys -- **No secrets storage**: Admin private key never stored on disk - -## 🔌 Network Configuration - -- **Default Port**: 8888 (WebSocket) -- **Protocol**: WebSocket with Nostr message format -- **Endpoints**: - - `ws://localhost:8888` - WebSocket relay - - `http://localhost:8888` - NIP-11 relay information (HTTP GET) - -## 🏃‍♂️ Usage Examples - -### Connect with a Nostr Client -```javascript -const relay = new WebSocket('ws://localhost:8888'); -relay.send(JSON.stringify(["REQ", "sub1", {"kinds": [1], "limit": 10}])); -``` - -### Update Configuration (using `nostrtool` or similar) -```bash -# Create configuration event with nostrtool -nostrtool event --kind 33334 --content "Updated config" \ - --tag d \ - --tag relay_description "My updated relay" \ - --private-key - -# Send to relay -nostrtool send ws://localhost:8888 -``` - -## 📈 Monitoring and Analytics - -### View Relay Status -```bash -# Check if relay is running -ps aux | grep c_relay - -# Check network port -netstat -tln | grep 8888 - -# View recent logs -tail -f relay.log -``` - -### Database Analytics -```bash -# Connect to relay database -sqlite3 .nrdb - -# View relay statistics -SELECT * FROM event_stats; - -# View configuration events -SELECT * FROM configuration_events; - -# View recent events -SELECT * FROM recent_events LIMIT 10; -``` - -## 🧪 Testing - -### Run Error Handling Tests -```bash -# Comprehensive test suite -tests/event_config_tests.sh - -# Quick validation tests -tests/quick_error_tests.sh -``` - -### Manual Testing -```bash -# Test WebSocket connection -wscat -c ws://localhost:8888 - -# Test NIP-11 information -curl http://localhost:8888 -``` - -## 🔧 Development - -### Build from Source -```bash -git clone -cd c-relay -git submodule update --init --recursive -make clean && make -``` - -### Debug Build -```bash -make debug -gdb ./build/c_relay_x86 -``` - -### Contributing -1. Fork the repository -2. Create a feature branch -3. Make changes with tests -4. Submit a pull request - ## 📜 Supported NIPs