Update/enforce tenant schema isolation #2635

Merged
mfreeman451 merged 22 commits from refs/pull/2635/head into testing 2026-01-08 17:04:21 +00:00
mfreeman451 commented 2026-01-06 23:24:53 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2228
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2228
Original created: 2026-01-06T23:24:53Z
Original updated: 2026-01-08T17:04:23Z
Original head: carverauto/serviceradar:update/enforce-tenant-schema-isolation
Original base: testing
Original merged: 2026-01-08T17:04:21Z 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, Bug fix, Tests


Description

  • Major architectural refactoring from poller to gateway model: Comprehensive rename and restructuring of the entire codebase to replace the legacy poller architecture with a modern gateway-centric design

  • Multi-tenant schema isolation enforcement: Implementation of tenant-scoped integrations, sources, and results storage with proper synchronization primitives across the platform

  • Gateway push-first communication model: Replaced pull-based result retrieval with push-based gateway communication via pushResultsForTenant and pushHeartbeat mechanisms

  • Protobuf schema modernization: Updated message types from PollerId to GatewayId, introduced new agent-gateway communication messages (AgentHelloRequest, AgentHelloResponse, AgentConfigRequest, AgentConfigResponse), and refactored service definitions to AgentGatewayService

  • Comprehensive gateway monitoring implementation: New 1540-line gateway health checks, recovery detection, alert handling, and streaming status report support with chunk reassembly

  • NATS bootstrap CLI functionality: Added complete NATS bootstrap implementation for operator, system account, and platform account credential generation

  • Edge onboarding service updates: Systematic terminology updates (200+ occurrences) and support for new EdgeOnboardingComponentTypeSync component type with updated KV configuration paths

  • Test suite updates: Refactored sync service tests to remove deprecated KV and gRPC client mocks, updated to focus on chunking logic and gateway architecture

  • Sanitization and terminology consistency: Updated protected key lists and SNMP checker service to use gateway terminology


Diagram Walkthrough

flowchart LR
  A["Legacy Poller<br/>Pull-based"] -->|Refactor| B["Gateway Architecture<br/>Push-based"]
  B --> C["Multi-tenant<br/>Isolation"]
  B --> D["Agent-Gateway<br/>Communication"]
  B --> E["NATS Bootstrap<br/>CLI"]
  C --> F["Tenant-scoped<br/>Storage"]
  D --> G["New RPC Methods<br/>Hello, GetConfig,<br/>PushStatus"]
  E --> H["Operator & Account<br/>Credentials"]

File Walkthrough

Relevant files
Enhancement
6 files
monitoring.pb.go
Refactor protobuf schema from poller to gateway architecture

proto/monitoring.pb.go

  • Renamed PollerId field to GatewayId across multiple message types
    (StatusRequest, ResultsRequest, StatusResponse, ResultsResponse)
  • Removed deprecated PollerStatusRequest, PollerStatusResponse, and
    ServiceStatus message types
  • Replaced PollerStatusChunk with new GatewayStatusRequest,
    GatewayStatusResponse, GatewayStatusChunk, and GatewayServiceStatus
    types
  • Added new agent-gateway communication messages: AgentHelloRequest,
    AgentHelloResponse, AgentConfigRequest, AgentConfigResponse,
    AgentCheckConfig
  • Added TenantId and TenantSlug fields to gateway-related messages for
    multi-tenant support
  • Updated service definitions from PollerService to AgentGatewayService
    with new RPC methods (Hello, GetConfig, PushStatus, StreamStatus)
+1039/-404
nats_bootstrap.go
Add comprehensive NATS bootstrap CLI implementation           

pkg/cli/nats_bootstrap.go

  • New file implementing NATS bootstrap functionality for ServiceRadar
    CLI
  • Provides handlers for nats-bootstrap and admin nats subcommands with
    flag parsing
  • Implements bootstrap workflow: API-based bootstrap, local bootstrap,
    and verification modes
  • Generates NATS operator, system account, and platform account
    credentials and configuration files
  • Includes helper functions for generating system and platform account
    credentials
  • Supports token generation, status checking, and tenant listing via
    Core API
+961/-0 
service.go
Multi-tenant gateway push-first sync service refactoring 

pkg/sync/service.go

  • Refactored sync service to support multi-tenant architecture with
    gateway push-first communication model
  • Removed KV client and gRPC client dependencies; added gateway client
    for push-based results and heartbeat
  • Introduced tenant-scoped integrations, sources, and results storage
    with proper synchronization primitives
  • Implemented config polling loop, heartbeat mechanism, and gateway
    enrollment/enrollment status tracking
  • Replaced legacy pull-based GetResults and StreamResults with gateway
    push via pushResultsForTenant and pushHeartbeat
  • Added chunking logic for large result payloads with configurable max
    chunk size
+1112/-317
gateways.go
Gateway monitoring and status management implementation   

pkg/core/gateways.go

  • New comprehensive gateway monitoring and status management
    implementation with 1540 lines of code
  • Implements gateway health checks, recovery detection, and alert
    handling with caching mechanisms
  • Adds streaming status report support for large datasets with chunk
    reassembly and service message handling
  • Includes device registration, service registry integration, and tenant
    schema isolation enforcement
+1540/-0
edge_onboarding.go
Rename poller to gateway in edge onboarding service           

pkg/core/edge_onboarding.go

  • Systematic rename of "poller" terminology to "gateway" throughout the
    edge onboarding service (200+ occurrences)
  • Updates type names, function names, variable names, and comments to
    reflect gateway-centric architecture
  • Adds support for EdgeOnboardingComponentTypeSync component type in
    package creation and validation
  • Updates KV configuration paths from config/pollers/ to
    config/gateways/ and related metadata keys
+208/-206
main.go
Update SNMP checker to use gateway terminology                     

cmd/checkers/snmp/main.go

  • Updates SNMP checker service instantiation from NewSNMPPollerService
    to NewSNMPGatewayService
  • Changes struct type from snmp.Poller to snmp.Gateway for consistency
    with gateway terminology
+1/-1     
Documentation
1 files
main.go
Update API documentation terminology                                         

cmd/core/main.go

  • Updated API description from "service pollers" to "service gateways"
    in Swagger documentation
+1/-1     
Configuration changes
2 files
prod.exs
Add production configuration for Elixir agent gateway       

elixir/serviceradar_agent_gateway/config/prod.exs

  • New file with production-specific Elixir configuration
  • Sets logger level to info for production environment
+4/-0     
.gitkeep
Add credentials directory placeholder                                       

docker/compose/creds/.gitkeep

  • Creates new directory placeholder file for Docker Compose credentials
    storage
+1/-0     
Dependencies
2 files
nats_account.pb.go
NATS account management protobuf definitions                         

proto/nats_account.pb.go

  • Auto-generated protobuf code for NATS account management service
  • Defines message types for tenant account creation, user credential
    generation, and JWT signing
  • Includes operator bootstrap, account limits, subject mappings, and
    permission management
  • Provides gRPC service definitions for account lifecycle operations
+1266/-0
nats_account_grpc.pb.go
NATS account service gRPC implementation                                 

proto/nats_account_grpc.pb.go

  • Auto-generated gRPC service code for NATS account management
    operations
  • Implements six RPC methods for operator bootstrap, account creation,
    and JWT signing/pushing
  • Provides both client and server interfaces with proper error handling
    and interceptor support
+376/-0 
Tests
1 files
armis_netbox_test.go
Update sync service tests for gateway architecture             

pkg/sync/armis_netbox_test.go

  • Updated test to remove KV and gRPC client mocks no longer used by sync
    service
  • Changed PollerID references to GatewayID throughout test fixtures
  • Simplified test to focus on chunking logic via buildResultsChunks
    instead of full streaming
  • Removed mock stream expectations and proto imports
