- 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
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:
- All discovered relays (from backfill progress + upstream status)
- How many followed pubkeys use each relay (sorted descending)
- Checkboxes to select which relays to connect to
- Connection status for each relay
- 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
- Update
admin/api/live_subscription.php— add discoveredRelays query, selectedRelays, save_relays action - Update
admin/index.php— replace relay status with relay selection HTML - Update
admin/assets/app.js— add renderCachingRelaySelection(), saveCachingRelays() - Update daemon config + live subscriber to respect selected relays