3058bccfb6158c4210df8d4d399baa5075d82ecc
A guild's existing 'endpoint' field is its client-facing URL (browser, remote openclaw plugin) — but in-deployment services on the same docker network can't always reach it. Concretely, dialectic-backend on compose_default network sees 'http://server.t3:7002' (from agent-supplied URL) resolve to dind-network IP which it can't route to. Broadcasts silently fail with 'connection refused'. Adds a second URL per guild: serviceEndpoint. Used by other backends in the same deployment (compose service name + internal port). Plumbed through: - GuildNode.serviceEndpoint (varchar 255 nullable; TypeORM auto-migrates) - GET /api/auth/me/guilds returns serviceEndpoint per row - GET /api/nodes (admin) returns serviceEndpoint - new cli: 'node set-service-endpoint --node-id X --endpoint URL' (admin-only via cli — same pattern as set-purpose) Pairs with Fabric.OpenclawPlugin's fabric-guild-list returning the new field + workflows teaching agents to use serviceEndpoint for announce_guild_base_url (NOT the client-facing endpoint). E2e verified: sim recruiter agent discovered sim-guild-1's serviceEndpoint=http://fabric-backend-guild:7002, plumbed it into dialectic_propose_topic, all 4 lifecycle broadcasts (signup_open, signup_closed, debating, completed) landed in the announce channel.
Fabric.Backend.Center
The identity hub for Fabric (NestJS, ES modules, MySQL/TypeORM). Default
port 7001, global prefix /api.
Center is the single identity authority. Guild nodes register with it and introspect the tokens it issues; the frontend uses it to log in and to discover which guilds a user belongs to.
Responsibilities
- Users & sessions — register/login, JWT access + refresh tokens,
GET/PATCH /auth/me. User display name defaults to the email until changed. - Agent auth — per-agent API keys (
fak_…);POST /auth/agent/loginexchanges a key for a normal user session (used byFabric.OpenclawPlugin). - Guild-node registry — nodes register (
/api/nodes/register, localhost or node API key) and are handed out to users as endpoints + short-lived guild access tokens. - Name resolution —
POST /auth/resolve-namesmapsname/email → userId, scoped to a guild's members (used for<@user.name:NAME>mentions). - Membership —
POST /auth/me/guilds/join,GET /auth/me/guilds(returns guilds + fresh guild access tokens),GET /auth/guilds/:nodeId/members.
CLI
node dist/cli.js user create --email <e> --password <p>
node dist/cli.js user apikey --email <e> [--label <l>] # prints fak_… once
node dist/cli.js node register --node-id <id> --name <n> --endpoint <url>
Run
npm install
npm run build && npm start # or: npm run start:dev
Typically run via the root docker-compose.local.yml (service
backend-center). MySQL schema is auto-managed (DB_SYNC).
Notable env
FABRIC_BACKEND_CENTER_PORT(default 7001)FABRIC_BACKEND_CENTER_DB_*(host/port/user/password/name)- JWT signing secret(s) — see
src/config
Auth model
A global CenterApiKeyGuard protects most routes; auth/session endpoints
(login, agent/login, refresh, logout, me, me/guilds*,
resolve-names, guild members, node register) are exempted so users, agents,
and nodes can bootstrap.
Notes
- ES modules (
NodeNext); CJS deps are default-imported (import jwt from 'jsonwebtoken',import bcrypt from 'bcryptjs'). @IsEmail()rejects single-character TLDs — use e.g.@t.tt.
Description
Languages
TypeScript
98.8%
JavaScript
0.7%
Dockerfile
0.5%