From 2eaff5b79fed364d5eba5eb38e4b7bf76326884d Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 3 Dec 2025 11:19:40 +0000 Subject: remove docs archive --- .../2025-11-04-evening/NEXT_SESSION_START_HERE.md | 650 --------------------- 1 file changed, 650 deletions(-) delete mode 100644 docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md (limited to 'docs/archive/2025-11-04-evening/NEXT_SESSION_START_HERE.md') 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 @@ -# Next Session Start Here - -**Date:** November 4, 2025 -**Purpose:** Quick start guide for next development session -**Status:** Ready for actix-web integration - ---- - -## 🎯 Immediate Goal - -**Integrate actix-web to serve both Nostr relay (WebSocket) and Git HTTP on the SAME PORT.** - -This is the critical architectural fix needed to match GRASP-01 requirements and ngit-relay's design. - ---- - -## 🚨 Critical Understanding - -### Single Port Architecture (from ../ngit-relay) - -``` -┌─────────────────────────────────────┐ -│ Single Port (8080) │ -│ │ -│ ┌─────────────────────────────┐ │ -│ │ HTTP/WebSocket Router │ │ -│ │ (nginx in ngit-relay) │ │ -│ │ (actix-web in ngit-grasp) │ │ -│ └──────────┬──────────────────┘ │ -│ │ │ -│ ┌──────┴──────┐ │ -│ ↓ ↓ │ -│ Git HTTP Nostr Relay │ -│ // / (WebSocket) │ -│ .git │ -└─────────────────────────────────────┘ -``` - -**Key Points:** -1. ONE port listens for all traffic -2. Router inspects path and decides where to send request -3. Git paths go to Git handler -4. Everything else goes to Nostr relay (with WebSocket upgrade) -5. CORS headers on ALL responses - ---- - -## 📋 Step-by-Step Implementation Plan - -### Step 1: Add actix-web Dependencies - -**File:** `Cargo.toml` - -```toml -[dependencies] -# Existing... -actix-web = "4" -actix-cors = "0.7" -actix-ws = "0.3" # For WebSocket support - -# Git HTTP backend -git-http-backend = "0.2" # Check latest version -``` - -**Why:** -- `actix-web` - HTTP framework with routing -- `actix-cors` - Easy CORS middleware -- `actix-ws` - WebSocket support -- `git-http-backend` - Git Smart HTTP protocol - -### Step 2: Create HTTP Router Module - -**File:** `src/http/mod.rs` (NEW) - -```rust -//! HTTP server with routing for Git and Nostr - -use actix_web::{web, App, HttpServer}; -use actix_cors::Cors; - -pub mod git; -pub mod nostr; - -pub async fn run_server(config: Config, storage: Storage) -> Result<()> { - let bind_addr = config.bind_address.clone(); - - HttpServer::new(move || { - App::new() - // CORS middleware (GRASP-01 requirement) - .wrap( - Cors::default() - .allow_any_origin() - .allowed_methods(vec!["GET", "POST"]) - .allowed_headers(vec!["Content-Type"]) - .max_age(3600) - ) - // Git HTTP routes - .service( - web::scope("/{npub}/{repo}") - .guard(guard::fn_guard(|ctx| { - // Only match *.git paths - ctx.head().uri.path().ends_with(".git") - })) - .route("", web::get().to(git::handle_git_request)) - .route("/{tail:.*}", web::to(git::handle_git_request)) - ) - // Nostr relay (WebSocket at /) - .route("/", web::get().to(nostr::handle_websocket)) - // Static files (optional) - .route("/", web::get().to(nostr::handle_http_root)) - }) - .bind(bind_addr)? - .run() - .await?; - - Ok(()) -} -``` - -**Why:** -- Single HTTP server listening on one port -- Routes by URL path pattern -- CORS applied to all routes -- Git paths (*.git) go to Git handler -- Root path (/) handles WebSocket upgrade for Nostr - -### Step 3: Create Git HTTP Handler - -**File:** `src/http/git.rs` (NEW) - -```rust -//! Git Smart HTTP handler - -use actix_web::{web, HttpRequest, HttpResponse, Result}; -use git_http_backend::{GitHttpBackend, Method}; - -pub async fn handle_git_request( - req: HttpRequest, - body: web::Bytes, - path: web::Path<(String, String)>, -) -> Result { - let (npub, repo) = path.into_inner(); - - // Construct repository path - let repo_path = format!("{}/{}/{}", - config.git_data_path, npub, repo); - - // Check if repository exists - if !std::path::Path::new(&repo_path).exists() { - return Ok(HttpResponse::NotFound() - .body("Repository not found")); - } - - // Parse Git HTTP request - let method = match *req.method() { - actix_web::http::Method::GET => Method::Get, - actix_web::http::Method::POST => Method::Post, - _ => return Ok(HttpResponse::MethodNotAllowed().finish()), - }; - - // Use git-http-backend to handle request - let backend = GitHttpBackend::new(&repo_path); - let response = backend.handle(method, req.path(), &body)?; - - // Convert to actix HttpResponse - Ok(HttpResponse::Ok() - .content_type(response.content_type) - .body(response.body)) -} -``` - -**Why:** -- Handles Git Smart HTTP protocol -- Serves from `{GIT_DATA_PATH}/{npub}/{repo}.git` -- Uses `git-http-backend` crate for protocol details -- Returns 404 if repo doesn't exist - -### Step 4: Create Nostr WebSocket Handler - -**File:** `src/http/nostr.rs` (NEW) - -```rust -//! Nostr relay WebSocket handler - -use actix_web::{web, HttpRequest, HttpResponse, Result}; -use actix_ws::Message; - -pub async fn handle_websocket( - req: HttpRequest, - stream: web::Payload, - storage: web::Data, -) -> Result { - // Upgrade to WebSocket - let (response, mut session, mut msg_stream) = actix_ws::handle(&req, stream)?; - - // Spawn task to handle WebSocket messages - actix_web::rt::spawn(async move { - while let Some(Ok(msg)) = msg_stream.next().await { - match msg { - Message::Text(text) => { - // Handle Nostr message (EVENT, REQ, CLOSE) - let responses = handle_nostr_message(&text, &storage).await; - for response in responses { - session.text(response).await.ok(); - } - } - Message::Ping(bytes) => { - session.pong(&bytes).await.ok(); - } - Message::Close(_) => break, - _ => {} - } - } - }); - - Ok(response) -} - -pub async fn handle_http_root() -> Result { - // Serve static HTML for browsers - Ok(HttpResponse::Ok() - .content_type("text/html") - .body("

