# ngit-grasp A [GRASP](https://gitworkshop.dev/danconwaydev.com/grasp) (Git Relays Authorized via Signed-Nostr Proofs) implementation in Rust. ## What's New πŸŽ‰ **Full GRASP-02 Implementation Complete!** ngit-grasp now features a sophisticated proactive sync system that automatically discovers relays, syncs events using NIP-77 negentropy, and hunts for missing git data across clone URLs. Key highlights: - ✨ **NIP-77 Negentropy**: Efficient set reconciliation with automatic REQ+EOSE fallback - ✨ **Intelligent Purgatory**: Auto-fetches missing git data from clone URLs (500ms for synced events, 3min for user pushes) - ✨ **Multi-Maintainer First-Class**: Pushed git data automatically syncs to all maintainer repositories - ✨ **Smart Throttling**: Respectful rate limiting (5 concurrent, 30/min per domain) with exponential backoff - ✨ **Live & Historic Sync**: Real-time event streaming plus daily full reconciliation - ✨ **Connection Health**: Exponential backoff, rate limit detection, dead relay handling See [GRASP-02 Proactive Sync](docs/explanation/grasp-02-proactive-sync.md) and [Purgatory Git Data Sync](docs/explanation/grasp-02-proactive-sync-purgatory-git-data.md) for details. ## Overview `ngit-grasp` is a Rust-based implementation of the GRASP protocol, which enables decentralized Git repository hosting with Nostr-based authorization. This implementation combines: - **Git Smart HTTP Backend**: Serves Git repositories over HTTP - **Nostr Relay**: Stores and validates repository announcements and state events - **Integrated Authorization**: Validates Git pushes against Nostr state events without requiring external hooks Unlike the reference implementation ([ngit-relay](https://gitworkshop.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-relay)) which uses nginx + git-http-backend + pre-receive hooks + Khatru (Go), `ngit-grasp` provides a unified Rust service that handles both Git and Nostr protocols natively. ## Status **Production Ready** - Full GRASP-01 and GRASP-02 implementation with comprehensive test coverage. ## Key Features - **Pure Rust Implementation**: Single binary, no external dependencies beyond Git itself - **Integrated Authorization**: Push validation happens inline during the Git receive-pack operation - **GRASP-01 Compliant**: Core service requirements for Git hosting with Nostr authorization - **GRASP-02 Proactive Sync**: Sophisticated relay-to-relay event and git data synchronization - **NIP-77 Negentropy**: Efficient set reconciliation with automatic fallback to REQ+EOSE - **Live & Historic Sync**: Real-time event streaming plus catch-up for past events - **Smart Throttling**: Respectful rate limiting (5 concurrent, 30/min per domain) with exponential backoff - **Multi-Maintainer First-Class**: Internal sync of pushed git data across all maintainer repositories - **Intelligent Purgatory**: Auto-fetches missing git data from clone URLs when events arrive first - **Discovery-Driven**: Dynamically connects to relays listed in repository announcements - **Developer-Friendly**: Built with modern Rust async patterns using tokio and actix-web ## Architecture Highlights ### Inline Authorization (GRASP-01) The key architectural decision is **inline authorization** rather than Git hooks: - Vendored and customised `git-http-backend` crate provides low-level access to the Git protocol - We intercept the `git-receive-pack` operation before spawning the Git process - Push validation happens by checking the Nostr relay for the latest state event - Only matching pushes are forwarded to the actual Git repository This approach provides: - **Better error messages**: Direct HTTP responses vs. hook stderr - **Simpler deployment**: No hook management or symlinks - **Tighter integration**: Shared state between Git and Nostr components - **Easier testing**: Pure Rust unit and integration tests ### Sophisticated Sync System (GRASP-02) The proactive sync implementation is production-grade with advanced features: **NIP-77 Negentropy with Intelligent Fallback:** - Attempts efficient set reconciliation via NIP-77 for full syncs - Automatically falls back to REQ+EOSE with pagination when negentropy unavailable - Combines live subscriptions (`limit:0`) with historic catch-up **Multi-Layer Filter Strategy:** - **Layer 1**: Repository announcements and maintainer lists (connection-level) - **Layer 2**: Events tagging repositories (a/A/q tags, batched per 100 repos) - **Layer 3**: Events tagging root events (e/E/q tags, batched per 100 IDs) **Connection Health Management:** - Exponential backoff for failed connections (5s β†’ 1 hour) - Rate limit detection with 65-second cooldown - Dead relay handling (24h+ failures β†’ minimal retry) - Quick reconnect (<15min) vs fresh start (>15min or daily) **Intelligent Purgatory with Active Git Data Hunting:** - Events without git data held in-memory for 30 minutes - **User events**: 3-minute delay (expect git push to follow) - **Synced events**: 500ms delay (batch burst arrivals, then hunt immediately) - Proactively fetches missing data from clone URLs every 2 minutes - Respectful throttling: 5 concurrent, 30 requests/min per domain - Round-robin fairness across repositories - Auto-release when data arrives, auto-expire after 30 minutes **First-Class Multi-Maintainer Support:** - Git data pushed to one maintainer's repo automatically syncs to all other maintainers - Shared object databases for storage efficiency (planned) - Seamless collaboration without manual coordination See [GRASP-02 Proactive Sync](docs/explanation/grasp-02-proactive-sync.md) for full architectural details. ## GRASP Compliance ### GRASP-01 (Core Service Requirements) βœ… - βœ… NIP-01 compliant Nostr relay at `/` - βœ… Accepts NIP-34 repository announcements and state events - βœ… Git Smart HTTP service at `//.git` - βœ… Push validation against Nostr state events - βœ… Multi-maintainer support via recursive maintainer sets - βœ… Support for `refs/nostr/` for PRs - βœ… CORS support for web-based Git clients - βœ… NIP-11 relay information document - βœ… **Purgatory**: Events without git data held for 30 minutes, auto-released when data arrives ### GRASP-02 (Proactive Sync) βœ… - βœ… **Relay Discovery**: Automatically connects to relays listed in repository announcements - βœ… **Event Sync**: Proactive sync from discovered relays using NIP-77 negentropy with REQ+EOSE fallback - Live subscriptions (`limit:0`) for real-time event streaming - Historic sync with automatic pagination for large result sets - Daily full reconciliation to detect drift - Connection health tracking with exponential backoff - βœ… **Git Data Sync**: Automatic fetching of missing git data from clone URLs - Smart timing: 3min delay for user events, 500ms for synced events - Respectful throttling: 5 concurrent requests, 30/min per domain - Round-robin fairness across repositories - Exponential backoff with fresh start on new events - βœ… **Multi-Maintainer Support**: Pushed git data automatically synced to all maintainer repositories - βœ… **Comprehensive Monitoring**: Prometheus metrics for sync health, bandwidth, and relay status **See**: [GRASP-02 Proactive Sync](docs/explanation/grasp-02-proactive-sync.md) and [Purgatory Git Data Sync](docs/explanation/grasp-02-proactive-sync-purgatory-git-data.md) ### GRASP-05 (Archive) - Planned - πŸ”„ Accept repositories not listing this instance - πŸ”„ Backup/mirror mode operation ## Roadmap ### GRASP-02 Enhancements **Proactive Sync Plus:** - πŸ”„ Scan read/write relays of repo/PR/Patch/Issue authors for related comments - πŸ”„ Stricter anti-spam mechanisms for author relay events - πŸ”„ Periodic scanning of relays in User Grasp Lists for announcements listing our relay ### Data Efficiency **Git Object Deduplication:** - πŸ”„ Shared object database across repositories - πŸ”„ Use `GIT_ALTERNATE_OBJECT_DIRECTORIES` or `.git/objects/info/alternates` - πŸ”„ Significant storage savings for multi-maintainer repositories ### Monitoring & Observability ngit-grasp exposes comprehensive Prometheus metrics at `/metrics` for: **Git Operations:** - Clone/fetch/push rates and bandwidth - Authorization results (accepted/rejected) - Top N repositories by bandwidth **Nostr Events:** - WebSocket connections (active, unique IPs, flagged abusers) - Events received, stored, rejected by kind - Purgatory status (events waiting for git data) **Sync Health (GRASP-02):** - Per-relay connection status and health states - Event sync rates and bandwidth - Git data fetch attempts and success rates - Domain throttling metrics **Configuration Options:** | Option | CLI Flag | Environment Variable | Default | | -------------------------- | --------------------------------------------- | ------------------------------------------------ | ------- | | Metrics enabled | `--metrics-enabled` | `NGIT_METRICS_ENABLED` | `true` | | Connection abuse threshold | `--metrics-connection-per-ip-abuse-threshold` | `NGIT_METRICS_CONNECTION_PER_IP_ABUSE_THRESHOLD` | `10` | | Top N repos | `--metrics-top-n-repos` | `NGIT_METRICS_TOP_N_REPOS` | `10` | **Privacy:** IP addresses are never exposed in metrics - only aggregate counts. See [Monitoring Overview](docs/explanation/monitoring.md) and [Prometheus Setup Guide](docs/how-to/prometheus-setup.md) for deployment. ### Delete Events Git data related to deleted Repositories should be archvied (and deleted after 90 days), also events related to ONLY this repository. ### Grasp Server Removed from Announcement Event Unless GRASP-05, This should cause the git data and events related ONLY to this repository to be archived (and deleted after 90 days). ### Mitigate DoS attack vector Grasp servers can be DoS by pushing large amounts of git data to `refs/nostr/` without having to first submit a signed nostr event. operators must temporarily disable pushes to `refs/nostr/*` without having recieved a signed event. This breaks the flow of sending PR / Update events in NIP-34 as the client doesnt know if the grasp server will accept git data / event so might include it as a server hint in `clone` without knowing whether the server will accept the data. Could an ephemeral event be sent to authorise or is that too complicated? Maybe require NIP-42 auth and authorise that IP address for the push based on WoT? ### Reject Commits with Secrets This a useful feature of other git servers. ### Store all user grasp lists (for better grasp discovery) βœ… **Implemented**: Kind 10317 (User Grasp List) events are now accepted and synced from all relays for better GRASP repository discovery. **Future enhancement**: We aspire to also accept and weekly sync kind 10002 (user relay lists) and kind 0 (user metadata) events, but only for authors of accepted events. This would require an additional state-driven layer 2 filter (see Roadmap in GRASP-02 Proactive Sync documentation). **Future enhancement**: should we periodically scan relays in UserGraspLists to check for announcements that list our relay? ## Technology Stack - **Rust**: Core language - **actix-web**: HTTP server framework - **git-http-backend**: Git protocol handling but vendored and customised for authorisation logic - **nostr-relay-builder**: Nostr relay infrastructure from rust-nostr - **nostr-sdk**: Nostr event handling and validation - **tokio**: Async runtime ## Quick Start ```bash # install ngit curl -Ls https://ngit.dev/install.sh | bash # Clone the repository git clone nostr://danconwaydev.com/relay.ngit.dev/ngit-grasp cd ngit-grasp # Build (using Nix for reproducible environment) nix develop -c cargo build --release # Configure cp .env.example .env # Edit .env with your settings # Required: NGIT_DOMAIN=your-domain.com # Optional: NGIT_SYNC_BOOTSTRAP_RELAY_URL=wss://relay.example.com # Run nix develop -c cargo run --release # Run tests nix develop -c cargo test --lib ``` **What happens on startup:** - Git HTTP server starts on configured bind address - Nostr relay begins accepting WebSocket connections - If bootstrap relay configured, sync system connects and discovers repositories - Purgatory system activates, ready to hunt for missing git data - Prometheus metrics exposed at `/metrics` **Don't have Nix?** See [Getting Started Tutorial](docs/tutorials/getting-started.md) for alternative setup methods. ## Configuration Configuration is loaded with the following priority (highest to lowest): 1. **CLI flags** (e.g., `--domain example.com`) 2. **Environment variables** (e.g., `NGIT_DOMAIN=example.com`) 3. **.env file** (loaded automatically if present) 4. **Built-in defaults** This means CLI flags always take precedence over environment variables, which take precedence over `.env` file values. ### CLI Usage ```bash # View all options with defaults ngit-grasp --help # Run with CLI flags (override everything else) ngit-grasp --domain relay.example.com --owner-npub npub1... --bind-address 0.0.0.0:8080 # Mix CLI flags with environment variables NGIT_OWNER_NPUB=npub1... ngit-grasp --domain relay.example.com ``` ### Configuration Options #### Core Settings | Option | CLI Flag | Environment Variable | Default | | ----------------- | --------------------- | ------------------------ | -------------------------------------------- | | Domain | `--domain` | `NGIT_DOMAIN` | (required) | | Owner npub | `--owner-npub` | `NGIT_OWNER_NPUB` | (optional) | | Relay name | `--relay-name` | `NGIT_RELAY_NAME` | `${domain} grasp relay` | | Relay description | `--relay-description` | `NGIT_RELAY_DESCRIPTION` | `Git Nostr Relay - a grasp implementation` | | Git data path | `--git-data-path` | `NGIT_GIT_DATA_PATH` | `./data/git` (temp dir for memory backend) | | Relay data path | `--relay-data-path` | `NGIT_RELAY_DATA_PATH` | `./data/relay` (temp dir for memory backend) | | Bind address | `--bind-address` | `NGIT_BIND_ADDRESS` | `127.0.0.1:8080` | | Database backend | `--database-backend` | `NGIT_DATABASE_BACKEND` | `lmdb` | #### GRASP-02 Sync Settings | Option | CLI Flag | Environment Variable | Default | | ------------------------- | --------------------------------------- | ------------------------------------------ | --------------- | | Bootstrap relay | `--sync-bootstrap-relay-url` | `NGIT_SYNC_BOOTSTRAP_RELAY_URL` | (optional) | | Base backoff | `--sync-base-backoff-secs` | `NGIT_SYNC_BASE_BACKOFF_SECS` | `5` seconds | | Max backoff | `--sync-max-backoff-secs` | `NGIT_SYNC_MAX_BACKOFF_SECS` | `3600` (1 hour) | | Disconnect check interval | `--sync-disconnect-check-interval-secs` | `NGIT_SYNC_DISCONNECT_CHECK_INTERVAL_SECS` | `60` seconds | | Disable negentropy | `--sync-disable-negentropy` | `NGIT_SYNC_DISABLE_NEGENTROPY` | `false` | | Batch window | N/A | `NGIT_SYNC_BATCH_WINDOW_MS` | `5000` ms | **Sync Notes:** - **Bootstrap relay**: Optional starting point for relay discovery. System automatically discovers additional relays from repository announcements. - **Backoff settings**: Controls exponential backoff for failed connections (`base * 2^(failures-1)`, capped at max). - **Negentropy**: Can be disabled for testing REQ+EOSE fallback behavior. - **Batch window**: Self-subscriber batches events for this duration before triggering sync filters. ### Database Backends - `lmdb`: LMDB backend (default, persistent, general purpose) - `memory`: In-memory database (fastest, no persistence - uses temp directories) - `nostrdb`: NostrDB backend (persistent, optimized for Nostr) [Not yet implemented] > **Note:** When using the `memory` backend, git data are automatically stored in temporary directories for ephemeral testing. ### Example: Production Deployment ```bash # Using environment variables (recommended for production) export NGIT_DOMAIN=gitnostr.com export NGIT_OWNER_NPUB=npub1... export NGIT_BIND_ADDRESS=0.0.0.0:8080 export NGIT_DATABASE_BACKEND=lmdb # Optional: Enable proactive sync from a bootstrap relay export NGIT_SYNC_BOOTSTRAP_RELAY_URL=wss://relay.damus.io # Optional: Tune sync behavior export NGIT_SYNC_BASE_BACKOFF_SECS=5 # Start backoff at 5 seconds export NGIT_SYNC_MAX_BACKOFF_SECS=3600 # Cap backoff at 1 hour ngit-grasp ``` **Production Tips:** - Set `NGIT_SYNC_BOOTSTRAP_RELAY_URL` to a well-connected relay for initial repository discovery - The system will automatically discover and connect to additional relays listed in repository announcements - Monitor sync health via Prometheus metrics at `/metrics` - Purgatory will automatically fetch missing git data from clone URLs ### Example: Development ```bash # Using .env file cp .env.example .env # Edit .env with your settings ngit-grasp # Or override specific values with CLI flags ngit-grasp --domain localhost:3000 --bind-address 127.0.0.1:3000 ``` ## Documentation We use the **[DiΓ‘taxis](https://diataxis.fr/)** framework for documentation: - **[Tutorials](docs/tutorials/)** - Learn by doing (Getting Started, First Audit) - **[How-To Guides](docs/how-to/)** - Solve specific problems (Deploy, Configure) - **[Reference](docs/reference/)** - Look up technical details (Config, Protocols) - **[Explanation](docs/explanation/)** - Understand concepts (Architecture, Decisions) **Start here:** [Documentation Index](docs/README.md) ## Development See [Architecture Overview](docs/explanation/architecture.md) for system design and [Test Strategy](docs/reference/test-strategy.md) for testing approach. ### Running Tests We have two test suites: **1. Main Project Tests (ngit-grasp)** ```bash # Run unit tests (no external dependencies) nix develop -c cargo test --lib # Run all integration tests (automatic relay management) nix develop -c cargo test --test nip01_compliance --test nip34_announcements # Run NIP-01 compliance tests nix develop -c cargo test --test nip01_compliance # Run NIP-34 announcement tests nix develop -c cargo test --test nip34_announcements # With detailed output nix develop -c cargo test --test nip01_compliance -- --nocapture # Run specific test nix develop -c cargo test --test nip01_compliance test_nip01_smoke ``` **Integration tests automatically:** - Start a fresh relay instance - Run compliance tests using grasp-audit library - Clean up when done - No manual relay management needed! **2. GRASP Audit Tool (grasp-audit)** The audit tool tests GRASP compliance of any relay (including ours or external ones). ```bash # Enter grasp-audit directory cd grasp-audit # Run unit tests nix develop -c cargo test # Test against any relay (including external ones) nix develop -c cargo run -- --url wss://relay.example.com # Or test against any external relay: nix develop -c cargo run -- --url wss://relay.example.com ``` ### Development Commands ```bash # Run with logging RUST_LOG=debug nix develop -c cargo run # Check code nix develop -c cargo clippy nix develop -c cargo fmt --check # Generate test coverage (requires tarpaulin) nix develop -c cargo tarpaulin --out Html ``` **Note:** Always use `nix develop` to ensure the correct build environment. See [docs/how-to/nix-flakes.md](docs/how-to/nix-flakes.md) for details. ## Project Structure ``` ngit-grasp/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ main.rs # Entry point, server setup β”‚ β”œβ”€β”€ lib.rs # Library exports β”‚ β”œβ”€β”€ config.rs # Configuration (core + sync settings) β”‚ β”œβ”€β”€ git/ β”‚ β”‚ β”œβ”€β”€ mod.rs # Git module + repository operations β”‚ β”‚ β”œβ”€β”€ handlers.rs # Git HTTP handlers β”‚ β”‚ β”œβ”€β”€ authorization.rs # Push validation logic (checks DB + purgatory) β”‚ β”‚ β”œβ”€β”€ protocol.rs # Git protocol encoding β”‚ β”‚ └── subprocess.rs # Git subprocess management β”‚ β”œβ”€β”€ nostr/ β”‚ β”‚ β”œβ”€β”€ mod.rs # Nostr module β”‚ β”‚ β”œβ”€β”€ builder.rs # Relay builder + Nip34WritePolicy β”‚ β”‚ β”œβ”€β”€ events.rs # Event parsing and validation β”‚ β”‚ └── policy/ # Sub-policies (split for maintainability) β”‚ β”‚ β”œβ”€β”€ mod.rs # Policy module exports β”‚ β”‚ β”œβ”€β”€ announcement.rs # Repository announcement validation β”‚ β”‚ β”œβ”€β”€ state.rs # State event validation + ref alignment β”‚ β”‚ β”œβ”€β”€ pr_event.rs # PR/PR Update validation β”‚ β”‚ └── related.rs # Forward/backward reference checking β”‚ β”œβ”€β”€ sync/ # GRASP-02 Proactive Sync (relay-to-relay) β”‚ β”‚ β”œβ”€β”€ mod.rs # SyncManager, main loop, data structures β”‚ β”‚ β”œβ”€β”€ algorithms.rs # derive_relay_targets(), compute_actions() β”‚ β”‚ β”œβ”€β”€ filters.rs # 3-layer filter building (announcements, repos, events) β”‚ β”‚ β”œβ”€β”€ health.rs # RelayHealthTracker (backoff, rate limits) β”‚ β”‚ β”œβ”€β”€ relay_connection.rs # RelayConnection, event loop lifecycle β”‚ β”‚ β”œβ”€β”€ self_subscriber.rs # SelfSubscriber (batched event discovery) β”‚ β”‚ └── metrics.rs # SyncMetrics for Prometheus β”‚ β”œβ”€β”€ purgatory/ # In-memory holding area for events awaiting git data β”‚ β”‚ β”œβ”€β”€ mod.rs # Purgatory core (state/PR storage, 30min expiry) β”‚ β”‚ β”œβ”€β”€ helpers.rs # State event ref matching, PR lookup β”‚ β”‚ β”œβ”€β”€ processing.rs # Unified git data processing (push + sync paths) β”‚ β”‚ └── sync/ # Proactive git data fetching β”‚ β”‚ β”œβ”€β”€ mod.rs # Public API (enqueue, main loop) β”‚ β”‚ β”œβ”€β”€ loop.rs # Sync loop (1s interval, debounced delays) β”‚ β”‚ β”œβ”€β”€ functions.rs # Core sync logic (try URLs, handle results) β”‚ β”‚ β”œβ”€β”€ queue.rs # SyncQueue (backoff, fresh start on new events) β”‚ β”‚ β”œβ”€β”€ throttle.rs # DomainThrottle (5 concurrent, 30/min, round-robin) β”‚ β”‚ └── context.rs # SyncContext trait + mock for testing β”‚ β”œβ”€β”€ http/ β”‚ β”‚ β”œβ”€β”€ mod.rs # HTTP module β”‚ β”‚ β”œβ”€β”€ landing.rs # Landing page handler β”‚ β”‚ └── nip11.rs # NIP-11 relay info document β”‚ └── metrics/ β”‚ β”œβ”€β”€ mod.rs # Prometheus metrics (Git, Nostr, Sync) β”‚ β”œβ”€β”€ bandwidth.rs # Bandwidth tracking β”‚ └── connection.rs # Connection tracking β”œβ”€β”€ docs/ # Documentation (DiΓ‘taxis framework) β”‚ β”œβ”€β”€ explanation/ # Architecture, decisions, GRASP-02 deep-dives β”‚ β”œβ”€β”€ how-to/ # Deployment, configuration guides β”‚ β”œβ”€β”€ tutorials/ # Getting started, first steps β”‚ └── reference/ # API docs, test strategy β”œβ”€β”€ tests/ # Integration tests (NIP-01, NIP-34, purgatory) β”œβ”€β”€ grasp-audit/ # Compliance audit subproject └── README.md ``` ## Comparison with ngit-relay | Feature | ngit-relay (Go) | ngit-grasp (Rust) | | ------------------- | ----------------------------------------- | ---------------------------------------------------- | | Language | Go | Rust | | Components | nginx + git-http-backend + hooks + Khatru | Single integrated binary | | Authorization | Pre-receive Git hook | Inline during receive-pack | | GRASP-01 | βœ… Complete | βœ… Complete | | GRASP-02 Event Sync | βœ… Limited | βœ… Advanced (NIP-77 negentropy + fallback) | | GRASP-02 Git Sync | βœ… Basic | βœ… Automatic purgatory hunting | | Multi-Maintainer | βœ… Supported | βœ… First-class (auto-sync across repos) | | Purgatory | βœ… 24-hour expiry | βœ… 30-minute expiry + proactive git data fetching | | Health Tracking | Basic | Advanced (exponential backoff, rate limit detection) | | Deployment | Docker + supervisord | Single binary | | Testing | Go tests + shell scripts | Rust unit + integration tests | | Performance | Good | Excellent (zero-copy, async) | | Monitoring | Basic logs | Comprehensive Prometheus metrics | ## Contributing Contributions welcome! Please: 1. Read [docs/explanation/architecture.md](docs/ARCHITECTURE.md) 2. Open an issue to discuss major changes 3. Follow Rust conventions and run `cargo fmt` + `cargo clippy` 4. Add tests for new functionality ## License MIT License - see [LICENSE](LICENSE) for details ## Related Projects - [GRASP Protocol](https://gitworkshop.dev/danconwaydev.com/grasp) - Protocol specification - [ngit-relay](https://gitworkshop.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-relay) - Reference implementation in Go - [ngit](https://gitworkshop.dev/ngit) - Nostr Git plugin for git CLI - [NIP-34](https://nips.nostr.com/34) - Git Stuff (Nostr protocol) ## Acknowledgments - Reference implementation by [@DanConwayDev](https://gitworkshop.dev/danconwaydev.com) - [rust-nostr](https://github.com/rust-nostr/nostr) team for excellent Nostr libraries - Git community for the Smart HTTP protocol