Device Identity Reconciliation Engine (DIRE) #2481

Merged
mfreeman451 merged 6 commits from refs/pull/2481/head into main 2025-11-27 05:30:03 +00:00
mfreeman451 commented 2025-11-27 04:53:09 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2022
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2022
Original created: 2025-11-27T04:53:09Z
Original updated: 2025-11-27T05:30:07Z
Original head: carverauto/serviceradar:core/device_count_creeping
Original base: main
Original merged: 2025-11-27T05:30:03Z by @mfreeman451

User description

IMPORTANT: Please sign the Developer Certificate of Origin

Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include
a DCO sign-off statement indicating the DCO acceptance in one commit message. Here
is an example DCO Signed-off-by line in a commit message:

Signed-off-by: J. Doe <j.doe@domain.com>

Describe your changes

Code checklist before requesting a review

  • I have signed the DCO?
  • The build completes without errors?
  • All tests are passing when running make test?

PR Type

Enhancement, Tests, Bug fix, Documentation


Description

  • Implements comprehensive Identity Reconciliation Engine (IRE) to address device duplication from IP churn through network sightings lifecycle management

  • Adds policy-driven promotion pipeline with confidence tiers: Tier 3 sightings → Tier 2 promotions → Tier 1 anchored devices

  • Introduces database schema for network_sightings, device_identifiers, fingerprints, subnet_policies, and audit tables with proper indexing and constraints

  • Implements core registry reconciliation with sighting ingestion, TTL management, and promotion eligibility evaluation based on policy gates

  • Adds background reapers for expiring stale sightings and cleaning up in-memory registry state

  • Provides comprehensive REST API endpoints for sighting management, promotion, dismissal, and audit trail queries

  • Implements OpenTelemetry metrics tracking promotion statistics (promoted, eligible, shadow-ready, blocked counts)

  • Adds frontend dashboard (SightingsDashboard) with filtering, pagination, promotion actions, and merge history visualization

  • Integrates promotion lineage visibility into device detail view showing sighting origin and audit events

  • Includes Prometheus alerting rules for monitoring promotion pipeline health and reconciliation staleness

  • Fixes SQL query formatting issues in hydrate.go with proper const keyword positioning and table reference corrections

  • Adds comprehensive documentation covering requirements, design, metrics, and alerting guidance

  • Updates Helm configuration with reaper settings, identity reconciliation profiles, and faker simulation parameters


Diagram Walkthrough

flowchart LR
  A["Network Sightings<br/>Ingestion"] -->|"Filter & Store"| B["Sighting<br/>Storage"]
  B -->|"TTL Check"| C["Reaper<br/>Expiration"]
  B -->|"Promotion<br/>Evaluation"| D["Policy Gates<br/>Assessment"]
  D -->|"Eligible"| E["Promote to<br/>Unified Device"]
  D -->|"Blocked"| F["Shadow Mode<br/>Backlog"]
  E -->|"Record"| G["Audit Trail<br/>& Metrics"]
  F -->|"Monitor"| G
  H["API Endpoints"] -->|"Query/Action"| B
  H -->|"Trigger"| D
  I["Frontend<br/>Dashboard"] -->|"Display"| H
  J["Device Detail"] -->|"Show Lineage"| G

File Walkthrough

Relevant files
Enhancement
33 files
registry.go
Identity reconciliation engine with sighting promotion pipeline

pkg/registry/registry.go

  • Added identityCfg and reconcileInterval fields to DeviceRegistry
    struct for identity reconciliation configuration
  • Implemented WithIdentityReconciliationConfig() and
    WithReconcileInterval() option functions for registry initialization
  • Added sighting ingestion logic in ProcessBatchDeviceUpdates() to
    filter and store network sightings when identity reconciliation is
    enabled
  • Implemented ReconcileSightings() method to promote eligible sightings
    to unified devices based on policy gates
  • Added helper functions: ingestSightings(), resolveSightingTTL(),
    copySightingMetadata(), buildUpdateFromNetworkSighting(),
    PromoteSighting(), DismissSighting(), ListSightingEvents(),
    ListSightings(), CountSightings(), and buildIdentifiersFromUpdate()
+525/-0 
cnpg_identity_reconciliation_queries.go
Database queries for sighting and promotion management     

pkg/db/cnpg_identity_reconciliation_queries.go

  • New file implementing database queries for identity reconciliation
    operations
  • Added SQL queries for listing promotable sightings, marking sightings
    promoted, listing active sightings, and counting sightings
  • Implemented functions: ListPromotableSightings(),
    MarkSightingsPromoted(), ListActiveSightings(),
    CountActiveSightings(), GetNetworkSighting(), UpdateSightingStatus(),
    ListSightingEvents(), ListSubnetPolicies(), and ListMergeAuditEvents()
  • All functions include proper error handling and CNPG read/write
    availability checks
+525/-0 
cnpg_identity_reconciliation_upserts.go
Database upsert operations for identifiers and events       

pkg/db/cnpg_identity_reconciliation_upserts.go

  • New file implementing database upsert operations for identity
    reconciliation
  • Added UpsertDeviceIdentifiers() to write device identifier rows with
    conflict resolution
  • Added InsertSightingEvents() to record audit events for sightings
  • Includes helper functions for argument building and validation with
    proper error handling
+231/-0 
cnpg_identity_reconciliation.go
Core sighting storage and expiration operations                   

pkg/db/cnpg_identity_reconciliation.go

  • New file for core identity reconciliation database operations
  • Implemented StoreNetworkSightings() to upsert active sightings with
    TTL management
  • Implemented ExpireNetworkSightings() to mark stale sightings as
    expired based on TTL
  • Includes helper functions for argument building and data validation
+178/-0 
identity.go
API endpoints for sighting management and reconciliation 

pkg/core/api/identity.go

  • New file implementing API handlers for identity reconciliation
    endpoints
  • Added handlers: handleListSightings(), handleReconcileSightings(),
    handlePromoteSighting(), handleDismissSighting(),
    handleSightingEvents(), handleListSubnetPolicies(), and
    handleMergeAuditHistory()
  • Includes query parameter parsing, authentication context extraction,
    and proper error responses
+248/-0 
identity_metrics.go
OpenTelemetry metrics for promotion reconciliation tracking

pkg/registry/identity_metrics.go

  • New file implementing OpenTelemetry metrics for identity promotion
    reconciliation
  • Defined 8 observable gauges tracking promotion statistics (promoted,
    eligible, shadow-ready, blocked, etc.)
  • Implemented recordIdentityPromotionMetrics() to update metrics with
    latest reconciliation run data
  • Uses atomic operations for thread-safe metric updates
+176/-0 
promotion_status.go
Sighting promotion eligibility evaluation logic                   

pkg/registry/promotion_status.go

  • New file implementing promotion eligibility evaluation logic
  • Implemented promotionStatusForSighting() to assess sighting readiness
    based on policy gates
  • Added helper functions: dedupeStrings() and formatDurationShort() for
    status formatting
  • Evaluates persistence windows, hostname/fingerprint requirements, and
    shadow mode status
+169/-0 
identity_reconciliation.go
Data models for identity reconciliation domain                     

pkg/models/identity_reconciliation.go

  • New file defining data models for identity reconciliation feature
  • Added types: NetworkSighting, DeviceIdentifier, SightingEvent,
    SubnetPolicy, MergeAuditEvent, and SightingPromotionStatus
  • Includes NetworkSightingStatus enum with states: active, promoted,
    expired, dismissed
+87/-0   
config.go
Configuration models for identity reconciliation and reaper

pkg/models/config.go

  • Added new configuration types: ReaperConfig, IdentityReaperProfile,
    IdentityReaperConfig, PromotionConfig, and FingerprintingConfig
  • Added IdentityReconciliationConfig to gate the identity reconciliation
    engine
  • Extended CoreServiceConfig with Reaper and Identity fields
  • Implemented custom JSON marshaling/unmarshaling for duration fields in
    reaper configuration
+88/-4   
identity_reaper.go
Background reaper for stale network sightings                       

pkg/core/identity_reaper.go

  • New file implementing the identity reconciliation reaper for expiring
    stale sightings
  • Implemented IdentityReaper struct with Start() and reap() methods
  • Periodically expires network sightings based on TTL and records expiry
    events
+86/-0   
reaper.go
Enhanced stale device reaper with registry cleanup             

pkg/core/reaper.go

  • Added registry field to StaleDeviceReaper struct
  • Updated NewStaleDeviceReaper() constructor to accept registry
    parameter
  • Added logic to call DeleteLocal() on registry when reaping stale
    devices
  • Updated reaper to clean up both database and in-memory registry state
+12/-2   
server.go
Server initialization for identity reconciliation pipeline

pkg/core/server.go

  • Updated device registry initialization to include identity
    reconciliation config and reconcile interval options
  • Modified stale device reaper initialization to use configurable
    interval and TTL from config
  • Added identity reconciliation reaper startup when identity feature is
    enabled
  • Implemented background goroutine for periodic sighting promotion
    reconciliation
+55/-4   
server.go
API server routes for identity reconciliation                       

pkg/core/api/server.go

  • Added WithIdentityConfig() option function to expose identity
    reconciliation config to API
  • Registered 7 new protected API routes for identity reconciliation
    endpoints
  • Routes include sightings listing, promotion, dismissal, events,
    policies, merge audit, and reconciliation trigger
+14/-0   
types.go
API server types for identity reconciliation support         

pkg/core/api/types.go

  • Added identityConfig field to APIServer struct
  • Extended DeviceRegistryService interface with 6 new methods for
    sighting operations and reconciliation
+7/-0     
interfaces.go
Database service interface extensions for identity reconciliation

pkg/db/interfaces.go

  • Extended Service interface with 11 new methods for identity
    reconciliation operations
  • Added methods for sighting storage, expiration, promotion, status
    updates, and audit queries
+14/-0   
interfaces.go
Registry manager interface extensions for sighting operations

pkg/registry/interfaces.go

  • Added DeleteLocal() method to Manager interface for in-memory device
    removal
  • Added 6 new methods for sighting operations: ReconcileSightings(),
    ListSightings(), CountSightings(), PromoteSighting(),
    DismissSighting(), and ListSightingEvents()
+22/-0   
unified_device.go
Added sighting discovery source type                                         

pkg/models/unified_device.go

  • Added DiscoverySourceSighting constant to discovery source enumeration
  • Reformatted discovery source constants for consistency
  • Updated field alignment comments in DeviceUpdate struct
+14/-13 
bootstrap.go
Bootstrap integration for identity config                               

pkg/core/bootstrap/bootstrap.go

  • Added identity configuration to API server options when present in
    core service config
+4/-0     
route.ts
Frontend API route for sighting events                                     

