diff --git a/README.md b/README.md index 79b54ee..d2a6a87 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,33 @@ kubectl expose deployment hypermind --type=LoadBalancer --port=3000 --target-por ``` +## » Configuration + +You can customize Hypermind's behavior using environment variables. + +### Extras +These features are disabled by default. Set them to `true` to enable. + +| Variable | Default | Description | +|----------|---------|-------------| +| `ENABLE_CHAT` | `false` | Enables the decentralized chat system. | +| `ENABLE_MAP` | `false` | Enables map visualization features. | + +### Refinement +Tune the network parameters to fit your system resources. The defaults are safe for most users. Don't change unless you know what you're doing. + +| Variable | Default | Description | +|----------|---------|-------------| +| `MAX_PEERS` | `50000` | Maximum number of peers to track in memory. | +| `MAX_MESSAGE_SIZE` | `2048` | Maximum size of a single message in bytes. | +| `MAX_RELAY_HOPS` | `2` | Maximum number of times a message is relayed. | +| `MAX_CONNECTIONS` | `32` | Maximum number of active P2P connections. | +| `HEARTBEAT_INTERVAL` | `5000` | How often (ms) to send heartbeat messages. | +| `CONNECTION_ROTATION_INTERVAL` | `30000` | How often (ms) to rotate connections. | +| `PEER_TIMEOUT` | `15000` | Time (ms) before a silent peer is considered offline. | +| `CHAT_RATE_LIMIT` | `5000` | Time window (ms) for chat rate limiting. | +| `VISUAL_LIMIT` | `500` | Max number of particles to render on the dashboard. | + ## » Ecosystem & Integrations The community has bravely stepped up to integrate Hypermind into critical monitoring infrastructure. diff --git a/public/app.js b/public/app.js index 3878f37..2523137 100644 --- a/public/app.js +++ b/public/app.js @@ -38,7 +38,8 @@ class Particle { } const updateParticles = (count) => { - const VISUAL_LIMIT = 500; + const limitAttr = canvas.getAttribute('data-visual-limit'); + const VISUAL_LIMIT = limitAttr ? parseInt(limitAttr) : 500; const visualCount = Math.min(count, VISUAL_LIMIT); const currentCount = particles.length; @@ -245,6 +246,7 @@ const terminal = document.getElementById('terminal'); const terminalOutput = document.getElementById('terminal-output'); const terminalInput = document.getElementById('terminal-input'); const terminalToggle = document.getElementById('terminal-toggle'); +const mapContainer = document.getElementById('map-container'); const promptEl = document.querySelector('.prompt'); let myId = null; let myChatHistory = []; @@ -392,6 +394,15 @@ evtSource.onmessage = (event) => { document.body.classList.remove('chat-collapsed'); } + if (data.mapEnabled) { + if (mapContainer) mapContainer.style.display = 'inline'; + } else { + if (mapContainer) mapContainer.style.display = 'none'; + if (document.getElementById('mapModal').classList.contains('active')) { + closeMap(); + } + } + if (data.id) myId = data.id; if (data.peers) { diff --git a/public/index.html b/public/index.html index 2aaae41..3be5eb3 100644 --- a/public/index.html +++ b/public/index.html @@ -9,7 +9,7 @@ - +
{{COUNT}}
Active Nodes
@@ -19,8 +19,8 @@
ID: {{ID}}
Direct Connections: {{DIRECT}}
- diagnostics | - map + diagnostics + | map
diff --git a/public/style.css b/public/style.css index dae9a2c..4193dc7 100644 --- a/public/style.css +++ b/public/style.css @@ -155,6 +155,10 @@ a { color: #4b5563; text-decoration: none; border-bottom: 1px dotted #4b5563; } transition: transform 0.3s ease; } +.hidden { + display: none !important; +} + .terminal.hidden { display: none; } diff --git a/server.js b/server.js index 5e0534a..40c9757 100644 --- a/server.js +++ b/server.js @@ -8,7 +8,7 @@ const { relayMessage } = require("./src/p2p/relay"); const { SwarmManager } = require("./src/p2p/swarm"); const { SSEManager } = require("./src/web/sse"); const { createServer, startServer } = require("./src/web/server"); -const { DIAGNOSTICS_INTERVAL, ENABLE_CHAT } = require("./src/config/constants"); +const { DIAGNOSTICS_INTERVAL, ENABLE_CHAT, ENABLE_MAP } = require("./src/config/constants"); const main = async () => { const identity = generateIdentity(); @@ -26,6 +26,7 @@ const main = async () => { id: identity.id, diagnostics: diagnostics.getStats(), chatEnabled: ENABLE_CHAT, + mapEnabled: ENABLE_MAP, peers: peerManager.getPeersWithIps() }); }; diff --git a/src/config/constants.js b/src/config/constants.js index 0b76032..aeb6be0 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -17,18 +17,20 @@ const MY_POW_PREFIX = "00000"; const VERIFICATION_POW_PREFIX = "0000"; const MAX_PEERS = parseInt(process.env.MAX_PEERS) || 50000; -const MAX_MESSAGE_SIZE = 2048; -const MAX_RELAY_HOPS = 2; -const MAX_CONNECTIONS = 15; +const MAX_MESSAGE_SIZE = parseInt(process.env.MAX_MESSAGE_SIZE) || 2048; +const MAX_RELAY_HOPS = parseInt(process.env.MAX_RELAY_HOPS) || 2; +const MAX_CONNECTIONS = parseInt(process.env.MAX_CONNECTIONS) || 15; -const HEARTBEAT_INTERVAL = 30000; -const CONNECTION_ROTATION_INTERVAL = 300000; -const PEER_TIMEOUT = 45000; +const HEARTBEAT_INTERVAL = parseInt(process.env.HEARTBEAT_INTERVAL) || 30000; +const CONNECTION_ROTATION_INTERVAL = parseInt(process.env.CONNECTION_ROTATION_INTERVAL) || 300000; +const PEER_TIMEOUT = parseInt(process.env.PEER_TIMEOUT) || 45000; const BROADCAST_THROTTLE = 1000; const DIAGNOSTICS_INTERVAL = 10000; const PORT = process.env.PORT || 3000; const ENABLE_CHAT = process.env.ENABLE_CHAT === "true"; -const CHAT_RATE_LIMIT = 5000; +const ENABLE_MAP = process.env.ENABLE_MAP === 'true'; +const CHAT_RATE_LIMIT = parseInt(process.env.CHAT_RATE_LIMIT) || 5000; +const VISUAL_LIMIT = parseInt(process.env.VISUAL_LIMIT) || 500; module.exports = { TOPIC_NAME, @@ -46,5 +48,7 @@ module.exports = { DIAGNOSTICS_INTERVAL, PORT, ENABLE_CHAT, + ENABLE_MAP, CHAT_RATE_LIMIT, + VISUAL_LIMIT, }; diff --git a/src/p2p/messaging.js b/src/p2p/messaging.js index 82dd178..f901ee4 100644 --- a/src/p2p/messaging.js +++ b/src/p2p/messaging.js @@ -3,7 +3,7 @@ const { verifySignature, createPublicKey, } = require("../core/security"); -const { MAX_RELAY_HOPS, ENABLE_CHAT } = require("../config/constants"); +const { MAX_RELAY_HOPS, ENABLE_CHAT, CHAT_RATE_LIMIT } = require("../config/constants"); const { BloomFilterManager } = require("../state/bloom"); class MessageHandler { diff --git a/src/web/routes.js b/src/web/routes.js index 39b57a1..8d4a077 100644 --- a/src/web/routes.js +++ b/src/web/routes.js @@ -1,7 +1,7 @@ const express = require("express"); const fs = require("fs"); const path = require("path"); -const { ENABLE_CHAT, CHAT_RATE_LIMIT } = require("../config/constants"); +const { ENABLE_CHAT, ENABLE_MAP, CHAT_RATE_LIMIT, VISUAL_LIMIT } = require("../config/constants"); const HTML_TEMPLATE = fs.readFileSync( path.join(__dirname, "../../public/index.html"), @@ -18,7 +18,9 @@ const setupRoutes = (app, identity, peerManager, swarm, sseManager, diagnostics) const html = HTML_TEMPLATE .replace(/\{\{COUNT\}\}/g, count) .replace(/\{\{ID\}\}/g, "..." + identity.id.slice(-8)) - .replace(/\{\{DIRECT\}\}/g, directPeers); + .replace(/\{\{DIRECT\}\}/g, directPeers) + .replace(/\{\{MAP_CLASS\}\}/g, ENABLE_MAP ? '' : 'hidden') + .replace(/\{\{VISUAL_LIMIT\}\}/g, VISUAL_LIMIT); res.send(html); }); @@ -67,11 +69,11 @@ const setupRoutes = (app, identity, peerManager, swarm, sseManager, diagnostics) } const now = Date.now(); - // Clean up old timestamps (older than 10 seconds) - chatHistory = chatHistory.filter(time => now - time < 10000); + // Clean up old timestamps (older than CHAT_RATE_LIMIT) + chatHistory = chatHistory.filter(time => now - time < CHAT_RATE_LIMIT); if (chatHistory.length >= 5) { - return res.status(429).json({ error: "Rate limit exceeded: Max 5 messages per 10 seconds" }); + return res.status(429).json({ error: `Rate limit exceeded: Max 5 messages per ${CHAT_RATE_LIMIT / 1000} seconds` }); } chatHistory.push(now);