# 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 in config table - 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 in config table 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 ### Admin API Event Structure ```json { "kind": 23456, "content": "base64_nip44_encrypted_command_array", "tags": [ ["p", ""] ] } ``` **Configuration Commands** (encrypted in content): - `["relay_description", "My Relay"]` - `["max_subscriptions_per_client", "25"]` - `["pow_min_difficulty", "16"]` **Auth Rule Commands** (encrypted in content): - `["blacklist", "pubkey", "hex_pubkey_value"]` - `["whitelist", "pubkey", "hex_pubkey_value"]` **Query Commands** (encrypted in content): - `["auth_query", "all"]` - `["system_command", "system_status"]` ### 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