v0.1.9 - program generates it's own private keys.
This commit is contained in:
994
docs/MANAGEMENT_SYSTEM_DESIGN.md
Normal file
994
docs/MANAGEMENT_SYSTEM_DESIGN.md
Normal file
@@ -0,0 +1,994 @@
|
||||
# Ginxsom Management System Design
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document outlines the design for a secure management interface for ginxsom (Blossom media storage server) based on c-relay's proven admin system architecture. The design uses Kind 23456/23457 events with NIP-44 encryption over WebSocket for real-time admin operations.
|
||||
|
||||
## 1. System Architecture
|
||||
|
||||
### 1.1 High-Level Overview
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Admin[Admin Client] -->|WebSocket| WS[WebSocket Handler]
|
||||
WS -->|Kind 23456| Auth[Admin Authorization]
|
||||
Auth -->|Decrypt NIP-44| Decrypt[Command Decryption]
|
||||
Decrypt -->|Parse JSON Array| Router[Command Router]
|
||||
Router -->|Route by Command Type| Handlers[Unified Handlers]
|
||||
Handlers -->|Execute| DB[(Database)]
|
||||
Handlers -->|Execute| FS[File System]
|
||||
Handlers -->|Generate Response| Encrypt[NIP-44 Encryption]
|
||||
Encrypt -->|Kind 23457| WS
|
||||
WS -->|WebSocket| Admin
|
||||
|
||||
style Admin fill:#e1f5ff
|
||||
style Auth fill:#fff3cd
|
||||
style Handlers fill:#d4edda
|
||||
style DB fill:#f8d7da
|
||||
```
|
||||
|
||||
### 1.2 Component Architecture
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Admin Interface"
|
||||
CLI[CLI Tool]
|
||||
Web[Web Dashboard]
|
||||
end
|
||||
|
||||
subgraph "ginxsom FastCGI Process"
|
||||
WS[WebSocket Endpoint]
|
||||
Auth[Authorization Layer]
|
||||
Router[Command Router]
|
||||
|
||||
subgraph "Unified Handlers"
|
||||
BlobH[Blob Handler]
|
||||
StorageH[Storage Handler]
|
||||
ConfigH[Config Handler]
|
||||
StatsH[Stats Handler]
|
||||
SystemH[System Handler]
|
||||
end
|
||||
|
||||
DB[(SQLite Database)]
|
||||
Storage[Blob Storage]
|
||||
end
|
||||
|
||||
CLI -->|WebSocket| WS
|
||||
Web -->|WebSocket| WS
|
||||
WS --> Auth
|
||||
Auth --> Router
|
||||
Router --> BlobH
|
||||
Router --> StorageH
|
||||
Router --> ConfigH
|
||||
Router --> StatsH
|
||||
Router --> SystemH
|
||||
|
||||
BlobH --> DB
|
||||
BlobH --> Storage
|
||||
StorageH --> Storage
|
||||
ConfigH --> DB
|
||||
StatsH --> DB
|
||||
SystemH --> DB
|
||||
|
||||
style Auth fill:#fff3cd
|
||||
style Router fill:#d4edda
|
||||
```
|
||||
|
||||
### 1.3 Data Flow for Admin Commands
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin
|
||||
participant WebSocket
|
||||
participant Auth
|
||||
participant Handler
|
||||
participant Database
|
||||
|
||||
Admin->>WebSocket: Kind 23456 Event (NIP-44 encrypted)
|
||||
WebSocket->>Auth: Verify admin signature
|
||||
Auth->>Auth: Check pubkey matches admin_pubkey
|
||||
Auth->>Auth: Verify event signature
|
||||
Auth->>WebSocket: Authorization OK
|
||||
WebSocket->>Handler: Decrypt & parse command array
|
||||
Handler->>Handler: Validate command structure
|
||||
Handler->>Database: Execute operation
|
||||
Database-->>Handler: Result
|
||||
Handler->>Handler: Build response JSON
|
||||
Handler->>WebSocket: Encrypt response (NIP-44)
|
||||
WebSocket->>Admin: Kind 23457 Event (encrypted response)
|
||||
```
|
||||
|
||||
### 1.4 Integration with Existing Ginxsom
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Existing Ginxsom"
|
||||
Main[main.c]
|
||||
BUD04[bud04.c - Mirror]
|
||||
BUD06[bud06.c - Requirements]
|
||||
BUD08[bud08.c - NIP-94]
|
||||
BUD09[bud09.c - Report]
|
||||
AdminAPI[admin_api.c - Basic Admin]
|
||||
Validator[request_validator.c]
|
||||
end
|
||||
|
||||
subgraph "New Management System"
|
||||
AdminWS[admin_websocket.c]
|
||||
AdminAuth[admin_auth.c]
|
||||
AdminHandlers[admin_handlers.c]
|
||||
AdminConfig[admin_config.c]
|
||||
end
|
||||
|
||||
Main -->|Initialize| AdminWS
|
||||
AdminWS -->|Use| AdminAuth
|
||||
AdminWS -->|Route to| AdminHandlers
|
||||
AdminHandlers -->|Query| BUD04
|
||||
AdminHandlers -->|Query| BUD06
|
||||
AdminHandlers -->|Query| BUD08
|
||||
AdminHandlers -->|Query| BUD09
|
||||
AdminHandlers -->|Update| AdminConfig
|
||||
AdminAuth -->|Use| Validator
|
||||
|
||||
style AdminWS fill:#d4edda
|
||||
style AdminAuth fill:#fff3cd
|
||||
style AdminHandlers fill:#e1f5ff
|
||||
```
|
||||
|
||||
## 2. Database Schema
|
||||
|
||||
### 2.1 Core Tables
|
||||
|
||||
Following c-relay's minimal approach, we need only two tables for key management:
|
||||
|
||||
#### relay_seckey Table
|
||||
```sql
|
||||
-- Stores relay's private key (used for signing Kind 23457 responses)
|
||||
CREATE TABLE relay_seckey (
|
||||
private_key_hex TEXT NOT NULL CHECK (length(private_key_hex) = 64),
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
```
|
||||
|
||||
**Note**: This table stores the relay's private key as plain hex (no encryption). The key is used to:
|
||||
- Sign Kind 23457 response events
|
||||
- Encrypt responses using NIP-44 (shared secret with admin pubkey)
|
||||
|
||||
#### config Table (Extended)
|
||||
```sql
|
||||
-- Existing config table, add admin_pubkey entry
|
||||
INSERT INTO config (key, value, data_type, description, category, requires_restart)
|
||||
VALUES (
|
||||
'admin_pubkey',
|
||||
'<64-char-hex-pubkey>',
|
||||
'string',
|
||||
'Public key of authorized admin (hex format)',
|
||||
'security',
|
||||
0
|
||||
);
|
||||
```
|
||||
|
||||
**Note**: Admin public key is stored in the config table, not a separate table. Admin private key is NEVER stored anywhere.
|
||||
|
||||
### 2.2 Schema Comparison with c-relay
|
||||
|
||||
| c-relay | ginxsom | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `relay_seckey` (private_key_hex, created_at) | `relay_seckey` (private_key_hex, created_at) | Relay private key storage |
|
||||
| `config` table entry for admin_pubkey | `config` table entry for admin_pubkey | Admin authorization |
|
||||
| No audit log | No audit log | Keep it simple |
|
||||
| No processed events tracking | No processed events tracking | Stateless processing |
|
||||
|
||||
### 2.3 Key Storage Strategy
|
||||
|
||||
**Relay Private Key**:
|
||||
- Stored in `relay_seckey` table as plain 64-character hex
|
||||
- Generated on first startup or provided via `--relay-privkey` CLI option
|
||||
- Used for signing Kind 23457 responses and NIP-44 encryption
|
||||
- Never exposed via API
|
||||
|
||||
**Admin Public Key**:
|
||||
- Stored in `config` table as plain 64-character hex
|
||||
- Generated on first startup or provided via `--admin-pubkey` CLI option
|
||||
- Used to verify Kind 23456 command signatures
|
||||
- Can be queried via admin API
|
||||
|
||||
**Admin Private Key**:
|
||||
- NEVER stored anywhere in the system
|
||||
- Kept only by the admin in their client/tool
|
||||
- Used to sign Kind 23456 commands and decrypt Kind 23457 responses
|
||||
|
||||
## 3. API Design
|
||||
|
||||
### 3.1 Command Structure
|
||||
|
||||
Following c-relay's pattern, all commands use JSON array format:
|
||||
|
||||
```json
|
||||
["command_name", {"param1": "value1", "param2": "value2"}]
|
||||
```
|
||||
|
||||
### 3.2 Event Structure
|
||||
|
||||
#### Kind 23456 - Admin Command Event
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 23456,
|
||||
"pubkey": "<admin-pubkey-hex>",
|
||||
"created_at": 1234567890,
|
||||
"tags": [
|
||||
["p", "<relay-pubkey-hex>"]
|
||||
],
|
||||
"content": "<nip44-encrypted-command-array>",
|
||||
"sig": "<signature>"
|
||||
}
|
||||
```
|
||||
|
||||
**Content (decrypted)**:
|
||||
```json
|
||||
["blob_list", {"limit": 100, "offset": 0}]
|
||||
```
|
||||
|
||||
#### Kind 23457 - Admin Response Event
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 23457,
|
||||
"pubkey": "<relay-pubkey-hex>",
|
||||
"created_at": 1234567890,
|
||||
"tags": [
|
||||
["p", "<admin-pubkey-hex>"],
|
||||
["e", "<original-command-event-id>"]
|
||||
],
|
||||
"content": "<nip44-encrypted-response>",
|
||||
"sig": "<signature>"
|
||||
}
|
||||
```
|
||||
|
||||
**Content (decrypted)**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"blobs": [
|
||||
{"sha256": "abc123...", "size": 1024, "type": "image/png"},
|
||||
{"sha256": "def456...", "size": 2048, "type": "video/mp4"}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Command Categories
|
||||
|
||||
#### Blob Operations
|
||||
- `blob_list` - List blobs with pagination
|
||||
- `blob_info` - Get detailed blob information
|
||||
- `blob_delete` - Delete blob(s)
|
||||
- `blob_mirror` - Mirror blob from another server
|
||||
|
||||
#### Storage Management
|
||||
- `storage_stats` - Get storage usage statistics
|
||||
- `storage_quota` - Get/set storage quotas
|
||||
- `storage_cleanup` - Clean up orphaned files
|
||||
|
||||
#### Configuration
|
||||
- `config_get` - Get configuration value(s)
|
||||
- `config_set` - Set configuration value(s)
|
||||
- `config_list` - List all configuration
|
||||
- `auth_rules_list` - List authentication rules
|
||||
- `auth_rules_add` - Add authentication rule
|
||||
- `auth_rules_remove` - Remove authentication rule
|
||||
|
||||
#### Statistics
|
||||
- `stats_uploads` - Upload statistics
|
||||
- `stats_bandwidth` - Bandwidth usage
|
||||
- `stats_storage` - Storage usage over time
|
||||
- `stats_users` - User activity statistics
|
||||
|
||||
#### System
|
||||
- `system_info` - Get system information
|
||||
- `system_restart` - Restart server (graceful)
|
||||
- `system_backup` - Trigger database backup
|
||||
- `system_restore` - Restore from backup
|
||||
|
||||
### 3.4 Command Examples
|
||||
|
||||
#### Example 1: List Blobs
|
||||
```json
|
||||
// Command (Kind 23456 content, decrypted)
|
||||
["blob_list", {
|
||||
"limit": 50,
|
||||
"offset": 0,
|
||||
"type": "image/*",
|
||||
"sort": "created_at",
|
||||
"order": "desc"
|
||||
}]
|
||||
|
||||
// Response (Kind 23457 content, decrypted)
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"blobs": [
|
||||
{
|
||||
"sha256": "abc123...",
|
||||
"size": 102400,
|
||||
"type": "image/png",
|
||||
"created": 1234567890,
|
||||
"url": "https://blossom.example.com/abc123.png"
|
||||
}
|
||||
],
|
||||
"total": 150,
|
||||
"limit": 50,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example 2: Delete Blob
|
||||
```json
|
||||
// Command
|
||||
["blob_delete", {
|
||||
"sha256": "abc123...",
|
||||
"confirm": true
|
||||
}]
|
||||
|
||||
// Response
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"deleted": true,
|
||||
"sha256": "abc123...",
|
||||
"freed_bytes": 102400
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example 3: Get Storage Stats
|
||||
```json
|
||||
// Command
|
||||
["storage_stats", {}]
|
||||
|
||||
// Response
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"total_blobs": 1500,
|
||||
"total_bytes": 5368709120,
|
||||
"total_bytes_human": "5.0 GB",
|
||||
"disk_usage": {
|
||||
"used": 5368709120,
|
||||
"available": 94631291904,
|
||||
"total": 100000000000,
|
||||
"percent": 5.4
|
||||
},
|
||||
"by_type": {
|
||||
"image/png": {"count": 500, "bytes": 2147483648},
|
||||
"image/jpeg": {"count": 300, "bytes": 1610612736},
|
||||
"video/mp4": {"count": 200, "bytes": 1610612736}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example 4: Set Configuration
|
||||
```json
|
||||
// Command
|
||||
["config_set", {
|
||||
"max_upload_size": 10485760,
|
||||
"allowed_mime_types": ["image/*", "video/mp4"]
|
||||
}]
|
||||
|
||||
// Response
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"updated": ["max_upload_size", "allowed_mime_types"],
|
||||
"requires_restart": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 Error Handling
|
||||
|
||||
All errors follow consistent format:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "BLOB_NOT_FOUND",
|
||||
"message": "Blob with hash abc123... not found",
|
||||
"details": {
|
||||
"sha256": "abc123..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Codes**:
|
||||
- `UNAUTHORIZED` - Invalid admin signature
|
||||
- `INVALID_COMMAND` - Unknown command or malformed structure
|
||||
- `INVALID_PARAMS` - Missing or invalid parameters
|
||||
- `BLOB_NOT_FOUND` - Requested blob doesn't exist
|
||||
- `STORAGE_FULL` - Storage quota exceeded
|
||||
- `DATABASE_ERROR` - Database operation failed
|
||||
- `SYSTEM_ERROR` - Internal server error
|
||||
|
||||
## 4. File Structure
|
||||
|
||||
### 4.1 New Files to Create
|
||||
|
||||
```
|
||||
src/
|
||||
├── admin_websocket.c # WebSocket endpoint for admin commands
|
||||
├── admin_websocket.h # WebSocket handler declarations
|
||||
├── admin_auth.c # Admin authorization (adapted from c-relay)
|
||||
├── admin_auth.h # Authorization function declarations
|
||||
├── admin_handlers.c # Unified command handlers
|
||||
├── admin_handlers.h # Handler function declarations
|
||||
├── admin_config.c # Configuration management
|
||||
├── admin_config.h # Config function declarations
|
||||
└── admin_keys.c # Key generation and storage
|
||||
admin_keys.h # Key management declarations
|
||||
|
||||
include/
|
||||
└── admin_system.h # Public admin system interface
|
||||
```
|
||||
|
||||
### 4.2 Files to Adapt from c-relay
|
||||
|
||||
| c-relay File | Purpose | Adaptation for ginxsom |
|
||||
|--------------|---------|------------------------|
|
||||
| `dm_admin.c` | Admin event processing | → `admin_websocket.c` (WebSocket instead of DM) |
|
||||
| `api.c` (lines 768-838) | NIP-44 encryption/response | → `admin_handlers.c` (response generation) |
|
||||
| `config.c` (lines 500-583) | Key storage/retrieval | → `admin_keys.c` (relay key management) |
|
||||
| `main.c` (lines 1389-1556) | CLI argument parsing | → `main.c` (add admin CLI options) |
|
||||
|
||||
### 4.3 Integration with Existing Files
|
||||
|
||||
**src/main.c**:
|
||||
- Add CLI options: `--admin-pubkey`, `--relay-privkey`
|
||||
- Initialize admin WebSocket endpoint
|
||||
- Generate keys on first startup
|
||||
|
||||
**src/admin_api.c** (existing):
|
||||
- Keep existing basic admin API
|
||||
- Add WebSocket admin endpoint
|
||||
- Route Kind 23456 events to new handlers
|
||||
|
||||
**db/schema.sql**:
|
||||
- Add `relay_seckey` table
|
||||
- Add `admin_pubkey` to config table
|
||||
|
||||
## 5. Implementation Plan
|
||||
|
||||
### 5.1 Phase 1: Foundation (Week 1)
|
||||
|
||||
**Goal**: Set up key management and database schema
|
||||
|
||||
**Tasks**:
|
||||
1. Create `relay_seckey` table in schema
|
||||
2. Add `admin_pubkey` to config table
|
||||
3. Implement `admin_keys.c`:
|
||||
- `generate_relay_keypair()`
|
||||
- `generate_admin_keypair()`
|
||||
- `store_relay_private_key()`
|
||||
- `load_relay_private_key()`
|
||||
- `get_admin_pubkey()`
|
||||
4. Update `main.c`:
|
||||
- Add CLI options (`--admin-pubkey`, `--relay-privkey`)
|
||||
- Generate keys on first startup
|
||||
- Print keys once (like c-relay)
|
||||
5. Test key generation and storage
|
||||
|
||||
**Deliverables**:
|
||||
- Working key generation
|
||||
- Keys stored in database
|
||||
- CLI options functional
|
||||
|
||||
### 5.2 Phase 2: Authorization (Week 2)
|
||||
|
||||
**Goal**: Implement admin event authorization
|
||||
|
||||
**Tasks**:
|
||||
1. Create `admin_auth.c` (adapted from c-relay's authorization):
|
||||
- `verify_admin_event()` - Check Kind 23456 signature
|
||||
- `check_admin_pubkey()` - Verify against stored admin_pubkey
|
||||
- `verify_relay_target()` - Check 'p' tag matches relay pubkey
|
||||
2. Add NIP-44 crypto functions (use existing nostr_core_lib):
|
||||
- `decrypt_admin_command()` - Decrypt Kind 23456 content
|
||||
- `encrypt_admin_response()` - Encrypt Kind 23457 content
|
||||
3. Test authorization flow
|
||||
4. Test encryption/decryption
|
||||
|
||||
**Deliverables**:
|
||||
- Working authorization layer
|
||||
- NIP-44 encryption functional
|
||||
- Unit tests for auth
|
||||
|
||||
### 5.3 Phase 3: WebSocket Endpoint (Week 3)
|
||||
|
||||
**Goal**: Create WebSocket handler for admin commands
|
||||
|
||||
**Tasks**:
|
||||
1. Create `admin_websocket.c`:
|
||||
- WebSocket endpoint at `/admin` or similar
|
||||
- Receive Kind 23456 events
|
||||
- Route to authorization layer
|
||||
- Parse command array from decrypted content
|
||||
- Route to appropriate handler
|
||||
- Build Kind 23457 response
|
||||
- Send encrypted response
|
||||
2. Integrate with existing FastCGI WebSocket handling
|
||||
3. Add connection management
|
||||
4. Test WebSocket communication
|
||||
|
||||
**Deliverables**:
|
||||
- Working WebSocket endpoint
|
||||
- Event routing functional
|
||||
- Response generation working
|
||||
|
||||
### 5.4 Phase 4: Command Handlers (Week 4-5)
|
||||
|
||||
**Goal**: Implement unified command handlers
|
||||
|
||||
**Tasks**:
|
||||
1. Create `admin_handlers.c` with unified handler pattern:
|
||||
- `handle_blob_command()` - Blob operations
|
||||
- `handle_storage_command()` - Storage management
|
||||
- `handle_config_command()` - Configuration
|
||||
- `handle_stats_command()` - Statistics
|
||||
- `handle_system_command()` - System operations
|
||||
2. Implement each command:
|
||||
- Blob: list, info, delete, mirror
|
||||
- Storage: stats, quota, cleanup
|
||||
- Config: get, set, list, auth_rules
|
||||
- Stats: uploads, bandwidth, storage, users
|
||||
- System: info, restart, backup, restore
|
||||
3. Add validation for each command
|
||||
4. Test each command individually
|
||||
|
||||
**Deliverables**:
|
||||
- All commands implemented
|
||||
- Validation working
|
||||
- Integration tests passing
|
||||
|
||||
### 5.5 Phase 5: Testing & Documentation (Week 6)
|
||||
|
||||
**Goal**: Comprehensive testing and documentation
|
||||
|
||||
**Tasks**:
|
||||
1. Create test suite:
|
||||
- Unit tests for each handler
|
||||
- Integration tests for full flow
|
||||
- Security tests for authorization
|
||||
- Performance tests for WebSocket
|
||||
2. Create admin CLI tool (simple Node.js/Python script):
|
||||
- Generate Kind 23456 events
|
||||
- Send via WebSocket
|
||||
- Decrypt Kind 23457 responses
|
||||
- Pretty-print results
|
||||
3. Write documentation:
|
||||
- Admin API reference
|
||||
- CLI tool usage guide
|
||||
- Security best practices
|
||||
- Troubleshooting guide
|
||||
4. Create example scripts
|
||||
|
||||
**Deliverables**:
|
||||
- Complete test suite
|
||||
- Working CLI tool
|
||||
- Full documentation
|
||||
- Example scripts
|
||||
|
||||
### 5.6 Phase 6: Web Dashboard (Optional, Week 7-8)
|
||||
|
||||
**Goal**: Create web-based admin interface
|
||||
|
||||
**Tasks**:
|
||||
1. Design web UI (React/Vue/Svelte)
|
||||
2. Implement WebSocket client
|
||||
3. Create command forms
|
||||
4. Add real-time updates
|
||||
5. Deploy dashboard
|
||||
|
||||
**Deliverables**:
|
||||
- Working web dashboard
|
||||
- User documentation
|
||||
- Deployment guide
|
||||
|
||||
## 6. Security Considerations
|
||||
|
||||
### 6.1 Key Security
|
||||
|
||||
**Relay Private Key**:
|
||||
- Stored in database as plain hex (following c-relay pattern)
|
||||
- Never exposed via API
|
||||
- Used only for signing responses
|
||||
- Backed up with database
|
||||
|
||||
**Admin Private Key**:
|
||||
- NEVER stored on server
|
||||
- Kept only by admin
|
||||
- Used to sign commands
|
||||
- Should be stored securely by admin (password manager, hardware key, etc.)
|
||||
|
||||
**Admin Public Key**:
|
||||
- Stored in config table
|
||||
- Used for authorization
|
||||
- Can be rotated by updating config
|
||||
|
||||
### 6.2 Authorization Flow
|
||||
|
||||
1. Receive Kind 23456 event
|
||||
2. Verify event signature (nostr_verify_event_signature)
|
||||
3. Check pubkey matches admin_pubkey from config
|
||||
4. Verify 'p' tag targets this relay
|
||||
5. Decrypt content using NIP-44
|
||||
6. Parse and validate command
|
||||
7. Execute command
|
||||
8. Encrypt response using NIP-44
|
||||
9. Sign Kind 23457 response
|
||||
10. Send response
|
||||
|
||||
### 6.3 Attack Mitigation
|
||||
|
||||
**Replay Attacks**:
|
||||
- Check event timestamp (reject old events)
|
||||
- Optional: Track processed event IDs (if needed)
|
||||
|
||||
**Unauthorized Access**:
|
||||
- Strict pubkey verification
|
||||
- Signature validation
|
||||
- Relay targeting check
|
||||
|
||||
**Command Injection**:
|
||||
- Validate all command parameters
|
||||
- Use parameterized SQL queries
|
||||
- Sanitize file paths
|
||||
|
||||
**DoS Protection**:
|
||||
- Rate limit admin commands
|
||||
- Timeout long-running operations
|
||||
- Limit response sizes
|
||||
|
||||
## 7. Command Line Interface
|
||||
|
||||
### 7.1 CLI Options (Following c-relay Pattern)
|
||||
|
||||
```bash
|
||||
ginxsom [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h, --help Show help message
|
||||
-v, --version Show version information
|
||||
-p, --port PORT Override server port
|
||||
--strict-port Fail if exact port unavailable
|
||||
-a, --admin-pubkey KEY Override admin public key (hex or npub)
|
||||
-r, --relay-privkey KEY Override relay private key (hex or nsec)
|
||||
--debug-level=N Set debug level (0-5)
|
||||
|
||||
Examples:
|
||||
ginxsom # Start server (auto-generate keys on first run)
|
||||
ginxsom -p 8080 # Start on port 8080
|
||||
ginxsom -a <npub> # Set admin pubkey
|
||||
ginxsom -r <nsec> # Set relay privkey
|
||||
ginxsom --debug-level=3 # Enable info-level debugging
|
||||
```
|
||||
|
||||
### 7.2 First Startup Behavior
|
||||
|
||||
On first startup (no database exists):
|
||||
|
||||
1. Generate relay keypair
|
||||
2. Generate admin keypair
|
||||
3. Print keys ONCE to console:
|
||||
```
|
||||
=== Ginxsom First Startup ===
|
||||
|
||||
Relay Keys (for server):
|
||||
Public Key (npub): npub1...
|
||||
Private Key (nsec): nsec1...
|
||||
|
||||
Admin Keys (for you):
|
||||
Public Key (npub): npub1...
|
||||
Private Key (nsec): nsec1...
|
||||
|
||||
IMPORTANT: Save these keys securely!
|
||||
The admin private key will NOT be shown again.
|
||||
The relay private key is stored in the database.
|
||||
|
||||
Database created: <relay-pubkey>.db
|
||||
```
|
||||
|
||||
4. Store relay private key in database
|
||||
5. Store admin public key in config
|
||||
6. Start server
|
||||
|
||||
### 7.3 Subsequent Startups
|
||||
|
||||
On subsequent startups:
|
||||
|
||||
1. Find existing database file
|
||||
2. Load relay private key from database
|
||||
3. Load admin public key from config
|
||||
4. Apply CLI overrides if provided
|
||||
5. Start server
|
||||
|
||||
## 8. Comparison with c-relay
|
||||
|
||||
### 8.1 Similarities
|
||||
|
||||
| Feature | c-relay | ginxsom |
|
||||
|---------|---------|---------|
|
||||
| Event Types | Kind 23456/23457 | Kind 23456/23457 |
|
||||
| Encryption | NIP-44 | NIP-44 |
|
||||
| Command Format | JSON arrays | JSON arrays |
|
||||
| Key Storage | relay_seckey table | relay_seckey table |
|
||||
| Admin Auth | config table | config table |
|
||||
| CLI Options | --admin-pubkey, --relay-privkey | --admin-pubkey, --relay-privkey |
|
||||
| Response Format | Encrypted JSON | Encrypted JSON |
|
||||
|
||||
### 8.2 Differences
|
||||
|
||||
| Aspect | c-relay | ginxsom |
|
||||
|--------|---------|---------|
|
||||
| Transport | WebSocket (Nostr relay) | WebSocket (FastCGI) |
|
||||
| Commands | Relay-specific (auth, config, stats) | Blossom-specific (blob, storage, mirror) |
|
||||
| Database | SQLite (events) | SQLite (blobs + metadata) |
|
||||
| File Storage | N/A | Blob storage on disk |
|
||||
| Integration | Standalone relay | FastCGI + nginx |
|
||||
|
||||
### 8.3 Architectural Decisions
|
||||
|
||||
**Why follow c-relay's pattern?**
|
||||
1. Proven in production
|
||||
2. Simple and secure
|
||||
3. No complex key management
|
||||
4. Minimal database schema
|
||||
5. Easy to understand and maintain
|
||||
|
||||
**What we're NOT doing (from initial design)**:
|
||||
1. ❌ NIP-17 gift wrap (too complex)
|
||||
2. ❌ Separate admin_keys table (use config)
|
||||
3. ❌ Audit log table (keep it simple)
|
||||
4. ❌ Processed events tracking (stateless)
|
||||
5. ❌ Key encryption before storage (plain hex)
|
||||
6. ❌ Migration strategy (new project)
|
||||
|
||||
## 9. Testing Strategy
|
||||
|
||||
### 9.1 Unit Tests
|
||||
|
||||
**admin_keys.c**:
|
||||
- Key generation produces valid keys
|
||||
- Keys can be stored and retrieved
|
||||
- Invalid keys are rejected
|
||||
|
||||
**admin_auth.c**:
|
||||
- Valid admin events pass authorization
|
||||
- Invalid signatures are rejected
|
||||
- Wrong pubkeys are rejected
|
||||
- Expired events are rejected
|
||||
|
||||
**admin_handlers.c**:
|
||||
- Each command handler works correctly
|
||||
- Invalid parameters are rejected
|
||||
- Error responses are properly formatted
|
||||
|
||||
### 9.2 Integration Tests
|
||||
|
||||
**Full Flow**:
|
||||
1. Generate admin keypair
|
||||
2. Create Kind 23456 command
|
||||
3. Send via WebSocket
|
||||
4. Verify authorization
|
||||
5. Execute command
|
||||
6. Receive Kind 23457 response
|
||||
7. Decrypt and verify response
|
||||
|
||||
**Security Tests**:
|
||||
- Unauthorized pubkey rejected
|
||||
- Invalid signature rejected
|
||||
- Replay attack prevented
|
||||
- Command injection prevented
|
||||
|
||||
### 9.3 Performance Tests
|
||||
|
||||
- WebSocket connection handling
|
||||
- Command processing latency
|
||||
- Concurrent admin operations
|
||||
- Large response handling
|
||||
|
||||
## 10. Future Enhancements
|
||||
|
||||
### 10.1 Short Term
|
||||
|
||||
1. **Command History**: Track admin commands for audit
|
||||
2. **Multi-Admin Support**: Multiple authorized admin pubkeys
|
||||
3. **Role-Based Access**: Different permission levels
|
||||
4. **Batch Operations**: Execute multiple commands in one request
|
||||
|
||||
### 10.2 Long Term
|
||||
|
||||
1. **Web Dashboard**: Full-featured web UI
|
||||
2. **Monitoring Integration**: Prometheus/Grafana metrics
|
||||
3. **Backup Automation**: Scheduled backups
|
||||
4. **Replication**: Multi-server blob replication
|
||||
5. **Advanced Analytics**: Usage patterns, trends, predictions
|
||||
|
||||
## 11. References
|
||||
|
||||
### 11.1 Nostr NIPs
|
||||
|
||||
- **NIP-01**: Basic protocol flow
|
||||
- **NIP-04**: Encrypted Direct Messages (deprecated, but reference)
|
||||
- **NIP-19**: bech32-encoded entities (npub, nsec)
|
||||
- **NIP-44**: Versioned Encryption (used for admin commands)
|
||||
|
||||
### 11.2 Blossom Specifications
|
||||
|
||||
- **BUD-01**: Blob Upload/Download
|
||||
- **BUD-02**: Blob Descriptor
|
||||
- **BUD-04**: Mirroring
|
||||
- **BUD-06**: Upload Requirements
|
||||
- **BUD-08**: NIP-94 Integration
|
||||
- **BUD-09**: Blob Reporting
|
||||
|
||||
### 11.3 c-relay Source Files
|
||||
|
||||
- `c-relay/src/dm_admin.c` - Admin event processing
|
||||
- `c-relay/src/api.c` - NIP-44 encryption
|
||||
- `c-relay/src/config.c` - Key storage
|
||||
- `c-relay/src/main.c` - CLI options
|
||||
- `c-relay/src/sql_schema.h` - Database schema
|
||||
|
||||
## 12. Appendix
|
||||
|
||||
### 12.1 Example Admin CLI Tool (Python)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ginxsom Admin CLI Tool
|
||||
Sends admin commands to ginxsom server via WebSocket
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
from nostr_sdk import Keys, Event, EventBuilder, Kind
|
||||
|
||||
class GinxsomAdmin:
|
||||
def __init__(self, server_url, admin_nsec, relay_npub):
|
||||
self.server_url = server_url
|
||||
self.admin_keys = Keys.parse(admin_nsec)
|
||||
self.relay_pubkey = Keys.parse(relay_npub).public_key()
|
||||
|
||||
async def send_command(self, command, params):
|
||||
"""Send admin command and wait for response"""
|
||||
# Build command array
|
||||
command_array = [command, params]
|
||||
|
||||
# Encrypt with NIP-44
|
||||
encrypted = self.admin_keys.nip44_encrypt(
|
||||
self.relay_pubkey,
|
||||
json.dumps(command_array)
|
||||
)
|
||||
|
||||
# Build Kind 23456 event
|
||||
event = EventBuilder(
|
||||
Kind(23456),
|
||||
encrypted,
|
||||
[["p", str(self.relay_pubkey)]]
|
||||
).to_event(self.admin_keys)
|
||||
|
||||
# Send via WebSocket
|
||||
async with websockets.connect(self.server_url) as ws:
|
||||
await ws.send(json.dumps(event.as_json()))
|
||||
|
||||
# Wait for Kind 23457 response
|
||||
response = await ws.recv()
|
||||
response_event = Event.from_json(response)
|
||||
|
||||
# Decrypt response
|
||||
decrypted = self.admin_keys.nip44_decrypt(
|
||||
self.relay_pubkey,
|
||||
response_event.content()
|
||||
)
|
||||
|
||||
return json.loads(decrypted)
|
||||
|
||||
# Usage
|
||||
async def main():
|
||||
admin = GinxsomAdmin(
|
||||
"ws://localhost:8080/admin",
|
||||
"nsec1...", # Admin private key
|
||||
"npub1..." # Relay public key
|
||||
)
|
||||
|
||||
# List blobs
|
||||
result = await admin.send_command("blob_list", {
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
})
|
||||
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### 12.2 Database Schema SQL
|
||||
|
||||
```sql
|
||||
-- Add to db/schema.sql
|
||||
|
||||
-- Relay Private Key Storage
|
||||
CREATE TABLE relay_seckey (
|
||||
private_key_hex TEXT NOT NULL CHECK (length(private_key_hex) = 64),
|
||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
);
|
||||
|
||||
-- Admin Public Key (add to config table)
|
||||
INSERT INTO config (key, value, data_type, description, category, requires_restart)
|
||||
VALUES (
|
||||
'admin_pubkey',
|
||||
'', -- Set during first startup
|
||||
'string',
|
||||
'Public key of authorized admin (64-char hex)',
|
||||
'security',
|
||||
0
|
||||
);
|
||||
|
||||
-- Relay Public Key (add to config table)
|
||||
INSERT INTO config (key, value, data_type, description, category, requires_restart)
|
||||
VALUES (
|
||||
'relay_pubkey',
|
||||
'', -- Set during first startup
|
||||
'string',
|
||||
'Public key of this relay (64-char hex)',
|
||||
'server',
|
||||
0
|
||||
);
|
||||
```
|
||||
|
||||
### 12.3 Makefile Updates
|
||||
|
||||
```makefile
|
||||
# Add to Makefile
|
||||
|
||||
# Admin system objects
|
||||
ADMIN_OBJS = build/admin_websocket.o \
|
||||
build/admin_auth.o \
|
||||
build/admin_handlers.o \
|
||||
build/admin_config.o \
|
||||
build/admin_keys.o
|
||||
|
||||
# Update main target
|
||||
build/ginxsom-fcgi: $(OBJS) $(ADMIN_OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
# Admin system rules
|
||||
build/admin_websocket.o: src/admin_websocket.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
build/admin_auth.o: src/admin_auth.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
build/admin_handlers.o: src/admin_handlers.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
build/admin_config.o: src/admin_config.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
build/admin_keys.o: src/admin_keys.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 2.0
|
||||
**Last Updated**: 2025-01-16
|
||||
**Status**: Ready for Implementation
|
||||
Reference in New Issue
Block a user