12 KiB
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:
<relay_pubkey>.dbcreated 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:
- Update
default_config_event.htemplate - Add parsing logic in
config.cload_config_from_database() - Add global config struct field in
config.h - Update documentation in
docs/configuration_guide.md
Adding New NIP Support
Integration Pattern:
- Event validation in
request_validator.c - Protocol handling in
main.cWebSocket callback - Database storage considerations in schema
- 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/<relay_pubkey>.db
└── relay.log
Production SystemD Deployment
/opt/c-relay/:
├── c_relay_x86
├── <relay_pubkey>.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:
- SQLite Write Performance: Single writer limitation
- JSON Parsing: Per-event parsing overhead
- Signature Validation: Cryptographic operations per event
- 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
- Configuration File Addition: Breaks event-based config paradigm
- Direct Database Modification: Bypasses signature validation
- Hard-Coded Ports: Conflicts with auto-fallback system
- Schema Modifications: Requires database recreation
- Admin Key Storage: Violates security model
- Blocking Operations: Interferes with WebSocket event loop
- Memory Leaks: JSON objects must be properly reference counted
- 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.]