From 9ba4bda355ccae30977b3e5ba15215df6661650c Mon Sep 17 00:00:00 2001 From: lklynet Date: Thu, 8 Jan 2026 12:31:56 -0500 Subject: [PATCH] feat(dashboard): add total unique peers counter and update docs - Add new counter for total unique peers in dashboard UI - Update command description to clarify default message scope - Enhance README with new features and better organization --- README.md | 198 ++++++++++++++++++++---------------------- public/app.js | 2 + public/index.html | 1 + public/js/commands.js | 2 +- src/web/routes.js | 2 + 5 files changed, 102 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index b5b7e84..68001d6 100644 --- a/README.md +++ b/README.md @@ -5,41 +5,73 @@ ### The High-Availability Solution to a Problem That Doesn't Exist. -**Hypermind** is a completely decentralized, Peer-to-Peer deployment counter. +**Hypermind** is a completely decentralized, Peer-to-Peer deployment counter and ephemeral chat platform. -It solves the critical infrastructure challenge of knowing exactly how many other people are currently wasting 50MB of RAM running this specific container. +It solves the critical infrastructure challenge of knowing exactly how many other people are currently wasting ~~50MB of~~ RAM running this specific container, while providing a secure, serverless way to say "hello" to them. --- -## » What is this? - -You have a server rack in your basement. You have 128GB of RAM. You have deployed the Arr stack, Home Assistant, Pi-hole, and a dashboard to monitor them all. **But you crave more.** +## What is this? You need a service that: -1. Does absolutely nothing useful. -2. Uses "Decentralized" and "P2P" in the description. -3. Makes a number go up on a screen. +1. **Does absolutely nothing useful.** +2. **Uses "Decentralized" and "P2P" in the description.** +3. **Makes a number go up on a screen.** **Enter Hypermind.** There is no central server. There is no database. There is only **The Swarm**. -## » How it works +## How it works -We utilize the **Hyperswarm** DHT (Distributed Hash Table) to achieve a singular, trivial goal of **Counting.** +We utilize the **Hyperswarm** DHT (Distributed Hash Table) to create a mesh network of useless nodes. -1. **Discovery:** Your node screams into the digital void (`hypermind-lklynet-v1`) to find friends. -2. **Gossip:** Nodes connect and whisper "I exist" to each other. -3. **Consensus:** Each node maintains a list of peers seen in the last 2.5 seconds. +1. **Discovery:** Your node screams into the digital void to find friends. +2. **Gossip:** Nodes connect and whisper "I exist" to each other. +3. **State:** + * **Active Count:** Maintained via a distributed LRU cache of peers seen in the last 45 seconds. + * **Total History:** Uses a **HyperLogLog** probabilistic data structure to estimate total unique peers with >98% accuracy. +4. **Chaos:** Connections are rotated every 5 minutes to ensure a dynamic, unblockable topology. -If you turn your container off, you vanish from the count. If everyone turns it off, the network ceases to exist. If you turn it back on, you are the Creator of the Universe (Population: 1). +## Features -## » Deployment +### 1. The Counter +It counts. That's the main thing. +* **Active Nodes:** Real-time count of currently online peers. +* **Total Unique:** A probabilistic estimate of every unique node ever encountered. -### Docker (The Fast Way) +### 2. Ephemeral Chat +A completely decentralized chat system built directly on top of the swarm topology. +* **Modes:** Local (direct neighbors) and Global (gossip relay). +* **Ephemeral:** No database. No history. +* **Markdown:** Full support for rich text. + +### 3. Visualizations +* **Particle Map:** Visualizes approximate peer locations (if enabled). +* **Themes:** Built-in theme switcher (Nord, Solarized, Tokyo Night, etc). + +--- + +## Usage + +### Dashboard +Open `http://localhost:3000`. The dashboard updates in **Realtime** via Server-Sent Events. + +### Chat Commands +* `/help` - Show all commands. +* `/local ` - Send message only to direct connections. +* `/whisper ` - Send a private message. +* `/block ` - Block a user. +* **Easter Eggs:** `/shrug`, `/tableflip`, `/heart`, and more. + +--- + +
+Deployment + +### Docker -Since you're probably pasting this into Portainer anyway: ```bash docker run -d \ @@ -47,8 +79,9 @@ docker run -d \ --network host \ --restart unless-stopped \ -e PORT=3000 \ + -e ENABLE_CHAT=true \ + -e ENABLE_MAP=true \ ghcr.io/lklynet/hypermind:latest - ``` > **⚠️ CRITICAL NETWORK NOTE:** @@ -67,7 +100,8 @@ services: restart: unless-stopped environment: - PORT=3000 - + - ENABLE_CHAT=true + - ENABLE_MAP=true ``` ### Kubernetes (The Enterprise Way) @@ -76,40 +110,40 @@ For when you need your useless counter to be orchestrated by a control plane. ```bash kubectl create deployment hypermind --image=ghcr.io/lklynet/hypermind:latest --port=3000 -kubectl set env deployment/hypermind PORT=3000 +kubectl set env deployment/hypermind PORT=3000 ENABLE_CHAT=true kubectl expose deployment hypermind --type=LoadBalancer --port=3000 --target-port=3000 - ``` -## » Configuration +
-You can customize Hypermind's behavior using environment variables. +
+Environment Variables -### Extras -These features are disabled by default. Set them to `true` to enable. +Hypermind is highly configurable. Use these variables to tune your experience. + +### Feature Flags | Variable | Default | Description | |----------|---------|-------------| -| `ENABLE_CHAT` | `false` | Enables the decentralized chat system. | -| `ENABLE_MAP` | `false` | Enables map visualization features. | -| `ENABLE_THEMES` | `true` | Controls the theme switcher function ([THEMES.md](THEMES.md)) - -### 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` | `5` | Maximum number of times a message is relayed. | -| `MAX_CONNECTIONS` | `15` | Maximum number of active P2P connections. | -| `HEARTBEAT_INTERVAL` | `30000` | How often (ms) to send heartbeat messages. | -| `CONNECTION_ROTATION_INTERVAL` | `300000` | How often (ms) to rotate connections. | -| `PEER_TIMEOUT` | `45000` | Time (ms) before a silent peer is considered offline. | -| `CHAT_RATE_LIMIT` | `5000` | Time window (ms) for chat rate limiting. | +| `ENABLE_CHAT` | `false` | Set to `true` to enable the P2P chat system. | +| `ENABLE_MAP` | `false` | Set to `true` to enable the map visualization. | +| `ENABLE_THEMES` | `true` | Set to `false` to disable the theme switcher. | | `VISUAL_LIMIT` | `500` | Max number of particles to render on the dashboard. | -## » Ecosystem & Integrations +### Network Tuning + +| Variable | Default | Description | +|----------|---------|-------------| +| `PORT` | `3000` | The web dashboard port. | +| `MAX_PEERS` | `50000` | Max peers to track in LRU cache. | +| `MAX_CONNECTIONS` | `15` | Max active TCP/UTP connections. | +| `MAX_RELAY_HOPS` | `5` | How far a global chat message travels (TTL). | +| `PEER_TIMEOUT` | `45000` | ms before a silent peer is considered offline. | + +
+ +
+Integrations The community has bravely stepped up to integrate Hypermind into critical monitoring infrastructure. @@ -119,9 +153,9 @@ Do you want your living room lights to turn red when the swarm grows? Of course The [Hypermind HA Integration](https://github.com/synssins/hypermind-ha) (installable via HACS) provides: -* **RGB Control:** 0 nodes = Green. 10,000 nodes = Red. -* **Sensors:** Swarm health checks and statistics logging. -* **WLED Support:** Visualize the swarm size on a literal LED strip. +* **RGB Control:** 0 nodes = Green. 10,000 nodes = Red. +* **Sensors:** Swarm health checks and statistics logging. +* **WLED Support:** Visualize the swarm size on a literal LED strip. ### Homepage Dashboard @@ -139,56 +173,17 @@ Add this to your `services.yaml`: method: GET mappings: - field: count - label: Swarm Size - - field: direct - label: Friends - + label: Active + - field: totalUnique + label: All Time ``` -To get the icon to work, you have to add the icon to `/app/public/icons`. If you have homepage running in a docker you mount an extra volume in your compose file. -See detailed [instructions](https://gethomepage.dev/configs/services/#icons). +To get the icon to work, you have to add the icon to `/app/public/icons`. See detailed [instructions](https://gethomepage.dev/configs/services/#icons). -## » Environment Variables +
-| Variable | Default | Description | -| --- | --- | --- | -| `PORT` | `3000` | The port the web dashboard listens on. Since `--network host` is used, this port opens directly on the host. | -| `MAX_PEERS` | `50000` | Maximum number of peers to track in the swarm. Unless you're expecting the entire internet to join, the default is probably fine. | -| `ENABLE_CHAT` | `false` | Set to `true` to enable the ephemeral P2P chat terminal. | - -## » Features - -### 1. The Counter -It counts. That's the main thing. - -### 2. Ephemeral Chat -**New:** A completely decentralized, ephemeral chat system built directly on top of the swarm topology. - -* **Ephemeral:** No database. No history. If you refresh, it's gone. -* **Restricted (Default):** You can only talk to your ~32 direct connections. -* **Global Mode:** Use `/global your message` to broadcast messages to the entire swarm (relayed via gossip). -* **Chaotic:** Every 30 seconds, the network rotates your connections. You might be mid-sentence and—*poof*—your audience changes. -* **Anonymous:** You are identified only by the last 4 characters of your node ID. - -To enable this feature, set `ENABLE_CHAT=true`. - -**Commands:** -* `/global on` - Enable global chat mode. -* `/global off` - Disable global chat mode (local only). -* `/global ` - Send a single message to the global swarm without switching modes. - -## » Usage - -Open your browser to: `http://localhost:3000` - -The dashboard updates in **Realtime** via Server-Sent Events. - -**You will see:** - -* **Active Nodes:** The total number of people currently running this joke. -* **Direct Connections:** The number of peers your node is actually holding hands with. - -## » Local Development +
+Local Development Want to contribute? Why? It already does nothing perfectly. But here is how anyway: @@ -198,38 +193,37 @@ npm install # Run the beast npm start - ``` -### Simulating Friends (Local Testing) - +### Simulating a Swarm You can run multiple instances locally to simulate popularity: ```bash -# Terminal 1 (You) +# Terminal 1 PORT=3000 npm start -# Terminal 2 (Your imaginary friend) +# Terminal 2 PORT=3001 npm start - ``` -They should discover each other, and the number will become `2`. Dopamine achieved. +They will discover each other via the local network DHT, and the number will become `2`. Dopamine achieved. + +
--- -### FAQ +## FAQ **Q: Is this crypto mining?** A: No. We respect your GPU too much. **Q: Does this store data?** -A: No. It has the short-term working memory of a honeybee (approx. 2.5 seconds). Which is biologically accurate and thematically consistent. +A: No. It has the short-term working memory of a honeybee (approx. 45 seconds). **Q: Why did you make this?** A: The homelab must grow. ¯\\_(ツ)_/¯ -## » Star History!! +## Star History diff --git a/public/app.js b/public/app.js index 2397f67..5c53da5 100644 --- a/public/app.js +++ b/public/app.js @@ -1,5 +1,6 @@ const countEl = document.getElementById("count"); const directEl = document.getElementById("direct"); +const totalUniqueEl = document.getElementById("total-unique"); const canvas = document.getElementById("network"); const ctx = canvas.getContext("2d"); let particles = []; @@ -696,6 +697,7 @@ evtSource.onmessage = (event) => { } directEl.innerText = data.direct; + if (totalUniqueEl) totalUniqueEl.innerText = data.totalUnique; if (data.diagnostics) { const d = data.diagnostics; diff --git a/public/index.html b/public/index.html index 7c7a39b..869913b 100644 --- a/public/index.html +++ b/public/index.html @@ -48,6 +48,7 @@
ID: {{ID}}
Direct Connections: {{DIRECT}}
+ Total Unique: {{TOTAL_UNIQUE}}
diagnostics | diff --git a/public/js/commands.js b/public/js/commands.js index 48b6348..cd5be4a 100644 --- a/public/js/commands.js +++ b/public/js/commands.js @@ -54,7 +54,7 @@ const ChatCommands = { { cmd: "/unblock <user>", desc: "Unblock a user" }, { cmd: "/local <msg>", - desc: "Send message to direct peers only", + desc: "Send message to direct peers only (Global by default)", }, { cmd: "/clear", desc: "Clear chat history" }, { cmd: "/help", desc: "Show this help menu" }, diff --git a/src/web/routes.js b/src/web/routes.js index 2b67b62..d4493b8 100644 --- a/src/web/routes.js +++ b/src/web/routes.js @@ -65,11 +65,13 @@ const setupRoutes = ( app.get("/", (req, res) => { const count = peerManager.size; const directPeers = swarm.getSwarm().connections.size; + const totalUnique = peerManager.totalUniquePeers; const html = HTML_TEMPLATE.replace(/\{\{COUNT\}\}/g, count) .replace(/\{\{ID\}\}/g, identity.screenname || "Unknown") .replace(/\{\{FULL_ID\}\}/g, identity.id) .replace(/\{\{DIRECT\}\}/g, directPeers) + .replace(/\{\{TOTAL_UNIQUE\}\}/g, totalUnique) .replace(/\{\{MAP_CLASS\}\}/g, ENABLE_MAP ? "" : "hidden") .replace(/\{\{THEMES_CLASS\}\}/g, ENABLE_THEMES ? "" : "hidden") .replace(/\{\{VISUAL_LIMIT\}\}/g, VISUAL_LIMIT);