upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-01-07Add SyncContext trait and MockSyncContext for purgatory syncDanConwayDev
Implement the abstraction layer for purgatory sync operations: - SyncContext trait: defines interface for repository data fetching, OID existence checks, git fetch operations, and event processing - ProcessResult: captures outcomes when releasing events from purgatory - MockSyncContext: test mock with builder pattern for configuring: - Clone URLs and which OIDs each URL provides - Needed OIDs (simulates purgatory state) - URL failure simulation - Fetch logging for assertions The trait uses async_trait for async method support and requires Send + Sync for use in concurrent sync operations. This abstraction enables unit testing of sync logic without I/O, while the real implementation (to be added later) will connect to actual database, git, and relay systems.
2026-01-07Add ThrottleManager for cross-domain rate limitingDanConwayDev
Implements ThrottleManager which manages all per-domain DomainThrottle instances and provides: - Throttle status checking via is_throttled() for sync URL selection - Request tracking via start_request()/complete_request() - Identifier queue management via enqueue_identifier() - Automatic domain throttle creation on first access - Thread-safe access via DashMap with Mutex-wrapped throttles The manager uses the configured max_concurrent and max_per_minute limits for all domains. Trigger-based queue processing (set_context, process_queued_identifier) will be added after SyncContext is available. Tests verify: - is_throttled reflects domain capacity correctly - enqueue_identifier creates domain throttle if needed - start_request creates domain throttle if needed
2026-01-07Add DomainThrottle for per-domain rate limitingDanConwayDev
Implement per-domain throttling for purgatory sync operations: - Concurrent request limit (max in-flight requests per domain) - Rate limit (max requests per minute via sliding window) - Fair round-robin queue processing across identifiers - In-progress tracking to prevent duplicate fetches - Tried URL tracking per identifier Add indexmap dependency for ordered iteration in round-robin queue. Includes 6 unit tests covering: - Concurrent limit enforcement - Rate limit enforcement (sliding window) - Round-robin fair processing - In-progress identifier skipping - Round-robin index adjustment on removal - Tried URL merging on re-enqueue
2026-01-07Add SyncQueueEntry with exponential backoff for purgatory syncDanConwayDev
Implement the sync queue entry struct that tracks sync state per identifier: - next_attempt: when the next sync should be attempted - attempt_count: for backoff calculation (resets on new events) - in_progress: prevents concurrent syncs for same identifier Backoff schedule: 20s → 40s → 80s → 120s (capped at 2 minutes) This is the foundation for the identifier-based purgatory sync system that will replace the current per-event syncing approach.
2026-01-07docs: purgatory tweak implementation planDanConwayDev
2026-01-07docs: purgatory update implemenation to to reflect ↵DanConwayDev
process_newly_available_git_data
2026-01-07docs: purgatory add unified process_newly_available_git_data to designDanConwayDev
2026-01-07docs: purgatory sync implemeantion planDanConwayDev
2026-01-07docs: purgatory design improve testing strategyDanConwayDev
2026-01-07docs: purgatory design improve process_satisfiable_eventsDanConwayDev
2026-01-06docs: purgatory design improvementsDanConwayDev
2026-01-06docs: purgatory git sync design with throttle queuingDanConwayDev
2026-01-06docs: purgatory git sync design simplifyDanConwayDev
2026-01-06docs: purgatory git sync designDanConwayDev
2026-01-05sync PR refs to all relivant reposDanConwayDev
2026-01-05sync PR refs (refs/nostr/<event-id>) to all owner repos when push receivedDanConwayDev
When a push to refs/nostr/<event-id> is received (PR data), the git data is now synced to all other owner repositories that share maintainers with the source owner. This mirrors the behavior added for state event data. Changes: - Add sync_pr_refs_to_owner_repos() function in git/sync.rs - Add PrSyncResult struct to track sync statistics - Add copy_single_commit_between_repos() helper function - Call PR sync in handle_receive_pack after successful push - Add unit test for PrSyncResult default values
2026-01-05sync all repos when authorised state data push receivedDanConwayDev
2026-01-05purgatory: git data sync applies state and saves eventDanConwayDev
2026-01-05purgatory: state git data sync use single command to fetch oidsDanConwayDev
2026-01-05purgatory: add state git data syncDanConwayDev
2026-01-02sync: use purgatoryDanConwayDev
don't save new events destined for purgatory events directly to db or serve on websockets don't download events already in purgatory via negentropy sync
2026-01-02test: fix issue kind numberDanConwayDev
2026-01-02docs: remove purgatory implementation planDanConwayDev
2025-12-31test: remove local sync on state git data received testDanConwayDev
because the current fixtures don't actually having mutliple owner_repos. they would need 2 announcements that both listed the service and a maintainer relationship. We could do this in grasp-audit but it would require an extra announcement from a different maintainer sent eariler on in the dependancy chain.
2025-12-31purgatory: when state data recieved sync across repositoiesDanConwayDev
2025-12-31test: fix ↵DanConwayDev
test_push_to_nostr_ref_with_correct_commit_after_event_received_accepted_and_event_served
2025-12-31test: fix test_head_set_after_git_push_with_required_oidsDanConwayDev
2025-12-31test: fix purgatory not-served check outcomeDanConwayDev
2025-12-31purgatory: fix pr event recieve codeDanConwayDev
2025-12-31purgatory: fix state event receive codeDanConwayDev
2025-12-30purgatory: improve git authorization integetrationDanConwayDev
2025-12-24feat(purgatory): add broken purgatory implementationDanConwayDev
2025-12-23test: add purgatory to PR push testDanConwayDev
commented out so it currently passes
2025-12-23test: add stub for purgatory maintainer repo sync testDanConwayDev
2025-12-23test: prepare other state tests for purgatory featureDanConwayDev
2025-12-23test: prepare OwnerStateDataPushed fixture for purgatoryDanConwayDev
This is the model for how to prepare all push tests for purgatory
2025-12-23test: remove test covered elsewhereDanConwayDev
as new feature purgatory is going to complicate having this test here. it will be better to have this covered in push authorisation
2025-12-23audit: updated grasp01 line refs to reflect purgatory insertionDanConwayDev
2025-12-23docs: purgatory designDanConwayDev
2025-12-22chore: cargo fmt and clippyDanConwayDev
2025-12-22chore: bump rust-nostr to latest masterDanConwayDev
so we can more easily support grasp purgatory feature
2025-12-22accept all UserGraspList for better discoveryDanConwayDev
2025-12-22docs: proactive sync hand written overview rewrite and AI update of restDanConwayDev
2025-12-22fix: sync consoldate subscription countDanConwayDev
2025-12-22sync: add req rate-limit detection and cooldownDanConwayDev
2025-12-19docs: sync updates to reflect changesDanConwayDev
2025-12-19docs: sync add pagination updatesDanConwayDev
2025-12-19test: sync add test for pagination of historic that requires manually ↵DanConwayDev
setting lower thresholds
2025-12-19feat(sync): implement pagination for historic_sync REQ+EOSE flowDanConwayDev
Add automatic pagination support for non-Negentropy historic sync to handle large result sets efficiently. When a subscription receives >= 75 events, the system automatically fetches the next page using the 'until' parameter. Changes: - Add PaginationState struct to track event counts and min timestamps - Add pagination_state HashMap to PendingBatch for per-subscription tracking - Add PAGINATION_THRESHOLD constant (75 events) - Pass pending_sync_index to event processor for state updates - Track events and timestamps as they arrive - Check threshold on EOSE and launch follow-up subscriptions - Initialize pagination state when creating historic sync subscriptions - Update test fixtures in algorithms.rs The pagination continues recursively until a page returns fewer than 75 events, ensuring complete historic data retrieval without overwhelming relay limits.
2025-12-19docs: cleanupDanConwayDev
2025-12-19Simplify sync metrics to track only newly saved eventsDanConwayDev
Replace broken event counting that occurred before duplicate/policy checks with accurate tracking of events that are new, accepted, and saved. Changes: - Added ProcessResult enum to track event processing outcomes - Modified process_event_static() to return ProcessResult - Replaced events_total (with source labels) with events_synced_total - Removed gap_events_total and event_source module - Removed eose_received flag (EOSE is per-subscription, not suitable) - Updated all tests to use new simplified API The new ngit_sync_events_synced_total metric only counts events that: 1. Are new (not duplicates) 2. Pass write policy validation 3. Are successfully saved to database All 165 tests pass (124 lib + 41 integration)
2025-12-19sync: fix autoclose on EOSE for historic filtersDanConwayDev
2025-12-19refactor: rename connect_and_subscribe to connectDanConwayDev
Separated connection from subscription logic. The RelayConnection.connect() method now only handles WebSocket connection establishment. Subscriptions are managed separately via handle_connect_or_reconnect. Changes: - Renamed RelayConnection::connect_and_subscribe() to connect() - Removed subscription logic from connect method - Updated call site in try_connect_relay() - Removed unused build_announcement_filter import
2025-12-19Fix: Capture old_last_connected before updating stateDanConwayDev
Bug: handle_connect_or_reconnect() was incorrectly calling quick_reconnect() on first connections instead of fresh_start(). Root cause: The code updated last_connected = Some(now) at line 808, then immediately read it back at line 932 to make the reconnection decision. This meant first connections saw elapsed = now - now = 0 seconds, which triggered quick_reconnect() instead of fresh_start(). Fix: Capture old_last_connected BEFORE updating the state, then use that value for the reconnection decision. Now first connections correctly see None and call fresh_start(). Impact: - First connections now properly use fresh_start() with full historic sync - Short disconnections (< 15 min) use quick_reconnect() with since filter - Long disconnections (> 15 min) use fresh_start() with full resync All 41 sync tests passing.
2025-12-19fix: prevent CLOSED messages from terminating relay connectionsDanConwayDev
The system was incorrectly treating subscription-specific CLOSED messages as connection-wide disconnects, causing live subscriptions to be terminated immediately after historic_sync completed. Two bugs fixed: 1. relay_connection.rs: Removed break on RelayMessage::Closed - it's subscription-specific, not connection-wide 2. mod.rs: Removed disconnect handling for RelayEvent::Closed - only log at DEBUG level and continue All 41 sync tests now pass including previously failing live sync tests.
2025-12-19sync: negentropy fixesDanConwayDev
2025-12-19test: fix test_multi_source_aggregate_countsDanConwayDev
2025-12-19test: fix test_live_sync_event_countDanConwayDev
2025-12-19test: clear names for sync testsDanConwayDev
2025-12-18docs: archive sync test refactoring options documentDanConwayDev
2025-12-18docs: document sync test refactoring findings and patternsDanConwayDev
- Add comprehensive test pattern guidance to tests/sync/mod.rs - Explain when to use run_sync_test() vs manual setup - Document helper scope and architectural limitations Key findings: - historic_sync.rs: 4 tests refactored, 143 lines removed (50% reduction) - live_sync/discovery/tag_variations: Manual setup required due to architectural incompatibilities (timing, multi-relay, assertions) - Helper works for batch historic verification, not real-time scenarios Detailed summary available in work/sync-test-refactor-summary.md
2025-12-18refactor: migrate historic_sync.rs tests to use run_sync_test() helperDanConwayDev
- Refactored all 4 tests in historic_sync.rs to use run_sync_test() - Tests maintain same logic and assertions, only setup simplified - Moved run_sync_test() and SyncTestResult outside #[cfg(test)] module - Updated validation to allow empty event slices (for announcement-only tests) - All 4 historic_sync tests passing (test_bootstrap_syncs_existing_layer2_events, test_relay_replays_events_after_restart, test_announcement_not_listing_relay_is_not_synced, test_history_sync_without_negentropy) - Result: 39/40 tests passing (1 more than Phase 1 baseline of 38/40)
2025-12-18refactor: rename bootstrap.rs to historic_sync.rs for clarityDanConwayDev
2025-12-18test: add unified run_sync_test() helper infrastructureDanConwayDev
Add SyncTestResult struct and run_sync_test() helper function to sync_helpers.rs for unified test setup. The helper automatically determines sync mode (historic vs live) based on which event slice has content. Features: - SyncTestResult: holds test fixtures (relays, keys, repo_coord) - run_sync_test(): unified setup for both historic and live sync tests - Panic guards for invalid usage (both slices or neither) - Unit tests for panic conditions Test results: 40 tests total, 38 passing (same as baseline) - 2 pre-existing metric test failures (unchanged from baseline) - All new panic condition tests passing - No regressions introduced
2025-12-18docs: establish sync test baseline before refactoringDanConwayDev
2025-12-18sync: turn off negentropy and fix some testsDanConwayDev
2025-12-18sync: fix sync connectionDanConwayDev
2025-12-18sync: new connection logicDanConwayDev
2025-12-18sync removing dead codeDanConwayDev
2025-12-16proactive sync prep - some helper functions written but not enabledDanConwayDev
2025-12-12docs: fix sync SyncMethod in architecture docDanConwayDev
2025-12-12fix: remove misleading fallback claim from negentropy sync error logDanConwayDev
The log message claimed 'will fall back to REQ+EOSE' but no such fallback was implemented - the function simply returns 0 and exits.
2025-12-12fix: unify sync state tracking for negentropy and REQ+EOSE pathsDanConwayDev
When negentropy (NIP-77) sync was enabled, the RelaySyncIndex was never updated to reflect historical sync completion. This caused the three-way diff algorithm in compute_actions() to malfunction, leading to: - Repeated sync attempts for the same items - Incorrect filter counting for consolidation - Potential premature relay disconnection This fix unifies both sync paths (REQ+EOSE and Negentropy) through a consistent PendingBatch flow: 1. Added SyncMethod enum to distinguish between sync types 2. Updated PendingBatch struct to include sync_method field 3. Extracted confirm_batch() method for unified batch confirmation 4. Modified negentropy_sync_and_process() to: - Create a PendingBatch before sync - Add batch to pending_sync_index - On success: Remove batch and call confirm_batch() - On failure: Remove batch without confirming The confirm_batch() method moves repos and root_events from the batch to the RelayState.repos and RelayState.root_events, ensuring the three-way diff works correctly regardless of sync method. Closes: negentropy-sync-state-tracking.md
2025-12-11sync: remove reply kind from sync filters for root eventsDanConwayDev
they are legacy and not root events
2025-12-11docs: relay-limitsDanConwayDev
2025-12-11fix: resolve all fmt and clippy warningsDanConwayDev
Main lib (src/): - Add #[allow(dead_code)] for build_info field (stored to prevent Prometheus unregistration) - Add #[allow(dead_code)] for first_seen field (reserved for future rate limiting) - Replace .or_insert_with(RelaySyncNeeds::default) with .or_default() - Replace manual div_ceil implementations with .div_ceil(100) Test code (tests/): - Replace .expect(&format!(...)) with .unwrap_or_else(|_| panic!(...)) - Remove needless borrows in fetch_metrics() calls - Add #[allow(dead_code)] and #[allow(unused_imports)] to test helpers module grasp-audit: - Apply cargo fmt to fix formatting
2025-12-11sync: test sync works without negentropy and add disable option in syncDanConwayDev
2025-12-11feat: implement NIP-77 negentropy sync for historical dataDanConwayDev
Replace EOSE-based sync completion with negentropy reconciliation for: - Initial connect (fresh sync) - Daily sync (Layer 1 announcements) - Stale reconnect (>15 min) Key changes: - Add NegentropySyncResult struct with remote_only, local_only, received fields - Add supports_negentropy() using try-and-fallback approach - Add negentropy_sync_filter() using nostr-sdk client.sync() API - Modify handle_connect_or_reconnect() to use negentropy for fresh/stale sync - Modify daily_sync() to use negentropy for Layer 1 - Single-warning logging per relay when negentropy fails Quick reconnects (<15 min) unchanged - still use REQ with since filter. If negentropy unsupported, gracefully falls back to REQ+EOSE flow.
2025-12-11test: nip77 smoke testDanConwayDev
2025-12-11docs(tests): replace catchup.rs test with documentationDanConwayDev
The catchup sync mechanism (reconnection with since filter) is implemented in src/sync/mod.rs handle_connect_or_reconnect(), but cannot be reliably integration tested with current infrastructure: - TestRelay uses in-memory database (events lost on stop) - No way to force WebSocket disconnection without stopping relay - Stopping syncing relay creates new instance (fresh sync, not catchup) Convert the skeleton test file to comprehensive documentation explaining: - How catchup sync works (since filter on reconnect) - The 15-minute quick reconnect window logic - Why integration testing is not feasible - Alternative approaches that could enable testing - Related tests that cover adjacent functionality
2025-12-11docs: simplify grasp-02 docDanConwayDev
2025-12-11docs: remove old grasp-02 design doc versionsDanConwayDev
2025-12-11fix docsDanConwayDev
2025-12-11fix(sync): add Layer 1 re-subscription to daily_sync()DanConwayDev
- Add Layer 1 (announcements) re-subscription in daily_sync() after unsubscribe_all() to ensure kinds 30617+30618 are re-established - Clarify comments in handle_connect_or_reconnect() explaining that Layer 1 subscription is established during connect_and_subscribe() Addresses implementation gaps from design vs implementation report: - Gap 1: Comments clarified (Layer 1 handled by connect_and_subscribe) - Gap 2: daily_sync() now re-subscribes to Layer 1 without since filter - Gap 3: consolidate() already had Layer 1 re-subscription (no change) All 125 unit tests and integration tests pass.
2025-12-11chore: remove unused sync config fieldsDanConwayDev
Remove 4 config fields that were defined but never used: - sync_startup_delay_secs - sync_reconnect_delay_secs - sync_reconnect_lookback_days - sync_startup_jitter_ms These fields were added during GRASP-02 planning but the implementation took a different approach (using hardcoded constants for quick reconnect windows and batch window via env var).
2025-12-11fix: sync metrics aggregate relay countsDanConwayDev
2025-12-11fix: classify sync events as startup/live based on EOSE, not relay typeDanConwayDev
Previously, events were classified as 'startup' or 'live' based on whether they came from a bootstrap relay (is_bootstrap flag). This meant ALL events from bootstrap relays were counted as 'startup', even events received after the initial sync completed. Now events are classified based on whether EOSE (End Of Stored Events) has been received for that connection: - Events BEFORE EOSE → 'startup' (historical events during initial sync) - Events AFTER EOSE → 'live' (new events via real-time subscription) This enables the test_live_sync_event_count test which validates that events received after sync connection is established are counted as live events. Also removed the #[ignore] attribute from test_live_sync_event_count since the metrics are now properly wired up.
2025-12-11docs(sync): document why RelayConnection uses Client instead of Relay directlyDanConwayDev
nostr-sdk 0.44's Relay::new() is pub(crate), making it impossible to construct a Relay directly from outside the crate. Relays can only be created through Client::add_relay() or RelayPool::add_relay(). This commit: - Adds 'Why Client instead of Relay directly?' section to struct docs - Updates run_event_loop() docs to explain the API constraint - Removes outdated 'Future Refactoring' suggestion (not feasible)
2025-12-11refactor: use Relay::notifications() for event-driven disconnect detectionDanConwayDev
Replace the 1-second polling loop with nostr-sdk's relay-level notification system that provides immediate disconnect detection via RelayNotification::RelayStatus. Key changes: - Use relay.notifications() instead of client.notifications() - Handle RelayNotification::RelayStatus { Disconnected | Terminated } to detect connection loss immediately without polling - Remove tokio::select! with interval timer - now uses simple match loop - Handle additional notification types (Authenticated, AuthenticationFailed) Why this is better: - Event-driven vs polling: no wasted CPU cycles checking every second - Immediate detection: disconnect triggers notification instantly - Uses nostr-sdk's built-in mechanism that was previously inaccessible at pool level (RelayStatus notifications are filtered out in RelayPoolNotification) Technical note: RelayNotification::RelayStatus is only available via Relay::notifications(), not Client::notifications(), because the pool-level broadcast filters out status change events. Future refactoring opportunity: Consider restructuring RelayConnection to hold a Relay directly instead of wrapping a Client, since we only manage one relay per connection anyway.
2025-12-11fix: wire up relay disconnection detection for metricsDanConwayDev
- Add periodic health check in RelayConnection::run_event_loop that polls nostr-sdk's relay.is_connected() every second to detect dead connections - When event channel closes without explicit Closed/Shutdown, send DisconnectNotification to SyncManager (fixes case where TCP drops silently) - Enable test_relay_connected_status test which validates the ngit_sync_relay_connected metric correctly reflects connection state The issue was that when a remote relay stops abruptly, nostr-sdk's notification receiver blocks indefinitely waiting for data. TCP disconnect detection without keepalive can take minutes. The health check polls nostr-sdk's internal relay status which detects disconnection promptly.
2025-12-11fix: resolve duplicate SyncMetrics registration preventing metrics recordingDanConwayDev
Root cause: Both Metrics::new() and SyncManager::new() were trying to register SyncMetrics with the same Prometheus registry. The second registration failed silently, leaving SyncManager.metrics = None, so record_connection_attempt() calls were no-ops. Changes: - SyncManager::new() now accepts Option<SyncMetrics> instead of Option<&Registry> - main.rs passes already-registered sync metrics from Metrics to SyncManager - Simplified test_connection_failure_increments_counter assertion - Marked 3 tests as #[ignore] pending relay tracking metrics wiring Tests fixed: - test_connection_failure_increments_counter (now counts failures) - test_health_state_degrades_on_failure (now tracks health state) - test_live_sync_layer3_events (already working, confirmed) Tests ignored (future work): - test_live_sync_event_count - test_multi_source_aggregate_counts - test_relay_connected_status
2025-12-11sync: add sync_base_backoff_secs config for better testingDanConwayDev
2025-12-11sync: improve connection timeout handlingDanConwayDev
2025-12-11fix(sync): improve metrics recording and connection failure detectionDanConwayDev
Changes: - Fix connection attempt metrics: record success/failure based on actual connection result instead of pre-emptively recording failure - Add health tracker integration on connection failure: call record_failure() and record_health_state() in error path - Add connection verification in relay_connection.rs: wait 500ms after connect() then verify is_connected() to detect silent failures - Add configurable disconnect check interval via NGIT_SYNC_DISCONNECT_CHECK_INTERVAL_SECS env var - Update TestRelay with fast test settings: startup_delay=0, jitter=0, disconnect_check_interval=1s - Add debug output to metrics tests for investigation Note: Tests may still fail due to 5-second base backoff in health tracker. A follow-up task will add NGIT_SYNC_BASE_BACKOFF_SECS config parameter to allow faster test cycles. Related: metrics-wiring-plan.md Tasks 1 & 2
2025-12-11Phase 4: Add health state and multi-relay aggregate testsDanConwayDev
2025-12-11Phase 3: Delete useless tests, add 3 real metrics testsDanConwayDev
Deleted 12 existence-only tests that provided zero confidence: - test_sync_metrics_exposed - test_sync_metric_names_present - test_connection_metrics_on_success - test_event_sync_metrics - test_health_state_metrics - test_gap_event_tracking - test_connection_failure_metrics - test_failure_counter_increments - test_relay_count_metrics - test_event_source_labels_in_metrics - test_multi_relay_load - test_gap_events_tracked_separately Kept 5 valuable tests: - test_prometheus_format_valid - test_concurrent_metrics_requests - test_metric_values_are_numeric - test_startup_sync_event_count - test_metrics_availability_during_sync Added 3 real value-checking tests (currently ignored): - test_connection_failure_increments_counter - test_live_sync_event_count - test_relay_connected_status Test results: 6 passed, 0 failed, 3 ignored
2025-12-11Phase 2: Add first real metrics test (startup event count)DanConwayDev
2025-12-11Phase 1: Add ParsedMetrics and MetricsTestHarness infrastructureDanConwayDev
2025-12-11test: add additional sync metrics tests for better coverage (Phase 8)DanConwayDev
2025-12-11refactor: move metrics tests to tests/sync/ structure (Phase 7)DanConwayDev