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

35 KiB

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
  2. Database Schema
  3. Keypair Management
  4. Authentication Architecture
  5. Management Commands
  6. API Design
  7. File Structure
  8. Implementation Plan
  9. Security Considerations
  10. Migration Strategy

1. System Architecture

1.1 Component Overview

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

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):

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:


2. Database Schema

2.1 New Tables

-- 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

-- 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 table (unified configuration)
  • blobs table (blob metadata)
  • auth_rules 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:

// 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:

# 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:

# 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:

# 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:

{
  "kind": 1059,
  "content": "<encrypted_seal>",
  "tags": [
    ["p", "<server_pubkey>"]
  ],
  "pubkey": "<random_ephemeral_key>",
  "created_at": 1234567890,
  "sig": "<signature>"
}

Seal Structure (encrypted in gift wrap):

{
  "kind": 13,
  "content": "<encrypted_rumor>",
  "tags": [
    ["p", "<admin_pubkey>"]
  ],
  "pubkey": "<admin_pubkey>",
  "created_at": 1234567890
}

Rumor Structure (encrypted in seal):

{
  "kind": 14,
  "content": "",
  "tags": [
    ["command", "list_blobs"],
    ["limit", "50"],
    ["offset", "0"]
  ],
  "pubkey": "<admin_pubkey>",
  "created_at": 1234567890
}

4.2 Authentication Flow

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:

// 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):

{
  "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):

{
  "status": "success",
  "command": "list_blobs",
  "data": {
    "blobs": [...],
    "total": 1234,
    "limit": 50,
    "offset": 0
  },
  "timestamp": 1234567890
}

Error Response:

{
  "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

{
  "kind": 14,
  "tags": [
    ["command", "delete_blob"],
    ["sha256", "abc123..."]
  ]
}

Step 1: Response

{
  "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

{
  "kind": 14,
  "tags": [
    ["command", "confirm"],
    ["pending_id", "42"]
  ]
}

Step 2: Response

{
  "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:

// 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:

// 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:

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:

  • Add /admin endpoint routing (line ~1640)
  • Initialize management system on startup
  • Add cleanup on shutdown

src/request_validator.c:

  • Add Kind 1059 (gift wrap) validation (line ~438)
  • Add gift wrap unwrapping logic
  • Add replay prevention checks

src/admin_api.c:

  • Keep existing endpoints for backwards compatibility
  • Add integration hooks for new management system
  • Share database access with new handlers

db/schema.sql:

  • Add new tables (server_keys, admin_keys, pending_changes, etc.)
  • Add indexes for performance
  • Add default configuration values

Makefile:

  • Add new source files to build
  • Add dependencies for new modules
  • Update clean targets

7.3 Integration Points

With Existing Admin API:

// 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:

// 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:

// 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:

{
  "full": ["*"],
  "read": ["list_*", "get_*"],
  "write": ["list_*", "get_*", "set_*", "add_*"],
  "admin": ["list_*", "get_*", "set_*", "add_*", "delete_*", "revoke_*"]
}

Permission Checks:

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:

{
  "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

# 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

# Generate server keypair
./build/ginxsom-fcgi --generate-server-key

# Verify keypair
./build/ginxsom-fcgi --show-server-pubkey

Step 3: Add Initial Admin

# 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

# 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:

# 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

{
  "command": "list_blobs",
  "limit": "50",
  "offset": "0",
  "filter_type": "image/*",
  "since": "1234567890"
}

get_blob_info

{
  "command": "get_blob_info",
  "sha256": "abc123..."
}

delete_blob (requires confirmation)

{
  "command": "delete_blob",
  "sha256": "abc123..."
}

Storage Commands

get_storage_stats

{
  "command": "get_storage_stats"
}

cleanup_orphans

{
  "command": "cleanup_orphans",
  "dry_run": "true"
}

Configuration Commands

get_config

{
  "command": "get_config",
  "key": "max_file_size"
}

set_config (requires confirmation)

{
  "command": "set_config",
  "key": "max_file_size",
  "value": "209715200"
}

Admin Commands

list_admins

{
  "command": "list_admins"
}

add_admin (requires confirmation)

{
  "command": "add_admin",
  "pubkey": "def456...",
  "name": "Secondary Admin",
  "permissions": "[\"read\",\"write\"]"
}

revoke_admin (requires confirmation)

{
  "command": "revoke_admin",
  "pubkey": "def456..."
}

Appendix B: Example Scripts

Setup Script

#!/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

#!/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

# 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!