OverviewFilesCommitsIssuesDocsReleasesPeers

wirecore commits / 40be3aa

Add runtime configuration API for relay tuning
Added configurable parameters for performance tuning: - maxConcurrentFetches: 1-100 (default: 5) - lazyLoadLimit: 1-100 (default: 20) - fetchTimeout: 1000-30000ms (default: 5000ms) - maxCachedMessages: 100-100000 (default: 10000) - remoteServer: URL (default: https://pub.wiredove.net) API endpoints: - GET /config - View current configuration - POST /config - Update configuration (JSON body) Web UI: - ⚙️ Configuration button opens tuning panel - Live updates with validation - Shows current values Use cases: - Reduce concurrent fetches if overwhelming remote server - Increase lazy load limit for faster initial page loads - Adjust timeouts based on network conditions - Change remote server for testing Environment variables also supported: - MAX_CONCURRENT_FETCHES - LAZY_LOAD_LIMIT - FETCH_TIMEOUT - MAX_CACHED_MESSAGES - REMOTE_SERVER Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
40be3aa7370411952ffb5ebf76dacf713582a9c7
by plankatron <fleeky@gmail.com> · 2026-01-20T17:16:14.000Z

Changed files (2) +192 -11

lib/wirecore-apds.js +17 -10
……@@ -13,9 +13,17 @@ import https from 'node:https'
1313import hypercore_crypto from 'hypercore-crypto'
1414import nacl from 'tweetnacl'
1515
16// Limit concurrent connections to avoid overwhelming pub.wiredove.net
17// (it's a simple Deno file server with no rate limiting)
18https.globalAgent.maxSockets = 5
16// Configuration (can be adjusted at runtime)
17export const config = {
18 maxConcurrentFetches: parseInt(process.env.MAX_CONCURRENT_FETCHES) || 5,
19 maxCachedMessages: parseInt(process.env.MAX_CACHED_MESSAGES) || 10000,
20 lazyLoadLimit: parseInt(process.env.LAZY_LOAD_LIMIT) || 20,
21 fetchTimeout: parseInt(process.env.FETCH_TIMEOUT) || 5000,
22 remoteServer: process.env.REMOTE_SERVER || 'https://pub.wiredove.net'
23}
24
25// Apply connection limit
26https.globalAgent.maxSockets = config.maxConcurrentFetches
1927
2028let store = null
2129let metaBee = null // For key-value metadata
……@@ -24,7 +32,6 @@ let messageCore = null // For append-only message log
2432let swarm = null
2533
2634// In-memory cache for performance (like original APDS)
27const MAX_CACHED_MESSAGES = 10000
2835let hashLog = []
2936let openedLog = []
3037let hashSet = new Set() // O(1) duplicate checking
……@@ -92,7 +99,7 @@ async function rebuildCache() {
9299
93100 // Read all messages from Hypercore (up to max cache size)
94101 const length = messageCore.length
95 const startIndex = Math.max(0, length - MAX_CACHED_MESSAGES)
102 const startIndex = Math.max(0, length - config.maxCachedMessages)
96103
97104 for (let i = startIndex; i < length; i++) {
98105 try {
……@@ -108,7 +115,7 @@ async function rebuildCache() {
108115 }
109116 }
110117
111 console.log(`Loaded ${hashLog.length} messages from Hypercore (last ${MAX_CACHED_MESSAGES})`)
118 console.log(`Loaded ${hashLog.length} messages from Hypercore (last ${config.maxCachedMessages})`)
112119}
113120
114121/**
……@@ -176,7 +183,7 @@ apds.get = async (hash) => {
176183 const fetchPromise = (async () => {
177184 try {
178185 const blob = await new Promise((resolve, reject) => {
179 const req = https.get(`https://pub.wiredove.net/${hash}`, { timeout: 5000 }, (res) => {
186 const req = https.get(`${config.remoteServer}/${hash}`, { timeout: config.fetchTimeout }, (res) => {
180187 if (res.statusCode === 404) {
181188 resolve(null)
182189 return
……@@ -287,7 +294,7 @@ apds.add = async (msg) => {
287294 await messageCore.append(Buffer.from(JSON.stringify(obj)))
288295
289296 // Update cache with memory limit (LRU-style)
290 if (hashLog.length >= MAX_CACHED_MESSAGES) {
297 if (hashLog.length >= config.maxCachedMessages) {
291298 const oldHash = hashLog.shift()
292299 openedLog.shift()
293300 hashSet.delete(oldHash)
……@@ -311,8 +318,8 @@ apds.query = async (query) => {
311318 ) :
312319 openedLog
313320
314 // Lazy-load missing text for first 20 messages in parallel
315 const limit = Math.min(20, results.length)
321 // Lazy-load missing text for first N messages in parallel
322 const limit = Math.min(config.lazyLoadLimit, results.length)
316323 const fetchPromises = []
317324
318325 for (let i = 0; i < limit; i++) {
relay/wirecore-relay.js +175 -1
……@@ -10,7 +10,7 @@
1010import http from 'http'
1111import https from 'https'
1212import { WebSocket, WebSocketServer } from 'ws'
13import { apds } from '../lib/wirecore-apds.js'
13import { apds, config } from '../lib/wirecore-apds.js'
1414
1515// Initialize wirecore-apds
1616await apds.start('wirecore-relay-v1')
……@@ -228,6 +228,39 @@ const handleHTTP = async (req, res) => {
228228 <button onclick="loadMessages()">🔄 Refresh</button>
229229 <button onclick="window.location.href='stats'">📊 Stats JSON</button>
230230 <button onclick="window.location.href='all'">📋 Messages JSON</button>
231 <button onclick="toggleConfig()">⚙️ Configuration</button>
232
233 <div id="configPanel" style="display: none; background: #161b22; border: 1px solid #30363d; border-radius: 6px; padding: 20px; margin-bottom: 20px;">
234 <h3 style="margin-bottom: 15px;">⚙️ Configuration</h3>
235 <div style="margin-bottom: 15px;">
236 <label style="display: block; margin-bottom: 5px; color: #8b949e;">Max Concurrent Fetches (1-100):</label>
237 <input type="number" id="maxConcurrentFetches" min="1" max="100" style="width: 100px; padding: 5px; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; border-radius: 4px;">
238 <span style="margin-left: 10px; color: #8b949e; font-size: 12px;">Current: <span id="currentMaxConcurrent">-</span></span>
239 </div>
240 <div style="margin-bottom: 15px;">
241 <label style="display: block; margin-bottom: 5px; color: #8b949e;">Lazy Load Limit (1-100):</label>
242 <input type="number" id="lazyLoadLimit" min="1" max="100" style="width: 100px; padding: 5px; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; border-radius: 4px;">
243 <span style="margin-left: 10px; color: #8b949e; font-size: 12px;">Current: <span id="currentLazyLoad">-</span></span>
244 </div>
245 <div style="margin-bottom: 15px;">
246 <label style="display: block; margin-bottom: 5px; color: #8b949e;">Fetch Timeout (1000-30000ms):</label>
247 <input type="number" id="fetchTimeout" min="1000" max="30000" step="1000" style="width: 100px; padding: 5px; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; border-radius: 4px;">
248 <span style="margin-left: 10px; color: #8b949e; font-size: 12px;">Current: <span id="currentTimeout">-</span>ms</span>
249 </div>
250 <div style="margin-bottom: 15px;">
251 <label style="display: block; margin-bottom: 5px; color: #8b949e;">Max Cached Messages (100-100000):</label>
252 <input type="number" id="maxCachedMessages" min="100" max="100000" step="100" style="width: 100px; padding: 5px; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; border-radius: 4px;">
253 <span style="margin-left: 10px; color: #8b949e; font-size: 12px;">Current: <span id="currentMaxCached">-</span></span>
254 </div>
255 <div style="margin-bottom: 15px;">
256 <label style="display: block; margin-bottom: 5px; color: #8b949e;">Remote Server:</label>
257 <input type="text" id="remoteServer" placeholder="https://pub.wiredove.net" style="width: 300px; padding: 5px; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; border-radius: 4px;">
258 <span style="margin-left: 10px; color: #8b949e; font-size: 12px;">Current: <span id="currentRemote">-</span></span>
259 </div>
260 <button onclick="updateConfig()" style="background: #238636;">💾 Save Configuration</button>
261 <button onclick="toggleConfig()" style="background: #6e7681; margin-left: 10px;">Cancel</button>
262 <div id="configResult" style="margin-top: 10px; padding: 10px; border-radius: 4px; display: none;"></div>
263 </div>
231264
232265 <div class="messages" id="messages">
233266 <div class="no-messages">Loading messages...</div>
……@@ -281,6 +314,75 @@ const handleHTTP = async (req, res) => {
281314 return div.innerHTML
282315 }
283316
317 async function loadConfig() {
318 try {
319 const res = await fetch('config')
320 const config = await res.json()
321 document.getElementById('currentMaxConcurrent').textContent = config.maxConcurrentFetches
322 document.getElementById('currentLazyLoad').textContent = config.lazyLoadLimit
323 document.getElementById('currentTimeout').textContent = config.fetchTimeout
324 document.getElementById('currentMaxCached').textContent = config.maxCachedMessages
325 document.getElementById('currentRemote').textContent = config.remoteServer
326
327 document.getElementById('maxConcurrentFetches').value = config.maxConcurrentFetches
328 document.getElementById('lazyLoadLimit').value = config.lazyLoadLimit
329 document.getElementById('fetchTimeout').value = config.fetchTimeout
330 document.getElementById('maxCachedMessages').value = config.maxCachedMessages
331 document.getElementById('remoteServer').value = config.remoteServer
332 } catch (err) {
333 console.error('Failed to load config:', err)
334 }
335 }
336
337 function toggleConfig() {
338 const panel = document.getElementById('configPanel')
339 if (panel.style.display === 'none') {
340 panel.style.display = 'block'
341 loadConfig()
342 } else {
343 panel.style.display = 'none'
344 }
345 }
346
347 async function updateConfig() {
348 const result = document.getElementById('configResult')
349 try {
350 const updates = {
351 maxConcurrentFetches: parseInt(document.getElementById('maxConcurrentFetches').value),
352 lazyLoadLimit: parseInt(document.getElementById('lazyLoadLimit').value),
353 fetchTimeout: parseInt(document.getElementById('fetchTimeout').value),
354 maxCachedMessages: parseInt(document.getElementById('maxCachedMessages').value),
355 remoteServer: document.getElementById('remoteServer').value
356 }
357
358 const res = await fetch('config', {
359 method: 'POST',
360 headers: { 'Content-Type': 'application/json' },
361 body: JSON.stringify(updates)
362 })
363
364 const data = await res.json()
365
366 if (data.success) {
367 result.style.display = 'block'
368 result.style.background = '#238636'
369 result.textContent = '✅ Configuration updated successfully!'
370 await loadConfig()
371 setTimeout(() => {
372 result.style.display = 'none'
373 }, 3000)
374 } else {
375 result.style.display = 'block'
376 result.style.background = '#da3633'
377 result.textContent = '❌ Failed to update: ' + (data.error || 'Unknown error')
378 }
379 } catch (err) {
380 result.style.display = 'block'
381 result.style.background = '#da3633'
382 result.textContent = '❌ Error: ' + err.message
383 }
384 }
385
284386 loadStats()
285387 loadMessages()
286388 setInterval(loadStats, 5000)
……@@ -330,6 +432,78 @@ const handleHTTP = async (req, res) => {
330432 return
331433 }
332434
435 // GET /config - view current configuration
436 if (key === 'config' && req.method === 'GET') {
437 res.writeHead(200, { 'Content-Type': 'application/json' })
438 res.end(JSON.stringify({
439 maxConcurrentFetches: config.maxConcurrentFetches,
440 maxCachedMessages: config.maxCachedMessages,
441 lazyLoadLimit: config.lazyLoadLimit,
442 fetchTimeout: config.fetchTimeout,
443 remoteServer: config.remoteServer
444 }, null, 2))
445 return
446 }
447
448 // POST /config - update configuration
449 if (key === 'config' && req.method === 'POST') {
450 let body = ''
451 req.on('data', chunk => body += chunk)
452 req.on('end', () => {
453 try {
454 const updates = JSON.parse(body)
455
456 // Validate and apply updates
457 if (updates.maxConcurrentFetches !== undefined) {
458 const val = parseInt(updates.maxConcurrentFetches)
459 if (val > 0 && val <= 100) {
460 config.maxConcurrentFetches = val
461 https.globalAgent.maxSockets = val
462 console.log(`Updated maxConcurrentFetches: ${val}`)
463 }
464 }
465
466 if (updates.maxCachedMessages !== undefined) {
467 const val = parseInt(updates.maxCachedMessages)
468 if (val >= 100 && val <= 100000) {
469 config.maxCachedMessages = val
470 console.log(`Updated maxCachedMessages: ${val}`)
471 }
472 }
473
474 if (updates.lazyLoadLimit !== undefined) {
475 const val = parseInt(updates.lazyLoadLimit)
476 if (val > 0 && val <= 100) {
477 config.lazyLoadLimit = val
478 console.log(`Updated lazyLoadLimit: ${val}`)
479 }
480 }
481
482 if (updates.fetchTimeout !== undefined) {
483 const val = parseInt(updates.fetchTimeout)
484 if (val >= 1000 && val <= 30000) {
485 config.fetchTimeout = val
486 console.log(`Updated fetchTimeout: ${val}ms`)
487 }
488 }
489
490 if (updates.remoteServer !== undefined && typeof updates.remoteServer === 'string') {
491 if (updates.remoteServer.startsWith('http://') || updates.remoteServer.startsWith('https://')) {
492 config.remoteServer = updates.remoteServer
493 console.log(`Updated remoteServer: ${updates.remoteServer}`)
494 }
495 }
496
497 res.writeHead(200, { 'Content-Type': 'application/json' })
498 res.end(JSON.stringify({ success: true, config }))
499 } catch (err) {
500 res.writeHead(400, { 'Content-Type': 'application/json' })
501 res.end(JSON.stringify({ error: 'Invalid JSON' }))
502 }
503 })
504 return
505 }
506
333507 // GET /{hash} - specific blob
334508 const blob = await apds.get(key)
335509 if (blob) {