ngit-grasp

Nostr relay at ws://

")) -} -``` - -**Why:** -- Handles WebSocket upgrade at `/` -- Reuses existing Nostr message handling logic -- Returns HTML for browsers (non-WebSocket requests) - -### Step 5: Update main.rs - -**File:** `src/main.rs` - -```rust -use anyhow::Result; -use tracing::{info, Level}; -use tracing_subscriber::FmtSubscriber; - -mod config; -mod http; // NEW -mod nostr; -mod storage; - -use config::Config; - -#[tokio::main] -async fn main() -> Result<()> { - // Initialize tracing - let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) - .finish(); - tracing::subscriber::set_global_default(subscriber)?; - - info!("Starting ngit-grasp..."); - - // Load configuration - let config = Config::from_env()?; - info!("Configuration: {}", config.bind_address); - - // Initialize storage - let storage = storage::Storage::new(&config)?; - info!("Storage initialized at: {}", config.relay_data_path); - - // Start HTTP server (Git + Nostr on same port) - info!("Starting server on {}", config.bind_address); - http::run_server(config, storage).await?; - - Ok(()) -} -``` - -**Why:** -- Replaces separate relay with unified HTTP server -- Single entry point for all services -- Simpler architecture - -### Step 6: Update Configuration - -**File:** `src/config.rs` - -Add field for Git data path: - -```rust -pub struct Config { - pub bind_address: String, - pub domain: String, - pub relay_data_path: String, - pub git_data_path: String, // NEW - // ... other fields -} - -impl Config { - pub fn from_env() -> Result { - Ok(Config { - bind_address: env::var("NGIT_BIND_ADDRESS") - .unwrap_or_else(|_| "127.0.0.1:8080".to_string()), - domain: env::var("NGIT_DOMAIN")?, - relay_data_path: env::var("NGIT_RELAY_DATA_PATH") - .unwrap_or_else(|_| "./data/relay".to_string()), - git_data_path: env::var("NGIT_GIT_DATA_PATH") // NEW - .unwrap_or_else(|_| "./data/repos".to_string()), - // ... - }) - } -} -``` - -**File:** `.env.example` - -```bash -# Service Configuration -NGIT_DOMAIN=example.com -NGIT_BIND_ADDRESS=127.0.0.1:8080 - -# Relay Information -NGIT_RELAY_NAME="ngit-grasp instance" -NGIT_RELAY_DESCRIPTION="Rust GRASP implementation" -NGIT_OWNER_NPUB="npub1..." - -# Storage Paths -NGIT_GIT_DATA_PATH=./data/repos -NGIT_RELAY_DATA_PATH=./data/relay - -# Logging -NGIT_LOG_LEVEL=INFO -RUST_LOG=info -``` - -### Step 7: Update Tests - -**File:** `tests/common/relay.rs` - -Update `start_with_port` to pass domain correctly: - -```rust -pub async fn start_with_port(port: u16) -> Self { - let bind_address = format!("127.0.0.1:{}", port); - let domain = format!("127.0.0.1:{}", port); // NEW - let url = format!("ws://{}", domain); - - let process = Command::new(&binary_path) - .env("NGIT_BIND_ADDRESS", &bind_address) - .env("NGIT_DOMAIN", &domain) // UPDATED - .env("NGIT_GIT_DATA_PATH", "./test-data/repos") // NEW - .env("NGIT_RELAY_DATA_PATH", "./test-data/relay") // NEW - .env("RUST_LOG", "warn") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .expect("Failed to start relay process"); - - // ... rest of method -} -``` - -**Why:** -- Domain must match bind address for announcement validation -- Separate test data directories -- Clean up test data after tests - -### Step 8: Add Git HTTP Tests - -**File:** `tests/grasp01_git_http.rs` (NEW) - -```rust -//! GRASP-01 Git HTTP Integration Tests -//! -//! Reference: ../grasp/01.md lines 15-40 -//! -//! These tests verify Git Smart HTTP service requirements: -//! - Serve repos at //.git -//! - Accept pushes matching state announcements -//! - CORS support - -mod common; - -use common::TestRelay; -use std::process::Command; - -#[tokio::test] -async fn test_git_clone_basic() { - // Reference: ../grasp/01.md line 15 - // MUST serve git repository via unauthenticated git smart http service - - let relay = TestRelay::start().await; - let domain = relay.domain(); - - // TODO: Create test repository announcement - // TODO: Clone via git clone http://{domain}/{npub}/{id}.git - - relay.stop().await; -} - -#[tokio::test] -async fn test_cors_headers() { - // Reference: ../grasp/01.md lines 32-40 - // MUST include CORS headers on all responses - - let relay = TestRelay::start().await; - let url = format!("http://{}/", relay.domain()); - - let response = reqwest::get(&url).await.unwrap(); - - // Check CORS headers - assert_eq!( - response.headers().get("access-control-allow-origin"), - Some(&"*".parse().unwrap()) - ); - - relay.stop().await; -} -``` - -**Why:** -- Tests reference GRASP protocol line numbers -- Verifies Git HTTP functionality -- Checks CORS compliance - ---- - -## 🔍 Verification Steps - -After implementing the above: - -### 1. Build and Run - -```bash -# Build -cargo build - -# Run server -NGIT_DOMAIN=localhost:8080 \ -NGIT_BIND_ADDRESS=127.0.0.1:8080 \ -NGIT_GIT_DATA_PATH=./data/repos \ -NGIT_RELAY_DATA_PATH=./data/relay \ -cargo run -``` - -### 2. Test Nostr Relay (WebSocket) - -```bash -# In another terminal -cd grasp-audit -cargo run -- --url ws://localhost:8080 -``` - -**Expected:** NIP-01 smoke tests should pass - -### 3. Test Git HTTP (Manual) - -```bash -# Create test repository -mkdir -p ./data/repos/npub1test/test-repo.git -cd ./data/repos/npub1test/test-repo.git -git init --bare - -# Try to clone -git clone http://localhost:8080/npub1test/test-repo.git -``` - -**Expected:** Should clone successfully (even if empty) - -### 4. Test CORS - -```bash -curl -v http://localhost:8080/ -H "Origin: https://example.com" -``` - -**Expected:** Response should include: -``` -access-control-allow-origin: * -access-control-allow-methods: GET, POST -access-control-allow-headers: Content-Type -``` - -### 5. Run Integration Tests - -```bash -# All tests -cargo test - -# Just NIP-01 -cargo test --test nip01_compliance - -# Just Git HTTP (when implemented) -cargo test --test grasp01_git_http -``` - -**Expected:** All tests pass - ---- - -## 🐛 Common Issues & Solutions - -### Issue: Port Already in Use - -**Symptom:** "Address already in use" error - -**Solution:** -```bash -# Find process using port -lsof -i :8080 - -# Kill it -kill -9 - -# Or use different port -NGIT_BIND_ADDRESS=127.0.0.1:8081 cargo run -``` - -### Issue: WebSocket Upgrade Fails - -**Symptom:** WebSocket connection refused - -**Solution:** -- Check actix-web WebSocket handling -- Verify `Upgrade: websocket` header is present -- Check actix-ws is properly configured - -### Issue: Git Clone Fails - -**Symptom:** "repository not found" or protocol error - -**Solution:** -- Verify repository exists at correct path -- Check git-http-backend configuration -- Ensure repository is bare (`git init --bare`) -- Check file permissions - -### Issue: CORS Headers Missing - -**Symptom:** Browser console shows CORS error - -**Solution:** -- Verify CORS middleware is applied -- Check middleware order (CORS should be first) -- Test with curl to see actual headers - ---- - -## 📚 Key Resources - -### GRASP Protocol -- `../grasp/01.md` - **THE SPEC** - Read this first! -- Lines 1-14: Nostr relay requirements -- Lines 15-31: Git HTTP service requirements -- Lines 32-40: CORS requirements - -### Reference Implementation -- `../ngit-relay/src/nginx.conf` - **ROUTING PATTERN** - - Lines 8-13: Single port listener - - Lines 15-48: Git HTTP routing - - Lines 50-94: Nostr relay routing -- `../ngit-relay/docker-compose.yml` - Port configuration -- `../ngit-relay/.env.example` - Environment variables - -### actix-web Documentation -- [Routing](https://actix.rs/docs/url-dispatch/) -- [WebSocket](https://actix.rs/docs/websockets/) -- [CORS](https://docs.rs/actix-cors/) - -### git-http-backend Crate -- [Docs](https://docs.rs/git-http-backend/) -- [Examples](https://github.com/w4/git-http-backend/tree/master/examples) - ---- - -## ✅ Success Criteria - -You'll know this step is complete when: - -1. ✅ Server starts on single port -2. ✅ WebSocket connects at `ws://localhost:8080/` -3. ✅ NIP-01 smoke tests pass -4. ✅ Can clone Git repo at `http://localhost:8080/npub.../repo.git` -5. ✅ CORS headers present on all responses -6. ✅ OPTIONS requests return 204 -7. ✅ All integration tests pass - ---- - -## 🎯 After This Step - -Once actix-web integration is complete: - -1. **Repository Provisioning** - - Create repos when announcements received - - Initialize bare repositories - - Set up directory structure - -2. **Push Authorization** - - Intercept git-receive-pack - - Validate against state announcements - - Handle maintainer sets - -3. **Full GRASP-01 Compliance** - - All tests passing - - Ready for production testing - ---- - -## 💡 Tips - -1. **Start Simple** - - Get basic HTTP routing working first - - Add WebSocket support second - - Add Git HTTP last - -2. **Test Incrementally** - - Test each component as you add it - - Don't wait until everything is done - -3. **Use curl for Debugging** - ```bash - # Test HTTP - curl -v http://localhost:8080/ - - # Test CORS - curl -v http://localhost:8080/ -H "Origin: https://example.com" - - # Test Git info/refs - curl http://localhost:8080/npub.../repo.git/info/refs?service=git-upload-pack - ``` - -4. **Check ngit-relay for Patterns** - - nginx.conf shows exact routing logic - - Copy the pattern, not the implementation - -5. **Keep Tests Running** - ```bash - # In one terminal - cargo watch -x 'test --test nip01_compliance' - - # Make changes, tests auto-run - ``` - ---- - -**Ready to Start?** Begin with Step 1 (Add Dependencies) - -**Questions?** Check `work/current_status.md` for context - -**Stuck?** Review `../ngit-relay/src/nginx.conf` for routing pattern - ---- - -**Last Updated:** November 4, 2025 -**Next Update:** After actix-web integration complete -- cgit v1.2.3