+18/-153
Bug fix
1 files
sanitize.go
Update sanitization for gateway identifier                             

pkg/deviceupdate/sanitize.go

  • Updated protected key list to use gateway_id instead of deprecated
    poller_id
+1/-1     
Additional files
101 files
.bazelignore +4/-0     
.bazelrc +5/-0     
.env-sample +33/-0   
.env.example +38/-0   
main.yml +18/-0   
AGENTS.md +175/-11
INSTALL.md +11/-11 
MODULE.bazel +5/-0     
Makefile +47/-14 
README-Docker.md +17/-2   
README.md +3/-3     
ROADMAP.md +1/-1     
BUILD.bazel +11/-6   
BUILD.bazel +12/-0   
mix_release.bzl +124/-49
BUILD.bazel +1/-0     
README.md +4/-4     
config.json +5/-6     
main.go +174/-74
build.rs +0/-1     
monitoring.proto +3/-26   
server.rs +2/-0     
README.md +2/-2     
monitoring.proto +2/-26   
server.rs +6/-6     
main.go +16/-2   
config.rs +26/-0   
grpc_server.rs +2/-2     
message_processor.rs +1/-0     
nats.rs +4/-0     
zen-consumer-with-otel.json +1/-0     
zen-consumer.json +1/-0     
app.go +1/-1     
config.json +4/-4     
config.json +4/-4     
BUILD.bazel +1/-0     
main.go +68/-0   
README.md +3/-3     
README.md +9/-12   
flowgger.toml +1/-0     
nats_output.rs +14/-0   
otel.toml +1/-0     
config.rs +13/-0   
nats_output.rs +7/-0     
setup.rs +1/-0     
BUILD.bazel +0/-25   
config.json +0/-111 
main.go +0/-138 
BUILD.bazel +0/-25   
config.json +0/-77   
main.go +0/-123 
main.go +1/-1     
config.rs +21/-0   
main.rs +23/-3   
docker-compose.elx.yml +109/-0 
docker-compose.spiffe.yml +8/-157 
docker-compose.yml +299/-280
Dockerfile.agent-gateway +94/-0   
Dockerfile.core-elx +108/-0 
Dockerfile.poller +0/-70   
Dockerfile.sync +0/-95   
Dockerfile.tools +1/-2     
Dockerfile.web-ng +6/-0     
agent-minimal.docker.json +6/-6     
agent.docker.json +5/-20   
agent.mtls.json +7/-10   
bootstrap-nested-spire.sh +0/-80   
datasvc.docker.json +3/-2     
datasvc.mtls.json +14/-1   
db-event-writer.docker.json +2/-2     
db-event-writer.mtls.json +3/-2     
FRICTION_POINTS.md +0/-355 
README.md +0/-207 
SETUP_GUIDE.md +0/-307 
docker-compose.edge-e2e.yml +0/-27   
manage-packages.sh +0/-211 
setup-edge-e2e.sh +0/-198 
edge-poller-restart.sh +0/-178 
downstream-agent.conf +0/-32   
env +0/-4     
server.conf +0/-51   
upstream-agent.conf +0/-32   
entrypoint-certs.sh +13/-9   
entrypoint-poller.sh +0/-274 
entrypoint-sync.sh +0/-96   
fix-cert-permissions.sh +2/-2     
flowgger.docker.toml +2/-1     
generate-certs.sh +214/-12
nats.docker.conf +16/-160
netflow-consumer.mtls.json +1/-0     
otel.docker.toml +2/-0     
pg_hba.conf +9/-0     
pg_ident.conf +17/-0   
poller-stack.compose.yml +0/-121 
poller.docker.json +0/-128 
poller.mtls.json +0/-135 
poller.spiffe.json +0/-55   
refresh-upstream-credentials.sh +0/-248 
seed-poller-kv.sh +0/-83   
setup-edge-poller.sh +0/-204 
Additional files not shown

