# 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