OpenTelemetry Demo, mapped from the code.
The OpenTelemetry Astronomy Shop: a polyglot shop in Go, C#, C++, Java, Kotlin, TypeScript, Python, Ruby, PHP, Rust and Elixir, on gRPC, HTTP and Kafka behind an Envoy proxy, with an AI agent layer. Kuuhaku read its source at commit 7ea09b8, with no configuration and no hand-fixing, and found 23 services in C#, Java, Python, Go, Ruby, Kotlin, TypeScript, JavaScript, PHP and Rust, and 30 connections: 19 over HTTP, 9 over gRPC and 2 through queues. Each one below says how it was found.
Services
Every repo or deployable the scanner found, with how many connections leave it and reach it.
| Service | Stack | Role | Calls | Called by |
|---|---|---|---|---|
frontend | TypeScript · Next.js | service | 7 | 3 |
checkout | Go | consumer | 8 | 1 |
frontend_proxy | not read: Docker only | service | 6 | 2 |
agent | Python · FastAPI | service | 2 | 2 |
product_catalog | Go | service | 0 | 3 |
shipping | Rust · Actix | service | 1 | 2 |
cart | C# · ASP.NET Core | service | 0 | 2 |
chatbot | Python · FastAPI | consumer | 1 | 1 |
currency | not read: Docker only | service | 0 | 2 |
load_generator | Python · Flask | consumer | 1 | 1 |
mcp | Python · FastAPI | consumer | 1 | 1 |
recommendation | Python | service | 1 | 1 |
telemetry_docs | Python | service | 1 | 1 |
accounting | C# · .NET | service | 0 | 1 |
ad | Java | service | 0 | 1 |
email | Ruby · Sinatra | service | 0 | 1 |
flagd_ui | Docker | service | 0 | 1 |
fraud_detection | Kotlin | service | 0 | 1 |
image_provider | not read: Docker only | service | 0 | 1 |
payment | JavaScript · Node | service | 0 | 1 |
quote | PHP · Slim | service | 0 | 1 |
react_native_app | TypeScript · React | consumer | 1 | 0 |
kafka | not read: Docker only | service | 0 | 0 |
Connections
config: the address is written in configuration (an env file, a compose file, a proxy route) and the call is matched to it. inferred: the call's path matches a route of the target and nothing contradicts it. Call sites count the places in the code that make the call.
| From | To | Type | Evidence | Call sites | How it was found |
|---|---|---|---|---|---|
agent | frontend | HTTP | config | 10 | URL worked out at ../shared/tools.py:22 |
agent | mcp | HTTP | config | 1 | MCP_ENDPOINT = mcp in compose.agent.yaml |
chatbot | agent | HTTP | config | 1 | URL worked out at src/chat_interface/chat_interface.py:37 |
checkout | accounting | queue | inferred | 1 | topic orders: main.go:671 → accounting Consumer.cs:46; topic orders: main.go:671 → accounting Consumer.cs:31 |
checkout | cart | gRPC | config | 2 | CART_ADDR = cart:7070 in compose.yaml; CartService client in main.go, served in cart/services/CartService.cs |
checkout | currency | HTTP | config | 1 | CURRENCY_ADDR = currency:7001 in compose.yaml |
checkout | email | HTTP | inferred | 1 | its path matches a route of email, which this repo is wired to |
checkout | fraud_detection | queue | inferred | 1 | topic orders: main.go:671 → fraud_detection src/main/kotlin/frauddetection/main.kt:26 |
checkout | payment | gRPC | config | 3 | PAYMENT_ADDR = payment:50051 in compose.yaml; PaymentService client in main.go, served in payment/index.js |
checkout | product_catalog | gRPC | config | 2 | PRODUCT_CATALOG_ADDR = product-catalog:3550 in compose.yaml; ProductCatalogService client in main.go, served in product_catalog/main.go |
checkout | shipping | HTTP | inferred | 2 | its path matches a route of shipping, which this repo is wired to |
frontend | ad | gRPC | config | 3 | AD_ADDR = ad:9555 in compose.yaml; AdService client in gateways/rpc/Ad.gateway.ts, served in ad/src/main/java/oteldemo/AdService.java |
frontend | cart | gRPC | config | 3 | CART_ADDR = cart:7070 in compose.yaml; CartService client in gateways/rpc/Cart.gateway.ts, served in cart/services/CartService.cs |
frontend | checkout | gRPC | config | 3 | CHECKOUT_ADDR = checkout:5050 in compose.yaml; CheckoutService client in gateways/rpc/Checkout.gateway.ts, served in checkout/main.go |
frontend | currency | HTTP | config | 2 | CURRENCY_ADDR = currency:7001 in compose.yaml |
frontend | product_catalog | gRPC | config | 3 | PRODUCT_CATALOG_ADDR = product-catalog:3550 in compose.yaml; ProductCatalogService client in gateways/rpc/ProductCatalog.gateway.ts, served in product_catalog/main.go |
frontend | recommendation | gRPC | config | 3 | RECOMMENDATION_ADDR = recommendation:9001 in compose.yaml; RecommendationService client in gateways/rpc/Recommendations.gateway.ts, served in recommendation/recommendation_server.py |
frontend | shipping | HTTP | config | 2 | SHIPPING_ADDR = http://shipping:50050 in compose.yaml, read in the same file |
frontend_proxy | chatbot | HTTP | config | 1 | CHATBOT_HOST = chatbot in compose.yaml |
frontend_proxy | flagd_ui | HTTP | config | 1 | FLAGD_UI_HOST = flagd-ui in compose.yaml |
frontend_proxy | frontend | HTTP | config | 11 | URL worked out at envoy.tmpl.yaml, through the / route in envoy.tmpl.yaml |
frontend_proxy | image_provider | HTTP | config | 1 | IMAGE_PROVIDER_HOST = image-provider in compose.yaml |
frontend_proxy | load_generator | HTTP | config | 1 | LOCUST_WEB_HOST = load-generator in compose.yaml |
frontend_proxy | telemetry_docs | HTTP | config | 1 | TELEMETRY_DOCS_HOST = telemetry-docs in compose.yaml |
load_generator | agent | HTTP | config | 11 | AGENT_ENDPOINT = agent in compose.agent.yaml, read in the same file |
mcp | frontend | HTTP | config | 10 | URL worked out at ../shared/tools.py:22 |
react_native_app | frontend_proxy | HTTP | config | 10 | http://localhost in utils/Settings.ts, 10 call paths match its routes |
recommendation | product_catalog | gRPC | config | 2 | PRODUCT_CATALOG_ADDR = product-catalog:3550 in compose.yaml; ProductCatalogService client in recommendation_server.py, served in product_catalog/main.go |
shipping | quote | HTTP | config | 1 | URL worked out at src/shipping_service/quote.rs:68 |
telemetry_docs | frontend_proxy | HTTP | config | 1 | http://localhost in mkdocs.yml |
Left for a person
10 items the scanner would not settle on its own and put in the review inbox instead of guessing, for example:
requestinfrontend: base URL assigned at runtime (gateways/Api.gateway.ts:17)fetchinreact_native_app: base URL assigned at runtime (utils/Request.ts:28)POST /get-quoteinfrontend_proxy: no route in frontend defines it (envoy.tmpl.yaml)GET /inload_generator: no route in agent defines it (locustfile.py:153)GET /api/products/*inload_generator: no route in agent defines it (locustfile.py:160)GET /api/recommendationsinload_generator: no route in agent defines it (locustfile.py:170)
Other systems
- Train Ticket: 48 services, 140 connections
- Online Boutique: 12 services, 16 connections
- eShop: 19 services, 31 connections
- Robot Shop: 9 services, 12 connections
- Sock Shop: 8 services, 7 connections
- Bank of Anthos: 8 services, 9 connections
- Quarkus Super Heroes: 7 services, 6 connections
- Spring PetClinic Microservices: 7 services, 14 connections
Your own system
Run npx kuuhaku scan . in a folder of repos: it reads the code on your machine and only the graph leaves it. Or connect GitHub or Bitbucket and let the hosted runner scan with a read-only key. The map, a page per repo, a review inbox and a check on every pull request follow. Start free.