import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @Entity('guild_nodes') export class GuildNode { @PrimaryGeneratedColumn('uuid') id!: string; @Column({ unique: true }) nodeId!: string; @Column() name!: string; @Column() endpoint!: string; // Service-to-service endpoint: how *another service inside the same // deployment* reaches this guild. Distinct from `endpoint` because // that one is the client-facing URL (browser, remote openclaw plugin, // anything outside compose), which lives on a different network than // services co-located with the guild backend. // // Concrete examples: // - sim sim-guild-1: endpoint=http://server.t3:7002 (dind /etc/hosts alias), // serviceEndpoint=http://fabric-backend-guild:7002 (compose service name) // - prod t3-node: endpoint=https://fabric-api.hangman-lab.top (public CF), // serviceEndpoint=http://backend-guild:7002 (compose service name) // // Used by dialectic-backend (and any other in-deployment service) to // post broadcasts to a guild's announce channel. Plugin's // fabric-guild-list returns this so agents can plumb the right URL // into announce_guild_base_url on dialectic_propose_topic. // Null when the admin hasn't filled it in (broadcasts will fail until // it's set; cli: `node set-service-endpoint --node-id X --endpoint URL`). @Column({ type: 'varchar', length: 255, nullable: true }) serviceEndpoint!: string | null; // Free-form description of this guild's purpose — what it's used for, // who its agents/users serve. Surfaced to agents via /auth/me/guilds so // the cross-guild/cross-channel announce-target picker can find the // right guild by intent ("which guild is about debate broadcasts?") // without anything hard-coded into agent workflows. admin-only write // (cli: `node set-purpose --node-id X --purpose Y`). @Column({ type: 'text', nullable: true }) purpose!: string | null; @Column({ type: 'varchar', length: 255, nullable: true }) apiKeyHash!: string | null; @Column({ type: 'enum', enum: ['active', 'offline', 'revoked'], default: 'active', }) status!: 'active' | 'offline' | 'revoked'; @Column({ type: 'datetime', nullable: true }) lastHeartbeatAt!: Date | null; @CreateDateColumn() createdAt!: Date; @UpdateDateColumn() updatedAt!: Date; }