Problem
frontend/src/features/network/services/networkService.ts contains a hardcoded PORT_SERVICE_MAP with 27 entries mapping "port/PROTOCOL" strings to semantic NodeType values (dns-server, web-server, ssh-server, etc.). This approach has two fundamental limitations:
- Services on non-standard ports (e.g. HTTPS on 8444, SSH on 2222) are never classified correctly.
- Encrypted flows (TLS/QUIC) carrying web traffic over arbitrary ports are classified as
unknown.
Additionally, NodeDetails.tsx has a parallel NODE_TYPE_DISPLAY map (11 entries) and constants.ts has NODE_TYPE_COLORS (11 entries) — three maps covering the same NodeType values that must be kept in sync manually.
Research findings
Surveyed npm packages (port-numbers, services-db, service-names-port-numbers) all map port numbers to IANA service names (e.g. "https", "domain") — not to the application-semantic NodeType labels the graph needs. A mapping layer from IANA name to NodeType would still be required, so no package removes the custom logic.
Better alternative: use nDPI appName as the primary signal. The appName field is already populated per-conversation by the backend nDPI service and is available on every Conversation in the graph. nDPI identifies:
- TLS/QUIC →
"TLS" / "QUIC" (web traffic on any port)
"DNS", "SSH", "FTP_CONTROL", "SMTP", "IMAP", "POP", "DHCP", "NTP"
"MySQL", "PostgreSQL", "Redis", "MongoDB", "Elasticsearch"
This is more accurate than port-based guessing, especially for encrypted flows.
Proposed Solution
1. Add nDPI app name → NodeType lookup as primary classifier
Add a NDPI_APP_MAP alongside PORT_SERVICE_MAP. In buildNetworkGraph, accumulate the set of distinct appName values seen on inbound edges to each node. In classifyNodeType, try NDPI_APP_MAP first, then fall back to PORT_SERVICE_MAP (for captures where nDPI / ndpiReader is unavailable and appName is null).
const NDPI_APP_MAP: Partial<Record<string, NodeType>> = {
'DNS': 'dns-server',
'HTTP': 'web-server',
'TLS': 'web-server',
'QUIC': 'web-server',
'SSH': 'ssh-server',
'FTP_CONTROL': 'ftp-server',
'FTP_DATA': 'ftp-server',
'SMTP': 'mail-server',
'SMTPTLS': 'mail-server',
'IMAP': 'mail-server',
'POP': 'mail-server',
'DHCP': 'dhcp-server',
'NTP': 'ntp-server',
'MySQL': 'database-server',
'PostgreSQL': 'database-server',
'Redis': 'database-server',
'MongoDB': 'database-server',
'Elasticsearch':'database-server',
};
2. Consolidate the three parallel node-type maps
PORT_SERVICE_MAP, NODE_TYPE_COLORS, and NODE_TYPE_DISPLAY all key on NodeType. Replace with a single NODE_TYPE_CONFIG object:
export const NODE_TYPE_CONFIG: Record<NodeType, {
label: string;
icon: string;
badgeClass: string;
color: string;
}> = {
'dns-server': { label: 'DNS Server', icon: 'bi-globe2', badgeClass: 'bg-warning text-dark', color: '#f39c12' },
'web-server': { label: 'Web Server', icon: 'bi-server', badgeClass: 'bg-success', color: '#2ecc71' },
'ssh-server': { label: 'SSH Server', icon: 'bi-terminal', badgeClass: 'bg-info text-dark', color: '#1abc9c' },
// ...all 11 types
};
This means adding a new node type requires touching exactly one place.
Files to Change
frontend/src/features/network/services/networkService.ts
frontend/src/features/network/constants.ts
frontend/src/components/network/NodeDetails/NodeDetails.tsx
Acceptance Criteria
Problem
frontend/src/features/network/services/networkService.tscontains a hardcodedPORT_SERVICE_MAPwith 27 entries mapping"port/PROTOCOL"strings to semanticNodeTypevalues (dns-server,web-server,ssh-server, etc.). This approach has two fundamental limitations:unknown.Additionally,
NodeDetails.tsxhas a parallelNODE_TYPE_DISPLAYmap (11 entries) andconstants.tshasNODE_TYPE_COLORS(11 entries) — three maps covering the sameNodeTypevalues that must be kept in sync manually.Research findings
Surveyed npm packages (
port-numbers,services-db,service-names-port-numbers) all map port numbers to IANA service names (e.g."https","domain") — not to the application-semanticNodeTypelabels the graph needs. A mapping layer from IANA name toNodeTypewould still be required, so no package removes the custom logic.Better alternative: use nDPI
appNameas the primary signal. TheappNamefield is already populated per-conversation by the backend nDPI service and is available on everyConversationin the graph. nDPI identifies:"TLS"/"QUIC"(web traffic on any port)"DNS","SSH","FTP_CONTROL","SMTP","IMAP","POP","DHCP","NTP""MySQL","PostgreSQL","Redis","MongoDB","Elasticsearch"This is more accurate than port-based guessing, especially for encrypted flows.
Proposed Solution
1. Add nDPI app name → NodeType lookup as primary classifier
Add a
NDPI_APP_MAPalongsidePORT_SERVICE_MAP. InbuildNetworkGraph, accumulate the set of distinctappNamevalues seen on inbound edges to each node. InclassifyNodeType, tryNDPI_APP_MAPfirst, then fall back toPORT_SERVICE_MAP(for captures where nDPI /ndpiReaderis unavailable andappNameis null).2. Consolidate the three parallel node-type maps
PORT_SERVICE_MAP,NODE_TYPE_COLORS, andNODE_TYPE_DISPLAYall key onNodeType. Replace with a singleNODE_TYPE_CONFIGobject:This means adding a new node type requires touching exactly one place.
Files to Change
frontend/src/features/network/services/networkService.tsfrontend/src/features/network/constants.tsfrontend/src/components/network/NodeDetails/NodeDetails.tsxAcceptance Criteria
PORT_SERVICE_MAPkept as a port-based fallback;NDPI_APP_MAPadded as primary classifierappNamevalues accumulated per graph node duringbuildNetworkGraphappNameis availableNODE_TYPE_COLORSandNODE_TYPE_DISPLAYmerged into a singleNODE_TYPE_CONFIGappNameis null