Files
ngit-grasp/closed/820a-relay-ngit-dev-migration.md

1077 lines
54 KiB
Markdown

# Migrate relay.ngit.dev from ngit-relay to ngit-grasp
**ID:** 820a
## Problem
relay.ngit.dev currently runs ngit-relay (reference implementation). We want to consolidate on ngit-grasp as the production implementation to:
- Reduce operational complexity (one codebase instead of two)
- Focus logging and observability improvements on a single project
- Leverage ngit-grasp's proven performance on ngit.danconwaydev.com
**Why this matters:** Running two different implementations increases maintenance burden and makes it harder to add features like comprehensive logging and performance monitoring. Consolidating on ngit-grasp enables:
- Unified logging and metrics infrastructure
- Single codebase for bug fixes and features
- Consistent behavior across all deployments
- Reduced testing and deployment complexity
## Plan
**High-Level Migration Approach:** Archive-based migration to ensure complete git data availability and prevent purgatory issues.
**Timeline:** 3-5 days total (from archive sync completion)
**Migration Phases:**
1. ✅ **Phase 1: Archive Sync** (1-2 days) - COMPLETED
- Deploy archive instance syncing from relay.ngit.dev
- Archive fetches all git data during sync
- Monitor sync progress and completion
2. ❌ **Phase 2: Validation** (4-6 hours) - BLOCKED
- Nostr events (kind 30617): 99.86% sync ✅
- State events (kind 30618): Production 39.3%, Archive 52.4% ❌ **INCOMPLETE**
- Production baseline established: 685 repos, 212 npubs, 95% git data match ✅
- Archive sync analysis complete: 1,227 repos, 1,226 npubs ✅
- **CRITICAL BLOCKERS IDENTIFIED:**
- 22 npubs in production NOT in archive (data loss risk) ❌
- 1,036 npubs in archive NOT in production (unexplained) ⚠️
- Repo count mismatches in shared npubs (sync inconsistency) ⚠️
- State event coverage <60% on both relays (expected >95%) ❌
- **Required:** Full investigation and re-sync before proceeding to Phase 3
- Next: Investigate missing npubs, audit extra npubs, reconcile data
3. **Phase 3: Test Instance** (1 day) - PENDING
- Deploy test instance syncing from archive
- Validate all repos accessible
- Verify no purgatory issues
4. **Phase 4: Production Cutover** (2-4 hours) - PENDING
- Stop ngit-relay, deploy ngit-grasp
- Monitor 30-minute critical window
- Rollback if issues detected
5. **Phase 5: Post-Migration Monitoring** (48 hours) - PENDING
- Intensive monitoring (15min → hourly → 4h → 8h intervals)
- Compare metrics to baseline
- Remove ngit-relay container after 7 days if successful
**Detailed Implementation:**
- [Migration Guide](../../docs/how-to/ngit-relay-migration-guide.md) - High-level migration phases and procedures
- [Migration Validation Guide](../../docs/how-to/migration-validation-guide.md) - Detailed validation procedures and troubleshooting
## Accelerated Migration Plan
**Timeline:** 3-5 days total (vs 3-5 weeks original)
### Day 0-3: Minimal Baseline Collection (2-3 days) - IN PROGRESS ⏳
**Goal:** Capture enough baseline data to detect major regressions
**Status:** Collection started 2026-01-17 23:30 UTC, extended to 2-3 days (until 2026-01-20/21)
**Minimum metrics needed:**
- [x] CPU usage patterns (1 full day cycle minimum) - COLLECTING ✅
- [x] Memory baseline (current RSS, growth rate) - COLLECTING ✅
- [x] Peak connection counts (identify daily peak hours) - COLLECTING ✅
- [x] Basic response time percentiles (p50, p95, p99) - Via connection stats ✅
- [x] Error rate baseline (if any) - Via systemd logs ✅
**Metrics Status (2026-01-18):**
- ✅ Critical metrics working: connections, CPU, memory, load average
- ❌ Container stats broken (cgroup access issues) - NOT CRITICAL
- ❌ FD counts broken (permission issues) - NOT CRITICAL
- Decision: Proceed with working metrics, sufficient for migration decision
**Quick setup:**
```bash
# On relay.ngit.dev VPS
# 1. Check current metrics via systemd/journalctl
systemctl status ngit-relay
journalctl -u ngit-relay --since "24 hours ago" | grep -i error
# 2. Capture system metrics snapshot
top -b -n 1 | head -20
free -h
ss -s # socket statistics
# 3. If nginx metrics available, capture 24h sample
# Otherwise, rely on system metrics + manual testing
```
**Acceptance criteria:**
- [ ] 2-3 days of continuous operation data (IN PROGRESS - ~10.5h collected)
- [ ] Identified peak usage hours (collecting data)
- [ ] No critical errors in baseline period (monitoring)
- [ ] Documented current resource usage (ongoing)
### Day 3-4: Rapid Preparation (4-6 hours)
**Configuration review:**
- [ ] Map ngit-relay config → ngit-grasp config
- [ ] Prepare NixOS service definition
- [ ] Document rollback procedure (keep ngit-relay container)
- [ ] Pre-build ngit-grasp on VPS (cache dependencies)
**Quick prep checklist:**
```bash
# 1. Review current ngit-relay config
docker inspect ngit-relay | jq '.[0].Config.Env'
# 2. Create ngit-grasp service config (see template below)
# 3. Test build on VPS
nix build github:DanConwayDev/ngit-grasp
# 4. Verify data directory permissions
ls -la /persistent/ngit-relay/
```
**Rollback plan:**
- Keep ngit-relay container stopped but available
- Document exact `docker start` command
- 5-minute rollback window if issues detected
### Day 4-5: Migration Execution (2-4 hours)
**Execution window:** Off-peak hours (based on Day 0-1 data)
**Steps:**
1. [ ] Stop ngit-relay container (don't remove)
2. [ ] Deploy ngit-grasp via NixOS
3. [ ] Verify service starts and binds to port
4. [ ] Test WebSocket connection
5. [ ] Test git clone operation
6. [ ] Monitor for 30 minutes (critical window)
**Go/No-Go criteria (30 min checkpoint):**
- ✅ Service running and accepting connections
- ✅ No error spikes in logs
- ✅ Memory usage within 2x of baseline
- ✅ CPU usage reasonable (<80% sustained)
- ✅ Git operations functional
**If No-Go:** Rollback immediately
```bash
systemctl stop ngit-grasp-production
docker start ngit-relay
# Total rollback time: <5 minutes
```
### Day 5-7: Intensive Monitoring (48 hours)
**Critical monitoring period:**
- [ ] Hour 1: Check every 15 minutes
- [ ] Hours 2-6: Check every hour
- [ ] Hours 6-24: Check every 4 hours
- [ ] Hours 24-48: Check every 8 hours
**Monitor:**
- CPU/memory trends (compare to baseline)
- Connection counts (should be similar)
- Error logs (any new patterns?)
- Response times (p95, p99)
- Git operation success rate
**Success criteria (48h):**
- ✅ No critical errors
- ✅ Resource usage ≤ 2x baseline (acceptable for new impl)
- ✅ No user complaints
- ✅ Git operations working
- ✅ WebSocket connections stable
**If successful:** Remove ngit-relay container after 7 days
**If issues:** Rollback and investigate
## Progress
### 2026-01-23 [Session 16:00] - Issue Paused - Starting Fresh with v2
- Decision: Migration has become too complex with extensive investigation history
- Reason: Need simpler, more focused approach with lightweight how-to documentation
- Action: Pausing this issue and creating new v2 issue to start from scratch
- Context: Will reference existing scripts and learnings from this worktree
- Goal: Create small, easy-to-implement how-to document for VPS migration
- Status: ⏸️ **PAUSED** - Superseded by new v2 migration issue
### 2026-01-17 [Session 22:00]
- Created: Initial issue based on pivot from 573b timeout diagnosis
- Context: Phase 1 of 573b is complete (connection limits increased, rate limiting fixed)
- Rationale: Better to consolidate on ngit-grasp than implement duplicate features in ngit-relay
- Blocker: Must collect baseline metrics BEFORE migration for objective comparison
- Next: Deploy nginx metrics to relay.ngit.dev and begin baseline collection
### 2026-01-17 [Session 23:30]
- Added: Accelerated migration plan (3-5 days vs 3-5 weeks)
- Decision: User wants to move quickly after minimal baseline collection
- Approach: 48-72h baseline → rapid prep → execute → intensive monitoring
- Risk mitigation: Keep ngit-relay container for instant rollback
- Next: Begin minimal baseline collection (48-72h)
### 2026-01-17 [Session 23:45] - Baseline Collection Started
- Completed: Enhanced diagnostics deployed to all three relays
- Improvements:
1. ✅ Fixed connection counting (port-based, not PID-based)
2. ✅ Added container stats collection (CPU, memory, network I/O)
3. ✅ Added connection state breakdown (ESTABLISHED, TIME_WAIT, etc.)
4. ✅ Created comprehensive MONITORING.md documentation
- Baseline Collection Status:
- **Started:** 2026-01-17 23:30 UTC
- **Duration:** 36 hours (until 2026-01-19 11:30 UTC)
- **Relays monitored:** gitnostr.com, relay.ngit.dev, ngit.danconwaydev.com
- **Enhanced metrics:** Connection counts per service, container stats, state breakdown
- **Purpose:** Establish baseline before relay.ngit.dev migration
- Timeline Update:
- **Day 0 (2026-01-17 23:30):** Baseline collection started
- **Day 0+10min (2026-01-17 23:40):** Verify data quality
- **Day 1.5 (2026-01-19 11:30):** Review baseline data (36 hours)
- **Day 2:** Rapid preparation (if baseline looks good)
- **Day 3:** Migration execution
- **Day 3-5:** Intensive monitoring
- Next Steps:
1. Check baseline at 10-minute mark (verify collection working)
2. Review baseline after 36 hours
3. Make migration decision based on baseline data quality
### 2026-01-18 [Session 10:00] - Baseline Collection Status Update
- Completed: 2 iterations of diagnostics fixes to improve metric reliability
- **Critical Metrics Working:**
- ✅ Connection counts per service (port-based detection working)
- ✅ System metrics (load average, memory, CPU breakdown)
- ✅ Top processes (identifying resource consumers)
- ✅ Connection state breakdown (ESTABLISHED, TIME_WAIT, etc.)
- ✅ Basic container visibility (containers appear in top processes)
- **Non-Critical Metrics Still Broken:**
- ❌ Container stats (cgroup access issues, requires privileged systemd-run)
- ❌ File descriptor counts per process (permission issues)
- Note: Container resource usage still visible in top processes section
- **Decision: Proceed with Current Metrics**
- Critical metrics (connections, CPU, memory) working reliably
- Broken metrics are nice-to-have, not essential for migration decision
- Have sufficient data to detect major regressions
- Can proceed with migration planning based on working metrics
- **Baseline Collection Extended:**
- Current duration: ~10.5 hours (started 2026-01-17 23:30 UTC)
- Extended to: 2-3 more days (until 2026-01-20 or 2026-01-21)
- Reason: Want more data to establish reliable daily patterns
- Collection continuing with working metrics
- **Updated Timeline:**
- **Day 0 (2026-01-17 23:30):** Baseline collection started
- **Day 0.5 (2026-01-18 10:00):** Diagnostics fixes complete, extending collection
- **Day 2-3 (2026-01-20/21):** Review baseline data (2-3 days total)
- **Day 3-4:** Rapid preparation (if baseline shows stable patterns)
- **Day 4-5:** Migration execution
- **Day 5-7:** Intensive monitoring
- **Next Steps:**
1. Continue baseline collection for 2-3 more days
2. Review baseline data patterns after collection period
3. Make migration decision based on baseline analysis
4. Proceed with rapid preparation if baseline looks good
### 2026-01-19 [Session 11:35] - Baseline Analysis Complete - VPS UPGRADE REQUIRED ⚠️
- Completed: Comprehensive analysis of 36 hours of baseline metrics
- **CRITICAL DECISION: Migration BLOCKED until VPS upgrade**
- **Baseline Findings (2026-01-17 23:30 to 2026-01-19 11:35 UTC):**
- **CPU:** Average 6.96 load (348% utilization), peak 11.83 (592%) - 🔴 SEVERELY OVERLOADED
- **Memory:** 96.8% average usage, only 130MB available - 🔴 MAXED OUT
- **Swap:** 2.0GB average (40% of 5GB), growing 0.75GB → 2.5GB - 🟡 HEAVILY USED
- **Connections:** Port 8081: 28 avg/120 peak, Port 8083: 31 avg/126 peak - ✅ ONLY 3% OF CAPACITY
- **Correlation:** Load vs connections = 0.210 (weak) - Confirms git operations are bottleneck, not connections
- **Peak Usage Patterns:**
- Peak hours: 16:00-20:00 UTC (load 6.5-9.9, connections 865-977)
- Off-peak hours: 00:00-12:00 UTC (load 6.3-7.4, connections 350-470)
- **Critical insight:** Even off-peak shows HIGH load (6.3-7.4), indicating baseline overload independent of traffic
- **Migration Risk Assessment:**
- **Current VPS (2-core, 4GB):** 🔴 **HIGH RISK** - Migration will add load spike to already overloaded system
- **Outcome:** High probability of service degradation or outages during migration
- **Recommendation:** ❌ **DO NOT MIGRATE** on current VPS
- **VPS Upgrade Requirements:**
- **Minimum (Survival):** 4 cores, 8GB RAM (~$20-40/month) - 🟡 MEDIUM RISK
- **Recommended (Healthy):** 6 cores, 8GB RAM (~$40-60/month) - 🟢 LOW RISK ✅
- **Optimal (Future-proof):** 8 cores, 16GB RAM (~$60-100/month) - 🟢 LOW RISK
- **Decision:** Upgrade to 6-core, 8GB minimum before migration
- **Updated Migration Timeline:**
- **REVISED:** 5-7 days total (was 3-5 days)
- **Day 0-1:** VPS upgrade + verification (NEW - CRITICAL)
- **Day 2-3:** Rapid preparation
- **Day 4-5:** Migration execution
- **Day 5-7:** Intensive monitoring
- **Data Quality:**
- ✅ 36 hours continuous collection, 2,160 data points
- ✅ Clear patterns identified, no anomalies
- ✅ **Sufficient for migration decision** - no additional collection needed
- ✅ Findings are conclusive: VPS upgrade required
- **Next Steps:**
1. **CRITICAL:** Upgrade VPS to 6-core, 8GB RAM (BLOCKER for migration)
2. Verify upgrade impact (24-48 hours monitoring, expect load to drop to ~60-70%)
3. Proceed with accelerated migration plan (Day 2-7)
4. Monitor intensively during and after migration
- **Files Created:**
- `/tmp/vps-baseline-report.md` - Comprehensive 36-hour analysis with migration scenarios
- **Status:** Baseline complete ✅, Migration BLOCKED ⚠️ pending VPS upgrade
- **Blocker:** VPS upgrade to 6-core, 8GB RAM required before migration can proceed
### 2026-01-20 [Session 16:00] - Worktree Created for Migration Work
- Started: Created dedicated worktree for migration work
- Context: Work has progressed in /tmp directory, now moving to proper worktree
- Next: Save relevant files from /tmp into this worktree and continue migration work
### 2026-01-20 [Session 18:30] - State Event Validation FAILED - Migration BLOCKED ❌
- Completed: State event (kind 30618) validation between relay.ngit.dev and archive
- **CRITICAL BLOCKER IDENTIFIED:**
- State event sync: **42.4%** (expected >99%) ❌
- Archive missing 338/587 repos from production (57.6% gap)
- Git data verification failed: sample repos have empty refs directories
- Purgatory suspected: repos synced but git data incomplete
- **Comparison to kind 30617:**
- Announcements (30617): 99.86% sync ✅
- State events (30618): 42.4% sync ❌
- Indicates archive failed to fetch/maintain git data for majority of repos
- **Root cause analysis:**
- Archive successfully synced event metadata
- Archive failed to fetch git data from clone URLs
- ngit-grasp correctly withholds state events when git data missing
- Result: 57.6% of repos would lose main/master branch visibility after migration
- **Files created:**
- `work/state-validation/STATE-VALIDATION-REPORT.md` - Comprehensive analysis
- `work/state-validation/missing-from-archive.txt` - List of 338 missing repos
- `work/state-validation/archive-metrics.txt` - Archive relay metrics
- **Migration status:** ❌ **BLOCKED** - Cannot proceed to Phase 3 with <50% state event coverage
- **Next actions required:**
1. Investigate why archive git fetches failed for 338 repos
2. Check archive sync logs for git fetch errors
3. Determine if sync still in progress or permanently failed
4. Fix git fetch issues and re-sync missing repos
5. Re-validate state events once archive sync issues resolved
- **Blocker:** Archive sync incomplete/failed - must achieve >99% coverage before migration
### 2026-01-20 [Session 19:00] - Archive Sync Blocker ROOT CAUSE FOUND 🔍
- Completed: Investigation of archive sync failures for missing 338/587 repos (57.6%)
- **ROOT CAUSE IDENTIFIED:**
- **Archive is attempting to clone from `https://relay.ngit.dev/...` URLs**
- **relay.ngit.dev does NOT serve HTTP git clones** - only WebSocket relay service
- Manual test confirmed: `git clone https://relay.ngit.dev/<npub>/<repo>.git` returns 404
- Archive has been running 36+ hours but silently failing on relay.ngit.dev URLs
- **Clone URL Pattern:**
```
["clone","https://git.shakespeare.diy/.../repo.git","https://relay.ngit.dev/.../repo.git"]
```
- Primary URLs (git.shakespeare.diy, github.com, etc.): ✅ Works
- Fallback URLs (relay.ngit.dev): ❌ 404 Not Found
- **Archive Metrics (Port 7443):**
- Service status: ✅ Running normally (19+ hours uptime)
- Events synced: 3,445 from wss://relay.ngit.dev
- Repositories tracked: 1,856 (matches 42% coverage)
- Disk usage: 4.8GB (40-48% of expected 10-12GB)
- **Confirms partial sync, not full sync**
- **Why Only 42% Coverage:**
- Archive successfully fetches repos with working primary clone URLs
- Archive FAILS for repos with only relay.ngit.dev clone URLs (338/587 = 57.6%)
- Missing repos likely have relay.ngit.dev as primary or only clone URL
- **Configuration Analysis:**
- Archive config is CORRECT: `NGIT_DOMAIN=archive.internal`
- Sync source is CORRECT: `wss://relay.ngit.dev`
- Archive is NOT filtering relay.ngit.dev URLs (domain is different)
- **Files Created:**
- `work/state-validation/ARCHIVE-SYNC-INVESTIGATION.md` - Full investigation report
- Documented service status, metrics, logs, root cause, and recommended fix
- **Recommended Fix: Option 1 (RECOMMENDED)**
- Configure relay.ngit.dev to serve HTTP git clones
- Add nginx/HTTP service pointing to `/persistent/grasp/production/git/`
- Serve at `https://relay.ngit.dev/<npub>/<repo>.git/`
- Makes relay.ngit.dev a complete git hosting service
- No event republishing required
- **Alternative Fixes:**
- Option 2: Update state events to remove non-working clone URLs (complex)
- Option 3: Enhance archive retry logic (doesn't solve URL problem)
- **Next Steps:**
1. Decide on fix approach (Option 1 recommended)
2. Configure HTTP git service on relay.ngit.dev
3. Restart archive to retry failed fetches
4. Monitor archive metrics for coverage improvement
5. Re-run state validation (expect >95% coverage after fix)
- **Migration Status:** ❌ **BLOCKED** - HTTP git service required on relay.ngit.dev
- **Success Criteria:** Archive coverage >95% (587/587 repos), git data size ~10-12GB
- **Issue Created:** 1ce6-purgatory-retry-mechanism.md - Documents purgatory retry problem for future fix
### 2026-01-20 [Session 16:00] - Git Data Analysis Complete - 70.8% Production Repos Fully Synced
- Completed: Comprehensive analysis of production git data vs state events
- **Key Findings:**
- **177/250 repos (70.8%)** have perfect sync on production ✅
- **62/250 repos (24.8%)** are empty/blank - legitimately missing git data
- **6/250 repos (2.4%)** have partial matches - need careful review
- **5/250 repos (2.0%)** have no matches - stale state events or force-pushed
- **Comparison to Archive:**
- Production: 70.8% complete matches, 24.8% empty/blank
- Archive: Only 13% repos with git data, 85% in purgatory
- **Critical insight:** Archive sync is failing for repos that production already has
- **Implication:** Archive-based migration approach has fundamental issue
- Archive can't sync repos that production already serves successfully
- Need to identify which "Complete Match" repos are missing from archive
- Review archive logs to understand why these repos aren't syncing
- **Next Steps:**
1. Identify which 177 "complete match" repos are missing from archive
2. Review archive logs to understand sync failures
3. Mark 62 empty/blank repos as not needed (can discard)
4. Careful review of 11 partial/no-match repos (architect analysis)
- **Files Created:**
- `/tmp/git-state-analysis/category-complete-matches.txt` - 177 fully synced repos
- `/tmp/git-state-analysis/category-empty-blank.txt` - 62 repos with no git data
- `/tmp/git-state-analysis/category-partial-matches.txt` - 6 repos needing review
- `/tmp/git-state-analysis/category-no-matches.txt` - 5 repos with wrong commit hashes
- **Migration Status:** Archive approach investigation ongoing ⚠️
### 2026-01-20 [Session 18:10] - COMPREHENSIVE ANALYSIS COMPLETE - Archive NOT Ready ❌
- Completed: Full production vs archive comparison with corrected analysis methodology
- **CRITICAL FINDING: Previous analysis had permission issues - actual results differ significantly**
- **Production Baseline (relay.ngit.dev - ngit-relay):**
- Total repos: 685 repos (6.5GB)
- NPub count: 212 unique users
- State events: 633 total, 249 served (39.3% coverage)
- Git data match: **95% of state events have matching git directories** ✅
- Directory structure: `/persistent/relay-ngit-dev-ngit-relay/data/repos/<npub>/<repo>.git`
- **Archive Status (ngit-grasp-relay-ngit-archive):**
- Total repos: 1,227 repos (5.4GB) - **79% MORE than production**
- NPub count: 1,226 unique users - **5.8x MORE than production**
- State events: 332 served (52.4% coverage) - **33% MORE than production**
- Service: Active and syncing (3+ hours uptime)
- Directory structure: `/persistent/grasp/sync-archive/git/<npub>/<repo>.git`
- **Migration Gap Analysis:**
- **Shared npubs:** 190 (both production and archive)
- **Missing from archive:** 22 npubs (in production but NOT in archive) ❌ **DATA LOSS RISK**
- **Extra in archive:** 1,036 npubs (in archive but NOT in production) ⚠️ **UNEXPLAINED**
- **Repo count mismatches:** Many shared npubs have different repo counts between prod/archive
- **State Event Coverage:**
- Production: 249/633 (39.3%) - **60.7% gap**
- Archive: 332/633 (52.4%) - **47.6% gap**
- Neither relay serves complete state event coverage
- **Analysis Script Issues Discovered:**
- Initial runs showed "100% no_git_dir" due to permission issues (script didn't use sudo)
- Corrected analysis with sudo shows 95% git data match in production
- Script expects specific directory structure that matches both production and archive
- **Migration Readiness Assessment:** ❌ **NOT READY**
- **Blocker 1:** 22 npubs missing from archive (data loss risk)
- **Blocker 2:** 1,036 unexplained extra npubs in archive (data quality concern)
- **Blocker 3:** Repo count mismatches in shared npubs (sync inconsistency)
- **Blocker 4:** Neither relay has complete state event coverage (<60%)
- **Required Actions Before Migration:**
1. **Investigate 22 missing npubs** - Why aren't they in archive? Recent additions? Blocked?
2. **Audit 1,036 extra npubs** - Where did they come from? Historical data? Test data?
3. **Reconcile repo count mismatches** - Identify stale/missing repos in shared npubs
4. **Full re-sync** - Ensure all 212 production npubs are in archive with correct data
5. **Validate state event coverage** - Understand why only 39-52% coverage (expected >95%)
- **Files Created:**
- Analysis reports from 4 parallel subagent investigations
- Production baseline: 685 repos, 212 npubs, 95% git data match
- Archive status: 1,227 repos, 1,226 npubs, active sync
- Gap analysis: 22 missing, 1,036 extra, inconsistent repo counts
- **Next Steps:**
1. Investigate the 22 missing npubs (check recent activity, sync logs)
2. Audit archive git history to understand 1,036 extra npubs
3. Create detailed sync plan to reconcile production → archive
4. Re-validate after sync completion
5. Only proceed to Phase 3 after achieving >95% coverage
- **Migration Status:** ❌ **BLOCKED** - Archive needs cleanup and full re-sync before migration
### 2026-01-21 [Session 10:00] - Fetch Failure Deep Dive - ROOT CAUSE CORRECTED
- Completed: Deep investigation of "not our ref" error and purgatory flow
- **Previous analysis was INCORRECT** - "not our ref" was a red herring
- **Actual Root Cause:** Negentropy sync failures preventing state event transfer
- **Key Findings:**
- Archive service IS running correctly on port 7443 (NOT 7334)
- "not our ref" error was from git.shakespeare.diy (stale data), NOT relay.ngit.dev
- Purgatory mechanism works correctly - tries multiple URLs, succeeds on fallback
- Real issue: State events not being synced via negentropy
- **Evidence:**
- storyforge-ai: State event received → purgatory → fetch failed from shakespeare → succeeded from relay.ngit.dev ✅
- 1-man-1-tank: State event NEVER received → never entered purgatory → empty git repo ❌
- No logs for 1-man-1-tank in archive (event never arrived)
- Archive has 500 announcements but only 336 state events (33% gap)
- **Purgatory Flow (Working):**
```
State event received → Added to purgatory → Sync triggered →
Try URL 1 (failed) → Try URL 2 (succeeded) → Git data fetched →
State event released from purgatory → Stored in database
```
- **Why 1-man-1-tank Failed:**
- State event created: Dec 17, 2025
- Archive started: Jan 20, 2026 (34 days later)
- Negentropy sync failed to transfer the state event
- Without state event, no purgatory entry, no git fetch
- **Sync Status:**
```
10:20:11 - WARN: Batch failed - ConnectedHistoricSyncFailures
10:20:17 - Historic sync complete with failures
```
- **Files Created:**
- `work/FETCH-FAILURE-DEEP-DIVE.md` - Comprehensive analysis report
- **Recommendations:**
1. Investigate negentropy sync failures (why state events not transferring)
2. Add more sync relays for redundancy
3. Implement state event re-fetch when announcement exists but state missing
4. Add monitoring for announcement/state event ratio
- **Migration Status:** ❌ **BLOCKED** - Must fix negentropy sync before proceeding
- **Next Steps:**
1. Check if there are filters preventing old state event sync
2. Restart archive to trigger fresh negentropy sync
3. Monitor logs for 1-man-1-tank state event arrival
4. Consider manual event injection for missing state events
### 2026-01-21 [Session 14:00] - Negentropy Fallback Analysis - SOLUTION IDENTIFIED ✅
- Completed: Architect review of negentropy fallback mechanism and sync failures
- **CRITICAL FINDING: Fallback IS working, but failed batches are NOT retried**
- **Negentropy Fallback Status:**
- ✅ Fallback to REQ+EOSE IS implemented correctly (`src/sync/mod.rs:3152-3512`)
- ✅ Archive detects ngit-relay doesn't support negentropy (NIP-77)
- ✅ Falls back to REQ+EOSE for current batch
- ❌ **Failed historic sync batches are NEVER retried** - this is the bug
- **Root Cause Confirmed:**
1. Archive started Jan 20, 2026 (many state events from Dec 2025)
2. Initial historic sync attempted negentropy against ngit-relay
3. Negentropy timed out (ngit-relay doesn't support NIP-77)
4. Batch marked as failed → status: `ConnectedHistoricSyncFailures`
5. REQ+EOSE fallback executed for current batch only
6. **Old state events from failed batches never retried** ❌
- **Why 500 Announcements vs 336 State Events:**
- Announcements: Synced via live subscription (new events after Jan 20)
- State events: Only received AFTER archive started
- Missing ~164 events: From before Jan 20, lost in failed historic sync
- **Sync Status:** COMPLETE (with failures) - not in progress
- `ConnectedHistoricSyncFailures` means historic sync finished but had failures
- Live sync active for new events
- No automatic retry for failed historic events
- **Solution: Disable Negentropy for Archive**
- Add `NGIT_SYNC_DISABLE_NEGENTROPY=true` to archive config
- Restart service to trigger fresh historic sync
- Forces REQ+EOSE for all historic sync (avoids timeout)
- Should sync all historic events successfully
- **Files Created:**
- `work/NEGENTROPY-FALLBACK-ANALYSIS.md` - Detailed code analysis and findings
- **Code References:**
- `src/sync/mod.rs:3152-3512` - Historic sync with fallback logic
- `src/sync/relay_connection.rs:508-584` - Negentropy failure detection
- `src/sync/mod.rs:1109-1113` - Batch failure handling (no retry)
- `src/config.rs:409-413` - `sync_disable_negentropy` config option
- **Migration Status:** ❌ **BLOCKED** - Must apply config fix and restart archive
- **Next Steps:**
1. Add `NGIT_SYNC_DISABLE_NEGENTROPY=true` to archive service config
2. Restart archive service to trigger fresh historic sync
3. Monitor sync progress (expect 500+ state events)
4. Verify state event count matches production (>95% coverage)
5. Proceed to Phase 3 validation once sync complete
### 2026-01-21 [Session 15:00] - Negentropy Fix Applied & Sync Query Investigation ⚠️
- Completed: Applied negentropy fix, verified working, but discovered sync query design issue
- **Negentropy Fix Applied Successfully:**
- ✅ Issue b4d0 created: "Failed historic sync batches not retried after negentropy timeout"
- ✅ Config updated: Added `syncDisableNegentropy = true` to archive service
- ✅ Deployed and restarted: Service running with REQ+EOSE sync
- ✅ Fix verified: Logs show `use_negentropy=false`, state events flowing
- ✅ State event count: 336 → 339 (increasing)
- ✅ Status: `Connected` (not `ConnectedHistoricSyncFailures`)
- **1-man-1-tank Investigation - CRITICAL FINDING:**
- ❌ State event still NOT received after negentropy fix
- ✅ Announcement (kind 30617) exists in archive
- ❌ State event (kind 30618) does NOT exist in archive
- ✅ State event exists on relay.ngit.dev and git.shakespeare.diy
- ❌ Git repo empty (no refs, no objects)
- **ROOT CAUSE: Sync Query Design Flaw**
- Announcements: Broad query `{"kinds": [10317, 30617]}` (no d-tag filter) ✅
- State events: Narrow query `{"kinds": [30618], "#d": [list of known repos]}` ❌
- **Problem:** State events only queried for repos archive already knows about
- **Chicken-and-egg:** 1-man-1-tank state event never synced → not in query list → never queried → never synced
- **Why This Happens:**
1. Archive receives announcement for 1-man-1-tank (broad query)
2. Archive creates empty git repo
3. Archive queries state events only for repos it already has state events for
4. 1-man-1-tank NOT in list → state event never queried
5. State event never arrives → git data never fetched → repo stays empty
- **Evidence from Logs:**
```
# Broad announcement query (works)
Sending '["REQ",...,{"kinds":[10317,30617]}]' to relay.ngit.dev
# Narrow state event query (misses 1-man-1-tank)
Sending '["REQ",...,{"#d":["BeatsBars","storyforge-ai",...],
"kinds":[30618]}]' to relay.ngit.dev
# Note: "1-man-1-tank" NOT in d-tag list
```
- **Impact Assessment:**
- Negentropy fix IS working (REQ+EOSE active, other repos syncing)
- But won't help repos whose state events were never queried
- Affects ALL repos where announcement arrived but state event didn't
- Explains persistent gap between announcement count and state event count
- **Recommended Fixes:**
1. **Option 1 (Best):** Add broad state event query `{"kinds": [30618]}` (no d-tag filter)
2. **Option 2:** Query state events for all known announcements (when announcement received)
3. **Option 3 (Immediate):** Manual re-sync by publishing state events to archive
- **Files Created:**
- `work/NEGENTROPY-FIX-VERIFICATION.md` - Verification of negentropy fix
- `work/1-MAN-1-TANK-SYNC-STATUS.md` - Detailed investigation of sync query issue
- **Migration Status:** ❌ **BLOCKED** - Sync query design prevents complete state event coverage
- **Next Steps:**
1. ~~Create issue for sync query design flaw~~ - Architecture is correct, not a design flaw
2. Investigate actual execution failure for 1-man-1-tank
3. Verify where the flow breaks (announcement → state event)
4. Fix identified issue
5. Achieve >95% state event coverage before proceeding to Phase 3
### 2026-01-21 [Session 16:00] - ACTUAL ROOT CAUSE: RepoSyncIndex Not Populated from Database 🎯
- Completed: Re-investigation with corrected understanding of sync architecture
- **Previous analysis was WRONG** - Sync query design is CORRECT, not flawed
- **Actual Architecture (Verified from Code):**
1. Layer 1 syncs announcements broadly: `{"kinds": [30617]}` (no d-tag filter)
2. Accepted announcements broadcast to local relay: `local_relay.notify_event(event)`
3. SelfSubscriber receives announcement, extracts identifier, queues for batch
4. Batch processing builds new filters including the NEW repo
5. State event filters created: `{"kinds": [30618], "#d": ["new-repo", ...]}`
6. AddFilters sent to SyncManager → state events synced
- **Architecture is CORRECT** - The flow is designed to sync state events for newly discovered repos
- **ACTUAL ROOT CAUSE: RepoSyncIndex Not Populated from Database**
- `RepoSyncIndex` is ONLY populated by SelfSubscriber receiving broadcasts
- `RepoSyncIndex` is NOT populated from database on startup or daily sync
- If broadcast is lost (channel overflow, timing), announcement in DB but NOT in RepoSyncIndex
- State event filter never created for that repo
- **Why This Happens:**
1. Archive starts, SelfSubscriber connects to local relay
2. SyncManager syncs 500+ announcements quickly
3. Each announcement broadcast via `notify_event()` (channel capacity: 1024)
4. If SelfSubscriber can't keep up, broadcasts overflow and are lost
5. Lost announcements never added to RepoSyncIndex
6. State event filters never created for those repos
- **Why Daily Sync Doesn't Fix It:**
- `fresh_start()` calls `recompute_new_sync_filters_for_relay()`
- Uses `derive_relay_targets(&repo_index)` - the RepoSyncIndex
- RepoSyncIndex STILL missing the announcement
- State event filter still never created
- **Evidence for 1-man-1-tank:**
- ✅ Announcement (30617) in database (verified via nak query)
- ❌ State event (30618) NOT in database
- ❌ Likely NOT in RepoSyncIndex (broadcast lost during initial sync)
- ❌ State event filter never created
- ❌ State event never synced
- **Code References:**
- `src/sync/mod.rs:2256-2257` - Uses RepoSyncIndex for filter creation
- `src/sync/mod.rs:2575-2577` - Broadcast to local relay
- `src/sync/self_subscriber.rs:155-169` - SelfSubscriber receives broadcast
- `src/sync/self_subscriber.rs:503-506` - Batch processing builds filters
- Broadcast channel capacity: 1024 (can overflow during initial sync)
- **Recommended Fixes:**
1. **Option 1 (Best):** Populate RepoSyncIndex from database on startup
2. **Option 2:** Query database in `recompute_new_sync_filters_for_relay()`
3. **Option 3:** Persist RepoSyncIndex to disk
4. **Option 4:** Increase broadcast channel capacity (doesn't fully solve issue)
- **Files Created:**
- `work/1-MAN-1-TANK-FLOW-ANALYSIS.md` - Comprehensive flow analysis and root cause
- **Migration Status:** ❌ **BLOCKED** - RepoSyncIndex bug prevents complete state event coverage
- **Next Steps:**
1. Create issue for RepoSyncIndex database population bug
2. Implement Option 1 (populate from database on startup)
3. Restart archive service to trigger database population
4. Verify 1-man-1-tank state event syncs successfully
5. Achieve >95% state event coverage before proceeding to Phase 3
### 2026-01-21 [Session 17:00] - Validation Strategy Designed 📋
- Completed: Comprehensive validation strategy for RepoSyncIndex hypothesis
- **Validation Approach: 3-Phase Strategy**
- **Phase 1 (Recommended First):** Diagnostic logging with `[DIAG:*]` prefixes
- 7 strategic log points tracking database → broadcast → SelfSubscriber → RepoSyncIndex → filters
- Minimal code changes, can deploy immediately
- Provides direct evidence of where broadcasts are lost
- **Phase 2:** Debug HTTP endpoints for real-time inspection
- `/debug/sync-gap` - Compare database vs RepoSyncIndex (JSON)
- `/debug/repo-sync-index/{repo_id}` - Check specific repo (e.g., 1-man-1-tank)
- Allows validating fix effectiveness
- **Phase 3:** Metrics for production monitoring
- `ngit_sync_repos_in_index_total` - Current RepoSyncIndex size
- `ngit_sync_gap_total` - Gap between database and index
- `ngit_sync_broadcast_overflow_total` - Failed broadcasts
- **Validation Steps:**
1. Baseline: Query database for 1-man-1-tank (exists) and state event (missing)
2. Deploy Phase 1 logging
3. Collect logs for 1-2 hours
4. Analyze: Count events at each stage (`DIAG:DB` vs `DIAG:SELFSUBSCRIBER`)
5. Confirm hypothesis: If DB count > SelfSubscriber count, broadcasts being lost
- **Success Criteria:**
- Database has N announcements, RepoSyncIndex has M < N (gap exists)
- 1-man-1-tank in database but NOT in RepoSyncIndex
- `DIAG:BROADCAST.*FAILED` messages in logs
- After fix (populate from DB), gap becomes 0 and state events sync
- **Timeline Estimate:**
- Phase 1 only: 1 day (minimum viable validation)
- Full validation + fix: 3-4 days
- **Files Created:**
- `work/REPOSYNCINDEX-VALIDATION-STRATEGY.md` - 904-line comprehensive strategy document
- **Alternative Hypotheses (if disproved):**
1. State event filters created but queries fail
2. Remote relay doesn't have state events
3. Write policy rejecting state events
4. Purgatory blocking state events
- **Risk Assessment:**
- Performance impact: Minimal (debug/trace level logs)
- Operational risk: Low (appropriate log levels, gated endpoints)
- Mitigation: Use structured logging, rate limiting, feature flags
- **Next Steps:**
1. Decide: Validate hypothesis first OR implement fix directly
2. If validating: Implement Phase 1 logging, collect data, analyze
3. If fixing directly: Implement database population on startup
4. Either way: Create issue for RepoSyncIndex bug
### 2026-01-21 [Session 18:00] - Migrating to Selective Archive Service ✅
- Completed: Migration from archive-all to selective relay.ngit.dev archive
- Context: grasp-audit package updated with `archiveGraspServices` option
- **Configuration changes made:**
- Removed: `ngit-grasp-archive-all.nix` (archived all relays)
- Added: `ngit-grasp-relay-ngit-dev.nix` (archives only relay.ngit.dev)
- Updated: `services/default.nix` to import new service file
- New data directory: `/persistent/grasp/relay-ngit-dev` (was `/persistent/grasp/sync-archive`)
- New domain: `archive-relay-ngit-dev.internal` (was `archive.internal`)
- Selective archiving: `archiveGraspServices = [ "relay.ngit.dev" ]`
- **Migration steps executed:**
1. ✅ Stopped old archive-all service
2. ✅ Deleted old archive-all data directory (freed 5.8GB)
3. ✅ Updated NixOS configuration and deployed
4. ✅ Created data directory manually (workaround for namespace issue)
5. ✅ Verified new service started successfully
- **Verification results:**
- Service status: ✅ Running (active since 16:09:47 CET)
- Archive mode: ✅ Confirmed (`archive_all=false`, read_only=true)
- Syncing from: ✅ `wss://relay.ngit.dev`
- Data archived: ✅ 779MB (and growing)
- Git fetches: ✅ Active (fetching repos from relay.ngit.dev)
- Service domain: ✅ `archive-relay-ngit-dev.internal`
- Port: ✅ 7443 (localhost only)
- **Disk space:**
- Before: 26GB used, 21GB available
- After cleanup: 20GB used, 27GB available (5.8GB freed)
- Current: 20.8GB used, 26.2GB available (779MB new data)
- **Issues encountered:**
- Mount namespace error on first deployment (data directory didn't exist)
- Resolution: Created directory manually, service started successfully
- Note: This is a known issue (b454) - module should create directories in ExecStartPre
- **Migration status:** ✅ **COMPLETE** - Selective archive service operational
### 2026-01-20 [Session 14:30] - Naughty List Blocker Identified
- Completed: Extensive state validation and root cause analysis
- **Blocker identified:** relay.ngit.dev on git "naughty list" preventing archive sync
- **Current metrics:**
- State events: 318/580 (55% of target, need >99%)
- Repos with git data: 297 (14% of total)
- Repos in purgatory: 1,803 (85% of total repos)
- Coverage: Only 55% (BLOCKED from proceeding)
- **Root cause analysis:**
- Permission issues FIXED ✅ (18 repos, all HTTP 200)
- Purgatory mechanism understood ✅ (30-min expiry, no 7-day block)
- Service restarted and purgatory cleared ✅
- BUT: Archive can't fetch from relay.ngit.dev (on naughty list)
- **Naughty list evidence:**
- Logs show: "Domain is on git naughty list - skipping identifier=X domain=relay.ngit.dev"
- BOTH relay.ngit.dev AND git.shakespeare.diy blocked
- Archive blocking its own bootstrap relay (likely bug)
- **Impact:**
- Many repos only have relay.ngit.dev clone URLs
- Archive can't fetch from blocked domain
- Repos stuck in purgatory without git data
- Can't serve state events without git data
- **Files created:**
- 7 analysis reports in `work/state-validation/reports/`
- 9 data files in `work/state-validation/data/`
- 3 metrics snapshots in `work/state-validation/metrics/`
- `work/NEXT-SESSION-HANDOFF.md` - Comprehensive handoff document
- `work/QUICK-REFERENCE.md` - Quick commands reference
- `work/FILE-MANIFEST.md` - File index with descriptions
- **Next session tasks:**
1. Find naughty list implementation in ngit-grasp codebase
2. Understand why relay.ngit.dev was added (appears to be bug)
3. Remove relay.ngit.dev from naughty list
4. Wait 1-2 hours for archive to fetch from unblocked domain
5. Reassess coverage (target: >580 state events)
- **Migration Status:** ❌ **BLOCKED** - Must resolve naughty list before Phase 3
### 2026-01-20 [Session 17:00] - Nostr Event Analysis Complete & Migration Guide Created
- Completed: Comprehensive Nostr event analysis comparing relay.ngit.dev and localhost archive
- Key Findings:
- **99.86% sync accuracy** between relay.ngit.dev (690 events) and localhost:7334 (2,157 events)
- Only **1 missing repository**: einundzwanzighh-gesundesgeld (created 2025-01-19)
- localhost:7334 is authoritative source with 2-year history vs relay.ngit.dev's 8-month subset
- boldwallet.git confirmed on localhost:7334 ✅, missing from relay.ngit.dev ❌
- Created: Comprehensive migration guide at `docs/how-to/ngit-relay-migration-guide.md`
- Documented two-relay architecture discovery
- Included helper scripts for querying, comparing, and validating relays
- Detailed 5-phase migration plan with validation steps
- Troubleshooting section for common issues
- Organized: Copied analysis files to `work/analysis/` for reference
- CORRECTED-ANALYSIS.md - Full event count comparison
- MISSING-ANALYSIS.md - Missing repository analysis
- missing-announcements.jsonl - Event ready for import
- missing-announcements-summary.txt - Human-readable summary
- Updated: Issue file reorganized to reference migration guide instead of duplicating content
- Validation section now summarizes findings with link to detailed analysis
- Plan section references migration guide for implementation details
- Maintains issue focus on WHAT/WHY, guide documents HOW
- Decision: Sync from ws://localhost:7334 for complete coverage (1,244 repos vs 602)
- Next: Continue with Phase 2 validation using helper scripts from migration guide
### 2026-01-19 [Session 15:00] - REVISED MIGRATION PLAN: Archive-Based Approach ✅
- **MAJOR PIVOT:** Discovered critical data integrity issue that changes migration approach
- **Problem Identified:**
- ngit-grasp only serves state events if ALL referenced git data is present
- ngit-relay serves state events regardless of missing git data
- Risk: Repos with missing data would lose main/master branch visibility after migration
- ngit-grasp fetches git data from clone URLs, but filters out its own domain
- **Solution: Archive-Based Migration**
- Deploy archive instance on VPS that syncs from relay.ngit.dev
- Archive uses different domain (archive.internal) so doesn't filter relay.ngit.dev URLs
- Archive fetches all git data from relay.ngit.dev during sync
- Test instance syncs from archive to validate before production cutover
- **Disk Space Assessment:**
- VPS has 49GB total, 21GB available (56% used)
- Current ngit services: 17.2GB (relay.ngit.dev: 7.5GB, gitnostr.com: 6.7GB, ngit.danconwaydev.com: 77MB)
- Old ngit-relay data: 2.9GB (deleted to free space)
- Estimated archive size: 10-12GB (with doxbox blacklist)
- After cleanup: 24GB available - sufficient for archive approach ✅
- **Archive Service Deployed:**
- Service: `ngit-grasp-relay-ngit-archive` (running on VPS)
- Domain: `archive.internal`
- Mode: `archiveAll = true` with `repositoryBlacklist = ["doxbox"]`
- Sync from: `wss://relay.ngit.dev`
- Port: `7443` (localhost only)
- Data dir: `/persistent/grasp/sync-archive`
- Status: ✅ **ACTIVE and syncing** (330MB synced as of 17:30 CET)
- **Issues Created/Fixed:**
- Issue b454: NixOS module should auto-create data directories in ExecStartPre
- Temporary fix: Added tmpfiles rules to create directories before service starts
- Permanent fix: Will be implemented in ngit-grasp module (tracked in b454)
- **Backup Created:**
- Full backup of relay.ngit.dev data (7.5GB) created on VPS
- Git repos: 7.1GB compressed
- Relay DB: 18MB compressed
- User downloading backup to laptop (slow connection, ~2 hours)
- **Updated Migration Plan:**
1. ✅ **Phase 1: Archive Sync** (IN PROGRESS)
- Archive service deployed and syncing from relay.ngit.dev
- Estimated completion: 10-12GB sync (currently 330MB)
- Monitor sync progress and final size
2. **Phase 2: Validation** (NEXT)
- Once archive sync complete, assess actual disk usage
- Verify all repos synced successfully
- Check for repos stuck in purgatory (missing git data)
- Compare repo list: archive vs relay.ngit.dev
3. **Phase 3: Test Instance** (PENDING)
- Deploy test instance on laptop (not VPS - saves 15GB)
- Test instance syncs from VPS archive
- Validate all repos accessible
- Verify no repos stuck in purgatory
4. **Phase 4: Production Cutover** (PENDING)
- Only after test instance validation passes
- Stop ngit-relay, deploy ngit-grasp
- Point to VPS archive or direct sync
- Monitor intensively for 48 hours
- **Key Decisions:**
- Test locally (laptop) instead of VPS to save disk space
- Use doxbox blacklist to reduce archive size
- Blank start archive on VPS (no upload needed - better bandwidth)
- Archive fetches git data from relay.ngit.dev during sync
- **Timeline Revised:**
- Archive sync: 1-2 days (depends on bandwidth and repo count)
- Validation: 4-6 hours
- Test instance: 1 day
- Production cutover: 2-4 hours
- Post-migration monitoring: 48 hours
- **Total: 3-5 days** (from archive sync complete)
- **Next Steps:**
1. Monitor archive sync progress: `ssh vps1 'du -sh /persistent/grasp/sync-archive'`
2. Wait for archive sync to complete (10-12GB target)
3. Validate archive completeness (repo count, git refs)
4. Assess if enough disk space for test instance on VPS or test locally
5. Proceed with Phase 2 validation
- **Status:** Archive syncing ✅, Migration approach validated ✅, Waiting for sync completion ⏳
## Validation Results
### Analysis Summary (2026-01-20)
Comprehensive Nostr event analysis completed comparing relay.ngit.dev and localhost archive:
**Event Counts:**
- **ws://localhost:7334** (archive relay): 2,157 events, 1,244 unique repos
- **relay.ngit.dev**: 690 events, 602 unique repos
**Sync Accuracy: 99.86%**
- Missing from localhost: 1 event (einundzwanzighh-gesundesgeld)
- Outdated events: 0
- Shared repos: 548
**Key Findings:**
- localhost:7334 is the **authoritative source** with complete history (Jan 2024 - Jan 2026)
- relay.ngit.dev is a filtered subset (May 2025 - Jan 2026)
- boldwallet.git found on localhost:7334 ✅, NOT on relay.ngit.dev ❌
- 696 repos exist ONLY on localhost (including boldwallet)
- 54 repos exist ONLY on relay.ngit.dev (newer additions)
**Recommendation:** Sync from ws://localhost:7334 for complete coverage.
**Detailed Analysis:** See [work/analysis/](../../work/analysis/) for complete event analysis and missing repository details.
**Migration Guide:** See [ngit-relay Migration Guide](../../docs/how-to/ngit-relay-migration-guide.md) for detailed validation steps, helper scripts, and troubleshooting procedures.
## Notes
- **Related issue:** 573b-production-timeout-diagnosis.md (timeout investigation that led to this)
- **Related issue:** 1ce6-purgatory-retry-mechanism.md (purgatory retry failures discovered during this migration)
- **Critical requirement:** Must collect baseline BEFORE migration for objective comparison
- **Success criteria:** Performance equal or better than ngit-relay baseline
- No increase in CPU/memory usage
- No new timeout patterns
- Connection handling remains stable
- Git operations perform equally well
- **Rollback plan:** Keep ngit-relay container for 2 weeks post-migration
- Can switch back immediately if issues arise
- Only remove after validation period passes
- **nginx metrics:** Implementation ready in ngit-relay repo
- Provides connection counts, request rates, upstream health
- Essential for before/after comparison
- **Current status:**
- relay.ngit.dev: Running ngit-relay v0.0.5 with 2000 conn/IP rate limit
- ngit.danconwaydev.com: Running ngit-grasp with 4096 max connections
- Both services stable after Phase 1 improvements
- **Migration benefits:**
- Single codebase to maintain and improve
- Can focus logging/observability work on ngit-grasp
- Proven performance (ngit.danconwaydev.com is stable)
- Simplified deployment and configuration
### Accelerated Migration: Configuration Template
**ngit-grasp service config for relay.ngit.dev:**
```nix
# services/ngit-grasp.nix
{ inputs, ... }:
{
imports = [ inputs.ngit-grasp.nixosModules.default ];
services.ngit-grasp.relay-ngit-dev = {
enable = true;
domain = "relay.ngit.dev";
# Network - bind to localhost, nginx handles TLS
bindAddress = "127.0.0.1";
port = 8082; # Or whatever port ngit-relay was using
# Storage - reuse existing data directory if possible
dataDir = "/persistent/ngit-relay"; # Or new dir: /persistent/ngit-grasp
# Identity - copy from ngit-relay config
relayName = "relay.ngit.dev";
relayDescription = "GRASP relay for git+nostr";
relayOwnerNsecFile = "/persistent/ngit-relay/relay-owner.nsec";
# Sync - bootstrap from ngit.danconwaydev.com
syncBootstrapRelayUrl = "wss://ngit.danconwaydev.com";
# Metrics - enable for monitoring
metricsEnabled = true;
# Logging - start with info, increase to debug if issues
logLevel = "info";
};
# Nginx/Caddy reverse proxy (existing config, just update upstream)
# Change upstream from ngit-relay container to 127.0.0.1:8082
}
```
**Key decisions:**
1. **Data directory:** Reuse `/persistent/ngit-relay` if compatible, or create new `/persistent/ngit-grasp`
2. **Port:** Use same port as ngit-relay container was using (check nginx config)
3. **Bootstrap relay:** Use ngit.danconwaydev.com (known good ngit-grasp instance)
4. **Metrics:** Enable from day 1 for comparison
### Accelerated Migration: Risk Assessment
**Risks with fast migration:**
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Insufficient baseline data | Medium | Medium | Collect 48-72h minimum; focus on peak hours |
| Performance regression | Low | High | ngit-grasp proven on ngit.danconwaydev.com; instant rollback available |
| Data compatibility issues | Low | Medium | Both use same event format; test with sample data first |
| Configuration errors | Medium | Low | Pre-validate config; test build before migration |
| User disruption | Low | Medium | Migrate during off-peak; monitor intensively first 48h |
| Rollback complications | Low | High | Keep ngit-relay container; document exact rollback steps |
**Why acceptable to move fast:**
1. ✅ **Proven implementation:** ngit-grasp runs ngit.danconwaydev.com successfully
2. ✅ **Easy rollback:** Container-based deployment allows 5-min rollback
3. ✅ **Low user impact:** relay.ngit.dev is not mission-critical (dev/test relay)
4. ✅ **Stable baseline:** ngit-relay v0.0.5 is stable after timeout fixes
5. ✅ **Intensive monitoring:** 48h of close monitoring catches issues early
**What we're trading off:**
- ❌ Detailed performance comparison (1-2 weeks baseline → 48-72h baseline)
- ❌ Comprehensive testing (full test suite → basic functionality tests)
- ❌ Gradual rollout (immediate cutover → no canary deployment)
**Acceptable because:**
- This is a development/test relay, not production-critical
- We have a proven implementation (ngit.danconwaydev.com)
- Rollback is trivial (restart container)
- Benefits (consolidated codebase) outweigh risks
### Accelerated Migration: Monitoring Checklist
**Pre-migration baseline (48-72h):**
```bash
# Capture these metrics before migration
echo "=== CPU Baseline ===" > baseline.txt
top -b -n 1 | grep ngit-relay >> baseline.txt
echo "=== Memory Baseline ===" >> baseline.txt
docker stats ngit-relay --no-stream >> baseline.txt
echo "=== Connection Baseline ===" >> baseline.txt
ss -s >> baseline.txt
echo "=== Error Baseline ===" >> baseline.txt
journalctl -u ngit-relay --since "24 hours ago" | grep -i error | wc -l >> baseline.txt
```
**Post-migration monitoring (48h intensive):**
```bash
# Run every 15 min (hour 1), hourly (hours 2-6), then decreasing frequency
# 1. Service health
systemctl status ngit-grasp-relay-ngit-dev
# 2. Resource usage
top -b -n 1 | grep ngit-grasp
# 3. Connection count
ss -s
# 4. Error check
journalctl -u ngit-grasp-relay-ngit-dev --since "15 minutes ago" | grep -i error
# 5. Metrics endpoint
curl http://localhost:8082/metrics | grep -E "ngit_(connections|events|git)"
# 6. Functional test
# WebSocket: Use websocat or browser
# Git: git ls-remote https://relay.ngit.dev/<npub>/<repo>.git
```
**Rollback trigger conditions:**
- 🚨 Service crashes or fails to restart
- 🚨 Error rate >10x baseline
- 🚨 Memory usage >4x baseline (sustained >30 min)
- 🚨 CPU usage >90% sustained >15 min
- 🚨 Git operations failing
- 🚨 WebSocket connections rejected
**Success indicators:**
- ✅ Service uptime >48h continuous
- ✅ Error rate ≤ baseline
- ✅ Resource usage ≤ 2x baseline
- ✅ All functional tests passing
- ✅ No user complaints