v0.1.1 - Cleaning things up.
This commit is contained in:
41
docs/AGENTS.md
Normal file
41
docs/AGENTS.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to agents when working with code in this repository.
|
||||
|
||||
## Critical Project-Specific Rules
|
||||
|
||||
- **Database Path**: Always use `db/ginxsom.db` - this is hardcoded, not configurable
|
||||
- **SQLite Build**: Uses local SQLite 3.37.2 in `sqlite3-build/` directory, NOT system SQLite
|
||||
- **Local Development**: Everything runs locally on port 9001, never use system nginx on port 80
|
||||
- **FastCGI Socket**: Uses `/tmp/ginxsom-fcgi.sock` for FastCGI communication
|
||||
- **Config Priority**: File-based config (XDG locations) overrides database config
|
||||
- **Admin Auth**: Uses Nostr events for admin API authentication (kind 24242 with "admin" tag)
|
||||
- **Blob Storage**: Files stored as `blobs/<sha256>.<ext>` where extension comes from MIME type
|
||||
- **Build Directory**: Must create `build/` directory before compilation
|
||||
- **Test Files**: Tests are in the tests/ directory, and they should be run from the root, i.e. 'tests/mirror_test_bud04.sh'
|
||||
|
||||
|
||||
## Non-Standard Commands
|
||||
|
||||
```bash
|
||||
# make clean && make && Restart nginx
|
||||
./restart-all.sh
|
||||
|
||||
# Start FastCGI daemon
|
||||
./scripts/start-fcgi.sh
|
||||
|
||||
# Test admin API with authentication
|
||||
source .admin_keys && ./scripts/test_admin.sh
|
||||
|
||||
# Setup wizard (creates signed config events)
|
||||
./scripts/setup.sh
|
||||
|
||||
```
|
||||
|
||||
## Critical Architecture Notes
|
||||
|
||||
- FastCGI app handles HEAD/PUT/DELETE requests, nginx serves GET directly from disk
|
||||
- Two-tier config: File config (signed Nostr events) + database config (key-value)
|
||||
- Admin API requires Nostr event signatures with specific tag structure
|
||||
- Database is single source of truth for blob existence (not filesystem)
|
||||
- Extension handling: URLs work with any extension, files stored with correct extension
|
||||
504
docs/AUTH_API.md
Normal file
504
docs/AUTH_API.md
Normal file
@@ -0,0 +1,504 @@
|
||||
# Authentication API Documentation
|
||||
|
||||
|
||||
## Authentication Flow and Order of Operations
|
||||
|
||||
### Authentication Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Request Received │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐ ╔═══════════════════╗
|
||||
│ Input Valid?├─No─►║ REJECT: Invalid ║
|
||||
└──────┬──────┘ ║ Input (~1μs) ║
|
||||
│Yes ╚═══════════════════╝
|
||||
▼
|
||||
┌─────────────┐ ╔═══════════════════╗
|
||||
│System Init? ├─No─►║ REJECT: Not ║
|
||||
└──────┬──────┘ ║ Initialized ║
|
||||
│Yes ╚═══════════════════╝
|
||||
▼
|
||||
┌─────────────┐
|
||||
│Auth Header? │
|
||||
└──────┬──────┘
|
||||
│Yes
|
||||
▼ ┌─────────────────────┐
|
||||
┌─────────────┐ No │ │
|
||||
│Parse Header ├────────────┤ Skip Nostr │
|
||||
└──────┬──────┘ │ Validation │
|
||||
│ │ │
|
||||
▼ └──────────┬──────────┘
|
||||
┌─────────────┐ ╔═══════════════════╗ │
|
||||
│Valid Base64?├─No─►║ REJECT: Malformed ║ │
|
||||
└──────┬──────┘ ║ Header (~10μs) ║ │
|
||||
│Yes ╚═══════════════════╝ │
|
||||
▼ │
|
||||
┌─────────────┐ ╔═══════════════════╗ │
|
||||
│Valid JSON? ├─No─►║ REJECT: Invalid ║ │
|
||||
└──────┬──────┘ ║ JSON (~50μs) ║ │
|
||||
│Yes ╚═══════════════════╝ │
|
||||
▼ │
|
||||
┌─────────────┐ ╔═══════════════════╗ │
|
||||
│Valid Struct?├─No─►║ REJECT: Invalid ║ │
|
||||
└──────┬──────┘ ║ Structure (~100μs)║ │
|
||||
│Yes ╚═══════════════════╝ │
|
||||
▼ │
|
||||
┌─────────────┐ ╔═══════════════════╗ │
|
||||
│Event Kind? │ ║ ║ │
|
||||
└──────┬──────┘ ║ DUAL AUTH MODES ║ │
|
||||
│ ╚═══════════════════╝ │
|
||||
▼ │
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Kind │ │ Kind │ │ Other │ │ Invalid │
|
||||
│ 22242 │ │ 24242 │ │ Kinds │ │ Kind │
|
||||
│ (NIP-42) │ │(Blossom) │ │ (Skip) │ │(Reject) │
|
||||
└─────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬────┘
|
||||
│ │ │ │
|
||||
▼ │ ▼ ▼
|
||||
┌──────────┐ │ ┌──────────┐ ╔═══════════════════╗
|
||||
│ NIP-42 │ │ │ Skip │ ║ REJECT: Invalid ║
|
||||
│Challenge │ │ │ Nostr │ ║ Event Kind ║
|
||||
│Validate │ │ │Validate │ ╚═══════════════════╝
|
||||
│(~500μs) │ │ └─────┬────┘ │
|
||||
└─────┬────┘ │ │ │
|
||||
│ │ ▼ │
|
||||
│ │ ┌──────────┐ │
|
||||
│ │ │ Extract │ │
|
||||
│ │ │ Context │ │
|
||||
│ │ └─────┬────┘ │
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ ECDSA SIGNATURE VERIFICATION │
|
||||
│ (~2ms) │
|
||||
└───────────────────────────┬────────────────────────────────┘
|
||||
│Yes
|
||||
▼ │
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│Operation Match? │No ║ REJECT: Unauth. ║
|
||||
│(Kind 24242 only)├──►║ Operation (~200μs)║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│Yes/Skip(Kind 22242)
|
||||
▼ │
|
||||
┌─────────────────┐ ╔═══════════════════╗ │
|
||||
│Event Expired? │Yes║ REJECT: Expired ║ │
|
||||
└─────────┬───────┘ ║ Event (~50μs) ║ │
|
||||
│No ╚═══════════════════╝ │
|
||||
▼ │
|
||||
┌─────────────────┐ │
|
||||
│Extract Pubkey │ │
|
||||
│& Auth Context │ │
|
||||
└─────────┬───────┘ │
|
||||
│ │
|
||||
▼◄───────────────────────────────────────┘
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│Auth Rules │ No ║ ALLOW: Rules ║
|
||||
│Enabled? ├────►║ Disabled ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│Yes
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│Generate Cache │
|
||||
│Key (SHA-256) │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│Cache Hit? │ Yes ║ RETURN: Cached ║
|
||||
│(~100μs lookup) ├────►║ Decision (~100μs) ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
╔═══════════════════════════════════════╗
|
||||
║ RULE EVALUATION ENGINE ║
|
||||
║ (Priority Order) ║
|
||||
╚═══════════════════════════════════════╝
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│1. Pubkey │ Yes ║ DENY: Pubkey ║
|
||||
│ Blacklisted? ├────►║ Blocked ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│2. Hash │ Yes ║ DENY: Hash ║
|
||||
│ Blacklisted? ├────►║ Blocked ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│3. MIME Type │ Yes ║ DENY: MIME ║
|
||||
│ Blacklisted? ├────►║ Blocked ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│4. Size Limit │ Yes ║ DENY: File ║
|
||||
│ Exceeded? ├────►║ Too Large ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│5. Pubkey │ Yes ║ ALLOW: Pubkey ║
|
||||
│ Whitelisted? ├────►║ Whitelisted ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│6. MIME Type │ Yes ║ ALLOW: MIME ║
|
||||
│ Whitelisted? ├────►║ Whitelisted ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
┌─────────────────┐ ╔═══════════════════╗
|
||||
│Whitelist Rules │ Yes ║ DENY: Not in ║
|
||||
│Exist? ├────►║ Whitelist ║
|
||||
└─────────┬───────┘ ╚═══════════════════╝
|
||||
│No
|
||||
▼
|
||||
╔═══════════════════╗
|
||||
║ ALLOW: Default ║
|
||||
║ Allow Policy ║
|
||||
╚═══════════════════╝
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Cache Decision │
|
||||
│ (5min TTL) │
|
||||
└─────────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Return Result │
|
||||
│ to Application │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Authentication Flow (Mermaid)
|
||||
|
||||
```mermaid
|
||||
|
||||
flowchart TD
|
||||
A[Request Received] --> B{Input Valid?}
|
||||
B -->|No| R1[REJECT: Invalid Input ~1μs]
|
||||
B -->|Yes| C{System Init?}
|
||||
C -->|No| R2[REJECT: Not Initialized]
|
||||
C -->|Yes| D{Auth Header?}
|
||||
D -->|No| SKIP[Skip Nostr Validation]
|
||||
D -->|Yes| E{Valid Base64?}
|
||||
E -->|No| R3[REJECT: Malformed Header ~10μs]
|
||||
E -->|Yes| F{Valid JSON?}
|
||||
F -->|No| R4[REJECT: Invalid JSON ~50μs]
|
||||
F -->|Yes| G{Valid Struct?}
|
||||
G -->|No| R5[REJECT: Invalid Structure ~100μs]
|
||||
G -->|Yes| H{Event Kind?}
|
||||
|
||||
%% Event Kind Branching
|
||||
H --> I[Kind 22242<br/>NIP-42]
|
||||
H --> J[Kind 24242<br/>Blossom]
|
||||
H --> K[Other Kinds<br/>Skip]
|
||||
H --> L[Invalid Kind]
|
||||
L --> R6[REJECT: Invalid Event Kind]
|
||||
|
||||
%% NIP-42 Path
|
||||
I --> M[NIP-42 Challenge<br/>Validation ~500μs]
|
||||
M --> N[ECDSA Signature<br/>Verification ~2ms]
|
||||
|
||||
%% Blossom Path
|
||||
J --> N
|
||||
|
||||
%% Skip Path
|
||||
K --> SKIP
|
||||
SKIP --> O[Extract Context]
|
||||
O --> N
|
||||
|
||||
%% Signature Verification
|
||||
N -->|No| R7[REJECT: Invalid Signature ~2ms]
|
||||
N -->|Yes| P{Operation Match?<br/>Kind 24242 only}
|
||||
P -->|No| R8[REJECT: Unauthorized Operation ~200μs]
|
||||
P -->|Yes/Skip 22242| Q{Event Expired?}
|
||||
Q -->|Yes| R9[REJECT: Expired Event ~50μs]
|
||||
Q -->|No| S[Extract Pubkey & Auth Context]
|
||||
|
||||
%% Rules Engine
|
||||
S --> T{Auth Rules Enabled?}
|
||||
T -->|No| ALLOW1[ALLOW: Rules Disabled]
|
||||
T -->|Yes| U[Generate Cache Key SHA-256]
|
||||
U --> V{Cache Hit?}
|
||||
V -->|Yes| CACHED[RETURN: Cached Decision ~100μs]
|
||||
V -->|No| W[RULE EVALUATION ENGINE<br/>Priority Order]
|
||||
|
||||
%% Rule Checks
|
||||
W --> X1{1. Pubkey Blacklisted?}
|
||||
X1 -->|Yes| DENY1[DENY: Pubkey Blocked]
|
||||
X1 -->|No| X2{2. Hash Blacklisted?}
|
||||
X2 -->|Yes| DENY2[DENY: Hash Blocked]
|
||||
X2 -->|No| X3{3. MIME Blacklisted?}
|
||||
X3 -->|Yes| DENY3[DENY: MIME Blocked]
|
||||
X3 -->|No| X4{4. Size Limit Exceeded?}
|
||||
X4 -->|Yes| DENY4[DENY: File Too Large]
|
||||
X4 -->|No| X5{5. Pubkey Whitelisted?}
|
||||
X5 -->|Yes| ALLOW2[ALLOW: Pubkey Whitelisted]
|
||||
X5 -->|No| X6{6. MIME Whitelisted?}
|
||||
X6 -->|Yes| ALLOW3[ALLOW: MIME Whitelisted]
|
||||
X6 -->|No| X7{Whitelist Rules Exist?}
|
||||
X7 -->|Yes| DENY5[DENY: Not in Whitelist]
|
||||
X7 -->|No| ALLOW4[ALLOW: Default Policy]
|
||||
|
||||
%% Final Steps
|
||||
ALLOW2 --> CACHE[Cache Decision 5min TTL]
|
||||
ALLOW3 --> CACHE
|
||||
ALLOW4 --> CACHE
|
||||
DENY1 --> CACHE
|
||||
DENY2 --> CACHE
|
||||
DENY3 --> CACHE
|
||||
DENY4 --> CACHE
|
||||
DENY5 --> CACHE
|
||||
CACHE --> RESULT[Return Result to Application]
|
||||
|
||||
%% Styling
|
||||
classDef rejectBox fill:#ff4444,stroke:#ffffff,color:#ffffff
|
||||
classDef allowBox fill:#44ff44,stroke:#ffffff,color:#000000
|
||||
classDef processBox fill:#4444ff,stroke:#ffffff,color:#ffffff
|
||||
classDef decisionBox fill:#ffff44,stroke:#000000,color:#000000
|
||||
|
||||
class R1,R2,R3,R4,R5,R6,R7,R8,R9,DENY1,DENY2,DENY3,DENY4,DENY5 rejectBox
|
||||
class ALLOW1,ALLOW2,ALLOW3,ALLOW4,CACHED allowBox
|
||||
class A,M,N,S,U,W,CACHE,RESULT,SKIP,O processBox
|
||||
class B,C,D,E,F,G,H,P,Q,T,V,X1,X2,X3,X4,X5,X6,X7 decisionBox
|
||||
```
|
||||
|
||||
### Performance Timeline (ASCII)
|
||||
|
||||
```
|
||||
Fast Path (Cache Hit) - Total: ~101μs
|
||||
┌─────┬─────────────────────────────────────────────────────────────────┬──────┐
|
||||
│ 1μs │ 100μs Cache Lookup │ 1μs │
|
||||
└─────┴─────────────────────────────────────────────────────────────────┴──────┘
|
||||
Input │ │ Return
|
||||
Valid │ SQLite SELECT │ Result
|
||||
|
||||
Typical Path (Valid Request) - Total: ~2.4ms
|
||||
┌──┬───┬────┬─────────────────────────┬────────┬────┬──┐
|
||||
│1μ│50μ│100μ│ 2000μs │ 200μs │100μ│1μ│
|
||||
└──┴───┴────┴─────────────────────────┴────────┴────┴──┘
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ │ ECDSA Signature │ Rule │Cache│Return
|
||||
│ │ │ │ Verification │ Eval │Store│Result
|
||||
│ │ │ │ (Most Expensive) │ │ │
|
||||
│ │ │
|
||||
│ │ JSON Parse
|
||||
│ Header Parse
|
||||
Input Validation
|
||||
|
||||
Worst Case (Full Validation) - Total: ~2.7ms
|
||||
┌──┬───┬────┬─────────────────────────┬─────────┬────┬──┐
|
||||
│1μ│50μ│100μ│ 2000μs │ 500μs │100μ│1μ│
|
||||
└──┴───┴────┴─────────────────────────┴─────────┴────┴──┘
|
||||
│
|
||||
All 6 Rule Checks
|
||||
(Multiple DB Queries)
|
||||
```
|
||||
|
||||
### Dual Authentication Architecture
|
||||
|
||||
The system supports **two authentication modes** that can work independently or together:
|
||||
|
||||
#### **NIP-42 Authentication (Kind 22242)**
|
||||
- **Purpose**: Client identity authentication ("who you are")
|
||||
- **Use Case**: Relay client authentication, WebSocket connections
|
||||
- **Event Structure**: Contains `relay` URL and `challenge` tags
|
||||
- **Flow**: Challenge/response pattern with relay-generated challenges
|
||||
- **Validation**: Verifies client identity against relay URL and challenge
|
||||
- **Database Storage**: Client sessions and challenge tracking
|
||||
|
||||
#### **Blossom Protocol Authentication (Kind 24242)**
|
||||
- **Purpose**: Operation authorization ("what you can do")
|
||||
- **Use Case**: File upload/delete/list operations
|
||||
- **Event Structure**: Contains operation tag `t=upload|delete|list` and content hash `x=hash`
|
||||
- **Flow**: Direct operation authorization with expiration
|
||||
- **Validation**: Verifies operation permissions and content integrity
|
||||
- **Database Storage**: Operation-specific authentication rules
|
||||
|
||||
#### **Integration Strategy**
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ NIP-42 Auth │ │ Blossom Auth │
|
||||
│ (Kind 22242) │ │ (Kind 24242) │
|
||||
│ │ │ │
|
||||
│ Client Identity │ │ Operation Perms │
|
||||
│ Challenge/Resp │ │ File Operations │
|
||||
└─────────┬───────┘ └─────────┬───────┘
|
||||
│ │
|
||||
└──────┬─────────┬─────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐
|
||||
│ Unified Rules │
|
||||
│ Engine │
|
||||
│ │
|
||||
│ • Pubkey Rules │
|
||||
│ • Hash Rules │
|
||||
│ • MIME Rules │
|
||||
│ • Size Limits │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
#### **Event Kind Processing**
|
||||
- **Kind 22242** (NIP-42): Validates against stored challenge + relay URL
|
||||
- **Kind 24242** (Blossom): Validates operation tags + content integrity
|
||||
- **Other Kinds**: Skip Nostr validation, proceed to rule evaluation
|
||||
- **Invalid Kind**: Reject immediately
|
||||
|
||||
#### **Dual Mode Benefits**
|
||||
- **Backwards Compatibility**: Existing Blossom clients continue working
|
||||
- **Enhanced Security**: NIP-42 provides cryptographic client identity
|
||||
- **Flexible Deployment**: Relays can require either or both methods
|
||||
- **Performance**: Separate validation paths optimize for each use case
|
||||
|
||||
### Request Processing Flow (DDoS-Optimized)
|
||||
|
||||
The authentication system is designed with **performance and DDoS protection** as primary concerns. Here's the exact order of operations:
|
||||
|
||||
#### Phase 1: Input Validation (Immediate Rejection)
|
||||
1. **Null Pointer Checks** - Reject malformed requests instantly (lines 122-128)
|
||||
2. **Initialization Check** - Verify system is properly initialized
|
||||
3. **Basic Structure Validation** - Ensure required fields are present
|
||||
|
||||
#### Phase 2: Nostr Event Validation (CPU Intensive)
|
||||
4. **Authorization Header Parsing** (lines 139-148)
|
||||
- Extract base64-encoded Nostr event from `Authorization: Nostr <base64>` header
|
||||
- Decode base64 to JSON (memory allocation + decoding)
|
||||
- **Early exit**: Invalid base64 or malformed header rejected immediately
|
||||
|
||||
5. **JSON Parsing** (lines 150-156)
|
||||
- Parse Nostr event JSON using cJSON
|
||||
- **Early exit**: Invalid JSON rejected before signature verification
|
||||
|
||||
6. **Nostr Event Structure Validation** (lines 159-166)
|
||||
- Validate event has required fields (kind, pubkey, sig, etc.)
|
||||
- **Early exit**: Invalid structure rejected before expensive crypto operations
|
||||
|
||||
7. **Event Kind Routing** (NEW - Dual Authentication)
|
||||
- **Kind 22242** (NIP-42): Route to NIP-42 challenge validation
|
||||
- **Kind 24242** (Blossom): Route to Blossom operation validation
|
||||
- **Other Kinds**: Skip Nostr validation, proceed to rules
|
||||
- **Invalid Kind**: Reject immediately
|
||||
|
||||
8. **NIP-42 Challenge Validation** (Kind 22242 Only)
|
||||
- Validate `relay` tag matches configured relay URL
|
||||
- Verify `challenge` tag exists and matches stored challenge
|
||||
- Check challenge expiration and single-use constraints
|
||||
- **Performance**: ~500μs additional validation overhead
|
||||
|
||||
9. **Cryptographic Signature Verification** (Both Paths)
|
||||
- **Most CPU-intensive operation** - ECDSA signature verification
|
||||
- Validates event authenticity using secp256k1
|
||||
- **Early exit**: Invalid signatures rejected before database queries
|
||||
|
||||
10. **Operation-Specific Validation** (Kind 24242 Only)
|
||||
- Verify event authorizes the requested operation (upload/delete/list)
|
||||
- Check required tags (t=operation, x=hash, expiration)
|
||||
- Validate timestamp and expiration
|
||||
- **Early exit**: Expired or mismatched events rejected
|
||||
|
||||
11. **Public Key Extraction** (Both Paths)
|
||||
- Extract validated public key from event for rule evaluation
|
||||
- Store authentication context (NIP-42 vs Blossom) for rule processing
|
||||
|
||||
#### Phase 3: Authentication Rules (Database Queries)
|
||||
10. **Rules System Check** (line 191)
|
||||
- Quick config check if authentication rules are enabled
|
||||
- **Early exit**: If disabled, allow request immediately
|
||||
|
||||
11. **Cache Lookup** (lines 1051-1054)
|
||||
- Generate SHA-256 cache key from request parameters
|
||||
- Check SQLite cache for previous decision
|
||||
- **Early exit**: Cache hit returns cached decision (5-minute TTL)
|
||||
|
||||
12. **Rule Evaluation** (Priority Order - lines 1061-1094):
|
||||
- **a. Pubkey Blacklist** (highest priority) - Immediate denial if matched
|
||||
- **b. Hash Blacklist** - Block specific content hashes
|
||||
- **c. MIME Type Blacklist** - Block dangerous file types
|
||||
- **d. File Size Limits** - Enforce upload size restrictions
|
||||
- **e. Pubkey Whitelist** - Allow specific users (only if not denied above)
|
||||
- **f. MIME Type Whitelist** - Allow specific file types
|
||||
|
||||
13. **Whitelist Default Denial** (lines 1097-1121)
|
||||
- If whitelist rules exist but none matched, deny request
|
||||
- Prevents whitelist bypass attacks
|
||||
|
||||
14. **Cache Storage** (line 1124)
|
||||
- Store decision in cache for future requests (5-minute TTL)
|
||||
|
||||
### DDoS Protection Features
|
||||
|
||||
#### **Fail-Fast Design**
|
||||
- **Input validation** happens before any expensive operations
|
||||
- **Authorization header parsing** fails fast on malformed data
|
||||
- **JSON parsing** rejects invalid data before signature verification
|
||||
- **Structure validation** happens before cryptographic operations
|
||||
|
||||
#### **Expensive Operations Last**
|
||||
- **Signature verification** only after structure validation
|
||||
- **Database queries** only after successful Nostr validation
|
||||
- **Cache prioritized** over database queries
|
||||
|
||||
#### **Caching Strategy**
|
||||
- **SHA-256 cache keys** prevent cache pollution attacks
|
||||
- **5-minute TTL** balances performance with rule changes
|
||||
- **LRU eviction** prevents memory exhaustion
|
||||
- **Per-request caching** includes all parameters (pubkey, operation, hash, MIME, size)
|
||||
|
||||
#### **Resource Limits**
|
||||
- **JSON parsing** limited to 4KB buffer size
|
||||
- **Cache entries** limited to prevent memory exhaustion
|
||||
- **Database connection pooling** (single connection with proper cleanup)
|
||||
- **String length limits** on all inputs
|
||||
|
||||
#### **Attack Mitigation**
|
||||
- **Base64 bombs** - Limited decode buffer size (4KB)
|
||||
- **JSON bombs** - cJSON library handles malformed JSON safely
|
||||
- **Cache poisoning** - Cryptographic cache keys prevent collisions
|
||||
- **Rule bypass** - Whitelist default denial prevents unauthorized access
|
||||
- **Replay attacks** - Timestamp and expiration validation
|
||||
- **Hash collision attacks** - Full SHA-256 verification
|
||||
|
||||
### Performance Characteristics
|
||||
|
||||
#### **Best Case** (Cached Decision):
|
||||
1. Input validation: ~1μs
|
||||
2. Cache lookup: ~100μs (SQLite SELECT)
|
||||
3. **Total: ~101μs**
|
||||
|
||||
#### **Worst Case** (Full Validation + Rule Evaluation):
|
||||
1. Input validation: ~1μs
|
||||
2. Base64 decoding: ~50μs
|
||||
3. JSON parsing: ~100μs
|
||||
4. Signature verification: ~2000μs (ECDSA)
|
||||
5. Database queries: ~500μs (6 rule checks)
|
||||
6. Cache storage: ~100μs
|
||||
7. **Total: ~2751μs (~2.7ms)**
|
||||
|
||||
#### **Typical Case** (Valid Request, Rules Enabled):
|
||||
1. Full validation: ~2200μs
|
||||
2. Cache miss, 2-3 rule checks: ~200μs
|
||||
3. **Total: ~2400μs (~2.4ms)**
|
||||
|
||||
### Security Order Rationale
|
||||
|
||||
The rule evaluation order is specifically designed for security:
|
||||
|
||||
1. **Blacklists First** - Immediate denial of known bad actors
|
||||
2. **Resource Limits** - Prevent resource exhaustion attacks
|
||||
3. **Whitelists Last** - Only allow after passing all security checks
|
||||
4. **Default Deny** - If whitelists exist but don't match, deny
|
||||
|
||||
This ensures that even if an attacker bypasses one layer, subsequent layers will catch the attack.
|
||||
509
docs/BLOSSOM_FLOW.md
Normal file
509
docs/BLOSSOM_FLOW.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# Blossom Protocol Flow Charts
|
||||
|
||||
This document provides ASCII flow charts illustrating how each Blossom Upgrade Document (BUD) works in practice.
|
||||
|
||||
---
|
||||
|
||||
## BUD-01: Basic Blob Retrieval
|
||||
|
||||
### GET Request Flow
|
||||
```
|
||||
Client Request nginx Database
|
||||
| | |
|
||||
| GET /<sha256> | |
|
||||
|--------------------------->| |
|
||||
| | |
|
||||
| | Check file exists |
|
||||
| | in blobs/ directory |
|
||||
| | |
|
||||
| File Found | |
|
||||
|<---------------------------| |
|
||||
| 200 OK + File Content | |
|
||||
| | |
|
||||
| File Not Found | |
|
||||
|<---------------------------| |
|
||||
| 404 Not Found | |
|
||||
```
|
||||
|
||||
### HEAD Request Flow
|
||||
```
|
||||
Client Request FastCGI App Database
|
||||
| | |
|
||||
| HEAD /<sha256> | |
|
||||
|------------------------->| |
|
||||
| | |
|
||||
| | Query blob metadata |
|
||||
| |-------------------------->|
|
||||
| | |
|
||||
| | Blob exists |
|
||||
| |<--------------------------|
|
||||
| | size, type, uploaded_at |
|
||||
| | |
|
||||
| 200 OK + Headers | |
|
||||
|<-------------------------| |
|
||||
| Content-Type: image/png | |
|
||||
| Content-Length: 12345 | |
|
||||
| | |
|
||||
| | Blob not found |
|
||||
| |<--------------------------|
|
||||
| | |
|
||||
| 404 Not Found | |
|
||||
|<-------------------------| |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-02: Blob Upload & Authentication
|
||||
|
||||
### Upload Without Authentication
|
||||
```
|
||||
Client FastCGI App File System Database
|
||||
| | | |
|
||||
| PUT /upload | | |
|
||||
|--------------------------->| | |
|
||||
| Content: <binary data> | | |
|
||||
| | | |
|
||||
| | Calculate SHA-256 | |
|
||||
| | hash of data | |
|
||||
| | | |
|
||||
| | Write to blobs/ | |
|
||||
| |-------------------------->| |
|
||||
| | | File saved |
|
||||
| |<--------------------------| |
|
||||
| | | |
|
||||
| | Store metadata | |
|
||||
| |----------------------------------------------->|
|
||||
| | | | INSERT |
|
||||
| |<-----------------------------------------------|
|
||||
| | | |
|
||||
| 200 OK | | |
|
||||
|<---------------------------| | |
|
||||
| { | | |
|
||||
| "url": "https://...", | | |
|
||||
| "sha256": "abc123...", | | |
|
||||
| "size": 12345, | | |
|
||||
| "type": "image/png", | | |
|
||||
| "uploaded": 1234567890 | | |
|
||||
| } | | |
|
||||
```
|
||||
|
||||
### Upload With Nostr Authentication
|
||||
```
|
||||
Client FastCGI App Nostr Validation File System Database
|
||||
| | | | |
|
||||
| PUT /upload | | | |
|
||||
|--------------------------->| | | |
|
||||
| Authorization: Nostr <evt> | | | |
|
||||
| Content: <binary data> | | | |
|
||||
| | | | |
|
||||
| | Parse auth event | | |
|
||||
| |-------------------------->| | |
|
||||
| | | • Verify signature | |
|
||||
| | | • Check expiration | |
|
||||
| | | • Validate tags | |
|
||||
| |<--------------------------| | |
|
||||
| | Valid ✓ | | |
|
||||
| | | | |
|
||||
| | Calculate hash | | |
|
||||
| | | | |
|
||||
| | Compare with 'x' tag | | |
|
||||
| | in auth event | | |
|
||||
| | | | |
|
||||
| | Hash matches ✓ | | |
|
||||
| | | | |
|
||||
| | Save file | | |
|
||||
| |----------------------------------------------->| |
|
||||
| | | | Write | |
|
||||
| |<-----------------------------------------------| |
|
||||
| | | | |
|
||||
| | Store metadata | | |
|
||||
| |-------------------------------------------------------------->|
|
||||
| | (include uploader_pubkey) | | | INSERT |
|
||||
| |<--------------------------------------------------------------|
|
||||
| | | | |
|
||||
| 200 OK + Blob Descriptor | | | |
|
||||
|<---------------------------| | | |
|
||||
```
|
||||
|
||||
### Authentication Failure Flows
|
||||
```
|
||||
Invalid Signature:
|
||||
Client --> FastCGI --> Nostr Validation --> 401 Unauthorized
|
||||
|
||||
Expired Event:
|
||||
Client --> FastCGI --> Nostr Validation --> 401 Unauthorized
|
||||
|
||||
Hash Mismatch:
|
||||
Client --> FastCGI --> Hash Check --> 409 Conflict
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-03: User Server Lists
|
||||
|
||||
### Server List Publication
|
||||
```
|
||||
User/Client Nostr Relay Other Clients
|
||||
| | |
|
||||
| Publish kind:10063 | |
|
||||
| event with server tags | |
|
||||
|------------------------>| |
|
||||
| { | |
|
||||
| "kind": 10063, | |
|
||||
| "tags": [ | |
|
||||
| ["server", "cdn1"], | |
|
||||
| ["server", "cdn2"] | |
|
||||
| ] | |
|
||||
| } | |
|
||||
| | |
|
||||
| | Store event |
|
||||
| | |
|
||||
| | |
|
||||
| | Query for user's |
|
||||
| | server list |
|
||||
| |<--------------------------|
|
||||
| | |
|
||||
| | Return server list |
|
||||
| |-------------------------->|
|
||||
```
|
||||
|
||||
### Client Blob Discovery
|
||||
```
|
||||
Client Original URL Author's Servers
|
||||
| | |
|
||||
| GET blob from URL | |
|
||||
|------------------------->| |
|
||||
| | |
|
||||
| 404 Not Found | |
|
||||
|<-------------------------| |
|
||||
| | |
|
||||
| Extract SHA-256 hash | |
|
||||
| from URL | |
|
||||
| | |
|
||||
| Query nostr for author's | |
|
||||
| server list (kind:10063) | |
|
||||
| | |
|
||||
| Try each server in order | |
|
||||
|--------------------------------------------------->|
|
||||
| | |
|
||||
| GET /<sha256> | |
|
||||
| | |
|
||||
| 200 OK + File Content | |
|
||||
|<---------------------------------------------------|
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-04: Blob Mirroring
|
||||
|
||||
### Mirror Request Flow
|
||||
```
|
||||
Client Server B Server A Database
|
||||
| | | |
|
||||
| PUT /mirror | | |
|
||||
|------------------------>| | |
|
||||
| { | | |
|
||||
| "url": "https:// | | |
|
||||
| serverA/abc123..." | | |
|
||||
| } | | |
|
||||
| Authorization: <event> | | |
|
||||
| | | |
|
||||
| | Validate auth event | |
|
||||
| | (check x tag matches) | |
|
||||
| | | |
|
||||
| | Download from URL | |
|
||||
| |------------------------>| |
|
||||
| | | |
|
||||
| | Stream blob content | |
|
||||
| |<------------------------| |
|
||||
| | | |
|
||||
| | Calculate SHA-256 | |
|
||||
| | during download | |
|
||||
| | | |
|
||||
| | Verify hash matches | |
|
||||
| | x tag in auth event | |
|
||||
| | | |
|
||||
| | Save blob locally | |
|
||||
| | | |
|
||||
| | Store metadata | |
|
||||
| |------------------------------------------------>|
|
||||
| | | | INSERT |
|
||||
| |<------------------------------------------------|
|
||||
| | | |
|
||||
| 200 OK | | |
|
||||
|<------------------------| | |
|
||||
| Blob Descriptor | | |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-05: Media Optimization
|
||||
|
||||
### Media Processing Flow
|
||||
```
|
||||
Client Media Server Optimization Engine File System
|
||||
| | | |
|
||||
| PUT /media | | |
|
||||
|------------------------>| | |
|
||||
| Content: <raw image> | | |
|
||||
| Content-Type: image/png | | |
|
||||
| Authorization: <event> | | |
|
||||
| | | |
|
||||
| | Validate auth | |
|
||||
| | (type="media") | |
|
||||
| | | |
|
||||
| | Process media | |
|
||||
| |------------------------>| |
|
||||
| | | • Resize/compress |
|
||||
| | | • Format conversion |
|
||||
| | | • Quality optimization|
|
||||
| |<------------------------| Optimized media |
|
||||
| | | |
|
||||
| | Calculate new hash | |
|
||||
| | | |
|
||||
| | Save optimized blob | |
|
||||
| |------------------------------------------------>|
|
||||
| | | | Write |
|
||||
| |<------------------------------------------------|
|
||||
| | | |
|
||||
| 200 OK | | |
|
||||
|<------------------------| | |
|
||||
| { | | |
|
||||
| "url": "new_hash...", | | |
|
||||
| "sha256": "def456...",| | |
|
||||
| "size": 8765, | | |
|
||||
| "type": "image/webp" | | |
|
||||
| } | | |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-06: Upload Requirements
|
||||
|
||||
### Upload Requirement Check
|
||||
```
|
||||
Client FastCGI App Configuration
|
||||
| | |
|
||||
| HEAD /upload | |
|
||||
|------------------------>| |
|
||||
| Authorization: <event> | |
|
||||
| (optional) | |
|
||||
| | |
|
||||
| | Check server config |
|
||||
| |------------------------>|
|
||||
| | | • Max file size
|
||||
| | | • Auth required?
|
||||
| | | • Allowed types
|
||||
| |<------------------------| • Rate limits
|
||||
| | |
|
||||
| | Validate auth if |
|
||||
| | provided |
|
||||
| | |
|
||||
| 200 OK | |
|
||||
|<------------------------| |
|
||||
| X-Upload-Size-Limit: | |
|
||||
| 10485760 | |
|
||||
| X-Upload-Auth-Required: | |
|
||||
| true | |
|
||||
| X-Upload-Types: | |
|
||||
| image/*,video/* | |
|
||||
```
|
||||
|
||||
### Upload Policy Enforcement
|
||||
```
|
||||
Client FastCGI App Policy Check
|
||||
| | |
|
||||
| PUT /upload | |
|
||||
|------------------------>| |
|
||||
| Content-Length: 50MB | |
|
||||
| | |
|
||||
| | Check against limits |
|
||||
| |------------------------>|
|
||||
| | | Size: 50MB > 10MB ✗
|
||||
| |<------------------------| REJECT
|
||||
| | |
|
||||
| 413 Payload Too Large | |
|
||||
|<------------------------| |
|
||||
| { | |
|
||||
| "error": "File too | |
|
||||
| large. Max: 10MB" | |
|
||||
| } | |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-07: Paid Upload/Download
|
||||
|
||||
### Payment Required Flow
|
||||
```
|
||||
Client Paid Server Payment Provider
|
||||
| | |
|
||||
| PUT /upload | |
|
||||
|------------------------>| |
|
||||
| | |
|
||||
| | Check payment required |
|
||||
| | |
|
||||
| 402 Payment Required | |
|
||||
|<------------------------| |
|
||||
| X-Lightning: lnbc... | |
|
||||
| X-Cashu: creq... | |
|
||||
| | |
|
||||
| User pays invoice | |
|
||||
|-------------------------------------------------->|
|
||||
| | |
|
||||
| PUT /upload (retry) | |
|
||||
|------------------------>| |
|
||||
| X-Lightning: <preimage> | |
|
||||
| | |
|
||||
| | Verify payment proof |
|
||||
| |------------------------>|
|
||||
| | | Valid ✓
|
||||
| |<------------------------|
|
||||
| | |
|
||||
| | Process upload |
|
||||
| | |
|
||||
| 200 OK + Blob Desc | |
|
||||
|<------------------------| |
|
||||
```
|
||||
|
||||
### Payment Methods
|
||||
```
|
||||
Lightning Payment:
|
||||
Client --> Server --> Lightning Node --> Payment Verification --> Upload Success
|
||||
|
||||
Cashu Payment:
|
||||
Client --> Server --> Cashu Mint --> Token Validation --> Upload Success
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-08: NIP-94 File Metadata
|
||||
|
||||
### Enhanced Blob Descriptor
|
||||
```
|
||||
Client Upload FastCGI App Metadata Generation Response
|
||||
| | | |
|
||||
| PUT /upload | | |
|
||||
|---------------------> | | |
|
||||
| | | |
|
||||
| | Process file | |
|
||||
| | | |
|
||||
| | Generate NIP-94 tags | |
|
||||
| |------------------------>| |
|
||||
| | | • ["url", "..."] |
|
||||
| | | • ["m", "image/png"] |
|
||||
| | | • ["x", "hash..."] |
|
||||
| | | • ["size", "12345"] |
|
||||
| | | • ["magnet", "..."] |
|
||||
| |<------------------------| NIP-94 tags |
|
||||
| | | |
|
||||
| | Build response | |
|
||||
| |------------------------------------------------>|
|
||||
| Enhanced Response | | |
|
||||
|<--------------------- | | |
|
||||
| { | | |
|
||||
| "url": "...", | | |
|
||||
| "sha256": "...", | | |
|
||||
| "nip94": [ | | |
|
||||
| ["url", "..."], | | |
|
||||
| ["m", "..."], | | |
|
||||
| ["x", "..."] | | |
|
||||
| ] | | |
|
||||
| } | | |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BUD-09: Blob Reporting
|
||||
|
||||
### Content Moderation Flow
|
||||
```
|
||||
Client/User FastCGI App Moderation System Action
|
||||
| | | |
|
||||
| PUT /report | | |
|
||||
|---------------------> | | |
|
||||
| NIP-56 report event | | |
|
||||
| { | | |
|
||||
| "kind": 1984, | | |
|
||||
| "tags": [ | | |
|
||||
| ["x", "hash..."] | | |
|
||||
| ], | | |
|
||||
| "content": "spam" | | |
|
||||
| } | | |
|
||||
| | | |
|
||||
| | Validate report | |
|
||||
| | | |
|
||||
| | Store report | |
|
||||
| |------------------------>| |
|
||||
| | | • Log report |
|
||||
| | | • Check reporter |
|
||||
| | | • Queue for review |
|
||||
| |<------------------------| Stored |
|
||||
| | | |
|
||||
| 200 OK | | |
|
||||
|<--------------------- | | |
|
||||
| | | |
|
||||
| | | Manual Review |
|
||||
| | |---------------------> |
|
||||
| | | | Remove blob
|
||||
| | | | Block hash
|
||||
| | | | Ban user
|
||||
```
|
||||
|
||||
### Automated Moderation
|
||||
```
|
||||
Trusted Reporter Report --> Immediate Action --> Blob Removed
|
||||
|
||||
Multiple Reports --> Temporary Hide --> Manual Review --> Final Decision
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Ginxsom Architecture Flow
|
||||
|
||||
### Nginx + FastCGI Integration
|
||||
```
|
||||
Internet nginx FastCGI App Database File System
|
||||
| | | | |
|
||||
| GET /<sha256> | | | |
|
||||
|-------------------->| | | |
|
||||
| | Direct file serve | | |
|
||||
| |----------------------------------------->| |
|
||||
| | | | blobs/ |
|
||||
| File Content |<-----------------------------------------| <hash> |
|
||||
|<--------------------| | | |
|
||||
| | | | |
|
||||
| HEAD /<sha256> | | | |
|
||||
|-------------------->| | | |
|
||||
| | Forward to FastCGI | | |
|
||||
| |-------------------->| | |
|
||||
| | | Query metadata | |
|
||||
| | |------------------->| |
|
||||
| | | | SELECT |
|
||||
| | |<-------------------| |
|
||||
| | Headers response | | |
|
||||
| |<--------------------| | |
|
||||
| Metadata Headers | | | |
|
||||
|<--------------------| | | |
|
||||
| | | | |
|
||||
| PUT /upload | | | |
|
||||
|-------------------->| | | |
|
||||
| | Forward to FastCGI | | |
|
||||
| |-------------------->| | |
|
||||
| | | Process upload | |
|
||||
| | | | |
|
||||
| | | Store metadata | |
|
||||
| | |------------------->| |
|
||||
| | | | INSERT |
|
||||
| | |<-------------------| |
|
||||
| | | | |
|
||||
| | | Save file | |
|
||||
| | |-------------------------------------->|
|
||||
| | | | Write |
|
||||
| | |<--------------------------------------|
|
||||
| | JSON response | | |
|
||||
| |<--------------------| | |
|
||||
| Blob Descriptor | | | |
|
||||
|<--------------------| | | |
|
||||
```
|
||||
|
||||
368
docs/FASTCGI.md
Normal file
368
docs/FASTCGI.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# FastCGI Protocol Flow Documentation
|
||||
|
||||
This document provides ASCII flow charts to understand how FastCGI works with nginx for the ginxsom blossom server.
|
||||
|
||||
## FastCGI Overview
|
||||
|
||||
FastCGI is a binary protocol that allows web servers (like nginx) to communicate with application servers efficiently. Unlike CGI which spawns a new process per request, FastCGI applications are persistent and can handle multiple concurrent requests.
|
||||
|
||||
## 1. FastCGI Connection Setup Flow
|
||||
|
||||
```
|
||||
┌─────────────────┐ Socket ┌─────────────────┐
|
||||
│ nginx │◄──────────────►│ FastCGI App │
|
||||
│ Web Server │ Connection │ (ginxsom) │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
│ │
|
||||
│ 1. Create Unix socket │
|
||||
│ /tmp/ginxsom.sock │
|
||||
│ │
|
||||
│ 2. FastCGI app binds & listens │
|
||||
│◄───────────────────────────────────│
|
||||
│ │
|
||||
│ 3. nginx connects when needed │
|
||||
│───────────────────────────────────►│
|
||||
│ │
|
||||
│ 4. Connection established │
|
||||
│◄──────────────────────────────────►│
|
||||
```
|
||||
|
||||
## 2. HTTP Request Processing Flow
|
||||
|
||||
```
|
||||
┌─────────┐ HTTP ┌─────────┐ FastCGI ┌─────────┐
|
||||
│ Client │────────────►│ nginx │─────────────►│ ginxsom │
|
||||
│Browser │ │ │ │ FastCGI │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │
|
||||
│ HTTP Request │ │
|
||||
│ PUT /upload │ │
|
||||
│────────────────────────► │
|
||||
│ │ │
|
||||
│ │ FCGI_BEGIN_REQUEST │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ FCGI_PARAMS │
|
||||
│ │ (HTTP headers, etc) │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ FCGI_STDIN │
|
||||
│ │ (Request body) │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ Process │
|
||||
│ │◄───────Request─────────│
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ (Response headers) │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ (Response body) │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ │ FCGI_END_REQUEST │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ HTTP Response │ │
|
||||
│◄──────────────────────│ │
|
||||
```
|
||||
|
||||
## 3. FastCGI Record Structure
|
||||
|
||||
```
|
||||
FastCGI Record Format:
|
||||
┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Version │ Type │RequestID│RequestID│ Length │ Length │ Padding │Reserved │
|
||||
│ (1) │ (1) │ Hi │ Lo │ Hi │ Lo │ Length │ (1) │
|
||||
│ │ │ (1) │ (1) │ (1) │ (1) │ (1) │ │
|
||||
└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
│◄────────────────── 8 bytes header ──────────────────────►│
|
||||
│ │
|
||||
│◄─────────────────── Content (Length bytes) ─────────────►│
|
||||
│ │
|
||||
│◄───── Padding (Padding Length bytes) ────►│
|
||||
|
||||
Record Types:
|
||||
- FCGI_BEGIN_REQUEST (1) - Start new request
|
||||
- FCGI_PARAMS (4) - Environment variables
|
||||
- FCGI_STDIN (5) - Request body data
|
||||
- FCGI_STDOUT (6) - Response data
|
||||
- FCGI_END_REQUEST (3) - End of request
|
||||
```
|
||||
|
||||
## 4. Ginxsom Endpoint Handling Flow
|
||||
|
||||
### 4a. Static File Request (Direct nginx)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │
|
||||
└─────────┘ └─────────┘
|
||||
│ │
|
||||
│ GET /{sha256hash} │
|
||||
│───────────────────────►│
|
||||
│ │
|
||||
│ │ Check file exists:
|
||||
│ │ /var/lib/ginxsom/files/
|
||||
│ │ {first2}/{remaining}
|
||||
│ │
|
||||
│ │ ┌─ File exists? ─┐
|
||||
│ │ │ YES │
|
||||
│ │ └────────────────┘
|
||||
│ │ │
|
||||
│ HTTP 200 + File │ │ Serve directly
|
||||
│◄──────────────────────│◄──────┘
|
||||
│ │
|
||||
|
||||
Alternative flow:
|
||||
│ │ ┌─ File exists? ─┐
|
||||
│ │ │ NO │
|
||||
│ │ └────────────────┘
|
||||
│ │ │
|
||||
│ HTTP 404 Not Found │ │
|
||||
│◄──────────────────────│◄──────┘
|
||||
```
|
||||
|
||||
### 4b. Upload Request (FastCGI)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │ │ ginxsom │ │ File │
|
||||
│ │ │ │ │ FastCGI │ │ System │
|
||||
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │ │
|
||||
│ PUT /upload │ │ │
|
||||
│ Auth: xyz │ │ │
|
||||
│ Body: file │ │ │
|
||||
│─────────────►│ │ │
|
||||
│ │ │ │
|
||||
│ │ FCGI_PARAMS │ │
|
||||
│ │ METHOD=PUT │ │
|
||||
│ │ URI=/upload │ │
|
||||
│ │ AUTH=xyz │ │
|
||||
│ │─────────────►│ │
|
||||
│ │ │ │
|
||||
│ │ FCGI_STDIN │ │
|
||||
│ │ (file data) │ │
|
||||
│ │─────────────►│ │
|
||||
│ │ │ │
|
||||
│ │ │ Verify Auth │
|
||||
│ │ │ (nostr sig) │
|
||||
│ │ │ │
|
||||
│ │ │ Calculate │
|
||||
│ │ │ SHA-256 │
|
||||
│ │ │ │
|
||||
│ │ │ Write file │
|
||||
│ │ │─────────────►│
|
||||
│ │ │ │
|
||||
│ │ │ Store │
|
||||
│ │ │ metadata │
|
||||
│ │ │────────────────────►
|
||||
│ │ │ │ DB
|
||||
│ │ │ │
|
||||
│ │ FCGI_STDOUT │ │
|
||||
│ │ 200 OK │ │
|
||||
│ │ {sha256} │ │
|
||||
│ │◄─────────────│ │
|
||||
│ │ │ │
|
||||
│ HTTP 200 OK │ │ │
|
||||
│ {sha256} │ │ │
|
||||
│◄─────────────│ │ │
|
||||
```
|
||||
|
||||
### 4c. HEAD Request for Metadata (FastCGI)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │ │ ginxsom │
|
||||
│ │ │ │ │ FastCGI │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │
|
||||
│ HEAD /{sha256} │ │
|
||||
│──────────────────►│ │
|
||||
│ │ │
|
||||
│ │ FCGI_PARAMS │
|
||||
│ │ METHOD=HEAD │
|
||||
│ │ BLOSSOM_HASH=... │
|
||||
│ │──────────────────►│
|
||||
│ │ │
|
||||
│ │ │ Query DB for
|
||||
│ │ │ file metadata
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ Content-Length: X │
|
||||
│ │ Content-Type: Y │
|
||||
│ │ x-sha256: hash │
|
||||
│ │◄──────────────────│
|
||||
│ │ │
|
||||
│ HTTP Headers Only │ │
|
||||
│◄──────────────────│ │
|
||||
```
|
||||
|
||||
## 5. Development vs Production Deployment
|
||||
|
||||
### Development Mode
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Terminal │ │ nginx │
|
||||
│ │ │ │
|
||||
│ ./ginxsom │◄──────────────►│ Config: │
|
||||
│ --fastcgi │ Unix Socket │ fastcgi_pass│
|
||||
│ --socket │ /tmp/ginxsom │ unix:/tmp/ │
|
||||
│ /tmp/... │ .sock │ ginxsom.sock│
|
||||
└─────────────┘ └─────────────┘
|
||||
│ │
|
||||
│ Direct execution │ sudo systemctl
|
||||
│ Easy debugging │ reload nginx
|
||||
│ Ctrl+C to stop │ (config changes)
|
||||
│ Real-time logs │
|
||||
```
|
||||
|
||||
### Production Mode
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ systemd │ │ nginx │
|
||||
│ │ │ │
|
||||
│ ginxsom │◄──────────────►│ Config: │
|
||||
│ .service │ Unix Socket │ fastcgi_pass│
|
||||
│ │ /run/ginxsom/ │ unix:/run/ │
|
||||
│ Auto-start │ ginxsom.sock │ ginxsom/... │
|
||||
│ Auto-restart│ │ │
|
||||
│ Log to file │ │ │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
## 6. Complete nginx + FastCGI Request Flow
|
||||
|
||||
```
|
||||
┌───────┐ ┌─────────────────────────────────────────────────┐ ┌─────────┐
|
||||
│Client │ │ nginx │ │ginxsom │
|
||||
└───────┘ └─────────────────────────────────────────────────┘ │FastCGI │
|
||||
│ │ └─────────┘
|
||||
│ GET /abc123...def │ │
|
||||
│───────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ location ~ ^/([a-f0-9]{64})$ { │
|
||||
│ │ # Check if static file exists │
|
||||
│ │ try_files /$prefix/$suffix =404 │
|
||||
│ │ } │
|
||||
│ │ │
|
||||
│ │ ┌─ File exists? ─┐ │
|
||||
│ │ │ YES │ │
|
||||
│ │ └────────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ │ Serve directly (fast!) │
|
||||
│ HTTP 200 + File │ │ │
|
||||
│◄──────────────────────│◄──────┘ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ PUT /upload │ │
|
||||
│ Authorization: xyz │ │
|
||||
│───────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ location /upload { │
|
||||
│ │ fastcgi_pass unix:/tmp/ │
|
||||
│ │ ginxsom.sock; │
|
||||
│ │ } │
|
||||
│ │ │
|
||||
│ │ ┌─ FastCGI Protocol ─┐ │
|
||||
│ │ │ FCGI_BEGIN_REQUEST │─────────────►│
|
||||
│ │ │ FCGI_PARAMS │─────────────►│
|
||||
│ │ │ FCGI_STDIN │─────────────►│
|
||||
│ │ └─────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ │ Verify
|
||||
│ │ │ nostr
|
||||
│ │ │ signature
|
||||
│ │ │
|
||||
│ │ │ Calculate
|
||||
│ │ │ SHA-256
|
||||
│ │ │
|
||||
│ │ │ Store file
|
||||
│ │ │ & metadata
|
||||
│ │ │
|
||||
│ │ ┌─ FastCGI Response ─┐ │
|
||||
│ │ │ FCGI_STDOUT │◄─────────────│
|
||||
│ │ │ FCGI_END_REQUEST │◄─────────────│
|
||||
│ │ └─────────────────────┘ │
|
||||
│ │ │
|
||||
│ HTTP 200 OK │ │
|
||||
│ {"sha256": "..."} │ │
|
||||
│◄──────────────────────│ │
|
||||
```
|
||||
|
||||
## 7. libfcgi Library Usage
|
||||
|
||||
### Basic FastCGI Application Structure
|
||||
|
||||
```c
|
||||
#include <fcgiapp.h>
|
||||
|
||||
int main() {
|
||||
FCGX_Request request;
|
||||
|
||||
// Initialize FastCGI library
|
||||
FCGX_Init();
|
||||
FCGX_InitRequest(&request, 0, 0);
|
||||
|
||||
// Main request processing loop
|
||||
while (FCGX_Accept_r(&request) == 0) {
|
||||
|
||||
// Read environment variables (HTTP headers, etc)
|
||||
char* method = FCGX_GetParam("REQUEST_METHOD", request.envp);
|
||||
char* uri = FCGX_GetParam("REQUEST_URI", request.envp);
|
||||
char* auth = FCGX_GetParam("HTTP_AUTHORIZATION", request.envp);
|
||||
|
||||
// Route requests
|
||||
if (strcmp(uri, "/health") == 0) {
|
||||
handle_health(&request);
|
||||
} else if (strcmp(uri, "/upload") == 0) {
|
||||
handle_upload(&request);
|
||||
} else if (strncmp(uri, "/head/", 6) == 0) {
|
||||
handle_head(&request);
|
||||
}
|
||||
|
||||
// Finish this request
|
||||
FCGX_Finish_r(&request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Reading Request Data
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ nginx sends: │ │ ginxsom reads: │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGI_PARAMS │────────►│ FCGX_GetParam() │
|
||||
│ REQUEST_METHOD │ │ "PUT" │
|
||||
│ REQUEST_URI │ │ "/upload" │
|
||||
│ CONTENT_LENGTH │ │ "1024" │
|
||||
│ HTTP_* │ │ headers │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGI_STDIN │────────►│ FCGX_GetStr() │
|
||||
│ (request body) │ │ file data │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Writing Response Data
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ ginxsom writes: │ │ nginx sends: │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGX_PutS() │────────►│ HTTP Response │
|
||||
│ "Content-Type: │ │ Headers │
|
||||
│ application/ │ │ │
|
||||
│ json\r\n\r\n" │ │ │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGX_PutS() │────────►│ HTTP Response │
|
||||
│ '{"status": │ │ Body │
|
||||
│ "ok"}' │ │ │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
This documentation shows how FastCGI enables nginx to efficiently serve static blossom files directly while delegating authenticated operations to the ginxsom FastCGI application.
|
||||
287
docs/IMPLEMENTATION.md
Normal file
287
docs/IMPLEMENTATION.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# Ginxsom Blossom Server Implementation Status
|
||||
|
||||
This document tracks the implementation status of ginxsom, a high-performance FastCGI-based Blossom server designed to work with nginx.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
- **nginx**: Handles static file serving (GET /<sha256>) for maximum performance
|
||||
- **FastCGI Application**: Handles authenticated operations, metadata queries, uploads
|
||||
- **SQLite Database**: Stores blob metadata and server configuration
|
||||
- **File Storage**: Flat directory structure in `blobs/` directory
|
||||
|
||||
---
|
||||
|
||||
## BUD-01: Blob Retrieval ✅ **COMPLETE**
|
||||
|
||||
### Infrastructure & Database
|
||||
- [x] Create directory structure (`blobs/`, `db/`, `logs/`)
|
||||
- [x] SQLite schema with `blobs` table (sha256, size, type, uploaded_at, uploader_pubkey, filename)
|
||||
- [x] Database initialization scripts and proper indexes
|
||||
|
||||
### GET /<sha256> Endpoint
|
||||
- [x] nginx static file serving with extension support (.txt, .jpg, .png, etc.)
|
||||
- [x] Extension fallback via `try_files` directive
|
||||
- [x] Proper MIME type detection and headers
|
||||
- [x] Cache headers (Cache-Control, immutable)
|
||||
- [x] 404 handling for missing blobs
|
||||
|
||||
### HEAD /<sha256> Endpoint
|
||||
- [x] FastCGI metadata handler
|
||||
- [x] Database metadata queries
|
||||
- [x] Proper HTTP headers (Content-Type, Content-Length)
|
||||
- [x] SHA-256 extraction from URL paths
|
||||
- [x] 404 responses for missing blobs
|
||||
|
||||
### Testing Status
|
||||
- [x] File serving works with all supported extensions
|
||||
- [x] HEAD requests return correct metadata
|
||||
- [x] 404 responses for missing files
|
||||
- [ ] Performance testing with large files
|
||||
|
||||
---
|
||||
|
||||
## BUD-02: File Upload & Authentication ✅ **COMPLETE**
|
||||
|
||||
### Nostr Authentication System
|
||||
- [x] nostr_core_lib integration and compilation
|
||||
- [x] secp256k1 context initialization (CRITICAL BUG FIXED)
|
||||
- [x] Authentication functions:
|
||||
- [x] `parse_authorization_header()` - Extract JSON from "Nostr base64(event)"
|
||||
- [x] `validate_blossom_event()` - Validate kind 24242 events
|
||||
- [x] `authenticate_request()` - Main authentication orchestrator
|
||||
- [x] Enhanced error handling with specific error types (event_expired, invalid_signature, etc.)
|
||||
- [x] API refactoring - upgraded from low-level crypto to `nostr_crypto_init()` API
|
||||
|
||||
### PUT /upload Endpoint
|
||||
- [x] Authorization header parsing and validation
|
||||
- [x] File upload streaming to temporary location
|
||||
- [x] SHA-256 hash calculation during upload
|
||||
- [x] Hash validation against authorization
|
||||
- [x] File permissions (644) for nginx serving
|
||||
- [x] Database metadata storage (uploader_pubkey, filename)
|
||||
- [x] Blob descriptor JSON response
|
||||
|
||||
### GET /list/<pubkey> Endpoint
|
||||
- [x] Extract pubkey from URL path
|
||||
- [x] Database queries for user's blobs
|
||||
- [x] Optional authorization with kind 24242 event validation
|
||||
- [x] Support for `since`/`until` query parameters
|
||||
- [x] JSON array responses with blob descriptors
|
||||
|
||||
### DELETE /<sha256> Endpoint
|
||||
- [x] SHA-256 extraction from URL
|
||||
- [x] Required authorization with kind 24242 validation
|
||||
- [x] Ownership verification (uploader_pubkey matching)
|
||||
- [x] File and database cleanup
|
||||
- [x] Proper error handling for missing files
|
||||
|
||||
### Testing Status
|
||||
- [x] Upload with valid nostr authentication (HTTP 200)
|
||||
- [x] Upload without authentication (proper error responses)
|
||||
- [x] Hash mismatch validation (409 Conflict)
|
||||
- [x] List endpoint returns proper JSON
|
||||
- [x] Delete endpoint with ownership checks
|
||||
- [x] File retrieval after upload working
|
||||
- [ ] File size limit testing
|
||||
|
||||
---
|
||||
|
||||
## BUD-03: Server List (User Server Lists) ⚪ **FOR CLIENTS, NOT SERVERS**
|
||||
|
||||
## BUD-04: Blob Mirroring ✅ **COMPLETE**
|
||||
|
||||
### HTTP Client Implementation
|
||||
- [x] CURL library integration and HTTP client functions
|
||||
- [x] `write_callback()` - Download response data with dynamic buffering
|
||||
- [x] `header_callback()` - Extract Content-Type headers
|
||||
- [x] `download_blob_from_url()` - Complete HTTP download with security controls
|
||||
- [x] Memory management and error handling
|
||||
|
||||
### PUT /mirror Endpoint
|
||||
- [x] nginx endpoint configured (`PUT /mirror`)
|
||||
- [x] FastCGI routing and request handling
|
||||
- [x] JSON request body parsing (extract `url` field)
|
||||
- [x] URL validation and security checks (HTTPS-only, SSRF protection)
|
||||
- [x] Remote blob downloading with CURL
|
||||
- [x] SHA-256 hash calculation and verification
|
||||
- [x] Content-Type detection (headers, URL extension, file signature)
|
||||
- [x] File storage with proper extensions (.png, .jpg, etc.)
|
||||
- [x] Database metadata storage
|
||||
- [x] Blob descriptor JSON response
|
||||
|
||||
### Security Features
|
||||
- [x] HTTPS-only URL validation (no HTTP allowed)
|
||||
- [x] SSRF protection (blocks localhost, private IPs: 127.x, 192.168.x, 10.x, 172.16-31.x)
|
||||
- [x] File size limits (100MB maximum)
|
||||
- [x] Request timeouts (30s total, 10s connect)
|
||||
- [x] SSL certificate verification
|
||||
- [x] Authorization hash verification (when provided)
|
||||
|
||||
### Testing Status
|
||||
- [x] Mirror request with valid HTTPS URL (HTTP 200)
|
||||
- [x] Hash verification against downloaded content
|
||||
- [x] Content-Type detection from PNG file
|
||||
- [x] File accessibility after mirroring
|
||||
- [x] HEAD request metadata retrieval
|
||||
- [x] Error handling for invalid URLs
|
||||
- [x] Security validation (private IP blocking)
|
||||
|
||||
---
|
||||
|
||||
## BUD-05: Media Optimization ⚪ **PARTIAL**
|
||||
|
||||
### Current Status
|
||||
- [x] nginx endpoint configured (`HEAD/PUT /media`)
|
||||
- [x] FastCGI routing established
|
||||
- [ ] Media processing libraries integration
|
||||
- [ ] Optimization algorithms implementation
|
||||
- [ ] Multi-format media handling
|
||||
- [ ] Optimization pipeline testing
|
||||
|
||||
---
|
||||
|
||||
## BUD-06: Upload Requirements ✅ **COMPLETE**
|
||||
|
||||
### HEAD /upload Pre-flight Validation
|
||||
- [x] `HEAD /upload` endpoint implementation
|
||||
- [x] Client header parsing (X-SHA-256, X-Content-Length, X-Content-Type)
|
||||
- [x] Pre-flight validation without file transfer:
|
||||
- [x] SHA-256 format validation
|
||||
- [x] File size limit checking (100MB default)
|
||||
- [ ] MIME type restrictions (policy 415 not enforced yet)
|
||||
- [x] Authentication validation (optional via rules system)
|
||||
- [x] Duplicate detection (policy configurable)
|
||||
- [x] Banned hash checking (via rules engine)
|
||||
- [x] Proper HTTP status codes (200, 400, 401, 409, 411, 413; 415 reserved for future MIME policy)
|
||||
- [x] X-Reason headers for error messages
|
||||
|
||||
### Upload Policy Configuration
|
||||
- [ ] Server configuration system
|
||||
- [ ] Maximum file size limits (currently hard limit in code; move to config)
|
||||
- [ ] Allowed MIME type restrictions
|
||||
- [ ] Rate limiting implementation
|
||||
- [ ] DOS protection benefits
|
||||
|
||||
---
|
||||
|
||||
## BUD-07: Payment Integration ⚪ **NOT IMPLEMENTED**
|
||||
|
||||
*Optional feature - not currently planned*
|
||||
|
||||
- [ ] 402 Payment Required responses
|
||||
- [ ] Lightning payment support
|
||||
- [ ] Cashu payment integration
|
||||
- [ ] Payment verification flows
|
||||
|
||||
---
|
||||
|
||||
## BUD-08: NIP-94 File Metadata Tags ✅ **COMPLETE**
|
||||
|
||||
### NIP-94 Integration
|
||||
- [x] Configuration system with SQLite server_config table
|
||||
- [x] `nip94_enabled` configuration key (default: true)
|
||||
- [x] `cdn_origin` configuration key (default: "http://localhost:9001")
|
||||
- [x] Centralized MIME type to extension mapping [`mime_to_extension()`](src/main.c:2024)
|
||||
- [x] Canonical blob URL generation [`nip94_build_blob_url()`](src/main.c:2055)
|
||||
- [x] NIP-94 metadata field emission [`nip94_emit_field()`](src/main.c:2201)
|
||||
|
||||
### Image Dimension Detection
|
||||
- [x] PNG dimension parsing from IHDR chunk [`parse_png_dimensions()`](src/main.c:2065)
|
||||
- [x] JPEG dimension parsing from SOF0/SOF2 markers [`parse_jpeg_dimensions()`](src/main.c:2089)
|
||||
- [x] WebP dimension parsing for VP8/VP8L/VP8X formats [`parse_webp_dimensions()`](src/main.c:2141)
|
||||
- [x] Universal dimension dispatcher [`nip94_get_dimensions()`](src/main.c:2184)
|
||||
|
||||
### Integration Points
|
||||
- [x] PUT /upload endpoint enhanced with NIP-94 metadata
|
||||
- [x] PUT /mirror endpoint enhanced with NIP-94 metadata
|
||||
- [x] Configuration-driven origin override for CDN support
|
||||
- [x] Conditional metadata emission based on `nip94_enabled` setting
|
||||
- [x] Proper JSON comma handling for optional nip94 field
|
||||
|
||||
### NIP-94 Tags Implemented
|
||||
- [x] `url` tag - Canonical blob URL with proper origin and extension
|
||||
- [x] `m` tag - MIME type (Content-Type)
|
||||
- [x] `x` tag - SHA-256 hash (lowercase hex)
|
||||
- [x] `size` tag - File size in bytes
|
||||
- [x] `dim` tag - Image dimensions (e.g., "1x1", "1920x1080") when available
|
||||
|
||||
### Configuration Keys
|
||||
- `nip94_enabled` (boolean): Enable/disable NIP-94 metadata emission (default: true)
|
||||
- `cdn_origin` (string): Base URL for blob URLs (default: "http://localhost:9001")
|
||||
|
||||
### Testing Status
|
||||
- [x] NIP-94 minimal tags (url, m, x, size) present in responses
|
||||
- [x] Image dimension detection working for PNG 1x1 test case
|
||||
- [x] Configuration-based enable/disable functionality
|
||||
- [x] CDN origin override affecting both descriptor and nip94 URLs
|
||||
- [x] Mirror endpoint NIP-94 integration (network-dependent)
|
||||
|
||||
---
|
||||
|
||||
## BUD-09: Content Reporting ✅ **COMPLETE**
|
||||
|
||||
### NIP-56 Report Event System
|
||||
- [x] Full NIP-56 kind 1984 report event implementation
|
||||
- [x] Report event structure validation (kind, required fields, x tags)
|
||||
- [x] SHA-256 blob hash extraction from x tags [`extract_blob_hashes_from_report()`](src/main.c:2408)
|
||||
- [x] Report type validation (nudity, malware, profanity, illegal, spam, impersonation, other)
|
||||
- [x] Nostr cryptographic signature verification
|
||||
- [x] Event timestamp and expiration validation
|
||||
|
||||
### PUT /report Endpoint
|
||||
- [x] nginx endpoint configuration (`PUT /report`)
|
||||
- [x] FastCGI routing and request handling [`handle_report_request()`](src/main.c:2516)
|
||||
- [x] JSON request body parsing and validation
|
||||
- [x] Content-Type enforcement (application/json required)
|
||||
- [x] Request size limits (1 byte to 10KB)
|
||||
- [x] HTTP method validation (PUT only, 405 for others)
|
||||
|
||||
### Report Validation Features
|
||||
- [x] Event structure validation [`validate_report_event_structure()`](src/main.c:2355)
|
||||
- [x] NIP-56 compliance checking (kind 1984, x tags required)
|
||||
- [x] SHA-256 hash format validation (64-char hex)
|
||||
- [x] Report type extensibility (accepts unknown types per NIP-56)
|
||||
- [x] Multiple blob reporting (multiple x tags supported)
|
||||
- [x] Optional tag support (e, p, server tags)
|
||||
|
||||
### Report Storage System
|
||||
- [x] Optional report storage in database [`store_blob_report()`](src/main.c:2485)
|
||||
- [x] Reporter pubkey extraction and storage
|
||||
- [x] Report type and content preservation
|
||||
- [x] Timestamp tracking for moderation review
|
||||
- [x] Configurable storage policy (server MAY store reports)
|
||||
|
||||
### Error Handling & Security
|
||||
- [x] Comprehensive error responses with specific error codes:
|
||||
- `invalid_json` - Malformed JSON requests
|
||||
- `invalid_content_length` - Size limit violations
|
||||
- `invalid_report_event` - NIP-56 structure violations
|
||||
- `no_blob_hashes` - Missing or invalid SHA-256 hashes
|
||||
- `unsupported_media_type` - Non-JSON Content-Type
|
||||
- [x] HTTP status code compliance (200, 400, 405, 415)
|
||||
- [x] Detailed error messages with troubleshooting information
|
||||
|
||||
### NIP-56 Compliance Features
|
||||
- [x] Report types: nudity, malware, profanity, illegal, spam, impersonation, other
|
||||
- [x] Extensible report type system (unknown types accepted)
|
||||
- [x] Multiple blob reporting via multiple x tags
|
||||
- [x] Optional metadata tags (e, p, server)
|
||||
- [x] Content field support (reports may include descriptions)
|
||||
- [x] Cryptographic signature verification via nostr event validation
|
||||
|
||||
### Testing Status
|
||||
- [x] Comprehensive test suite with 22 test cases [`tests/report_test_bud09.sh`](tests/report_test_bud09.sh)
|
||||
- [x] All NIP-56 report types tested (nudity, malware, profanity, illegal, spam, impersonation, other)
|
||||
- [x] Event validation testing (missing x tags, wrong kind, invalid JSON)
|
||||
- [x] Error handling testing (empty body, wrong Content-Type, unsupported methods)
|
||||
- [x] Multiple blob reporting functionality
|
||||
- [x] Optional tag support with comprehensive NIP-56 structure
|
||||
- [x] Unknown report type extensibility
|
||||
- [x] Edge cases (empty content, long content, invalid hashes)
|
||||
|
||||
### Integration Points
|
||||
- [x] Full integration with existing nostr authentication system
|
||||
- [x] Database integration for optional report storage
|
||||
- [x] nginx FastCGI routing configuration
|
||||
- [x] Error response standardization with existing endpoints
|
||||
|
||||
232
docs/NIP42_INTEGRATION_PLAN.md
Normal file
232
docs/NIP42_INTEGRATION_PLAN.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# NIP-42 Authentication Integration Plan
|
||||
|
||||
## Overview
|
||||
This document outlines the integration of NIP-42 client-to-relay authentication into the existing ginxsom authentication system. The goal is to support both NIP-42 (kind 22242) and Blossom (kind 24242) authentication methods in a unified system.
|
||||
|
||||
## Current System Analysis
|
||||
|
||||
### Existing Authentication Flow
|
||||
1. **Blossom Protocol (kind 24242)**: Operation-specific authorization
|
||||
- Tags: `t` (method), `x` (hash), `expiration`
|
||||
- Purpose: Authorize specific file operations (upload, delete, etc.)
|
||||
- Location: `validate_nostr_event()` in `request_validator.c:379`
|
||||
|
||||
2. **Rules Engine**: Policy-based access control
|
||||
- Pubkey whitelist/blacklist
|
||||
- Hash blacklist
|
||||
- MIME type restrictions
|
||||
- File size limits
|
||||
- Location: `evaluate_auth_rules()` in `request_validator.c:1029`
|
||||
|
||||
### Integration Points Identified
|
||||
1. **Header Structure**: Extend `nostr_request_t` to support NIP-42 context
|
||||
2. **Event Validation**: Support both kind 22242 (NIP-42) and 24242 (Blossom)
|
||||
3. **Challenge Management**: Add challenge generation and storage
|
||||
4. **Database Schema**: Add challenge storage tables
|
||||
5. **Authentication Flow**: Support dual authentication modes
|
||||
|
||||
## NIP-42 Integration Architecture
|
||||
|
||||
### Dual Authentication Strategy
|
||||
```
|
||||
Client Authentication (NIP-42) → Operation Authorization (Blossom) → Rules Engine
|
||||
```
|
||||
|
||||
- **NIP-42 (kind 22242)**: Proves client identity to relay
|
||||
- **Blossom (kind 24242)**: Authorizes specific operations
|
||||
- **Rules Engine**: Applies policy-based restrictions
|
||||
|
||||
### Authentication Flow Options
|
||||
1. **NIP-42 Only**: Client authenticates identity, all operations allowed by rules
|
||||
2. **Blossom Only**: Current system - operation-specific authorization
|
||||
3. **Dual Mode**: NIP-42 identity + Blossom operation authorization
|
||||
4. **Rules Only**: No cryptographic auth, rules-based access only
|
||||
|
||||
## Technical Implementation Plan
|
||||
|
||||
### 1. Extend Request Validator Header (`request_validator.h`)
|
||||
```c
|
||||
// Add NIP-42 support structures
|
||||
typedef struct {
|
||||
char challenge[256]; // Current challenge for client
|
||||
time_t challenge_created; // Challenge timestamp
|
||||
char relay_url[256]; // Relay URL for challenge
|
||||
int is_authenticated; // NIP-42 auth status
|
||||
char authenticated_pubkey[65]; // Pubkey from NIP-42 auth
|
||||
} nostr_nip42_context_t;
|
||||
|
||||
// Extend nostr_request_t
|
||||
typedef struct {
|
||||
// ... existing fields ...
|
||||
nostr_nip42_context_t* nip42_context; // NIP-42 authentication context
|
||||
const char* challenge; // Challenge for this request
|
||||
} nostr_request_t;
|
||||
```
|
||||
|
||||
### 2. Database Schema Extensions
|
||||
```sql
|
||||
-- NIP-42 challenge storage
|
||||
CREATE TABLE IF NOT EXISTS nip42_challenges (
|
||||
challenge_id TEXT PRIMARY KEY,
|
||||
challenge TEXT NOT NULL,
|
||||
relay_url TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
expires_at INTEGER NOT NULL,
|
||||
client_ip TEXT
|
||||
);
|
||||
|
||||
-- NIP-42 authentication sessions
|
||||
CREATE TABLE IF NOT EXISTS nip42_sessions (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
pubkey TEXT NOT NULL,
|
||||
challenge_id TEXT NOT NULL,
|
||||
authenticated_at INTEGER NOT NULL,
|
||||
expires_at INTEGER NOT NULL,
|
||||
client_ip TEXT,
|
||||
FOREIGN KEY (challenge_id) REFERENCES nip42_challenges(challenge_id)
|
||||
);
|
||||
```
|
||||
|
||||
### 3. Core Functions to Add
|
||||
```c
|
||||
// NIP-42 challenge management
|
||||
int nostr_nip42_generate_server_challenge(char* challenge_out, size_t challenge_size);
|
||||
int nostr_nip42_store_challenge(const char* challenge, const char* relay_url, const char* client_ip);
|
||||
int nostr_nip42_verify_challenge_response(const char* auth_header, const char* relay_url, nostr_request_result_t* result);
|
||||
|
||||
// Enhanced validation
|
||||
int validate_nip42_event(struct cJSON* event, const char* expected_challenge, const char* relay_url);
|
||||
int validate_blossom_event(struct cJSON* event, const char* expected_hash, const char* method);
|
||||
|
||||
// Dual authentication support
|
||||
int nostr_validate_request_dual(const nostr_request_t* request, nostr_request_result_t* result);
|
||||
```
|
||||
|
||||
### 4. Enhanced Event Validation
|
||||
Extend `validate_nostr_event()` to handle both event kinds:
|
||||
```c
|
||||
static int validate_nostr_event(struct cJSON* event, const char* expected_hash, const char* method) {
|
||||
struct cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
|
||||
int kind = cJSON_GetNumberValue(kind_json);
|
||||
|
||||
switch (kind) {
|
||||
case 22242: // NIP-42 authentication
|
||||
return validate_nip42_event(event, expected_hash, method);
|
||||
case 24242: // Blossom authorization
|
||||
return validate_blossom_event(event, expected_hash, method);
|
||||
default:
|
||||
return NOSTR_ERROR_EVENT_INVALID_KIND;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Ginxsom Integration Points
|
||||
|
||||
#### Main.c Changes
|
||||
```c
|
||||
// Add challenge endpoint
|
||||
if (strcmp(request_method, "GET") == 0 && strstr(request_uri, "/.well-known/nostr/challenge")) {
|
||||
handle_nip42_challenge_request(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Enhanced authentication in existing endpoints
|
||||
int handle_upload_request(FCGX_Request* request) {
|
||||
// ... existing code ...
|
||||
|
||||
// Check for NIP-42 challenge header
|
||||
const char* challenge_header = FCGX_GetParam("HTTP_X_NOSTR_CHALLENGE", request->envp);
|
||||
|
||||
nostr_request_t nostr_request = {
|
||||
.operation = "upload",
|
||||
.auth_header = auth_header,
|
||||
.resource_hash = file_hash,
|
||||
.mime_type = content_type,
|
||||
.file_size = content_length,
|
||||
.challenge = challenge_header,
|
||||
.nip42_context = &nip42_ctx
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### New Configuration Keys
|
||||
```sql
|
||||
-- NIP-42 specific settings
|
||||
INSERT OR REPLACE INTO auth_config VALUES ('nip42_enabled', 'true');
|
||||
INSERT OR REPLACE INTO auth_config VALUES ('nip42_challenge_ttl', '600');
|
||||
INSERT OR REPLACE INTO auth_config VALUES ('nip42_session_ttl', '3600');
|
||||
INSERT OR REPLACE INTO auth_config VALUES ('auth_mode', 'dual'); -- dual, nip42_only, blossom_only, rules_only
|
||||
INSERT OR REPLACE INTO auth_config VALUES ('relay_url', 'http://localhost:9001');
|
||||
```
|
||||
|
||||
### Authentication Modes
|
||||
1. **dual**: NIP-42 + Blossom + Rules (most secure)
|
||||
2. **nip42_only**: NIP-42 + Rules (client identity required)
|
||||
3. **blossom_only**: Blossom + Rules (current system)
|
||||
4. **rules_only**: Rules only (no cryptographic auth)
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Test Cases to Add
|
||||
1. **NIP-42 Challenge Flow**: Generate challenge, verify response
|
||||
2. **Kind 22242 Validation**: Test NIP-42 event structure and signatures
|
||||
3. **Dual Authentication**: NIP-42 auth + Blossom authorization
|
||||
4. **Challenge Expiration**: Test challenge TTL and cleanup
|
||||
5. **Session Management**: Test session creation and expiration
|
||||
6. **Mixed Mode Testing**: Different auth modes in same server
|
||||
7. **Error Handling**: Invalid challenges, expired sessions, etc.
|
||||
|
||||
### Test File Structure
|
||||
```bash
|
||||
tests/
|
||||
├── nip42_basic_test.sh # Basic NIP-42 functionality
|
||||
├── nip42_challenge_test.sh # Challenge generation and validation
|
||||
├── nip42_dual_auth_test.sh # Dual authentication mode
|
||||
└── nip42_integration_test.sh # Full integration testing
|
||||
```
|
||||
|
||||
## Build System Integration
|
||||
|
||||
### Files to Update
|
||||
1. **build.sh**: Already includes `nip042.c` in sources
|
||||
2. **nostr_core.h**: Already includes `nip042.h`
|
||||
3. **Makefile**: May need to link additional NIP-42 functions
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. ✅ **Analysis Complete**: Current system understood
|
||||
2. **Extend Headers**: Add NIP-42 structures to request_validator.h
|
||||
3. **Database Schema**: Add challenge and session tables
|
||||
4. **Core Functions**: Implement challenge management
|
||||
5. **Event Validation**: Extend validation for kind 22242
|
||||
6. **Request Validation**: Update nostr_validate_request()
|
||||
7. **Ginxsom Integration**: Add challenge endpoints and auth checks
|
||||
8. **Build Integration**: Ensure NIP-42 is compiled
|
||||
9. **Testing**: Create comprehensive test suite
|
||||
10. **Documentation**: Update API documentation
|
||||
|
||||
## Benefits of This Integration
|
||||
|
||||
1. **Standards Compliance**: Full NIP-42 support for Nostr ecosystem compatibility
|
||||
2. **Flexible Authentication**: Support multiple auth modes based on use case
|
||||
3. **Enhanced Security**: Dual authentication provides defense in depth
|
||||
4. **Backward Compatibility**: Existing Blossom auth continues to work
|
||||
5. **Future Proof**: Ready for Nostr relay ecosystem integration
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Phase 1: Basic NIP-42 Support
|
||||
- Add NIP-42 validation alongside existing system
|
||||
- No breaking changes to current auth
|
||||
|
||||
### Phase 2: Enhanced Integration
|
||||
- Add challenge management and session handling
|
||||
- Optional dual authentication mode
|
||||
|
||||
### Phase 3: Full Integration
|
||||
- Complete NIP-42 relay compatibility
|
||||
- Advanced session management features
|
||||
|
||||
This integration maintains full backward compatibility while adding powerful new authentication capabilities that align with Nostr ecosystem standards.
|
||||
219
docs/README_ADMIN_API.md
Normal file
219
docs/README_ADMIN_API.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# Ginxsom Admin API
|
||||
|
||||
A Nostr-compliant admin interface for the Ginxsom Blossom server that provides programmatic access to server statistics, configuration, and file management operations.
|
||||
|
||||
## Overview
|
||||
|
||||
The admin API allows server administrators to:
|
||||
- View server statistics (file counts, storage usage, user metrics)
|
||||
- Retrieve and update server configuration settings
|
||||
- Browse recent uploaded files with pagination
|
||||
- Monitor server health and disk usage
|
||||
|
||||
All operations require Nostr authentication using admin-authorized public keys.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Configure Admin Access
|
||||
|
||||
Add your admin pubkey to the server configuration:
|
||||
|
||||
```bash
|
||||
# Generate admin keys (keep private key secure!)
|
||||
ADMIN_PRIVKEY=$(nak key generate)
|
||||
ADMIN_PUBKEY=$(echo "$ADMIN_PRIVKEY" | nak key public)
|
||||
|
||||
# Configure server
|
||||
sqlite3 db/ginxsom.db << EOF
|
||||
INSERT OR REPLACE INTO server_config (key, value, description) VALUES
|
||||
('admin_pubkey', '$ADMIN_PUBKEY', 'Nostr public key authorized for admin operations'),
|
||||
('admin_enabled', 'true', 'Enable admin interface');
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2. Build and Start Server
|
||||
|
||||
```bash
|
||||
make clean && make
|
||||
spawn-fcgi -s /tmp/ginxsom-fcgi.sock -n ./build/ginxsom-fcgi
|
||||
nginx -c $(pwd)/config/local-nginx.conf
|
||||
```
|
||||
|
||||
### 3. Test the API
|
||||
|
||||
```bash
|
||||
# Run the complete test suite
|
||||
./tests/admin_test.sh
|
||||
|
||||
# Or test individual endpoints
|
||||
export ADMIN_PRIVKEY="your_private_key_here"
|
||||
./tests/admin_test.sh
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### GET /api/health
|
||||
System health check (no authentication required).
|
||||
```bash
|
||||
curl http://localhost:9001/api/health
|
||||
```
|
||||
|
||||
### GET /api/stats
|
||||
Server statistics and metrics.
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"total_files": 1234,
|
||||
"total_bytes": 104857600,
|
||||
"total_size_mb": 100.0,
|
||||
"unique_uploaders": 56,
|
||||
"avg_file_size": 85049,
|
||||
"file_types": {
|
||||
"image/png": 45,
|
||||
"image/jpeg": 32
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/config
|
||||
Current server configuration.
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"max_file_size": "104857600",
|
||||
"require_auth": "false",
|
||||
"server_name": "ginxsom",
|
||||
"nip94_enabled": "true"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /api/config
|
||||
Update server configuration.
|
||||
```json
|
||||
{
|
||||
"max_file_size": "209715200",
|
||||
"require_auth": "true",
|
||||
"nip94_enabled": "true"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/files
|
||||
Recent uploaded files with pagination.
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"files": [
|
||||
{
|
||||
"sha256": "abc123...",
|
||||
"size": 184292,
|
||||
"type": "application/pdf",
|
||||
"uploaded_at": 1725105921,
|
||||
"uploader_pubkey": "def456...",
|
||||
"url": "http://localhost:9001/abc123.pdf"
|
||||
}
|
||||
],
|
||||
"total": 1234,
|
||||
"limit": 50,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Manual API Usage with nak and curl
|
||||
|
||||
### Generate Admin Authentication Event
|
||||
|
||||
```bash
|
||||
# Create an authenticated event
|
||||
EVENT=$(nak event -k 24242 -c "admin_request" \
|
||||
--tag t="GET" \
|
||||
--tag expiration="$(date -d '+1 hour' +%s)" \
|
||||
--sec "$ADMIN_PRIVKEY")
|
||||
|
||||
# Send authenticated request
|
||||
AUTH_HEADER="Nostr $(echo "$EVENT" | base64 -w 0)"
|
||||
curl -H "Authorization: $AUTH_HEADER" http://localhost:9001/api/stats
|
||||
```
|
||||
|
||||
### Update Configuration
|
||||
|
||||
```bash
|
||||
# Create PUT event (method in tag)
|
||||
EVENT=$(nak event -k 24242 -c "admin_request" \
|
||||
--tag t="PUT" \
|
||||
--tag expiration="$(date -d '+1 hour' +%s)" \
|
||||
--sec "$ADMIN_PRIVKEY")
|
||||
|
||||
AUTH_HEADER="Nostr $(echo "$EVENT" | base64 -w 0)"
|
||||
|
||||
curl -X PUT -H "Authorization: $AUTH_HEADER" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"max_file_size": "209715200", "require_auth": "true"}' \
|
||||
http://localhost:9001/api/config
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
- **Nostr Authentication**: All admin operations require valid Nostr kind 24242 events
|
||||
- **Pubkey Verification**: Only events signed by configured admin pubkeys are accepted
|
||||
- **Event Expiration**: Admin events must include expiration timestamps for security
|
||||
- **Access Control**: Separate enable/disable flag for admin interface
|
||||
|
||||
## Development and Testing
|
||||
|
||||
### Prerequisites
|
||||
- nak (https://github.com/fiatjaf/nak)
|
||||
- curl, jq
|
||||
- sqlite3
|
||||
|
||||
### Run Tests
|
||||
```bash
|
||||
# Make test script executable
|
||||
chmod +x tests/admin_test.sh
|
||||
|
||||
# Run complete test suite
|
||||
./tests/admin_test.sh
|
||||
|
||||
# Run with specific admin key
|
||||
export ADMIN_PRIVKEY="your_private_key"
|
||||
./tests/admin_test.sh
|
||||
```
|
||||
|
||||
### Build System
|
||||
```bash
|
||||
# Clean build
|
||||
make clean
|
||||
|
||||
# Build with debug info
|
||||
make debug
|
||||
|
||||
# Run FastCGI process
|
||||
make run
|
||||
```
|
||||
|
||||
## Files Added/Modified
|
||||
|
||||
- `src/admin_api.h` - Admin API function declarations
|
||||
- `src/admin_api.c` - Complete admin API implementation
|
||||
- `src/main.c` - Updated with admin API routing
|
||||
- `config/local-nginx.conf` - Updated with admin API routes
|
||||
- `tests/admin_test.sh` - Complete test suite
|
||||
- `Makefile` - Updated to compile admin_api.c
|
||||
- `README_ADMIN_API.md` - This documentation
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- **Nostr Relay Integration**: Automatic relay subscription for remote admin control
|
||||
- **Admin Pubkey Rotation**: Support for multiple admin keys and key rotation
|
||||
- **Audit Logging**: Detailed logging of admin operations
|
||||
- **Rate Limiting**: Prevent abuse of admin endpoints
|
||||
- **Web Dashboard**: Optional HTML/CSS/JavaScript frontend
|
||||
|
||||
---
|
||||
|
||||
The admin API provides a secure, Nostr-compliant interface for server management through command-line tools while maintaining full compatibility with the existing Blossom protocol implementation.
|
||||
259
docs/SUBMODULES.md
Normal file
259
docs/SUBMODULES.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Git Submodules Guide for Ginxsom
|
||||
|
||||
This project uses git submodules to include external dependencies that we reference but never modify.
|
||||
|
||||
## Current Submodules
|
||||
|
||||
- **blossom**: The official Blossom protocol specification and BUD documents
|
||||
- **nostr_core_lib**: Core nostr functionality for authentication and event validation
|
||||
|
||||
## Key Principles
|
||||
|
||||
1. **Read-Only**: We never modify submodule content directly
|
||||
2. **Version Pinning**: Submodules are pinned to specific commits for stability
|
||||
3. **Upstream Updates**: We periodically update to newer versions when needed
|
||||
|
||||
---
|
||||
|
||||
## Common Git Submodule Commands
|
||||
|
||||
### Initial Setup (for new clones)
|
||||
```bash
|
||||
# Clone the main repository
|
||||
git clone ssh://git@git.laantungir.net:222/laantungir/ginxsom.git
|
||||
|
||||
# Initialize and update all submodules
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### One-Line Clone (includes submodules)
|
||||
```bash
|
||||
git clone --recursive ssh://git@git.laantungir.net:222/laantungir/ginxsom.git
|
||||
```
|
||||
|
||||
### Update Submodules to Latest
|
||||
```bash
|
||||
# Update to latest commit on default branch
|
||||
git submodule update --remote
|
||||
|
||||
# Update specific submodule
|
||||
git submodule update --remote blossom
|
||||
git submodule update --remote nostr_core_lib
|
||||
```
|
||||
|
||||
### Pin Submodule to Specific Version
|
||||
```bash
|
||||
# Go into submodule directory
|
||||
cd blossom
|
||||
|
||||
# Checkout specific commit/tag
|
||||
git checkout v1.2.3 # or specific commit hash
|
||||
|
||||
# Go back to main repo
|
||||
cd ..
|
||||
|
||||
# Stage the submodule change
|
||||
git add blossom
|
||||
|
||||
# Commit the pin
|
||||
git commit -m "Pin blossom submodule to v1.2.3"
|
||||
```
|
||||
|
||||
### Check Submodule Status
|
||||
```bash
|
||||
# Show current submodule commits
|
||||
git submodule status
|
||||
|
||||
# Show if submodules have uncommitted changes
|
||||
git status
|
||||
|
||||
# Show submodule branch/commit info
|
||||
git submodule foreach git log --oneline -1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow Best Practices
|
||||
|
||||
### Daily Development
|
||||
1. **Never edit submodule files directly** - they're read-only references
|
||||
2. **Use `git status` regularly** - it shows submodule state changes
|
||||
3. **Commit submodule updates separately** - makes history cleaner
|
||||
|
||||
### When Submodule Updates Available
|
||||
```bash
|
||||
# Check what's new upstream
|
||||
cd blossom
|
||||
git fetch
|
||||
git log HEAD..origin/main --oneline
|
||||
cd ..
|
||||
|
||||
# If updates look good, update the pin
|
||||
git submodule update --remote blossom
|
||||
git add blossom
|
||||
git commit -m "Update blossom submodule to latest"
|
||||
```
|
||||
|
||||
### Team Collaboration
|
||||
```bash
|
||||
# After pulling main repo changes
|
||||
git pull
|
||||
|
||||
# Update submodules if they changed
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Or use this one-liner for pulls
|
||||
git pull --recurse-submodules
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### "Submodule path contains unstaged changes"
|
||||
```bash
|
||||
# This means files in submodule were accidentally modified
|
||||
cd <submodule_path>
|
||||
git checkout . # Discard changes
|
||||
cd ..
|
||||
```
|
||||
|
||||
### "Submodule not initialized"
|
||||
```bash
|
||||
# Initialize and update
|
||||
git submodule update --init <submodule_name>
|
||||
```
|
||||
|
||||
### "Detached HEAD state in submodule"
|
||||
```bash
|
||||
# This is normal! Submodules are pinned to specific commits
|
||||
# Only worry if you need to track a branch instead of a commit
|
||||
```
|
||||
|
||||
### Accidentally Committed Changes in Submodule
|
||||
```bash
|
||||
# Go to submodule
|
||||
cd <submodule_path>
|
||||
|
||||
# Reset to the pinned commit
|
||||
git reset --hard <pinned_commit_hash>
|
||||
|
||||
cd ..
|
||||
|
||||
# The main repo should now show no changes to submodule
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Build System
|
||||
|
||||
### Makefile Integration
|
||||
```makefile
|
||||
# Ensure submodules are updated before building
|
||||
build: update-submodules
|
||||
# build commands here
|
||||
|
||||
update-submodules:
|
||||
git submodule update --init --recursive
|
||||
|
||||
.PHONY: build update-submodules
|
||||
```
|
||||
|
||||
### CI/CD Integration
|
||||
```bash
|
||||
# In your CI/CD pipeline, always initialize submodules
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project-Specific Usage
|
||||
|
||||
### Blossom Submodule
|
||||
- **Purpose**: Read BUD specifications and protocol documentation
|
||||
- **Usage**: Reference only, never modify
|
||||
- **Update frequency**: When new BUDs are released or existing ones updated
|
||||
|
||||
### nostr_core_lib Submodule
|
||||
- **Purpose**: Core nostr functionality for ginxsom authentication
|
||||
- **Usage**: Link against in build process, never modify source
|
||||
- **Update frequency**: When bug fixes or new features needed
|
||||
|
||||
---
|
||||
|
||||
## Advanced Submodule Operations
|
||||
|
||||
### Change Submodule URL
|
||||
```bash
|
||||
# Edit .gitmodules file
|
||||
vim .gitmodules
|
||||
|
||||
# Update git config
|
||||
git submodule sync
|
||||
|
||||
# Update the submodule
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
### Remove a Submodule
|
||||
```bash
|
||||
# Remove from .gitmodules
|
||||
git config -f .gitmodules --remove-section submodule.<name>
|
||||
|
||||
# Remove from .git/config
|
||||
git config -f .git/config --remove-section submodule.<name>
|
||||
|
||||
# Remove directory
|
||||
git rm --cached <submodule_path>
|
||||
rm -rf <submodule_path>
|
||||
|
||||
# Commit changes
|
||||
git add .gitmodules
|
||||
git commit -m "Remove <name> submodule"
|
||||
```
|
||||
|
||||
### Track a Branch Instead of Commit
|
||||
```bash
|
||||
# Edit .gitmodules to add branch
|
||||
[submodule "blossom"]
|
||||
path = blossom
|
||||
url = ssh://git@git.laantungir.net:222/laantungir/blossom.git
|
||||
branch = main
|
||||
|
||||
# Update to track branch
|
||||
git submodule update --remote --merge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Verify submodule sources** - only use trusted repositories
|
||||
2. **Pin to specific commits** - avoid automatically tracking latest
|
||||
3. **Review submodule updates** - check changes before updating pins
|
||||
4. **Use SSH URLs** - more secure than HTTPS for private repos
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Checklist
|
||||
|
||||
- [ ] Did you run `git submodule update --init --recursive` after cloning?
|
||||
- [ ] Are you trying to edit files in submodule directories? (Don't!)
|
||||
- [ ] Did you commit submodule changes to the main repository?
|
||||
- [ ] Are your SSH keys set up for the submodule repositories?
|
||||
- [ ] Is the submodule URL accessible from your current environment?
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Action | Command |
|
||||
|--------|---------|
|
||||
| Clone with submodules | `git clone --recursive <repo>` |
|
||||
| Initialize after clone | `git submodule update --init --recursive` |
|
||||
| Update all submodules | `git submodule update --remote` |
|
||||
| Update one submodule | `git submodule update --remote <name>` |
|
||||
| Check status | `git submodule status` |
|
||||
| Reset submodule | `cd <sub> && git checkout . && cd ..` |
|
||||
|
||||
Remember: **Submodules are for code we use but never change!**
|
||||
1088
docs/WEB_ADMIN_SPECIFICATION.md
Normal file
1088
docs/WEB_ADMIN_SPECIFICATION.md
Normal file
File diff suppressed because it is too large
Load Diff
387
docs/admin_specification.md
Normal file
387
docs/admin_specification.md
Normal file
@@ -0,0 +1,387 @@
|
||||
# Ginxsom Admin System - Comprehensive Specification
|
||||
|
||||
## Overview
|
||||
|
||||
The Ginxsom admin system provides both programmatic (API-based) and interactive (web-based) administration capabilities for the Ginxsom Blossom server. The system is designed around Nostr-based authentication and supports multiple administration workflows including first-run setup, ongoing configuration management, and operational monitoring.
|
||||
|
||||
## Architecture Components
|
||||
|
||||
### 1. Configuration System
|
||||
- **File-based configuration**: Signed Nostr events stored as JSON files following XDG Base Directory specification
|
||||
- **Database configuration**: Key-value pairs stored in SQLite for runtime configuration
|
||||
- **Interactive setup**: Command-line wizard for initial server configuration
|
||||
- **Manual setup**: Scripts for generating signed configuration events
|
||||
|
||||
### 2. Authentication & Authorization
|
||||
- **Nostr-based auth**: All admin operations require valid Nostr event signatures
|
||||
- **Admin pubkey verification**: Only configured admin public keys can perform admin operations
|
||||
- **Event validation**: Full cryptographic verification of Nostr events including structure, signature, and expiration
|
||||
- **Method-specific authorization**: Different event types for different operations (upload, admin, delete, etc.)
|
||||
|
||||
### 3. API System
|
||||
- **RESTful endpoints**: `/api/*` routes for programmatic administration
|
||||
- **Command-line testing**: Complete test suite using `nak` and `curl`
|
||||
- **JSON responses**: Structured data for all admin operations
|
||||
- **CORS support**: Cross-origin requests for web admin interface
|
||||
|
||||
### 4. Web Interface (Future)
|
||||
- **Single-page application**: Self-contained HTML file with inline CSS/JS
|
||||
- **Real-time monitoring**: Statistics and system health dashboards
|
||||
- **Configuration management**: GUI for server settings
|
||||
- **File management**: Browse and manage uploaded blobs
|
||||
|
||||
## Configuration System Architecture
|
||||
|
||||
### File-based Configuration (Priority 1)
|
||||
|
||||
**Location**: Follows XDG Base Directory Specification
|
||||
- `$XDG_CONFIG_HOME/ginxsom/ginxsom_config_event.json`
|
||||
- Falls back to `$HOME/.config/ginxsom/ginxsom_config_event.json`
|
||||
|
||||
**Format**: Signed Nostr event containing server configuration
|
||||
```json
|
||||
{
|
||||
"kind": 33333,
|
||||
"created_at": 1704067200,
|
||||
"tags": [
|
||||
["server_privkey", "server_private_key_hex"],
|
||||
["cdn_origin", "https://cdn.example.com"],
|
||||
["max_file_size", "104857600"],
|
||||
["nip94_enabled", "true"]
|
||||
],
|
||||
"content": "Ginxsom server configuration",
|
||||
"pubkey": "admin_public_key_hex",
|
||||
"id": "event_id_hash",
|
||||
"sig": "event_signature"
|
||||
}
|
||||
```
|
||||
|
||||
**Loading Process**:
|
||||
1. Check for file-based config at XDG location
|
||||
2. Validate Nostr event structure and signature
|
||||
3. Extract configuration from event tags
|
||||
4. Apply settings to server (database storage)
|
||||
5. Fall back to database-only config if file missing/invalid
|
||||
|
||||
### Database Configuration (Priority 2)
|
||||
|
||||
**Table**: `server_config`
|
||||
```sql
|
||||
CREATE TABLE server_config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
**Key Configuration Items**:
|
||||
- `admin_pubkey`: Authorized admin public key
|
||||
- `admin_enabled`: Enable/disable admin interface
|
||||
- `cdn_origin`: Base URL for blob access
|
||||
- `max_file_size`: Maximum upload size in bytes
|
||||
- `nip94_enabled`: Enable NIP-94 metadata emission
|
||||
- `auth_rules_enabled`: Enable authentication rules system
|
||||
|
||||
### Setup Workflows
|
||||
|
||||
#### Interactive Setup (Command Line)
|
||||
```bash
|
||||
# First-run detection
|
||||
if [[ ! -f "$XDG_CONFIG_HOME/ginxsom/ginxsom_config_event.json" ]]; then
|
||||
echo "=== Ginxsom First-Time Setup Required ==="
|
||||
echo "1. Run interactive setup wizard"
|
||||
echo "2. Exit and create config manually"
|
||||
read -p "Choice (1/2): " choice
|
||||
|
||||
if [[ "$choice" == "1" ]]; then
|
||||
./scripts/setup.sh
|
||||
else
|
||||
echo "Manual setup: Run ./scripts/generate_config.sh"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
#### Manual Setup (Script-based)
|
||||
```bash
|
||||
# Generate configuration event
|
||||
./scripts/generate_config.sh --admin-key <admin_pubkey> \
|
||||
--server-key <server_privkey> \
|
||||
--cdn-origin "https://cdn.example.com" \
|
||||
--output "$XDG_CONFIG_HOME/ginxsom/ginxsom_config_event.json"
|
||||
```
|
||||
|
||||
### C Implementation Functions
|
||||
|
||||
#### Configuration Loading
|
||||
```c
|
||||
// Get XDG-compliant config file path
|
||||
int get_config_file_path(char* path, size_t path_size);
|
||||
|
||||
// Load and validate config event from file
|
||||
int load_server_config(const char* config_path);
|
||||
|
||||
// Extract config from validated event and apply to server
|
||||
int apply_config_from_event(cJSON* event);
|
||||
|
||||
// Interactive setup runner for first-run
|
||||
int run_interactive_setup(const char* config_path);
|
||||
```
|
||||
|
||||
#### Security Features
|
||||
- Server private key stored only in memory (never in database)
|
||||
- Config file must be signed Nostr event
|
||||
- Full cryptographic validation of config events
|
||||
- Admin pubkey verification for all operations
|
||||
|
||||
## Admin API Specification
|
||||
|
||||
### Authentication Model
|
||||
|
||||
All admin API endpoints (except `/api/health`) require Nostr authentication:
|
||||
|
||||
**Authorization Header Format**:
|
||||
```
|
||||
Authorization: Nostr <base64-encoded-event>
|
||||
```
|
||||
|
||||
**Required Event Structure**:
|
||||
```json
|
||||
{
|
||||
"kind": 24242,
|
||||
"created_at": 1704067200,
|
||||
"tags": [
|
||||
["t", "GET"],
|
||||
["expiration", "1704070800"]
|
||||
],
|
||||
"content": "admin_request",
|
||||
"pubkey": "admin_public_key",
|
||||
"id": "event_id",
|
||||
"sig": "event_signature"
|
||||
}
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
#### GET /api/health
|
||||
**Purpose**: System health check (no authentication required)
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"database": "connected",
|
||||
"blob_directory": "accessible",
|
||||
"server_time": 1704067200,
|
||||
"uptime": 3600,
|
||||
"disk_usage": {
|
||||
"total_bytes": 1073741824,
|
||||
"used_bytes": 536870912,
|
||||
"available_bytes": 536870912,
|
||||
"usage_percent": 50.0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/stats
|
||||
**Purpose**: Server statistics and metrics
|
||||
**Authentication**: Required (admin pubkey)
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"total_files": 1234,
|
||||
"total_bytes": 104857600,
|
||||
"total_size_mb": 100.0,
|
||||
"unique_uploaders": 56,
|
||||
"first_upload": 1693929600,
|
||||
"last_upload": 1704067200,
|
||||
"avg_file_size": 85049,
|
||||
"file_types": {
|
||||
"image/png": 45,
|
||||
"image/jpeg": 32,
|
||||
"application/pdf": 12,
|
||||
"other": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/config
|
||||
**Purpose**: Retrieve current server configuration
|
||||
**Authentication**: Required (admin pubkey)
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"cdn_origin": "http://localhost:9001",
|
||||
"max_file_size": "104857600",
|
||||
"nip94_enabled": "true",
|
||||
"auth_rules_enabled": "false",
|
||||
"auth_cache_ttl": "300"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT /api/config
|
||||
**Purpose**: Update server configuration
|
||||
**Authentication**: Required (admin pubkey)
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"max_file_size": "209715200",
|
||||
"nip94_enabled": "true",
|
||||
"cdn_origin": "https://cdn.example.com"
|
||||
}
|
||||
```
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Configuration updated successfully",
|
||||
"updated_keys": ["max_file_size", "cdn_origin"]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET /api/files
|
||||
**Purpose**: List recent files with pagination
|
||||
**Authentication**: Required (admin pubkey)
|
||||
**Parameters**:
|
||||
- `limit` (default: 50): Number of files to return
|
||||
- `offset` (default: 0): Pagination offset
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"files": [
|
||||
{
|
||||
"sha256": "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553",
|
||||
"size": 184292,
|
||||
"type": "application/pdf",
|
||||
"uploaded_at": 1725105921,
|
||||
"uploader_pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"filename": "document.pdf",
|
||||
"url": "http://localhost:9001/b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf"
|
||||
}
|
||||
],
|
||||
"total": 1234,
|
||||
"limit": 50,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Status
|
||||
|
||||
### ✅ Completed Components
|
||||
1. **Database-based configuration loading** - Implemented in main.c
|
||||
2. **Admin API authentication system** - Implemented in admin_api.c
|
||||
3. **Nostr event validation** - Full cryptographic verification
|
||||
4. **Admin pubkey verification** - Database-backed authorization
|
||||
5. **Basic API endpoints** - Health, stats, config, files
|
||||
|
||||
### ✅ Recently Completed Components
|
||||
1. **File-based configuration system** - Fully implemented in main.c with XDG compliance
|
||||
2. **Interactive setup wizard** - Complete shell script with guided setup process (`scripts/setup.sh`)
|
||||
3. **Manual config generation** - Full-featured command-line config generator (`scripts/generate_config.sh`)
|
||||
4. **Testing infrastructure** - Comprehensive admin API test suite (`scripts/test_admin.sh`)
|
||||
5. **Documentation system** - Complete setup and usage documentation (`scripts/README.md`)
|
||||
|
||||
### 📋 Planned Components
|
||||
1. **Web admin interface** - Single-page HTML application
|
||||
2. **Enhanced monitoring** - Real-time statistics dashboard
|
||||
3. **Bulk operations** - Multi-file management APIs
|
||||
4. **Configuration validation** - Advanced config checking
|
||||
5. **Audit logging** - Admin action tracking
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Enable Admin Interface
|
||||
```bash
|
||||
# Configure admin pubkey and enable interface
|
||||
sqlite3 db/ginxsom.db << EOF
|
||||
INSERT OR REPLACE INTO server_config (key, value, description) VALUES
|
||||
('admin_pubkey', 'your_admin_public_key_here', 'Authorized admin public key'),
|
||||
('admin_enabled', 'true', 'Enable admin interface');
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2. Test API Access
|
||||
```bash
|
||||
# Generate admin authentication event
|
||||
ADMIN_PRIVKEY="your_admin_private_key"
|
||||
EVENT=$(nak event -k 24242 -c "admin_request" \
|
||||
--tag t="GET" \
|
||||
--tag expiration="$(date -d '+1 hour' +%s)" \
|
||||
--sec "$ADMIN_PRIVKEY")
|
||||
|
||||
# Test admin API
|
||||
AUTH_HEADER="Nostr $(echo "$EVENT" | base64 -w 0)"
|
||||
curl -H "Authorization: $AUTH_HEADER" http://localhost:9001/api/stats
|
||||
```
|
||||
|
||||
### 3. Configure File-based Setup (Future)
|
||||
```bash
|
||||
# Create XDG config directory
|
||||
mkdir -p "$XDG_CONFIG_HOME/ginxsom"
|
||||
|
||||
# Generate signed config event
|
||||
./scripts/generate_config.sh \
|
||||
--admin-key "your_admin_pubkey" \
|
||||
--server-key "generated_server_privkey" \
|
||||
--output "$XDG_CONFIG_HOME/ginxsom/ginxsom_config_event.json"
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Authentication Security
|
||||
- **Event expiration**: All admin events must include expiration timestamps
|
||||
- **Signature validation**: Full secp256k1 cryptographic verification
|
||||
- **Replay protection**: Event IDs tracked to prevent reuse
|
||||
- **Admin key rotation**: Support for updating admin pubkeys
|
||||
|
||||
### Configuration Security
|
||||
- **File permissions**: Config files should be readable only by server user
|
||||
- **Private key handling**: Server private keys never stored in database
|
||||
- **Config validation**: All configuration changes validated before application
|
||||
- **Backup verification**: Config events cryptographically verifiable
|
||||
|
||||
### Operational Security
|
||||
- **Access logging**: All admin operations logged with timestamps
|
||||
- **Rate limiting**: API endpoints protected against abuse
|
||||
- **Input validation**: All user input sanitized and validated
|
||||
- **Database security**: Prepared statements prevent SQL injection
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. Web Admin Interface
|
||||
- Self-contained HTML file with inline CSS/JavaScript
|
||||
- Real-time monitoring dashboards
|
||||
- Visual configuration management
|
||||
- File upload/management interface
|
||||
|
||||
### 2. Advanced Monitoring
|
||||
- Performance metrics collection
|
||||
- Alert system for critical events
|
||||
- Historical data trending
|
||||
- Resource usage tracking
|
||||
|
||||
### 3. Multi-admin Support
|
||||
- Multiple authorized admin pubkeys
|
||||
- Role-based permissions (read-only vs full admin)
|
||||
- Admin action audit trails
|
||||
- Delegation capabilities
|
||||
|
||||
### 4. Integration Features
|
||||
- Nostr relay integration for admin events
|
||||
- Webhook notifications for admin actions
|
||||
- External authentication providers
|
||||
- API key management for programmatic access
|
||||
|
||||
This specification represents the current understanding and planned development of the Ginxsom admin system, focusing on security, usability, and maintainability.
|
||||
Reference in New Issue
Block a user