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/NEXT_SESSION_START_HERE.md
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-03 11:19:40 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-03 11:19:40 +0000
commit2eaff5b79fed364d5eba5eb38e4b7bf76326884d (patch)
treedeacd6294f8860096ee82ee76930204efd65e33c /docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md
parent57bc8cd9c021feaf08e139e8fb62800bc476068e (diff)
remove docs archive
Diffstat (limited to 'docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md')
-rw-r--r--docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md650
1 files changed, 0 insertions, 650 deletions
diff --git a/docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md b/docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md
deleted file mode 100644
index 890d5ed..0000000
--- a/docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md
+++ /dev/null
@@ -1,650 +0,0 @@
1# Next Session Start Here
2
3**Date:** November 4, 2025
4**Purpose:** Quick start guide for next development session
5**Status:** Ready for actix-web integration
6
7---
8
9## 🎯 Immediate Goal
10
11**Integrate actix-web to serve both Nostr relay (WebSocket) and Git HTTP on the SAME PORT.**
12
13This is the critical architectural fix needed to match GRASP-01 requirements and ngit-relay's design.
14
15---
16
17## 🚨 Critical Understanding
18
19### Single Port Architecture (from ../ngit-relay)
20
21```
22┌─────────────────────────────────────┐
23│ Single Port (8080) │
24│ │
25│ ┌─────────────────────────────┐ │
26│ │ HTTP/WebSocket Router │ │
27│ │ (nginx in ngit-relay) │ │
28│ │ (actix-web in ngit-grasp) │ │
29│ └──────────┬──────────────────┘ │
30│ │ │
31│ ┌──────┴──────┐ │
32│ ↓ ↓ │
33│ Git HTTP Nostr Relay │
34│ /<npub>/ / (WebSocket) │
35│ <id>.git │
36└─────────────────────────────────────┘
37```
38
39**Key Points:**
401. ONE port listens for all traffic
412. Router inspects path and decides where to send request
423. Git paths go to Git handler
434. Everything else goes to Nostr relay (with WebSocket upgrade)
445. CORS headers on ALL responses
45
46---
47
48## 📋 Step-by-Step Implementation Plan
49
50### Step 1: Add actix-web Dependencies
51
52**File:** `Cargo.toml`
53
54```toml
55[dependencies]
56# Existing...
57actix-web = "4"
58actix-cors = "0.7"
59actix-ws = "0.3" # For WebSocket support
60
61# Git HTTP backend
62git-http-backend = "0.2" # Check latest version
63```
64
65**Why:**
66- `actix-web` - HTTP framework with routing
67- `actix-cors` - Easy CORS middleware
68- `actix-ws` - WebSocket support
69- `git-http-backend` - Git Smart HTTP protocol
70
71### Step 2: Create HTTP Router Module
72
73**File:** `src/http/mod.rs` (NEW)
74
75```rust
76//! HTTP server with routing for Git and Nostr
77
78use actix_web::{web, App, HttpServer};
79use actix_cors::Cors;
80
81pub mod git;
82pub mod nostr;
83
84pub async fn run_server(config: Config, storage: Storage) -> Result<()> {
85 let bind_addr = config.bind_address.clone();
86
87 HttpServer::new(move || {
88 App::new()
89 // CORS middleware (GRASP-01 requirement)
90 .wrap(
91 Cors::default()
92 .allow_any_origin()
93 .allowed_methods(vec!["GET", "POST"])
94 .allowed_headers(vec!["Content-Type"])
95 .max_age(3600)
96 )
97 // Git HTTP routes
98 .service(
99 web::scope("/{npub}/{repo}")
100 .guard(guard::fn_guard(|ctx| {
101 // Only match *.git paths
102 ctx.head().uri.path().ends_with(".git")
103 }))
104 .route("", web::get().to(git::handle_git_request))
105 .route("/{tail:.*}", web::to(git::handle_git_request))
106 )
107 // Nostr relay (WebSocket at /)
108 .route("/", web::get().to(nostr::handle_websocket))
109 // Static files (optional)
110 .route("/", web::get().to(nostr::handle_http_root))
111 })
112 .bind(bind_addr)?
113 .run()
114 .await?;
115
116 Ok(())
117}
118```
119
120**Why:**
121- Single HTTP server listening on one port
122- Routes by URL path pattern
123- CORS applied to all routes
124- Git paths (*.git) go to Git handler
125- Root path (/) handles WebSocket upgrade for Nostr
126
127### Step 3: Create Git HTTP Handler
128
129**File:** `src/http/git.rs` (NEW)
130
131```rust
132//! Git Smart HTTP handler
133
134use actix_web::{web, HttpRequest, HttpResponse, Result};
135use git_http_backend::{GitHttpBackend, Method};
136
137pub async fn handle_git_request(
138 req: HttpRequest,
139 body: web::Bytes,
140 path: web::Path<(String, String)>,
141) -> Result<HttpResponse> {
142 let (npub, repo) = path.into_inner();
143
144 // Construct repository path
145 let repo_path = format!("{}/{}/{}",
146 config.git_data_path, npub, repo);
147
148 // Check if repository exists
149 if !std::path::Path::new(&repo_path).exists() {
150 return Ok(HttpResponse::NotFound()
151 .body("Repository not found"));
152 }
153
154 // Parse Git HTTP request
155 let method = match *req.method() {
156 actix_web::http::Method::GET => Method::Get,
157 actix_web::http::Method::POST => Method::Post,
158 _ => return Ok(HttpResponse::MethodNotAllowed().finish()),
159 };
160
161 // Use git-http-backend to handle request
162 let backend = GitHttpBackend::new(&repo_path);
163 let response = backend.handle(method, req.path(), &body)?;
164
165 // Convert to actix HttpResponse
166 Ok(HttpResponse::Ok()
167 .content_type(response.content_type)
168 .body(response.body))
169}
170```
171
172**Why:**
173- Handles Git Smart HTTP protocol
174- Serves from `{GIT_DATA_PATH}/{npub}/{repo}.git`
175- Uses `git-http-backend` crate for protocol details
176- Returns 404 if repo doesn't exist
177
178### Step 4: Create Nostr WebSocket Handler
179
180**File:** `src/http/nostr.rs` (NEW)
181
182```rust
183//! Nostr relay WebSocket handler
184
185use actix_web::{web, HttpRequest, HttpResponse, Result};
186use actix_ws::Message;
187
188pub async fn handle_websocket(
189 req: HttpRequest,
190 stream: web::Payload,
191 storage: web::Data<Storage>,
192) -> Result<HttpResponse> {
193 // Upgrade to WebSocket
194 let (response, mut session, mut msg_stream) = actix_ws::handle(&req, stream)?;
195
196 // Spawn task to handle WebSocket messages
197 actix_web::rt::spawn(async move {
198 while let Some(Ok(msg)) = msg_stream.next().await {
199 match msg {
200 Message::Text(text) => {
201 // Handle Nostr message (EVENT, REQ, CLOSE)
202 let responses = handle_nostr_message(&text, &storage).await;
203 for response in responses {
204 session.text(response).await.ok();
205 }
206 }
207 Message::Ping(bytes) => {
208 session.pong(&bytes).await.ok();
209 }
210 Message::Close(_) => break,
211 _ => {}
212 }
213 }
214 });
215
216 Ok(response)
217}
218
219pub async fn handle_http_root() -> Result<HttpResponse> {
220 // Serve static HTML for browsers
221 Ok(HttpResponse::Ok()
222 .content_type("text/html")
223 .body("<html><body><h1>ngit-grasp</h1><p>Nostr relay at ws://</p></body></html>"))
224}
225```
226
227**Why:**
228- Handles WebSocket upgrade at `/`
229- Reuses existing Nostr message handling logic
230- Returns HTML for browsers (non-WebSocket requests)
231
232### Step 5: Update main.rs
233
234**File:** `src/main.rs`
235
236```rust
237use anyhow::Result;
238use tracing::{info, Level};
239use tracing_subscriber::FmtSubscriber;
240
241mod config;
242mod http; // NEW
243mod nostr;
244mod storage;
245
246use config::Config;
247
248#[tokio::main]
249async fn main() -> Result<()> {
250 // Initialize tracing
251 let subscriber = FmtSubscriber::builder()
252 .with_max_level(Level::DEBUG)
253 .finish();
254 tracing::subscriber::set_global_default(subscriber)?;
255
256 info!("Starting ngit-grasp...");
257
258 // Load configuration
259 let config = Config::from_env()?;
260 info!("Configuration: {}", config.bind_address);
261
262 // Initialize storage
263 let storage = storage::Storage::new(&config)?;
264 info!("Storage initialized at: {}", config.relay_data_path);
265
266 // Start HTTP server (Git + Nostr on same port)
267 info!("Starting server on {}", config.bind_address);
268 http::run_server(config, storage).await?;
269
270 Ok(())
271}
272```
273
274**Why:**
275- Replaces separate relay with unified HTTP server
276- Single entry point for all services
277- Simpler architecture
278
279### Step 6: Update Configuration
280
281**File:** `src/config.rs`
282
283Add field for Git data path:
284
285```rust
286pub struct Config {
287 pub bind_address: String,
288 pub domain: String,
289 pub relay_data_path: String,
290 pub git_data_path: String, // NEW
291 // ... other fields
292}
293
294impl Config {
295 pub fn from_env() -> Result<Self> {
296 Ok(Config {
297 bind_address: env::var("NGIT_BIND_ADDRESS")
298 .unwrap_or_else(|_| "127.0.0.1:8080".to_string()),
299 domain: env::var("NGIT_DOMAIN")?,
300 relay_data_path: env::var("NGIT_RELAY_DATA_PATH")
301 .unwrap_or_else(|_| "./data/relay".to_string()),
302 git_data_path: env::var("NGIT_GIT_DATA_PATH") // NEW
303 .unwrap_or_else(|_| "./data/repos".to_string()),
304 // ...
305 })
306 }
307}
308```
309
310**File:** `.env.example`
311
312```bash
313# Service Configuration
314NGIT_DOMAIN=example.com
315NGIT_BIND_ADDRESS=127.0.0.1:8080
316
317# Relay Information
318NGIT_RELAY_NAME="ngit-grasp instance"
319NGIT_RELAY_DESCRIPTION="Rust GRASP implementation"
320NGIT_OWNER_NPUB="npub1..."
321
322# Storage Paths
323NGIT_GIT_DATA_PATH=./data/repos
324NGIT_RELAY_DATA_PATH=./data/relay
325
326# Logging
327NGIT_LOG_LEVEL=INFO
328RUST_LOG=info
329```
330
331### Step 7: Update Tests
332
333**File:** `tests/common/relay.rs`
334
335Update `start_with_port` to pass domain correctly:
336
337```rust
338pub async fn start_with_port(port: u16) -> Self {
339 let bind_address = format!("127.0.0.1:{}", port);
340 let domain = format!("127.0.0.1:{}", port); // NEW
341 let url = format!("ws://{}", domain);
342
343 let process = Command::new(&binary_path)
344 .env("NGIT_BIND_ADDRESS", &bind_address)
345 .env("NGIT_DOMAIN", &domain) // UPDATED
346 .env("NGIT_GIT_DATA_PATH", "./test-data/repos") // NEW
347 .env("NGIT_RELAY_DATA_PATH", "./test-data/relay") // NEW
348 .env("RUST_LOG", "warn")
349 .stdout(Stdio::null())
350 .stderr(Stdio::null())
351 .spawn()
352 .expect("Failed to start relay process");
353
354 // ... rest of method
355}
356```
357
358**Why:**
359- Domain must match bind address for announcement validation
360- Separate test data directories
361- Clean up test data after tests
362
363### Step 8: Add Git HTTP Tests
364
365**File:** `tests/grasp01_git_http.rs` (NEW)
366
367```rust
368//! GRASP-01 Git HTTP Integration Tests
369//!
370//! Reference: ../grasp/01.md lines 15-40
371//!
372//! These tests verify Git Smart HTTP service requirements:
373//! - Serve repos at /<npub>/<identifier>.git
374//! - Accept pushes matching state announcements
375//! - CORS support
376
377mod common;
378
379use common::TestRelay;
380use std::process::Command;
381
382#[tokio::test]
383async fn test_git_clone_basic() {
384 // Reference: ../grasp/01.md line 15
385 // MUST serve git repository via unauthenticated git smart http service
386
387 let relay = TestRelay::start().await;
388 let domain = relay.domain();
389
390 // TODO: Create test repository announcement
391 // TODO: Clone via git clone http://{domain}/{npub}/{id}.git
392
393 relay.stop().await;
394}
395
396#[tokio::test]
397async fn test_cors_headers() {
398 // Reference: ../grasp/01.md lines 32-40
399 // MUST include CORS headers on all responses
400
401 let relay = TestRelay::start().await;
402 let url = format!("http://{}/", relay.domain());
403
404 let response = reqwest::get(&url).await.unwrap();
405
406 // Check CORS headers
407 assert_eq!(
408 response.headers().get("access-control-allow-origin"),
409 Some(&"*".parse().unwrap())
410 );
411
412 relay.stop().await;
413}
414```
415
416**Why:**
417- Tests reference GRASP protocol line numbers
418- Verifies Git HTTP functionality
419- Checks CORS compliance
420
421---
422
423## 🔍 Verification Steps
424
425After implementing the above:
426
427### 1. Build and Run
428
429```bash
430# Build
431cargo build
432
433# Run server
434NGIT_DOMAIN=localhost:8080 \
435NGIT_BIND_ADDRESS=127.0.0.1:8080 \
436NGIT_GIT_DATA_PATH=./data/repos \
437NGIT_RELAY_DATA_PATH=./data/relay \
438cargo run
439```
440
441### 2. Test Nostr Relay (WebSocket)
442
443```bash
444# In another terminal
445cd grasp-audit
446cargo run -- --url ws://localhost:8080
447```
448
449**Expected:** NIP-01 smoke tests should pass
450
451### 3. Test Git HTTP (Manual)
452
453```bash
454# Create test repository
455mkdir -p ./data/repos/npub1test/test-repo.git
456cd ./data/repos/npub1test/test-repo.git
457git init --bare
458
459# Try to clone
460git clone http://localhost:8080/npub1test/test-repo.git
461```
462
463**Expected:** Should clone successfully (even if empty)
464
465### 4. Test CORS
466
467```bash
468curl -v http://localhost:8080/ -H "Origin: https://example.com"
469```
470
471**Expected:** Response should include:
472```
473access-control-allow-origin: *
474access-control-allow-methods: GET, POST
475access-control-allow-headers: Content-Type
476```
477
478### 5. Run Integration Tests
479
480```bash
481# All tests
482cargo test
483
484# Just NIP-01
485cargo test --test nip01_compliance
486
487# Just Git HTTP (when implemented)
488cargo test --test grasp01_git_http
489```
490
491**Expected:** All tests pass
492
493---
494
495## 🐛 Common Issues & Solutions
496
497### Issue: Port Already in Use
498
499**Symptom:** "Address already in use" error
500
501**Solution:**
502```bash
503# Find process using port
504lsof -i :8080
505
506# Kill it
507kill -9 <PID>
508
509# Or use different port
510NGIT_BIND_ADDRESS=127.0.0.1:8081 cargo run
511```
512
513### Issue: WebSocket Upgrade Fails
514
515**Symptom:** WebSocket connection refused
516
517**Solution:**
518- Check actix-web WebSocket handling
519- Verify `Upgrade: websocket` header is present
520- Check actix-ws is properly configured
521
522### Issue: Git Clone Fails
523
524**Symptom:** "repository not found" or protocol error
525
526**Solution:**
527- Verify repository exists at correct path
528- Check git-http-backend configuration
529- Ensure repository is bare (`git init --bare`)
530- Check file permissions
531
532### Issue: CORS Headers Missing
533
534**Symptom:** Browser console shows CORS error
535
536**Solution:**
537- Verify CORS middleware is applied
538- Check middleware order (CORS should be first)
539- Test with curl to see actual headers
540
541---
542
543## 📚 Key Resources
544
545### GRASP Protocol
546- `../grasp/01.md` - **THE SPEC** - Read this first!
547- Lines 1-14: Nostr relay requirements
548- Lines 15-31: Git HTTP service requirements
549- Lines 32-40: CORS requirements
550
551### Reference Implementation
552- `../ngit-relay/src/nginx.conf` - **ROUTING PATTERN**
553 - Lines 8-13: Single port listener
554 - Lines 15-48: Git HTTP routing
555 - Lines 50-94: Nostr relay routing
556- `../ngit-relay/docker-compose.yml` - Port configuration
557- `../ngit-relay/.env.example` - Environment variables
558
559### actix-web Documentation
560- [Routing](https://actix.rs/docs/url-dispatch/)
561- [WebSocket](https://actix.rs/docs/websockets/)
562- [CORS](https://docs.rs/actix-cors/)
563
564### git-http-backend Crate
565- [Docs](https://docs.rs/git-http-backend/)
566- [Examples](https://github.com/w4/git-http-backend/tree/master/examples)
567
568---
569
570## ✅ Success Criteria
571
572You'll know this step is complete when:
573
5741. ✅ Server starts on single port
5752. ✅ WebSocket connects at `ws://localhost:8080/`
5763. ✅ NIP-01 smoke tests pass
5774. ✅ Can clone Git repo at `http://localhost:8080/npub.../repo.git`
5785. ✅ CORS headers present on all responses
5796. ✅ OPTIONS requests return 204
5807. ✅ All integration tests pass
581
582---
583
584## 🎯 After This Step
585
586Once actix-web integration is complete:
587
5881. **Repository Provisioning**
589 - Create repos when announcements received
590 - Initialize bare repositories
591 - Set up directory structure
592
5932. **Push Authorization**
594 - Intercept git-receive-pack
595 - Validate against state announcements
596 - Handle maintainer sets
597
5983. **Full GRASP-01 Compliance**
599 - All tests passing
600 - Ready for production testing
601
602---
603
604## 💡 Tips
605
6061. **Start Simple**
607 - Get basic HTTP routing working first
608 - Add WebSocket support second
609 - Add Git HTTP last
610
6112. **Test Incrementally**
612 - Test each component as you add it
613 - Don't wait until everything is done
614
6153. **Use curl for Debugging**
616 ```bash
617 # Test HTTP
618 curl -v http://localhost:8080/
619
620 # Test CORS
621 curl -v http://localhost:8080/ -H "Origin: https://example.com"
622
623 # Test Git info/refs
624 curl http://localhost:8080/npub.../repo.git/info/refs?service=git-upload-pack
625 ```
626
6274. **Check ngit-relay for Patterns**
628 - nginx.conf shows exact routing logic
629 - Copy the pattern, not the implementation
630
6315. **Keep Tests Running**
632 ```bash
633 # In one terminal
634 cargo watch -x 'test --test nip01_compliance'
635
636 # Make changes, tests auto-run
637 ```
638
639---
640
641**Ready to Start?** Begin with Step 1 (Add Dependencies)
642
643**Questions?** Check `work/current_status.md` for context
644
645**Stuck?** Review `../ngit-relay/src/nginx.conf` for routing pattern
646
647---
648
649**Last Updated:** November 4, 2025
650**Next Update:** After actix-web integration complete