web/src/app/api/identity/sightings/[id]/events/route.ts

  • New Next.js API route handler for fetching sighting events
  • Implements GET endpoint that proxies to backend identity API
  • Includes query parameter forwarding, authentication header handling,
    and error responses
+75/-0   
route.ts
Frontend API route for sighting dismissal                               

web/src/app/api/identity/sightings/[id]/dismiss/route.ts

  • New Next.js API route handler for dismissing sightings
  • Implements POST endpoint that proxies to backend identity API
  • Includes request body parsing, authentication header handling, and
    error responses
+75/-0   
route.ts
Add sightings API endpoint with pagination                             

web/src/app/api/identity/sightings/route.ts

  • New API route handler for fetching network sightings with pagination
    support
  • Implements query parameter handling for partition, limit, and offset
  • Proxies requests to backend identity API with authentication headers
  • Includes error handling with detailed error responses
+77/-0   
route.ts
Add merge audit history API endpoint                                         

web/src/app/api/identity/merge-audit/route.ts

  • New API route for retrieving device merge audit history
  • Supports filtering by device_id and pagination with limit parameter
  • Proxies to backend merge-audit endpoint with proper authentication
  • Comprehensive error handling and response formatting
+73/-0   
route.ts
Add subnet policies API endpoint                                                 

web/src/app/api/identity/policies/route.ts

  • New API route for fetching subnet policies
  • Supports pagination via limit query parameter
  • Proxies to backend identity policies endpoint
  • Includes error handling and authentication
+68/-0   
identity.ts
Add identity reconciliation type definitions                         

web/src/types/identity.ts

  • Defines TypeScript interfaces for identity reconciliation domain
  • Includes NetworkSighting, SightingEvent, SubnetPolicy, and
    MergeAuditEvent types
  • Defines response wrapper types for API endpoints
  • Includes promotion status and configuration metadata types
+89/-0   
route.ts
Add sighting promotion API endpoint                                           

web/src/app/api/identity/sightings/[id]/promote/route.ts

  • New API route for promoting a sighting to a device
  • Accepts sighting ID as dynamic route parameter
  • Implements POST handler to trigger promotion via backend
  • Includes validation, error handling, and authentication
+64/-0   
route.ts
Add reconciliation trigger API endpoint                                   

web/src/app/api/identity/reconcile/route.ts

  • New API route to trigger identity reconciliation process
  • Implements POST handler that proxies to backend reconciliation
    endpoint
  • Includes authentication and error handling
  • Returns reconciliation result to caller
+55/-0   
SightingsDashboard.tsx
Add comprehensive sightings management dashboard UI           

web/src/components/Identity/SightingsDashboard.tsx

  • Large new React component for managing network sightings and device
    promotion
  • Implements sightings list with filtering, pagination, and detailed
    view panel
  • Provides promote/dismiss actions with optional reason tracking
  • Includes audit trail viewing, merge history, subnet policies, and
    historical lookup features
  • Displays promotion eligibility status and policy blockers with
    explanatory hints
+1624/-0
DeviceDetail.tsx
Add promotion lineage visibility to device detail               

web/src/components/Devices/DeviceDetail.tsx

  • Adds import for Info icon and SightingEvent type
  • Refactors metadata extraction into memoized hooks (metadataFlag,
    metadataValue)
  • Adds state for tracking promotion events and loading/error states
  • Implements fetching of promotion audit trail when device has promotion
    sighting ID
  • Adds new "Promotion lineage" section displaying sighting origin,
    promotion mode, and audit events
  • Includes link to view sighting in sightings dashboard
+207/-44
00000000000009_identity_reconciliation_schema.up.sql
Add identity reconciliation database schema                           

pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql

  • Creates database schema for identity reconciliation system
  • Defines tables for subnet_policies, fingerprints, network_sightings,
    device_identifiers
  • Adds audit tables for sighting_events and merge_audit with proper
    indexing
  • Includes foreign key relationships and unique constraints for data
    integrity
+101/-0 
page.tsx
New Identity & Sightings dashboard page component               

web/src/app/identity/page.tsx

  • Creates new Next.js page component for Identity & Sightings dashboard
    with Apache 2.0 license header
  • Implements server-side rendering with noStore() to disable caching for
    real-time sighting data
  • Extracts search parameters for sighting, history_actor, and
    history_partition filters
  • Renders SightingsDashboard component with Suspense fallback and passes
    filter parameters
+61/-0   
BUILD.bazel
Add identity reconciliation database layer files                 

pkg/db/BUILD.bazel

  • Adds three new database files for identity reconciliation:
    cnpg_identity_reconciliation.go,
    cnpg_identity_reconciliation_queries.go, and
    cnpg_identity_reconciliation_upserts.go
  • Implements database layer for sightings, identifiers, and
    reconciliation operations
+3/-0     
Sidebar.tsx
Add Identity navigation to sidebar menu                                   

web/src/components/Sidebar.tsx

  • Imports Fingerprint icon from lucide-react library
  • Adds new navigation item for Identity page with /identity route and
    Fingerprint icon
  • Inserts Identity navigation between Devices and Network menu items
+2/-0     
BUILD.bazel
Add identity reaper implementation file                                   

pkg/core/BUILD.bazel

  • Adds identity_reaper.go file to core package build configuration
  • Implements reaper component for managing sighting TTLs and
    low-confidence device cleanup per subnet policies
+1/-0     
Tests
4 files
mock_db.go
Mock implementations for identity reconciliation database methods

pkg/db/mock_db.go

  • Added mock implementations for 16 new database methods related to
    identity reconciliation
  • Includes mocks for sighting operations, device identifiers, events,
    and audit history
  • All mocks follow existing patterns with proper call recording and type
    assertions
+178/-0 
promotion_status_test.go
Unit tests for promotion status evaluation                             

pkg/registry/promotion_status_test.go

  • New test file with 5 test cases for promotion status evaluation
  • Tests cover disabled state, persistence blocking, auto-promotion
    disabled, shadow mode, and eligible scenarios
  • Includes helper function containsHint() for assertion validation
+123/-0 
reaper_test.go
Updated reaper tests for registry cleanup                               

pkg/core/reaper_test.go

  • Updated test setup to include mock registry parameter for
    NewStaleDeviceReaper()
  • Added mock expectations for DeleteLocal() calls on registry during
    reaping
+6/-1     
mock_registry.go
Mock implementations for registry sighting operations       

pkg/registry/mock_registry.go

  • Added mock implementations for 9 new registry manager methods
  • Includes mocks for sighting operations, promotion, dismissal, and
    event listing
  • All mocks follow existing patterns with proper call recording
+100/-0 
Configuration changes
5 files
config.go
Default configuration values for identity reconciliation 

pkg/core/config.go

  • Added 10 default constants for identity reconciliation engine (IRE)
    configuration
  • Implemented applyIdentityDefaults() function to apply sensible
    defaults for identity reconciliation config
  • Integrated identity config normalization into normalizeConfig()
    function
+81/-0   
BUILD.bazel
Bazel build configuration update                                                 

pkg/core/api/BUILD.bazel

  • Added identity.go to the go_library sources list
+1/-0     
values.yaml
Update Helm values with identity and reaper config             

helm/serviceradar/values.yaml

  • Updates all service image tags from latest to specific commit SHA for
    reproducibility
  • Adds comprehensive core.reaper configuration for stale device cleanup
  • Adds core.identity configuration block with promotion, fingerprinting,
    and reaper profiles
  • Adds faker.simulation configuration for IP shuffle behavior
+57/-18 
serviceradar-config.yaml
Add identity reconciliation config to Helm template           

helm/serviceradar/files/serviceradar-config.yaml

  • Adds reaper configuration section with interval and TTL settings
  • Adds comprehensive identity_reconciliation configuration with
    promotion, fingerprinting, and reaper profiles
  • Configures per-subnet TTL profiles (default, dynamic, guest, static)
  • Parameterizes faker simulation settings for total devices and IP
    shuffle behavior
+43/-4   
kustomization.yaml
Add identity Prometheus rules to demo kustomization           

k8s/demo/base/kustomization.yaml

  • Adds reference to new identity-prometheus-rules.yaml resource file
  • Integrates identity reconciliation Prometheus rules into demo
    deployment
+1/-0     
Bug fix
1 files
hydrate.go
SQL query formatting and table reference fixes                     

pkg/registry/hydrate.go

  • Fixed SQL query formatting by moving const keyword to proper position
  • Corrected table reference from table(device_capability_registry) to
    device_capability_registry
+2/-2     
Documentation
6 files
spec.md
Add identity reconciliation requirements specification     

openspec/changes/add-identity-reconciliation-engine/specs/device-identity-reconciliation/spec.md

  • Documents requirements for network sightings lifecycle management
  • Specifies policy-driven promotion with TTL enforcement
  • Defines identifier indexing and strong-ID merge behavior
  • Includes reaper and auditability requirements with example scenarios
  • Modifies sightings UI/API requirement to include pagination and
    promotion context
+61/-0   
identity-alerts.md
Add identity reconciliation alert rules documentation       

docs/docs/identity-alerts.md

  • New documentation file with Prometheus alert rules for identity
    reconciliation
  • Defines alerts for promotion policy blocks, shadow backlog,
    reconciliation stalls, and volume surges
  • Includes alert expressions, severity levels, and remediation guidance
  • Provides dashboard suggestions for monitoring identity metrics
+63/-0   
design.md
Identity Reconciliation Engine design and architecture     

openspec/changes/add-identity-reconciliation-engine/design.md

  • Introduces comprehensive design for Identity & Reconciliation Engine
    (IRE) to address device duplication from IP churn
  • Defines data model with network_sightings, device_identifiers,
    fingerprints, subnet_policies, and audit tables
  • Establishes confidence tiers (Tier 3 sightings, Tier 2 promotions,
    Tier 1 anchored devices) with policy-driven promotion rules
  • Details migration plan with 7 phases from schema addition through
    legacy path removal, including shadow-mode validation
+26/-0   
tasks.md
Implementation tasks and deployment status for IRE             

openspec/changes/add-identity-reconciliation-engine/tasks.md

  • Tracks 11 implementation tasks for IRE with checkmarks indicating
    completion status (8 complete, 3 pending)
  • Completed tasks include data model, feature flags, ingestion updates,
    core engine, reaper, API/UI, and metrics
  • Pending tasks cover migration/backfill, comprehensive testing, and
    configuration API exposure
  • Documents deployment status with OCI image builds, pushes, and Helm
    deployment to demo environment
+17/-0   
identity-metrics.md
Identity reconciliation metrics and alerting guide             

