Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@ You need a service that:

There is no central server. There is no database. There is only **The Swarm**.

## » Features

- **🔢 Live Counter**: See how many nodes are running worldwide
- **🗺️ Peer Map**: Visualize node locations on an interactive world map
- **💬 Ephemeral Chat**: Send fleeting messages that vanish when you restart
- **📊 Diagnostics**: Watch message flows, bandwidth, and peer health
- **✨ Particle Visualization**: Because numbers alone are boring

## » How it works

We utilize the **Hyperswarm** DHT (Distributed Hash Table) to achieve a singular, trivial goal of **Counting.**

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.
3. **Consensus:** Each node maintains a list of peers seen in the last 15 seconds.

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

Expand Down Expand Up @@ -67,6 +75,7 @@ services:
restart: unless-stopped
environment:
- PORT=3000
- ENABLE_CHAT=true # Optional: enable ephemeral chat

```

Expand Down Expand Up @@ -126,6 +135,7 @@ See detailed [instructions](https://gethomepage.dev/configs/services/#icons).
| --- | --- | --- |
| `PORT` | `3000` | The port the web dashboard listens on. Since `--network host` is used, this port opens directly on the host. |
| `MAX_PEERS` | `1000000` | 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` | Enable ephemeral P2P chat. Messages exist only in memory and vanish when nodes restart. |

## » Usage

Expand All @@ -137,6 +147,18 @@ The dashboard updates in **Realtime** via Server-Sent Events.

* **Active Nodes:** The total number of people currently running this joke.
* **Direct Connections:** The number of peers your node is actually holding hands with.
* **Peer Map:** Click "map" to see where your fellow nodes are located (approximately).
* **Diagnostics:** Click "diagnostics" for the nerdy details.

### Chat (Optional)

When `ENABLE_CHAT=true`, a terminal-style chat panel appears. You can:

- Set a nickname (stored locally, max 16 chars)
- Send short messages (max 140 chars, because constraints breed creativity)
- Watch messages appear from across the swarm

Messages are rate-limited (2s cooldown, 5 per 30s) and disappear when you restart. It's like Snapchat, but for infrastructure nerds.

## » Local Development

Expand All @@ -149,6 +171,9 @@ npm install
# Run the beast
npm start

# With chat enabled
ENABLE_CHAT=true npm start

```

### Simulating Friends (Local Testing)
Expand All @@ -166,6 +191,29 @@ PORT=3001 npm start

They should discover each other, and the number will become `2`. Dopamine achieved.

### Testing in WSL2 / Docker Desktop

DHT discovery sometimes fails in virtualized environments. Use the TCP relay:

```bash
# Terminal 1 - Start relay server
node relay-server.js

# Terminal 2 - Node 1
RELAY_PORT=4000 ENABLE_CHAT=true PORT=3000 node server.js

# Terminal 3 - Node 2
RELAY_PORT=4000 ENABLE_CHAT=true PORT=3001 node server.js
```

---

## » Documentation

- [Architecture Guide](docs/ARCHITECTURE.md) — How it works under the hood
- [API Reference](docs/API.md) — Endpoints for integration
- [Troubleshooting](docs/TROUBLESHOOTING.md) — When things go wrong

---

### FAQ
Expand All @@ -174,11 +222,14 @@ They should discover each other, and the number will become `2`. Dopamine achiev
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. 15 seconds). Which is biologically accurate and thematically consistent.

**Q: Why did you make this?**
A: The homelab must grow. ¯\\_(ツ)_/¯

**Q: Is the chat secure?**
A: Messages are signed but not encrypted. Don't share secrets. It's gossip, literally.

## » Star History!!

<a href="https://star-history.com/#lklynet/hypermind&Date">
Expand Down
207 changes: 207 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# API Reference

Hypermind exposes a simple HTTP API for monitoring and integration.

## Endpoints

### GET `/`

Returns the HTML dashboard.

### GET `/api/stats`

Returns current node statistics as JSON.

**Response:**

```json
{
"count": 42,
"totalUnique": 150,
"direct": 5,
"id": "302a300506032b6570032100...",
"diagnostics": {
"heartbeatsReceived": 100,
"heartbeatsRelayed": 80,
"invalidPoW": 0,
"duplicateSeq": 5,
"invalidSig": 0,
"newPeersAdded": 3,
"bytesReceived": 15000,
"bytesRelayed": 12000,
"leaveMessages": 1
}
}
```

| Field | Type | Description |
|-------|------|-------------|
| `count` | number | Current active peer count |
| `totalUnique` | number | Estimated total unique peers ever seen |
| `direct` | number | Number of direct connections |
| `id` | string | This node's identity (hex-encoded public key) |
| `diagnostics` | object | Metrics from the last 10-second interval |

**Diagnostics Fields:**

| Field | Description |
|-------|-------------|
| `heartbeatsReceived` | HEARTBEAT messages received |
| `heartbeatsRelayed` | Messages forwarded to other peers |
| `invalidPoW` | Messages rejected for bad Proof-of-Work |
| `duplicateSeq` | Messages rejected for stale sequence number |
| `invalidSig` | Messages rejected for bad signature |
| `newPeersAdded` | New unique peers discovered |
| `bytesReceived` | Total bytes received |
| `bytesRelayed` | Total bytes forwarded |
| `leaveMessages` | LEAVE messages processed |

### GET `/events`

Server-Sent Events stream for real-time updates.

**Event Format:**

```
data: {"count":42,"totalUnique":150,"direct":5,...}

