The relay-owner key is intended to double as the service identity used by ngit-ci, but clients could only discover it through HTTP and owner-authored coordinator events without repository roots failed admission. Sign a minimal kind-0 NIP-05 bot profile at startup — per NIP-24 `name` is always set, here to the scheme-less public URL — alongside a single unmarked kind-10002 relay entry. An operator-customized profile is kept and never overwritten, and no identity event — locally stored or freshly generated — is published before the local database and at least one user-index relay have been successfully checked for that kind, so a database wiped and reseeded during an index outage can never displace a customized profile surviving on the indexes. Every send is preceded by a per-relay re-check: an identity found on an index relay is adopted locally, where replaceable-event semantics keep the newest copy, and is never overwritten, so publication only fills gaps on index relays that individually confirm they hold none; propagating a profile update onto an index that already has one is left to the operator's own client. A kind with no local copy is not even seeded until a reachable user-index relay confirms it holds no identity of that kind. Publication never blocks startup, retries transient failures with a capped backoff, and stops on terminal protocol rejections. With no user-index relays configured, missing kinds are seeded locally right away. In private mode (GRASP-08) identity events are seeded and served locally but never published, so a private relay does not advertise its existence. Trust valid owner-signed events only for kinds without a dedicated admission policy, such as ngit-ci coordinator advertisements that carry no repository root tag. Owner-signed NIP-34 announcements, state events, and PRs run the normal announcement validation, ref alignment, and purgatory git-data handling like any other author, and the relay's own kind 0/10002 identity is always accepted. The NIP-09/NIP-62 deletion gate still runs before any owner acceptance, so replaying a retracted owner event cannot undo its tombstone, and owner deletion/vanish requests keep their lifecycle handling. Because the event blacklist cannot block the owner key, rotating the key is the only remediation if it is compromised; this is documented. NGIT_DOMAIN is assumed to be the documented bare public authority: loopback authorities use ws and other hosts advertise wss, with bare IPv6 authorities bracketed. The test fixture reuses relay-owner keys across TestRelay::restart, and gains caller-provided keys, a private-mode member setup, and explicit user-index relay lists; MockRelay can start pre-seeded so a "recovering" index deterministically holds prior state. Identity retry intervals honor the existing NGIT_TEST fast-timer convention. No new configuration switches or ngit-ci changes are included. Includes rustfmt fixes for src/nostr/policy/announcement.rs and tests/private_mode.rs, which arrived on master unformatted and would otherwise fail the workspace format gate. Validated with rustfmt, strict workspace Clippy, the relay_identity and private_mode integration binaries, and the full workspace test suite. Individual sync/grasp06 integration tests fail intermittently under parallel full-suite load, each passing in isolation and on rerun; the same intermittent failures reproduce on origin/master without these changes.
Explanation
Understanding-oriented documentation - Concepts, design decisions, and the "why" behind ngit-grasp.
What Is Explanation?
Explanation documentation helps you understand concepts and design decisions, providing context and discussing alternatives.
Characteristics:
- ✅ Understanding-oriented (clarify concepts)
- ✅ Theoretical (ideas and design)
- ✅ Discuss alternatives
- ✅ Provide context and background
- ✅ Answer "why" questions
Not explanation:
- ❌ Step-by-step lessons (those are Tutorials)
- ❌ Problem-solving recipes (those are How-To)
- ❌ Technical specifications (those are Reference)
Available Explanation Documentation
Architecture Overview
Understand the system design and component interaction
Topics:
- Overall architecture
- Component responsibilities
- Data flows
- Technology choices
- Design patterns
Read when: You want to understand how ngit-grasp works as a system
Inline Authorization
Why we validate pushes inline instead of using Git hooks
Topics:
- The authorization problem
- Git hooks approach
- Inline approach
- Comparison and trade-offs
- Implementation details
Read when: You want to understand the core architectural decision
Design Decisions
Key architectural choices and their rationale
Topics:
- Inline authorization vs hooks
- Technology stack choices
- Storage design
- API design
- Performance considerations
Read when: You want to know why things are the way they are
Comparison with ngit-relay
How ngit-grasp differs from the reference implementation
Topics:
- Architecture comparison
- Component differences
- Trade-offs
- Migration path
- Compatibility
Read when: You're familiar with ngit-relay and want to understand differences
Purgatory Design
In-memory holding area for events awaiting git data
Topics:
- The "which arrives first?" problem
- Separate storage for state vs PR events
- Late binding for state events
- Bidirectional waiting for PR events
- Authorization during push
Read when: You want to understand how ngit-grasp handles out-of-order event/git data arrival
GRASP-02 Proactive Sync
Relay-to-relay synchronization for repository discovery
Topics:
- Negentropy-based event sync
- Repository announcement discovery
- Relay management and reconnection
- Layer 2 filtering
- Bootstrap and dynamic relay discovery
Read when: You want to understand how ngit-grasp discovers and syncs repositories across relays
GRASP-03 Proactive Sync Plus
NIP-65 inbox/outbox discovery for accepted repository conversations
Read when: You want to understand how accepted conversations are recovered from participant mailboxes
Sync Scaling Constraints and Budgets
Relay-imposed limits and how sync spends them at scale
Topics:
- Verified relay limits (strfry, live NIP-11, our embedded relay)
- Per-connection budget ledger (live vs historic vs fallback)
- Byte-budgeted filter chunking and REQ packing
- Bounded negentropy concurrency
- Multi-connection escalation and serving-side obligations
Read when: You're changing filter construction, subscription management, or sync concurrency, and need the constraint justification
GRASP-02 Purgatory Git Data Fetching
Proactive git data fetching from remote servers
Topics:
- Identifier-based batching
- Exponential backoff with fresh start
- Domain throttling (5 concurrent, 30/min)
- Debounced delays (3min user, 500ms sync)
- 30-minute expiry
- Mock-based testability
Read when: You want to understand how purgatory automatically fetches missing git data
Unified Git Data Sync
Shared processing for git push and purgatory sync paths
Topics:
- Why unify push and sync processing
- OID syncing to owner repos
- Ref alignment logic
- Event release from purgatory
- WebSocket notification
Read when: You want to understand how git data is processed consistently regardless of arrival method
Monitoring Overview
Prometheus metrics and observability
Topics:
- Metrics philosophy
- Connection tracking
- Git operation metrics
- Nostr event metrics
- Privacy considerations
Read when: You want to understand how to monitor ngit-grasp in production
Defensive Measures & Rate Limiting
Protection against abuse, spam, and denial-of-service attacks
Topics:
- Connection and subscription management
- Event publishing rate limits
- Content filtering (blacklists/whitelists)
- Event validation plugin system (WritePolicy/QueryPolicy)
- Relay health management (naughty list, exponential backoff)
- Privacy-preserving IP tracking
- Future enhancements (per-IP rate limiting)
Read when: You want to understand how ngit-grasp protects against abuse and what defensive features are available
GRASP-05 Archive Mode
Read-only mirroring of repositories
Topics:
- Archive whitelist configuration
- Archive-all mode
- Read-only mode defaults
- Use cases for backup/mirror relays
Read when: You want to understand how to run an archive/backup relay
GRASP-08 Private Service Authentication
Service-wide NIP-42/NIP-98 authentication for private repositories
Topics:
- Fail-closed private mode and indistinguishable 401 responses
- The GRASP-08 repository-scoped NIP-98 profile vs generic NIP-98
- NIP-42 authentication outside the embedded relay
- Service-wide membership and dynamic accepted-relay-owner admission
- Trust model and follow-up scope
Read when: You want to understand how a private GRASP instance authenticates clients and peers
Repository Lifecycle
Handling repository removal, holding, archive, recovery, and purgatory
Topics:
- Repository lifecycle architecture
- Delete disrespector concept
- Preventing left-pad scenarios
- Archival policies
- Holding, recovery, purgatory, and operator curation flows
Read when: You want to understand how ngit-grasp keeps nostr state and git data aligned across deletion, vanish, moderation, recovery, and purgatory flows
Planned Explanation Documentation
GRASP Protocol Design
Status: 🔜 Planned
Topics:
- Why Nostr for Git?
- Authorization model
- Trust and verification
- Decentralization benefits
Storage Architecture
Status: 🔜 Planned
Topics:
- Why separate Git and Nostr storage?
- Indexing strategy
- Performance considerations
- Scaling approach
Testing Philosophy
Status: 🔜 Planned
Topics:
- Why test isolation?
- Integration vs unit tests
- Compliance testing approach
- Test-driven development
Performance Considerations
Status: 🔜 Planned
Topics:
- Async architecture
- Caching strategy
- Database choices
- Bottlenecks and solutions
How to Use Explanation Documentation
- Read to understand - Not to accomplish a task
- Follow your curiosity - Read what interests you
- Connect concepts - Link ideas together
- Question and explore - Think critically
Not sure if this is what you need?
- Want to learn by doing? → Tutorials
- Need to solve a problem? → How-To Guides
- Looking for technical details? → Reference
Contributing Explanation Documentation
When writing explanation:
DO:
- ✅ Discuss concepts and ideas
- ✅ Provide context and background
- ✅ Explain alternatives
- ✅ Use analogies and examples
- ✅ Connect to broader context
- ✅ Answer "why" questions
DON'T:
- ❌ Provide step-by-step instructions (link to Tutorials/How-To)
- ❌ List technical details (link to Reference)
- ❌ Assume you must be comprehensive
- ❌ Avoid opinions (explanation can be opinionated)
Template:
# Explanation: [Topic]
**Purpose:** [What concept/decision this explains]
**Audience:** [Who wants to understand this]
---
## The Problem/Question
[What are we trying to understand?]
---
## Background
[Context and history]
---
## Our Approach
[How we address it]
### Why This Works
[Explanation of benefits]
### Trade-offs
[What we gain and lose]
---
## Alternatives Considered
### [Alternative 1]
**Pros:**
- [Benefits]
**Cons:**
- [Drawbacks]
**Why we didn't choose it:**
[Reasoning]
---
## Conclusion
[Summary of understanding]
---
## Related Documentation
- [Links to relevant docs]
See Diátaxis: Explanation for detailed guidance.
Part of the ngit-grasp documentation using the Diátaxis framework.