Implements GRASP-05 specification for accepting repository announcements
that don't list this relay, enabling archive, mirror, and backup use cases.
Core Features:
- Three whitelist formats: <npub>, <npub>/<identifier>, <identifier>
- Archive-all mode for complete ecosystem mirrors
- Fail-fast npub validation at startup
- Read-only enforcement (archived repos reject pushes)
- Full GRASP-02 sync (git data + Nostr events)
- Dynamic archive status (no flags/metadata)
Implementation:
- Add ArchiveWhitelistEntry enum with Pubkey/Repository/Identifier variants
- Add ArchiveConfig with validation and matching logic
- Update AnnouncementResult to include AcceptArchive variant
- Refactor validate_announcement() to return AnnouncementResult with archive check
- Update AnnouncementPolicy with catch-all pattern for cleaner code
- Wire archive config through builder and policy layers
Configuration:
- NGIT_ARCHIVE_ALL: Accept all announcements (⚠️ storage risk)
- NGIT_ARCHIVE_WHITELIST: Comma-separated whitelist entries
- Updated docs, .env.example, and nix/module.nix
Testing:
- 28 unit tests for config parsing and whitelist matching
- 7 integration tests for archive mode validation
- All 296 tests passing
Validation Priority:
1. Lists our service → Accept (GRASP-01, read/write)
2. Is maintainer → AcceptMaintainer (multi-maintainer, read/write)
3. Matches archive config → AcceptArchive (GRASP-05, read-only)
4. None of above → Reject
Security Considerations:
- Archive-all mode has storage/bandwidth DoS risk
- Identifier-only format matches any pubkey (use npub/identifier for high-value)
- Invalid npubs cause startup failure (fail-fast)
Documentation:
- Concise explanation focused on rationale
- Reference docs updated with all config options
- README updated to reflect completed feature
- Removed from roadmap, added to compliance section
See docs/explanation/grasp-05-archive.md for details.
4.5 KiB
GRASP-05 Archive Mode
Purpose: Understand archive/mirror/backup functionality
Audience: Operators and developers
What It Does
GRASP-05 enables ngit-grasp to accept repository announcements that don't list your relay, allowing you to run an archive, mirror, or backup service.
Standard GRASP-01: Announcement must list your service → You host it (read/write)
GRASP-05 Extension: Announcement matches your whitelist → You archive it (read-only)
Why It Exists
Problem
In GRASP-01 strict mode, you can only host repositories whose maintainers explicitly list your relay. This prevents:
- Creating backup archives of critical projects without maintainer cooperation
- Building comprehensive mirrors of the Nostr Git ecosystem
- Providing disaster recovery for projects that might disappear
Solution
Archive mode relaxes the "must list service" requirement for whitelisted repositories, enabling passive mirroring while maintaining read-only guarantees.
How It Works
Three Whitelist Formats
| Format | Example | Archives |
|---|---|---|
<npub> |
npub1alice... |
All repos from Alice |
<npub>/<identifier> |
npub1bob.../linux |
Only Bob's linux repo |
<identifier> |
bitcoin-core |
Any bitcoin-core repo (⚠️ any pubkey) |
Configuration:
# Specific repos (safest)
NGIT_ARCHIVE_WHITELIST=npub1torvalds.../linux,npub1satoshi.../bitcoin
# All repos from trusted maintainers
NGIT_ARCHIVE_WHITELIST=npub1alice...,npub1bob...
# Archive everything (⚠️ storage risk)
NGIT_ARCHIVE_ALL=true
Validation Priority
Announcements are checked in this order:
- Lists your service? →
Accept(GRASP-01, read/write) - Is author a maintainer? →
AcceptMaintainer(multi-maintainer, read/write) - Matches archive config? →
AcceptArchive(GRASP-05, read-only) - None of the above →
Reject
This ensures GRASP-01 compliant repos are always writable, even if they match the archive whitelist.
Storage Model
Archived repos use the same directory structure as hosted repos:
<git_data_path>/
npub1alice.../
hosted-repo.git/ # Lists your service (writable)
archived-repo.git/ # Whitelisted (read-only)
No flags or metadata - archive status determined dynamically from config + announcement contents.
Full Sync
Archived repositories trigger complete GRASP-02 sync:
- ✅ Nostr events (PRs, issues, patches)
- ✅ Git data via purgatory
- ✅ Same validation as hosted repos
Archive mode is a complete mirror, not just git-only backup.
Security Considerations
1. Archive-All Mode (Dangerous)
Don't use NGIT_ARCHIVE_ALL=true unless:
- You have unlimited storage/bandwidth
- You trust the relay network
- You've implemented monitoring
Attack vector: Anyone can publish announcements → unlimited storage consumption.
2. Identifier-Only Format (Risky)
NGIT_ARCHIVE_WHITELIST=bitcoin-core # Matches ANY pubkey!
Malicious users can publish fake repos with popular identifiers. Use <npub>/<identifier> for high-value archives.
3. Npub Validation
Invalid npubs → server fails to start (fail-fast). Identifiers aren't validated (any string allowed).
Operational Guide
Start Small
# Day 1: One critical repo
NGIT_ARCHIVE_WHITELIST=npub1torvalds.../linux
# Week 1: Add trusted maintainers
NGIT_ARCHIVE_WHITELIST=npub1alice...,npub1bob...
# Month 1: Consider popular identifiers (with monitoring)
NGIT_ARCHIVE_WHITELIST=npub1alice...,bitcoin-core
Monitor Growth
Watch for:
- Storage consumption rate
- Purgatory git fetch failures
- Bandwidth usage spikes
Whitelist Changes
Current: Static config - edit .env, restart server
Future: REST API for dynamic management (no restart)
Comparison: Hosted vs Archived
| Aspect | Hosted (GRASP-01) | Archived (GRASP-05) |
|---|---|---|
| Announcement must list you | ✅ Required | ❌ Whitelisted instead |
| Git pushes | ✅ Accepted | ❌ Rejected (read-only) |
| GRASP-02 sync | ✅ Full sync | ✅ Full sync |
| Relay discovery | ✅ Listed | ❌ Not listed |
| Use case | Hosting workspace | Backup/mirror |
Related Documentation
- Configuration Reference -
NGIT_ARCHIVE_*options - GRASP-05 Spec - Protocol specification
- GRASP-02 Sync - How sync works
Part of the ngit-grasp explanation documentation