diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-10 21:12:07 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-10 21:12:07 +0000 |
| commit | a9ff76e7e294fb54ae3a6876bca3e30ac6a5bdef (patch) | |
| tree | d1caa42f4f4264bba348baedcb7e114cf35c48d1 /docs | |
| parent | 0bae1738ace1af196272a333b5d835a7e497861b (diff) | |
docs: rewrite ngit-relay comparison based on actual implementations
- Correct git protocol: ngit-grasp implements HTTP layer, not full git implementation
- Correct nostr relay: both use libraries (Khatru vs nostr-relay-builder)
- Highlight key difference: ngit-relay has NO nostr event sync (only git sync)
- Explain code size difference: mainly due to event sync (~5k lines) that ngit-relay lacks
- Update when-to-choose: ngit-grasp required for event discovery from relay network
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/explanation/comparison.md | 495 |
1 files changed, 309 insertions, 186 deletions
diff --git a/docs/explanation/comparison.md b/docs/explanation/comparison.md index be16f9e..72e49a7 100644 --- a/docs/explanation/comparison.md +++ b/docs/explanation/comparison.md | |||
| @@ -1,256 +1,379 @@ | |||
| 1 | # ngit-grasp vs ngit-relay Comparison | 1 | # ngit-grasp vs ngit-relay Comparison |
| 2 | 2 | ||
| 3 | ## High-Level Comparison | 3 | This document compares ngit-grasp (this project) with ngit-relay (the reference implementation) based on their actual implementations. |
| 4 | |||
| 5 | ## High-Level Overview | ||
| 4 | 6 | ||
| 5 | | Aspect | ngit-relay (Reference) | ngit-grasp (This Project) | | 7 | | Aspect | ngit-relay (Reference) | ngit-grasp (This Project) | |
| 6 | |--------|------------------------|---------------------------| | 8 | |--------|------------------------|---------------------------| |
| 7 | | **Language** | Go | Rust | | 9 | | **Language** | Go | Rust | |
| 8 | | **Architecture** | Multi-process (nginx, git-http-backend, hooks, relay) | Single integrated process | | 10 | | **Architecture** | Multi-process (nginx + fcgiwrap + khatru + sync daemon) | Single integrated process | |
| 9 | | **Authorization** | Git pre-receive hook | Inline HTTP handler | | 11 | | **Git Protocol** | git-http-backend (C via fcgiwrap) | HTTP layer in Rust + git subprocess | |
| 10 | | **Packaging** | Docker + supervisord | Single binary or Docker | | 12 | | **Authorization** | Pre-receive Git hook | Inline HTTP handler validation | |
| 11 | | **Configuration** | Multiple config files | Environment variables | | 13 | | **Nostr Relay** | Khatru (Go library) | nostr-relay-builder (Rust library) | |
| 12 | | **Deployment** | Docker Compose | Binary or Docker | | 14 | | **Event Store** | Badger (Go KV database) | LMDB or NostrDB (Rust) | |
| 13 | | **Testing** | Go tests + shell scripts | Rust unit + integration tests | | 15 | | **Proactive Sync** | Git-only (polls DB + fetches from git servers) | Nostr event sync + git sync (event-driven) | |
| 16 | | **Process Management** | supervisord (4 processes) | Single tokio runtime | | ||
| 17 | | **Packaging** | Docker with supervisord | Single static binary or Docker | | ||
| 18 | | **Configuration** | Environment variables | Environment variables + CLI flags | | ||
| 19 | | **Total Code** | ~1,866 lines of Go | ~25,000 lines of Rust | | ||
| 14 | 20 | ||
| 15 | ## Component Breakdown | 21 | ## Architecture Comparison |
| 16 | 22 | ||
| 17 | ### ngit-relay (Go) | 23 | ### ngit-relay (Multi-Process) |
| 18 | 24 | ||
| 19 | ``` | 25 | ``` |
| 20 | ┌─────────────────────────────────────────────────┐ | 26 | ┌──────────────── Docker Container ────────────────┐ |
| 21 | │ Docker Container │ | 27 | │ │ |
| 22 | ├─────────────────────────────────────────────────┤ | 28 | │ ┌─────────────────────────────────────────────┐ │ |
| 23 | │ │ | 29 | │ │ supervisord │ │ |
| 24 | │ ┌──────────┐ ┌─────────────────────┐ │ | 30 | │ │ - fcgiwrap (git-http-backend wrapper) │ │ |
| 25 | │ │ nginx │────────▶│ git-http-backend │ │ | 31 | │ │ - nginx (HTTP + reverse proxy) │ │ |
| 26 | │ │ :80 │ │ (C binary) │ │ | 32 | │ │ - ngit-relay-khatru (Nostr relay) │ │ |
| 27 | │ └──────────┘ └──────────┬──────────┘ │ | 33 | │ │ - ngit-relay-proactive-sync (sync daemon) │ │ |
| 28 | │ │ │ │ | 34 | │ └─────────────────────────────────────────────┘ │ |
| 29 | │ │ ▼ │ | 35 | │ │ |
| 30 | │ │ ┌─────────────────┐ │ | 36 | │ ┌──────────┐ ┌────────────────────┐ │ |
| 31 | │ │ │ Git Repo │ │ | 37 | │ │ nginx │────────▶│ git-http-backend │ │ |
| 32 | │ │ │ + Hooks │ │ | 38 | │ │ :80 │ │ (C binary via CGI) │ │ |
| 33 | │ │ └────────┬────────┘ │ | 39 | │ └──────┬───┘ └──────────┬─────────┘ │ |
| 34 | │ │ │ │ | 40 | │ │ │ │ |
| 35 | │ │ ▼ │ | 41 | │ │ ▼ │ |
| 36 | │ │ ┌─────────────────┐ │ | 42 | │ │ ┌──────────────────┐ │ |
| 37 | │ │ │ pre-receive │ │ | 43 | │ │ │ Git Repos │ │ |
| 38 | │ │ │ (Go binary) │ │ | 44 | │ │ │ + pre-receive │ │ |
| 39 | │ │ └────────┬────────┘ │ | 45 | │ │ │ hook (Go) │ │ |
| 40 | │ │ │ │ | 46 | │ │ └────────┬─────────┘ │ |
| 41 | │ │ │ WebSocket │ | 47 | │ │ │ WebSocket │ |
| 42 | │ │ ▼ │ | 48 | │ │ │ query │ |
| 43 | │ │ ┌─────────────────┐ │ | 49 | │ │ ▼ │ |
| 44 | │ └─────────────────▶│ Khatru Relay │ │ | 50 | │ │ ┌──────────────────┐ │ |
| 45 | │ │ (Go) │ │ | 51 | │ └──────────────▶│ Khatru Relay │ │ |
| 46 | │ └─────────────────┘ │ | 52 | │ │ :3334 │ │ |
| 47 | │ │ | 53 | │ │ (Badger DB) │ │ |
| 48 | │ ┌──────────────────────────────────────────┐ │ | 54 | │ └──────────────────┘ │ |
| 49 | │ │ supervisord │ │ | 55 | │ │ |
| 50 | │ │ - nginx │ │ | 56 | │ Separate sync daemon polls relay DB │ |
| 51 | │ │ - khatru │ │ | 57 | │ and fetches from remote git servers │ |
| 52 | │ │ - proactive-sync │ │ | 58 | │ │ |
| 53 | │ └──────────────────────────────────────────┘ │ | 59 | └───────────────────────────────────────────────────┘ |
| 54 | │ │ | ||
| 55 | └─────────────────────────────────────────────────┘ | ||
| 56 | ``` | 60 | ``` |
| 57 | 61 | ||
| 58 | ### ngit-grasp (Rust) | 62 | ### ngit-grasp (Single Process) |
| 59 | 63 | ||
| 60 | ``` | 64 | ``` |
| 61 | ┌─────────────────────────────────────────────────┐ | 65 | ┌────────────── ngit-grasp (Single Binary) ─────────────┐ |
| 62 | │ ngit-grasp (Single Binary) │ | 66 | │ │ |
| 63 | ├─────────────────────────────────────────────────┤ | 67 | │ ┌──────────────────────────────────────────────────┐ │ |
| 64 | │ │ | 68 | │ │ hyper HTTP Server (:8080) │ │ |
| 65 | │ ┌──────────────────────────────────────────┐ │ | 69 | │ │ - WebSocket upgrade for Nostr relay │ │ |
| 66 | │ │ actix-web HTTP Server │ │ | 70 | │ │ - Git Smart HTTP handlers │ │ |
| 67 | │ │ :8080 │ │ | 71 | │ │ - Landing page + metrics endpoint │ │ |
| 68 | │ └───────┬──────────────────────┬────────────┘ │ | 72 | │ └───────┬──────────────────────┬───────────────────┘ │ |
| 69 | │ │ │ │ | 73 | │ │ │ │ |
| 70 | │ ▼ ▼ │ | 74 | │ ▼ ▼ │ |
| 71 | │ ┌──────────────┐ ┌──────────────────┐ │ | 75 | │ ┌──────────────┐ ┌────────────────────┐ │ |
| 72 | │ │ Git Handlers │ │ Nostr Relay │ │ | 76 | │ │ Git Handlers │ │ Nostr Relay │ │ |
| 73 | │ │ │ │ (relay-builder) │ │ | 77 | │ │ (HTTP layer) │ │ (nostr-relay- │ │ |
| 74 | │ │ - upload-pk │ │ │ │ | 78 | │ │ │ │ builder library) │ │ |
| 75 | │ │ - receive-pk │◀─────│ - Policies │ │ | 79 | │ │ - info/refs │ │ - NIP-34 Policy │ │ |
| 76 | │ │ + inline │ query│ - Event store │ │ | 80 | │ │ - upload-pk │◀─────┤ (inline query) │ │ |
| 77 | │ │ validation │ │ - WebSocket │ │ | 81 | │ │ - receive-pk │ auth │ - LMDB/NostrDB │ │ |
| 78 | │ └──────┬───────┘ └──────────────────┘ │ | 82 | │ │ + inline │ check│ - WebSocket │ │ |
| 79 | │ │ │ | 83 | │ │ validation │ │ - NIP-11 endpoint │ │ |
| 80 | │ ▼ │ | 84 | │ └──────┬───────┘ └──────────┬─────────┘ │ |
| 81 | │ ┌──────────────┐ │ | 85 | │ │ │ │ |
| 82 | │ │ Git Repos │ │ | 86 | │ ▼ ▼ │ |
| 83 | │ │ (spawned │ │ | 87 | │ ┌──────────────┐ ┌────────────────────┐ │ |
| 84 | │ │ git cmds) │ │ | 88 | │ │ git binary │ │ Purgatory │ │ |
| 85 | │ └──────────────┘ │ | 89 | │ │ upload-pack │ │ (in-memory queue) │ │ |
| 86 | │ │ | 90 | │ │ receive-pk │ │ + sync loop │ │ |
| 87 | │ ┌──────────────────────────────────────────┐ │ | 91 | │ └──────────────┘ └────────────────────┘ │ |
| 88 | │ │ Shared State (Arc<AppState>) │ │ | 92 | │ │ |
| 89 | │ │ - RepositoryManager │ │ | 93 | │ ┌──────────────────────────────────────────────────┐ │ |
| 90 | │ │ - NostrClient │ │ | 94 | │ │ SyncManager (tokio background task) │ │ |
| 91 | │ │ - StateCache │ │ | 95 | │ │ - Multi-relay Nostr event sync (GRASP-02) │ │ |
| 92 | │ └──────────────────────────────────────────┘ │ | 96 | │ │ - Negentropy + REQ/EOSE support │ │ |
| 93 | │ │ | 97 | │ │ - Health tracking & exponential backoff │ │ |
| 94 | └─────────────────────────────────────────────────┘ | 98 | │ │ - Git fetch from remote servers (via purgatory) │ │ |
| 99 | │ └──────────────────────────────────────────────────┘ │ | ||
| 100 | │ │ | ||
| 101 | │ ┌──────────────────────────────────────────────────┐ │ | ||
| 102 | │ │ Shared State (Arc<T>) │ │ | ||
| 103 | │ │ - Database (LMDB/NostrDB/Memory) │ │ | ||
| 104 | │ │ - Purgatory (DashMap - concurrent queue) │ │ | ||
| 105 | │ │ - Metrics (Prometheus) │ │ | ||
| 106 | │ └──────────────────────────────────────────────────┘ │ | ||
| 107 | │ │ | ||
| 108 | └────────────────────────────────────────────────────────┘ | ||
| 95 | ``` | 109 | ``` |
| 96 | 110 | ||
| 97 | ## Detailed Feature Comparison | 111 | ## Feature Comparison |
| 112 | |||
| 113 | ### Key Architectural Difference: Nostr Event Sync | ||
| 114 | |||
| 115 | **The biggest difference between the two implementations is how they handle Nostr events:** | ||
| 116 | |||
| 117 | | Aspect | ngit-relay | ngit-grasp | | ||
| 118 | |--------|-----------|-----------| | ||
| 119 | | **Event Arrival** | Relies on clients to push events directly | Proactively syncs events from other relays | | ||
| 120 | | **Discovery** | None - only stores what clients send | Discovers events from relay network | | ||
| 121 | | **Coordination** | Events and git data handled separately | Purgatory coordinates events + git data | | ||
| 122 | | **Completeness** | May miss events if clients don't push to this relay | Actively fetches missing events from network | | ||
| 123 | | **Implementation** | No event sync code (~0 lines) | Full multi-relay sync system (~5,000 lines) | | ||
| 124 | |||
| 125 | **Example scenario:** | ||
| 126 | - User creates PR on relay A, pushes git data to server B | ||
| 127 | - **ngit-relay**: Only knows about events/data pushed directly to it | ||
| 128 | - **ngit-grasp**: Discovers PR event from relay A, fetches git data from server B | ||
| 129 | |||
| 130 | This is why ngit-grasp has ~13x more code - the majority is implementing GRASP-02 proactive event sync. | ||
| 98 | 131 | ||
| 99 | ### Git Protocol Handling | 132 | ### Git Protocol Implementation |
| 100 | 133 | ||
| 101 | | Feature | ngit-relay | ngit-grasp | | 134 | | Feature | ngit-relay | ngit-grasp | |
| 102 | |---------|-----------|-----------| | 135 | |---------|-----------|-----------| |
| 103 | | Implementation | git-http-backend (C) | git-http-backend (Rust crate) | | 136 | | **HTTP Server** | nginx | hyper (Rust) | |
| 104 | | Process model | nginx → C binary | actix-web → Rust handler | | 137 | | **Git Backend** | git-http-backend (C) via fcgiwrap | HTTP protocol layer (Rust) + git binary | |
| 105 | | Upload pack | Passthrough | Passthrough with validation | | 138 | | **Process Model** | FastCGI spawns git-http-backend | HTTP handler spawns git subprocess | |
| 106 | | Receive pack | Hook-based auth | Inline validation | | 139 | | **Upload Pack** | C binary passthrough | Rust parses HTTP → spawns `git upload-pack` | |
| 107 | | Error handling | Hook stderr | HTTP response | | 140 | | **Receive Pack** | C binary → pre-receive hook | Rust validates → spawns `git receive-pack` | |
| 108 | | CORS | nginx config | actix-cors middleware | | 141 | | **Authorization** | Go hook queries relay via WebSocket | In-process function call before git spawn | |
| 142 | | **Error Reporting** | Hook stderr → git client | HTTP response body (before git runs) | | ||
| 143 | | **CORS** | nginx config | hyper middleware | | ||
| 144 | | **Lines of Code** | ~0 (uses C binary) + hook ~135 | ~1,000+ (HTTP protocol layer) | | ||
| 109 | 145 | ||
| 110 | ### Nostr Relay | 146 | ### Authorization Logic |
| 111 | 147 | ||
| 112 | | Feature | ngit-relay | ngit-grasp | | 148 | | Feature | ngit-relay | ngit-grasp | |
| 113 | |---------|-----------|-----------| | 149 | |---------|-----------|-----------| |
| 114 | | Implementation | Khatru (Go) | nostr-relay-builder (Rust) | | 150 | | **Location** | pre-receive hook (separate Go binary) | Inline HTTP handler (Rust) | |
| 115 | | Event store | Badger (Go) | LMDB or NDB (Rust) | | 151 | | **Trigger** | Git invokes hook during push | HTTP handler before spawning git | |
| 116 | | Policies | Go functions | Rust traits | | 152 | | **State Query** | WebSocket to localhost:3334 | Direct database query (in-process) | |
| 117 | | WebSocket | Khatru built-in | nostr-relay-builder | | 153 | | **Latency** | +50-100ms (hook spawn + WS query) | +10-20ms (function call) | |
| 118 | | NIP-11 | Manual JSON | Built-in support | | 154 | | **Error Channel** | stderr → git client | HTTP 403 response | |
| 155 | | **Ref Parsing** | Read from stdin (hook protocol) | Parse from HTTP request body | | ||
| 156 | | **Maintainer Resolution** | Recursive Go function | Recursive Rust function (similar) | | ||
| 157 | | **State Caching** | None (queries relay per push) | Purgatory tracks pending events | | ||
| 119 | 158 | ||
| 120 | ### Authorization Logic | 159 | ### Nostr Relay |
| 160 | |||
| 161 | | Feature | ngit-relay | ngit-grasp | | ||
| 162 | |---------|-----------|-----------| | ||
| 163 | | **Implementation** | Khatru (Go library) | nostr-relay-builder (Rust library) | | ||
| 164 | | **Database** | Badger (Go KV store) | LMDB or NostrDB (Rust) | | ||
| 165 | | **Process** | Separate process on :3334 | Integrated (same binary) | | ||
| 166 | | **Policies** | Go functions in `policies.go` | Rust traits (modular sub-policies) | | ||
| 167 | | **Event Validation** | Single function with branches | 4 separate policy modules | | ||
| 168 | | **WebSocket** | Khatru built-in | nostr-relay-builder + hyper | | ||
| 169 | | **NIP-11** | Manual JSON in code | Built-in support from library | | ||
| 170 | | **Connection** | Separate from HTTP | Shared hyper server | | ||
| 171 | | **Lines of Code** | ~186 (policies.go) + Khatru library | ~3,000+ (policy modules) + nostr-relay-builder library | | ||
| 172 | |||
| 173 | ### Proactive Sync | ||
| 121 | 174 | ||
| 122 | | Feature | ngit-relay | ngit-grasp | | 175 | | Feature | ngit-relay | ngit-grasp | |
| 123 | |---------|-----------|-----------| | 176 | |---------|-----------|-----------| |
| 124 | | Location | pre-receive hook | HTTP handler | | 177 | | **Architecture** | Separate daemon (`ngit-relay-proactive-sync`) | Integrated SyncManager (tokio task) | |
| 125 | | Language | Go | Rust | | 178 | | **Nostr Event Sync** | ❌ None (relies on client pushes) | ✅ Multi-relay sync with negentropy/REQ | |
| 126 | | State query | WebSocket to localhost:3334 | In-process function call | | 179 | | **Git Data Sync** | ✅ Polls local DB + fetches from git servers | ✅ Event-driven via purgatory queue | |
| 127 | | Error reporting | stderr → git client | HTTP response body | | 180 | | **Sync Trigger** | Timer (every 15 minutes) | Immediate on event arrival + timer for retries | |
| 128 | | Ref validation | Line-by-line stdin | Parsed from request body | | 181 | | **Relay Discovery** | N/A (no event sync) | Dynamic from 30617 announcement events | |
| 129 | | Maintainer resolution | Recursive Go function | Recursive Rust function | | 182 | | **Protocol** | Git fetch only | Nostr WebSocket + git fetch | |
| 130 | | State caching | Per-request | Shared cache with TTL | | 183 | | **Concurrency** | Goroutines (per-repo iteration) | Tokio async tasks (per-relay connections) | |
| 184 | | **Health Tracking** | Basic retry on git fetch failures | RelayHealthTracker with exponential backoff | | ||
| 185 | | **Connection Management** | N/A (no Nostr connections) | Persistent connections with reconnect | | ||
| 186 | | **Coordination** | Separate process | Purgatory + SyncManager coordination | | ||
| 187 | | **Lines of Code** | ~112 (main.go) + ~305 (git sync) | ~5,000+ (Nostr sync + git sync + coordination) | | ||
| 131 | 188 | ||
| 132 | ### Repository Management | 189 | ### Repository Management |
| 133 | 190 | ||
| 134 | | Feature | ngit-relay | ngit-grasp | | 191 | | Feature | ngit-relay | ngit-grasp | |
| 135 | |---------|-----------|-----------| | 192 | |---------|-----------|-----------| |
| 136 | | Creation | Event hook + shell commands | Event hook + tokio::process | | 193 | | **Creation** | Event hook → shell commands | Event hook → tokio::process | |
| 137 | | Configuration | git config via shell | git config via tokio::process | | 194 | | **Trigger** | `EventReceiveHook()` in Go | `handle_announcement()` in Rust | |
| 138 | | Hook installation | Symlinks | Not needed (inline auth) | | 195 | | **Configuration** | `git config` via shell | `git config` via tokio::process | |
| 139 | | Permissions | chown nginx:nginx | tokio::fs permissions | | 196 | | **Hook Installation** | Symlinks to pre-receive/post-receive | Not needed (inline auth) | |
| 140 | | Path structure | `<npub>/<id>.git` | `<npub>/<id>.git` (same) | | 197 | | **Permissions** | `chown nginx:nginx` | tokio::fs permissions | |
| 198 | | **Path Structure** | `<npub>/<id>.git` | `<npub>/<id>.git` (same) | | ||
| 141 | 199 | ||
| 142 | ### Deployment | 200 | ### Event Coordination (Purgatory) |
| 143 | 201 | ||
| 144 | | Feature | ngit-relay | ngit-grasp | | 202 | | Feature | ngit-relay | ngit-grasp | |
| 145 | |---------|-----------|-----------| | 203 | |---------|-----------|-----------| |
| 146 | | Dependencies | nginx, git, Go runtime | git, Rust binary (no runtime) | | 204 | | **Implementation** | None | Dedicated Purgatory system | |
| 147 | | Process management | supervisord | Single process (tokio) | | 205 | | **Purpose** | N/A | Solves "which arrives first?" problem | |
| 148 | | Configuration | Multiple files + .env | .env only | | 206 | | **Storage** | N/A | In-memory DashMap (thread-safe) | |
| 149 | | Docker image size | ~500MB (Alpine + tools) | ~50MB (scratch + binary + git) | | 207 | | **Expiry** | N/A | 30 minutes default TTL | |
| 150 | | Startup time | ~2-5 seconds | ~0.5 seconds | | 208 | | **State Events** | Accepted (git sync happens later via timer) | Queued until git data arrives | |
| 151 | | Memory usage | ~100-200MB (multiple processes) | ~50-100MB (single process) | | 209 | | **PR Events** | Accepted (references may be missing) | Queued with placeholder refs | |
| 210 | | **Sync Queue** | Timer-based (polls all repos) | Event-driven (only syncs needed repos) | | ||
| 211 | | **Cleanup** | N/A | Background task (60s interval) | | ||
| 212 | | **Lines of Code** | 0 | ~2,000+ | | ||
| 152 | 213 | ||
| 153 | ### Development Experience | 214 | **Impact**: ngit-relay accepts all events and relies on periodic sync to eventually fetch git data. ngit-grasp holds events in purgatory and triggers targeted syncs, providing faster convergence and better coordination between Nostr events and git data. |
| 215 | |||
| 216 | ### Deployment & Operations | ||
| 154 | 217 | ||
| 155 | | Feature | ngit-relay | ngit-grasp | | 218 | | Feature | ngit-relay | ngit-grasp | |
| 156 | |---------|-----------|-----------| | 219 | |---------|-----------|-----------| |
| 157 | | Build time | Fast (Go) | Medium (Rust first build, then fast) | | 220 | | **Dependencies** | nginx, git, fcgiwrap, supervisord, Go runtime | git, Rust binary (statically linked) | |
| 158 | | Type safety | Go (good) | Rust (excellent) | | 221 | | **Process Count** | 4 (supervisord + nginx + khatru + sync) | 1 (single tokio runtime) | |
| 159 | | Testing | Go test + shell | Rust test (unit + integration) | | 222 | | **Configuration** | `.env` file | `.env` + CLI flags (clap) | |
| 160 | | Debugging | Multiple processes | Single process | | 223 | | **Docker Image Size** | ~500MB (Alpine + tools + Go runtime) | ~100MB (Debian slim + git + binary) | |
| 161 | | Hot reload | Manual | cargo-watch | | 224 | | **Startup Time** | ~2-5 seconds (multiple processes) | ~0.5 seconds (single process) | |
| 162 | | IDE support | Good (Go) | Excellent (rust-analyzer) | | 225 | | **Memory (Idle)** | ~150-200MB (4 processes + Go GC) | ~50-100MB (single process, no GC) | |
| 226 | | **Logs** | supervisord → stdout (4 streams) | tracing → stdout (unified) | | ||
| 227 | | **Monitoring** | None built-in | Prometheus metrics endpoint | | ||
| 228 | | **Binary Distribution** | Docker only | Native binary + Docker | | ||
| 229 | |||
| 230 | ### Development Experience | ||
| 163 | 231 | ||
| 164 | ## Performance Comparison (Estimated) | 232 | | Feature | ngit-relay | ngit-grasp | |
| 233 | |---------|-----------|-----------| | ||
| 234 | | **Build Time** | Fast (~5s incremental, Go) | Slow first build (~5min), fast incremental | | ||
| 235 | | **Type Safety** | Good (Go interfaces) | Excellent (Rust traits + ownership) | | ||
| 236 | | **Testing** | Go tests + shell scripts | Rust unit + integration tests | | ||
| 237 | | **Test Relay** | Manual Docker setup | `TestRelay` fixture (auto-start binary) | | ||
| 238 | | **Debugging** | Multi-process (harder) | Single process (easier) | | ||
| 239 | | **IDE Support** | Good (gopls) | Excellent (rust-analyzer) | | ||
| 240 | | **Async Model** | Goroutines (simple) | Tokio (more complex) | | ||
| 241 | | **Error Handling** | `error` interface + if checks | Result<T, E> + `?` operator | | ||
| 242 | | **Dependencies** | Go modules | Cargo crates (larger ecosystem) | | ||
| 243 | |||
| 244 | ### Code Complexity | ||
| 245 | |||
| 246 | | Component | ngit-relay | ngit-grasp | Notes | | ||
| 247 | |-----------|-----------|-----------|-------| | ||
| 248 | | Main server | 129 | 196 | ngit-relay uses supervisord | | ||
| 249 | | Git HTTP protocol | 0 (C binary via fcgiwrap) | ~1,000 | ngit-grasp implements HTTP layer | | ||
| 250 | | Auth logic (hooks) | 135 + 52 | 0 | ngit-grasp inline, no hooks | | ||
| 251 | | Auth logic (inline) | 0 | ~800 | ngit-grasp authorization module | | ||
| 252 | | Nostr relay policies | 186 | ~3,000 | Both use libraries (Khatru vs nostr-relay-builder) | | ||
| 253 | | Git-only proactive sync | 112 + 305 | 0 | ngit-relay git sync only | | ||
| 254 | | Nostr event proactive sync | 0 | ~5,000 | ngit-grasp adds full event sync (GRASP-02 v4) | | ||
| 255 | | Purgatory coordination | 0 | ~2,000 | ngit-grasp event/git coordination | | ||
| 256 | | Shared utils | 241 + 132 | ~4,000 | ngit-grasp more comprehensive | | ||
| 257 | | Config | ~50 | ~400 | ngit-grasp CLI + validation | | ||
| 258 | | Metrics | 0 | ~1,500 | ngit-grasp Prometheus | | ||
| 259 | | **Total** | **~1,866** | **~25,000** | ngit-grasp 13x more code | | ||
| 260 | |||
| 261 | **Why the difference?** | ||
| 262 | - **Nostr event sync**: ngit-relay has NONE, ngit-grasp implements full multi-relay event sync (~5,000 lines) | ||
| 263 | - **Git HTTP protocol**: ngit-relay uses C binary, ngit-grasp implements HTTP layer (~1,000 lines) | ||
| 264 | - **Purgatory coordination**: ngit-grasp adds event/git coordination system (~2,000 lines) | ||
| 265 | - **Metrics & observability**: ngit-grasp includes comprehensive monitoring (~1,500 lines) | ||
| 266 | - Both use relay libraries (Khatru vs nostr-relay-builder), but ngit-grasp has more modular policies | ||
| 267 | |||
| 268 | ### Performance Characteristics (Estimated) | ||
| 165 | 269 | ||
| 166 | | Metric | ngit-relay | ngit-grasp | Notes | | 270 | | Metric | ngit-relay | ngit-grasp | Notes | |
| 167 | |--------|-----------|-----------|-------| | 271 | |--------|-----------|-----------|-------| |
| 168 | | Startup | ~2-5s | ~0.5s | Fewer processes | | 272 | | **Startup** | ~2-5s | ~0.5s | Single process vs multi-process | |
| 169 | | Memory | ~150MB | ~75MB | Single process, no GC | | 273 | | **Memory (Idle)** | ~150MB | ~75MB | No GC, single process | |
| 170 | | CPU (idle) | ~1-2% | ~0.5% | Fewer processes | | 274 | | **Memory (Active)** | ~200MB+ | ~100-150MB | Depends on event volume | |
| 171 | | Push latency | +50-100ms | +10-20ms | No hook spawn overhead | | 275 | | **CPU (Idle)** | ~1-2% | ~0.5% | Fewer processes | |
| 172 | | Clone latency | ~same | ~same | Both passthrough to Git | | 276 | | **Push Latency** | +50-100ms | +10-20ms | No hook spawn overhead | |
| 173 | | Concurrent pushes | Good | Excellent | Tokio async vs goroutines | | 277 | | **Clone Latency** | ~same | ~same | Both passthrough to git | |
| 174 | | Event ingestion | Good | Excellent | Rust async + zero-copy | | 278 | | **Concurrent Pushes** | Good (goroutines) | Excellent (tokio async) | |
| 175 | 279 | | **Event Ingestion** | Good (Badger) | Excellent (LMDB zero-copy) | | |
| 176 | *Note: These are estimates. Actual performance depends on workload and hardware.* | 280 | | **Sync Throughput** | Moderate (polling) | High (negentropy + async) | |
| 177 | 281 | ||
| 178 | ## Code Complexity | 282 | *These are estimates based on architecture. Actual performance depends on workload.* |
| 179 | |||
| 180 | ### Lines of Code (Estimated) | ||
| 181 | |||
| 182 | | Component | ngit-relay | ngit-grasp | | ||
| 183 | |-----------|-----------|-----------| | ||
| 184 | | Main server | ~150 | ~200 | | ||
| 185 | | Git handlers | ~0 (C binary) | ~500 | | ||
| 186 | | Auth logic | ~200 | ~300 | | ||
| 187 | | Nostr relay | ~500 | ~100 (using library) | | ||
| 188 | | Shared utils | ~300 | ~200 | | ||
| 189 | | Config/setup | ~200 | ~100 | | ||
| 190 | | **Total** | **~1,350** | **~1,400** | | ||
| 191 | |||
| 192 | Similar complexity, but ngit-grasp has: | ||
| 193 | - More Git protocol code (we implement it) | ||
| 194 | - Less Nostr relay code (using library) | ||
| 195 | - Less deployment code (no hooks/supervisord) | ||
| 196 | 283 | ||
| 197 | ## Migration Path | 284 | ## Migration Path |
| 198 | 285 | ||
| 199 | For users of ngit-relay, migration to ngit-grasp would involve: | 286 | For users of ngit-relay, migration to ngit-grasp involves: |
| 200 | 287 | ||
| 201 | 1. **Export data** from Badger to LMDB/NDB | 288 | ### Data Migration |
| 202 | 2. **Copy Git repositories** (same structure) | ||
| 203 | 3. **Update environment variables** (mostly compatible) | ||
| 204 | 4. **Change deployment** from Docker Compose to binary/Docker | ||
| 205 | 5. **Update URLs** if domain changes | ||
| 206 | 289 | ||
| 207 | The **Nostr events** and **Git data** are compatible - only the server changes. | 290 | 1. **Events**: Export from Badger → Import to LMDB/NostrDB |
| 291 | - No direct migration tool yet (would need to be built) | ||
| 292 | - Alternative: Use proactive sync to re-fetch from other relays | ||
| 293 | 2. **Git Repositories**: Direct copy (same structure) | ||
| 294 | ```bash | ||
| 295 | cp -r /srv/ngit-relay/repos/* /path/to/ngit-grasp/data/git/ | ||
| 296 | ``` | ||
| 297 | 3. **Configuration**: Translate environment variables | ||
| 298 | - Most variables are compatible (`NGIT_DOMAIN`, etc.) | ||
| 299 | - Remove nginx/supervisord-specific configs | ||
| 300 | |||
| 301 | ### Compatibility | ||
| 302 | |||
| 303 | - **Git Data**: 100% compatible (same repository structure) | ||
| 304 | - **Nostr Events**: 100% compatible (standard NIP-34) | ||
| 305 | - **HTTP URLs**: Compatible (same path structure) | ||
| 306 | - **Git Hooks**: ngit-grasp doesn't use hooks (inline auth instead) | ||
| 307 | |||
| 308 | ### Downtime | ||
| 309 | |||
| 310 | - Option 1: Run both in parallel (different domains), gradually migrate | ||
| 311 | - Option 2: Short downtime for data copy + config update | ||
| 208 | 312 | ||
| 209 | ## When to Choose Each | 313 | ## When to Choose Each |
| 210 | 314 | ||
| 211 | ### Choose ngit-relay (Reference) if: | 315 | ### Choose ngit-relay (Reference) if: |
| 212 | 316 | ||
| 213 | - ✅ You need a proven, production-tested implementation | 317 | - ✅ You need proven, production-tested code |
| 214 | - ✅ You're already familiar with Go | 318 | - ✅ You're already familiar with Go ecosystem |
| 215 | - ✅ You want to stay close to the reference | 319 | - ✅ You prefer simple, minimal codebases (~1,866 lines) |
| 216 | - ✅ You need to deploy immediately | 320 | - ✅ You trust battle-tested C binaries (git-http-backend) |
| 217 | - ✅ You prefer Docker Compose workflows | 321 | - ✅ You want to stay close to the reference implementation |
| 322 | - ✅ You need to deploy immediately without complexity | ||
| 323 | - ✅ Your users will push events directly to your relay (no sync needed) | ||
| 324 | - ✅ You only need git data sync, not Nostr event sync | ||
| 218 | 325 | ||
| 219 | ### Choose ngit-grasp (This Project) if: | 326 | ### Choose ngit-grasp (This Project) if: |
| 220 | 327 | ||
| 328 | - ✅ **You need Nostr event sync from other relays** (the main differentiator) | ||
| 221 | - ✅ You want better performance and lower resource usage | 329 | - ✅ You want better performance and lower resource usage |
| 222 | - ✅ You prefer Rust's type safety and ecosystem | 330 | - ✅ You prefer Rust's type safety and memory safety |
| 223 | - ✅ You want simpler deployment (single binary) | 331 | - ✅ You want simpler deployment (single binary, no supervisord) |
| 224 | - ✅ You want to contribute to a modern codebase | 332 | - ✅ You need event/git data coordination (purgatory) |
| 225 | - ✅ You're building on top of the GRASP protocol | 333 | - ✅ You want inline authorization (lower latency) |
| 226 | - ✅ You want inline authorization over hooks | 334 | - ✅ You need comprehensive observability (Prometheus metrics) |
| 227 | - ✅ You need better integration testing | 335 | - ✅ You're comfortable with more complex codebase (~25,000 lines) |
| 336 | - ✅ You want full GRASP-02 v4 multi-relay event discovery | ||
| 228 | 337 | ||
| 229 | ## Future Roadmap Comparison | 338 | ## Current Status |
| 230 | 339 | ||
| 231 | ### ngit-relay (Reference) | 340 | ### ngit-relay (Reference) |
| 232 | - ✅ GRASP-01 complete | 341 | - ✅ GRASP-01 complete and production-ready |
| 233 | - 🔄 GRASP-02 in progress | 342 | - ✅ Git data proactive sync (fetches from git servers) |
| 234 | - ⏭️ GRASP-05 planned | 343 | - ❌ No Nostr event sync (relies on client pushes) |
| 235 | - ⏭️ NIP-42 auth-to-read | 344 | - ✅ Battle-tested in production |
| 236 | - ⏭️ NIP-70 protected events | 345 | - 🔄 Community adoption growing |
| 237 | - ⏭️ Spam prevention | ||
| 238 | 346 | ||
| 239 | ### ngit-grasp (This Project) | 347 | ### ngit-grasp (This Project) |
| 240 | - 🔄 GRASP-01 in development | 348 | - ✅ GRASP-01 complete with comprehensive testing |
| 241 | - ⏭️ GRASP-02 planned (easier with Rust async) | 349 | - ✅ GRASP-02 v4 multi-relay Nostr event sync with negentropy |
| 242 | - ⏭️ GRASP-05 planned | 350 | - ✅ Git data proactive sync (via purgatory queue) |
| 243 | - ⏭️ Advanced caching strategies | 351 | - ✅ Purgatory system for event/git coordination |
| 244 | - ⏭️ Metrics and observability | 352 | - ✅ Prometheus metrics and health tracking |
| 245 | - ⏭️ Plugin system for custom policies | 353 | - ✅ NIP-77 negentropy support |
| 354 | - ✅ Full integration test suite | ||
| 355 | - 🔄 Production deployment validation ongoing | ||
| 246 | 356 | ||
| 247 | ## Conclusion | 357 | ## Conclusion |
| 248 | 358 | ||
| 249 | Both implementations are valid approaches to GRASP: | 359 | Both implementations are valid approaches to GRASP with different philosophies: |
| 360 | |||
| 361 | - **ngit-relay** prioritizes simplicity - clients push events, relay syncs git data (~1,866 lines) | ||
| 362 | - **ngit-grasp** prioritizes completeness - syncs both events and git data from network (~25,000 lines) | ||
| 363 | |||
| 364 | **The fundamental difference**: ngit-relay expects clients to push Nostr events to it. ngit-grasp proactively discovers and syncs events from other relays in the network. | ||
| 250 | 365 | ||
| 251 | - **ngit-relay** is the mature, proven reference implementation | 366 | The choice depends on your priorities: |
| 252 | - **ngit-grasp** is a modern, performant alternative with better DX | ||
| 253 | 367 | ||
| 254 | The choice depends on your priorities: stability vs. performance, familiarity vs. innovation, proven vs. cutting-edge. | 368 | | Priority | Recommendation | |
| 369 | |----------|---------------| | ||
| 370 | | **Simplicity** | ngit-relay | | ||
| 371 | | **Event Discovery** | ngit-grasp (syncs from network) | | ||
| 372 | | **Production Stability** | ngit-relay (more battle-tested) | | ||
| 373 | | **Event Completeness** | ngit-grasp (proactive sync) | | ||
| 374 | | **Low Resources** | ngit-grasp (single binary, lower memory) | | ||
| 375 | | **Quick Deploy** | ngit-relay (Docker Compose) | | ||
| 376 | | **Development** | ngit-grasp (better tooling, type safety) | | ||
| 377 | | **Network Resilience** | ngit-grasp (multi-relay sync) | | ||
| 255 | 378 | ||
| 256 | For new deployments where performance and simplicity matter, **ngit-grasp** is the recommended choice. For production systems requiring maximum stability, **ngit-relay** is the safer bet until ngit-grasp reaches maturity. | 379 | For deployments where **Nostr event sync** is important (discovering events from other relays), **ngit-grasp** is required. For simpler deployments where users will push events directly, **ngit-relay** is sufficient and battle-tested. |