upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
AgeCommit message (Collapse)Author
2026-01-07Wire up new purgatory sync loop, remove legacy sync_state_git_dataDanConwayDev
Phase 13 of purgatory-sync-redesign: - Add sync loop startup in main.rs (RealSyncContext + ThrottleManager + start_sync_loop) - Update add_state() and add_pr() to automatically enqueue for background sync - Remove start_state_sync() call from state.rs (now handled by sync loop) - Remove orphaned legacy functions: sync_state_git_data, fetch_missing_oids_from_server, get_most_complete_local_repo, identify_missing_oids, get_date_of_most_recent_commit_on_default_branch - Clean up unused imports in purgatory/mod.rs
2026-01-07Add RealSyncContext implementation for production purgatory syncDanConwayDev
Implement the production SyncContext that connects to real systems: - RealSyncContext struct holding purgatory, database, git_data_path, our_domain, and local_relay references - fetch_repository_data: delegates to git::authorization module - collect_needed_oids: collects commit hashes from state events (branches/tags) and PR events (c-tag) in purgatory - oid_exists: delegates to git::oid_exists function - fetch_oids: uses git fetch --depth=1 to retrieve specific OIDs from remote servers, running in spawn_blocking for async safety - process_newly_available_git_data: delegates to the unified function in git::sync module for consistent post-git-data processing - has_pending_events: delegates to purgatory method - find_target_repo: finds first existing owner repository on disk - our_domain: returns configured domain for clone URL filtering This enables the purgatory sync loop to use real database queries, git operations, and event processing instead of mocks.
2026-01-07refactor: remove align_repository_with_state duplicationDanConwayDev
- Remove duplicate AlignmentResult struct from nostr/policy/state.rs - Remove duplicate align_repository_with_state method from StatePolicy - Import and use the canonical implementation from git::sync - Re-export AlignmentResult from git::sync in policy/mod.rs The git::sync version is preferred as it: - Handles symbolic refs (ref:) properly by skipping them - Uses git::oid_exists which is more general than git::commit_exists - Has a cleaner iteration pattern (delete first, then update/create)
2026-01-07git: removed duplicate default branch updateDanConwayDev
this is now handled through process_newly_available_git_data
2026-01-07purgatory: more robust process_purgatory_state_events syncingDanConwayDev
2026-01-07purgatory: improve process_newly_available_git_data state event syncDanConwayDev
2026-01-07Refactor handle_receive_pack to use unified process_newly_available_git_dataDanConwayDev
Replace ~100 lines of duplicated post-push processing in handle_receive_pack with a single call to the unified process_newly_available_git_data function. The unified function handles all post-git-data-available processing: - Discovering satisfiable events from purgatory (state and PR events) - Syncing OIDs to authorized owner repos - Aligning refs (+ setting HEAD) in all owner repos - Saving events to database - Notifying WebSocket subscribers - Removing from purgatory This ensures consistent behavior regardless of how git data arrives (git push vs purgatory sync fetching from remote servers). Also mark test-only internal methods with #[cfg(test)] to silence dead code warnings.
2026-01-07Add unified process_newly_available_git_data functionDanConwayDev
Implement the unified function that handles all post-git-data-available processing, regardless of how data arrived (git push or purgatory sync). This function: - Discovers satisfiable events from purgatory (state and PR events) - Syncs OIDs to authorized owner repos - Aligns refs and sets HEAD - Saves events to database - Notifies WebSocket subscribers - Removes from purgatory New additions: - ProcessResult struct for tracking processing outcomes - process_newly_available_git_data async function in src/git/sync.rs - Helper functions: extract_identifier_from_repo_path, extract_identifier_from_pr_event - Purgatory::find_prs_for_identifier method for PR event discovery - Unit tests for all helper functions Also fixes: - Simplified extract_domain to avoid url crate dependency - Removed unused imports in sync/loop.rs
2026-01-07Add background sync loop for purgatory identifier processingDanConwayDev
Implement the main sync loop that runs in the background and processes identifiers that are ready for git data synchronization: - Runs every 1 second (hardcoded interval, not configurable) - Finds all ready identifiers where !in_progress && next_attempt <= now - Spawns parallel tasks for each ready identifier - Each task calls sync_identifier to try fetching git data from remotes - Applies backoff when sync completes but events remain in purgatory - Removes identifiers from queue when sync completes or no events remain The loop integrates with the existing sync infrastructure: - Uses SyncContext trait for testability - Uses ThrottleManager for domain-based rate limiting - Uses sync_identifier for the actual fetch orchestration This enables automatic background fetching of git data for events in purgatory, complementing the existing push-triggered sync path.
2026-01-07Add sync queue to Purgatory with enqueue_sync and has_pending_eventsDanConwayDev
- Add sync_queue field to Purgatory struct for tracking identifiers that need background git data fetching - Implement enqueue_sync() with debouncing - resets attempt_count and updates next_attempt when new events arrive for an identifier already in queue - Add enqueue_sync_default() for user-submitted events (3 minute delay to wait for git push) - Add enqueue_sync_immediate() for sync-triggered events (500ms delay for batching burst arrivals) - Implement has_pending_events() to check if an identifier has state events or PR events in purgatory - Add helper methods: sync_queue(), remove_from_sync_queue(), sync_queue_size() - Add unit tests for debouncing behavior and pending event detection
2026-01-07Add sync_identifier orchestration and ThrottleManager queue processingDanConwayDev
Implement the main sync orchestration function and trigger-based queue processing for throttled domains: sync_identifier function: - Orchestrates syncing git data for a single identifier - Tries all non-throttled URLs in sequence - Checks completion after each fetch (no pending events or all OIDs fetched) - Enqueues with throttled domains when non-throttled URLs are exhausted - Returns true if complete, false if events remain (for backoff) ThrottleManager enhancements: - Add set_context() to provide SyncContext for queue processing - Add try_process_next() to spawn tasks when capacity frees - Add process_queued_identifier() to handle queued work - Update complete_request() to trigger processing on completion - Update enqueue_identifier() to trigger processing when capacity available - Add internal methods for non-Arc testing compatibility Generic function updates: - Add ?Sized bound to sync_identifier_next_url, sync_identifier_from_url, sync_identifier, and get_throttled_domains_with_untried_urls for dynamic dispatch support (Arc<dyn SyncContext>) Tests: - sync_identifier_tries_multiple_urls_until_complete: verifies sequential URL fetching until all OIDs are available - sync_identifier_enqueues_throttled_domains_when_incomplete: verifies throttled domains get the identifier enqueued for later processing - has_queued_work_reflects_queue_state: verifies queue state tracking
2026-01-07Add core sync functions for identifier-based purgatory synchronizationDanConwayDev
Implement sync_identifier_next_url and sync_identifier_from_url functions that provide the core URL selection and fetch logic for purgatory sync. sync_identifier_next_url: - Pure URL selection logic with no side effects - Filters out our own domain and already-tried URLs - Respects domain throttling when domain parameter is None - Can target a specific domain when domain parameter is Some sync_identifier_from_url: - Fetches OIDs from a specific URL via the SyncContext - Tracks request start/completion with ThrottleManager for rate limiting - Calls process_newly_available_git_data on successful fetch Also adds get_throttled_domains_with_untried_urls helper for the main sync loop to know which DomainThrottle queues to enqueue identifiers to. These functions are designed to be called by both: - Main sync loop (tries non-throttled URLs immediately) - DomainThrottle queue processing (when capacity frees up) Includes 10 unit tests covering: - Throttled domain skipping - Tried URL skipping - Our domain filtering - Specific domain targeting - Fetch success/failure handling - Throttle request tracking
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-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
2025-12-31purgatory: when state data recieved sync across repositoiesDanConwayDev
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-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-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-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-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-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-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-11docs: simplify grasp-02 docDanConwayDev
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-11feat: add event metrics tracking throughout sync (Phase 5)DanConwayDev
2025-12-10feat: connect REGISTRY to SyncManager (Phase 3)DanConwayDev
2025-12-10feat: add metrics field to SyncManager (Phase 2)DanConwayDev
2025-12-10feat: create sync metrics module (Phase 1)DanConwayDev
2025-12-10fix: enable Layer 3 sync by adding root events to pending queueDanConwayDev
When root events (issues/patches) are received via self-subscription, handle_root_event() was only updating the repo_sync_index directly. This caused process_batch() to early-return when pending.is_empty(), so Layer 3 filters for comments/replies were never created. The fix adds root events to both: 1. repo_sync_index (for immediate availability) 2. pending queue (to trigger Layer 3 filter creation in next batch) Critical: The pending entry must include relays from repo_sync_index so derive_relay_targets() knows where to send Layer 3 subscriptions. The Layer 3 test now verifies that events sent BEFORE the subscription is established are still synced - proving subscriptions without 'since' correctly fetch historical events. Enabled 4 previously ignored Layer 3 tests: - test_live_sync_layer3_events - test_layer3_sync_with_lowercase_e_tag - test_layer3_sync_with_uppercase_e_tag - test_layer3_sync_with_q_tag
2025-12-10feat(sync): broadcast synced events to WebSocket subscribersDanConwayDev
Enable recursive relay discovery by broadcasting synced events to WebSocket subscribers via LocalRelay.notify_event(). This allows the SelfSubscriber to receive 30617 announcements synced from external relays and discover additional relay URLs to connect to. Changes: - Pass LocalRelay to SyncManager::new() from main.rs - Add local_relay field to SyncManager struct - Call notify_event() after saving synced events to database - Enable test_recursive_relay_discovery_syncs_announcement test The test verifies that when relay_a syncs announcement_x from bootstrap relay_b (which lists relay_c), relay_a discovers and connects to relay_c to sync announcement_y. Fixes recursive relay discovery from bootstrap sync.
2025-12-10sync: fix connection registration issueDanConwayDev
2025-12-10improve: count all active subscriptions in get_filter_count (IMPROVE-1)DanConwayDev
2025-12-10refactor: remove insert-remove pattern in spawn_relay_connection (SIMPLIFY-3)DanConwayDev
2025-12-10refactor: deduplicate SelfSubscriber select branches (SIMPLIFY-2)DanConwayDev
2025-12-10refactor: remove redundant RelayAction enum (SIMPLIFY-1)DanConwayDev
2025-12-10feat: add automatic reconnection with exponential backoff (IMPROVE-2)DanConwayDev
2025-12-10fix: don't add 30617 announcement IDs to root_events (BUG-2)DanConwayDev
2025-12-10fix: add Layer 1 re-subscription on quick reconnect (BUG-1)DanConwayDev
2025-12-10sync: implement graceful shutdown for all tasks and connectionsDanConwayDev
2025-12-10sync: enhance SelfSubscriber with reconnect and root event trackingDanConwayDev
2025-12-10sync: implement relay removal for empty non-bootstrap relaysDanConwayDev
2025-12-10sync: implement daily timer for periodic fresh syncDanConwayDev
2025-12-10sync: implement filter consolidation systemDanConwayDev
2025-12-10sync: complete AddFilters handler with auto-spawningDanConwayDev
2025-12-10sync: implement unified connect/reconnect with since filtersDanConwayDev
2025-12-10sync: implement PendingBatch EOSE confirmation flowDanConwayDev
2025-12-10sync: implement disconnect handler with state cleanupDanConwayDev
2025-12-10sync: integrate health tracking and connection storageDanConwayDev
2025-12-10sync v4 mvpDanConwayDev
2025-12-10stub of sync v4DanConwayDev
2025-12-10improve sync designDanConwayDev
2025-12-09sync initalize from dbDanConwayDev
2025-12-09basic sync stubDanConwayDev
2025-12-08proposed sync change to use self subscribe to trigger everythingDanConwayDev
2025-12-05remove stupid tests and methodsDanConwayDev
2025-12-05rename sunc_bootstrap_relay_urlDanConwayDev
2025-12-05fix basic sync testsDanConwayDev
2025-12-05sync fixesDanConwayDev
2025-12-04feat(sync): Phase 6 - observability and production readinessDanConwayDev
- Add SyncMetrics with full Prometheus integration - Track sync gaps via catchup events - Update Grafana dashboard with sync panels - Document all sync configuration options - Update design doc with implementation notes