docs/docs/identity-metrics.md

  • Documents 12 OpenTelemetry metrics emitted on serviceradar.identity
    meter for promotion monitoring
  • Provides alert recommendations for policy blocks, shadow backlog, run
    staleness, throughput dips, and volume surges
  • Includes dashboard panel guidance with single-stat gauges, time-series
    trends, and run age tracking
  • Specifies metric thresholds and durations for alerting (e.g.,
    15-minute staleness, 30-minute shadow backlog)
+27/-0   
proposal.md
IRE proposal and impact analysis                                                 

openspec/changes/add-identity-reconciliation-engine/proposal.md

  • Proposes addition of Identity & Reconciliation Engine to address
    device duplication from IP churn
  • Outlines key changes: network sighting lifecycle, identifier indexing,
    schema additions, API/UI surfaces, and metrics
  • Identifies affected components: registry device resolver,
    sweep/poller/sync ingestion, CNPG schema, Helm values, and background
    jobs
  • Justifies need for formal sighting treatment separate from durable
    devices with policy-driven promotion
+15/-0   
Monitoring
1 files
identity-prometheus-rules.yaml
Prometheus alerting rules for identity reconciliation       

k8s/demo/base/identity-prometheus-rules.yaml

  • Defines 5 PrometheusRule alerts for monitoring identity reconciliation
    health and promotion pipeline
  • Covers policy-blocked promotions, shadow mode backlog, stalled
    reconciliation, throughput drops, and volume surges
  • Includes severity levels (warning, critical, info) with descriptive
    annotations for operator guidance
  • Alerts trigger on specific metric thresholds and durations to catch
    reconciliation failures early
+50/-0   
Dependencies
1 files
BUILD.bazel
Add OpenTelemetry dependencies to registry                             

pkg/registry/BUILD.bazel

  • Adds two new OpenTelemetry dependencies: @io_opentelemetry_go_otel and
    @io_opentelemetry_go_otel_metric
  • Enables metrics instrumentation for identity reconciliation engine in
    registry package
+2/-0     
Additional files
1 files
starrocks-values.yaml [link]   