Imported from GitHub pull request. Original GitHub pull request: #2228 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2228 Original created: 2026-01-06T23:24:53Z Original updated: 2026-01-08T17:04:23Z Original head: carverauto/serviceradar:update/enforce-tenant-schema-isolation Original base: testing Original merged: 2026-01-08T17:04:21Z 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, Bug fix, Tests ___ ### **Description** - **Major architectural refactoring from poller to gateway model**: Comprehensive rename and restructuring of the entire codebase to replace the legacy poller architecture with a modern gateway-centric design - **Multi-tenant schema isolation enforcement**: Implementation of tenant-scoped integrations, sources, and results storage with proper synchronization primitives across the platform - **Gateway push-first communication model**: Replaced pull-based result retrieval with push-based gateway communication via `pushResultsForTenant` and `pushHeartbeat` mechanisms - **Protobuf schema modernization**: Updated message types from `PollerId` to `GatewayId`, introduced new agent-gateway communication messages (`AgentHelloRequest`, `AgentHelloResponse`, `AgentConfigRequest`, `AgentConfigResponse`), and refactored service definitions to `AgentGatewayService` - **Comprehensive gateway monitoring implementation**: New 1540-line gateway health checks, recovery detection, alert handling, and streaming status report support with chunk reassembly - **NATS bootstrap CLI functionality**: Added complete NATS bootstrap implementation for operator, system account, and platform account credential generation - **Edge onboarding service updates**: Systematic terminology updates (200+ occurrences) and support for new `EdgeOnboardingComponentTypeSync` component type with updated KV configuration paths - **Test suite updates**: Refactored sync service tests to remove deprecated KV and gRPC client mocks, updated to focus on chunking logic and gateway architecture - **Sanitization and terminology consistency**: Updated protected key lists and SNMP checker service to use gateway terminology ___ ### Diagram Walkthrough ```mermaid flowchart LR A["Legacy Poller<br/>Pull-based"] -->|Refactor| B["Gateway Architecture<br/>Push-based"] B --> C["Multi-tenant<br/>Isolation"] B --> D["Agent-Gateway<br/>Communication"] B --> E["NATS Bootstrap<br/>CLI"] C --> F["Tenant-scoped<br/>Storage"] D --> G["New RPC Methods<br/>Hello, GetConfig,<br/>PushStatus"] E --> H["Operator & Account<br/>Credentials"] ``` <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>6 files</summary><table> <tr> <td> <details> <summary><strong>monitoring.pb.go</strong><dd><code>Refactor protobuf schema from poller to gateway architecture</code></dd></summary> <hr> proto/monitoring.pb.go <ul><li>Renamed <code>PollerId</code> field to <code>GatewayId</code> across multiple message types <br>(<code>StatusRequest</code>, <code>ResultsRequest</code>, <code>StatusResponse</code>, <code>ResultsResponse</code>)<br> <li> Removed deprecated <code>PollerStatusRequest</code>, <code>PollerStatusResponse</code>, and <br><code>ServiceStatus</code> message types<br> <li> Replaced <code>PollerStatusChunk</code> with new <code>GatewayStatusRequest</code>, <br><code>GatewayStatusResponse</code>, <code>GatewayStatusChunk</code>, and <code>GatewayServiceStatus</code> <br>types<br> <li> Added new agent-gateway communication messages: <code>AgentHelloRequest</code>, <br><code>AgentHelloResponse</code>, <code>AgentConfigRequest</code>, <code>AgentConfigResponse</code>, <br><code>AgentCheckConfig</code><br> <li> Added <code>TenantId</code> and <code>TenantSlug</code> fields to gateway-related messages for <br>multi-tenant support<br> <li> Updated service definitions from <code>PollerService</code> to <code>AgentGatewayService</code> <br>with new RPC methods (<code>Hello</code>, <code>GetConfig</code>, <code>PushStatus</code>, <code>StreamStatus</code>)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4f7e955b42854cc9cf3fb063b95e58a04f36271e6a0c1cb42ea6d7953dd96cc4">+1039/-404</a></td> </tr> <tr> <td> <details> <summary><strong>nats_bootstrap.go</strong><dd><code>Add comprehensive NATS bootstrap CLI implementation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/cli/nats_bootstrap.go <ul><li>New file implementing NATS bootstrap functionality for ServiceRadar <br>CLI<br> <li> Provides handlers for <code>nats-bootstrap</code> and <code>admin nats</code> subcommands with <br>flag parsing<br> <li> Implements bootstrap workflow: API-based bootstrap, local bootstrap, <br>and verification modes<br> <li> Generates NATS operator, system account, and platform account <br>credentials and configuration files<br> <li> Includes helper functions for generating system and platform account <br>credentials<br> <li> Supports token generation, status checking, and tenant listing via <br>Core API</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2176d03ba6ab4ccc1b0587a4727171546eecf6123e4df815ead583aaab062457">+961/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>service.go</strong><dd><code>Multi-tenant gateway push-first sync service refactoring</code>&nbsp; </dd></summary> <hr> pkg/sync/service.go <ul><li>Refactored sync service to support multi-tenant architecture with <br>gateway push-first communication model<br> <li> Removed KV client and gRPC client dependencies; added gateway client <br>for push-based results and heartbeat<br> <li> Introduced tenant-scoped integrations, sources, and results storage <br>with proper synchronization primitives<br> <li> Implemented config polling loop, heartbeat mechanism, and gateway <br>enrollment/enrollment status tracking<br> <li> Replaced legacy pull-based <code>GetResults</code> and <code>StreamResults</code> with gateway <br>push via <code>pushResultsForTenant</code> and <code>pushHeartbeat</code><br> <li> Added chunking logic for large result payloads with configurable max <br>chunk size</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4ea5a5b3d811fa94c6a84c71c0ddb920ce177a376d13cb776f05dd6017c4c7e">+1112/-317</a></td> </tr> <tr> <td> <details> <summary><strong>gateways.go</strong><dd><code>Gateway monitoring and status management implementation</code>&nbsp; &nbsp; </dd></summary> <hr> pkg/core/gateways.go <ul><li>New comprehensive gateway monitoring and status management <br>implementation with 1540 lines of code<br> <li> Implements gateway health checks, recovery detection, and alert <br>handling with caching mechanisms<br> <li> Adds streaming status report support for large datasets with chunk <br>reassembly and service message handling<br> <li> Includes device registration, service registry integration, and tenant <br>schema isolation enforcement</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-260332914ad2238e720f4637a71b0f9f01e899102bc6b37f7827782e56b0b5c0">+1540/-0</a></td> </tr> <tr> <td> <details> <summary><strong>edge_onboarding.go</strong><dd><code>Rename poller to gateway in edge onboarding service</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/core/edge_onboarding.go <ul><li>Systematic rename of "poller" terminology to "gateway" throughout the <br>edge onboarding service (200+ occurrences)<br> <li> Updates type names, function names, variable names, and comments to <br>reflect gateway-centric architecture<br> <li> Adds support for <code>EdgeOnboardingComponentTypeSync</code> component type in <br>package creation and validation<br> <li> Updates KV configuration paths from <code>config/pollers/</code> to <br><code>config/gateways/</code> and related metadata keys</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-85874e3c4bdcc9110db09909f10648d44cdee554b26c987f910502321eb20b5c">+208/-206</a></td> </tr> <tr> <td> <details> <summary><strong>main.go</strong><dd><code>Update SNMP checker to use gateway terminology</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> cmd/checkers/snmp/main.go <ul><li>Updates SNMP checker service instantiation from <code>NewSNMPPollerService</code> <br>to <code>NewSNMPGatewayService</code><br> <li> Changes struct type from <code>snmp.Poller</code> to <code>snmp.Gateway</code> for consistency <br>with gateway terminology</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-f25402eade63525184cb5e7437accff93c7b9338eebe81add6dc5f2a9eb12550">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>main.go</strong><dd><code>Update API documentation terminology</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> cmd/core/main.go <ul><li>Updated API description from "service pollers" to "service gateways" <br>in Swagger documentation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4ab3fd1d4debc53dd2499d94a0f60c648fdae4235dd1e3678095a975f5bb434a">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>prod.exs</strong><dd><code>Add production configuration for Elixir agent gateway</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> elixir/serviceradar_agent_gateway/config/prod.exs <ul><li>New file with production-specific Elixir configuration<br> <li> Sets logger level to <code>info</code> for production environment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-5fdc859dfda96a02326392f61218325913f22f9f0c75a1276ee940d5db9fc62b">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>.gitkeep</strong><dd><code>Add credentials directory placeholder</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/compose/creds/.gitkeep <ul><li>Creates new directory placeholder file for Docker Compose credentials <br>storage</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d72c41aab2d6f2c230a4340dfefe7917cdd12bed942c825aa0d4c9875a637bac">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Dependencies</strong></td><td><details><summary>2 files</summary><table> <tr> <td> <details> <summary><strong>nats_account.pb.go</strong><dd><code>NATS account management protobuf definitions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> proto/nats_account.pb.go <ul><li>Auto-generated protobuf code for NATS account management service<br> <li> Defines message types for tenant account creation, user credential <br>generation, and JWT signing<br> <li> Includes operator bootstrap, account limits, subject mappings, and <br>permission management<br> <li> Provides gRPC service definitions for account lifecycle operations</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-49eb93c28e2d86d8dcf4ee78fd24bed498cf3fcfaa8e49849a36e70980420087">+1266/-0</a></td> </tr> <tr> <td> <details> <summary><strong>nats_account_grpc.pb.go</strong><dd><code>NATS account service gRPC implementation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> proto/nats_account_grpc.pb.go <ul><li>Auto-generated gRPC service code for NATS account management <br>operations<br> <li> Implements six RPC methods for operator bootstrap, account creation, <br>and JWT signing/pushing<br> <li> Provides both client and server interfaces with proper error handling <br>and interceptor support</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-af98ae5f073d25a2ea0a044c996542fc65d215f6a70f14a9fba0447d87b34e11">+376/-0</a>&nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>armis_netbox_test.go</strong><dd><code>Update sync service tests for gateway architecture</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/sync/armis_netbox_test.go <ul><li>Updated test to remove KV and gRPC client mocks no longer used by sync <br>service<br> <li> Changed <code>PollerID</code> references to <code>GatewayID</code> throughout test fixtures<br> <li> Simplified test to focus on chunking logic via <code>buildResultsChunks</code> <br>instead of full streaming<br> <li> Removed mock stream expectations and proto imports</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-6c09f2ea7dc15ea7debda80b6c359075fc0ae2cf5d12eac07d053301c5d5af51">+18/-153</a></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>sanitize.go</strong><dd><code>Update sanitization for gateway identifier</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> pkg/deviceupdate/sanitize.go <ul><li>Updated protected key list to use <code>gateway_id</code> instead of deprecated <br><code>poller_id</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e13cc657b9aa7f5581dbf7ff1edd7437065cd38900314b7cc69f6518c980145f">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>101 files</summary><table> <tr> <td><strong>.bazelignore</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-a5641cd37d6ad98b32cdfce1980836cc68312277bc6a7052f55da02ada5bc6cf">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>.bazelrc</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-544556920c45b42cbfe40159b082ce8af6bd929e492d076769226265f215832f">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>.env-sample</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-c4368a972a7fa60d9c4e333cebf68cdb9a67acb810451125c02e3b7eb2594e3d">+33/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>.env.example</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-a3046da0d15a27e89f2afe639b25748a7ad4d9290af3e7b1b6c1a5533c8f0a8c">+38/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3">+18/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>AGENTS.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-a54ff182c7e8acf56acfd6e4b9c3ff41e2c41a31c9b211b2deb9df75d9a478f9">+175/-11</a></td> </tr> <tr> <td><strong>INSTALL.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-09b140a43ebfdd8dbec31ce72cafffd15164d2860fd390692a030bcb932b54a0">+11/-11</a>&nbsp; </td> </tr> <tr> <td><strong>MODULE.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-6136fc12446089c3db7360e923203dd114b6a1466252e71667c6791c20fe6bdc">+5/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Makefile</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52">+47/-14</a>&nbsp; </td> </tr> <tr> <td><strong>README-Docker.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9fd61d24482efe68c22d8d41e2a1dcc440f39195aa56e7a050f2abe598179efd">+17/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+3/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ROADMAP.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-683343bdf93f55ed3cada86151abb8051282e1936e58d4e0a04beca95dff6e51">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-884fa9353a5226345e44fbabea3300efc7a87dfbcde0b6a42521ca51823f1b68">+11/-6</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-0e80ea46aeb61a873324685edb96eae864c7a2004fbb7ee404b4ec951190ba10">+12/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>mix_release.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-86ec281f99363b6b6eb1f49e21d83b7eeca93a35b552b9f305fffc6855e38ccd">+124/-49</a></td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-143f8d1549d52f28906f19ce28e5568a5be474470ff103c2c1e63c3e6b08d670">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-bfd308915d0cf522e7fc76600dee687617dc69165ab22502a1d219850c0c0860">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-5b1bc8fe77422534739bdd3a38dc20d2634a86c171265c34e1b5d0c5a61b6bab">+5/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-61358711e980ccf505246fd3915f97cbd3a380e9b66f6fa5aad46749968c5ca3">+174/-74</a></td> </tr> <tr> <td><strong>build.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-251e7a923f45f8f903e510d10f183366bda06d281c8ecc3669e1858256e2186d">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>monitoring.proto</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-b56f709f4a0a3db694f2124353908318631f23e20b7846bc4b8ee869e2e0632a">+3/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-bce0f4ca6548712f224b73816825d28e831acbbff7dbed3c98671ed50f65d028">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2e9751b437fa61442aac074c7a4a912d0ac50ac3ea156ac8aedd8478d21c6bdb">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>monitoring.proto</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9faf6025eb0d3d38383f5b7ad2b733abeb38454d5e4de3e83994e94b12d87a50">+2/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2c4395fee16396339c3eea518ad9bec739174c67c9cedf62e6848c17136dd33e">+6/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-ed4d81d29a7267f93fd77e17993fd3491b9ef6ded18490b4514d10ed1d803bc2">+16/-2</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-05038f3867985e757de9027609950e682bad6d1992dac6acd7c28962a3c65dc4">+26/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>grpc_server.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e4564a93f6cf84ff91cd3d8141fc9272ec9b4ec19defd107afa42be01fcfed5b">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>message_processor.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9fcbc5358a9009e60a8cd22d21e5a9ea652787c727732d0b869e0865495114c3">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-97f7335def0ad5d644b594a1076ae2d7080b11259cbb8de22c7946cc8e4b39f8">+4/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>zen-consumer-with-otel.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-68375f1f7847e1fbdf75664f6be65b1ad94ae6ce86ed73fc5964d65054668acb">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>zen-consumer.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4d308af9802a93a0f656e8c02a3b5fcd8991407bb18360f087470db74e1f9524">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>app.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4ad8a289575edf3b163088617b7a40ae1305c29ced0c7d59b3751c57d6938072">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2423ef78d36e905ae993b69ff59f5df6b2e1b9492fb0fa8c6d0aad7c76d2d229">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-ef778d85ac6f9652c25cb0d631f0fe8dfb3edac4dde5d719a4fc2926fb5c3216">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-c62c0139ebdb337369f4067567cd2c52b8e7decb3ddfabc77f9f67b2f6e5789c">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-5e7731adfb877918cd65d9d5531621312496450fd550fea2682efca4ca8fe816">+68/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-0b0725713b87dca1de57200214a4fe04633f0d856c39aa8032280227bf8e8141">+3/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-f425b4378f84e0ba0c6f532facff17ff5d55b4dc6033d8bf35130a159cd2ba32">+9/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>flowgger.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-af9f49f931e282dca53d1f0521b036d222fe671f77e61a876a84cf4c6d7cca4d">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats_output.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-a82e2e4d413539bf0b414b5629665b19648447523994cba639c4d1238aa5a0c1">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>otel.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-c64b9ace832b8ea57a2be62f84166e03bb1904882635d444ec76a880cdf14cc0">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-abbaec651da3d6af96b482e0f77bb909b65dbe0cabd78b5803769cc9dab0a1b0">+13/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>nats_output.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-6b585ea3564a481174e04da1270e2e13edd4e2b980d02a2652d6d21e6d82a498">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>setup.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-3891f667deb20fd26e296d3e2742c57378d3764fe1743118e612465ae360391f">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e1f7c698e0e3a4e6afa971c1140e71cbf22593fbb19c81cb26b02c15c5dc46ec">+0/-25</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9edc2486fff55fc399e0ac96dba5137948a7ea7285f5ef7846835355684b7ab5">+0/-111</a>&nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4b8ec845da50cd58d011e69f9d1c30530ee1968df26616b8768bb1fc03433bbe">+0/-138</a>&nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4f5d2ea4260d490a0d6f28adde0b35eca8af77d22f3ee366a783946c53687619">+0/-25</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-bcac20d6b3cb81f0059e766839ba1ee59a885009249501b0ba1182ebb1daea25">+0/-77</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-78dc6bc53f1c760c66f43ff5f486bfe78a65bee8b2e0d4862293ec0892da2b29">+0/-123</a>&nbsp; </td> </tr> <tr> <td><strong>main.go</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-bc6eeb1b05bcb9179525e32fac1de9926b5823ec3504be546ab10c5c9740f544">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>config.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-c89b88ba4d2bf0a054d0ba69a672a92c30140b8d19503d67b980a218ffe3106d">+21/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.rs</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-33b655d8730ae3e9c844ee280787d11f1b0d5343119188273f89558805f814ba">+23/-3</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>docker-compose.elx.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9562070d7ad4a3e9b2d06567008cf35de1d96448d914b3b45bf6c36d97cdd914">+109/-0</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.spiffe.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-603fd9e7d40841d174f26b95d0cb0c9537430bf3f7a5da3ccbba4ea3d8ac66c9">+8/-157</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e45e45baeda1c1e73482975a664062aa56f20c03dd9d64a827aba57775bed0d3">+299/-280</a></td> </tr> <tr> <td><strong>Dockerfile.agent-gateway</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-332bc81a932ae08efa711a71b60fe0954d99bf17ebdab00a3baaa177a44de8b0">+94/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.core-elx</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-5ec7a971285669999af442a0c7f141c34f7fd9180257307f5c4ed12f789a2182">+108/-0</a>&nbsp; </td> </tr> <tr> <td><strong>Dockerfile.poller</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d3ba129830fb366bfe23b00db4ef6218b10fc981d3c04842b1b3b3b367a8982f">+0/-70</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.sync</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-0227933b9961fd553af1d229e89d71a0271fdc475081bbcef49b587941af1eda">+0/-95</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.tools</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-0258db71e4070e342198965f1d046f3097640850b037df8a2287a7e239630add">+1/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>Dockerfile.web-ng</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-92d43af1965575d56c3380ecc8a81024aac2ff36f039ec2d3839e9fc7852bc10">+6/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent-minimal.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-1f09fad94636c90373af8e270f6ba0332ae4f4d1df50a4909729280a3a9691e6">+6/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-5d33fe703515d03076d31261ecf946e9c6fc668cf5bf65099d49b670739e455e">+5/-20</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>agent.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-008f2216f159a9bd5db9cc90baaf6f1e64487df7af05b56ab3b9d6c4946aa95f">+7/-10</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>bootstrap-nested-spire.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-ab4746a08fb1e0b307a1e47660cd22182e283a087cba87dcbff0fdfe750f44f1">+0/-80</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>datasvc.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-3f2719d3dbfe042e8383739e3c78e74e5f851a44e5e46bea8e79c4b79fdcc34f">+3/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>datasvc.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-3a45619e57f1e6e9a31486ec7fffb33ef246e271f82bac272ee0a946b88da70a">+14/-1</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>db-event-writer.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9fc51271f7ef5bb460160013e24e44e829b730656891d26fc49d5fe72fbb3147">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>db-event-writer.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-7a33f95f7545499abf0ed9fc91b58499ab209639e4885019579c959583fc7496">+3/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>FRICTION_POINTS.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-b0653c58880f810ba832c0500733d63de309db98b43009fe73a1862494cf41bd">+0/-355</a>&nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-31849f033cfc932acee35f549c069abb1f36101c352e553dd6bff8713b29f98c">+0/-207</a>&nbsp; </td> </tr> <tr> <td><strong>SETUP_GUIDE.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4914f8640a78038e45f51235a624535672680dc902de5f107fc051f4f281913">+0/-307</a>&nbsp; </td> </tr> <tr> <td><strong>docker-compose.edge-e2e.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-575d19ea771bdf8102cb9729db43a1bfd6afc2527160e54105beeac2e314f362">+0/-27</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>manage-packages.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-3c2ff6febbddb956c71557894adaf7d0a39a1f20dda120fe126364946bc47280">+0/-211</a>&nbsp; </td> </tr> <tr> <td><strong>setup-edge-e2e.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2714e2c7e111f69ea9e9f5ddd7f6a70fa5ea96e3a53b851cb13b8b8b7cd12917">+0/-198</a>&nbsp; </td> </tr> <tr> <td><strong>edge-poller-restart.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-96a8fe52c38fd0d7c14895127df34a27be311cac89c53d28ee178661b629bd22">+0/-178</a>&nbsp; </td> </tr> <tr> <td><strong>downstream-agent.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-747de0375ced42af978ca7dac239862bdabb7f6bd0bd634f134b485517a7b4ee">+0/-32</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>env</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-686f1a954c542f2ec9bf14c3170648b65190ad242c7f3a95a0f872ae41b8b1c6">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>server.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-025f5b5ab79526cf549ca1fdb90dd659ba76b438f05a7f77d916d18728c4b572">+0/-51</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>upstream-agent.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e8a869ddf4affa31536a8d4e4e6f09c40072a7026da2c609d93c6ecf04138902">+0/-32</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>entrypoint-certs.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-83d6800b184a5233c66c69766286b0a60fece1bc64addb112d9f8dc019437f05">+13/-9</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>entrypoint-poller.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e202d27e3331088745eb55cdd2b3e40ac3f5df109d9ff5c76c0faed60772807a">+0/-274</a>&nbsp; </td> </tr> <tr> <td><strong>entrypoint-sync.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-9d5620b8e6833309dbafb8ee6b6b75c3b942d163c3fe7f1a9827958b2d640265">+0/-96</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>fix-cert-permissions.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-17ea40a11edcaa7c85bb4215fda46b5a32505246fef0ab5f3ed47b28470c5ec8">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>flowgger.docker.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-824f8797b418d4b9f5ea41e4a3741a0ed64b881f343072464489a76b7ea01008">+2/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>generate-certs.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-8298241543b4744a6ac7780c760ac5b5a0a87ba62de19c8612ebe1aba0996ebd">+214/-12</a></td> </tr> <tr> <td><strong>nats.docker.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-06f2012494f428fe1bfb304972061c2094e0d99da88ba9af6914f7776872e6eb">+16/-160</a></td> </tr> <tr> <td><strong>netflow-consumer.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-f15920e8498a24f71ce3eec4f48fe8fefbb1765a90362998af779a660fcef9e1">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>otel.docker.toml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d4af38790e3657b7589cd37a7539d5308b032f11caba7aa740ddc86bf99f4415">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>pg_hba.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-7bd5f7292054916c7e5997f4c84ac9ec07d4c945621a48936c2aed0575fb96eb">+9/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>pg_ident.conf</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-e7b8ce062e32c61fdc3bcc9e525c1f1df1c8008fbc02b11409e58c67baa17cc5">+17/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>poller-stack.compose.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-f3b5c991c2c1f7646db0ca4ed9bcb5df0f313ce6a05d8f3c890f80c873f776f5">+0/-121</a>&nbsp; </td> </tr> <tr> <td><strong>poller.docker.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d64ebb69ec31e831efd187c47a5bfff2573960306b177f6464e91cb44a3c709d">+0/-128</a>&nbsp; </td> </tr> <tr> <td><strong>poller.mtls.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-ef5d74bb3607431245c2bf06169d7fee89cae817e114035075b59a671229ab46">+0/-135</a>&nbsp; </td> </tr> <tr> <td><strong>poller.spiffe.json</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-4e04bd23a0216287d5c0bb3831e0f95e7922ed03e8386a10ae7f4873e4fdb538">+0/-55</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>refresh-upstream-credentials.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d3b3a8fcdea1b49c9e1c0ecc12d61fb6d416313520e8ad52edbee9094dbdc271">+0/-248</a>&nbsp; </td> </tr> <tr> <td><strong>seed-poller-kv.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-c12070f475dbe7dc83e747fa6ec9d2ebdbdd97921a54f372abc89a102b783ad7">+0/-83</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>setup-edge-poller.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-d7aec89d87f4cc98f4d6935e49a8f6ce571bc6dda254d894e93b60922f3a775f">+0/-204</a>&nbsp; </td> </tr> <tr> <td><strong>Additional files not shown</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/2228/files#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td> </tr> </table></details></td></tr></tbody></table> </details> ___
qodo-code-review[bot] commented 2026-01-06 23:26:36 +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/2228#issuecomment-3716718426
Original created: 2026-01-06T23:26:36Z

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
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: 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

  • 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/2228#issuecomment-3716718426 Original created: 2026-01-06T23:26:36Z --- ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/41e2093784917eae3369939ab27e073d9e525559 --> 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>🟢</td><td><details><summary><strong>No security concerns identified</strong></summary> No security vulnerabilities detected by AI analysis. Human verification advised for critical code. </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=1>🟢</td><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 rowspan=5>⚪</td> <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 2026-01-06 23:29:25 +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/2228#issuecomment-3716723482
