CI Relationship System Documentation
Overview
The CI Relationship System in NopeSight v3 provides intelligent mapping of network connections between Configuration Items (CIs) to automatically discover and maintain service dependencies. The system uses a bidirectional relationship architecture that ensures data consistency and accurate direction determination.
Core Concepts
Bidirectional Relationships
Unlike traditional systems that create separate relationship records from each CI's perspective, NopeSight uses a single relationship object for each connection pair. This approach:
- Prevents duplicate relationships between the same CIs
- Maintains consistency across multiple scans
- Reduces data redundancy in the CMDB
- Provides dual perspectives through sourceView and targetView attributes
Direction Determination
The system uses a hierarchical approach to determine the correct direction of relationships:
- ListeningService Registry (Highest Priority)
- Connection State Analysis (LISTEN state)
- Well-Known Port Detection
- Port Range Classification (Ephemeral vs Registered)
- Deterministic Fallback (Lower port number)
- Alphabetical Ordering (Last resort)
Architecture
Data Model
CIRelationship {
source: ObjectId, // CI initiating connection
target: ObjectId, // CI receiving connection
sourceType: String, // 'CI' or 'UnknownDevice'
targetType: String, // 'CI' or 'UnknownDevice'
sourceIdentifier: String, // For unknown devices (IP:port)
targetIdentifier: String, // For unknown devices (IP:port)
type: ObjectId, // Relationship type (Connected To)
attributes: Map {
sourceView: { // Source CI's perspective
process: String,
pid: Number,
localPorts: [Number],
remotePorts: [Number],
connectionCount: Number,
lastUpdated: Date
},
targetView: { // Target CI's perspective
process: String,
pid: Number,
localPorts: [Number],
remotePorts: [Number],
connectionCount: Number,
lastUpdated: Date
},
servicePorts: [Number], // Service ports involved
protocol: String, // TCP/UDP
confidence: String, // high/medium/low/guess
directionReason: String, // Explanation of direction
bidirectional: Boolean, // For peer-to-peer connections
lastUpdatedFrom: String // Last CI that updated this
}
}
Processing Flow
Direction Determination Algorithm
1. ListeningService Check
The system first checks if either port is registered as a listening service:
// SQL Server custom ports example
ListeningService {
name: "SQL Server Custom Instance",
ports: [
{ port: 1440, protocol: "TCP" },
{ port: 1488, protocol: "TCP" }
],
tenant: ObjectId("..."),
isActive: true
}
Result: CI with listening port becomes the target (receiver)
2. Connection State Analysis
For connections with state information:
- State = 2 (LISTEN): CI is the target
- State = 5 (ESTABLISHED): Further analysis needed
- State = 11 (TIME_WAIT): CI was likely the source
3. Well-Known Ports
Common service ports indicate the server side:
const WELL_KNOWN_PORTS = [
21, // FTP
22, // SSH
80, // HTTP
443, // HTTPS
1433, // SQL Server
1521, // Oracle
3306, // MySQL
5432, // PostgreSQL
// ... etc
]
Result: CI with well-known port becomes the target
4. Port Range Classification
- Registered Ports (1024-49151): Likely services
- Ephemeral Ports (49152-65535): Client connections
Example:
- CI1 using port 50123 (ephemeral) → CI2 using port 8080 (registered)
- Direction: CI1 is source, CI2 is target
5. Non-Listener Connections
For peer-to-peer or RPC connections where neither side is listening:
// Example: RPC connection
CI1: Port 48765 → CI2: Port 49123
Both ephemeral, no listener detected
// Fallback rules applied:
1. Lower port number (48765) assumed to be service
2. CI2 becomes source, CI1 becomes target
3. Confidence marked as "low" or "guess"
Implementation Details
Key Functions
determineSourceAndTarget(ci1, ci2, conn1, conn2, tenant)
Main direction determination logic implementing the hierarchical rules.
Parameters:
ci1: First CI objectci2: Second CI object (can be null)conn1: Connection data from ci1's perspectiveconn2: Connection data from ci2's perspective (optional)tenant: Tenant ID for service lookup
Returns:
{
source: CI, // Source CI object
target: CI, // Target CI object
confidence: String, // 'high'|'medium'|'low'|'guess'
reason: String, // Explanation
bidirectional: Boolean // True for peer connections
}
findOrCreateRelationship(sourceCI, targetCI, type, identifier)
Finds existing relationship or creates new one, ensuring single record per connection pair.
Key Logic:
- Searches for relationship in both directions
- Corrects direction if existing relationship is reversed
- Handles unknown devices with identifiers
updateRelationshipView(relationship, scanningCI, connectionData, isSource, appInfo)
Updates the appropriate view (sourceView or targetView) based on scanning CI's role.
Features:
- Aggregates multiple connections to same endpoint
- Preserves historical data (ports, processes)
- Maintains connection counts
- Stores recent connection details (last 100)
- Enriches with application information
- Adds software family metadata
Connection History:
connections: [
{
localAddress: "10.1.1.50",
remoteAddress: "10.1.1.100",
localPort: 52456,
remotePort: 1440,
protocol: "TCP",
state: 5, // ESTABLISHED
process: "java.exe",
pid: 12345
},
// ... up to 100 recent connections
]
Unknown Device Handling
When a remote CI cannot be found:
// Unknown device representation
{
sourceType: 'UnknownDevice',
sourceIdentifier: '192.168.1.100:3306', // IP:port format
target: knownCI._id
}
This allows tracking connections to:
- External services
- Unscanned devices
- Temporary connections
- Cloud services
Real-World Scenarios
Scenario 1: Database Connection
Application Server (KTPRDISOMS) → Database Server (KCTSDB01)
Port 52456 → Port 1440 (SQL Server)
Analysis:
1. Port 1440 found in ListeningService
2. KCTSDB01 identified as target
3. Relationship: KTPRDISOMS (source) → KCTSDB01 (target)
4. Confidence: high
Scenario 2: Web Service
Load Balancer → Web Server
Port 54321 → Port 443 (HTTPS)
Analysis:
1. Port 443 is well-known HTTPS port
2. Web Server identified as target
3. Relationship: Load Balancer (source) → Web Server (target)
4. Confidence: high
Scenario 3: RPC/Peer Connection
Service A ↔ Service B
Port 45123 ↔ Port 45678 (both RPC)
Analysis:
1. No listening service detected
2. Both ports in registered range
3. Lower port (45123) assumed as service
4. Relationship: Service B (source) → Service A (target)
5. Confidence: guess
6. Marked as bidirectional
Scenario 4: Unknown External Service
Internal Server → External API
Port 53421 → 34.102.136.180:443
Analysis:
1. External IP not in CMDB
2. Port 443 indicates HTTPS service
3. Creates UnknownDevice target
4. Relationship: Internal Server (source) → Unknown:34.102.136.180:443 (target)
5. Confidence: medium
Configuration
ListeningService Management
Add custom service ports for accurate direction detection:
// Via API or database
POST /api/listening-services
{
"name": "Custom Application",
"ports": [
{ "port": 9090, "protocol": "TCP" }
],
"tenant": "tenant-id",
"isActive": true
}
Relationship Type Configuration
The system uses "Connected To" relationship type by default:
{
name: "Connected To",
description: "Network connection between CIs",
category: "Dependency",
isActive: true
}
Troubleshooting
Common Issues
1. Incorrect Relationship Direction
Symptom: Database showing as source instead of target
Solution:
- Verify port is in ListeningService registry
- Check connection state values
- Review confidence level in relationship attributes
2. Duplicate Relationships
Symptom: Multiple relationships between same CIs
Solution:
- Run cleanup script to merge duplicates
- Verify both CIs update same relationship object
- Check tenant isolation isn't creating separate records
3. Unknown Device Proliferation
Symptom: Many UnknownDevice entries
Solution:
- Ensure network discovery covers all devices
- Check IP resolution in ServerNetworkAdapter
- Verify tenant boundaries aren't blocking CI discovery
Debugging
Enable detailed logging:
// In cirelation_processor.js
logger.debug(`Direction determined: ${direction.reason}`, {
source: direction.source?.name,
target: direction.target?.name,
confidence: direction.confidence
});
Check relationship attributes for direction reasoning:
db.cirelationships.findOne({ _id: relationshipId })
// Review attributes.directionReason
// Check attributes.confidence
Migration from Previous Version
Identifying Duplicates
// MongoDB query to find potential duplicates
db.cirelationships.aggregate([
{
$group: {
_id: {
ci1: { $cond: [{ $lt: ["$source", "$target"] }, "$source", "$target"] },
ci2: { $cond: [{ $lt: ["$source", "$target"] }, "$target", "$source"] }
},
count: { $sum: 1 },
relationships: { $push: "$_id" }
}
},
{ $match: { count: { $gt: 1 } } }
])
Cleanup Script
// Merge duplicate relationships
async function cleanupDuplicateRelationships() {
const duplicates = await findDuplicates();
for (const dup of duplicates) {
const [keep, ...remove] = dup.relationships;
// Merge views from duplicates
await mergeRelationshipViews(keep, remove);
// Delete duplicates
await CIRelationship.deleteMany({
_id: { $in: remove }
});
}
}
Best Practices
1. Service Registration
Always register custom service ports in ListeningService:
- Improves direction accuracy
- Reduces "guess" confidence relationships
- Helps with service discovery
2. Scan Frequency
Regular scanning ensures:
- Both sides update relationship views
- Connection state changes are captured
- Unknown devices get identified when scanned
3. Tenant Isolation
Maintain proper tenant boundaries:
- Services should be in same tenant as their dependencies
- Cross-tenant relationships need special handling
- Global services can use null tenant
4. Performance Optimization
Caching Strategy
Port Cache:
- 5-minute TTL for well-known ports
- Tenant-specific cache entries
- Reduces ListeningService queries
Application Cache (recommended):
// Implement in-memory cache for frequent lookups
const APP_CACHE = new Map();
const CACHE_TTL = 10 * 60 * 1000; // 10 minutes
Processing Optimizations
-
Skip Rule Processing:
relationship._skipRuleProcessing = true;Bypasses unnecessary post-save hooks
-
Connection Aggregation:
- Groups by remote address
- Finds most common port combinations
- Reduces duplicate processing
-
Batch Operations:
- Process multiple CIs in parallel
- Use MongoDB bulk operations for cleanup
-
Query Optimization:
// Indexes needed:
db.cirelationships.createIndex({ source: 1, target: 1 })
db.cirelationships.createIndex({ "attributes.servicePorts": 1 })
db.listeningservices.createIndex({ "ports.port": 1, tenant: 1 })
Statistics and Monitoring
{
relationshipsCreated: 0,
relationshipsUpdated: 0,
unknownDevicesCreated: 0,
unknownDevicesUpdated: 0,
errors: 0,
duplicatesFound: 0,
duplicatesMerged: 0,
skippedConnections: 0
}
Use these metrics to:
- Monitor processing efficiency
- Identify data quality issues
- Track unknown device discovery
- Measure deduplication effectiveness
API Reference
Relationship Query API
// Get all relationships for a CI
GET /api/ci-relationships?ci=<ci-id>
// Get relationships by direction
GET /api/ci-relationships?source=<ci-id>
GET /api/ci-relationships?target=<ci-id>
// Get relationships with confidence filter
GET /api/ci-relationships?ci=<ci-id>&confidence=high
Response Format
{
"_id": "relationship-id",
"source": {
"_id": "ci-id",
"name": "Server-A"
},
"target": {
"_id": "ci-id",
"name": "Server-B"
},
"type": {
"name": "Connected To"
},
"attributes": {
"servicePorts": [443],
"protocol": "TCP",
"confidence": "high",
"directionReason": "Well-known port: 443 on ci2",
"sourceView": {
"localPorts": [54321],
"remotePorts": [443],
"connectionCount": 5
},
"targetView": {
"localPorts": [443],
"remotePorts": [54321],
"connectionCount": 5
}
}
}
Future Enhancements
Planned Features
-
Machine Learning Direction Detection
- Train model on confirmed relationships
- Improve accuracy for ambiguous connections
- Pattern recognition for complex services
-
Service Discovery Enhancement
- Automatic service type detection
- Protocol analysis beyond port numbers
- Application fingerprinting
-
Relationship Lifecycle Management
- Automatic cleanup of stale relationships
- Historical relationship tracking
- Change impact analysis
-
Cross-Tenant Relationships
- Secure cross-tenant dependency mapping
- Service provider/consumer models
- Multi-tenant service catalogs