Imported from GitHub pull request. Original GitHub pull request: #2022 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2022 Original created: 2025-11-27T04:53:09Z Original updated: 2025-11-27T05:30:07Z Original head: carverauto/serviceradar:core/device_count_creeping Original base: main Original merged: 2025-11-27T05:30:03Z by @mfreeman451 --- ### **User description** ## IMPORTANT: Please sign the Developer Certificate of Origin Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include a [DCO sign-off statement]( https://developercertificate.org/) indicating the DCO acceptance in one commit message. Here is an example DCO Signed-off-by line in a commit message: ``` Signed-off-by: J. Doe <j.doe@domain.com> ``` ## Describe your changes ## Issue ticket number and link ## Code checklist before requesting a review - [ ] I have signed the DCO? - [ ] The build completes without errors? - [ ] All tests are passing when running make test? ___ ### **PR Type** Enhancement, Tests, Bug fix, Documentation ___ ### **Description** - Implements comprehensive Identity Reconciliation Engine (IRE) to address device duplication from IP churn through network sightings lifecycle management - Adds policy-driven promotion pipeline with confidence tiers: Tier 3 sightings → Tier 2 promotions → Tier 1 anchored devices - Introduces database schema for `network_sightings`, `device_identifiers`, `fingerprints`, `subnet_policies`, and audit tables with proper indexing and constraints - Implements core registry reconciliation with sighting ingestion, TTL management, and promotion eligibility evaluation based on policy gates - Adds background reapers for expiring stale sightings and cleaning up in-memory registry state - Provides comprehensive REST API endpoints for sighting management, promotion, dismissal, and audit trail queries - Implements OpenTelemetry metrics tracking promotion statistics (promoted, eligible, shadow-ready, blocked counts) - Adds frontend dashboard (`SightingsDashboard`) with filtering, pagination, promotion actions, and merge history visualization - Integrates promotion lineage visibility into device detail view showing sighting origin and audit events - Includes Prometheus alerting rules for monitoring promotion pipeline health and reconciliation staleness - Fixes SQL query formatting issues in hydrate.go with proper `const` keyword positioning and table reference corrections - Adds comprehensive documentation covering requirements, design, metrics, and alerting guidance - Updates Helm configuration with reaper settings, identity reconciliation profiles, and faker simulation parameters ___ ### Diagram Walkthrough ```mermaid flowchart LR A["Network Sightings<br/>Ingestion"] -->|"Filter & Store"| B["Sighting<br/>Storage"] B -->|"TTL Check"| C["Reaper<br/>Expiration"] B -->|"Promotion<br/>Evaluation"| D["Policy Gates<br/>Assessment"] D -->|"Eligible"| E["Promote to<br/>Unified Device"] D -->|"Blocked"| F["Shadow Mode<br/>Backlog"] E -->|"Record"| G["Audit Trail<br/>& Metrics"] F -->|"Monitor"| G H["API Endpoints"] -->|"Query/Action"| B H -->|"Trigger"| D I["Frontend<br/>Dashboard"] -->|"Display"| H J["Device Detail"] -->|"Show Lineage"| G ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>33 files</summary><table> <tr> <td> <details> <summary><strong>registry.go</strong><dd><code>Identity reconciliation engine with sighting promotion pipeline</code></dd></summary> <hr> pkg/registry/registry.go <ul><li>Added <code>identityCfg</code> and <code>reconcileInterval</code> fields to <code>DeviceRegistry</code> <br>struct for identity reconciliation configuration<br> <li> Implemented <code>WithIdentityReconciliationConfig()</code> and <br><code>WithReconcileInterval()</code> option functions for registry initialization<br> <li> Added sighting ingestion logic in <code>ProcessBatchDeviceUpdates()</code> to <br>filter and store network sightings when identity reconciliation is <br>enabled<br> <li> Implemented <code>ReconcileSightings()</code> method to promote eligible sightings <br>to unified devices based on policy gates<br> <li> Added helper functions: <code>ingestSightings()</code>, <code>resolveSightingTTL()</code>, <br><code>copySightingMetadata()</code>, <code>buildUpdateFromNetworkSighting()</code>, <br><code>PromoteSighting()</code>, <code>DismissSighting()</code>, <code>ListSightingEvents()</code>, <br><code>ListSightings()</code>, <code>CountSightings()</code>, and <code>buildIdentifiersFromUpdate()</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-cb61d8f79451b9541de4a8cc0811523a68d15452b2f5971c7618ea5b423cf4ec">+525/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>cnpg_identity_reconciliation_queries.go</strong><dd><code>Database queries for sighting and promotion management</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/db/cnpg_identity_reconciliation_queries.go <ul><li>New file implementing database queries for identity reconciliation <br>operations<br> <li> Added SQL queries for listing promotable sightings, marking sightings <br>promoted, listing active sightings, and counting sightings<br> <li> Implemented functions: <code>ListPromotableSightings()</code>, <br><code>MarkSightingsPromoted()</code>, <code>ListActiveSightings()</code>, <br><code>CountActiveSightings()</code>, <code>GetNetworkSighting()</code>, <code>UpdateSightingStatus()</code>, <br><code>ListSightingEvents()</code>, <code>ListSubnetPolicies()</code>, and <code>ListMergeAuditEvents()</code><br> <li> All functions include proper error handling and CNPG read/write <br>availability checks</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-9711d3905792c75b1bfaf5a3a0de9d8b7d7bb5ceb11be9a444ac5745eb00054f">+525/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>cnpg_identity_reconciliation_upserts.go</strong><dd><code>Database upsert operations for identifiers and events</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/db/cnpg_identity_reconciliation_upserts.go <ul><li>New file implementing database upsert operations for identity <br>reconciliation<br> <li> Added <code>UpsertDeviceIdentifiers()</code> to write device identifier rows with <br>conflict resolution<br> <li> Added <code>InsertSightingEvents()</code> to record audit events for sightings<br> <li> Includes helper functions for argument building and validation with <br>proper error handling</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-957dbd9cd93b91df2c274b2a7e01e238709d9a0e759081e4ac02b957047f28f7">+231/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>cnpg_identity_reconciliation.go</strong><dd><code>Core sighting storage and expiration operations</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/db/cnpg_identity_reconciliation.go <ul><li>New file for core identity reconciliation database operations<br> <li> Implemented <code>StoreNetworkSightings()</code> to upsert active sightings with <br>TTL management<br> <li> Implemented <code>ExpireNetworkSightings()</code> to mark stale sightings as <br>expired based on TTL<br> <li> Includes helper functions for argument building and data validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-a5983fb383f0fe5d0270e7689e4f0f104a02b5f38598b1d4708832d41a79962a">+178/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity.go</strong><dd><code>API endpoints for sighting management and reconciliation</code>&nbsp; </dd></summary> <hr> pkg/core/api/identity.go <ul><li>New file implementing API handlers for identity reconciliation <br>endpoints<br> <li> Added handlers: <code>handleListSightings()</code>, <code>handleReconcileSightings()</code>, <br><code>handlePromoteSighting()</code>, <code>handleDismissSighting()</code>, <br><code>handleSightingEvents()</code>, <code>handleListSubnetPolicies()</code>, and <br><code>handleMergeAuditHistory()</code><br> <li> Includes query parameter parsing, authentication context extraction, <br>and proper error responses</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0">+248/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity_metrics.go</strong><dd><code>OpenTelemetry metrics for promotion reconciliation tracking</code></dd></summary> <hr> pkg/registry/identity_metrics.go <ul><li>New file implementing OpenTelemetry metrics for identity promotion <br>reconciliation<br> <li> Defined 8 observable gauges tracking promotion statistics (promoted, <br>eligible, shadow-ready, blocked, etc.)<br> <li> Implemented <code>recordIdentityPromotionMetrics()</code> to update metrics with <br>latest reconciliation run data<br> <li> Uses atomic operations for thread-safe metric updates</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-fcd2f8364d180b314ea4b2676718fd233a91d0c5ef39daac2681cd9dadf885e1">+176/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>promotion_status.go</strong><dd><code>Sighting promotion eligibility evaluation logic</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/registry/promotion_status.go <ul><li>New file implementing promotion eligibility evaluation logic<br> <li> Implemented <code>promotionStatusForSighting()</code> to assess sighting readiness <br>based on policy gates<br> <li> Added helper functions: <code>dedupeStrings()</code> and <code>formatDurationShort()</code> for <br>status formatting<br> <li> Evaluates persistence windows, hostname/fingerprint requirements, and <br>shadow mode status</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-288cd08accaea9b202bb41b08520fb68b4842a213c7e68b38645395c540d3d82">+169/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity_reconciliation.go</strong><dd><code>Data models for identity reconciliation domain</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/models/identity_reconciliation.go <ul><li>New file defining data models for identity reconciliation feature<br> <li> Added types: <code>NetworkSighting</code>, <code>DeviceIdentifier</code>, <code>SightingEvent</code>, <br><code>SubnetPolicy</code>, <code>MergeAuditEvent</code>, and <code>SightingPromotionStatus</code><br> <li> Includes <code>NetworkSightingStatus</code> enum with states: active, promoted, <br>expired, dismissed</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-39b6c1e250aa888e23cf0f444c697efb1d97e7de8f64983ff9f8a67572beb5cc">+87/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>config.go</strong><dd><code>Configuration models for identity reconciliation and reaper</code></dd></summary> <hr> pkg/models/config.go <ul><li>Added new configuration types: <code>ReaperConfig</code>, <code>IdentityReaperProfile</code>, <br><code>IdentityReaperConfig</code>, <code>PromotionConfig</code>, and <code>FingerprintingConfig</code><br> <li> Added <code>IdentityReconciliationConfig</code> to gate the identity reconciliation <br>engine<br> <li> Extended <code>CoreServiceConfig</code> with <code>Reaper</code> and <code>Identity</code> fields<br> <li> Implemented custom JSON marshaling/unmarshaling for duration fields in <br>reaper configuration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-366a7f033cccba511935056e3e5bd509657f2e86213958a84b1d0d3ec606db83">+88/-4</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity_reaper.go</strong><dd><code>Background reaper for stale network sightings</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/identity_reaper.go <ul><li>New file implementing the identity reconciliation reaper for expiring <br>stale sightings<br> <li> Implemented <code>IdentityReaper</code> struct with <code>Start()</code> and <code>reap()</code> methods<br> <li> Periodically expires network sightings based on TTL and records expiry <br>events</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-bf0c01bfda7d100504d594637ea4916f04155f9fb7048a41ce410a0b2ef29618">+86/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>reaper.go</strong><dd><code>Enhanced stale device reaper with registry cleanup</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/reaper.go <ul><li>Added <code>registry</code> field to <code>StaleDeviceReaper</code> struct<br> <li> Updated <code>NewStaleDeviceReaper()</code> constructor to accept registry <br>parameter<br> <li> Added logic to call <code>DeleteLocal()</code> on registry when reaping stale <br>devices<br> <li> Updated reaper to clean up both database and in-memory registry state</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-f726319fea6a381a430fc9d50a4cecc5b0d908e404f0b36f4f71ca968c1f7486">+12/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>server.go</strong><dd><code>Server initialization for identity reconciliation pipeline</code></dd></summary> <hr> pkg/core/server.go <ul><li>Updated device registry initialization to include identity <br>reconciliation config and reconcile interval options<br> <li> Modified stale device reaper initialization to use configurable <br>interval and TTL from config<br> <li> Added identity reconciliation reaper startup when identity feature is <br>enabled<br> <li> Implemented background goroutine for periodic sighting promotion <br>reconciliation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-a12d34beb8640efbbedad28f8610fd58afeea457572d82e4fd295ab2a6bb8ee6">+55/-4</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>server.go</strong><dd><code>API server routes for identity reconciliation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/api/server.go <ul><li>Added <code>WithIdentityConfig()</code> option function to expose identity <br>reconciliation config to API<br> <li> Registered 7 new protected API routes for identity reconciliation <br>endpoints<br> <li> Routes include sightings listing, promotion, dismissal, events, <br>policies, merge audit, and reconciliation trigger</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-1bb99367fdd853c728b7cfbf5893a293f6d217144dfb5282cb8dd32e5261021e">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>types.go</strong><dd><code>API server types for identity reconciliation support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/api/types.go <ul><li>Added <code>identityConfig</code> field to <code>APIServer</code> struct<br> <li> Extended <code>DeviceRegistryService</code> interface with 6 new methods for <br>sighting operations and reconciliation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-39f024121630282f9e3eeee5b77e5a63a87950b9eae4f479277a289796b42d56">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>interfaces.go</strong><dd><code>Database service interface extensions for identity reconciliation</code></dd></summary> <hr> pkg/db/interfaces.go <ul><li>Extended <code>Service</code> interface with 11 new methods for identity <br>reconciliation operations<br> <li> Added methods for sighting storage, expiration, promotion, status <br>updates, and audit queries</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-c230fe0c315251837357bfde4ae7f7b34080398d8e48af6bf78badb2124271f3">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>interfaces.go</strong><dd><code>Registry manager interface extensions for sighting operations</code></dd></summary> <hr> pkg/registry/interfaces.go <ul><li>Added <code>DeleteLocal()</code> method to <code>Manager</code> interface for in-memory device <br>removal<br> <li> Added 6 new methods for sighting operations: <code>ReconcileSightings()</code>, <br><code>ListSightings()</code>, <code>CountSightings()</code>, <code>PromoteSighting()</code>, <br><code>DismissSighting()</code>, and <code>ListSightingEvents()</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-21496eb3d34ec02e874f36c98cdd2ca7f39130369caf774c672dcf4be1619503">+22/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>unified_device.go</strong><dd><code>Added sighting discovery source type</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/models/unified_device.go <ul><li>Added <code>DiscoverySourceSighting</code> constant to discovery source enumeration<br> <li> Reformatted discovery source constants for consistency<br> <li> Updated field alignment comments in <code>DeviceUpdate</code> struct</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-e05758fa3d62acc9613f0b1191e034c8d4d115ec85debcbe9bda2968d34adaba">+14/-13</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>bootstrap.go</strong><dd><code>Bootstrap integration for identity config</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/bootstrap/bootstrap.go <ul><li>Added identity configuration to API server options when present in <br>core service config</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-b3cc8955e22d15be8a676a46643ccffc5283d26f2c0ef2fb21a73894d4933912">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Frontend API route for sighting events</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/sightings/[id]/events/route.ts <ul><li>New Next.js API route handler for fetching sighting events<br> <li> Implements GET endpoint that proxies to backend identity API<br> <li> Includes query parameter forwarding, authentication header handling, <br>and error responses</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-e84537bbe57ef86554926810c838b1d58656272ed0915d34ee0244cbf1b047eb">+75/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Frontend API route for sighting dismissal</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/sightings/[id]/dismiss/route.ts <ul><li>New Next.js API route handler for dismissing sightings<br> <li> Implements POST endpoint that proxies to backend identity API<br> <li> Includes request body parsing, authentication header handling, and <br>error responses</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-60dcc31e214dbd0965b10111d70bc78b82ae79e710926ddc15d8a55c4e31467e">+75/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Add sightings API endpoint with pagination</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/sightings/route.ts <ul><li>New API route handler for fetching network sightings with pagination <br>support<br> <li> Implements query parameter handling for <code>partition</code>, <code>limit</code>, and <code>offset</code><br> <li> Proxies requests to backend identity API with authentication headers<br> <li> Includes error handling with detailed error responses</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-3d6327c82142422748fbdfe5556a1ec0d540fdffd0ce3b09c3b6b7e47d416241">+77/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Add merge audit history API endpoint</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/merge-audit/route.ts <ul><li>New API route for retrieving device merge audit history<br> <li> Supports filtering by <code>device_id</code> and pagination with <code>limit</code> parameter<br> <li> Proxies to backend merge-audit endpoint with proper authentication<br> <li> Comprehensive error handling and response formatting</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-bd27ff165fe55e9c3b69adeaa2247eca3cf364aef28faf693db8be790a4c7eae">+73/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Add subnet policies API endpoint</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/policies/route.ts <ul><li>New API route for fetching subnet policies<br> <li> Supports pagination via <code>limit</code> query parameter<br> <li> Proxies to backend identity policies endpoint<br> <li> Includes error handling and authentication</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-00105b93f9dabec55b1d87e2479c00f428d9095d7a6a6adf8de907b74334f473">+68/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity.ts</strong><dd><code>Add identity reconciliation type definitions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/types/identity.ts <ul><li>Defines TypeScript interfaces for identity reconciliation domain<br> <li> Includes <code>NetworkSighting</code>, <code>SightingEvent</code>, <code>SubnetPolicy</code>, and <br><code>MergeAuditEvent</code> types<br> <li> Defines response wrapper types for API endpoints<br> <li> Includes promotion status and configuration metadata types</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-2adf4016dedea0e6e25bcc88daa1af72c61c4319b31ac5cffc27734d7ca795f2">+89/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Add sighting promotion API endpoint</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/sightings/[id]/promote/route.ts <ul><li>New API route for promoting a sighting to a device<br> <li> Accepts sighting ID as dynamic route parameter<br> <li> Implements POST handler to trigger promotion via backend<br> <li> Includes validation, error handling, and authentication</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-30f0a7031ca883309f1a797ac7142921787e72c66214489f4e3499810ec65147">+64/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>route.ts</strong><dd><code>Add reconciliation trigger API endpoint</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/api/identity/reconcile/route.ts <ul><li>New API route to trigger identity reconciliation process<br> <li> Implements POST handler that proxies to backend reconciliation <br>endpoint<br> <li> Includes authentication and error handling<br> <li> Returns reconciliation result to caller</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-99d32617d55933536dcc9e352f1a0777ee49e5b19e1594bfd25ecc00e805747d">+55/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>SightingsDashboard.tsx</strong><dd><code>Add comprehensive sightings management dashboard UI</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/components/Identity/SightingsDashboard.tsx <ul><li>Large new React component for managing network sightings and device <br>promotion<br> <li> Implements sightings list with filtering, pagination, and detailed <br>view panel<br> <li> Provides promote/dismiss actions with optional reason tracking<br> <li> Includes audit trail viewing, merge history, subnet policies, and <br>historical lookup features<br> <li> Displays promotion eligibility status and policy blockers with <br>explanatory hints</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-60f5a44a92234ab6d477f3544924efe1f225f6aa6e2f936eccfea4505aaf0bf6">+1624/-0</a></td> </tr> <tr> <td> <details> <summary><strong>DeviceDetail.tsx</strong><dd><code>Add promotion lineage visibility to device detail</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/components/Devices/DeviceDetail.tsx <ul><li>Adds import for <code>Info</code> icon and <code>SightingEvent</code> type<br> <li> Refactors metadata extraction into memoized hooks (<code>metadataFlag</code>, <br><code>metadataValue</code>)<br> <li> Adds state for tracking promotion events and loading/error states<br> <li> Implements fetching of promotion audit trail when device has promotion <br>sighting ID<br> <li> Adds new "Promotion lineage" section displaying sighting origin, <br>promotion mode, and audit events<br> <li> Includes link to view sighting in sightings dashboard</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-2dbb02c57d308332683ec3795ef41e65387b5d1c30c559f01b535b5e704ba872">+207/-44</a></td> </tr> <tr> <td> <details> <summary><strong>00000000000009_identity_reconciliation_schema.up.sql</strong><dd><code>Add identity reconciliation database schema</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql <ul><li>Creates database schema for identity reconciliation system<br> <li> Defines tables for <code>subnet_policies</code>, <code>fingerprints</code>, <code>network_sightings</code>, <br><code>device_identifiers</code><br> <li> Adds audit tables for <code>sighting_events</code> and <code>merge_audit</code> with proper <br>indexing<br> <li> Includes foreign key relationships and unique constraints for data <br>integrity</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-8e0daeec5bbb474a62e9f2f1c22236044e5d911957c9ca059b29e482e45afb4e">+101/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>page.tsx</strong><dd><code>New Identity & Sightings dashboard page component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/app/identity/page.tsx <ul><li>Creates new Next.js page component for Identity & Sightings dashboard <br>with Apache 2.0 license header<br> <li> Implements server-side rendering with <code>noStore()</code> to disable caching for <br>real-time sighting data<br> <li> Extracts search parameters for <code>sighting</code>, <code>history_actor</code>, and <br><code>history_partition</code> filters<br> <li> Renders <code>SightingsDashboard</code> component with Suspense fallback and passes <br>filter parameters</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-faca274c1cefd7b665633ec6b133a58b0de89820f493f092867037197803f9f6">+61/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Add identity reconciliation database layer files</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/db/BUILD.bazel <ul><li>Adds three new database files for identity reconciliation: <br><code>cnpg_identity_reconciliation.go</code>, <br><code>cnpg_identity_reconciliation_queries.go</code>, and <br><code>cnpg_identity_reconciliation_upserts.go</code><br> <li> Implements database layer for sightings, identifiers, and <br>reconciliation operations</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-adb10902cc707bef3ae70e4f5cdf15c34842727adc0f02fcf2dac044a3bfe004">+3/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Sidebar.tsx</strong><dd><code>Add Identity navigation to sidebar menu</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/components/Sidebar.tsx <ul><li>Imports <code>Fingerprint</code> icon from lucide-react library<br> <li> Adds new navigation item for Identity page with <code>/identity</code> route and <br><code>Fingerprint</code> icon<br> <li> Inserts Identity navigation between Devices and Network menu items</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-219f9604c678f564c98ff1920201ea980331348dfb5dff7314a34d9fd198f1b1">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Add identity reaper implementation file</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/BUILD.bazel <ul><li>Adds <code>identity_reaper.go</code> file to core package build configuration<br> <li> Implements reaper component for managing sighting TTLs and <br>low-confidence device cleanup per subnet policies</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-af133ce6c45ec51c66bcb5fe0b424291d78688285b614275765003b83c177534">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>4 files</summary><table> <tr> <td> <details> <summary><strong>mock_db.go</strong><dd><code>Mock implementations for identity reconciliation database methods</code></dd></summary> <hr> pkg/db/mock_db.go <ul><li>Added mock implementations for 16 new database methods related to <br>identity reconciliation<br> <li> Includes mocks for sighting operations, device identifiers, events, <br>and audit history<br> <li> All mocks follow existing patterns with proper call recording and type <br>assertions</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-30e38f888d4849fc40d7ebb1559c2a84c43aa8cd13b3b89fd7ec6cf873b243c7">+178/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>promotion_status_test.go</strong><dd><code>Unit tests for promotion status evaluation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/registry/promotion_status_test.go <ul><li>New test file with 5 test cases for promotion status evaluation<br> <li> Tests cover disabled state, persistence blocking, auto-promotion <br>disabled, shadow mode, and eligible scenarios<br> <li> Includes helper function <code>containsHint()</code> for assertion validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-c2e0d7d349c12f8c129223330b5cba02d420c9d95c94b400c31b977488b873b9">+123/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>reaper_test.go</strong><dd><code>Updated reaper tests for registry cleanup</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/reaper_test.go <ul><li>Updated test setup to include mock registry parameter for <br><code>NewStaleDeviceReaper()</code><br> <li> Added mock expectations for <code>DeleteLocal()</code> calls on registry during <br>reaping</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-05d0ad601fa303f61c6a5664ad928a0579844f3ebe0c599dfea4260bbcd27d1a">+6/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>mock_registry.go</strong><dd><code>Mock implementations for registry sighting operations</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/registry/mock_registry.go <ul><li>Added mock implementations for 9 new registry manager methods<br> <li> Includes mocks for sighting operations, promotion, dismissal, and <br>event listing<br> <li> All mocks follow existing patterns with proper call recording</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-1c3085186c5d9b0c552ee50f98a3ff2251904cf3c9adedfd693f16488539f0bb">+100/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>5 files</summary><table> <tr> <td> <details> <summary><strong>config.go</strong><dd><code>Default configuration values for identity reconciliation</code>&nbsp; </dd></summary> <hr> pkg/core/config.go <ul><li>Added 10 default constants for identity reconciliation engine (IRE) <br>configuration<br> <li> Implemented <code>applyIdentityDefaults()</code> function to apply sensible <br>defaults for identity reconciliation config<br> <li> Integrated identity config normalization into <code>normalizeConfig()</code> <br>function</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-a2dcdf3bac7ca59032d52f7a0635b5dbcfcb83a6f07cc45db8f57fba0cc33c30">+81/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Bazel build configuration update</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/api/BUILD.bazel - Added `identity.go` to the go_library sources list </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-ee91bfc5d078aab2befc64f89c36dca0de7342465f414ecd70292d092153bfcc">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>values.yaml</strong><dd><code>Update Helm values with identity and reaper config</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> helm/serviceradar/values.yaml <ul><li>Updates all service image tags from <code>latest</code> to specific commit SHA for <br>reproducibility<br> <li> Adds comprehensive <code>core.reaper</code> configuration for stale device cleanup<br> <li> Adds <code>core.identity</code> configuration block with promotion, fingerprinting, <br>and reaper profiles<br> <li> Adds <code>faker.simulation</code> configuration for IP shuffle behavior</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-d4449c7cb70362554b274f81eae5a4b81a8e81df494282e383d1b7ea3871c452">+57/-18</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>serviceradar-config.yaml</strong><dd><code>Add identity reconciliation config to Helm template</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> helm/serviceradar/files/serviceradar-config.yaml <ul><li>Adds <code>reaper</code> configuration section with interval and TTL settings<br> <li> Adds comprehensive <code>identity_reconciliation</code> configuration with <br>promotion, fingerprinting, and reaper profiles<br> <li> Configures per-subnet TTL profiles (default, dynamic, guest, static)<br> <li> Parameterizes faker simulation settings for total devices and IP <br>shuffle behavior</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-b8c8d2484103b11c396bc60d290c81df63c30a0f81103eceb5852a17e1d2b5e3">+43/-4</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>kustomization.yaml</strong><dd><code>Add identity Prometheus rules to demo kustomization</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> k8s/demo/base/kustomization.yaml <ul><li>Adds reference to new <code>identity-prometheus-rules.yaml</code> resource file<br> <li> Integrates identity reconciliation Prometheus rules into demo <br>deployment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-c4260176971b950ef1b967a2631b446225071906172f56287c465ad2e29788d9">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Bug fix</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>hydrate.go</strong><dd><code>SQL query formatting and table reference fixes</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/registry/hydrate.go <ul><li>Fixed SQL query formatting by moving <code>const</code> keyword to proper position<br> <li> Corrected table reference from <code>table(device_capability_registry)</code> to <br><code>device_capability_registry</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-e27e237716b5830731f55caab246c132a8ba98f3b37fe5e39de3867038941e4d">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>6 files</summary><table> <tr> <td> <details> <summary><strong>spec.md</strong><dd><code>Add identity reconciliation requirements specification</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-identity-reconciliation-engine/specs/device-identity-reconciliation/spec.md <ul><li>Documents requirements for network sightings lifecycle management<br> <li> Specifies policy-driven promotion with TTL enforcement<br> <li> Defines identifier indexing and strong-ID merge behavior<br> <li> Includes reaper and auditability requirements with example scenarios<br> <li> Modifies sightings UI/API requirement to include pagination and <br>promotion context</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-6e956f86e942a3106c94ea3f1a1bd46210c035e9240be297a488b639449d3e90">+61/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity-alerts.md</strong><dd><code>Add identity reconciliation alert rules documentation</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/identity-alerts.md <ul><li>New documentation file with Prometheus alert rules for identity <br>reconciliation<br> <li> Defines alerts for promotion policy blocks, shadow backlog, <br>reconciliation stalls, and volume surges<br> <li> Includes alert expressions, severity levels, and remediation guidance<br> <li> Provides dashboard suggestions for monitoring identity metrics</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-571059c39d6764b240172870a77d3c592ce6614da678e51624cd7ff0787bbf9d">+63/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>design.md</strong><dd><code>Identity Reconciliation Engine design and architecture</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-identity-reconciliation-engine/design.md <ul><li>Introduces comprehensive design for Identity & Reconciliation Engine <br>(IRE) to address device duplication from IP churn<br> <li> Defines data model with <code>network_sightings</code>, <code>device_identifiers</code>, <br><code>fingerprints</code>, <code>subnet_policies</code>, and audit tables<br> <li> Establishes confidence tiers (Tier 3 sightings, Tier 2 promotions, <br>Tier 1 anchored devices) with policy-driven promotion rules<br> <li> Details migration plan with 7 phases from schema addition through <br>legacy path removal, including shadow-mode validation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-888d2efd1371a95f95ba77bf4506d180c1fd65434603a56cf2e8a3ad495386ce">+26/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>tasks.md</strong><dd><code>Implementation tasks and deployment status for IRE</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-identity-reconciliation-engine/tasks.md <ul><li>Tracks 11 implementation tasks for IRE with checkmarks indicating <br>completion status (8 complete, 3 pending)<br> <li> Completed tasks include data model, feature flags, ingestion updates, <br>core engine, reaper, API/UI, and metrics<br> <li> Pending tasks cover migration/backfill, comprehensive testing, and <br>configuration API exposure<br> <li> Documents deployment status with OCI image builds, pushes, and Helm <br>deployment to demo environment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-7d7c4d6b4718fd5255ec47aa62dee71b5da2c83ff2160164603454379b618181">+17/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>identity-metrics.md</strong><dd><code>Identity reconciliation metrics and alerting guide</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/identity-metrics.md <ul><li>Documents 12 OpenTelemetry metrics emitted on <code>serviceradar.identity</code> <br>meter for promotion monitoring<br> <li> Provides alert recommendations for policy blocks, shadow backlog, run <br>staleness, throughput dips, and volume surges<br> <li> Includes dashboard panel guidance with single-stat gauges, time-series <br>trends, and run age tracking<br> <li> Specifies metric thresholds and durations for alerting (e.g., <br>15-minute staleness, 30-minute shadow backlog)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-523b90d85a235c19105c7da3934c67515bdf1a6d45c2c8eb5131ab14f74976f9">+27/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>proposal.md</strong><dd><code>IRE proposal and impact analysis</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/add-identity-reconciliation-engine/proposal.md <ul><li>Proposes addition of Identity & Reconciliation Engine to address <br>device duplication from IP churn<br> <li> Outlines key changes: network sighting lifecycle, identifier indexing, <br>schema additions, API/UI surfaces, and metrics<br> <li> Identifies affected components: registry device resolver, <br>sweep/poller/sync ingestion, CNPG schema, Helm values, and background <br>jobs<br> <li> Justifies need for formal sighting treatment separate from durable <br>devices with policy-driven promotion</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-f3c3c8d9463a1c6eb3934bb6c516985accb7f58b402a0d5e414ff65c1c732bc3">+15/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Monitoring</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>identity-prometheus-rules.yaml</strong><dd><code>Prometheus alerting rules for identity reconciliation</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> k8s/demo/base/identity-prometheus-rules.yaml <ul><li>Defines 5 PrometheusRule alerts for monitoring identity reconciliation <br>health and promotion pipeline<br> <li> Covers policy-blocked promotions, shadow mode backlog, stalled <br>reconciliation, throughput drops, and volume surges<br> <li> Includes severity levels (warning, critical, info) with descriptive <br>annotations for operator guidance<br> <li> Alerts trigger on specific metric thresholds and durations to catch <br>reconciliation failures early</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-7221b9f96b2fbdfc6ea70aab8f1f3a2d865196b886ff8caf6c4853c7606c6d29">+50/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Add OpenTelemetry dependencies to registry</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/registry/BUILD.bazel <ul><li>Adds two new OpenTelemetry dependencies: <code>@io_opentelemetry_go_otel</code> and <br><code>@io_opentelemetry_go_otel_metric</code><br> <li> Enables metrics instrumentation for identity reconciliation engine in <br>registry package</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-3e838afce9a84935e04b7ff8fd3e48d5452c21538a3ea1d36e3fd00aa3c30cd0">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>starrocks-values.yaml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2022/files#diff-629a22773c4defcafc537318bd7e7ad6c7f04dca66ce5280104ee1c4ed3e8c81">[link]</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
qodo-code-review[bot] commented 2025-11-27 04:54:15 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2022#issuecomment-3584200448
Original created: 2025-11-27T04:54:15Z

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Untrusted input logging

