···7070 return out, nil
7171}
72727373+// Instances returns every gossipped service instance across the cluster.
7474+// One round trip — useful when you want to render a per-node view rather
7575+// than per-service.
7676+func (cat *Catalog) Instances() ([]*CatalogService, error) {
7777+ r := cat.c.newRequest("GET", "/v1/catalog/instances")
7878+ _, resp, err := requireOK(cat.c.doRequest(r))
7979+ if err != nil {
8080+ return nil, err
8181+ }
8282+ defer closeResponseBody(resp)
8383+ var out []*CatalogService
8484+ if err := decodeJSONBody(resp.Body, &out); err != nil {
8585+ return nil, err
8686+ }
8787+ return out, nil
8888+}
8989+7390// Nodes returns every node serf knows about.
7491func (cat *Catalog) Nodes() ([]*Node, error) {
7592 r := cat.c.newRequest("GET", "/v1/catalog/nodes")
+6
internal/agent/agent.go
···245245 return a.services.ServiceInstances(name)
246246}
247247248248+// CatalogAllInstances returns every gossipped service instance across the
249249+// whole cluster as a flat slice.
250250+func (a *Agent) CatalogAllInstances() []NodeService {
251251+ return a.services.AllInstances()
252252+}
253253+248254// CatalogNodeServices returns the services owned by the named node.
249255func (a *Agent) CatalogNodeServices(node string) map[string]*api.AgentService {
250256 return a.services.NodeServices(node)
+23
internal/agent/catalog_endpoint.go
···88 "j3s.sh/cascade/api"
99)
10101111+// catalogInstances serves GET /v1/catalog/instances. Returns one entry
1212+// per (node, service) pair across the whole cluster.
1313+func (s *HTTPHandlers) catalogInstances(w http.ResponseWriter, r *http.Request) {
1414+ instances := s.agent.CatalogAllInstances()
1515+ out := make([]api.CatalogService, 0, len(instances))
1616+ for _, inst := range instances {
1717+ entry := api.CatalogService{
1818+ Node: inst.Node,
1919+ ServiceID: inst.Service.ID,
2020+ ServiceName: inst.Service.Service,
2121+ ServiceAddress: inst.Service.Address,
2222+ ServicePort: inst.Service.Port,
2323+ ServiceTags: inst.Service.Tags,
2424+ ServiceMeta: inst.Service.Meta,
2525+ }
2626+ if m, ok := s.agent.MemberByName(inst.Node); ok {
2727+ entry.Address = m.Addr.String()
2828+ }
2929+ out = append(out, entry)
3030+ }
3131+ s.writeJSON(w, r, out)
3232+}
3333+1134// catalogServices serves GET /v1/catalog/services. Returns the cluster-wide
1235// map of service name -> union of tags seen on any instance.
1336func (s *HTTPHandlers) catalogServices(w http.ResponseWriter, r *http.Request) {