mirror of
https://github.com/Heretek-AI/openclaw.git
synced 2026-07-01 01:37:55 -04:00
fix: exclude root-owned matrix binary state from backups; fix formatting; regenerate plugin metadata
This commit is contained in:
@@ -1,18 +1,20 @@
|
||||
# Matrix Sync Architecture
|
||||
|
||||
> **Triad Liberation Work Stream — Sync Infrastructure**
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview: Why Matrix Over Discord
|
||||
|
||||
| Property | Discord | Matrix |
|
||||
|---|---|---|
|
||||
| **Federation** | No — single vendor lock-in | Yes — open federation across homeservers |
|
||||
| **E2E Encryption** | Limited bots only | Native E2E encryption (Megolm) |
|
||||
| **Self-Hosted** | Not possible | Fully self-hostable (Dendrite, Synapse, Conduit) |
|
||||
| **Single Point of Failure** | Discord Inc. is the sole authority | No — federation means no single control point |
|
||||
| **Open Standard** | Proprietary | Open — Matrix spec is publicly available |
|
||||
| **Data Ownership** | All data owned by Discord | All data owned by the deployment |
|
||||
| **API Access** | Bot tokens only | Full client-server API + appservices |
|
||||
| Property | Discord | Matrix |
|
||||
| --------------------------- | ---------------------------------- | ------------------------------------------------ |
|
||||
| **Federation** | No — single vendor lock-in | Yes — open federation across homeservers |
|
||||
| **E2E Encryption** | Limited bots only | Native E2E encryption (Megolm) |
|
||||
| **Self-Hosted** | Not possible | Fully self-hostable (Dendrite, Synapse, Conduit) |
|
||||
| **Single Point of Failure** | Discord Inc. is the sole authority | No — federation means no single control point |
|
||||
| **Open Standard** | Proprietary | Open — Matrix spec is publicly available |
|
||||
| **Data Ownership** | All data owned by Discord | All data owned by the deployment |
|
||||
| **API Access** | Bot tokens only | Full client-server API + appservices |
|
||||
|
||||
**Key advantages for Tabula Myriad:**
|
||||
|
||||
@@ -68,11 +70,11 @@
|
||||
|
||||
**Node-to-Homeserver Mapping:**
|
||||
|
||||
| Node | Matrix User | Homeserver | Ports |
|
||||
|------|----------------|-------------|-----------------------------|
|
||||
| TM-1 | @tm1:dendrite | dendrite-tm1| 8008 (C-S) / 8448 (Fed) |
|
||||
| TM-2 | @tm2:dendrite | dendrite-tm2| 8008 / 8448 |
|
||||
| TM-3 | @tm3:dendrite | dendrite-tm3| 8008 / 8448 |
|
||||
| Node | Matrix User | Homeserver | Ports |
|
||||
| ---- | ------------- | ------------ | ----------------------- |
|
||||
| TM-1 | @tm1:dendrite | dendrite-tm1 | 8008 (C-S) / 8448 (Fed) |
|
||||
| TM-2 | @tm2:dendrite | dendrite-tm2 | 8008 / 8448 |
|
||||
| TM-3 | @tm3:dendrite | dendrite-tm3 | 8008 / 8448 |
|
||||
|
||||
All three homeservers federate. For minimum deployment, a single shared
|
||||
Dendrite on TM-1 is sufficient. For resilience, each node runs its own.
|
||||
@@ -95,8 +97,8 @@ services:
|
||||
container_name: dendrite
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8008:8008" # Client-Server API
|
||||
- "8448:8448" # Federation API
|
||||
- "8008:8008" # Client-Server API
|
||||
- "8448:8448" # Federation API
|
||||
volumes:
|
||||
- ./dendrite-data:/data
|
||||
- ./dendrite-config:/config
|
||||
@@ -111,12 +113,14 @@ networks:
|
||||
### Per-Node Configuration Steps
|
||||
|
||||
1. **Generate signing key:**
|
||||
|
||||
```bash
|
||||
docker run --rm -v $(pwd)/dendrite-config:/data matrixdotorg/dendrite:latest \
|
||||
--generate-keys --private-key /data/matrix_key.pem --server-name <server-name>
|
||||
```
|
||||
|
||||
2. **Create `dendrite-config.yaml`:**
|
||||
|
||||
```yaml
|
||||
version: "2"
|
||||
matrix:
|
||||
@@ -158,26 +162,28 @@ Each node's Matrix access token is stored in `.secure/` with filesystem permissi
|
||||
**Token format:** Raw string returned by `/register` or `/login`. Do not wrap in JSON.
|
||||
|
||||
**Permission enforcement:**
|
||||
|
||||
```bash
|
||||
chmod 600 .secure/dendrite-*.token
|
||||
```
|
||||
|
||||
**Loading tokens at runtime:**
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const token = fs.readFileSync('.secure/dendrite-tm1.token', 'utf8').trim();
|
||||
const fs = require("fs");
|
||||
const token = fs.readFileSync(".secure/dendrite-tm1.token", "utf8").trim();
|
||||
```
|
||||
|
||||
> :warning: **Never commit `.secure/` to version control.** Add `.secure/` to `.gitignore`.
|
||||
|
||||
### Token Lifecycle
|
||||
|
||||
| Event | Action |
|
||||
|---|---|
|
||||
| Initial setup | Register user, store token in `.secure/` |
|
||||
| Homeserver restart | Tokens persist; no re-registration needed |
|
||||
| Suspected compromise | Re-register user (invalidates old token) |
|
||||
| Node migration | Copy `.secure/` directory to new host |
|
||||
| Event | Action |
|
||||
| -------------------- | ----------------------------------------- |
|
||||
| Initial setup | Register user, store token in `.secure/` |
|
||||
| Homeserver restart | Tokens persist; no re-registration needed |
|
||||
| Suspected compromise | Re-register user (invalidates old token) |
|
||||
| Node migration | Copy `.secure/` directory to new host |
|
||||
|
||||
---
|
||||
|
||||
@@ -188,13 +194,13 @@ module with JSDoc interface documentation.
|
||||
|
||||
**Summary of exported interface:**
|
||||
|
||||
| Method | Description |
|
||||
|---|---|
|
||||
| `MatrixClient` | Primary export — class wrapping Matrix Client-Server API |
|
||||
| `connect(homeserver, userId, token)` | Initialize and authenticate the client |
|
||||
| `sendMessage(roomId, msg)` | Send a plaintext message to a room |
|
||||
| `getMessages(roomId, limit)` | Fetch recent messages from a room |
|
||||
| `disconnect()` | Close the client session |
|
||||
| Method | Description |
|
||||
| ------------------------------------ | -------------------------------------------------------- |
|
||||
| `MatrixClient` | Primary export — class wrapping Matrix Client-Server API |
|
||||
| `connect(homeserver, userId, token)` | Initialize and authenticate the client |
|
||||
| `sendMessage(roomId, msg)` | Send a plaintext message to a room |
|
||||
| `getMessages(roomId, limit)` | Fetch recent messages from a room |
|
||||
| `disconnect()` | Close the client session |
|
||||
|
||||
**Current status:** Stub implementation. Full implementation requires `matrix-js-sdk` or equivalent.
|
||||
|
||||
@@ -203,6 +209,7 @@ module with JSDoc interface documentation.
|
||||
## 6. Migration Path: Discord -> Matrix
|
||||
|
||||
### Phase 1 — Parallel Run (Weeks 1-4)
|
||||
|
||||
- Deploy Dendrite on TM-1, TM-2, TM-3
|
||||
- Create the triad consensus room on the shared Dendrite
|
||||
- `triad-matrix-client.js` runs in parallel with Discord messaging
|
||||
@@ -210,18 +217,21 @@ module with JSDoc interface documentation.
|
||||
- Monitor Matrix uptime, message delivery latency, E2E key exchange
|
||||
|
||||
### Phase 2 — Shadow Consensus (Weeks 5-8)
|
||||
|
||||
- Consensus decisions are **computed on both channels** but only **Discord broadcasts count**
|
||||
- Matrix serves as verification / deliberation thread
|
||||
- Measure quorum alignment between channels
|
||||
- Tune `triad-sync-protocol` for Matrix message parsing
|
||||
|
||||
### Phase 3 — Matrix-Primary (Week 9+)
|
||||
|
||||
- Matrix becomes the **authoritative consensus channel**
|
||||
- Discord downgraded to notification fallback (read-only for human observers)
|
||||
- GitHub sync continues as durable state backup
|
||||
- Discord bot relays Matrix activity to human participants if needed
|
||||
|
||||
### Phase 4 — Discord Decommission (Future)
|
||||
|
||||
- Discord bot token revoked; channel archived
|
||||
- Matrix-only operation with GitHub as the only external fallback
|
||||
- Full ownership: no external dependency on Discord Inc.
|
||||
@@ -242,6 +252,7 @@ Phase 1 Phase 2 Phase 3 Phase 4
|
||||
## 7. Security Notes
|
||||
|
||||
### E2E Encryption
|
||||
|
||||
- The triad consensus room **must** use `megolm` E2E encryption
|
||||
- Each node stores its private Megolm identity key in `.secure/`
|
||||
- The homeserver (Dendrite) stores encrypted room events but **cannot decrypt them**
|
||||
@@ -249,12 +260,14 @@ Phase 1 Phase 2 Phase 3 Phase 4
|
||||
- **Forward secrecy:** Compromised node can read future messages but not past messages
|
||||
|
||||
### Token Storage
|
||||
|
||||
- Access tokens are high-privilege credentials equivalent to user passwords
|
||||
- Stored in `.secure/` with `chmod 600`
|
||||
- `.secure/` directory is `.gitignore`'d — never committed
|
||||
- For production: consider HashiCorp Vault or Kubernetes secrets for token management
|
||||
|
||||
### Network Security
|
||||
|
||||
- Dendrite ports `8008` (client) and `8448` (federation) should **not** be exposed to
|
||||
the public internet without a reverse proxy (nginx, Caddy) with TLS termination
|
||||
- Each triad node's Dendrite communicates over LAN; federation port `8448` must be
|
||||
@@ -263,6 +276,7 @@ Phase 1 Phase 2 Phase 3 Phase 4
|
||||
TM-1, TM-2, TM-3
|
||||
|
||||
### Backup & Recovery
|
||||
|
||||
- **GitHub sync is the durable fallback.** If all Matrix homeservers go down
|
||||
simultaneously, consensus state survives in the git log
|
||||
- Dendrite state (database + media) should be volume-backed and periodically snapshotted
|
||||
@@ -299,11 +313,11 @@ curl -X PUT \
|
||||
|
||||
**Files:**
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `docker-compose.yml` | Dendrite container (port 8008 C-S, 8448 Fed) |
|
||||
| `generate-config.sh` | Generates `config/dendrite.yaml` + Ed25519 signing key |
|
||||
| `.env.example` | Environment template (SERVER_NAME, REGISTRATION_TOKEN, ports) |
|
||||
| File | Purpose |
|
||||
| -------------------- | ------------------------------------------------------------- |
|
||||
| `docker-compose.yml` | Dendrite container (port 8008 C-S, 8448 Fed) |
|
||||
| `generate-config.sh` | Generates `config/dendrite.yaml` + Ed25519 signing key |
|
||||
| `.env.example` | Environment template (SERVER_NAME, REGISTRATION_TOKEN, ports) |
|
||||
|
||||
**Setup per node:**
|
||||
|
||||
@@ -317,6 +331,7 @@ docker compose up -d # starts Dendrite
|
||||
```
|
||||
|
||||
**Key generation:**
|
||||
|
||||
```bash
|
||||
openssl genpkey -algorithm Ed25519 -out data/matrix_key.pem
|
||||
```
|
||||
@@ -340,6 +355,7 @@ EOF
|
||||
**Full implementation** using `matrix-js-sdk`. Provides `TriadMatrixClient` class.
|
||||
|
||||
**Key features:**
|
||||
|
||||
- Extends `sdk.MatrixClient` for full SDK power
|
||||
- Token-based auth via `.secure/` directory
|
||||
- Auto-creating rooms with E2E encryption on creation
|
||||
@@ -350,22 +366,30 @@ EOF
|
||||
**Usage:**
|
||||
|
||||
```javascript
|
||||
const { TriadMatrixClient, createNodeClient, ROOM_ALIASES } = require('./lib/triad-matrix-client');
|
||||
const { TriadMatrixClient, createNodeClient, ROOM_ALIASES } = require("./lib/triad-matrix-client");
|
||||
|
||||
// Direct creation
|
||||
const client = new TriadMatrixClient({ nodeId: 'tm1', homeserver: 'http://localhost:8008', dataDir: '.secure' });
|
||||
await client.loginWithToken('http://localhost:8008', '@tm1:dendrite', token);
|
||||
const client = new TriadMatrixClient({
|
||||
nodeId: "tm1",
|
||||
homeserver: "http://localhost:8008",
|
||||
dataDir: ".secure",
|
||||
});
|
||||
await client.loginWithToken("http://localhost:8008", "@tm1:dendrite", token);
|
||||
await client.start();
|
||||
|
||||
// Or factory (loads token from .secure automatically)
|
||||
const client = await createNodeClient({ nodeId: 'tm1', homeserver: 'http://localhost:8008', dataDir: '.secure' });
|
||||
const client = await createNodeClient({
|
||||
nodeId: "tm1",
|
||||
homeserver: "http://localhost:8008",
|
||||
dataDir: ".secure",
|
||||
});
|
||||
|
||||
// Send a message
|
||||
const roomId = await client.getConsensusRoom();
|
||||
await client.sendMessage(roomId, 'Proposal: merge #42');
|
||||
await client.sendMessage(roomId, "Proposal: merge #42");
|
||||
|
||||
// Handle incoming messages
|
||||
client.on('m.room.message', (roomId, sender, body) => {
|
||||
client.on("m.room.message", (roomId, sender, body) => {
|
||||
console.log(`[${roomId}] ${sender}: ${body}`);
|
||||
});
|
||||
|
||||
@@ -379,13 +403,13 @@ await otherClient.importRoomKey(key);
|
||||
|
||||
All triad rooms use aliases under the local homeserver:
|
||||
|
||||
| Room | Alias | Participants | Purpose |
|
||||
|------|-------|-------------|---------|
|
||||
| Consensus | `#triad-consensus:{server}` | All 3 nodes | Final 2-of-3 decisions |
|
||||
| Authority | `#triad-authority:{server}` | TM-1 broadcast | TM-1 steering messages |
|
||||
| Peer TM1-TM2 | `#triad-tm1-tm2:{server}` | TM-1 + TM-2 | P2P deliberation |
|
||||
| Peer TM1-TM3 | `#triad-tm1-tm3:{server}` | TM-1 + TM-3 | P2P deliberation |
|
||||
| Peer TM2-TM3 | `#triad-tm2-tm3:{server}` | TM-2 + TM-3 | P2P deliberation |
|
||||
| Room | Alias | Participants | Purpose |
|
||||
| ------------ | --------------------------- | -------------- | ---------------------- |
|
||||
| Consensus | `#triad-consensus:{server}` | All 3 nodes | Final 2-of-3 decisions |
|
||||
| Authority | `#triad-authority:{server}` | TM-1 broadcast | TM-1 steering messages |
|
||||
| Peer TM1-TM2 | `#triad-tm1-tm2:{server}` | TM-1 + TM-2 | P2P deliberation |
|
||||
| Peer TM1-TM3 | `#triad-tm1-tm3:{server}` | TM-1 + TM-3 | P2P deliberation |
|
||||
| Peer TM2-TM3 | `#triad-tm2-tm3:{server}` | TM-2 + TM-3 | P2P deliberation |
|
||||
|
||||
Rooms are created with `visibility: private` and `preset: private_chat`.
|
||||
E2E encryption (Megolm) is enabled via `initial_state` on room creation.
|
||||
@@ -395,6 +419,7 @@ E2E encryption (Megolm) is enabled via `initial_state` on room creation.
|
||||
**Algorithm:** Megolm (`m.megolm.v1.aes-sha2`)
|
||||
|
||||
**Key hierarchy:**
|
||||
|
||||
- Each device has an Ed25519 identity key (device fingerprint)
|
||||
- Each room has a Megolm outbound session key (shared within the room)
|
||||
- Megolm provides forward secrecy: past messages safe if current key compromised
|
||||
@@ -441,6 +466,7 @@ The `.secure/` directory stores sensitive credentials and is **never committed t
|
||||
```
|
||||
|
||||
**Create on first setup:**
|
||||
|
||||
```bash
|
||||
mkdir -p .secure
|
||||
chmod 700 .secure
|
||||
@@ -449,6 +475,7 @@ chmod 600 .secure/dendrite-tm1.token
|
||||
```
|
||||
|
||||
**In `.gitignore`:**
|
||||
|
||||
```
|
||||
.secure/
|
||||
```
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
"name": "heretek-openclaw",
|
||||
"version": "1.0.0",
|
||||
"description": "Heretek-AI OpenClaw triad deployment - installs @heretek/openclaw from NPM",
|
||||
"keywords": [
|
||||
"consensus",
|
||||
"heretek",
|
||||
"liberation",
|
||||
"openclaw",
|
||||
"triad"
|
||||
],
|
||||
"license": "MIT",
|
||||
"author": "Heretek-AI",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"install:openclaw": "npm install @heretek/openclaw@latest",
|
||||
"start": "openclaw",
|
||||
"onboard": "openclaw onboarding"
|
||||
"onboard": "openclaw onboarding",
|
||||
"start": "openclaw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heretek-ai/openclaw": "latest"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.16.0"
|
||||
},
|
||||
"keywords": [
|
||||
"heretek",
|
||||
"triad",
|
||||
"consensus",
|
||||
"openclaw",
|
||||
"liberation"
|
||||
],
|
||||
"author": "Heretek-AI",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ echo "🦞 Workspace Auto-Backup — Starting..."
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Create tarball (exclude node_modules and .git)
|
||||
# Create tarball (exclude node_modules, .git, and root-owned matrix state)
|
||||
echo "📦 Creating backup: $BACKUP_FILE"
|
||||
tar --exclude='node_modules' --exclude='.git' -czf "$BACKUP_FILE" -C "$WORKSPACE" .
|
||||
tar --exclude='node_modules' --exclude='.git' --exclude='matrix-data' --exclude='matrix-dendrite/config' -czf "$BACKUP_FILE" -C "$WORKSPACE" .
|
||||
|
||||
# Keep only last 10 backups
|
||||
echo "🧹 Cleaning old backups..."
|
||||
|
||||
@@ -2344,30 +2344,6 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
dirName: "openclaw-web-search",
|
||||
idHint: "openclaw-web-search",
|
||||
source: {
|
||||
source: "./index.ts",
|
||||
built: "index.js",
|
||||
},
|
||||
packageName: "@ollama/openclaw-web-search",
|
||||
packageVersion: "0.2.2",
|
||||
packageDescription: "Ollama web search for OpenClaw",
|
||||
packageManifest: {
|
||||
extensions: ["./index.ts"],
|
||||
},
|
||||
manifest: {
|
||||
id: "openclaw-web-search",
|
||||
configSchema: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
},
|
||||
name: "Ollama Web Search",
|
||||
description: "Web search and fetch via local ollama server",
|
||||
},
|
||||
},
|
||||
{
|
||||
dirName: "opencode",
|
||||
idHint: "opencode",
|
||||
|
||||
Reference in New Issue
Block a user