Description: The dismissal endpoint accepts an unvalidated JSON 'reason' field and echoes it back in
audit logs without sanitization, risking log injection or storing untrusted content that
could later be reflected (e.g., in UIs) and enable XSS if not escaped downstream.
identity.go [137-146]

Referred Code
var body struct {
	Reason string `json:"reason"`
}
_ = json.NewDecoder(r.Body).Decode(&body)

actor := "system"
if user, ok := auth.GetUserFromContext(r.Context()); ok && user != nil && user.Email != "" {
	actor = user.Email
}

Unvalidated metadata storage

Description: Sightings ingestion copies arbitrary metadata fields directly from updates (including
'hostname', 'mac', and other keys) into database without normalization or validation,
which can enable downstream injection risks (e.g., SQL/JSON query manipulation, log/HTML
injection) if later rendered or used to build queries.
registry.go [187-205]

Referred Code
	if len(sightings) > 0 {
		if err := r.ingestSightings(ctx, sightings); err != nil {
			r.logger.Warn().Err(err).Int("count", len(sightings)).Msg("Failed to ingest network sightings")
		}
	}

	valid = filtered
	if len(valid) == 0 {
		return nil
	}
}

// Resolve device IDs to canonical ServiceRadar UUIDs
// This ensures devices are identified by stable IDs rather than ephemeral IPs
if r.deviceIdentityResolver != nil {
	if err := r.deviceIdentityResolver.ResolveDeviceIDs(ctx, valid); err != nil {
		r.logger.Warn().Err(err).Msg("Device identity resolution failed")
	}
}
Audit spoofing risk

