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);