Files
c-relay-pg/plans/caching_relay_selection_plan.md
T
Laan Tungir ca7a4b6722 v2.1.36 - Unified relay table, caching/backfill separation, inbox defaults, profile gating, and UI fixes
- Added caching_relays unified table with live_enabled/backfill_enabled columns
- Separated --reset-backfill from --start-caching as independent flags
- Removed redundant caching_enabled master setting; daemon derives from live/backfill
- Set caching_inbox_enabled=true by default; removed Inbox toggle from Backfill page
- Set caching_live_strategy=cache_all by default
- Fixed outbox relay discovery to store ALL discovered relays, not just covering set
- Added store_kind_0_information config (default: true) to gate profile sync trigger
- Regenerated pg_schema.h from pg_schema.sql to include caching_relays table
- Fixed process toggle button styling to match monochrome aesthetic
- Fixed radio button styling to match black/white/red theme
- Simplified Backfill page: removed Service Status and Inbox Status sections
- Backfill status now respects config setting, not just daemon state
- Admin config API now bumps caching_config_generation for caching-related changes
- make_and_restart_relay.sh now resets PostgreSQL schema on fresh restart
2026-08-03 19:37:09 -04:00

7.3 KiB

Caching Page: Relay Selection List

Goal

Replace the current "Upstream Relay Status" box on the caching page with an interactive relay selection list that shows:

  1. All discovered relays (from backfill progress + upstream status)
  2. How many followed pubkeys use each relay (sorted descending)
  3. Checkboxes to select which relays to connect to
  4. Connection status for each relay
  5. Ability to change selections on the fly

Data Sources

caching_backfill_relay_progress table

Contains (author_pubkey, relay_url) pairs for every followed author and their outbox relays. We can query:

SELECT relay_url, COUNT(DISTINCT author_pubkey) AS follow_count
FROM caching_backfill_relay_progress
GROUP BY relay_url
ORDER BY follow_count DESC

caching_upstream_relays table

Contains current connection status (status_code, status_text) for relays the daemon is connected to.

New config key: caching_live_relays

A comma-separated list of relay URLs that the live subscriber should connect to. When empty, defaults to the bootstrap relays from config.

UI Design

┌─ RELAY SELECTION ──────────────────────────────────────────┐
│                                                             │
│  ☑ relay.damus.io .............. 8 follows .... connected  │
│  ☑ nos.lol ...................... 7 follows .... connected  │
│  ☑ relay.primal.net ............ 5 follows .... connected  │
│  ☐ nostr.mom ................... 3 follows .... connecting │
│  ☐ relay.ditto.pub ............. 2 follows .... disconnected│
│  ☐ laantungir.net/relay ........ 1 follow ..... error      │
│                                                             │
│  [Save Relay Selection]                                     │
│                                                             │
│  Bootstrap relays (always connected):                       │
│  • relay.damus.io                                           │
│  • nos.lol                                                  │
│  • relay.primal.net                                         │
│  • laantungir.net/relay                                     │
└─────────────────────────────────────────────────────────────┘

Changes Required

1. New API Endpoint or Extended Endpoint

Add a new query to admin/api/live_subscription.php GET that returns:

{
  "config": { ... },
  "state": { ... },
  "upstreamRelays": [ ... ],
  "discoveredRelays": [
    {"relay_url": "wss://relay.damus.io", "follow_count": 8},
    {"relay_url": "wss://nos.lol", "follow_count": 7}
  ],
  "selectedRelays": ["wss://relay.damus.io", "wss://nos.lol"]
}

The discoveredRelays comes from caching_backfill_relay_progress (GROUP BY relay_url). The selectedRelays comes from the caching_live_relays config key.

POST action save_relays:

$stmt = $pdo->prepare("INSERT INTO config (key, value, data_type) VALUES ('caching_live_relays', ?, 'string')
    ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value");
$stmt->execute([$relayList]);
// Bump config generation

2. Admin Page HTML: admin/index.php

Replace the current "Upstream Relay Status" section with the new relay selection list:

<div class="input-group">
    <h3>Relay Selection</h3>
    <div id="caching-relay-selection" class="status-display">
        <p>Loading...</p>
    </div>
    <div class="inline-buttons" style="margin-top:8px">
        <button type="button" onclick="saveCachingRelays()">Save Relay Selection</button>
    </div>
    <div id="caching-relay-save-status" class="status-message"></div>
</div>

3. Admin JS: admin/assets/app.js

New function renderCachingRelaySelection() called from loadCaching():

function renderCachingRelaySelection(d) {
    const el = document.getElementById('caching-relay-selection');
    if (!el) return;
    
    // Merge discovered relays with upstream status
    const relayMap = {};
    (d.discoveredRelays || []).forEach(r => {
        relayMap[r.relay_url] = { follow_count: r.follow_count, checked: false, status: null };
    });
    (d.upstreamRelays || []).forEach(r => {
        if (!relayMap[r.relay_url]) relayMap[r.relay_url] = { follow_count: 0 };
        relayMap[r.relay_url].status = r;
    });
    
    const selected = new Set(d.selectedRelays || []);
    const bootstrap = new Set(d.config?.caching_bootstrap_relays?.split(',') || []);
    
    // Sort by follow_count descending
    const sorted = Object.entries(relayMap).sort((a, b) => b[1].follow_count - a[1].follow_count);
    
    let html = '<div class="relay-selection-list">';
    sorted.forEach(([url, info]) => {
        const checked = selected.has(url) ? 'checked' : '';
        const host = url.replace(/^wss?:\/\//, '').replace(/\/relay$/, '');
        const isBootstrap = bootstrap.has(url);
        let statusLabel = 'unknown';
        let statusClass = 'relay-status-unknown';
        if (info.status) {
            if (info.status.status_code == 2) { statusClass = 'relay-status-ok'; statusLabel = 'connected'; }
            else if (info.status.status_code == 1) { statusClass = 'relay-status-connecting'; statusLabel = 'connecting'; }
            else if (info.status.status_code == 0) { statusClass = 'relay-status-disconnected'; statusLabel = 'disconnected'; }
            else { statusClass = 'relay-status-error'; statusLabel = esc(info.status.status_text || 'error'); }
        }
        html += `<div class="relay-selection-row ${statusClass}">
            <input type="checkbox" class="relay-checkbox" value="${esc(url)}" ${checked}>
            <span class="relay-url" title="${esc(url)}">${esc(host)}</span>
            <span class="relay-follow-count">${info.follow_count} follows</span>
            <span class="relay-status-badge">${statusLabel}</span>
        </div>`;
    });
    html += '</div>';
    el.innerHTML = html;
}

New function saveCachingRelays():

async function saveCachingRelays() {
    const checkboxes = document.querySelectorAll('#caching-relay-selection .relay-checkbox:checked');
    const relays = Array.from(checkboxes).map(cb => cb.value).join(',');
    // POST to live_subscription.php with action=save_relays
}

4. Daemon Config: caching/src/pg_config.c

Read caching_live_relays config key. If set, use only those relays for the live subscriber instead of all upstream pool relays.

5. Daemon Live Subscriber: caching/src/live_subscriber.c

Modify get_relay_urls() or open_subscription() to filter the relay list based on cfg->live.selected_relays when configured.

Implementation Order

  1. Update admin/api/live_subscription.php — add discoveredRelays query, selectedRelays, save_relays action
  2. Update admin/index.php — replace relay status with relay selection HTML
  3. Update admin/assets/app.js — add renderCachingRelaySelection(), saveCachingRelays()
  4. Update daemon config + live subscriber to respect selected relays