data: {"type":"CHAT","sender":"302a...","nick":"alice","content":"hello","timestamp":1704326400000}

data: {"type":"SYSTEM","content":"Connection established with Node ...abc12345","timestamp":1704326400000}
```

**Event Types:**

1. **Stats Update**: Regular payload with `count`, `direct`, etc.
2. **Chat Message**: Has `type: "CHAT"` with message content
3. **System Message**: Has `type: "SYSTEM"` for connect/disconnect events

**Usage Example (JavaScript):**

```javascript
const events = new EventSource('/events');

events.onmessage = (event) => {
const data = JSON.parse(event.data);

if (data.type === 'CHAT') {
console.log(`${data.nick || data.sender}: ${data.content}`);
} else if (data.type === 'SYSTEM') {
console.log(`[SYSTEM] ${data.content}`);
} else {
console.log(`Peers: ${data.count}, Direct: ${data.direct}`);
}
};
```

### POST `/api/chat`

Send a chat message (requires `ENABLE_CHAT=true`).

**Request:**

```json
{
"msg": "Hello, swarm!",
"nick": "alice"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `msg` | string | Yes | Message content (max 140 chars) |
| `nick` | string | No | Nickname (max 16 chars, alphanumeric) |

**Response (Success):**

```json
{
"success": true
}
```

**Response (Rate Limited):**

```json
{
"error": "Rate limited. Please wait."
}
```

**Response (Chat Disabled):**

```json
{
"error": "Chat is disabled"
}
```

**Rate Limits:**
- Minimum 2 seconds between messages
- Maximum 5 messages per 30-second window

## Integration Examples

### Homepage Dashboard Widget

Add to your `services.yaml`:

```yaml
- Hypermind:
icon: /icons/hypermind2.png
href: http://YOUR_IP:3000
widget:
type: customapi
url: http://YOUR_IP:3000/api/stats
method: GET
mappings:
- field: count
label: Swarm Size
- field: direct
label: Direct Peers
```

### Home Assistant Sensor

```yaml
sensor:
- platform: rest
name: Hypermind Swarm Size
resource: http://YOUR_IP:3000/api/stats
value_template: "{{ value_json.count }}"
json_attributes:
- direct
- totalUnique
scan_interval: 30
```

### Shell Script Monitoring

```bash
#!/bin/bash
while true; do
stats=$(curl -s http://localhost:3000/api/stats)
count=$(echo $stats | jq .count)
direct=$(echo $stats | jq .direct)
echo "$(date): Peers=$count Direct=$direct"
sleep 10
done
```

### Python Integration

```python
import requests
import sseclient

# One-time stats fetch
stats = requests.get('http://localhost:3000/api/stats').json()
print(f"Current peers: {stats['count']}")

# Real-time monitoring
response = requests.get('http://localhost:3000/events', stream=True)
client = sseclient.SSEClient(response)
for event in client.events():
data = json.loads(event.data)
print(f"Peers: {data.get('count', 'N/A')}")
```
Loading