Description: The promote sighting endpoint derives the 'actor' from the authenticated user's email and
records it without additional verification or normalization; if upstream identity
assertions are weak or email contains unexpected characters, audit trails could be spoofed
or contain malicious content.
identity.go [96-105]

Referred Code

actor := "system"
if user, ok := auth.GetUserFromContext(r.Context()); ok && user != nil && user.Email != "" {
	actor = user.Email
}

update, err := s.deviceRegistry.PromoteSighting(r.Context(), sightingID, actor)
if err != nil {
	s.writeAPIError(w, http.StatusBadRequest, err.Error())
	return
Race/inconsistent upsert

Description: The upsert on 'network_sightings' uses ON CONFLICT (partition, ip) WHERE status='active',
which may allow bypass by toggling status or race conditions leading to duplicate active
rows if status changes between insert and update; this can be exploited to create
inconsistent state for promotion decisions.
cnpg_identity_reconciliation.go [30-37]

Referred Code
ON CONFLICT (partition, ip) WHERE status = 'active'
DO UPDATE SET
	last_seen = EXCLUDED.last_seen,
	ttl_expires_at = EXCLUDED.ttl_expires_at,
	fingerprint_id = COALESCE(EXCLUDED.fingerprint_id, network_sightings.fingerprint_id),
	metadata = COALESCE(network_sightings.metadata, '{}'::jsonb) || COALESCE(EXCLUDED.metadata, '{}'::jsonb),
	source = EXCLUDED.source;
`
DoS via reconciliation triggers

Description: Background reconciliation is triggered on a fixed ticker without rate limiting per
tenant/partition or backoff; an authenticated user can also trigger '/identity/reconcile'
causing concurrent runs, potentially enabling resource exhaustion or DoS via repeated
promotions.
server.go [412-438]

Referred Code
if s.config.Identity != nil && s.config.Identity.Enabled && s.config.Identity.Reaper.Interval > 0 {
	identityInterval := time.Duration(s.config.Identity.Reaper.Interval)
	s.logger.Info().
		Dur("interval", identityInterval).
		Msg("Initializing identity reconciliation reaper")
	identityReaper := NewIdentityReaper(s.DB, s.logger, identityInterval)
	go identityReaper.Start(ctx)

	// Background promotion/merge reconciliation
	go func() {
		ticker := time.NewTicker(identityInterval)
		defer ticker.Stop()

		for {
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
				if s.DeviceRegistry != nil {
					if err := s.DeviceRegistry.ReconcileSightings(ctx); err != nil {
						s.logger.Warn().Err(err).Msg("Identity reconciliation promotion failed")


 ... (clipped 6 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Error context: API handlers return internal error messages directly to clients without consistent
wrapping or redaction, which may lack robust context handling and uniform validation
across endpoints.

Referred Code
sightings, err := s.deviceRegistry.ListSightings(r.Context(), partition, limit, offset)
if err != nil {
	s.writeAPIError(w, http.StatusInternalServerError, err.Error())
	return
}

total, err := s.deviceRegistry.CountSightings(r.Context(), partition)
if err != nil {
	s.writeAPIError(w, http.StatusInternalServerError, err.Error())
	return

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Error leakage: Handlers write err.Error() to HTTP responses (e.g., database or registry errors), which
could expose internal details to end users.

Referred Code
	s.writeAPIError(w, http.StatusInternalServerError, err.Error())
	return
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input validation: Query and body parameters (e.g., limit, offset, reason) are minimally validated and path
IDs are only checked for emptiness, which may require stronger validation and constraints.

Referred Code
partition := r.URL.Query().Get("partition")
limit := 100
offset := 0
if raw := r.URL.Query().Get("limit"); raw != "" {
	if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 {
		limit = parsed
	}
}
if raw := r.URL.Query().Get("offset"); raw != "" {
	if parsed, err := strconv.Atoi(raw); err == nil && parsed >= 0 {
		offset = parsed
	}
}

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
- Requires Further Human Verification
🏷️ - Compliance label
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2022#issuecomment-3584200448 Original created: 2025-11-27T04:54:15Z --- _You are nearing your monthly Qodo Merge usage quota. For more information, please visit [here](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users)._ ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/d11299a4006be82d031b5221a9646f8f063fc132 --> Below is a summary of compliance checks for this PR:<br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=5>⚪</td> <td><details><summary><strong>Untrusted input logging </strong></summary><br> <b>Description:</b> The dismissal endpoint accepts an unvalidated JSON 'reason' field and echoes it back in <br>audit logs without sanitization, risking log injection or storing untrusted content that <br>could later be reflected (e.g., in UIs) and enable XSS if not escaped downstream.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0R137-R146'>identity.go [137-146]</a></strong><br> <details open><summary>Referred Code</summary> ```go var body struct { Reason string `json:"reason"` } _ = json.NewDecoder(r.Body).Decode(&body) actor := "system" if user, ok := auth.GetUserFromContext(r.Context()); ok && user != nil && user.Email != "" { actor = user.Email } ``` </details></details></td></tr> <tr><td><details><summary><strong>Unvalidated metadata storage </strong></summary><br> <b>Description:</b> Sightings ingestion copies arbitrary metadata fields directly from updates (including <br>'hostname', 'mac', and other keys) into database without normalization or validation, <br>which can enable downstream injection risks (e.g., SQL/JSON query manipulation, log/HTML <br>injection) if later rendered or used to build queries.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-cb61d8f79451b9541de4a8cc0811523a68d15452b2f5971c7618ea5b423cf4ecR187-R205'>registry.go [187-205]</a></strong><br> <details open><summary>Referred Code</summary> ```go if len(sightings) > 0 { if err := r.ingestSightings(ctx, sightings); err != nil { r.logger.Warn().Err(err).Int("count", len(sightings)).Msg("Failed to ingest network sightings") } } valid = filtered if len(valid) == 0 { return nil } } // Resolve device IDs to canonical ServiceRadar UUIDs // This ensures devices are identified by stable IDs rather than ephemeral IPs if r.deviceIdentityResolver != nil { if err := r.deviceIdentityResolver.ResolveDeviceIDs(ctx, valid); err != nil { r.logger.Warn().Err(err).Msg("Device identity resolution failed") } } ``` </details></details></td></tr> <tr><td><details><summary><strong>Audit spoofing risk </strong></summary><br> <b>Description:</b> The promote sighting endpoint derives the 'actor' from the authenticated user's email and <br>records it without additional verification or normalization; if upstream identity <br>assertions are weak or email contains unexpected characters, audit trails could be spoofed <br>or contain malicious content.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0R96-R105'>identity.go [96-105]</a></strong><br> <details open><summary>Referred Code</summary> ```go actor := "system" if user, ok := auth.GetUserFromContext(r.Context()); ok && user != nil && user.Email != "" { actor = user.Email } update, err := s.deviceRegistry.PromoteSighting(r.Context(), sightingID, actor) if err != nil { s.writeAPIError(w, http.StatusBadRequest, err.Error()) return ``` </details></details></td></tr> <tr><td><details><summary><strong>Race/inconsistent upsert </strong></summary><br> <b>Description:</b> The upsert on 'network_sightings' uses ON CONFLICT (partition, ip) WHERE status='active', <br>which may allow bypass by toggling status or race conditions leading to duplicate active <br>rows if status changes between insert and update; this can be exploited to create <br>inconsistent state for promotion decisions.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-a5983fb383f0fe5d0270e7689e4f0f104a02b5f38598b1d4708832d41a79962aR30-R37'>cnpg_identity_reconciliation.go [30-37]</a></strong><br> <details open><summary>Referred Code</summary> ```go ON CONFLICT (partition, ip) WHERE status = 'active' DO UPDATE SET last_seen = EXCLUDED.last_seen, ttl_expires_at = EXCLUDED.ttl_expires_at, fingerprint_id = COALESCE(EXCLUDED.fingerprint_id, network_sightings.fingerprint_id), metadata = COALESCE(network_sightings.metadata, '{}'::jsonb) || COALESCE(EXCLUDED.metadata, '{}'::jsonb), source = EXCLUDED.source; ` ``` </details></details></td></tr> <tr><td><details><summary><strong>DoS via reconciliation triggers</strong></summary><br> <b>Description:</b> Background reconciliation is triggered on a fixed ticker without rate limiting per <br>tenant/partition or backoff; an authenticated user can also trigger '/identity/reconcile' <br>causing concurrent runs, potentially enabling resource exhaustion or DoS via repeated <br>promotions.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-a12d34beb8640efbbedad28f8610fd58afeea457572d82e4fd295ab2a6bb8ee6R412-R438'>server.go [412-438]</a></strong><br> <details open><summary>Referred Code</summary> ```go if s.config.Identity != nil && s.config.Identity.Enabled && s.config.Identity.Reaper.Interval > 0 { identityInterval := time.Duration(s.config.Identity.Reaper.Interval) s.logger.Info(). Dur("interval", identityInterval). Msg("Initializing identity reconciliation reaper") identityReaper := NewIdentityReaper(s.DB, s.logger, identityInterval) go identityReaper.Start(ctx) // Background promotion/merge reconciliation go func() { ticker := time.NewTicker(identityInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: if s.DeviceRegistry != nil { if err := s.DeviceRegistry.ReconcileSightings(ctx); err != nil { s.logger.Warn().Err(err).Msg("Identity reconciliation promotion failed") ... (clipped 6 lines) ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary>🎫 <strong>No ticket provided </strong></summary> - [ ] Create ticket/issue <!-- /create_ticket --create_ticket=true --> </details></td></tr> <tr><td colspan='2'><strong>Codebase Duplication Compliance</strong></td></tr> <tr><td>⚪</td><td><details><summary><strong>Codebase context is not defined </strong></summary> Follow the <a href='https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/'>guide</a> to enable codebase context checks. </details></td></tr> <tr><td colspan='2'><strong>Custom Compliance</strong></td></tr> <tr><td rowspan=3>🟢</td><td> <details><summary><strong>Generic: Comprehensive Audit Trails</strong></summary><br> **Objective:** To create a detailed and reliable record of critical system actions for security analysis <br>and compliance.<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td> <details><summary><strong>Generic: Meaningful Naming and Self-Documenting Code</strong></summary><br> **Objective:** Ensure all identifiers clearly express their purpose and intent, making code <br>self-documenting<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td> <details><summary><strong>Generic: Secure Logging Practices</strong></summary><br> **Objective:** To ensure logs are useful for debugging and auditing without exposing sensitive <br>information like PII, PHI, or cardholder data.<br> **Status:** Passed<br> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td rowspan=3>⚪</td> <td><details> <summary><strong>Generic: Robust Error Handling and Edge Case Management</strong></summary><br> **Objective:** Ensure comprehensive error handling that provides meaningful context and graceful <br>degradation<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0R34-R43'><strong>Error context</strong></a>: API handlers return internal error messages directly to clients without consistent <br>wrapping or redaction, which may lack robust context handling and uniform validation <br>across endpoints.<br> <details open><summary>Referred Code</summary> ```go sightings, err := s.deviceRegistry.ListSightings(r.Context(), partition, limit, offset) if err != nil { s.writeAPIError(w, http.StatusInternalServerError, err.Error()) return } total, err := s.deviceRegistry.CountSightings(r.Context(), partition) if err != nil { s.writeAPIError(w, http.StatusInternalServerError, err.Error()) return ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Secure Error Handling</strong></summary><br> **Objective:** To prevent the leakage of sensitive system information through error messages while <br>providing sufficient detail for internal debugging.<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0R36-R38'><strong>Error leakage</strong></a>: Handlers write err.Error() to HTTP responses (e.g., database or registry errors), which <br>could expose internal details to end users.<br> <details open><summary>Referred Code</summary> ```go s.writeAPIError(w, http.StatusInternalServerError, err.Error()) return } ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td><details> <summary><strong>Generic: Security-First Input Validation and Data Handling</strong></summary><br> **Objective:** Ensure all data inputs are validated, sanitized, and handled securely to prevent <br>vulnerabilities<br> **Status:** <br><a href='https://github.com/carverauto/serviceradar/pull/2022/files#diff-6561d8fc8daf275c514d54c99743f4e0f694d6392fb6bf7dd2bb7ad88e3b9ab0R20-R33'><strong>Input validation</strong></a>: Query and body parameters (e.g., limit, offset, reason) are minimally validated and path <br>IDs are only checked for emptiness, which may require stronger validation and constraints.<br> <details open><summary>Referred Code</summary> ```go partition := r.URL.Query().Get("partition") limit := 100 offset := 0 if raw := r.URL.Query().Get("limit"); raw != "" { if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 { limit = parsed } } if raw := r.URL.Query().Get("offset"); raw != "" { if parsed, err := strconv.Atoi(raw); err == nil && parsed >= 0 { offset = parsed } } ``` </details> > Learn more about managing compliance <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#configuration-options'>generic rules</a> or creating your own <a href='https://qodo-merge-docs.qodo.ai/tools/compliance/#custom-compliance'>custom rules</a> </details></td></tr> <tr><td align="center" colspan="2"> - [ ] Update <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> <details><summary>Compliance status legend</summary> 🟢 - Fully Compliant<br> 🟡 - Partial Compliant<br> 🔴 - Not Compliant<br> ⚪ - Requires Further Human Verification<br> 🏷️ - Compliance label<br> </details>
qodo-code-review[bot] commented 2025-11-27 04:55:27 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2022#issuecomment-3584202374
Original created: 2025-11-27T04:55:27Z

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Code Suggestions

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix incorrect promise handling for route parameters