Original created: 2026-01-06T23:29:25Z

PR Code Suggestions

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent race condition on sources

Add a read lock around the access to s.sources in runDiscovery to prevent a data
race with concurrent configuration updates.

pkg/sync/service.go [402-415]

 func (s *SimpleSyncService) runDiscovery(ctx context.Context) error {
 	start := time.Now()
 	s.logger.Info().
 		Time("started_at", start).
 		Msg("Starting discovery cycle")
 
 	tenantIntegrations := s.snapshotTenantIntegrations()
 	if len(tenantIntegrations) == 0 {
+		s.configMu.RLock()
 		allDeviceUpdates, discoveryErrors := s.runDiscoveryForIntegrations(ctx, "", "", s.sources)
+		s.configMu.RUnlock()
 		s.updateResultsStore(allDeviceUpdates)
 
 		if err := s.pushResultsForTenant(ctx, "", "", allDeviceUpdates); err != nil {
 			s.logger.Error().Err(err).Msg("Failed to push sync results to gateway")
 		}
 ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a data race on s.sources between runDiscovery and UpdateConfig and provides the correct fix using a read lock, preventing a potential panic.

High
Fill gateway identifier in chunks

Populate the GatewayId field in GatewayServiceStatus and GatewayStatusChunk
using the gatewayID from the service configuration.

pkg/sync/service.go [1190-1238]

     func (s *SimpleSyncService) buildGatewayStatusChunks(
     	chunks []*proto.ResultsChunk,
     	tenantID string,
     	tenantSlug string,
     ) []*proto.GatewayStatusChunk {
     	s.configMu.RLock()
     	agentID := s.config.AgentID
     	partition := s.config.Partition
++    gatewayID := s.config.GatewayID
     	s.configMu.RUnlock()
 
     	statusChunks := make([]*proto.GatewayStatusChunk, 0, len(chunks))
 
     	for _, chunk := range chunks {
     		…
     		status := &proto.GatewayServiceStatus{
     			ServiceName:  syncServiceName,
     			Available:    true,
     			Message:      chunk.Data,
     			ServiceType:  syncServiceType,
     			ResponseTime: 0,
     			AgentId:      agentID,
--   			GatewayId:    "",
++   			GatewayId:    gatewayID,
     			Partition:    partition,
     			Source:       syncResultsSource,
     			…
     		}
 
     		statusChunks = append(statusChunks, &proto.GatewayStatusChunk{
--   			GatewayId:   "",
++   			GatewayId:   gatewayID,
     			AgentId:     agentID,
     			Timestamp:   chunk.Timestamp,
     			Partition:   partition,
     			IsFinal:     chunk.IsFinal,
     			…
     		})
     	}
     	return statusChunks
     }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that GatewayId is being left empty in status chunks and proposes populating it from the configuration, which adds important context to the data.

Medium
Skip pushes when unenrolled

In pushResultsForTenant, handle errGatewayNotEnrolled by returning nil to skip
the push, rather than propagating it as an error.

pkg/sync/service.go [1240-1256]

     func (s *SimpleSyncService) pushResultsForTenant(
     	ctx context.Context,
     	tenantID string,
     	tenantSlug string,
     	allDeviceUpdates map[string][]*models.DeviceUpdate,
     ) error {
     	if s.gatewayClient == nil {
     		return nil
     	}
 
     	if err := s.ensureGatewayConnected(ctx); err != nil {
     		return err
     	}
-    	if err := s.ensureGatewayEnrolled(ctx); err != nil {
-    		return err
-    	}
+-    if err := s.ensureGatewayEnrolled(ctx); err != nil {
+-        return err
+-    }
++    if err := s.ensureGatewayEnrolled(ctx); err != nil {
++        if errors.Is(err, errGatewayNotEnrolled) {
++            // skip push until gateway is enrolled
++            return nil
++        }
++        return err
++    }
     	…
     }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly proposes to handle errGatewayNotEnrolled gracefully by skipping the result push instead of propagating an error, which is a more robust behavior for this specific case.

Medium
Enforce critical location data validation

Return an error instead of only logging a warning when req.Partition or
resolvedSourceIP are missing in PushStatus to enforce this critical requirement.

pkg/core/gateways.go [883-890]

 	// Validate required location fields - critical for device registration
 	if req.Partition == "" || resolvedSourceIP == "" {
-		s.logger.Warn().
+		s.logger.Error().
 			Str("gateway_id", req.GatewayId).
 			Str("partition", req.Partition).
 			Str("source_ip", resolvedSourceIP).
 			Str("source_ip_raw", req.SourceIp).
-			Msg("CRITICAL: Status report missing required location data, device registration will be skipped")
+			Msg("Status report missing required location data, rejecting.")
+		return nil, fmt.Errorf("status report from gateway %s is missing required location data (partition or source_ip)", req.GatewayId)
 	}

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that logging a warning for missing critical data is insufficient and could lead to silent failures; returning an error enforces data integrity and is a significant improvement in robustness.

Medium
General
Improve locking for tenant store

Refactor tenantResultsStore to use a more efficient double-checked locking
pattern and remove a redundant nil check for s.tenantResults.

pkg/sync/service.go [1514-1534]

 func (s *SimpleSyncService) tenantResultsStore(tenantID string) *StreamingResultsStore {
 	s.tenantMu.RLock()
-	store := s.tenantResults[tenantID]
+	store, ok := s.tenantResults[tenantID]
 	s.tenantMu.RUnlock()
-	if store != nil {
+	if ok {
 		return store
 	}
 
 	s.tenantMu.Lock()
 	defer s.tenantMu.Unlock()
-	store = s.tenantResults[tenantID]
-	if store != nil {
+	// Double-check in case another goroutine created it while we were waiting for the lock
+	if store, ok := s.tenantResults[tenantID]; ok {
 		return store
 	}
+
 	store = &StreamingResultsStore{}
-	if s.tenantResults == nil {
-		s.tenantResults = make(map[string]*StreamingResultsStore)
-	}
 	s.tenantResults[tenantID] = store
 	return store
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies an opportunity to improve the locking pattern in tenantResultsStore for better performance under contention and correctly points out a redundant nil check.

Low
Fix incomplete poller to gateway rename

In resolveGatewayID, complete the refactoring from "poller" to "gateway" by
updating remaining internal strings, default values, and error types.

pkg/core/edge_onboarding.go [1578-1620]

-func (s *edgeOnboardingService) resolvePollerID(ctx context.Context, label, override string) (string, error) {
+func (s *edgeOnboardingService) resolveGatewayID(ctx context.Context, label, override string) (string, error) {
 	candidate := strings.TrimSpace(strings.ToLower(override))
 	if candidate != "" {
-		candidate = sanitizePollerID(candidate)
+		candidate = sanitizeGatewayID(candidate)
 		if candidate == "" {
-			return "", fmt.Errorf("%w: poller_id contains no valid characters", models.ErrEdgeOnboardingInvalidRequest)
+			return "", fmt.Errorf("%w: gateway_id contains no valid characters", models.ErrEdgeOnboardingInvalidRequest)
 		}
-		if err := s.ensurePollerIDAvailable(ctx, candidate); err != nil {
+		if err := s.ensureGatewayIDAvailable(ctx, candidate); err != nil {
 			return "", err
 		}
 		return candidate, nil
 	}
 
-	base := sanitizePollerID(label)
+	base := sanitizeGatewayID(label)
 	if base == "" {
-		base = "edge-poller"
+		base = "edge-gateway"
 	}
-	if s.cfg.PollerIDPrefix != "" {
-		base = sanitizePollerID(strings.ToLower(s.cfg.PollerIDPrefix) + "-" + base)
+	if s.cfg.GatewayIDPrefix != "" {
+		base = sanitizeGatewayID(strings.ToLower(s.cfg.GatewayIDPrefix) + "-" + base)
 	}
 
 	candidate = base
-	if err := s.ensurePollerIDAvailable(ctx, candidate); err == nil {
+	if err := s.ensureGatewayIDAvailable(ctx, candidate); err == nil {
 		return candidate, nil
-	} else if !errors.Is(err, models.ErrEdgeOnboardingPollerConflict) {
+	} else if !errors.Is(err, models.ErrEdgeOnboardingGatewayConflict) {
 		return "", err
 	}
 
 	for i := 0; i < 10; i++ {
 		suffix, err := randomHex(3)
 		if err != nil {
 			return "", err
 		}
 		candidate = fmt.Sprintf("%s-%s", base, suffix)
-		if err := s.ensurePollerIDAvailable(ctx, candidate); err == nil {
+		if err := s.ensureGatewayIDAvailable(ctx, candidate); err == nil {
 			return candidate, nil
-		} else if !errors.Is(err, models.ErrEdgeOnboardingPollerConflict) {
+		} else if !errors.Is(err, models.ErrEdgeOnboardingGatewayConflict) {
 			return "", err
 		}
 	}
 
-	return "", fmt.Errorf("%w: unable to generate unique poller_id", models.ErrEdgeOnboardingPollerConflict)
+	return "", fmt.Errorf("%w: unable to generate unique gateway_id", models.ErrEdgeOnboardingGatewayConflict)
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies that the refactoring from poller to gateway is incomplete within the resolveGatewayID function, and fixing these inconsistencies improves code clarity and maintainability.

Low
Use idiomatic error check for stream

In receiveAndAssembleChunks, replace the brittle err.Error() == "EOF" check with
the more robust and idiomatic errors.Is(err, io.EOF).

pkg/core/gateways.go [991-1038]

 func (s *Server) receiveAndAssembleChunks(
 	_ context.Context, stream proto.AgentGatewayService_StreamStatusServer) ([]*proto.GatewayServiceStatus, streamMetadata, error) {
 	var metadata streamMetadata
 
 	serviceMessages := make(map[string][]byte)
 
 	serviceMetadata := make(map[string]*proto.GatewayServiceStatus)
 
 	for {
 		chunk, err := stream.Recv()
 		if err != nil {
-			if err.Error() == "EOF" {
+			if errors.Is(err, io.EOF) {
 				break
 			}
 
 			return nil, metadata, fmt.Errorf("error receiving stream chunk: %w", err)
 		}
 
 		// Extract metadata from first chunk
 		if metadata.gatewayID == "" {
 			metadata = streamMetadata{
 				gatewayID: chunk.GatewayId,
 				agentID:   chunk.AgentId,
 				partition: chunk.Partition,
 				sourceIP:  chunk.SourceIp,
 				timestamp: chunk.Timestamp,
 			}
 		}
 
 		s.logChunkReceipt(chunk)
 
 		// Process all services with normal reassembly - but we'll handle sync services specially later
 		s.collectServiceChunks(chunk.Services, serviceMessages, serviceMetadata)
 
 		if chunk.IsFinal {
 			break
 		}
 	}
 
 	// Assemble all services (including sync services that need reassembly for processing)
 	allServices := s.assembleServices(serviceMessages, serviceMetadata)
 
 	s.logger.Info().
 		Int("total_services", len(allServices)).
 		Msg("Completed service message assembly")
 
 	return allServices, metadata, nil
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out that using errors.Is(err, io.EOF) is more robust and idiomatic than string comparison for error checking, improving code quality and maintainability.

Low
  • Update
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2228#issuecomment-3716723482 Original created: 2026-01-06T23:29:25Z --- ## PR Code Suggestions ✨ <!-- 41e2093 --> 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=4>Possible issue</td> <td> <details><summary>Prevent race condition on sources</summary> ___ **Add a read lock around the access to <code>s.sources</code> in <code>runDiscovery</code> to prevent a data <br>race with concurrent configuration updates.** [pkg/sync/service.go [402-415]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4ea5a5b3d811fa94c6a84c71c0ddb920ce177a376d13cb776f05dd6017c4c7eR402-R415) ```diff func (s *SimpleSyncService) runDiscovery(ctx context.Context) error { start := time.Now() s.logger.Info(). Time("started_at", start). Msg("Starting discovery cycle") tenantIntegrations := s.snapshotTenantIntegrations() if len(tenantIntegrations) == 0 { + s.configMu.RLock() allDeviceUpdates, discoveryErrors := s.runDiscoveryForIntegrations(ctx, "", "", s.sources) + s.configMu.RUnlock() s.updateResultsStore(allDeviceUpdates) if err := s.pushResultsForTenant(ctx, "", "", allDeviceUpdates); err != nil { s.logger.Error().Err(err).Msg("Failed to push sync results to gateway") } ... ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a data race on `s.sources` between `runDiscovery` and `UpdateConfig` and provides the correct fix using a read lock, preventing a potential panic. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>Fill gateway identifier in chunks</summary> ___ **Populate the <code>GatewayId</code> field in <code>GatewayServiceStatus</code> and <code>GatewayStatusChunk</code> <br>using the <code>gatewayID</code> from the service configuration.** [pkg/sync/service.go [1190-1238]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4ea5a5b3d811fa94c6a84c71c0ddb920ce177a376d13cb776f05dd6017c4c7eR1190-R1238) ```diff func (s *SimpleSyncService) buildGatewayStatusChunks( chunks []*proto.ResultsChunk, tenantID string, tenantSlug string, ) []*proto.GatewayStatusChunk { s.configMu.RLock() agentID := s.config.AgentID partition := s.config.Partition ++ gatewayID := s.config.GatewayID s.configMu.RUnlock() statusChunks := make([]*proto.GatewayStatusChunk, 0, len(chunks)) for _, chunk := range chunks { … status := &proto.GatewayServiceStatus{ ServiceName: syncServiceName, Available: true, Message: chunk.Data, ServiceType: syncServiceType, ResponseTime: 0, AgentId: agentID, -- GatewayId: "", ++ GatewayId: gatewayID, Partition: partition, Source: syncResultsSource, … } statusChunks = append(statusChunks, &proto.GatewayStatusChunk{ -- GatewayId: "", ++ GatewayId: gatewayID, AgentId: agentID, Timestamp: chunk.Timestamp, Partition: partition, IsFinal: chunk.IsFinal, … }) } return statusChunks } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that `GatewayId` is being left empty in status chunks and proposes populating it from the configuration, which adds important context to the data. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Skip pushes when unenrolled</summary> ___ **In <code>pushResultsForTenant</code>, handle <code>errGatewayNotEnrolled</code> by returning <code>nil</code> to skip <br>the push, rather than propagating it as an error.** [pkg/sync/service.go [1240-1256]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4ea5a5b3d811fa94c6a84c71c0ddb920ce177a376d13cb776f05dd6017c4c7eR1240-R1256) ```diff func (s *SimpleSyncService) pushResultsForTenant( ctx context.Context, tenantID string, tenantSlug string, allDeviceUpdates map[string][]*models.DeviceUpdate, ) error { if s.gatewayClient == nil { return nil } if err := s.ensureGatewayConnected(ctx); err != nil { return err } - if err := s.ensureGatewayEnrolled(ctx); err != nil { - return err - } +- if err := s.ensureGatewayEnrolled(ctx); err != nil { +- return err +- } ++ if err := s.ensureGatewayEnrolled(ctx); err != nil { ++ if errors.Is(err, errGatewayNotEnrolled) { ++ // skip push until gateway is enrolled ++ return nil ++ } ++ return err ++ } … } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly proposes to handle `errGatewayNotEnrolled` gracefully by skipping the result push instead of propagating an error, which is a more robust behavior for this specific case. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>Enforce critical location data validation</summary> ___ **Return an error instead of only logging a warning when <code>req.Partition</code> or <br><code>resolvedSourceIP</code> are missing in <code>PushStatus</code> to enforce this critical requirement.** [pkg/core/gateways.go [883-890]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-260332914ad2238e720f4637a71b0f9f01e899102bc6b37f7827782e56b0b5c0R883-R890) ```diff // Validate required location fields - critical for device registration if req.Partition == "" || resolvedSourceIP == "" { - s.logger.Warn(). + s.logger.Error(). Str("gateway_id", req.GatewayId). Str("partition", req.Partition). Str("source_ip", resolvedSourceIP). Str("source_ip_raw", req.SourceIp). - Msg("CRITICAL: Status report missing required location data, device registration will be skipped") + Msg("Status report missing required location data, rejecting.") + return nil, fmt.Errorf("status report from gateway %s is missing required location data (partition or source_ip)", req.GatewayId) } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that logging a warning for missing critical data is insufficient and could lead to silent failures; returning an error enforces data integrity and is a significant improvement in robustness. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=3>General</td> <td> <details><summary>Improve locking for tenant store</summary> ___ **Refactor <code>tenantResultsStore</code> to use a more efficient double-checked locking <br>pattern and remove a redundant nil check for <code>s.tenantResults</code>.** [pkg/sync/service.go [1514-1534]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-b4ea5a5b3d811fa94c6a84c71c0ddb920ce177a376d13cb776f05dd6017c4c7eR1514-R1534) ```diff func (s *SimpleSyncService) tenantResultsStore(tenantID string) *StreamingResultsStore { s.tenantMu.RLock() - store := s.tenantResults[tenantID] + store, ok := s.tenantResults[tenantID] s.tenantMu.RUnlock() - if store != nil { + if ok { return store } s.tenantMu.Lock() defer s.tenantMu.Unlock() - store = s.tenantResults[tenantID] - if store != nil { + // Double-check in case another goroutine created it while we were waiting for the lock + if store, ok := s.tenantResults[tenantID]; ok { return store } + store = &StreamingResultsStore{} - if s.tenantResults == nil { - s.tenantResults = make(map[string]*StreamingResultsStore) - } s.tenantResults[tenantID] = store return store } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies an opportunity to improve the locking pattern in `tenantResultsStore` for better performance under contention and correctly points out a redundant nil check. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Fix incomplete poller to gateway rename</summary> ___ **In <code>resolveGatewayID</code>, complete the refactoring from "poller" to "gateway" by <br>updating remaining internal strings, default values, and error types.** [pkg/core/edge_onboarding.go [1578-1620]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-85874e3c4bdcc9110db09909f10648d44cdee554b26c987f910502321eb20b5cR1578-R1620) ```diff -func (s *edgeOnboardingService) resolvePollerID(ctx context.Context, label, override string) (string, error) { +func (s *edgeOnboardingService) resolveGatewayID(ctx context.Context, label, override string) (string, error) { candidate := strings.TrimSpace(strings.ToLower(override)) if candidate != "" { - candidate = sanitizePollerID(candidate) + candidate = sanitizeGatewayID(candidate) if candidate == "" { - return "", fmt.Errorf("%w: poller_id contains no valid characters", models.ErrEdgeOnboardingInvalidRequest) + return "", fmt.Errorf("%w: gateway_id contains no valid characters", models.ErrEdgeOnboardingInvalidRequest) } - if err := s.ensurePollerIDAvailable(ctx, candidate); err != nil { + if err := s.ensureGatewayIDAvailable(ctx, candidate); err != nil { return "", err } return candidate, nil } - base := sanitizePollerID(label) + base := sanitizeGatewayID(label) if base == "" { - base = "edge-poller" + base = "edge-gateway" } - if s.cfg.PollerIDPrefix != "" { - base = sanitizePollerID(strings.ToLower(s.cfg.PollerIDPrefix) + "-" + base) + if s.cfg.GatewayIDPrefix != "" { + base = sanitizeGatewayID(strings.ToLower(s.cfg.GatewayIDPrefix) + "-" + base) } candidate = base - if err := s.ensurePollerIDAvailable(ctx, candidate); err == nil { + if err := s.ensureGatewayIDAvailable(ctx, candidate); err == nil { return candidate, nil - } else if !errors.Is(err, models.ErrEdgeOnboardingPollerConflict) { + } else if !errors.Is(err, models.ErrEdgeOnboardingGatewayConflict) { return "", err } for i := 0; i < 10; i++ { suffix, err := randomHex(3) if err != nil { return "", err } candidate = fmt.Sprintf("%s-%s", base, suffix) - if err := s.ensurePollerIDAvailable(ctx, candidate); err == nil { + if err := s.ensureGatewayIDAvailable(ctx, candidate); err == nil { return candidate, nil - } else if !errors.Is(err, models.ErrEdgeOnboardingPollerConflict) { + } else if !errors.Is(err, models.ErrEdgeOnboardingGatewayConflict) { return "", err } } - return "", fmt.Errorf("%w: unable to generate unique poller_id", models.ErrEdgeOnboardingPollerConflict) + return "", fmt.Errorf("%w: unable to generate unique gateway_id", models.ErrEdgeOnboardingGatewayConflict) } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 5</summary> __ Why: The suggestion correctly identifies that the refactoring from `poller` to `gateway` is incomplete within the `resolveGatewayID` function, and fixing these inconsistencies improves code clarity and maintainability. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Use idiomatic error check for stream</summary> ___ **In <code>receiveAndAssembleChunks</code>, replace the brittle <code>err.Error() == "EOF"</code> check with <br>the more robust and idiomatic <code>errors.Is(err, io.EOF)</code>.** [pkg/core/gateways.go [991-1038]](https://github.com/carverauto/serviceradar/pull/2228/files#diff-260332914ad2238e720f4637a71b0f9f01e899102bc6b37f7827782e56b0b5c0R991-R1038) ```diff func (s *Server) receiveAndAssembleChunks( _ context.Context, stream proto.AgentGatewayService_StreamStatusServer) ([]*proto.GatewayServiceStatus, streamMetadata, error) { var metadata streamMetadata serviceMessages := make(map[string][]byte) serviceMetadata := make(map[string]*proto.GatewayServiceStatus) for { chunk, err := stream.Recv() if err != nil { - if err.Error() == "EOF" { + if errors.Is(err, io.EOF) { break } return nil, metadata, fmt.Errorf("error receiving stream chunk: %w", err) } // Extract metadata from first chunk if metadata.gatewayID == "" { metadata = streamMetadata{ gatewayID: chunk.GatewayId, agentID: chunk.AgentId, partition: chunk.Partition, sourceIP: chunk.SourceIp, timestamp: chunk.Timestamp, } } s.logChunkReceipt(chunk) // Process all services with normal reassembly - but we'll handle sync services specially later s.collectServiceChunks(chunk.Services, serviceMessages, serviceMetadata) if chunk.IsFinal { break } } // Assemble all services (including sync services that need reassembly for processing) allServices := s.assembleServices(serviceMessages, serviceMetadata) s.logger.Info(). Int("total_services", len(allServices)). Msg("Completed service message assembly") return allServices, metadata, nil } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion correctly points out that using `errors.Is(err, io.EOF)` is more robust and idiomatic than string comparison for error checking, improving code quality and maintainability. </details></details></td><td align=center>Low </td></tr> <tr><td align="center" colspan="2"> - [ ] Update <!-- /improve_multi --more_suggestions=true --> </td><td></td></tr></tbody></table>
mfreeman451 commented 2026-01-07 06:21:22 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR review comment.

Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2228#discussion_r2667211397
Original created: 2026-01-07T06:21:22Z
Original path: web-ng/lib/serviceradar_web_ng/edge/onboarding_events.ex
Original line: 35

hardcoded in default tenant stuff? investigating..

Imported GitHub PR review comment. Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2228#discussion_r2667211397 Original created: 2026-01-07T06:21:22Z Original path: web-ng/lib/serviceradar_web_ng/edge/onboarding_events.ex Original line: 35 --- hardcoded in default tenant stuff? investigating..
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!2635
No description provided.