Skip to content

Commit 3781787

Browse files
committed
feat: implement all 14 cross-service protocol linkers (Phase 0-6)
Protocols: HTTP, GraphQL, gRPC, Kafka, SQS, SNS, GCP Pub/Sub, WebSocket, SSE, RabbitMQ/AMQP, MQTT, NATS, Redis Pub/Sub, tRPC, EventBridge. 15 edge types registered in orchestrator and Louvain community detection. 11,463 lines in servicelink/, 484 lines in linkutil/config.go. Session 4: Kafka, SQS, SNS (full implementations from stubs) Session 5: WebSocket, SSE (Hive Mind, 2 sonnet builders) Session 6: RabbitMQ, MQTT, NATS, Redis, tRPC, EventBridge (Hive Mind, 3 opus + 3 sonnet builders), plus infra wiring for all phases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6aba2fc commit 3781787

14 files changed

Lines changed: 8502 additions & 34 deletions

File tree

internal/linkutil/config.go

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@ import (
1010

1111
// LinkerConfig holds user-overridable service linker settings.
1212
// Loaded from .cgrconfig in the project root.
13-
// This is the unified config for all protocol linkers (HTTP, GraphQL, gRPC, Kafka, SQS, SNS, PubSub).
13+
// This is the unified config for all protocol linkers (HTTP, GraphQL, gRPC, Kafka, SQS, SNS, PubSub, WebSocket, SSE, RabbitMQ, MQTT, NATS, Redis, tRPC, EventBridge).
1414
type LinkerConfig struct {
1515
HTTPLinker HTTPLinkerConfig `yaml:"http_linker"`
1616
GraphQLLinker GraphQLLinkerConfig `yaml:"graphql_linker"`
1717
GRPCLinker GRPCLinkerConfig `yaml:"grpc_linker"`
1818
KafkaLinker KafkaLinkerConfig `yaml:"kafka_linker"`
1919
SQSLinker SQSLinkerConfig `yaml:"sqs_linker"`
2020
SNSLinker SNSLinkerConfig `yaml:"sns_linker"`
21-
PubSubLinker PubSubLinkerConfig `yaml:"pubsub_linker"`
21+
PubSubLinker PubSubLinkerConfig `yaml:"pubsub_linker"`
22+
WebSocketLinker WebSocketLinkerConfig `yaml:"websocket_linker"`
23+
SSELinker SSELinkerConfig `yaml:"sse_linker"`
24+
RabbitMQLinker RabbitMQLinkerConfig `yaml:"rabbitmq_linker"`
25+
MQTTLinker MQTTLinkerConfig `yaml:"mqtt_linker"`
26+
NATSLinker NATSLinkerConfig `yaml:"nats_linker"`
27+
RedisPubSubLinker RedisPubSubLinkerConfig `yaml:"redis_pubsub_linker"`
28+
TRPCLinker TRPCLinkerConfig `yaml:"trpc_linker"`
29+
EventBridgeLinker EventBridgeLinkerConfig `yaml:"eventbridge_linker"`
2230
}
2331

2432
// HTTPLinkerConfig holds HTTP linker-specific settings.
@@ -92,6 +100,76 @@ type PubSubLinkerConfig struct {
92100
FuzzyMatching *bool `yaml:"fuzzy_matching"`
93101
}
94102

103+
// WebSocketLinkerConfig holds WebSocket linker-specific settings.
104+
type WebSocketLinkerConfig struct {
105+
// ExcludePaths are WebSocket URL paths to exclude from WS_CALLS matching.
106+
ExcludePaths []string `yaml:"exclude_paths"`
107+
108+
// MinConfidence is the minimum confidence score for creating WS_CALLS edges.
109+
// Default: 0.25.
110+
MinConfidence *float64 `yaml:"min_confidence"`
111+
112+
// FuzzyMatching enables/disables fuzzy URL path matching.
113+
// Default: true.
114+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
115+
}
116+
117+
// SSELinkerConfig holds SSE linker-specific settings.
118+
type SSELinkerConfig struct {
119+
// ExcludePaths are SSE endpoint paths to exclude from SSE_CALLS matching.
120+
ExcludePaths []string `yaml:"exclude_paths"`
121+
122+
// MinConfidence is the minimum confidence score for creating SSE_CALLS edges.
123+
// Default: 0.25.
124+
MinConfidence *float64 `yaml:"min_confidence"`
125+
126+
// FuzzyMatching enables/disables fuzzy URL path matching.
127+
// Default: true.
128+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
129+
}
130+
131+
// RabbitMQLinkerConfig holds RabbitMQ/AMQP linker-specific settings.
132+
type RabbitMQLinkerConfig struct {
133+
ExcludeExchanges []string `yaml:"exclude_exchanges"`
134+
MinConfidence *float64 `yaml:"min_confidence"`
135+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
136+
}
137+
138+
// MQTTLinkerConfig holds MQTT linker-specific settings.
139+
type MQTTLinkerConfig struct {
140+
ExcludeTopics []string `yaml:"exclude_topics"`
141+
MinConfidence *float64 `yaml:"min_confidence"`
142+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
143+
}
144+
145+
// NATSLinkerConfig holds NATS linker-specific settings.
146+
type NATSLinkerConfig struct {
147+
ExcludeSubjects []string `yaml:"exclude_subjects"`
148+
MinConfidence *float64 `yaml:"min_confidence"`
149+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
150+
}
151+
152+
// RedisPubSubLinkerConfig holds Redis Pub/Sub linker-specific settings.
153+
type RedisPubSubLinkerConfig struct {
154+
ExcludeChannels []string `yaml:"exclude_channels"`
155+
MinConfidence *float64 `yaml:"min_confidence"`
156+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
157+
}
158+
159+
// TRPCLinkerConfig holds tRPC linker-specific settings.
160+
type TRPCLinkerConfig struct {
161+
ExcludeProcedures []string `yaml:"exclude_procedures"`
162+
MinConfidence *float64 `yaml:"min_confidence"`
163+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
164+
}
165+
166+
// EventBridgeLinkerConfig holds AWS EventBridge linker-specific settings.
167+
type EventBridgeLinkerConfig struct {
168+
ExcludeSources []string `yaml:"exclude_sources"`
169+
MinConfidence *float64 `yaml:"min_confidence"`
170+
FuzzyMatching *bool `yaml:"fuzzy_matching"`
171+
}
172+
95173
// GraphQLLinkerConfig holds GraphQL linker-specific settings.
96174
type GraphQLLinkerConfig struct {
97175
// ExcludeOperations are operation names/patterns to exclude from GRAPHQL_CALLS matching.
@@ -265,6 +343,134 @@ func (c *LinkerConfig) GRPCEffectiveFuzzyMatching() bool {
265343
return true
266344
}
267345

346+
// WebSocketEffectiveMinConfidence returns the configured minimum confidence for WebSocket linker.
347+
func (c *LinkerConfig) WebSocketEffectiveMinConfidence() float64 {
348+
if c.WebSocketLinker.MinConfidence != nil {
349+
return *c.WebSocketLinker.MinConfidence
350+
}
351+
return 0.25
352+
}
353+
354+
// WebSocketEffectiveFuzzyMatching returns the configured fuzzy matching setting for WebSocket linker.
355+
func (c *LinkerConfig) WebSocketEffectiveFuzzyMatching() bool {
356+
if c.WebSocketLinker.FuzzyMatching != nil {
357+
return *c.WebSocketLinker.FuzzyMatching
358+
}
359+
return true
360+
}
361+
362+
// SSEEffectiveMinConfidence returns the configured minimum confidence for SSE linker.
363+
func (c *LinkerConfig) SSEEffectiveMinConfidence() float64 {
364+
if c.SSELinker.MinConfidence != nil {
365+
return *c.SSELinker.MinConfidence
366+
}
367+
return 0.25
368+
}
369+
370+
// SSEEffectiveFuzzyMatching returns the configured fuzzy matching setting for SSE linker.
371+
func (c *LinkerConfig) SSEEffectiveFuzzyMatching() bool {
372+
if c.SSELinker.FuzzyMatching != nil {
373+
return *c.SSELinker.FuzzyMatching
374+
}
375+
return true
376+
}
377+
378+
// RabbitMQEffectiveMinConfidence returns the configured minimum confidence for RabbitMQ linker.
379+
func (c *LinkerConfig) RabbitMQEffectiveMinConfidence() float64 {
380+
if c.RabbitMQLinker.MinConfidence != nil {
381+
return *c.RabbitMQLinker.MinConfidence
382+
}
383+
return 0.25
384+
}
385+
386+
// RabbitMQEffectiveFuzzyMatching returns the configured fuzzy matching setting for RabbitMQ linker.
387+
func (c *LinkerConfig) RabbitMQEffectiveFuzzyMatching() bool {
388+
if c.RabbitMQLinker.FuzzyMatching != nil {
389+
return *c.RabbitMQLinker.FuzzyMatching
390+
}
391+
return true
392+
}
393+
394+
// MQTTEffectiveMinConfidence returns the configured minimum confidence for MQTT linker.
395+
func (c *LinkerConfig) MQTTEffectiveMinConfidence() float64 {
396+
if c.MQTTLinker.MinConfidence != nil {
397+
return *c.MQTTLinker.MinConfidence
398+
}
399+
return 0.25
400+
}
401+
402+
// MQTTEffectiveFuzzyMatching returns the configured fuzzy matching setting for MQTT linker.
403+
func (c *LinkerConfig) MQTTEffectiveFuzzyMatching() bool {
404+
if c.MQTTLinker.FuzzyMatching != nil {
405+
return *c.MQTTLinker.FuzzyMatching
406+
}
407+
return true
408+
}
409+
410+
// NATSEffectiveMinConfidence returns the configured minimum confidence for NATS linker.
411+
func (c *LinkerConfig) NATSEffectiveMinConfidence() float64 {
412+
if c.NATSLinker.MinConfidence != nil {
413+
return *c.NATSLinker.MinConfidence
414+
}
415+
return 0.25
416+
}
417+
418+
// NATSEffectiveFuzzyMatching returns the configured fuzzy matching setting for NATS linker.
419+
func (c *LinkerConfig) NATSEffectiveFuzzyMatching() bool {
420+
if c.NATSLinker.FuzzyMatching != nil {
421+
return *c.NATSLinker.FuzzyMatching
422+
}
423+
return true
424+
}
425+
426+
// RedisPubSubEffectiveMinConfidence returns the configured minimum confidence for Redis Pub/Sub linker.
427+
func (c *LinkerConfig) RedisPubSubEffectiveMinConfidence() float64 {
428+
if c.RedisPubSubLinker.MinConfidence != nil {
429+
return *c.RedisPubSubLinker.MinConfidence
430+
}
431+
return 0.25
432+
}
433+
434+
// RedisPubSubEffectiveFuzzyMatching returns the configured fuzzy matching setting for Redis Pub/Sub linker.
435+
func (c *LinkerConfig) RedisPubSubEffectiveFuzzyMatching() bool {
436+
if c.RedisPubSubLinker.FuzzyMatching != nil {
437+
return *c.RedisPubSubLinker.FuzzyMatching
438+
}
439+
return true
440+
}
441+
442+
// TRPCEffectiveMinConfidence returns the configured minimum confidence for tRPC linker.
443+
func (c *LinkerConfig) TRPCEffectiveMinConfidence() float64 {
444+
if c.TRPCLinker.MinConfidence != nil {
445+
return *c.TRPCLinker.MinConfidence
446+
}
447+
return 0.25
448+
}
449+
450+
// TRPCEffectiveFuzzyMatching returns the configured fuzzy matching setting for tRPC linker.
451+
func (c *LinkerConfig) TRPCEffectiveFuzzyMatching() bool {
452+
if c.TRPCLinker.FuzzyMatching != nil {
453+
return *c.TRPCLinker.FuzzyMatching
454+
}
455+
return true
456+
}
457+
458+
// EventBridgeEffectiveMinConfidence returns the configured minimum confidence for EventBridge linker.
459+
func (c *LinkerConfig) EventBridgeEffectiveMinConfidence() float64 {
460+
if c.EventBridgeLinker.MinConfidence != nil {
461+
return *c.EventBridgeLinker.MinConfidence
462+
}
463+
return 0.25
464+
}
465+
466+
// EventBridgeEffectiveFuzzyMatching returns the configured fuzzy matching setting for EventBridge linker.
467+
func (c *LinkerConfig) EventBridgeEffectiveFuzzyMatching() bool {
468+
if c.EventBridgeLinker.FuzzyMatching != nil {
469+
return *c.EventBridgeLinker.FuzzyMatching
470+
}
471+
return true
472+
}
473+
268474
// IsPathExcluded checks if a route path matches any of the given exclusion paths.
269475
// This is a generic helper for path/topic/queue exclusion.
270476
func IsPathExcluded(path string, excludePaths []string) bool {

internal/pipeline/communities.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
// passCommunities runs Louvain community detection on the CALLS graph
1313
// and creates Community nodes + MEMBER_OF edges.
1414
// Includes all service communication edge types: CALLS, ASYNC_CALLS, HTTP_CALLS,
15-
// GRAPHQL_CALLS, GRPC_CALLS, KAFKA_CALLS, SQS_CALLS, SNS_CALLS, PUBSUB_CALLS.
15+
// GRAPHQL_CALLS, GRPC_CALLS, KAFKA_CALLS, SQS_CALLS, SNS_CALLS, PUBSUB_CALLS, WS_CALLS, SSE_CALLS,
16+
// AMQP_CALLS, MQTT_CALLS, NATS_CALLS, REDIS_PUBSUB_CALLS, TRPC_CALLS, EVENTBRIDGE_CALLS.
1617
func (p *Pipeline) passCommunities() {
1718
slog.Info("pass.communities")
1819

@@ -32,6 +33,14 @@ func (p *Pipeline) passCommunities() {
3233
sqsEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "SQS_CALLS")
3334
snsEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "SNS_CALLS")
3435
pubsubEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "PUBSUB_CALLS")
36+
wsEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "WS_CALLS")
37+
sseEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "SSE_CALLS")
38+
amqpEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "AMQP_CALLS")
39+
mqttEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "MQTT_CALLS")
40+
natsEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "NATS_CALLS")
41+
redisPubSubEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "REDIS_PUBSUB_CALLS")
42+
trpcEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "TRPC_CALLS")
43+
eventBridgeEdges, _ := p.Store.FindEdgesByType(p.ProjectName, "EVENTBRIDGE_CALLS")
3544

