Network Flow Discovery
Network Flow Discovery automatically builds a graph of service-to-service, service-to-database, and service-to-external flows by reading Kubernetes NetworkPolicies and GKE FQDNNetworkPolicies.
How it works
- A NetworkFlowDiscovery Custom Resource is created, linked to a Portal via
spec.portalRef(auto-created at startup for the main portal) - The controller periodically reconciles (every 60s) and:
- Lists all
NetworkPolicyresources (ingress rules → service-to-service flows) - Lists all
FQDNNetworkPolicyresources (egress rules → service-to-database/external flows) - Builds a graph of nodes and edges
- Stores nodes in a FlowNodeSet CR and edges in a FlowEdgeSet CR (both owned by the parent)
- Lists all
- The web UI and MCP server read from FlowNodeSet/FlowEdgeSet — no cluster queries at request time
CRDs
NetworkFlowDiscovery
The parent resource that drives reconciliation.
apiVersion: sreportal.io/v1alpha1
kind: NetworkFlowDiscovery
metadata:
name: netflow-main
namespace: sreportal-system
spec:
portalRef: main
# Optional: restrict to specific namespaces (default: all)
namespaces: []Status fields:
| Field | Description |
|---|---|
status.nodeCount | Number of discovered nodes |
status.edgeCount | Number of discovered edges |
status.lastReconcileTime | When the graph was last rebuilt |
status.conditions | Ready condition indicating reconciliation success |
FlowNodeSet
Stores all discovered nodes. Auto-created and owned by the parent NetworkFlowDiscovery.
- Named
<parent>-nodes(e.g.netflow-main-nodes) spec.discoveryRefreferences the parentstatus.nodes[]contains the discovered nodes
Each node has the following fields:
| Field | Description | Example |
|---|---|---|
id | Unique identifier | service:namespace-a:service-1 |
label | Human-readable name | service-1 |
namespace | Kubernetes namespace | namespace-a |
nodeType | Classification (see below) | service |
group | Logical group (namespace) | namespace-a |
FlowEdgeSet
Stores all discovered edges. Auto-created and owned by the parent NetworkFlowDiscovery.
- Named
<parent>-edges(e.g.netflow-main-edges) spec.discoveryRefreferences the parentstatus.edges[]contains the discovered edges
Each edge has the following fields:
| Field | Description | Example |
|---|---|---|
from | Source node id | service:namespace-a:service-1 |
to | Target node id | service:namespace-b:service-2 |
edgeType | Flow type (see below) | cross-ns |
used | Whether traffic has been observed on this edge | true |
Deleting the parent NetworkFlowDiscovery automatically garbage-collects both children.
Node types
Nodes are classified by port:
| Type | Rule |
|---|---|
service | Pods referenced in NetworkPolicy ingress rules (app.kubernetes.io/name label) |
cron | CronJobs referenced in NetworkPolicy ingress rules (basename label) |
database | FQDNs on ports 5432, 1433, 3306 |
messaging | FQDNs on ports 5672, 5671 |
external | All other FQDNs in egress rules |
Edge types
| Type | Meaning |
|---|---|
internal | Flow between services in the same namespace |
cross-ns | Flow between services in different namespaces |
cron | Flow from a cron job to a service |
database | Flow from a service to a database |
messaging | Flow from a service to a messaging broker |
external | Flow from a service to an external endpoint |
Web UI
The Network Policies page (/:portalName/netpol) is organized into three tabs:
Flow Matrix
Lists every service with its outgoing and incoming flows. Features:
- Collapsible namespace and service sections
- Search filter and namespace dropdown
- Copy as Markdown for documentation/audit export

Cross-Namespace
Aggregated matrix showing flow counts between namespaces. Useful for understanding inter-team dependencies.

Flow Explorer
Select any resource (database, service, external endpoint) to see its direct incoming and outgoing flows in a butterfly layout:
- Left column: services that call the selected resource (incoming), grouped by namespace in collapsible sections
- Center: the selected resource with incoming/outgoing counts
- Right column: services/resources that the selected resource calls (outgoing), grouped by namespace in collapsible sections
Each flow displays a used indicator (green check or red cross) showing whether traffic has been observed. This flag is populated by an optional flow observer (Hubble gRPC or Prometheus) when available in the cluster.
Click on any connected service to navigate to it and explore its own flows — this allows depth-first exploration without needing a multi-level BFS view.

MCP Server
Available at /mcp/netpol with two tools:
| Tool | Description | Parameters |
|---|---|---|
list_network_flows | List all nodes and edges in the flow graph | search |
get_service_flows | Get incoming/outgoing flows for a specific service | service (required) |
Kubernetes API calls
The controller makes exactly two LIST calls per reconciliation cycle (every 60s):
GET /apis/networking.k8s.io/v1/networkpoliciesGET /apis/networking.gke.io/v1alpha3/fqdnnetworkpolicies(silently skipped if CRD is absent)
The web UI and MCP server read only from FlowNodeSet/FlowEdgeSet CRDs — zero cluster queries at request time.