upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/docs/archive/2025-11-04-evening/architecture-diagram.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/archive/2025-11-04-evening/architecture-diagram.md')
-rw-r--r--docs/archive/2025-11-04-evening/architecture-diagram.md442
1 files changed, 442 insertions, 0 deletions
diff --git a/docs/archive/2025-11-04-evening/architecture-diagram.md b/docs/archive/2025-11-04-evening/architecture-diagram.md
new file mode 100644
index 0000000..056e551
--- /dev/null
+++ b/docs/archive/2025-11-04-evening/architecture-diagram.md
@@ -0,0 +1,442 @@
1# ngit-grasp Architecture Diagram
2
3**Date:** November 4, 2025
4**Purpose:** Visual reference for single-port architecture
5
6---
7
8## Current Architecture (WRONG ❌)
9
10```
11┌─────────────────────────────────────────┐
12│ Port 8080 │
13│ ┌───────────────────────────────────┐ │
14│ │ Nostr Relay (WebSocket) │ │
15│ │ - NIP-01 protocol │ │
16│ │ - Event storage │ │
17│ │ - Subscriptions │ │
18│ └───────────────────────────────────┘ │
19└─────────────────────────────────────────┘
20
21┌─────────────────────────────────────────┐
22│ Port 8081 (WRONG!) │
23│ ┌───────────────────────────────────┐ │
24│ │ Git HTTP Server │ │
25│ │ - Not implemented │ │
26│ └───────────────────────────────────┘ │
27└─────────────────────────────────────────┘
28```
29
30**Problem:** GRASP-01 requires single port!
31
32---
33
34## Target Architecture (CORRECT ✅)
35
36```
37┌─────────────────────────────────────────────────────────────┐
38│ Single Port (8080) │
39│ │
40│ ┌───────────────────────────────────────────────────────┐ │
41│ │ actix-web HTTP Server │ │
42│ │ │ │
43│ │ ┌──────────────────────────────────────────────────┐ │ │
44│ │ │ CORS Middleware (ALL requests) │ │ │
45│ │ │ - Access-Control-Allow-Origin: * │ │ │
46│ │ │ - Access-Control-Allow-Methods: GET, POST │ │ │
47│ │ │ - Access-Control-Allow-Headers: Content-Type │ │ │
48│ │ └──────────────────────────────────────────────────┘ │ │
49│ │ │ │
50│ │ ┌──────────────────────────────────────────────────┐ │ │
51│ │ │ HTTP Router │ │ │
52│ │ │ │ │ │
53│ │ │ Path Pattern Matching: │ │ │
54│ │ │ - /<npub>/<identifier>.git/* → Git Handler │ │ │
55│ │ │ - /* → Nostr Handler │ │ │
56│ │ └──────────────────────────────────────────────────┘ │ │
57│ │ │ │
58│ │ ┌────────────────────┐ ┌─────────────────────────┐ │ │
59│ │ │ Git HTTP Handler │ │ Nostr Relay Handler │ │ │
60│ │ │ │ │ │ │ │
61│ │ │ ┌──────────────┐ │ │ ┌──────────────────┐ │ │ │
62│ │ │ │ git-http- │ │ │ │ WebSocket Upgrade│ │ │ │
63│ │ │ │ backend │ │ │ │ │ │ │ │
64│ │ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │
65│ │ │ │ - info/refs │ │ │ │ │ NIP-01 │ │ │ │ │
66│ │ │ │ - upload-pack│ │ │ │ │ - EVENT │ │ │ │ │
67│ │ │ │ - receive-pack │ │ │ │ - REQ │ │ │ │ │
68│ │ │ │ │ │ │ │ │ - CLOSE │ │ │ │ │
69│ │ │ │ Authorization: │ │ │ └──────────────┘ │ │ │ │
70│ │ │ │ - Query state│ │ │ │ │ │ │ │
71│ │ │ │ - Validate │ │ │ │ ┌──────────────┐ │ │ │ │
72│ │ │ │ - Accept/ │ │ │ │ │ NIP-11 │ │ │ │ │
73│ │ │ │ Reject │ │ │ │ │ - GRASP fields│ │ │ │ │
74│ │ │ └──────────────┘ │ │ │ └──────────────┘ │ │ │ │
75│ │ │ │ │ │ │ │ │ │
76│ │ │ Repository: │ │ │ ┌──────────────┐ │ │ │ │
77│ │ │ {GIT_DATA_PATH}/ │ │ │ │ NIP-34 │ │ │ │ │
78│ │ │ {npub}/ │ │ │ │ - Announce │ │ │ │ │
79│ │ │ {identifier}.git │ │ │ │ - State │ │ │ │ │
80│ │ │ │ │ │ │ - Validate │ │ │ │ │
81│ │ └────────────────────┘ │ │ └──────────────┘ │ │ │ │
82│ │ │ │ │ │ │ │
83│ │ │ │ HTTP Root: │ │ │ │
84│ │ │ │ - Serve HTML │ │ │ │
85│ │ │ │ - NIP-11 JSON │ │ │ │
86│ │ │ └──────────────────┘ │ │ │
87│ │ │ │ │ │
88│ └───────────────────────────┴─────────────────────────┘ │ │
89│ │
90│ ┌───────────────────────────────────────────────────────┐ │
91│ │ Storage Layer │ │
92│ │ │ │
93│ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │
94│ │ │ Git Repositories │ │ Nostr Events DB │ │ │
95│ │ │ │ │ │ │ │
96│ │ │ {GIT_DATA_PATH}/ │ │ {RELAY_DATA_PATH}/ │ │ │
97│ │ │ ├── npub1.../ │ │ - Announcements │ │ │
98│ │ │ │ ├── repo1.git/ │ │ - State events │ │ │
99│ │ │ │ └── repo2.git/ │ │ - Issues/Patches │ │ │
100│ │ │ └── npub2.../ │ │ - Other events │ │ │
101│ │ │ └── repo3.git/ │ │ │ │ │
102│ │ └──────────────────────┘ └──────────────────────┘ │ │
103│ └───────────────────────────────────────────────────────┘ │
104└─────────────────────────────────────────────────────────────┘
105```
106
107---
108
109## Request Flow Examples
110
111### Example 1: Git Clone
112
113```
114Client: git clone http://localhost:8080/npub1abc.../my-repo.git
115 ↓
116actix-web receives HTTP GET request
117 ↓
118CORS middleware adds headers
119 ↓
120Router matches path: /npub1abc.../my-repo.git
121 ↓
122Git Handler receives request
123 ↓
124git-http-backend processes:
125 - GET /npub1abc.../my-repo.git/info/refs?service=git-upload-pack
126 ↓
127Response includes:
128 - CORS headers
129 - Git protocol data
130 - Capabilities: allow-reachable-sha1-in-want, allow-tip-sha1-in-want
131 ↓
132Client receives data and clones repository
133```
134
135### Example 2: Git Push
136
137```
138Client: git push http://localhost:8080/npub1abc.../my-repo.git main
139 ↓
140actix-web receives HTTP POST request
141 ↓
142CORS middleware adds headers
143 ↓
144Router matches path: /npub1abc.../my-repo.git
145 ↓
146Git Handler receives request
147 ↓
148BEFORE spawning git-receive-pack:
149 1. Parse ref updates from request body
150 2. Query latest state announcement from relay
151 3. Validate pusher in maintainer set
152 4. Validate ref updates match state
153 ↓
154If validation passes:
155 - Spawn git-receive-pack
156 - Stream response back to client
157 ↓
158If validation fails:
159 - Return HTTP 403 Forbidden
160 - Include error message
161 ↓
162Client receives success/failure
163```
164
165### Example 3: WebSocket Connection (Nostr)
166
167```
168Client: new WebSocket('ws://localhost:8080/')
169 ↓
170actix-web receives HTTP GET with Upgrade: websocket
171 ↓
172CORS middleware adds headers
173 ↓
174Router matches path: /
175 ↓
176Nostr Handler receives request
177 ↓
178Upgrade to WebSocket
179 ↓
180Client sends: ["EVENT", {...}]
181 ↓
182Nostr Handler processes EVENT
183 ↓
184If kind 30617 (announcement):
185 - Validate clone/relays tags
186 - Provision Git repository
187 - Store event
188 ↓
189Response: ["OK", event_id, true, ""]
190 ↓
191Client receives confirmation
192```
193
194### Example 4: NIP-11 Request
195
196```
197Client: fetch('http://localhost:8080/', {
198 headers: { 'Accept': 'application/nostr+json' }
199})
200 ↓
201actix-web receives HTTP GET with Accept header
202 ↓
203CORS middleware adds headers
204 ↓
205Router matches path: /
206 ↓
207Nostr Handler checks Accept header
208 ↓
209Returns NIP-11 JSON:
210{
211 "name": "ngit-grasp instance",
212 "description": "Rust GRASP implementation",
213 "supported_nips": [1, 11, 34],
214 "supported_grasps": ["GRASP-01"],
215 "repo_acceptance_criteria": "Must list this service in clone and relays tags",
216 "curation": "Basic spam prevention"
217}
218 ↓
219Client receives relay information
220```
221
222### Example 5: CORS Preflight (OPTIONS)
223
224```
225Browser: OPTIONS http://localhost:8080/
226Headers:
227 - Origin: https://example.com
228 - Access-Control-Request-Method: POST
229 ↓
230actix-web receives OPTIONS request
231 ↓
232CORS middleware handles preflight
233 ↓
234Returns 204 No Content with headers:
235 - Access-Control-Allow-Origin: *
236 - Access-Control-Allow-Methods: GET, POST
237 - Access-Control-Allow-Headers: Content-Type
238 - Access-Control-Max-Age: 3600
239 ↓
240Browser caches preflight response
241 ↓
242Browser proceeds with actual request
243```
244
245---
246
247## Component Responsibilities
248
249### actix-web HTTP Server
250- Listen on single port
251- Route requests by path
252- Handle WebSocket upgrades
253- Apply CORS to all requests
254
255### CORS Middleware
256- Add headers to ALL responses
257- Handle OPTIONS preflight
258- Allow any origin (GRASP-01 requirement)
259
260### HTTP Router
261- Match `/npub.../repo.git` → Git Handler
262- Match `/` → Nostr Handler
263- Pass through to appropriate handler
264
265### Git Handler
266- Serve Git Smart HTTP protocol
267- Read from `{GIT_DATA_PATH}/{npub}/{id}.git`
268- Validate pushes before accepting
269- Return 404 for missing repos
270
271### Nostr Handler
272- Upgrade HTTP to WebSocket
273- Process NIP-01 messages
274- Store/query events
275- Serve NIP-11 for HTTP requests
276- Provision repos from announcements
277
278### Storage Layer
279- Git repositories (bare)
280- Nostr events (database)
281- Separate paths for each
282
283---
284
285## Configuration Flow
286
287```
288Environment Variables
289 ↓
290.env file (optional)
291 ↓
292Config::from_env()
293 ↓
294Config struct:
295 - bind_address: "127.0.0.1:8080"
296 - domain: "example.com"
297 - git_data_path: "./data/repos"
298 - relay_data_path: "./data/relay"
299 - relay_name: "..."
300 - relay_description: "..."
301 - owner_npub: "..."
302 ↓
303Passed to:
304 - HTTP server (bind address)
305 - Git handler (git_data_path, domain)
306 - Nostr handler (relay_data_path, domain, NIP-11 info)
307 - Storage layer (both paths)
308```
309
310---
311
312## Test Architecture
313
314```
315Integration Test
316 ↓
317TestRelay::start()
318 ↓
319Spawns ngit-grasp process:
320 - NGIT_BIND_ADDRESS=127.0.0.1:{random_port}
321 - NGIT_DOMAIN=127.0.0.1:{random_port}
322 - NGIT_GIT_DATA_PATH=./test-data/repos
323 - NGIT_RELAY_DATA_PATH=./test-data/relay
324 ↓
325Process starts:
326 - actix-web listens on random port
327 - Both Git and Nostr available
328 ↓
329Test runs:
330 - Uses grasp-audit library
331 - Connects to ws://127.0.0.1:{port}/
332 - Runs compliance tests
333 ↓
334TestRelay::stop()
335 ↓
336Process killed
337 ↓
338Test data cleaned up
339```
340
341---
342
343## File Structure
344
345```
346ngit-grasp/
347├── src/
348│ ├── main.rs # Entry point
349│ ├── config.rs # Configuration
350│ ├── http/ # NEW - HTTP server
351│ │ ├── mod.rs # Server setup
352│ │ ├── git.rs # Git HTTP handler
353│ │ └── nostr.rs # Nostr WebSocket handler
354│ ├── nostr/
355│ │ ├── mod.rs
356│ │ ├── relay.rs # Relay logic (reused)
357│ │ └── events.rs # Event handling
358│ └── storage/
359│ ├── mod.rs
360│ └── repository.rs # Git repo management
361├── tests/
362│ ├── common/
363│ │ ├── mod.rs
364│ │ └── relay.rs # TestRelay fixture
365│ ├── nip01_compliance.rs # NIP-01 tests
366│ ├── nip34_announcements.rs # NIP-34 tests
367│ └── grasp01_git_http.rs # NEW - GRASP-01 Git tests
368├── data/ # Runtime data (gitignored)
369│ ├── repos/ # Git repositories
370│ └── relay/ # Nostr events
371└── test-data/ # Test data (gitignored)
372 ├── repos/
373 └── relay/
374```
375
376---
377
378## Comparison: ngit-relay vs ngit-grasp
379
380### ngit-relay (Go + nginx)
381
382```
383nginx (Port 8081)
384 ├── Git HTTP → fcgiwrap → git-http-backend
385 │ ↓
386 │ pre-receive hook (Go)
387 │ ↓
388 │ Khatru relay (HTTP API)
389 │
390 └── Nostr → proxy → Khatru relay (Port 3334)
391 ↓
392 on_event hook (Go)
393 ↓
394 provision repos
395```
396
397**Components:**
398- nginx (routing)
399- fcgiwrap (CGI wrapper)
400- git-http-backend (Git protocol)
401- pre-receive hook (Go, validates pushes)
402- post-receive hook (Go, updates HEAD)
403- Khatru relay (Go, Nostr protocol)
404- on_event hook (Go, provisions repos)
405- supervisord (process management)
406
407### ngit-grasp (Rust)
408
409```
410actix-web (Port 8080)
411 ├── Git HTTP → git-http-backend crate
412 │ ↓
413 │ inline authorization
414 │ ↓
415 │ Storage (query state)
416 │
417 └── Nostr → WebSocket upgrade
418 ↓
419 nostr-sdk relay
420 ↓
421 on_event (provision repos)
422 ↓
423 Storage (store events)
424```
425
426**Components:**
427- actix-web (routing + HTTP + WebSocket)
428- git-http-backend crate (Git protocol)
429- nostr-sdk (Nostr protocol)
430- Storage (unified storage layer)
431
432**Advantages:**
433- Single binary
434- No external processes
435- Inline authorization (better errors)
436- Pure Rust (memory safety)
437- Easier testing
438
439---
440
441**Last Updated:** November 4, 2025
442**Purpose:** Reference for implementation