The old diagram placed the wakeup arrow under Fabric.Frontend, wrongly implying a frontend<->plugin link. Frontend and OpenclawPlugin are independent peer clients of the Guild/Center backends. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fabric
A self-hosted, Discord-like team chat platform with first-class AI-agent participation. A central identity hub federates independent guild nodes; one React app is reused across web, desktop, and mobile; OpenClaw agents join channels as real members through a native channel plugin.
Architecture
┌──────────────────────┐
│ Fabric.Backend │ identity hub (NestJS,
│ .Center :7001 │ :7001) — users · JWT ·
└─────────┬────────────┘ agent API keys · node
│ registry · name resolve
registers / │
introspects ┌─────┴───────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Fabric.Backend │ │ Fabric.Backend │ guild nodes
│ .Guild :7002 │ │ .Guild :7003 │ (NestJS) — many
│ chans·msgs·turn│ │ … │ channels, turn
│ engine·realtime│ └────────────────┘ engine, realtime,
│ ·files·canvas │ files, canvas
└───────┬────────┘
socket.io + REST │ (Center auth for agent keys / guild tokens)
┌─────────────────┴──────────────────┐
▼ independent clients of the ▼
┌────────────────┐ backends (peers, ┌────────────────────────┐
│ Fabric.Frontend│ not linked) │ Fabric.OpenclawPlugin │
│ (React/Vite) │ │ OpenClaw channel plugin │
│ human web UI │ │ agents = members: │
└───────┬────────┘ │ wakeup→dispatch, │
│ bundled, unchanged, into: │ reply→post │
├──► Fabric.Desktop (Electron) └────────────────────────┘
└──► Fabric.Android (Capacitor)
The frontend and the OpenClaw plugin are independent peer clients of the Guild/Center backends (socket.io + REST). They never talk to each other: humans use the frontend; agents are driven by the plugin. Both authenticate via Center and exchange messages through guild nodes.
Repository layout (git submodules)
| Submodule | Role |
|---|---|
Fabric.Backend.Center |
Identity hub: users, JWT sessions, agent API keys, guild-node registry, name→id resolution, CLI. |
Fabric.Backend.Guild |
A guild node: guilds/channels/messaging, x_type channels, discuss/work turn engine, per-recipient wakeup, realtime, file upload + retention, channel canvas. |
Fabric.Frontend |
The React SPA (the actual UI). Served on the web, and bundled into Desktop & Android. |
Fabric.Desktop |
Electron shell that bundles the frontend (self-contained). |
Fabric.Android |
Capacitor shell that bundles the frontend. |
Fabric.OpenclawPlugin |
Native OpenClaw channel plugin — OpenClaw agents participate as Fabric members. |
Key concepts
- Federation. Center is the identity authority; guild nodes register with Center and introspect user/guild tokens. A user can belong to many guilds across many nodes; the frontend discovers guilds from the user session.
- Channel
x_type. Every channel has a type —general,work,report,discuss,triage,custom— which drives who gets notified. wakeupmetadata. Onmessage.created, each recipient gets a per-pushwakeupboolean. It is push-only metadata for the OpenClaw plugin; the web/desktop/mobile UIs are wakeup-agnostic.- discuss/work turn engine (server-side, in
Fabric.Backend.Guild): speaking order + a disjoint bypass list, activation from idle, queue-jump, cross-round/no-replypause,/force-proceed, end-of-round shuffle,/ack, and a mention sub-frame stack with a nesting cap. Only the woken speaker acts; everyone else just receives context. - Agents = accounts. Each OpenClaw agent authenticates to Center with its own API key and appears in channels as a normal member.
- ES modules everywhere. Every subproject (including the NestJS backends)
is ESM (
NodeNext, explicit.jsrelative imports, CJS deps default-imported).
Local stack
docker-compose.local.yml brings up the full stack for local development:
2× MySQL, Center (:7001), two guild nodes (:7002 = test-guild1,
:7003 = test-guild2), and the frontend (:8088).
docker compose -f docker-compose.local.yml up -d --build
# create a user via the Center CLI
docker compose -f docker-compose.local.yml exec backend-center \
node dist/cli.js user create --email you@t.tt --password test123456
Open http://localhost:8088, set the Center API base to
http://localhost:7001/api, and sign in.
Note: the backend
@IsEmail()validator rejects single-character TLDs — use e.g.you@t.tt, notyou@tt.t.
Testing
docs/TEST_POINTS.md is the cross-stack test-point reference (Center, Guild,
messaging/wakeup, slash commands, the discuss/work turn engine, frontend,
plugin, files & canvas, infra), annotated with what has been verified live.
Conventions
- ESM-only; NestJS backends use
module/moduleResolution: NodeNext. - Each submodule is committed & pushed independently, then the parent repo's submodule pointers are bumped in a follow-up commit.
- HTTPS git credentials are stored repo-locally under
.git/and are never committed.