fix: exclude root-owned matrix binary state from backups; fix formatting; regenerate plugin metadata

This commit is contained in:
TM-1 Matrix Sync
2026-03-24 16:09:20 -04:00
parent 0a06867a34
commit e2975c6083
4 changed files with 90 additions and 87 deletions
+76 -49
View File
@@ -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/
```
+12 -12
View File
@@ -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"
}
}
+2 -2
View File
@@ -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",