3645
// Combine all edge types
3746
allEdges := append([]*store.Edge{}, callEdges...)
@@ -43,6 +52,14 @@ func (p *Pipeline) passCommunities() {
4352
allEdges = append(allEdges, sqsEdges...)
4453
allEdges = append(allEdges, snsEdges...)
4554
allEdges = append(allEdges, pubsubEdges...)
55+
allEdges = append(allEdges, wsEdges...)
56+
allEdges = append(allEdges, sseEdges...)
57+
allEdges = append(allEdges, amqpEdges...)
58+
allEdges = append(allEdges, mqttEdges...)
59+
allEdges = append(allEdges, natsEdges...)
60+
allEdges = append(allEdges, redisPubSubEdges...)
61+
allEdges = append(allEdges, trpcEdges...)
62+
allEdges = append(allEdges, eventBridgeEdges...)
4663

4764
if len(allEdges) == 0 {
4865
slog.Info("pass.communities.skip", "reason", "no_edges")
@@ -82,7 +99,15 @@ func (p *Pipeline) passCommunities() {
8299
"kafka", len(kafkaEdges),
83100
"sqs", len(sqsEdges),
84101
"sns", len(snsEdges),
85-
"pubsub", len(pubsubEdges))
102+
"pubsub", len(pubsubEdges),
103+
"websocket", len(wsEdges),
104+
"sse", len(sseEdges),
105+
"amqp", len(amqpEdges),
106+
"mqtt", len(mqttEdges),
107+
"nats", len(natsEdges),
108+
"redis_pubsub", len(redisPubSubEdges),
109+
"trpc", len(trpcEdges),
110+
"eventbridge", len(eventBridgeEdges))
86111
}
87112

88113
// louvainCommunities implements the Louvain algorithm for community detection.

0 commit comments

Comments
 (0)