hzhang 3058bccfb6 feat(guild-discovery): add serviceEndpoint for backend-to-backend reachability
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.
2026-05-23 22:21:03 +01:00
2026-05-15 18:47:35 +01:00
2026-05-15 18:47:35 +01:00

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/login exchanges a key for a normal user session (used by Fabric.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 resolutionPOST /auth/resolve-names maps name/email → userId, scoped to a guild's members (used for <@user.name:NAME> mentions).
  • MembershipPOST /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
No description provided
Readme 362 KiB
Languages
TypeScript 98.8%
JavaScript 0.7%
Dockerfile 0.5%