Files
ginxsom/Trash/Ginxsom_Management_System_Design.md
2025-11-20 07:53:58 -04:00

1389 lines
35 KiB
Markdown

# Ginxsom Management System Design
## Executive Summary
This document outlines the design for a secure management interface for ginxsom, a Blossom media storage server. The design adapts proven patterns from c-relay's management system while addressing ginxsom's specific requirements for blob storage, authentication, and FastCGI architecture.
**Key Design Principles:**
- **Security First**: NIP-17 gift wrap encryption for all admin commands
- **Database-Centric**: Configuration and state stored in SQLite
- **Unified Handler**: Tag-based routing similar to c-relay
- **Two-Step Confirmation**: Critical operations require explicit confirmation
- **Backwards Compatible**: Integrates with existing admin API without breaking changes
---
## Table of Contents
1. [System Architecture](#1-system-architecture)
2. [Database Schema](#2-database-schema)
3. [Keypair Management](#3-keypair-management)
4. [Authentication Architecture](#4-authentication-architecture)
5. [Management Commands](#5-management-commands)
6. [API Design](#6-api-design)
7. [File Structure](#7-file-structure)
8. [Implementation Plan](#8-implementation-plan)
9. [Security Considerations](#9-security-considerations)
10. [Migration Strategy](#10-migration-strategy)
---
## 1. System Architecture
### 1.1 Component Overview
```mermaid
flowchart TB
subgraph Client["Admin Client"]
CLI[CLI Tool / nak]
WEB[Web Interface]
end
subgraph Gateway["nginx Gateway"]
NGINX[nginx]
end
subgraph FastCGI["FastCGI Application"]
MAIN[main.c]
VALIDATOR[request_validator.c]
ADMIN[admin_api.c]
MGMT[management_handler.c<br/>NEW]
DM[dm_admin.c<br/>NEW]
end
subgraph Storage["Data Layer"]
DB[(SQLite Database)]
BLOBS[Blob Storage]
KEYS[Key Storage<br/>Memory Only]
end
CLI -->|NIP-17 Gift Wrap| NGINX
WEB -->|NIP-17 Gift Wrap| NGINX
NGINX -->|FastCGI| MAIN
MAIN --> VALIDATOR
VALIDATOR --> ADMIN
ADMIN --> MGMT
MGMT --> DM
DM --> DB
DM --> KEYS
MGMT --> BLOBS
style MGMT fill:#ff9
style DM fill:#ff9
style KEYS fill:#f99
```
### 1.2 Data Flow for Admin Commands
```mermaid
sequenceDiagram
participant Admin
participant nginx
participant FastCGI
participant Validator
participant DM Handler
participant Database
Admin->>nginx: POST /admin<br/>NIP-17 Gift Wrap
nginx->>FastCGI: Forward Request
FastCGI->>Validator: Validate Auth
Validator->>Validator: Decrypt Gift Wrap
Validator->>Validator: Verify Admin Pubkey
Validator-->>FastCGI: Auth Result
alt Auth Failed
FastCGI-->>Admin: 401/403 Error
else Auth Success
FastCGI->>DM Handler: Parse Command
DM Handler->>DM Handler: Extract Tags
DM Handler->>DM Handler: Route to Handler
alt Critical Operation
DM Handler->>Database: Store Pending Change
DM Handler-->>Admin: Confirmation Required
Admin->>nginx: POST /admin<br/>Confirmation Event
nginx->>FastCGI: Forward Confirmation
FastCGI->>DM Handler: Execute Pending
DM Handler->>Database: Apply Changes
else Non-Critical
DM Handler->>Database: Execute Directly
end
DM Handler->>DM Handler: Encrypt Response (NIP-44)
DM Handler-->>Admin: Encrypted Result
end
```
### 1.3 Integration with Existing System
The management system integrates with ginxsom's existing architecture:
**Existing Components (Keep):**
- [`src/main.c`](src/main.c:1): FastCGI request loop and routing
- [`src/request_validator.c`](src/request_validator.c:1): Centralized authentication
- [`src/admin_api.c`](src/admin_api.c:1): Current admin API endpoints
- [`db/schema.sql`](db/schema.sql:1): Base database schema
**New Components (Add):**
- `src/management_handler.c`: Unified command handler
- `src/dm_admin.c`: NIP-17 gift wrap processing
- `src/keypair_manager.c`: Server and admin key management
- `src/pending_changes.c`: Two-step confirmation system
**Modified Components:**
- [`src/main.c`](src/main.c:1640): Add `/admin` endpoint routing
- [`src/request_validator.c`](src/request_validator.c:683): Add NIP-17 validation
- [`db/schema.sql`](db/schema.sql:1): Add management tables
---
## 2. Database Schema
### 2.1 New Tables
```sql
-- Server keypair (relay identity)
CREATE TABLE IF NOT EXISTS server_keys (
id INTEGER PRIMARY KEY CHECK (id = 1), -- Singleton table
public_key TEXT NOT NULL CHECK (length(public_key) = 64),
private_key_encrypted TEXT NOT NULL, -- Encrypted with system key
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
last_rotated INTEGER,
UNIQUE(public_key)
);
-- Admin public keys (authorized administrators)
CREATE TABLE IF NOT EXISTS admin_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
public_key TEXT NOT NULL UNIQUE CHECK (length(public_key) = 64),
name TEXT, -- Human-readable identifier
permissions TEXT NOT NULL DEFAULT 'full', -- JSON array of permissions
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
created_by TEXT, -- Admin pubkey who added this key
last_used INTEGER,
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1))
);
-- Pending changes requiring confirmation
CREATE TABLE IF NOT EXISTS pending_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
change_type TEXT NOT NULL, -- 'delete_blob', 'update_config', 'add_admin', etc.
change_data TEXT NOT NULL, -- JSON with change details
requested_by TEXT NOT NULL, -- Admin pubkey
requested_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
expires_at INTEGER NOT NULL, -- Confirmation deadline
confirmed INTEGER NOT NULL DEFAULT 0 CHECK (confirmed IN (0, 1)),
confirmed_at INTEGER,
executed INTEGER NOT NULL DEFAULT 0 CHECK (executed IN (0, 1)),
executed_at INTEGER,
result TEXT -- Execution result or error
);
-- Admin command audit log
CREATE TABLE IF NOT EXISTS admin_audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
admin_pubkey TEXT NOT NULL,
command TEXT NOT NULL, -- Command tag from event
parameters TEXT, -- JSON with command parameters
success INTEGER NOT NULL CHECK (success IN (0, 1)),
error_message TEXT,
timestamp INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
client_ip TEXT,
event_id TEXT -- Nostr event ID for traceability
);
-- NIP-17 gift wrap tracking (prevent replay)
CREATE TABLE IF NOT EXISTS gift_wrap_tracking (
event_id TEXT PRIMARY KEY,
admin_pubkey TEXT NOT NULL,
processed_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
expires_at INTEGER NOT NULL
);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_admin_keys_pubkey ON admin_keys(public_key);
CREATE INDEX IF NOT EXISTS idx_admin_keys_enabled ON admin_keys(enabled);
CREATE INDEX IF NOT EXISTS idx_pending_changes_expires ON pending_changes(expires_at);
CREATE INDEX IF NOT EXISTS idx_pending_changes_requested_by ON pending_changes(requested_by);
CREATE INDEX IF NOT EXISTS idx_audit_log_timestamp ON admin_audit_log(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_log_admin ON admin_audit_log(admin_pubkey);
CREATE INDEX IF NOT EXISTS idx_gift_wrap_expires ON gift_wrap_tracking(expires_at);
```
### 2.2 Schema Extensions to Existing Tables
```sql
-- Add management-related config keys
INSERT OR IGNORE INTO config (key, value, description) VALUES
('server_pubkey', '', 'Server public key (relay identity)'),
('admin_dm_enabled', 'true', 'Enable NIP-17 DM-based admin commands'),
('admin_confirmation_timeout', '300', 'Seconds to confirm critical operations'),
('admin_session_timeout', '3600', 'Admin session timeout in seconds'),
('admin_rate_limit', '100', 'Max admin commands per hour per admin'),
('admin_audit_retention', '2592000', 'Audit log retention in seconds (30 days)');
```
### 2.3 Migration from Current Schema
The current schema already has:
- [`config`](db/schema.sql:21) table (unified configuration)
- [`blobs`](db/schema.sql:8) table (blob metadata)
- [`auth_rules`](db/schema.sql:47) table (authentication rules)
**Migration Strategy:**
1. Add new tables without modifying existing ones
2. Populate `admin_keys` from existing `config.admin_pubkey`
3. Generate server keypair on first startup if not exists
4. Maintain backwards compatibility with existing admin API
---
## 3. Keypair Management
### 3.1 Server Keypair (Relay Identity)
**Purpose:** Identifies the ginxsom server in Nostr ecosystem
**Generation:**
```c
// On first startup or explicit generation
int generate_server_keypair(void) {
unsigned char privkey[32];
unsigned char pubkey[32];
// Generate using nostr_core_lib
if (nostr_generate_keypair(privkey, pubkey) != NOSTR_SUCCESS) {
return -1;
}
// Store in database (encrypted)
char pubkey_hex[65];
char privkey_hex[65];
nostr_bytes_to_hex(pubkey, 32, pubkey_hex);
nostr_bytes_to_hex(privkey, 32, privkey_hex);
// Encrypt private key before storage
char encrypted_privkey[256];
encrypt_with_system_key(privkey_hex, encrypted_privkey);
// Store in database
return store_server_keypair(pubkey_hex, encrypted_privkey);
}
```
**Storage:**
- Public key: Stored in `server_keys.public_key` and `config.server_pubkey`
- Private key: **NEVER** stored in database - kept in process memory only
- On startup: Load from database, decrypt, keep in memory
- On shutdown: Clear from memory securely
**Command-Line Options:**
```bash
# Generate new server keypair
ginxsom-fcgi --generate-server-key
# Import existing keypair
ginxsom-fcgi --import-server-key <hex_private_key>
# Show server public key
ginxsom-fcgi --show-server-pubkey
```
### 3.2 Admin Keypair Management
**Purpose:** Authorize administrators to manage the server
**Initial Setup:**
```bash
# Generate admin keypair (done by admin, not server)
ADMIN_PRIVKEY=$(nak key generate)
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public)
# Add admin to server (requires existing admin or direct DB access)
sqlite3 db/ginxsom.db << EOF
INSERT INTO admin_keys (public_key, name, permissions, created_by)
VALUES ('$ADMIN_PUBKEY', 'Primary Admin', '["full"]', 'system');
EOF
```
**Runtime Management:**
```bash
# Add new admin (via DM command)
nak event -k 14 --envelope --sec $ADMIN_PRIVKEY \
--tag command add_admin \
--tag pubkey $NEW_ADMIN_PUBKEY \
--tag name "Secondary Admin" \
--tag permissions '["read","write"]' \
| curl -X POST http://localhost:9001/admin -d @-
# List admins
nak event -k 14 --envelope --sec $ADMIN_PRIVKEY \
--tag command list_admins \
| curl -X POST http://localhost:9001/admin -d @-
# Revoke admin
nak event -k 14 --envelope --sec $ADMIN_PRIVKEY \
--tag command revoke_admin \
--tag pubkey $ADMIN_TO_REVOKE \
| curl -X POST http://localhost:9001/admin -d @-
```
### 3.3 Key Rotation
**Server Key Rotation:**
- Generate new keypair
- Update database
- Announce new pubkey via NIP-11 relay info
- Keep old key for 30 days for transition
**Admin Key Rotation:**
- Admin generates new keypair
- Uses old key to authorize new key
- Old key remains valid during transition period
- Explicit revocation of old key after confirmation
---
## 4. Authentication Architecture
### 4.1 NIP-17 Gift Wrap Overview
**Why NIP-17?**
- End-to-end encryption between admin and server
- Prevents eavesdropping on admin commands
- Provides forward secrecy
- Standard Nostr protocol (interoperable)
**Gift Wrap Structure:**
```json
{
"kind": 1059,
"content": "<encrypted_seal>",
"tags": [
["p", "<server_pubkey>"]
],
"pubkey": "<random_ephemeral_key>",
"created_at": 1234567890,
"sig": "<signature>"
}
```
**Seal Structure (encrypted in gift wrap):**
```json
{
"kind": 13,
"content": "<encrypted_rumor>",
"tags": [
["p", "<admin_pubkey>"]
],
"pubkey": "<admin_pubkey>",
"created_at": 1234567890
}
```
**Rumor Structure (encrypted in seal):**
```json
{
"kind": 14,
"content": "",
"tags": [
["command", "list_blobs"],
["limit", "50"],
["offset", "0"]
],
"pubkey": "<admin_pubkey>",
"created_at": 1234567890
}
```
### 4.2 Authentication Flow
```mermaid
sequenceDiagram
participant Admin
participant Server
participant DB
Admin->>Admin: Create Kind 14 Rumor<br/>(command + params)
Admin->>Admin: Wrap in Kind 13 Seal<br/>(encrypt with server pubkey)
Admin->>Admin: Wrap in Kind 1059 Gift<br/>(encrypt with ephemeral key)
Admin->>Server: POST /admin<br/>Gift Wrap Event
Server->>Server: Unwrap Gift<br/>(decrypt with server privkey)
Server->>Server: Unwrap Seal<br/>(decrypt with admin pubkey)
Server->>Server: Extract Rumor<br/>(Kind 14 command)
Server->>DB: Check admin_pubkey authorized
alt Not Authorized
Server-->>Admin: 403 Forbidden
else Authorized
Server->>DB: Check gift wrap not replayed
alt Replayed
Server-->>Admin: 409 Conflict (Replay)
else Fresh
Server->>DB: Store gift wrap ID
Server->>Server: Parse command tags
Server->>Server: Execute command
Server->>DB: Log to audit_log
Server->>Server: Encrypt response (NIP-44)
Server-->>Admin: Encrypted Response
end
end
```
### 4.3 Validation Rules
**Gift Wrap Validation:**
1. Event kind must be 1059
2. Must have `p` tag with server pubkey
3. Signature must be valid
4. Created_at within acceptable time window (±5 minutes)
**Seal Validation:**
1. Decryption must succeed with server private key
2. Event kind must be 13
3. Must have `p` tag with admin pubkey
**Rumor Validation:**
1. Decryption must succeed with admin public key
2. Event kind must be 14
3. Admin pubkey must be in `admin_keys` table with `enabled=1`
4. Must have `command` tag
5. Event ID not in `gift_wrap_tracking` (prevent replay)
6. Created_at within acceptable time window
**Integration with Existing Validator:**
Add to [`src/request_validator.c`](src/request_validator.c:1):
```c
// Add to event kind routing (around line 438)
else if (event_kind == 1059) {
// NIP-17 Gift Wrap for admin commands
int gift_wrap_result = validate_gift_wrap_admin(event);
if (gift_wrap_result != NOSTR_SUCCESS) {
result->valid = 0;
result->error_code = gift_wrap_result;
strcpy(result->reason, "Gift wrap validation failed");
cJSON_Delete(event);
return NOSTR_SUCCESS;
}
}
```
---
## 5. Management Commands
### 5.1 Command Categories
**Blob Operations:**
- `list_blobs` - List blobs with filters
- `get_blob_info` - Get detailed blob metadata
- `delete_blob` - Delete blob (requires confirmation)
- `mirror_blob` - Mirror blob from another server
- `verify_blob` - Verify blob integrity
**Storage Management:**
- `get_storage_stats` - Get storage usage statistics
- `get_disk_usage` - Get disk space information
- `cleanup_orphans` - Remove orphaned files
- `set_quota` - Set storage quota for user
**Configuration:**
- `get_config` - Get configuration values
- `set_config` - Set configuration value (requires confirmation)
- `list_auth_rules` - List authentication rules
- `add_auth_rule` - Add authentication rule
- `remove_auth_rule` - Remove authentication rule (requires confirmation)
**Statistics:**
- `get_stats` - Get server statistics
- `get_upload_stats` - Get upload statistics by time period
- `get_user_stats` - Get per-user statistics
- `get_bandwidth_stats` - Get bandwidth usage
**System:**
- `get_health` - Get system health status
- `get_version` - Get server version info
- `backup_database` - Create database backup
- `restore_database` - Restore from backup (requires confirmation)
- `rotate_logs` - Rotate log files
**Admin Management:**
- `list_admins` - List admin keys
- `add_admin` - Add new admin key (requires confirmation)
- `revoke_admin` - Revoke admin key (requires confirmation)
- `get_audit_log` - Get admin audit log
### 5.2 Command Structure
**Request Format (Kind 14 Rumor):**
```json
{
"kind": 14,
"content": "",
"tags": [
["command", "list_blobs"],
["limit", "50"],
["offset", "0"],
["filter_type", "image/*"],
["since", "1234567890"]
],
"pubkey": "<admin_pubkey>",
"created_at": 1234567890
}
```
**Response Format (NIP-44 Encrypted):**
```json
{
"status": "success",
"command": "list_blobs",
"data": {
"blobs": [...],
"total": 1234,
"limit": 50,
"offset": 0
},
"timestamp": 1234567890
}
```
**Error Response:**
```json
{
"status": "error",
"command": "list_blobs",
"error": {
"code": "INVALID_PARAMETER",
"message": "Invalid limit value",
"details": "Limit must be between 1 and 1000"
},
"timestamp": 1234567890
}
```
### 5.3 Two-Step Confirmation
**Critical Operations Requiring Confirmation:**
- `delete_blob` - Permanent data loss
- `set_config` - System behavior changes
- `add_admin` - Security implications
- `revoke_admin` - Access control changes
- `restore_database` - Data integrity risk
**Confirmation Flow:**
**Step 1: Request**
```json
{
"kind": 14,
"tags": [
["command", "delete_blob"],
["sha256", "abc123..."]
]
}
```
**Step 1: Response**
```json
{
"status": "confirmation_required",
"command": "delete_blob",
"pending_id": "42",
"details": {
"sha256": "abc123...",
"size": 1048576,
"type": "image/jpeg",
"uploaded_at": 1234567890
},
"expires_at": 1234568190,
"message": "This will permanently delete the blob. Send confirmation within 5 minutes."
}
```
**Step 2: Confirmation**
```json
{
"kind": 14,
"tags": [
["command", "confirm"],
["pending_id", "42"]
]
}
```
**Step 2: Response**
```json
{
"status": "success",
"command": "delete_blob",
"data": {
"sha256": "abc123...",
"deleted": true
},
"timestamp": 1234567900
}
```
---
## 6. API Design
### 6.1 Endpoint Routing
**New Endpoint:**
- `POST /admin` - Unified admin command endpoint (NIP-17 gift wrap)
**Existing Endpoints (Keep):**
- `GET /api/stats` - Statistics (existing admin API)
- `GET /api/config` - Configuration (existing admin API)
- `PUT /api/config` - Update config (existing admin API)
- `GET /api/files` - File listing (existing admin API)
- `GET /api/health` - Health check (existing admin API)
**Integration Strategy:**
- New `/admin` endpoint for NIP-17 DM-based commands
- Existing `/api/*` endpoints remain for backwards compatibility
- Both systems share same database and authentication
- Gradual migration path for clients
### 6.2 Request Handling
**Handler Registration in [`src/main.c`](src/main.c:1640):**
```c
// Add to main request loop (around line 1640)
else if (strcmp(request_method, "POST") == 0 &&
strcmp(request_uri, "/admin") == 0) {
// Handle NIP-17 gift wrap admin commands
handle_admin_dm_request();
}
```
**Unified Handler Pattern:**
```c
// src/management_handler.c
void handle_admin_dm_request(void) {
// 1. Read gift wrap event from request body
char *event_json = read_request_body();
// 2. Validate and unwrap gift wrap
admin_command_t cmd;
int result = unwrap_admin_gift_wrap(event_json, &cmd);
if (result != SUCCESS) {
send_admin_error(result, "Failed to unwrap gift wrap");
return;
}
// 3. Check admin authorization
if (!is_admin_authorized(cmd.admin_pubkey)) {
send_admin_error(ERROR_UNAUTHORIZED, "Not authorized");
return;
}
// 4. Check for replay
if (is_gift_wrap_replayed(cmd.event_id)) {
send_admin_error(ERROR_REPLAY, "Gift wrap already processed");
return;
}
// 5. Route to command handler
admin_response_t response;
result = route_admin_command(&cmd, &response);
// 6. Log to audit log
log_admin_command(&cmd, result);
// 7. Encrypt and send response
send_admin_response(&response, cmd.admin_pubkey);
}
```
### 6.3 Tag-Based Routing
**Command Router:**
```c
int route_admin_command(admin_command_t *cmd, admin_response_t *response) {
const char *command = get_tag_value(cmd->tags, "command");
if (!command) {
return ERROR_MISSING_COMMAND;
}
// Blob operations
if (strcmp(command, "list_blobs") == 0) {
return handle_list_blobs(cmd, response);
} else if (strcmp(command, "get_blob_info") == 0) {
return handle_get_blob_info(cmd, response);
} else if (strcmp(command, "delete_blob") == 0) {
return handle_delete_blob(cmd, response);
}
// Storage management
else if (strcmp(command, "get_storage_stats") == 0) {
return handle_get_storage_stats(cmd, response);
} else if (strcmp(command, "cleanup_orphans") == 0) {
return handle_cleanup_orphans(cmd, response);
}
// Configuration
else if (strcmp(command, "get_config") == 0) {
return handle_get_config(cmd, response);
} else if (strcmp(command, "set_config") == 0) {
return handle_set_config(cmd, response);
}
// Admin management
else if (strcmp(command, "list_admins") == 0) {
return handle_list_admins(cmd, response);
} else if (strcmp(command, "add_admin") == 0) {
return handle_add_admin(cmd, response);
}
// Confirmation
else if (strcmp(command, "confirm") == 0) {
return handle_confirm_pending(cmd, response);
}
else {
return ERROR_UNKNOWN_COMMAND;
}
}
```
---
## 7. File Structure
### 7.1 New Files
**Core Management:**
```
src/management_handler.c - Unified command handler and routing
src/management_handler.h - Handler interface definitions
src/dm_admin.c - NIP-17 gift wrap processing
src/dm_admin.h - Gift wrap interface
src/keypair_manager.c - Server and admin key management
src/keypair_manager.h - Key management interface
src/pending_changes.c - Two-step confirmation system
src/pending_changes.h - Pending changes interface
```
**Command Handlers:**
```
src/commands/blob_commands.c - Blob operation handlers
src/commands/storage_commands.c - Storage management handlers
src/commands/config_commands.c - Configuration handlers
src/commands/stats_commands.c - Statistics handlers
src/commands/admin_commands.c - Admin management handlers
src/commands/system_commands.c - System operation handlers
```
**Utilities:**
```
src/utils/encryption.c - NIP-44 encryption utilities
src/utils/audit_log.c - Audit logging utilities
src/utils/response_builder.c - Response formatting
```
### 7.2 Modified Files
**[`src/main.c`](src/main.c:1):**
- Add `/admin` endpoint routing (line ~1640)
- Initialize management system on startup
- Add cleanup on shutdown
**[`src/request_validator.c`](src/request_validator.c:1):**
- Add Kind 1059 (gift wrap) validation (line ~438)
- Add gift wrap unwrapping logic
- Add replay prevention checks
**[`src/admin_api.c`](src/admin_api.c:1):**
- Keep existing endpoints for backwards compatibility
- Add integration hooks for new management system
- Share database access with new handlers
**[`db/schema.sql`](db/schema.sql:1):**
- Add new tables (server_keys, admin_keys, pending_changes, etc.)
- Add indexes for performance
- Add default configuration values
**[`Makefile`](Makefile):**
- Add new source files to build
- Add dependencies for new modules
- Update clean targets
### 7.3 Integration Points
**With Existing Admin API:**
```c
// src/admin_api.c - Add integration function
void admin_api_notify_config_change(const char *key, const char *value) {
// Called by management system when config changes
// Invalidates cache, triggers reload
nostr_request_validator_force_cache_refresh();
}
```
**With Request Validator:**
```c
// src/request_validator.c - Add gift wrap validation
int validate_gift_wrap_admin(cJSON *event) {
// Validate Kind 1059 structure
// Unwrap and validate seal
// Unwrap and validate rumor
// Check admin authorization
// Check replay prevention
return NOSTR_SUCCESS;
}
```
**With Database:**
```c
// All handlers use shared database connection
// Consistent error handling
// Transaction support for atomic operations
```
---
## 8. Implementation Plan
### 8.1 Phase 1: Foundation (Week 1-2)
**Goals:**
- Database schema extensions
- Keypair management
- Basic gift wrap processing
**Tasks:**
1. Create database migration script
2. Implement `keypair_manager.c`
- Server keypair generation
- Admin keypair storage
- Key loading on startup
3. Implement `dm_admin.c`
- Gift wrap unwrapping
- Seal decryption
- Rumor extraction
4. Add gift wrap validation to `request_validator.c`
5. Create test suite for gift wrap processing
**Deliverables:**
- Working keypair management
- Gift wrap unwrap/validation
- Database schema updated
- Unit tests passing
### 8.2 Phase 2: Command Infrastructure (Week 3-4)
**Goals:**
- Unified command handler
- Tag-based routing
- Response encryption
**Tasks:**
1. Implement `management_handler.c`
- Request parsing
- Command routing
- Response building
2. Implement `pending_changes.c`
- Two-step confirmation
- Timeout handling
- Execution tracking
3. Add `/admin` endpoint to `main.c`
4. Implement audit logging
5. Create command handler templates
**Deliverables:**
- Working command routing
- Two-step confirmation system
- Audit logging functional
- Integration tests passing
### 8.3 Phase 3: Core Commands (Week 5-6)
**Goals:**
- Implement essential management commands
- Integration with existing systems
**Tasks:**
1. Implement blob commands
- `list_blobs`
- `get_blob_info`
- `delete_blob` (with confirmation)
2. Implement storage commands
- `get_storage_stats`
- `get_disk_usage`
- `cleanup_orphans`
3. Implement config commands
- `get_config`
- `set_config` (with confirmation)
4. Implement admin commands
- `list_admins`
- `add_admin` (with confirmation)
- `revoke_admin` (with confirmation)
5. Integration testing with existing admin API
**Deliverables:**
- Core commands functional
- Integration with existing API
- End-to-end tests passing
### 8.4 Phase 4: Advanced Features (Week 7-8)
**Goals:**
- Statistics and monitoring
- System operations
- CLI tooling
**Tasks:**
1. Implement stats commands
- `get_stats`
- `get_upload_stats`
- `get_user_stats`
2. Implement system commands
- `backup_database`
- `restore_database`
- `rotate_logs`
3. Create CLI tool for admin operations
4. Create web interface (optional)
5. Performance optimization
6. Security audit
**Deliverables:**
- All commands implemented
- CLI tool functional
- Performance benchmarks
- Security review complete
### 8.5 Phase 5: Documentation & Deployment (Week 9-10)
**Goals:**
- Complete documentation
- Deployment guides
- Migration tools
**Tasks:**
1. Write admin documentation
2. Create setup guides
3. Create migration scripts
4. Write troubleshooting guide
5. Create example scripts
6. Production deployment testing
**Deliverables:**
- Complete documentation
- Migration tools
- Deployment guides
- Production-ready release
---
## 9. Security Considerations
### 9.1 Threat Model
**Threats:**
1. **Unauthorized Access**: Attacker gains admin privileges
2. **Replay Attacks**: Reuse of captured gift wrap events
3. **Man-in-the-Middle**: Interception of admin commands
4. **Privilege Escalation**: Regular user gains admin access
5. **Data Exfiltration**: Unauthorized access to blob data
6. **Denial of Service**: Resource exhaustion via admin commands
**Mitigations:**
1. **NIP-17 Encryption**: End-to-end encryption prevents MITM
2. **Gift Wrap Tracking**: Prevents replay attacks
3. **Admin Key Management**: Strict authorization checks
4. **Two-Step Confirmation**: Prevents accidental critical operations
5. **Audit Logging**: Tracks all admin actions
6. **Rate Limiting**: Prevents DoS via admin commands
### 9.2 Key Security
**Server Private Key:**
- **NEVER** stored in database
- Loaded into memory on startup
- Cleared on shutdown
- Protected by OS memory protection
- Consider using secure enclave if available
**Admin Private Keys:**
- **NEVER** stored on server
- Managed by admin clients only
- Rotated regularly
- Revoked immediately if compromised
**Database Encryption:**
- Consider encrypting sensitive config values
- Use system keyring for encryption keys
- Implement key rotation mechanism
### 9.3 Access Control
**Permission Levels:**
```json
{
"full": ["*"],
"read": ["list_*", "get_*"],
"write": ["list_*", "get_*", "set_*", "add_*"],
"admin": ["list_*", "get_*", "set_*", "add_*", "delete_*", "revoke_*"]
}
```
**Permission Checks:**
```c
int check_admin_permission(const char *admin_pubkey, const char *command) {
// Load admin permissions from database
cJSON *permissions = get_admin_permissions(admin_pubkey);
// Check if command is allowed
if (has_permission(permissions, command)) {
return 1;
}
// Check wildcard permissions
if (has_permission(permissions, "*")) {
return 1;
}
return 0;
}
```
### 9.4 Audit Trail
**What to Log:**
- All admin commands (success and failure)
- Admin key additions/revocations
- Configuration changes
- Critical operations (delete, restore, etc.)
- Authentication failures
- Suspicious activity
**Log Format:**
```json
{
"timestamp": 1234567890,
"admin_pubkey": "abc123...",
"command": "delete_blob",
"parameters": {"sha256": "def456..."},
"success": true,
"client_ip": "192.168.1.100",
"event_id": "ghi789..."
}
```
**Log Retention:**
- Keep audit logs for 30 days minimum
- Archive older logs to separate storage
- Implement log rotation
- Protect logs from tampering
---
## 10. Migration Strategy
### 10.1 Backwards Compatibility
**Existing Admin API:**
- Keep all existing `/api/*` endpoints
- No breaking changes to current API
- Gradual deprecation over 6 months
- Clear migration documentation
**Database:**
- Additive schema changes only
- No modifications to existing tables
- Migration script handles upgrades
- Rollback capability
**Configuration:**
- Existing config keys remain valid
- New config keys added with defaults
- No changes to config file format
### 10.2 Migration Steps
**Step 1: Database Migration**
```bash
# Backup existing database
cp db/ginxsom.db db/ginxsom.db.backup
# Run migration script
sqlite3 db/ginxsom.db < db/migrations/002_add_management_system.sql
# Verify migration
sqlite3 db/ginxsom.db "SELECT name FROM sqlite_master WHERE type='table';"
```
**Step 2: Generate Server Keypair**
```bash
# Generate server keypair
./build/ginxsom-fcgi --generate-server-key
# Verify keypair
./build/ginxsom-fcgi --show-server-pubkey
```
**Step 3: Add Initial Admin**
```bash
# Generate admin keypair
ADMIN_PRIVKEY=$(nak key generate)
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public)
# Add to database
sqlite3 db/ginxsom.db << EOF
INSERT INTO admin_keys (public_key, name, permissions, created_by)
VALUES ('$ADMIN_PUBKEY', 'Primary Admin', '["full"]', 'system');
EOF
# Save admin private key securely
echo "$ADMIN_PRIVKEY" > ~/.config/ginxsom/admin.key
chmod 600 ~/.config/ginxsom/admin.key
```
**Step 4: Test New System**
```bash
# Test gift wrap command
nak event -k 14 --envelope --sec $ADMIN_PRIVKEY \
--tag command list_admins \
| curl -X POST http://localhost:9001/admin -d @-
# Verify response
```
**Step 5: Update Clients**
- Update admin scripts to use new `/admin` endpoint
- Migrate from `/api/*` to NIP-17 commands
- Test thoroughly before production
### 10.3 Rollback Plan
**If Issues Occur:**
1. Stop ginxsom service
2. Restore database backup
3. Revert to previous version
4. Investigate issues
5. Fix and retry migration
**Database Rollback:**
```bash
# Stop service
systemctl stop ginxsom
# Restore backup
cp db/ginxsom.db.backup db/ginxsom.db
# Restart service
systemctl start ginxsom
```
---
## Appendix A: Command Reference
### Blob Commands
**list_blobs**
```json
{
"command": "list_blobs",
"limit": "50",
"offset": "0",
"filter_type": "image/*",
"since": "1234567890"
}
```
**get_blob_info**
```json
{
"command": "get_blob_info",
"sha256": "abc123..."
}
```
**delete_blob** (requires confirmation)
```json
{
"command": "delete_blob",
"sha256": "abc123..."
}
```
### Storage Commands
**get_storage_stats**
```json
{
"command": "get_storage_stats"
}
```
**cleanup_orphans**
```json
{
"command": "cleanup_orphans",
"dry_run": "true"
}
```
### Configuration Commands
**get_config**
```json
{
"command": "get_config",
"key": "max_file_size"
}
```
**set_config** (requires confirmation)
```json
{
"command": "set_config",
"key": "max_file_size",
"value": "209715200"
}
```
### Admin Commands
**list_admins**
```json
{
"command": "list_admins"
}
```
**add_admin** (requires confirmation)
```json
{
"command": "add_admin",
"pubkey": "def456...",
"name": "Secondary Admin",
"permissions": "[\"read\",\"write\"]"
}
```
**revoke_admin** (requires confirmation)
```json
{
"command": "revoke_admin",
"pubkey": "def456..."
}
```
---
## Appendix B: Example Scripts
### Setup Script
```bash
#!/bin/bash
# setup_admin.sh - Initial admin setup
set -e
echo "Ginxsom Admin Setup"
echo "==================="
# Generate admin keypair
echo "Generating admin keypair..."
ADMIN_PRIVKEY=$(nak key generate)
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public)
echo "Admin Public Key: $ADMIN_PUBKEY"
# Save private key
mkdir -p ~/.config/ginxsom
echo "$ADMIN_PRIVKEY" > ~/.config/ginxsom/admin.key
chmod 600 ~/.config/ginxsom/admin.key
echo "Private key saved to ~/.config/ginxsom/admin.key"
# Add to database
echo "Adding admin to database..."
sqlite3 db/ginxsom.db << EOF
INSERT INTO admin_keys (public_key, name, permissions, created_by)
VALUES ('$ADMIN_PUBKEY', 'Primary Admin', '["full"]', 'system');
EOF
echo "Admin setup complete!"
echo "Test with: ./admin_command.sh list_admins"
```
### Admin Command Script
```bash
#!/bin/bash
# admin_command.sh - Send admin command
ADMIN_PRIVKEY=$(cat ~/.config/ginxsom/admin.key)
COMMAND=$1
shift
# Build tags
TAGS=""
for arg in "$@"; do
KEY=$(echo "$arg" | cut -d= -f1)
VALUE=$(echo "$arg" | cut -d= -f2-)
TAGS="$TAGS --tag $KEY \"$VALUE\""
done
# Send command
eval nak event -k 14 --envelope --sec "$ADMIN_PRIVKEY" \
--tag command "$COMMAND" \
$TAGS \
| curl -s -X POST http://localhost:9001/admin -d @- \
| jq .
```
### Usage Examples
```bash
# List admins
./admin_command.sh list_admins
# Get storage stats
./admin_command.sh get_storage_stats
# List blobs
./admin_command.sh list_blobs limit=10 offset=0
# Delete blob (with confirmation)
./admin_command.sh delete_blob sha256=abc123...
# Then confirm
./admin_command.sh confirm pending_id=42
```
---
## Conclusion
This design provides a secure, scalable management system for ginxsom that:
1. **Leverages Proven Patterns**: Adapts c-relay's successful architecture
2. **Maintains Security**: NIP-17 encryption and two-step confirmation
3. **Ensures Compatibility**: Integrates with existing admin API
4. **Enables Growth**: Extensible command system for future features
5. **Provides Auditability**: Complete audit trail of admin actions
The phased implementation plan allows for incremental development and testing, while the migration strategy ensures smooth transition from the current system.
**Next Steps:**
1. Review and approve design
2. Begin Phase 1 implementation
3. Set up development environment
4. Create test infrastructure
5. Start coding!