// Package membership is what the mesh issues an assignment to serve and to reach (novox/hq ADR 0160), as a // process reads it from the bus: the Membership shape, the one subject it is published on, the stream that // holds it, and the bus address helpers a reader needs. The controller composes it and the route proxy reads // it; both use this one definition, so there is no second copy to drift (novox/hq issue 1633). package membership import ( "encoding/json" "strings" ) // AssignmentsStream holds every assignment's membership, the newest per subject. const AssignmentsStream = "ASSIGNMENTS" // Subject is the one address a runtime derives for itself: where its own membership is published, from // the two names its credential carries. Everything else is in the membership. func Subject(node, module string) string { return "mesh.assignment." + node + "." + module } // DirectGetSubject is the subject-addressed direct get of a membership from its stream: the one request an // account may make of the stream. func DirectGetSubject(node, module string) string { return "$JS.API.DIRECT.GET." + AssignmentsStream + "." + Subject(node, module) } // Membership is one assignment's subjects: what this instance of a module on this machine serves, // and what it may reach. type Membership struct { Node string `json:"node"` Module string `json:"module"` // Serves is every address a tool of this instance answers on. `{tool}` stands for the tool's // own name. An address with a queue is shared with the module's other instances. Serves []Served `json:"serves"` // Seats is every verb of a seat this instance holds, at the subject the seat's callers use. Seats []SeatServed `json:"seats,omitempty"` // Emits is where an event of this module lands; `{event}` stands for the event's name. Emits string `json:"emits"` // Reaches is each tool this module may call, `.`, to the subjects that reach it. Reaches map[string][]string `json:"reaches,omitempty"` // Tools is where this instance answers what it serves. Tools string `json:"tools"` // Receives is what this assignment is given for each requirement it receives, by requirement (novox/hq // ADR 0167): kept as JSON because the catalogue owns the shape of a contribution. Receives map[string]json.RawMessage `json:"receives,omitempty"` // Mesh is every machine's address on the private network (novox/hq ADR 0167). Mesh []string `json:"mesh,omitempty"` // State is every bucket this module's code may reach, and whether it may write it (novox/hq ADR 0201). State []StateIssued `json:"state,omitempty"` // Logs is every log this module's code may reach (novox/hq ADR 0297). Logs []LogIssued `json:"logs,omitempty"` // SeatTraffic is what this module's code may submit, say, hear, take, ask, answer and read on seats // that name their caller or their kind (novox/hq ADR 0259 §3). SeatTraffic *SeatTraffic `json:"seat-traffic,omitempty"` // Streams are this module's own streams, Hears the module streams it hears (novox/hq ADR 0363). Streams []StreamIssued `json:"streams,omitempty"` Hears []HeardIssued `json:"hears,omitempty"` // Waits are the event families a session may wait on through the mesh MCP server (novox/hq ADR 0367 §2). Waits []WaitIssued `json:"waits,omitempty"` } // Served is one address a tool is answered on. type Served struct { Subject string `json:"subject"` Queue string `json:"queue,omitempty"` } // SeatServed is one verb of a held seat, where its callers ask. type SeatServed struct { Seat string `json:"seat"` Verb string `json:"verb"` Subject string `json:"subject"` } // StateIssued is one bucket an assignment may reach, by the name its module uses for it (novox/hq ADR 0201). // Key, when set, is the one key it may reach (novox/hq ADR 0260): this machine's. type StateIssued struct { Name string `json:"name"` Bucket string `json:"bucket"` Writes bool `json:"writes,omitempty"` Key string `json:"key,omitempty"` } // LogIssued is one log an assignment may reach, by the name its module uses for it (novox/hq ADR 0297). type LogIssued struct { Name string `json:"name"` Stream string `json:"stream"` Subject string `json:"subject"` Writes bool `json:"writes"` } // Worker is one durable consumer a holder pulls a seat's work from. type Worker struct { Stream string Consumer string Filter string } // SeatTraffic is one module's seat traffic beyond tools. Publish and Subscribe are subject patterns, in the // server's wildcards. type SeatTraffic struct { Publish []string `json:"publish,omitempty"` Subscribe []string `json:"subscribe,omitempty"` Answers []string `json:"answers,omitempty"` Workers []Worker `json:"workers,omitempty"` Records []string `json:"records,omitempty"` Kinds []KindHeld `json:"kinds,omitempty"` } // KindHeld is one kind of a kinded bench and who holds it. type KindHeld struct { Seat string `json:"seat"` Kind string `json:"kind"` Module string `json:"module"` Node string `json:"node"` Capabilities []string `json:"capabilities,omitempty"` } // StreamIssued is one of a module's own streams as its membership names it (ADR 0363 §2). type StreamIssued struct { Stream string `json:"stream"` Events []string `json:"events"` Subject string `json:"subject"` } // HeardIssued is one module stream an assignment hears (ADR 0363 §8). type HeardIssued struct { Stream string `json:"stream"` Consumer string `json:"consumer"` } // WaitIssued is one family as a membership issues it: the name a session asks for, and its subject. type WaitIssued struct { Event string `json:"event"` Subject string `json:"subject"` } // CredentialIn reads the credential out of a bus address, `[scheme://]user:password@host:port`, splitting on // the last at-sign so a password containing one stays whole, and returns the address without it. func CredentialIn(address string) (user, password, bare string) { at := strings.LastIndex(address, "@") if at < 0 { return "", "", address } scheme := "" rest := address[:at] if i := strings.Index(rest, "://"); i >= 0 { scheme, rest = rest[:i+3], rest[i+3:] } user, password, _ = strings.Cut(rest, ":") return user, password, scheme + address[at+1:] } // BareAddress is a bus address with any credential stripped, for something that only needs to know // whether a server is answering there. func BareAddress(address string) string { _, _, bare := CredentialIn(address) if bare == "" { return address } if strings.Contains(bare, "://") { return bare } return "nats://" + bare }