Fix the RouteProps interface and remove the await keyword when accessing
props.params to prevent a runtime error in the Next.js route handler.

web/src/app/api/identity/sightings/[id]/events/route.ts [20-25]

 interface RouteProps {
-  params: Promise<{ id: string }>;
+  params: { id: string };
 }
 
 export async function GET(req: NextRequest, props: RouteProps) {
-  const { id } = await props.params;
+  const { id } = props.params;
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical runtime error in the new API route handler caused by incorrectly awaiting a non-promise params object, which would make the endpoint fail.

High
Fix incorrect handling of searchParams

Correct the handling of the searchParams prop by removing the unnecessary await
and updating its type from a Promise to a plain object, as it is not a promise
in Next.js App Router pages.

web/src/app/identity/page.tsx [26-40]

 type IdentityPageProps = {
-  searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;
+  searchParams?: { [key: string]: string | string[] | undefined };
 };
 
 export default async function IdentityPage({ searchParams }: IdentityPageProps) {
   noStore();
 
-  const resolved = searchParams ? await searchParams : undefined;
-
   const prefillSightingId =
-    typeof resolved?.sighting === "string" ? resolved.sighting : undefined;
+    typeof searchParams?.sighting === "string" ? searchParams.sighting : undefined;
   const historyActorFilter =
-    typeof resolved?.history_actor === "string" ? resolved.history_actor : undefined;
+    typeof searchParams?.history_actor === "string"
+      ? searchParams.history_actor
+      : undefined;
   const historyPartitionFilter =
-    typeof resolved?.history_partition === "string" ? resolved.history_partition : undefined;
+    typeof searchParams?.history_partition === "string"
+      ? searchParams.history_partition
+      : undefined;
 
   return (
 ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a fundamental misunderstanding of the Next.js App Router's searchParams prop, which is an object, not a Promise. Correcting this prevents a runtime error and fixes the component's core logic for handling URL parameters.

High
Fix an undefined function call

Add the missing helper function toNullableString in
pkg/db/cnpg_identity_reconciliation_upserts.go to fix a compilation error in
buildSightingEventArgs.

pkg/db/cnpg_identity_reconciliation_upserts.go [195-202]

 return []interface{}{
 	sightingID,
 	toNullableString(stringPtr(ev.DeviceID)),
 	eventType,
 	actor,
 	details,
 	sanitizeTimestamp(createdAt),
 }, nil
+}
 
+func toNullableString(v *string) interface{} {
+	if v == nil {
+		return nil
+	}
+	return *v
+
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a call to an undefined function toNullableString, which would cause a compilation error, and provides the correct implementation to fix it.

Medium
Add cascade delete to foreign key

Add ON DELETE CASCADE to the device_id foreign key in the device_identifiers
table to ensure associated identifiers are automatically removed when a device
is deleted.

pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql [59-70]

 CREATE TABLE IF NOT EXISTS device_identifiers (
-    device_id        TEXT            NOT NULL REFERENCES unified_devices(device_id),
+    device_id        TEXT            NOT NULL REFERENCES unified_devices(device_id) ON DELETE CASCADE,
     id_type          TEXT            NOT NULL,
     id_value         TEXT            NOT NULL,
     confidence       TEXT            NOT NULL,
     source           TEXT,
     first_seen       TIMESTAMPTZ     NOT NULL DEFAULT now(),
     last_seen        TIMESTAMPTZ     NOT NULL DEFAULT now(),
     verified         BOOLEAN         NOT NULL DEFAULT FALSE,
     metadata         JSONB           NOT NULL DEFAULT '{}'::jsonb,
     PRIMARY KEY (id_type, id_value)
 );
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies that the lack of an ON DELETE clause on the device_id foreign key would block device deletions, leading to orphaned data or failed merges. Adding ON DELETE CASCADE is a critical improvement for data integrity and the correct functioning of the reconciliation logic.

Medium
Persist sighting status in shadow mode

In ReconcileSightings, persist the updated sighting promotion status to the
database even when running in shadow mode to prevent displaying stale
information.

pkg/registry/registry.go [559-566]

 if promoCfg.ShadowMode {
 	recordIdentityPromotionMetrics(len(sightings), 0, eligibleAuto, shadowReady, blockedPolicy, true, now)
 	r.logger.Info().
 		Int("promotable_shadow", shadowReady).
 		Int("blocked_policy", blockedPolicy).
 		Msg("Identity reconciliation promotion shadow pass")
+
+	// Persist updated promotion status for sightings even in shadow mode.
+	if err := r.db.StoreNetworkSightings(ctx, sightings); err != nil {
+		r.logger.Warn().Err(err).Msg("Failed to update sighting promotion status in shadow mode")
+	}
 	return nil
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that sighting promotion statuses calculated in shadow mode are not persisted, which could be useful for observability. The proposed change is valid and improves the feature's utility.

Medium
Remove foreign keys from audit table

Remove the foreign key constraints on from_device_id and to_device_id in the
merge_audit table to decouple the audit log from the device lifecycle and
prevent deletion blocks.

pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql [88-97]

 CREATE TABLE IF NOT EXISTS merge_audit (
     event_id         UUID            PRIMARY KEY DEFAULT gen_random_uuid(),
-    from_device_id   TEXT            NOT NULL REFERENCES unified_devices(device_id),
-    to_device_id     TEXT            NOT NULL REFERENCES unified_devices(device_id),
+    from_device_id   TEXT            NOT NULL,
+    to_device_id     TEXT            NOT NULL,
     reason           TEXT,
     confidence_score NUMERIC,
     source           TEXT,
     details          JSONB           NOT NULL DEFAULT '{}'::jsonb,
     created_at       TIMESTAMPTZ     NOT NULL DEFAULT now()
 );
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the foreign key constraints on the merge_audit table would block device deletion, and correctly proposes removing them to ensure the audit trail's independence and prevent future operational failures.

Medium
General
Handle JSON parsing errors properly

Instead of silently ignoring JSON parsing errors, return a 400 Bad Request
response to the client to improve API robustness and debuggability.

web/src/app/api/identity/sightings/[id]/dismiss/route.ts [34-42]

 let reason = "";
 try {
   const body = await req.json();
   if (typeof body?.reason === "string") {
     reason = body.reason;
   }
-} catch {
-  // ignore parse errors and fallback to empty reason
+} catch (error) {
+  // A missing or malformed body is a client error
+  return NextResponse.json(
+    {
+      error: "Invalid request body",
+      detail: error instanceof Error ? error.message : "Could not parse JSON body",
+    },
+    { status: 400 },
+  );
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that silently ignoring JSON parsing errors is poor practice and proposes a more robust error handling strategy by returning a 400 error.

Medium
  • Update
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2022#issuecomment-3584202374 Original created: 2025-11-27T04:55:27Z --- _You are nearing your monthly Qodo Merge usage quota. For more information, please visit [here](https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users)._ ## PR Code Suggestions ✨ <!-- d11299a --> Explore these optional code suggestions: <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=6>Possible issue</td> <td> <details><summary>Fix incorrect promise handling for route parameters</summary> ___ **Fix the <code>RouteProps</code> interface and remove the <code>await</code> keyword when accessing <br><code>props.params</code> to prevent a runtime error in the Next.js route handler.** [web/src/app/api/identity/sightings/[id]/events/route.ts [20-25]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-e84537bbe57ef86554926810c838b1d58656272ed0915d34ee0244cbf1b047ebR20-R25) ```diff interface RouteProps { - params: Promise<{ id: string }>; + params: { id: string }; } export async function GET(req: NextRequest, props: RouteProps) { - const { id } = await props.params; + const { id } = props.params; ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=0 --> <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a critical runtime error in the new API route handler caused by incorrectly awaiting a non-promise `params` object, which would make the endpoint fail. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Fix incorrect handling of searchParams</summary> ___ **Correct the handling of the <code>searchParams</code> prop by removing the unnecessary <code>await</code> <br>and updating its type from a <code>Promise</code> to a plain object, as it is not a promise <br>in Next.js App Router pages.** [web/src/app/identity/page.tsx [26-40]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-faca274c1cefd7b665633ec6b133a58b0de89820f493f092867037197803f9f6R26-R40) ```diff type IdentityPageProps = { - searchParams?: Promise<{ [key: string]: string | string[] | undefined }>; + searchParams?: { [key: string]: string | string[] | undefined }; }; export default async function IdentityPage({ searchParams }: IdentityPageProps) { noStore(); - const resolved = searchParams ? await searchParams : undefined; - const prefillSightingId = - typeof resolved?.sighting === "string" ? resolved.sighting : undefined; + typeof searchParams?.sighting === "string" ? searchParams.sighting : undefined; const historyActorFilter = - typeof resolved?.history_actor === "string" ? resolved.history_actor : undefined; + typeof searchParams?.history_actor === "string" + ? searchParams.history_actor + : undefined; const historyPartitionFilter = - typeof resolved?.history_partition === "string" ? resolved.history_partition : undefined; + typeof searchParams?.history_partition === "string" + ? searchParams.history_partition + : undefined; return ( ... ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a fundamental misunderstanding of the Next.js App Router's `searchParams` prop, which is an object, not a Promise. Correcting this prevents a runtime error and fixes the component's core logic for handling URL parameters. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Fix an undefined function call</summary> ___ **Add the missing helper function <code>toNullableString</code> in <br><code>pkg/db/cnpg_identity_reconciliation_upserts.go</code> to fix a compilation error in <br><code>buildSightingEventArgs</code>.** [pkg/db/cnpg_identity_reconciliation_upserts.go [195-202]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-957dbd9cd93b91df2c274b2a7e01e238709d9a0e759081e4ac02b957047f28f7R195-R202) ```diff return []interface{}{ sightingID, toNullableString(stringPtr(ev.DeviceID)), eventType, actor, details, sanitizeTimestamp(createdAt), }, nil +} +func toNullableString(v *string) interface{} { + if v == nil { + return nil + } + return *v + ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=2 --> <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies a call to an undefined function `toNullableString`, which would cause a compilation error, and provides the correct implementation to fix it. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Add cascade delete to foreign key</summary> ___ **Add <code>ON DELETE CASCADE</code> to the <code>device_id</code> foreign key in the <code>device_identifiers</code> <br>table to ensure associated identifiers are automatically removed when a device <br>is deleted.** [pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql [59-70]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-8e0daeec5bbb474a62e9f2f1c22236044e5d911957c9ca059b29e482e45afb4eR59-R70) ```diff CREATE TABLE IF NOT EXISTS device_identifiers ( - device_id TEXT NOT NULL REFERENCES unified_devices(device_id), + device_id TEXT NOT NULL REFERENCES unified_devices(device_id) ON DELETE CASCADE, id_type TEXT NOT NULL, id_value TEXT NOT NULL, confidence TEXT NOT NULL, source TEXT, first_seen TIMESTAMPTZ NOT NULL DEFAULT now(), last_seen TIMESTAMPTZ NOT NULL DEFAULT now(), verified BOOLEAN NOT NULL DEFAULT FALSE, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, PRIMARY KEY (id_type, id_value) ); ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=3 --> <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: This suggestion correctly identifies that the lack of an `ON DELETE` clause on the `device_id` foreign key would block device deletions, leading to orphaned data or failed merges. Adding `ON DELETE CASCADE` is a critical improvement for data integrity and the correct functioning of the reconciliation logic. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Persist sighting status in shadow mode</summary> ___ **In <code>ReconcileSightings</code>, persist the updated sighting promotion status to the <br>database even when running in shadow mode to prevent displaying stale <br>information.** [pkg/registry/registry.go [559-566]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-cb61d8f79451b9541de4a8cc0811523a68d15452b2f5971c7618ea5b423cf4ecR559-R566) ```diff if promoCfg.ShadowMode { recordIdentityPromotionMetrics(len(sightings), 0, eligibleAuto, shadowReady, blockedPolicy, true, now) r.logger.Info(). Int("promotable_shadow", shadowReady). Int("blocked_policy", blockedPolicy). Msg("Identity reconciliation promotion shadow pass") + + // Persist updated promotion status for sightings even in shadow mode. + if err := r.db.StoreNetworkSightings(ctx, sightings); err != nil { + r.logger.Warn().Err(err).Msg("Failed to update sighting promotion status in shadow mode") + } return nil } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=4 --> <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that sighting promotion statuses calculated in shadow mode are not persisted, which could be useful for observability. The proposed change is valid and improves the feature's utility. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Remove foreign keys from audit table</summary> ___ **Remove the foreign key constraints on <code>from_device_id</code> and <code>to_device_id</code> in the <br><code>merge_audit</code> table to decouple the audit log from the device lifecycle and <br>prevent deletion blocks.** [pkg/db/cnpg/migrations/00000000000009_identity_reconciliation_schema.up.sql [88-97]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-8e0daeec5bbb474a62e9f2f1c22236044e5d911957c9ca059b29e482e45afb4eR88-R97) ```diff CREATE TABLE IF NOT EXISTS merge_audit ( event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - from_device_id TEXT NOT NULL REFERENCES unified_devices(device_id), - to_device_id TEXT NOT NULL REFERENCES unified_devices(device_id), + from_device_id TEXT NOT NULL, + to_device_id TEXT NOT NULL, reason TEXT, confidence_score NUMERIC, source TEXT, details JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=5 --> <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that the foreign key constraints on the `merge_audit` table would block device deletion, and correctly proposes removing them to ensure the audit trail's independence and prevent future operational failures. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=1>General</td> <td> <details><summary>Handle JSON parsing errors properly</summary> ___ **Instead of silently ignoring JSON parsing errors, return a <code>400 Bad Request</code> <br>response to the client to improve API robustness and debuggability.** [web/src/app/api/identity/sightings/[id]/dismiss/route.ts [34-42]](https://github.com/carverauto/serviceradar/pull/2022/files#diff-60dcc31e214dbd0965b10111d70bc78b82ae79e710926ddc15d8a55c4e31467eR34-R42) ```diff let reason = ""; try { const body = await req.json(); if (typeof body?.reason === "string") { reason = body.reason; } -} catch { - // ignore parse errors and fallback to empty reason +} catch (error) { + // A missing or malformed body is a client error + return NextResponse.json( + { + error: "Invalid request body", + detail: error instanceof Error ? error.message : "Could not parse JSON body", + }, + { status: 400 }, + ); } ``` - [ ] **Apply / Chat** <!-- /improve --apply_suggestion=6 --> <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that silently ignoring JSON parsing errors is poor practice and proposes a more robust error handling strategy by returning a `400` error. </details></details></td><td align=center>Medium </td></tr> <tr><td align="center" colspan="2"> - [ ] Update <!-- /improve_multi --more_suggestions=true --> </td><td></td></tr></tbody></table>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!2481
No description provided.