1947 srql rewrite for pg #2416

Merged
mfreeman451 merged 8 commits from refs/pull/2416/head into main 2025-11-17 08:25:53 +00:00
mfreeman451 commented 2025-11-17 01:20:30 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #1948
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/1948
Original created: 2025-11-17T01:20:30Z
Original updated: 2025-11-17T08:26:40Z
Original head: carverauto/serviceradar:1947-srql-rewrite-for-pg
Original base: main
Original merged: 2025-11-17T08:25:53Z 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


Description

  • Complete rewrite of SRQL query service from OCaml to Rust with Diesel ORM backend targeting CNPG (CloudNativePG)

  • Implemented new Rust SRQL service with modular architecture:

    • parser.rs: DSL parser converting key:value syntax to AST with support for filters, ordering, limits, and time ranges
    • query/ modules: Diesel-based query builders for devices, events, and logs entities with field filtering and ordering
    • server.rs: Axum HTTP server with /healthz, /api/query, and /translate endpoints and API key authentication
    • models.rs: Diesel data models for DeviceRow, EventRow, LogRow with JSON serialization
    • time.rs: Time range parsing utilities supporting relative durations, keywords, and absolute ranges
    • pagination.rs: Cursor-based pagination with base64 URL-safe encoding
    • config.rs: Environment-based configuration loader with SRQL_* prefixed settings
    • db.rs: Async PostgreSQL connection pooling using diesel-async and bb8
  • Removed deprecated Proton-specific _tp_time fields from TypeScript types and UI components

  • Updated all build infrastructure:

    • Removed OCaml/opam dependencies from Bazel, Docker, and build configurations
    • Updated RBE executor image from v1.0.9 to v1.0.11 with Oracle Linux 9 base
    • Migrated Docker images and RPM builders from OCaml to Rust toolchain
    • Updated Cargo.toml workspace to include new rust/srql crate
  • Updated Kubernetes manifests with SRQL service deployment for base, staging, and production environments

  • Updated documentation and specifications to reflect Rust implementation and CNPG backend migration

  • Removed entire OCaml SRQL codebase (~200 files) including Proton client integration


Diagram Walkthrough

flowchart LR
  A["OCaml SRQL<br/>Proton Backend"] -->|"Complete Rewrite"| B["Rust SRQL<br/>Diesel + CNPG"]
  B --> C["Parser<br/>DSL to AST"]
  B --> D["Query Engine<br/>Devices/Events/Logs"]
  B --> E["Axum Server<br/>HTTP API"]
  D --> F["Diesel ORM<br/>PostgreSQL"]
  E --> G["API Key Auth<br/>Pagination"]
  C --> D
  F --> H["CNPG Database<br/>Hypertables"]

File Walkthrough

Relevant files
Bug fix
3 files
logs.ts
Remove deprecated timestamp field from logs type                 

web/src/types/logs.ts

  • Removed deprecated _tp_time field from the Log interface
  • Fixed trailing newline in file export statement
+1/-2     
SweepResultsTable.tsx
Remove Proton-specific timestamp field from sweep results

web/src/components/Network/SweepResultsTable.tsx

  • Removed _tp_time field from SweepResult interface (Proton-specific
    timestamp)
  • Updated discovery time display to use canonical timestamp field
    instead of _tp_time
  • Aligns UI with CNPG schema that uses standard timestamp columns
+1/-2     
Dashboard.tsx
Remove Proton-specific timestamp field from events dashboard

web/src/components/Events/Dashboard.tsx

  • Removed event._tp_time from timestamp candidates array
    (Proton-specific field)
  • Simplified date formatting to use only event.event_timestamp
  • Aligns UI with CNPG schema that uses standard timestamp columns
+1/-2     
Enhancement
16 files
events.rs
Add Diesel query builder for events entity                             

rust/srql/src/query/events.rs

  • New module implementing Diesel-based query execution for events entity
  • Supports filtering on 14 event fields (id, type, source, subject,
    datacontenttype, remote_addr, host, specversion, severity,
    short_message, version, level)
  • Implements ordering by event_timestamp with default descending sort
  • Provides execute() and to_debug_sql() functions for query planning
+349/-0 
logs.rs
Add Diesel query builder for logs entity                                 

rust/srql/src/query/logs.rs

  • New module implementing Diesel-based query execution for logs entity
  • Supports filtering on 11 log fields (trace_id, span_id, service_name,
    service_version, service_instance, scope_name, scope_version,
    severity_text, body, severity_number)
  • Implements ordering by timestamp and severity_number with default
    descending sort
  • Provides execute() and to_debug_sql() functions for query planning
+311/-0 
parser.rs
Implement SRQL DSL parser with AST generation                       

rust/srql/src/parser.rs

  • New SRQL DSL parser converting key:value syntax into structured AST
  • Supports entity selection (in:), filters, ordering (sort:/order:),
    limits, and time filters
  • Implements tokenization with quote/parenthesis depth tracking for
    complex values
  • Includes comprehensive unit tests for basic queries, list parsing, and
    time filters
+385/-0 
devices.rs
Add Diesel query builder for devices entity                           

rust/srql/src/query/devices.rs

  • New module implementing Diesel-based query execution for devices
    entity
  • Supports filtering on 11 device fields including device_id, hostname,
    ip, mac, poller_id, agent_id, is_available, device_type, service_type,
    service_status, and discovery_sources
  • Implements ordering by device_id, first_seen, last_seen, and
    version_info
  • Provides execute() and to_debug_sql() functions with default ordering
    by last_seen descending
+294/-0 
time.rs
Add time range parsing utilities for SRQL                               

rust/srql/src/time.rs

  • New time utilities module for translating SRQL time presets to chrono
    ranges
  • Supports relative durations (hours/days), keywords (today/yesterday),
    and absolute ranges
  • Implements parsing for multiple time formats including RFC3339 and
    YYYY-MM-DD HH:MM:SS
  • Includes unit tests for relative days and absolute range parsing
+199/-0 
mod.rs
Add query engine orchestrating entity-specific executors 

rust/srql/src/query/mod.rs

  • New query engine module orchestrating SRQL query execution across
    devices, events, and logs entities
  • Implements QueryEngine struct managing connection pooling and query
    planning
  • Provides execute_query() and translate() methods for query execution
    and SQL debugging
  • Handles pagination via cursor encoding/decoding and limit enforcement
+192/-0 
models.rs
Add Diesel data models for CNPG entities                                 

rust/srql/src/models.rs

  • New data models for CNPG-backed SRQL queries
  • Defines DeviceRow, EventRow, and LogRow structs with Diesel queryable
    derives
  • Implements into_json() methods for each model to serialize database
    rows to JSON
  • Includes field mappings for all queryable columns across three
    entities
+132/-0 
config.rs
Add environment-based configuration loader                             

rust/srql/src/config.rs

  • New configuration module loading SRQL service settings from
    environment variables
  • Supports SRQL_* prefixed config for listen address, database URL, pool
    size, API key, and limits
  • Implements address resolution and default value handling for optional
    settings
  • Provides fallback to DATABASE_URL if SRQL_DATABASE_URL not set
+133/-0 
server.rs
Add Axum-based HTTP server with endpoints                               

rust/srql/src/server.rs

  • New HTTP server module using Axum framework
  • Implements /healthz, /api/query, and /translate endpoints
  • Enforces API key authentication via x-api-key header
  • Manages connection pooling and request routing to query engine
+93/-0   
schema.rs
Add Diesel schema definitions for CNPG tables                       

rust/srql/src/schema.rs

  • New Diesel schema definitions for CNPG tables
  • Defines unified_devices, events, and logs table structures with column
    types
  • Maps database columns to Rust types (Text, Timestamptz, Int4, Array,
    Jsonb, Bool)
  • Includes SQL name mappings for reserved keywords (e.g., type
    event_type)
+70/-0   
pagination.rs
Add cursor-based pagination helpers                                           

rust/srql/src/pagination.rs

  • New pagination utilities for cursor-based offset encoding/decoding
  • Uses base64 URL-safe encoding for cursor payloads
  • Provides encode_cursor() and decode_cursor() functions with validation
  • Includes round-trip and error handling tests
+45/-0   
db.rs
Add async Postgres connection pooling                                       

rust/srql/src/db.rs

  • New database connection module using diesel-async with bb8 pooling
  • Implements connect_pool() function creating async Postgres connection
    pool
  • Respects max_pool_size configuration from AppConfig
+19/-0   
lib.rs
Add library root and bootstrap function                                   

rust/srql/src/lib.rs

  • New library root exposing all SRQL modules
  • Implements run() bootstrap function loading config and starting server
  • Exports public modules for config, database, error handling, models,
    and query engine
+22/-0   
telemetry.rs
Add tracing initialization utilities                                         

rust/srql/src/telemetry.rs

  • New telemetry module initializing tracing subscriber
  • Uses EnvFilter for configurable log levels (defaults to info)
  • Implements one-time initialization via OnceCell
+11/-0   
state.rs
Add application state container                                                   

rust/srql/src/state.rs

  • New application state module holding shared config and query engine
  • Implements AppState struct for Axum state management
+14/-0   
main.rs
Add main entry point for SRQL binary                                         

rust/srql/src/main.rs

  • New binary entry point for SRQL service
  • Initializes tracing and calls library run() function
+7/-0     
Error handling
1 files
error.rs
Add error types and Axum response handling                             

rust/srql/src/error.rs

  • New error handling module defining ServiceError enum with Axum
    response conversion
  • Supports Config, Auth, InvalidRequest, NotImplemented, and Internal
    error variants
  • Implements HTTP status code mapping and JSON error response
    serialization
  • Includes error logging for non-client errors
+54/-0   
Configuration changes
20 files
entrypoint-srql.sh
Migrate entrypoint from OCaml Proton to Rust CNPG               

docker/compose/entrypoint-srql.sh

  • Replaced OCaml service entrypoint with Rust-based SRQL service
    configuration
  • Removed Proton connectivity environment variables and certificate
    mappings
  • Added CNPG connection string builder with URL encoding and SSL
    parameter support
  • Implements password file loading with timeout and fallback handling
+72/-32 
packages.bzl
Update SRQL package metadata for Rust build                           

packaging/packages.bzl

  • Updated SRQL package description from OCaml to Rust implementation
  • Changed binary target from //ocaml/srql:srql_server to
    //rust/srql:srql_bin
  • Updated dependencies from OCaml runtime libraries to Postgres client
    libraries
+4/-4     
Dockerfile.rbe
Replace OCaml RBE image with Rust-compatible base               

docker/Dockerfile.rbe

  • Replaced OCaml/opam base image with Oracle Linux 9
  • Removed OCaml toolchain and opam package manager setup
  • Added PostgreSQL 16 development libraries and Rust toolchain
  • Simplified environment to support Rust and C/C++ builds with Postgres
    support
+25/-74 
Dockerfile.rpm.srql
Migrate SRQL RPM build from OCaml to Rust                               

docker/rpm/Dockerfile.rpm.srql

  • Replaced OCaml builder stage with Rust builder using rust:1.86-slim
  • Removed opam and OCaml dependency installation
  • Simplified RPM builder stage from OCaml/opam base to Oracle Linux 9
  • Updated build process to compile Rust binary and package as RPM
+43/-65 
serviceradar-srql.yaml
Add Kubernetes deployment for Rust SRQL service                   

k8s/demo/base/serviceradar-srql.yaml

  • New Kubernetes deployment manifest for Rust-based SRQL service
  • Configures CNPG connection parameters via environment variables
  • Implements health checks on /healthz endpoint
  • Includes resource limits, volume mounts for certificates, and service
    definition
+105/-0 
Dockerfile.srql-builder
Migrate SRQL builder image from OCaml to Rust                       

docker/builders/Dockerfile.srql-builder

  • Replaced OCaml/opam builder with Rust builder using rust:1.86-slim
  • Removed OCaml, dune, and Proton dependency installation
  • Added PostgreSQL development libraries and build tools
  • Simplified to multi-stage build producing Rust binary artifact
+17/-47 
docker-compose.yml
Update docker-compose SRQL config for CNPG backend             

docker-compose.yml

  • Removed Proton connectivity environment variables (PROTON_HOST,
    PROTON_PORT, PROTON_TLS, etc.)
  • Added CNPG connection parameters (CNPG_HOST, CNPG_PORT, CNPG_DATABASE,
    CNPG_USERNAME, CNPG_SSLMODE)
  • Updated health check endpoint from /health to /healthz
  • Removed proton service dependency
+12/-11 
BUILD
Remove OCaml RBE platforms and update executor version     

build/rbe/BUILD

  • Updated RBE executor container image from v1.0.9 to v1.0.11
  • Removed OCaml-specific RBE execution platforms (ocaml_rbe_ocamlopt and
    ocaml_rbe_ocamlc)
  • Changed platform comment from OCaml/opam focus to Oracle Linux based
    executor
+2/-26   
BUILD.bazel
Switch SRQL Docker image from OCaml to Rust binary             

docker/images/BUILD.bazel

  • Changed SRQL binary source from //ocaml/srql:srql_server to
    //rust/srql:srql_bin
  • Updated base image from @serviceradar_srql_linux_amd64 to
    @ubuntu_noble
  • Simplified environment variables and adjusted workdir configuration
+4/-5     
components.json
Update SRQL packaging from OCaml to Rust build configuration

packaging/components.json

  • Updated SRQL component description from OCaml to Rust query service
  • Changed dependencies from OCaml runtime (libev4, libgmp10) to
    PostgreSQL client (libpq5)
  • Updated build method from ocaml to rust and source path from
    ocaml/srql to rust/srql
  • Changed Dockerfile reference to
    docker/builders/Dockerfile.srql-builder
+6/-8     
BUILD.bazel
Update build platform RBE executor image references           

build/platforms/BUILD.bazel

  • Updated RBE container image references from
    gcr.io/buildbuddy-io/executor:latest to
    docker://ghcr.io/carverauto/serviceradar/rbe-executor:v1.0.11
  • Applied to both x86_64 and aarch64 platform configurations
+2/-2     
kustomization.yaml
Add SRQL service image to production Kustomize configuration

k8s/demo/prod/kustomization.yaml

  • Added new image entry for ghcr.io/carverauto/serviceradar-srql with
    latest tag
  • Enables SRQL service deployment in production Kubernetes environment
+2/-0     
buildbuddy.yaml
Update BuildBuddy RBE executor container image                     

buildbuddy.yaml

  • Updated execution platform container image from
    gcr.io/buildbuddy-io/executor:latest to
    docker://ghcr.io/carverauto/serviceradar/rbe-executor:v1.0.11
  • Ensures BuildBuddy RBE uses consistent executor version
+2/-2     
BUILD.bazel
New Bazel build rules for Rust SRQL service                           

rust/srql/BUILD.bazel

  • New Bazel build configuration for Rust SRQL library and binary targets
  • Defines srql_lib library with glob source patterns and srql_bin binary
    entry point
  • Integrates with workspace dependencies via all_crate_deps macro
+23/-0   
kustomization.yaml
Add SRQL service image to staging Kustomize configuration

k8s/demo/staging/kustomization.yaml

  • Added new image entry for ghcr.io/carverauto/serviceradar-srql with
    latest tag
  • Enables SRQL service deployment in staging Kubernetes environment
+2/-0     
BUILD.bazel
Update RBE executor version and cwalk alias target             

BUILD.bazel

  • Updated RBE executor container image from v1.0.9 to v1.0.11
  • Changed cwalk alias target from external repository to local path
    //third_party/cwalk:cwalk
+2/-2     
Dockerfile.srql
Update Docker Compose SRQL image to use Rust build             

docker/compose/Dockerfile.srql

  • Updated SRQL source image from versioned OCaml image to latest Rust
    image
  • Changed comment to reflect Bazel-built image instead of OCaml compiler
    stack
  • Maintains fast local Compose builds by reusing published image
+3/-4     
kustomization.yaml
Add SRQL service to base Kustomize resources                         

k8s/demo/base/kustomization.yaml

  • Added serviceradar-srql.yaml to resources list
  • Enables SRQL service deployment in base Kubernetes configuration
+1/-0     
Cargo.toml
Add Rust SRQL crate to workspace members                                 

Cargo.toml

  • Added rust/srql to workspace members list
  • Integrates new Rust SRQL service into monorepo build system
+1/-0     
main.yml
Remove OCaml test filter from CI workflow                               

.github/workflows/main.yml

  • Removed -ocaml from test tag filters in CI pipeline
  • Reflects removal of OCaml-specific tests after SRQL migration
+1/-1     
Dependencies
2 files
MODULE.bazel
Remove OCaml dependencies and update RBE image                     

MODULE.bazel

  • Removed OCaml-related Bazel dependencies (makeheaders, rules_ocaml,
    tools_opam)
  • Updated RBE executor container image tag from v1.0.9 to v1.0.11
+1/-212 
Cargo.toml
New Rust SRQL service Cargo dependencies manifest               

rust/srql/Cargo.toml

  • New Cargo manifest for Rust SRQL service with Diesel ORM and async
    PostgreSQL support
  • Included dependencies for HTTP server (Axum), serialization (serde),
    and database pooling (diesel-async)
  • Added tracing, UUID, and chrono for observability and timestamp
    handling
+28/-0   
Documentation
11 files
spec.md
Add SRQL Rust implementation specification                             

openspec/changes/replace-srql-dsl/specs/srql/spec.md

  • New specification document defining requirements for Rust SRQL service
  • Documents Diesel-backed query execution against CNPG clusters
  • Specifies backward compatibility with existing SRQL queries and
    dashboards
  • Defines removal of OCaml translator and exclusive routing to Rust
    service
+40/-0   
srql-language-reference.md
Update SRQL documentation for Rust implementation               

docs/docs/srql-language-reference.md

  • Updated documentation to reference Rust-based SRQL service instead of
    OCaml
  • Changed query planner references from ocaml/srql to rust/srql
  • Updated entity mapping references to Diesel query builders in
    rust/srql/src/query
  • Updated CLI validation tool path from OCaml to Rust implementation
+3/-3     
AGENTS.md
Update developer documentation for Rust SRQL                         

AGENTS.md

  • Updated project overview to reference Rust SRQL service instead of
    OCaml
  • Changed SRQL path from ocaml/srql/ to rust/srql/
  • Updated testing instructions from dune to cargo for SRQL tests
  • Updated formatting guidelines from dune fmt to cargo fmt for Rust code
+5/-5     
project.md
Update project overview for Rust SRQL service                       

openspec/project.md

  • Updated tech stack description from OCaml SRQL to Rust SRQL service
  • Changed SRQL path from ocaml/srql to rust/srql
  • Updated testing instructions from dune to cargo for SRQL tests
  • Updated database backend from Proton to CNPG with Diesel
+2/-2     
README.md
Add RBE executor image build and deployment guide               

k8s/buildbuddy/README.md

  • Added new section documenting executor pods vs. Bazel action image
    distinction
  • Provided instructions for building and pushing custom RBE executor
    image
  • Documented tag update process across configuration files
  • Clarified that Bazel action image is customized while executor pods
    use upstream
+23/-0   
03-proton.md
Complete architecture rewrite from Proton to CNPG unified telemetry
store

sr-architecture-and-design/prd/03-proton.md

  • Replaced entire Proton/ClickHouse architecture with CNPG + TimescaleDB
    + Apache AGE stack
  • Introduced hypertables for metrics/logs/events with compression and
    retention policies
  • Added Apache AGE graph model for topology, device relationships, and
    discovery outputs
  • Updated SRQL to compile into SQL targeting hypertables and AGE graph
    traversals
  • Simplified data flow: agents/pollers → CNPG hypertables → SRQL queries
    → dashboards/alerts
+187/-564
tasks.md
SRQL Rust translator implementation task breakdown             

openspec/changes/replace-srql-dsl/tasks.md

  • Defined task checklist for Rust SRQL translator implementation
  • Covered parser/planner porting, HTTP API surface, and Diesel
    integration with CNPG
  • Included DSL compatibility migration tasks and removal of dual-run
    plumbing
  • Added operational integration tasks for deployment manifests and
    service cutover
+14/-0   
proposal.md
SRQL DSL replacement proposal from OCaml to Rust                 

openspec/changes/replace-srql-dsl/proposal.md

  • Documented rationale for replacing OCaml SRQL with Rust implementation
  • Outlined design of Rust translator using Diesel.rs targeting CNPG
    schemas
  • Described migration strategy with dual-running, toggles, and
    documentation updates
  • Identified impact on codebase, dependencies, and operational readiness
+14/-0   
search-planner-operations.md
Update SRQL service path in operational documentation       

docs/docs/search-planner-operations.md

  • Updated SRQL service log reference from ocaml/srql to rust/srql
  • Reflects migration from OCaml to Rust implementation in operational
    documentation
+1/-1     
BUILD.md
Remove OCaml from build system dependencies                           

BUILD.md

  • Removed ocaml from system dependencies installation command
  • Reflects removal of OCaml toolchain requirement after SRQL migration
    to Rust
+1/-3     
repo_lint.md
Update repo lint documentation test file reference             

docs/LF/repo_lint.md

  • Updated test directory reference from ocaml/test_boolean_query.ml to
    rust/srql/src/lib.rs
  • Reflects repository structure change after OCaml SRQL removal
+1/-2     
Additional files
101 files
.dockerignore +0/-1     
ocaml-build-test.yml +0/-53   
ocaml-lint.yml +0/-34   
Makefile +0/-116 
README.md +0/-2     
OCAML_BAZEL_MIGRATION.md +0/-139 
.ocamlformat +0/-3     
README.md +0/-273 
PRD.md +0/-463 
dune +0/-1     
dune-project +0/-2     
srql-translator.opam +0/-36   
BUILD.bazel +0/-256 
Dockerfile +0/-75   
REMAINING_WORK.md +0/-195 
SRQL_SYNTAX.md +0/-49   
srql_translator_alias.ml +0/-12   
cli.ml +0/-38   
dune +0/-21   
main.ml +0/-807 
srql_cli.ml +0/-115 
dune +0/-8     
entity_mapping.ml +0/-78   
field_mapping.ml +0/-225 
field_mapping.mli +0/-3     
json_conv.ml +0/-205 
json_conv.mli +0/-4     
proton_client.ml +0/-286 
proton_client.mli +0/-87   
query_ast.ml +0/-23   
query_parser.ml +0/-305 
query_planner.ml +0/-642 
query_validator.ml +0/-95   
sql_ir.ml +0/-28   
sql_sanitize.ml +0/-42   
sql_sanitize.mli +0/-14   
translator.ml +0/-320 
ocaml_local_rules.bzl +0/-30   
dune +0/-20   
run_live_srql.ml +0/-356 
test_bounded_unbounded.ml +0/-66   
test_direct_proton.ml +0/-40   
test_insecure_tls.ml +0/-116 
test_json_conv.ml +0/-88   
test_proton_integration.ml +0/-136 
test_query_engine.ml +0/-369 
test_simple_connection.ml +0/-83   
test_sql_sanitize.ml +0/-190 
test_boolean_query.ml +0/-6     
test_direct_proton.cmi [link]   
test_direct_proton.cmo [link]   
test_direct_proton.ml +0/-40   
test_schema_query.ml +0/-60   
test_simple_query.ml +0/-62   
setup-package.sh +0/-38   
README.md +0/-22   
BUILD.bazel +0/-4     
platform_exec_properties.patch +0/-60   
stdlib_bundle.patch +0/-56   
stdlib_env.patch +0/-341 
0001-Fix-use-after-free-in-findlibc.patch +0/-40   
0001-Rename-ppx-deriving-ocaml-import.patch +0/-25   
0001-Sanitize-ocaml-import-target-names.patch +0/-26   
BUILD.bazel +0/-6     
MODULE.bazel +0/-26   
copy_stdlib_runtime.sh +0/-37   
.bazelignore +0/-8     
.bazelrc +0/-5     
LICENSE +0/-201 
MODULE.bazel +0/-32   
README.adoc +0/-402 
WORKSPACE +0/-2     
BUILD.bazel +0/-1     
aspects.bzl +0/-18   
cmt.bzl +0/-29   
depsets.bzl +0/-118 
ocamlcmt.bzl +0/-105 
ocamlobjinfo.bzl +0/-143 
providers.bzl +0/-52   
BUILD.bazel +0/-4     
BUILD.bazel +0/-4     
opam.bzl +0/-243 
opam.mustache +0/-23   
opam_build.bzl +0/-260 
rules.bzl +0/-14   
BUILD.bazel +0/-1     
CONFIG.bzl +0/-9     
BUILD.bazel +0/-28   
colors.bzl +0/-40   
BUILD.bazel +0/-74   
config_pkg.c +0/-261 
ext_emit_build_bazel.c +0/-571 
main.c +0/-266 
BUILD.bazel +0/-12   
dbg_repo.bzl +0/-131 
dbg_runner.bzl +0/-92   
ocamldebug_runner.c +0/-91   
obazl.opamrc +0/-400 
BUILD.bazel +0/-12   
ocaml_repo.bzl +0/-121 
Additional files not shown

Imported from GitHub pull request. Original GitHub pull request: #1948 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/1948 Original created: 2025-11-17T01:20:30Z Original updated: 2025-11-17T08:26:40Z Original head: carverauto/serviceradar:1947-srql-rewrite-for-pg Original base: main Original merged: 2025-11-17T08:25:53Z 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 ___ ### **Description** - Complete rewrite of SRQL query service from OCaml to Rust with Diesel ORM backend targeting CNPG (CloudNativePG) - Implemented new Rust SRQL service with modular architecture: * `parser.rs`: DSL parser converting key:value syntax to AST with support for filters, ordering, limits, and time ranges * `query/` modules: Diesel-based query builders for devices, events, and logs entities with field filtering and ordering * `server.rs`: Axum HTTP server with `/healthz`, `/api/query`, and `/translate` endpoints and API key authentication * `models.rs`: Diesel data models for DeviceRow, EventRow, LogRow with JSON serialization * `time.rs`: Time range parsing utilities supporting relative durations, keywords, and absolute ranges * `pagination.rs`: Cursor-based pagination with base64 URL-safe encoding * `config.rs`: Environment-based configuration loader with SRQL_* prefixed settings * `db.rs`: Async PostgreSQL connection pooling using diesel-async and bb8 - Removed deprecated Proton-specific `_tp_time` fields from TypeScript types and UI components - Updated all build infrastructure: * Removed OCaml/opam dependencies from Bazel, Docker, and build configurations * Updated RBE executor image from v1.0.9 to v1.0.11 with Oracle Linux 9 base * Migrated Docker images and RPM builders from OCaml to Rust toolchain * Updated Cargo.toml workspace to include new `rust/srql` crate - Updated Kubernetes manifests with SRQL service deployment for base, staging, and production environments - Updated documentation and specifications to reflect Rust implementation and CNPG backend migration - Removed entire OCaml SRQL codebase (~200 files) including Proton client integration ___ ### Diagram Walkthrough ```mermaid flowchart LR A["OCaml SRQL<br/>Proton Backend"] -->|"Complete Rewrite"| B["Rust SRQL<br/>Diesel + CNPG"] B --> C["Parser<br/>DSL to AST"] B --> D["Query Engine<br/>Devices/Events/Logs"] B --> E["Axum Server<br/>HTTP API"] D --> F["Diesel ORM<br/>PostgreSQL"] E --> G["API Key Auth<br/>Pagination"] C --> D F --> H["CNPG Database<br/>Hypertables"] ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Bug fix</strong></td><td><details><summary>3 files</summary><table> <tr> <td> <details> <summary><strong>logs.ts</strong><dd><code>Remove deprecated timestamp field from logs type</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> web/src/types/logs.ts <ul><li>Removed deprecated <code>_tp_time</code> field from the <code>Log</code> interface<br> <li> Fixed trailing newline in file export statement</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3cb6acbf1723419ca5e2dbeeccb5266ff904336e1237c18372b6bc1bc4442e27">+1/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>SweepResultsTable.tsx</strong><dd><code>Remove Proton-specific timestamp field from sweep results</code></dd></summary> <hr> web/src/components/Network/SweepResultsTable.tsx <ul><li>Removed <code>_tp_time</code> field from <code>SweepResult</code> interface (Proton-specific <br>timestamp)<br> <li> Updated discovery time display to use canonical <code>timestamp</code> field <br>instead of <code>_tp_time</code><br> <li> Aligns UI with CNPG schema that uses standard timestamp columns</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f5a8a6b8edb3671ab4674759deb0fd482d744638d9823cab519525ec3a291d2f">+1/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Dashboard.tsx</strong><dd><code>Remove Proton-specific timestamp field from events dashboard</code></dd></summary> <hr> web/src/components/Events/Dashboard.tsx <ul><li>Removed <code>event._tp_time</code> from timestamp candidates array <br>(Proton-specific field)<br> <li> Simplified date formatting to use only <code>event.event_timestamp</code><br> <li> Aligns UI with CNPG schema that uses standard timestamp columns</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-17227c96fc757cf48d6c146484347cb0403c012adae05041af866e428d586858">+1/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>16 files</summary><table> <tr> <td> <details> <summary><strong>events.rs</strong><dd><code>Add Diesel query builder for events entity</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/events.rs <ul><li>New module implementing Diesel-based query execution for events entity<br> <li> Supports filtering on 14 event fields (id, type, source, subject, <br>datacontenttype, remote_addr, host, specversion, severity, <br>short_message, version, level)<br> <li> Implements ordering by <code>event_timestamp</code> with default descending sort<br> <li> Provides <code>execute()</code> and <code>to_debug_sql()</code> functions for query planning</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ad603163a35223c63c68279e53a1a4c9219b9096042a168d8a87443abaa29a58">+349/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>logs.rs</strong><dd><code>Add Diesel query builder for logs entity</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/logs.rs <ul><li>New module implementing Diesel-based query execution for logs entity<br> <li> Supports filtering on 11 log fields (trace_id, span_id, service_name, <br>service_version, service_instance, scope_name, scope_version, <br>severity_text, body, severity_number)<br> <li> Implements ordering by <code>timestamp</code> and <code>severity_number</code> with default <br>descending sort<br> <li> Provides <code>execute()</code> and <code>to_debug_sql()</code> functions for query planning</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f4d3b33667f2e79a6ebe4cfff931f93c728d9a81c305ac13586e623850a504db">+311/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>parser.rs</strong><dd><code>Implement SRQL DSL parser with AST generation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/parser.rs <ul><li>New SRQL DSL parser converting key:value syntax into structured AST<br> <li> Supports entity selection (<code>in:</code>), filters, ordering (<code>sort:</code>/<code>order:</code>), <br>limits, and time filters<br> <li> Implements tokenization with quote/parenthesis depth tracking for <br>complex values<br> <li> Includes comprehensive unit tests for basic queries, list parsing, and <br>time filters</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66">+385/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>devices.rs</strong><dd><code>Add Diesel query builder for devices entity</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/query/devices.rs <ul><li>New module implementing Diesel-based query execution for devices <br>entity<br> <li> Supports filtering on 11 device fields including device_id, hostname, <br>ip, mac, poller_id, agent_id, is_available, device_type, service_type, <br>service_status, and discovery_sources<br> <li> Implements ordering by device_id, first_seen, last_seen, and <br>version_info<br> <li> Provides <code>execute()</code> and <code>to_debug_sql()</code> functions with default ordering <br>by last_seen descending</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3202f22fff6863ed7848a129c49e2323322462b379d896d3fca2e59aa6f7b4c5">+294/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>time.rs</strong><dd><code>Add time range parsing utilities for SRQL</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/time.rs <ul><li>New time utilities module for translating SRQL time presets to chrono <br>ranges<br> <li> Supports relative durations (hours/days), keywords (today/yesterday), <br>and absolute ranges<br> <li> Implements parsing for multiple time formats including RFC3339 and <br>YYYY-MM-DD HH:MM:SS<br> <li> Includes unit tests for relative days and absolute range parsing</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-53912bc0c1e18ee038a27561478ebac74affd9d13a4fdf24e760a1694ea5b587">+199/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>mod.rs</strong><dd><code>Add query engine orchestrating entity-specific executors</code>&nbsp; </dd></summary> <hr> rust/srql/src/query/mod.rs <ul><li>New query engine module orchestrating SRQL query execution across <br>devices, events, and logs entities<br> <li> Implements <code>QueryEngine</code> struct managing connection pooling and query <br>planning<br> <li> Provides <code>execute_query()</code> and <code>translate()</code> methods for query execution <br>and SQL debugging<br> <li> Handles pagination via cursor encoding/decoding and limit enforcement</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491">+192/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>models.rs</strong><dd><code>Add Diesel data models for CNPG entities</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/models.rs <ul><li>New data models for CNPG-backed SRQL queries<br> <li> Defines <code>DeviceRow</code>, <code>EventRow</code>, and <code>LogRow</code> structs with Diesel queryable <br>derives<br> <li> Implements <code>into_json()</code> methods for each model to serialize database <br>rows to JSON<br> <li> Includes field mappings for all queryable columns across three <br>entities</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-57c82b01f92e9bd40063d0b0178c12d452771ac133f2121fb0ac008b167da367">+132/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>config.rs</strong><dd><code>Add environment-based configuration loader</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/config.rs <ul><li>New configuration module loading SRQL service settings from <br>environment variables<br> <li> Supports SRQL_* prefixed config for listen address, database URL, pool <br>size, API key, and limits<br> <li> Implements address resolution and default value handling for optional <br>settings<br> <li> Provides fallback to DATABASE_URL if SRQL_DATABASE_URL not set</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8a094b8b89c66adf329551634fa2c9c0e2ad9a1043f42012ad11cd3bf3f8ab6a">+133/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>server.rs</strong><dd><code>Add Axum-based HTTP server with endpoints</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/server.rs <ul><li>New HTTP server module using Axum framework<br> <li> Implements <code>/healthz</code>, <code>/api/query</code>, and <code>/translate</code> endpoints<br> <li> Enforces API key authentication via <code>x-api-key</code> header<br> <li> Manages connection pooling and request routing to query engine</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811">+93/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>schema.rs</strong><dd><code>Add Diesel schema definitions for CNPG tables</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/schema.rs <ul><li>New Diesel schema definitions for CNPG tables<br> <li> Defines <code>unified_devices</code>, <code>events</code>, and <code>logs</code> table structures with column <br>types<br> <li> Maps database columns to Rust types (Text, Timestamptz, Int4, Array, <br>Jsonb, Bool)<br> <li> Includes SQL name mappings for reserved keywords (e.g., <code>type</code> → <br><code>event_type</code>)</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fe4906f42b52f96b7bc2d3431885b996b6701cfb88416905ae130b472d536ea">+70/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>pagination.rs</strong><dd><code>Add cursor-based pagination helpers</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> rust/srql/src/pagination.rs <ul><li>New pagination utilities for cursor-based offset encoding/decoding<br> <li> Uses base64 URL-safe encoding for cursor payloads<br> <li> Provides <code>encode_cursor()</code> and <code>decode_cursor()</code> functions with validation<br> <li> Includes round-trip and error handling tests</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-51495b72fd38e815762d3e169edd82b4cfc9af718d4e652be6361caad52f63ee">+45/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>db.rs</strong><dd><code>Add async Postgres connection pooling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/db.rs <ul><li>New database connection module using diesel-async with bb8 pooling<br> <li> Implements <code>connect_pool()</code> function creating async Postgres connection <br>pool<br> <li> Respects max_pool_size configuration from AppConfig</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8d5a0c48024aa12a3e06de065ca71e940d2b5007fbc34ef87471b90d7937891c">+19/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>lib.rs</strong><dd><code>Add library root and bootstrap function</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/lib.rs <ul><li>New library root exposing all SRQL modules<br> <li> Implements <code>run()</code> bootstrap function loading config and starting server<br> <li> Exports public modules for config, database, error handling, models, <br>and query engine</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8c0401c0d0bb9761ed66ff5328703755132a87d625f77878f53037e2329644b8">+22/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>telemetry.rs</strong><dd><code>Add tracing initialization utilities</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> rust/srql/src/telemetry.rs <ul><li>New telemetry module initializing tracing subscriber<br> <li> Uses <code>EnvFilter</code> for configurable log levels (defaults to info)<br> <li> Implements one-time initialization via <code>OnceCell</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ad796cd13af93c5a23a761311c19ce05b49f394fd3ec0d4a453aa1d36b34dff4">+11/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>state.rs</strong><dd><code>Add application state container</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; &nbsp; </dd></summary> <hr> rust/srql/src/state.rs <ul><li>New application state module holding shared config and query engine<br> <li> Implements <code>AppState</code> struct for Axum state management</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-763a99a03d0249664ae2a7f00544c28f8fa9126d50d27272213c79e31c8a556e">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>main.rs</strong><dd><code>Add main entry point for SRQL binary</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> rust/srql/src/main.rs <ul><li>New binary entry point for SRQL service<br> <li> Initializes tracing and calls library <code>run()</code> function</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7b2037bd92516b1fa742ee8a7390098072e890be55a220393358c8b7c739a779">+7/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Error handling</strong></td><td><details><summary>1 files</summary><table> <tr> <td> <details> <summary><strong>error.rs</strong><dd><code>Add error types and Axum response handling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/src/error.rs <ul><li>New error handling module defining <code>ServiceError</code> enum with Axum <br>response conversion<br> <li> Supports Config, Auth, InvalidRequest, NotImplemented, and Internal <br>error variants<br> <li> Implements HTTP status code mapping and JSON error response <br>serialization<br> <li> Includes error logging for non-client errors</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-bbce632df3c872615ffcaf66d552a3358906e98b03a46c2ead10ce9db260b3f0">+54/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>20 files</summary><table> <tr> <td> <details> <summary><strong>entrypoint-srql.sh</strong><dd><code>Migrate entrypoint from OCaml Proton to Rust CNPG</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/compose/entrypoint-srql.sh <ul><li>Replaced OCaml service entrypoint with Rust-based SRQL service <br>configuration<br> <li> Removed Proton connectivity environment variables and certificate <br>mappings<br> <li> Added CNPG connection string builder with URL encoding and SSL <br>parameter support<br> <li> Implements password file loading with timeout and fallback handling</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514ac">+72/-32</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>packages.bzl</strong><dd><code>Update SRQL package metadata for Rust build</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> packaging/packages.bzl <ul><li>Updated SRQL package description from OCaml to Rust implementation<br> <li> Changed binary target from <code>//ocaml/srql:srql_server</code> to <br><code>//rust/srql:srql_bin</code><br> <li> Updated dependencies from OCaml runtime libraries to Postgres client <br>libraries</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-9bfe2a5141a9e402bb5a5a8fca53b9eea64396ec18108c535392e1054c90b913">+4/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Dockerfile.rbe</strong><dd><code>Replace OCaml RBE image with Rust-compatible base</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/Dockerfile.rbe <ul><li>Replaced OCaml/opam base image with Oracle Linux 9<br> <li> Removed OCaml toolchain and opam package manager setup<br> <li> Added PostgreSQL 16 development libraries and Rust toolchain<br> <li> Simplified environment to support Rust and C/C++ builds with Postgres <br>support</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-40936cbae5822a0a5fa8016befa08eb3a7836c93328e8043dcdfb3885a6201b2">+25/-74</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Dockerfile.rpm.srql</strong><dd><code>Migrate SRQL RPM build from OCaml to Rust</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/rpm/Dockerfile.rpm.srql <ul><li>Replaced OCaml builder stage with Rust builder using <code>rust:1.86-slim</code><br> <li> Removed opam and OCaml dependency installation<br> <li> Simplified RPM builder stage from OCaml/opam base to Oracle Linux 9<br> <li> Updated build process to compile Rust binary and package as RPM</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-4aed257fbbf9eddfb69e5c2c023f017af5ab13107d3221efc9ebfa288b9583e3">+43/-65</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>serviceradar-srql.yaml</strong><dd><code>Add Kubernetes deployment for Rust SRQL service</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> k8s/demo/base/serviceradar-srql.yaml <ul><li>New Kubernetes deployment manifest for Rust-based SRQL service<br> <li> Configures CNPG connection parameters via environment variables<br> <li> Implements health checks on <code>/healthz</code> endpoint<br> <li> Includes resource limits, volume mounts for certificates, and service <br>definition</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-671bb3bfd8bad4c29730ae3c96b9b78e1d446fa378742bd2091b1edad63fec87">+105/-0</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Dockerfile.srql-builder</strong><dd><code>Migrate SRQL builder image from OCaml to Rust</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/builders/Dockerfile.srql-builder <ul><li>Replaced OCaml/opam builder with Rust builder using <code>rust:1.86-slim</code><br> <li> Removed OCaml, dune, and Proton dependency installation<br> <li> Added PostgreSQL development libraries and build tools<br> <li> Simplified to multi-stage build producing Rust binary artifact</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f10af98ad3045a3aeb6c2828c532daf6ad102b27ffe0da2111baa55be5e93ae8">+17/-47</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>docker-compose.yml</strong><dd><code>Update docker-compose SRQL config for CNPG backend</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker-compose.yml <ul><li>Removed Proton connectivity environment variables (PROTON_HOST, <br>PROTON_PORT, PROTON_TLS, etc.)<br> <li> Added CNPG connection parameters (CNPG_HOST, CNPG_PORT, CNPG_DATABASE, <br>CNPG_USERNAME, CNPG_SSLMODE)<br> <li> Updated health check endpoint from <code>/health</code> to <code>/healthz</code><br> <li> Removed proton service dependency</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e45e45baeda1c1e73482975a664062aa56f20c03dd9d64a827aba57775bed0d3">+12/-11</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD</strong><dd><code>Remove OCaml RBE platforms and update executor version</code>&nbsp; &nbsp; &nbsp; </dd></summary> <hr> build/rbe/BUILD <ul><li>Updated RBE executor container image from v1.0.9 to v1.0.11<br> <li> Removed OCaml-specific RBE execution platforms (<code>ocaml_rbe_ocamlopt</code> and <br><code>ocaml_rbe_ocamlc</code>)<br> <li> Changed platform comment from OCaml/opam focus to Oracle Linux based <br>executor</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ac0d5204dc44afc495e4f6899ae668175ade69fa6b3324c94261ae2461586a21">+2/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Switch SRQL Docker image from OCaml to Rust binary</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/images/BUILD.bazel <ul><li>Changed SRQL binary source from <code>//ocaml/srql:srql_server</code> to <br><code>//rust/srql:srql_bin</code><br> <li> Updated base image from <code>@serviceradar_srql_linux_amd64</code> to <br><code>@ubuntu_noble</code><br> <li> Simplified environment variables and adjusted workdir configuration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0e4db31c224a8f72ae8e870a849e38a59d74a2c7f7b04347b0b3eb07e20c5a80">+4/-5</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>components.json</strong><dd><code>Update SRQL packaging from OCaml to Rust build configuration</code></dd></summary> <hr> packaging/components.json <ul><li>Updated SRQL component description from OCaml to Rust query service<br> <li> Changed dependencies from OCaml runtime (<code>libev4</code>, <code>libgmp10</code>) to <br>PostgreSQL client (<code>libpq5</code>)<br> <li> Updated build method from <code>ocaml</code> to <code>rust</code> and source path from <br><code>ocaml/srql</code> to <code>rust/srql</code><br> <li> Changed Dockerfile reference to <br><code>docker/builders/Dockerfile.srql-builder</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3ae5949d89b0252d10fce9bf950231c8151a73b2154dccfe4e7261acc116582c">+6/-8</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Update build platform RBE executor image references</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> build/platforms/BUILD.bazel <ul><li>Updated RBE container image references from <br><code>gcr.io/buildbuddy-io/executor:latest</code> to <br><code>docker://ghcr.io/carverauto/serviceradar/rbe-executor:v1.0.11</code><br> <li> Applied to both x86_64 and aarch64 platform configurations</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-d7da264d8f13c39aafc9e2343c3f9649ee1b143f653edda46521f21378a8467e">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>kustomization.yaml</strong><dd><code>Add SRQL service image to production Kustomize configuration</code></dd></summary> <hr> k8s/demo/prod/kustomization.yaml <ul><li>Added new image entry for <code>ghcr.io/carverauto/serviceradar-srql</code> with <br><code>latest</code> tag<br> <li> Enables SRQL service deployment in production Kubernetes environment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0527e7f19d087f3576d5755a79554797ffbab78b1a7efaa38984b4f3241f6fc9">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>buildbuddy.yaml</strong><dd><code>Update BuildBuddy RBE executor container image</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> buildbuddy.yaml <ul><li>Updated execution platform container image from <br><code>gcr.io/buildbuddy-io/executor:latest</code> to <br><code>docker://ghcr.io/carverauto/serviceradar/rbe-executor:v1.0.11</code><br> <li> Ensures BuildBuddy RBE uses consistent executor version</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-455c97ce748484a181e002949dbe70422aedc497a358e023dc162776ce940751">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>New Bazel build rules for Rust SRQL service</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/BUILD.bazel <ul><li>New Bazel build configuration for Rust SRQL library and binary targets<br> <li> Defines <code>srql_lib</code> library with glob source patterns and <code>srql_bin</code> binary <br>entry point<br> <li> Integrates with workspace dependencies via <code>all_crate_deps</code> macro</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-92fd182413864e6ff93d5dd84e774dd0feab2d73c7e7a56f3519e76b121dd8f7">+23/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>kustomization.yaml</strong><dd><code>Add SRQL service image to staging Kustomize configuration</code></dd></summary> <hr> k8s/demo/staging/kustomization.yaml <ul><li>Added new image entry for <code>ghcr.io/carverauto/serviceradar-srql</code> with <br><code>latest</code> tag<br> <li> Enables SRQL service deployment in staging Kubernetes environment</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ae7d8d4134a595a9d278924988f58e1843ad4d5d24b4df3b2c976dd3610a1b64">+2/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.bazel</strong><dd><code>Update RBE executor version and cwalk alias target</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> BUILD.bazel <ul><li>Updated RBE executor container image from v1.0.9 to v1.0.11<br> <li> Changed <code>cwalk</code> alias target from external repository to local path <br><code>//third_party/cwalk:cwalk</code></ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7fc57714ef13c3325ce2a1130202edced92fcccc0c6db34a72f7b57f60d552a3">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Dockerfile.srql</strong><dd><code>Update Docker Compose SRQL image to use Rust build</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docker/compose/Dockerfile.srql <ul><li>Updated SRQL source image from versioned OCaml image to <code>latest</code> Rust <br>image<br> <li> Changed comment to reflect Bazel-built image instead of OCaml compiler <br>stack<br> <li> Maintains fast local Compose builds by reusing published image</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e0103823c18ea9202ec83a15bc9484f47137179adc12bb22c77e7108d415a184">+3/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>kustomization.yaml</strong><dd><code>Add SRQL service to base Kustomize resources</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> k8s/demo/base/kustomization.yaml <ul><li>Added <code>serviceradar-srql.yaml</code> to resources list<br> <li> Enables SRQL service deployment in base Kubernetes configuration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-c4260176971b950ef1b967a2631b446225071906172f56287c465ad2e29788d9">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Cargo.toml</strong><dd><code>Add Rust SRQL crate to workspace members</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> Cargo.toml <ul><li>Added <code>rust/srql</code> to workspace members list<br> <li> Integrates new Rust SRQL service into monorepo build system</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>main.yml</strong><dd><code>Remove OCaml test filter from CI workflow</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> .github/workflows/main.yml <ul><li>Removed <code>-ocaml</code> from test tag filters in CI pipeline<br> <li> Reflects removal of OCaml-specific tests after SRQL migration</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7829468e86c1cc5d5133195b5cb48e1ff6c75e3e9203777f6b2e379d9e4882b3">+1/-1</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>MODULE.bazel</strong><dd><code>Remove OCaml dependencies and update RBE image</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> MODULE.bazel <ul><li>Removed OCaml-related Bazel dependencies (<code>makeheaders</code>, <code>rules_ocaml</code>, <br><code>tools_opam</code>)<br> <li> Updated RBE executor container image tag from v1.0.9 to v1.0.11</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-6136fc12446089c3db7360e923203dd114b6a1466252e71667c6791c20fe6bdc">+1/-212</a>&nbsp; </td> </tr> <tr> <td> <details> <summary><strong>Cargo.toml</strong><dd><code>New Rust SRQL service Cargo dependencies manifest</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> rust/srql/Cargo.toml <ul><li>New Cargo manifest for Rust SRQL service with Diesel ORM and async <br>PostgreSQL support<br> <li> Included dependencies for HTTP server (Axum), serialization (serde), <br>and database pooling (diesel-async)<br> <li> Added tracing, UUID, and chrono for observability and timestamp <br>handling</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8820eedf4ac2fc0e3a187999b96b922256704dfff7fd4477b3fcdb9ef4300533">+28/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>11 files</summary><table> <tr> <td> <details> <summary><strong>spec.md</strong><dd><code>Add SRQL Rust implementation specification</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/replace-srql-dsl/specs/srql/spec.md <ul><li>New specification document defining requirements for Rust SRQL service<br> <li> Documents Diesel-backed query execution against CNPG clusters<br> <li> Specifies backward compatibility with existing SRQL queries and <br>dashboards<br> <li> Defines removal of OCaml translator and exclusive routing to Rust <br>service</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ec3441ecb6cd70a996cccd38f8d68206dd67ba959d32de5f40a2141d88830060">+40/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>srql-language-reference.md</strong><dd><code>Update SRQL documentation for Rust implementation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/srql-language-reference.md <ul><li>Updated documentation to reference Rust-based SRQL service instead of <br>OCaml<br> <li> Changed query planner references from <code>ocaml/srql</code> to <code>rust/srql</code><br> <li> Updated entity mapping references to Diesel query builders in <br><code>rust/srql/src/query</code><br> <li> Updated CLI validation tool path from OCaml to Rust implementation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7fa04d96755318e393180aaae41cb1374c1da332b3fca76c29e443fd7146ef5f">+3/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>AGENTS.md</strong><dd><code>Update developer documentation for Rust SRQL</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> AGENTS.md <ul><li>Updated project overview to reference Rust SRQL service instead of <br>OCaml<br> <li> Changed SRQL path from <code>ocaml/srql/</code> to <code>rust/srql/</code><br> <li> Updated testing instructions from dune to cargo for SRQL tests<br> <li> Updated formatting guidelines from dune fmt to cargo fmt for Rust code</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-a54ff182c7e8acf56acfd6e4b9c3ff41e2c41a31c9b211b2deb9df75d9a478f9">+5/-5</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>project.md</strong><dd><code>Update project overview for Rust SRQL service</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/project.md <ul><li>Updated tech stack description from OCaml SRQL to Rust SRQL service<br> <li> Changed SRQL path from <code>ocaml/srql</code> to <code>rust/srql</code><br> <li> Updated testing instructions from dune to cargo for SRQL tests<br> <li> Updated database backend from Proton to CNPG with Diesel</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-10c431237ea8388147f27e3a0750ece7b3be53ca35a986acf49b6565508e012e">+2/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>README.md</strong><dd><code>Add RBE executor image build and deployment guide</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> k8s/buildbuddy/README.md <ul><li>Added new section documenting executor pods vs. Bazel action image <br>distinction<br> <li> Provided instructions for building and pushing custom RBE executor <br>image<br> <li> Documented tag update process across configuration files<br> <li> Clarified that Bazel action image is customized while executor pods <br>use upstream</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b9a22507afd694c735354f80d644288a1bad09fcc380eab7e7ca1b5b61e1cd1b">+23/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>03-proton.md</strong><dd><code>Complete architecture rewrite from Proton to CNPG unified telemetry </code><br><code>store</code></dd></summary> <hr> sr-architecture-and-design/prd/03-proton.md <ul><li>Replaced entire Proton/ClickHouse architecture with CNPG + TimescaleDB <br>+ Apache AGE stack<br> <li> Introduced hypertables for metrics/logs/events with compression and <br>retention policies<br> <li> Added Apache AGE graph model for topology, device relationships, and <br>discovery outputs<br> <li> Updated SRQL to compile into SQL targeting hypertables and AGE graph <br>traversals<br> <li> Simplified data flow: agents/pollers → CNPG hypertables → SRQL queries <br>→ dashboards/alerts</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-43f4559ca42e6d93d5fd23e466b2cf3400a72816af1f9a27673c28de8262b8b5">+187/-564</a></td> </tr> <tr> <td> <details> <summary><strong>tasks.md</strong><dd><code>SRQL Rust translator implementation task breakdown</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/replace-srql-dsl/tasks.md <ul><li>Defined task checklist for Rust SRQL translator implementation<br> <li> Covered parser/planner porting, HTTP API surface, and Diesel <br>integration with CNPG<br> <li> Included DSL compatibility migration tasks and removal of dual-run <br>plumbing<br> <li> Added operational integration tasks for deployment manifests and <br>service cutover</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-5be57e06e9ccee3043fe2a975bd0c266fe972e23f7b4438be7c70d1d09c35ed7">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>proposal.md</strong><dd><code>SRQL DSL replacement proposal from OCaml to Rust</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> openspec/changes/replace-srql-dsl/proposal.md <ul><li>Documented rationale for replacing OCaml SRQL with Rust implementation<br> <li> Outlined design of Rust translator using Diesel.rs targeting CNPG <br>schemas<br> <li> Described migration strategy with dual-running, toggles, and <br>documentation updates<br> <li> Identified impact on codebase, dependencies, and operational readiness</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-65a7bc84effa38ac6a43b4f47021e04aad4d7e0d8fc26d99260fe143cd5cbf91">+14/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>search-planner-operations.md</strong><dd><code>Update SRQL service path in operational documentation</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/docs/search-planner-operations.md <ul><li>Updated SRQL service log reference from <code>ocaml/srql</code> to <code>rust/srql</code><br> <li> Reflects migration from OCaml to Rust implementation in operational <br>documentation</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-603215d70bf28587f606811c0cc257dadec37a130a8e4fe95a4664c52727ddca">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>BUILD.md</strong><dd><code>Remove OCaml from build system dependencies</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> BUILD.md <ul><li>Removed <code>ocaml</code> from system dependencies installation command<br> <li> Reflects removal of OCaml toolchain requirement after SRQL migration <br>to Rust</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-40f60e1037245d7b8a98a7325d53890a717da9979adeb54a61a795c4ba07f9c9">+1/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td> <details> <summary><strong>repo_lint.md</strong><dd><code>Update repo lint documentation test file reference</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary> <hr> docs/LF/repo_lint.md <ul><li>Updated test directory reference from <code>ocaml/test_boolean_query.ml</code> to <br><code>rust/srql/src/lib.rs</code><br> <li> Reflects repository structure change after OCaml SRQL removal</ul> </details> </td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-dfe29a772e511687dd2d217459b7017d8372833553adb5b897130db616d9ba73">+1/-2</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>.dockerignore</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2f754321d62f08ba8392b9b168b83e24ea2852bb5d815d63e767f6c3d23c6ac5">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>ocaml-build-test.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-c84bfd564288a05e2cf23310cbe1d0d566e1c6e9372389a67a4fc113e8dc649f">+0/-53</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ocaml-lint.yml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-204e2b3bc66687a5546a45ad23cb017a3b551ecc106685c6bc36e3f8a1c366e3">+0/-34</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Makefile</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52">+0/-116</a>&nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+0/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>OCAML_BAZEL_MIGRATION.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-be32b6d71cbc65ce76d7415a467232d02ec7df489828afa0413b44220318d218">+0/-139</a>&nbsp; </td> </tr> <tr> <td><strong>.ocamlformat</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f3b545e9e46a817e8c0c50782a5de32cc33a6dd43f11b5301998a2d5ab70258a">+0/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-a9dda44dc49e2e6d92155dd8b8a470a975b7327cfd9655067e3ce26cd1e8ac56">+0/-273</a>&nbsp; </td> </tr> <tr> <td><strong>PRD.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-39301c03f1c1e3eb3dac51c6b59d5f81b3b2f1186c1d2b5a6042b6c0a892589e">+0/-463</a>&nbsp; </td> </tr> <tr> <td><strong>dune</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0499a19a86a660582ab65914fcf7d078cc729b93f3e1b5334f9b8b80318f5665">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>dune-project</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-c3afeed85519ab92043be3a246f90fefa0783b38863c9869efd6f981b83b5c10">+0/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>srql-translator.opam</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-900d10104207edf7fbf525b51ccffa5711742688c181ed29a41cabd9ca1579c2">+0/-36</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3cf19e3c1427984c9b33005d104936b2a2d5bcece3509bfe67a4d6aa0c1205b4">+0/-256</a>&nbsp; </td> </tr> <tr> <td><strong>Dockerfile</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-086d9103a276f32ea4c77dca8e9cd4a5b47c6cb4a73d1cbfd5f7d825361c19d1">+0/-75</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>REMAINING_WORK.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-6ccf4d5c3881313a5d65628622f20633523024cdc9dff425b79f6b2d5fd72632">+0/-195</a>&nbsp; </td> </tr> <tr> <td><strong>SRQL_SYNTAX.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-a05b6f97a6861ba78c4ce3169b8946110e4eab9d1ccd42a549630ec2cc41e28d">+0/-49</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>srql_translator_alias.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-949608b32a2a72f917a6abb17f36a8bedd9653638e465987675c980c93e4bdff">+0/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>cli.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0d36e61b4b9bcd258865917bf3a837854aed9268e4598ef2875d2335d5dd01fb">+0/-38</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>dune</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8d74319b07e5cb27694e55ae1484ea992387ee5e534ebfd322710c5c44304e4f">+0/-21</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>main.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-eef5ff32dece7828774801b08bbee80cdb527bf0a21c0cb4ab91318f31f865d7">+0/-807</a>&nbsp; </td> </tr> <tr> <td><strong>srql_cli.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-5d777ffd86cd672e8f72baf427408ead9e28cfce715966b27227eed869b0619c">+0/-115</a>&nbsp; </td> </tr> <tr> <td><strong>dune</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-db02d534205c0550bebf7d98def425dde154d7ef37a7f3379b65293454f16228">+0/-8</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>entity_mapping.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b72b5ae7933263f4e466c25037cc269868634df4a2b2a27dcbcd04fe742b0003">+0/-78</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>field_mapping.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-202ecb348ad59ad7d01c3319a6719ffcb0d04acc7d7b4a8ec5b69c825513fca9">+0/-225</a>&nbsp; </td> </tr> <tr> <td><strong>field_mapping.mli</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-10fb572f18ed4cafe9e64b15af624715d0ec4147344dbe47d89db4582a9e4e39">+0/-3</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>json_conv.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-fd3878e97c12a546ee1db87d795ef2c62fea6e9d05ac7ecefedce0cb4481bade">+0/-205</a>&nbsp; </td> </tr> <tr> <td><strong>json_conv.mli</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b4a01aaaee0d76d40617e37062a26e3e9a35708493a5b6935986e9cd76d31f4b">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>proton_client.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-474590ab3468b98ee363f3cd78535c90bc83cf3b2e77ae9871db741e2d8dea06">+0/-286</a>&nbsp; </td> </tr> <tr> <td><strong>proton_client.mli</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2b549609ea8b875de4a039282ea09be7655ebda3f27f078ea603b8e77b9e31c6">+0/-87</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>query_ast.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ee0b14b7c25ed652bb6893887775fed90c22501dd71fe42fdc1beeb51a171eac">+0/-23</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>query_parser.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7a3cc3c9d8f376309fa5cc4c97a360b5471cc42def790f4b0421c7f518ca9eb9">+0/-305</a>&nbsp; </td> </tr> <tr> <td><strong>query_planner.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b215cfcd9aca2dc66252f05b2e2c2a39cde9d86744ee5f87b935077ed3863b06">+0/-642</a>&nbsp; </td> </tr> <tr> <td><strong>query_validator.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f488cc3e7ee6e13be4a8103817590a1d0c86780c62109b6909fe1aed65ffe8db">+0/-95</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>sql_ir.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e8a30619726b7581ba83efc140c894b178903ecd638dbe08b430f75e29efd7a1">+0/-28</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>sql_sanitize.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ea5db2cd548d99737c7279aa1204949142692da8987d368be121f20a983a2d95">+0/-42</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>sql_sanitize.mli</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-d87429863c5d82b8a66429664cbd8d852bfc4fa3d549c3e30d2baecb5e6c4678">+0/-14</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>translator.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-c33a65c30e4433358fbe3a1806b6c35291fd999f3abfdaad0ca38419e94764b9">+0/-320</a>&nbsp; </td> </tr> <tr> <td><strong>ocaml_local_rules.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8dc5ba8316eca59bab1d8ab474045b745fc76df930ae5667f88de9baa5355946">+0/-30</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>dune</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-607aff476a99ecfd51910ab2ca1d2fc7127c7f84ad6751c2b6ef26061b60d003">+0/-20</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>run_live_srql.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ea25a2c5be7493fef8b79f6c49ab84140d70b2458f61c001139f3eec92bfb5e9">+0/-356</a>&nbsp; </td> </tr> <tr> <td><strong>test_bounded_unbounded.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3840c3919e65488587d26254fb2c1781bb64fc4da29d0afd8ae3b821acc7a9fe">+0/-66</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_direct_proton.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-d577bd71d651aeb4540ccef238708f0c821a4582c7b214547e1c89ab6c78a621">+0/-40</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_insecure_tls.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f6b776d63a9400a37098f1a436f25208d9ebb8846ad45263011fad724dd18822">+0/-116</a>&nbsp; </td> </tr> <tr> <td><strong>test_json_conv.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-822572125330b35bc77e20916c60f557a791d8691a6dfd5c6b1d123036efdef7">+0/-88</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_proton_integration.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e63ed65a4d752967b073b16b70b28319e8a3568c83e739e81d574c52a1b24dd7">+0/-136</a>&nbsp; </td> </tr> <tr> <td><strong>test_query_engine.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0c1dcb695d94269d0e3f899c01455e21cb50242b4665e3a0ef5f2b198c5cbe2f">+0/-369</a>&nbsp; </td> </tr> <tr> <td><strong>test_simple_connection.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-414b2c95a515bbae22db029e3b9f87c4931aaef886a2b8b4a9f13e9cc0c521ee">+0/-83</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_sql_sanitize.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8e9284704ffb8e9d0b5ad1d4641f03b26b291dc87a714647a5d8a0f129ca3453">+0/-190</a>&nbsp; </td> </tr> <tr> <td><strong>test_boolean_query.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ac5bbbc046fa1592dfe13e17e40f9e3a4952864d54322e577be1937c9eb17893">+0/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_direct_proton.cmi</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2d089b08825cd67383e5cb671172ad7c07a89aaa4e6aab4c5a4692a1c85f2d89">[link]</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_direct_proton.cmo</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-9016aec38d2352f15ea86b9cc2c3c55161573e701693b1647263ec889a7bb34a">[link]</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_direct_proton.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f4b8c16ffa83d55794bd7a3f0ac448495db523c2d30a3377cbef8d8cef9fbc46">+0/-40</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_schema_query.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b902b232b09b17e77b376d3d7426e7945bfc5e618156851f5bc843c1b3c1412d">+0/-60</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>test_simple_query.ml</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f88b28c98f22a66122d0413ee3457375789ab9f36448dd6125b4ff3752d8841d">+0/-62</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>setup-package.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-388e4f6f99131c27ccade0a4d7f16112297ec05e4dda0a23e142f787498ac004">+0/-38</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-6bb0f490f3f528e61a401c040689dd04b4dc1a429227fbc6d4c2afa97f38f39a">+0/-22</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-aab6d7d8a1797043f9501a759e9b799efcc03dad755c8c06a01bf5f0d52f8480">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>platform_exec_properties.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ef5baf58450bc5fbbc939fbaf0283202e1baf01cf7e560bacf39cddee346fb3d">+0/-60</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>stdlib_bundle.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-610bb17782cbb1d9294eda1a2cba25b55bee50412cf605c914a4d09aadf984d1">+0/-56</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>stdlib_env.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-6d6af657959990d8c4d66e30664dde69d15cabaa13875cb3e9e56b100870d516">+0/-341</a>&nbsp; </td> </tr> <tr> <td><strong>0001-Fix-use-after-free-in-findlibc.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-9b2f09d4d345cf1fd275e458786eb76dbd387e6d836f5005fc4fb3c2863580e8">+0/-40</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>0001-Rename-ppx-deriving-ocaml-import.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-dfae463f637b3c07d91dc17baaef671b9c2e930da61b5cdb492212f290e7d0a6">+0/-25</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>0001-Sanitize-ocaml-import-target-names.patch</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-73ee814eee098aec0b0bb653f890d87c0946d627b9d221937f10e6cce19fd17a">+0/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ecbc2ee44d1456f6fef0d4a089d406a7031b686033bd1f5f52dcb34aa170d43c">+0/-6</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>MODULE.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-4b7cf813fb2401d99a77af90aef9e75d5cd62723150033e96450b446a98f253c">+0/-26</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>copy_stdlib_runtime.sh</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-25e9bc5b61a67056f2d9d18c069057afd2897273d494f9c24d371295f3f215eb">+0/-37</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>.bazelignore</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ad7d288b2fadd7bff9aa655d3d60796383f2979bc3e5dc45f095320947e65022">+0/-8</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>.bazelrc</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-9c606fde9a1dc774a68cce2c9836b8250eb742320f1d85dd6fdd68f55cd95e6a">+0/-5</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>LICENSE</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-5566f18293526b6a1a99790678a6b055e9d8ffca8fcfa95a0a1f5e9b04b9a4d7">+0/-201</a>&nbsp; </td> </tr> <tr> <td><strong>MODULE.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-01610002be534f0be15306510ff427ec0b89769e01f483e91011579489af96b6">+0/-32</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.adoc</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f48569445ebb6c206334bd507dcc8940f5081acb0f2ff55a6d17d78eb90b125b">+0/-402</a>&nbsp; </td> </tr> <tr> <td><strong>WORKSPACE</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-5d92faa40bfff9c267294489a4d6a82af3c7edb8546095560587761e0ff04846">+0/-2</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8009f06b6cf55865aa939bed6780187a8b654f747770710031d53b5a2a60b614">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>aspects.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-d568fed16181eee6de1480285503a065f2adf07cfdf19034cf3cc436b8f2d246">+0/-18</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>cmt.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e463bebf5b1f7440728d30999b684389fedb5100bd34586e08a9926e21c5f7a4">+0/-29</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>depsets.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-7812555c1f7c9a9c1a46f7f140ed024eeb62ca91e02bfcb71ae3cef4da271539">+0/-118</a>&nbsp; </td> </tr> <tr> <td><strong>ocamlcmt.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ebe804eef679711e6e7b64ae6b18c284fce5a1d405c1ac7d2bd32545429a31d2">+0/-105</a>&nbsp; </td> </tr> <tr> <td><strong>ocamlobjinfo.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3730f61d719ec70018faba5218e1a680e275bb8551368dd378804a744cdbadad">+0/-143</a>&nbsp; </td> </tr> <tr> <td><strong>providers.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-1961946b163f7ba20fb877f935faff96aba63fa00dfe4d9b3043335f26185e1b">+0/-52</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-224324b9a6434c7c478de110de7f57b35d36c4c8bfa0c2dcea6b01f47535382a">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-996b0af0e9bab98bff791bdd75634801c661e5b50610befb009c814d550f6e10">+0/-4</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>opam.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-0fc5b8c674156c0d13a5aa6b608cd4086045060a3a864a35acb0ddb97e367b56">+0/-243</a>&nbsp; </td> </tr> <tr> <td><strong>opam.mustache</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-74b7aef7066ee4e954f19a9c1c17a675085eebabf4a2e939aa0586f064b46dc1">+0/-23</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>opam_build.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ab7d1c11c1853a8099df5eef67e169f5f3ba73ec4265b6dfcbf3f9672dccdf17">+0/-260</a>&nbsp; </td> </tr> <tr> <td><strong>rules.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-1e7f2bdc7ac4dff3292fbefd11dcaf58cb7695c1aada6310597f60a335f1cafc">+0/-14</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e2ab3e3030f0453c56111ee87d6df3fe6e2103216c8b31b81d8c835f30df60b1">+0/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>CONFIG.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-fdea91c3cdfda840036fcceb097e2e5e217c8c4f0a848ad1980be1f3c0e4cdad">+0/-9</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-e665471311b97f49a5ad459638b4d551c08f761dd2188f2146e8bd97f392dd1f">+0/-28</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>colors.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-389412df226bcadf9547c4a493052332baf42747de78301ab5c790b25b762101">+0/-40</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-6c487b380bd8ffa8f7f7b9f82b688302d9f5775e365cd965c16a0320b8b85cf2">+0/-74</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>config_pkg.c</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-3390ede1999a50450869f0c1713613b41557afe0ad2fd2a81cfeff9ac656a3c0">+0/-261</a>&nbsp; </td> </tr> <tr> <td><strong>ext_emit_build_bazel.c</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-96dec2b952a9257baf70027d3bbd9bff07cf487110b06999d38847e3bbf3b577">+0/-571</a>&nbsp; </td> </tr> <tr> <td><strong>main.c</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-cd8890016a7c965b61f92edf0696668870cafe375f4d948fc235aaee8f0863e5">+0/-266</a>&nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-14d5b54e7e94c99e8a2e5a4f7882258ded02682bb59929578c5d4a5add683502">+0/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>dbg_repo.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-66a7af1a7086a5314e624371774f7eac6ec7084378360a228814bff912928c44">+0/-131</a>&nbsp; </td> </tr> <tr> <td><strong>dbg_runner.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-b3626dffd2cf31de05bba37172aec77d731c89579f32dfca729f9373cebdf615">+0/-92</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ocamldebug_runner.c</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f261eae8aab159c2dde4be8cb01073dd9cebda5304370ef2472cea2771779dfe">+0/-91</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>obazl.opamrc</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-8a3f2ef5fa2d4ad1c1d9b68f012486ab41ce8a898044f94d046f5b35dc133d59">+0/-400</a>&nbsp; </td> </tr> <tr> <td><strong>BUILD.bazel</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-12e380fbc9e51db0b5f42a3d099e7c426240b472b008a641203cfd20c6ecf636">+0/-12</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>ocaml_repo.bzl</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-84c77825d28bdbd21eedcc9507dccc3d6c5d084a6451bf8dff189517335f79e5">+0/-121</a>&nbsp; </td> </tr> <tr> <td><strong>Additional files not shown</strong></td> <td><a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
qodo-code-review[bot] commented 2025-11-17 01:22:01 +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/1948#issuecomment-3539606843
Original created: 2025-11-17T01:22:01Z

PR Compliance Guide 🔍

(Compliance updated until commit github.com/carverauto/serviceradar@66ae3e4358)

Below is a summary of compliance checks for this PR:

Security Compliance
Missing authentication

Description: API key authentication compares the provided header only to an optional configured key,
and if none is configured it fully disables authentication, which could expose query
endpoints if SRQL_API_KEY or KV key is not set.
server.rs [96-111]

Referred Code
}

fn enforce_api_key(headers: &HeaderMap, api_keys: &ApiKeyStore) -> Result<()> {
    if let Some(expected) = api_keys.current() {
        let provided = headers
            .get("x-api-key")
            .and_then(|value| value.to_str().ok())
            .map(str::trim);

        if provided != Some(expected.trim()) {
            return Err(ServiceError::Auth);
        }
    }

    Ok(())
}

Credential exposure via env

Description: DATABASE_URL is constructed by embedding the plaintext password in the URL, risking
exposure via process/env or logs; although percent-encoded, it still sets DATABASE_URL env
var which can leak in diagnostics.
entrypoint-srql.sh [120-133]

Referred Code
SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}"
if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then
    SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")"
fi

if [ -n "$CNPG_PASSWORD_VALUE" ]; then
    ENCODED_PASS="$(urlencode "$CNPG_PASSWORD_VALUE")"
    SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}:${ENCODED_PASS}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}"
else
    echo "Warning: CNPG password not provided; SRQL will attempt passwordless connection" >&2
    SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}"
fi

DB_TARGET_DESC="${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}"
SQL disclosure

Description: Translate endpoint returns generated SQL text to authorized clients which could aid an
attacker in reconnaissance if API key is weak or disabled.
mod.rs [120-136]

Referred Code
        .await
        .map_err(|err| ServiceError::Internal(err.into()))?;

    let results = match plan.entity {
        Entity::Devices => devices::execute(&mut conn, &plan).await?,
        Entity::Events => events::execute(&mut conn, &plan).await?,
        Entity::Logs => logs::execute(&mut conn, &plan).await?,
    };

    let pagination = self.build_pagination(&plan, results.len() as i64);
    Ok(QueryResponse {
        results,
        pagination,
        error: None,
    })
}


Ticket Compliance
🟡
🎫 #1947
🟢 Rewrite SRQL service to target PostgreSQL/CNPG backend, replacing OCaml implementation.
Provide Rust implementation using Diesel ORM for Devices, Events, and Logs queries.
Implement SRQL DSL parser translating key:value syntax to an AST with filters, sorting,
limits, and time ranges.
Expose HTTP API endpoints for health, query execution, and translation (debug SQL),
secured by API key.
Add cursor-based pagination support.
Provide environment-based configuration for service (listen address, DB URL, limits, rate
limiting, timeouts, API key).
Update build and packaging to use Rust crate, adjust Docker/RBE images, and RPM/DEB
packaging.
Remove Proton/OCaml-specific types and config from web and infra, including deprecated
_tp_time fields.
Update documentation to reflect Rust/CNPG migration and new SRQL behavior.
Update Kubernetes manifests to deploy the new SRQL service.
Validate end-to-end query correctness and performance against CNPG in staging/production.
Confirm API key provisioning via KV store works in deployed environments.
Verify Kubernetes manifests integrate with cluster ingress/auth and that images are
published.
Run UI flows to confirm removal of _tp_time does not break logs/events pages.
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

Generic: Secure Error Handling

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

Status: Passed

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

Generic: Comprehensive Audit Trails

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

Status:
Missing audit logs: New endpoints authenticate via API key but do not emit structured audit logs for critical
actions like query execution or translation requests (who, what, result), making
auditability uncertain.

Referred Code
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<QueryRequest>,
) -> Result<Json<QueryResponse>> {
    enforce_api_key(&headers, &state.api_keys)?;
    let response = state.query.execute_query(request).await;

    match response {
        Ok(rows) => Ok(Json(rows)),
        Err(err) => Err(err),
    }
}

async fn translate(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<TranslateRequest>,
) -> Result<Json<TranslateResponse>> {
    enforce_api_key(&headers, &state.api_keys)?;
    let response = state.query.translate(request).await?;
    Ok(Json(response))



 ... (clipped 1 lines)

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:
Generic internal wrap: Database pool acquisition and query load errors are wrapped as generic internal errors
without contextual details about the operation or inputs, which may limit actionable
debugging.

Referred Code
let plan = self.plan(&request, ast)?;
let mut conn = self
    .pool
    .get()
    .await
    .map_err(|err| ServiceError::Internal(err.into()))?;

let results = match plan.entity {
    Entity::Devices => devices::execute(&mut conn, &plan).await?,
    Entity::Events => events::execute(&mut conn, &plan).await?,
    Entity::Logs => logs::execute(&mut conn, &plan).await?,
};

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:
Secret exposure risk: The entrypoint echoes connection target details and constructs DATABASE_URL; although it
avoids printing passwords, it logs database target which could be sensitive in some
environments and lacks structured logging.

Referred Code
# Build DATABASE_URL if not explicitly provided
DB_TARGET_DESC="custom DATABASE_URL"
if [ -z "$SRQL_DATABASE_URL" ]; then
    CNPG_HOST_VALUE="${CNPG_HOST:-cnpg-rw}"
    CNPG_PORT_VALUE="${CNPG_PORT:-5432}"
    CNPG_DATABASE_VALUE="${CNPG_DATABASE:-telemetry}"
    CNPG_USERNAME_VALUE="${CNPG_USERNAME:-postgres}"
    CNPG_SSLMODE_VALUE="${CNPG_SSLMODE:-require}"
    CNPG_CERT_DIR_VALUE="${CNPG_CERT_DIR:-/etc/serviceradar/certs}"
    CNPG_ROOT_CERT_VALUE="${CNPG_ROOT_CERT:-}"
    if [ -z "$CNPG_ROOT_CERT_VALUE" ] && [ -n "$CNPG_CERT_DIR_VALUE" ]; then
        if [ -f "${CNPG_CERT_DIR_VALUE}/root.pem" ]; then
            CNPG_ROOT_CERT_VALUE="${CNPG_CERT_DIR_VALUE}/root.pem"
        fi
    fi
    CNPG_PASSWORD_VALUE="$(load_cnpg_password)"

    SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}"
    if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then
        SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")"
    fi


 ... (clipped 18 lines)

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:
Limited validation: The SRQL parser accepts arbitrary field names as filters and defers validation to query
layers, which may allow unsupported fields to pass silently in some paths and merits
verification of full validation coverage across entities.

Referred Code
            filters.push(build_filter(raw_key, value));
        }
    }
}

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

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
- Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit 66ae3e4
Security Compliance
Weak API key auth

Description: API key authentication relies solely on the presence and equality of the X-API-Key header
without secondary protections (e.g., per-user scoping or rotation enforcement), making
header leakage sufficient to access query endpoints.
server.rs [97-111]

Referred Code

fn enforce_api_key(headers: &HeaderMap, api_keys: &ApiKeyStore) -> Result<()> {
    if let Some(expected) = api_keys.current() {
        let provided = headers
            .get("x-api-key")
            .and_then(|value| value.to_str().ok())
            .map(str::trim);

        if provided != Some(expected.trim()) {
            return Err(ServiceError::Auth);
        }
    }

    Ok(())
}

Credential exposure risk

Description: DATABASE_URL is constructed by embedding a percent-encoded password directly in the URL,
which can be exposed via process env or runtime diagnostics; consider using libpq env vars
or secret mounts to avoid credential exposure.
entrypoint-srql.sh [120-138]

Referred Code
    SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}"
    if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then
        SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")"
    fi

    if [ -n "$CNPG_PASSWORD_VALUE" ]; then
        ENCODED_PASS="$(urlencode "$CNPG_PASSWORD_VALUE")"
        SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}:${ENCODED_PASS}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}"
    else
        echo "Warning: CNPG password not provided; SRQL will attempt passwordless connection" >&2
        SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}"
    fi

    DB_TARGET_DESC="${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}"
fi

export SRQL_DATABASE_URL
export DATABASE_URL="$SRQL_DATABASE_URL"

SQL injection surface

Description: The text filter macros pass user-supplied strings into Diesel eq/ilike clauses; while
Diesel parameterizes values, ensure no raw SQL fragments are constructed from user input
elsewhere to prevent SQL injection (verify all uses of sql! macros avoid user
concatenation).
mod.rs [1-66]

Referred Code
macro_rules! apply_text_filter {
    ($query:expr, $filter:expr, $column:expr) => {{
        let __next = match $filter.op {
            crate::parser::FilterOp::Eq => {
                let value = $filter.value.as_scalar()?.to_string();
                $query.filter($column.eq(value))
            }
            crate::parser::FilterOp::NotEq => {
                let value = $filter.value.as_scalar()?.to_string();
                $query.filter($column.ne(value))
            }
            crate::parser::FilterOp::Like => {
                let value = $filter.value.as_scalar()?.to_string();
                $query.filter($column.ilike(value))
            }
            crate::parser::FilterOp::NotLike => {
                let value = $filter.value.as_scalar()?.to_string();
                $query.filter($column.not_ilike(value))
            }
            crate::parser::FilterOp::In => {
                let values = $filter.value.as_list()?.to_vec();



 ... (clipped 45 lines)
Ticket Compliance
🟡
🎫 #1947
🟢 Rewrite SRQL query service to target PostgreSQL/CNPG using Rust and Diesel.
Provide a parser to translate SRQL key:value DSL into a structured AST.
Implement query builders for devices, events, and logs with filtering, ordering, limits,
and time ranges.
Expose HTTP endpoints for health, query execution, and query translation, protected by API
key auth.
Implement time range parsing supporting relative and absolute specifications.
Implement cursor-based pagination.
Provide configuration via environment variables (SRQL_*), including database URL and rate
limiting.
Replace/remove deprecated Proton-specific fields in frontend types.
Update build, packaging, and Docker/Kubernetes manifests to deploy the new Rust SRQL
service.
Validate end-to-end deployment in Kubernetes environments (base/staging/prod) with CNPG
connectivity.
Verify API key distribution/rotation via external KV (`kvutil`) in target environments.
Load/performance testing of new rate limiting, pagination, and query execution under
production workloads.
Confirm frontend integrations against the new SRQL API and data shapes in UI flows.
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

Generic: Secure Error Handling

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

Status: Passed

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

Generic: Comprehensive Audit Trails

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

Status:
Missing audit logs: New HTTP endpoints and API key checks do not emit audit logs for access attempts,
authentication outcomes, or query executions, making it unclear if critical actions are
recorded.

Referred Code
async fn query(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<QueryRequest>,
) -> Result<Json<QueryResponse>> {
    enforce_api_key(&headers, &state.api_keys)?;
    let response = state.query.execute_query(request).await;

    match response {
        Ok(rows) => Ok(Json(rows)),
        Err(err) => Err(err),
    }
}

async fn translate(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<TranslateRequest>,
) -> Result<Json<TranslateResponse>> {
    enforce_api_key(&headers, &state.api_keys)?;
    let response = state.query.translate(request).await?;



 ... (clipped 2 lines)

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:
Input validation gaps: Filters accept raw strings for LIKE and IN without visible normalization or length limits,
and server returns parsed SQL without pagination limits in translate, which may require
additional validation and safeguards.

Referred Code
fn apply_filter<'a>(mut query: LogsQuery<'a>, filter: &Filter) -> Result<LogsQuery<'a>> {
    match filter.field.as_str() {
        "trace_id" => {
            query = apply_text_filter!(query, filter, col_trace_id)?;
        }
        "span_id" => {
            query = apply_text_filter!(query, filter, col_span_id)?;
        }
        "service_name" => {
            query = apply_text_filter!(query, filter, col_service_name)?;
        }
        "service_version" => {
            query = apply_text_filter!(query, filter, col_service_version)?;
        }
        "service_instance" => {
            query = apply_text_filter!(query, filter, col_service_instance)?;
        }
        "scope_name" => {
            query = apply_text_filter!(query, filter, col_scope_name)?;
        }
        "scope_version" => {



 ... (clipped 48 lines)

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:
Logs may expose DB: Entrypoint echoes database target details which may reveal infrastructure metadata in
container logs; also no evidence that application avoids logging sensitive request data.

Referred Code
export SRQL_DATABASE_URL
export DATABASE_URL="$SRQL_DATABASE_URL"

echo "SRQL listening on ${SRQL_LISTEN_HOST}:${SRQL_LISTEN_PORT} (database target: ${DB_TARGET_DESC})"

exec "$@"

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:
Unbounded inputs: The SRQL parser accepts arbitrary tokens, list sizes, and patterns without explicit
maximum lengths or field whitelisting enforcement at the parser level, which could lead to
denial-of-service or excessive query complexity.

Referred Code
pub fn parse(input: &str) -> Result<QueryAst> {
    let mut entity = None;
    let mut filters = Vec::new();
    let mut order = Vec::new();
    let mut limit = None;
    let mut time_filter = None;

    for token in tokenize(input) {
        let (raw_key, raw_value) = split_token(&token)?;
        let key = raw_key.trim().to_lowercase();
        let value = parse_value(raw_value);

        match key.as_str() {
            "in" => {
                entity = Some(parse_entity(value.as_scalar()?)?);
            }
            "limit" => {
                let parsed = value
                    .as_scalar()?
                    .parse::<i64>()
                    .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?;



 ... (clipped 109 lines)

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

Compliance check up to commit 49b3464
Security Compliance
Weak API key auth

Description: API key authentication relies on exact header match of 'x-api-key' without rate limiting
or optional rotation, which risks brute-force if exposed; ensure TLS and consider rate
limiting and secret management.
server.rs [81-90]

Referred Code
fn enforce_api_key(headers: &HeaderMap, config: &AppConfig) -> Result<()> {
    if let Some(expected) = &config.api_key {
        let provided = headers
            .get("x-api-key")
            .and_then(|value| value.to_str().ok());

        if provided != Some(expected.as_str()) {
            return Err(ServiceError::Auth);
        }
    }

Info exposure in logs

Description: DATABASE_URL is constructed from environment values and printed partially to logs, which
may leak host/user/database info; ensure secrets (password) are not logged and consider
suppressing URL output entirely.
entrypoint-srql.sh [103-136]

Referred Code
# Build DATABASE_URL if not explicitly provided
if [ -z "$SRQL_DATABASE_URL" ]; then
    CNPG_HOST_VALUE="${CNPG_HOST:-cnpg-rw}"
    CNPG_PORT_VALUE="${CNPG_PORT:-5432}"
    CNPG_DATABASE_VALUE="${CNPG_DATABASE:-telemetry}"
    CNPG_USERNAME_VALUE="${CNPG_USERNAME:-postgres}"
    CNPG_SSLMODE_VALUE="${CNPG_SSLMODE:-require}"
    CNPG_CERT_DIR_VALUE="${CNPG_CERT_DIR:-/etc/serviceradar/certs}"
    CNPG_ROOT_CERT_VALUE="${CNPG_ROOT_CERT:-}"
    if [ -z "$CNPG_ROOT_CERT_VALUE" ] && [ -n "$CNPG_CERT_DIR_VALUE" ]; then
        if [ -f "${CNPG_CERT_DIR_VALUE}/root.pem" ]; then
            CNPG_ROOT_CERT_VALUE="${CNPG_CERT_DIR_VALUE}/root.pem"
        fi
    fi
    CNPG_PASSWORD_VALUE="$(load_cnpg_password)"

    SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}"
    if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then
        SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")"
    fi



 ... (clipped 13 lines)
Broad LIKE patterns

Description: The DSL parser accepts list and scalar values and maps '%' to LIKE without explicit
escaping; while Diesel parameterizes inputs, LIKE patterns could enable broad scans or
unexpected matches—verify expected behavior and consider explicit wildcard policy.
parser.rs [206-286]

Referred Code
let mut tokens = Vec::new();
let mut current = String::new();
let mut quote = None;
let mut depth = 0usize;
let mut escape = false;

for ch in input.chars() {
    if escape {
        current.push(ch);
        escape = false;
        continue;
    }

    if let Some(q) = quote {
        if ch == '\\' {
            escape = true;
            continue;
        }
        if ch == q {
            quote = None;
        }



 ... (clipped 60 lines)
Secrets in env vars

Description: Falls back to reading DATABASE_URL from environment which may include credentials in plain
text; ensure secrets are injected securely and not logged elsewhere.
config.rs [74-83]

Referred Code
let database_url = raw
    .srql_database_url
    .or(raw.database_url)
    .or_else(|| env::var("DATABASE_URL").ok())
    .context("SRQL_DATABASE_URL or DATABASE_URL must be set")?;

let allowed_origins = raw.srql_allowed_origins.and_then(|csv| {
    let trimmed: Vec<_> = csv
        .split(',')
        .filter_map(|part| {

Ticket Compliance
🟡
🎫 #1947
🟢 Replace the OCaml-based SRQL service with a Rust implementation using Diesel ORM targeting
CloudNativePG (PostgreSQL).
Provide a parser to translate SRQL DSL (key:value syntax) into an AST with filters,
ordering, limits, and time ranges.
Implement Diesel-backed query modules for devices, events, and logs, supporting field
filters and ordering.
Expose HTTP endpoints for health, query execution, and translation to SQL, with API key
authentication.
Implement cursor-based pagination and limit enforcement with sensible defaults and
maximums.
Implement configuration via environment variables (SRQL_*), including DB URL, listen
address, API key, limits, and CORS.
Add time range parsing supporting relative durations, keywords (today/yesterday), and
absolute ranges.
Update build and packaging to use the Rust toolchain, adjust RBE image, Dockerfiles, and
RPM packaging.
Remove Proton-specific fields from frontend types and align UI to CNPG-backed schema.
Provide Diesel models and schema for devices, events, and logs with JSON serialization.
Verify end-to-end functionality against a live CNPG instance, including schema
availability and query performance.
Validate API key auth and CORS behavior in deployed environments.
Confirm UI components fully align with backend changes beyond the shown type deletion
(manual browser testing).
Validate packaging and deployment across environments (base/staging/prod) including
systemd units and RPM install.
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

Generic: Secure Error Handling

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

Status: Passed

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

Generic: Comprehensive Audit Trails

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

Status:
Missing audit logging: New API endpoints execute queries without explicit audit logs of requester identity,
action, and outcome beyond generic tracing, which may be acceptable if centralized logging
is configured elsewhere.

Referred Code
async fn query(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<QueryRequest>,
) -> Result<Json<QueryResponse>> {
    enforce_api_key(&headers, &state.config)?;
    let response = state.query.execute_query(request).await;

    match response {
        Ok(rows) => Ok(Json(rows)),
        Err(err) => Err(err),
    }
}

async fn translate(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<TranslateRequest>,
) -> Result<Json<TranslateResponse>> {
    enforce_api_key(&headers, &state.config)?;
    let response = state.query.translate(request).await?;



 ... (clipped 2 lines)

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:
Partial edge handling: Filters fall through silently for unsupported fields (no-op) which may hide user errors
and lacks explicit validation feedback for unknown filter names.

Referred Code
    "level" => {
        let value = parse_i32(filter.value.as_scalar()?)?;
        query = match filter.op {
            FilterOp::Eq => query.filter(col_level.eq(value)),
            FilterOp::NotEq => query.filter(col_level.ne(value)),
            _ => {
                return Err(ServiceError::InvalidRequest(
                    "level only supports equality comparisons".into(),
                ))
            }
        };
    }
    _ => {}
}

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:
Potential sensitive data: The JSON responses include raw attributes fields (e.g., logs attributes and
resource_attributes) whose contents are not scrubbed and may contain sensitive data
depending on upstream ingestion.

Referred Code

impl LogRow {
    pub fn into_json(self) -> serde_json::Value {
        serde_json::json!({
            "timestamp": self.timestamp,
            "trace_id": self.trace_id,
            "span_id": self.span_id,
            "severity_text": self.severity_text,
            "severity_number": self.severity_number,
            "body": self.body,
            "service_name": self.service_name,
            "service_version": self.service_version,
            "service_instance": self.service_instance,
            "scope_name": self.scope_name,
            "scope_version": self.scope_version,
            "attributes": self.attributes.clone(),
            "resource_attributes": self.resource_attributes,
            "raw_data": self.attributes.unwrap_or_default(),
        })
    }

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 scope: The SRQL parser and query builders validate many cases, but unknown filter fields are
accepted as no-ops and body size/rate limits are not visible here, requiring confirmation
that upstream limits and auth are enforced.

Referred Code
async fn query(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<QueryRequest>,
) -> Result<Json<QueryResponse>> {
    enforce_api_key(&headers, &state.config)?;
    let response = state.query.execute_query(request).await;

    match response {
        Ok(rows) => Ok(Json(rows)),
        Err(err) => Err(err),
    }
}

async fn translate(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(request): Json<TranslateRequest>,
) -> Result<Json<TranslateResponse>> {
    enforce_api_key(&headers, &state.config)?;
    let response = state.query.translate(request).await?;



 ... (clipped 2 lines)

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

Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/1948#issuecomment-3539606843 Original created: 2025-11-17T01:22:01Z --- ## PR Compliance Guide 🔍 <!-- https://github.com/carverauto/serviceradar/commit/66ae3e4358b091b43ca4f90cb4d96c8f2c9d4051 --> #### (Compliance updated until commit https://github.com/carverauto/serviceradar/commit/66ae3e4358b091b43ca4f90cb4d96c8f2c9d4051) 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=3>⚪</td> <td><details><summary><strong>Missing authentication </strong></summary><br> <b>Description:</b> API key authentication compares the provided header only to an optional configured key, <br>and if none is configured it fully disables authentication, which could expose query <br>endpoints if SRQL_API_KEY or KV key is not set.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R96-R111'>server.rs [96-111]</a></strong><br> <details open><summary>Referred Code</summary> ```rust } fn enforce_api_key(headers: &HeaderMap, api_keys: &ApiKeyStore) -> Result<()> { if let Some(expected) = api_keys.current() { let provided = headers .get("x-api-key") .and_then(|value| value.to_str().ok()) .map(str::trim); if provided != Some(expected.trim()) { return Err(ServiceError::Auth); } } Ok(()) } ``` </details></details></td></tr> <tr><td><details><summary><strong>Credential exposure via env </strong></summary><br> <b>Description:</b> DATABASE_URL is constructed by embedding the plaintext password in the URL, risking <br>exposure via process/env or logs; although percent-encoded, it still sets DATABASE_URL env <br>var which can leak in diagnostics.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR120-R133'>entrypoint-srql.sh [120-133]</a></strong><br> <details open><summary>Referred Code</summary> ```shell SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}" if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")" fi if [ -n "$CNPG_PASSWORD_VALUE" ]; then ENCODED_PASS="$(urlencode "$CNPG_PASSWORD_VALUE")" SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}:${ENCODED_PASS}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}" else echo "Warning: CNPG password not provided; SRQL will attempt passwordless connection" >&2 SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}" fi DB_TARGET_DESC="${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}" ``` </details></details></td></tr> <tr><td><details><summary><strong>SQL disclosure</strong></summary><br> <b>Description:</b> Translate endpoint returns generated SQL text to authorized clients which could aid an <br>attacker in reconnaissance if API key is weak or disabled.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491R120-R136'>mod.rs [120-136]</a></strong><br> <details open><summary>Referred Code</summary> ```rust .await .map_err(|err| ServiceError::Internal(err.into()))?; let results = match plan.entity { Entity::Devices => devices::execute(&mut conn, &plan).await?, Entity::Events => events::execute(&mut conn, &plan).await?, Entity::Logs => logs::execute(&mut conn, &plan).await?, }; let pagination = self.build_pagination(&plan, results.len() as i64); Ok(QueryResponse { results, pagination, error: None, }) } ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>🟡</td> <td> <details> <summary>🎫 <a href=https://github.com/carverauto/serviceradar/issues/1947>#1947</a></summary> <table width='100%'><tbody> <tr><td rowspan=10>🟢</td> <td>Rewrite SRQL service to target PostgreSQL/CNPG backend, replacing OCaml implementation.</td></tr> <tr><td>Provide Rust implementation using Diesel ORM for Devices, Events, and Logs queries.</td></tr> <tr><td>Implement SRQL DSL parser translating key:value syntax to an AST with filters, sorting, <br>limits, and time ranges.</td></tr> <tr><td>Expose HTTP API endpoints for health, query execution, and translation (debug SQL), <br>secured by API key.</td></tr> <tr><td>Add cursor-based pagination support.</td></tr> <tr><td>Provide environment-based configuration for service (listen address, DB URL, limits, rate <br>limiting, timeouts, API key).</td></tr> <tr><td>Update build and packaging to use Rust crate, adjust Docker/RBE images, and RPM/DEB <br>packaging.</td></tr> <tr><td>Remove Proton/OCaml-specific types and config from web and infra, including deprecated <br>_tp_time fields.</td></tr> <tr><td>Update documentation to reflect Rust/CNPG migration and new SRQL behavior.</td></tr> <tr><td>Update Kubernetes manifests to deploy the new SRQL service.</td></tr> <tr><td rowspan=4>⚪</td> <td>Validate end-to-end query correctness and performance against CNPG in staging/production.</td></tr> <tr><td>Confirm API key provisioning via KV store works in deployed environments.</td></tr> <tr><td>Verify Kubernetes manifests integrate with cluster ingress/auth and that images are <br>published.</td></tr> <tr><td>Run UI flows to confirm removal of _tp_time does not break logs/events pages.</td></tr> </tbody></table> </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=2>🟢</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> <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:** 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=4>⚪</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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R74-R95'><strong>Missing audit logs</strong></a>: New endpoints authenticate via API key but do not emit structured audit logs for critical <br>actions like query execution or translation requests (who, what, result), making <br>auditability uncertain.<br> <details open><summary>Referred Code</summary> ```rust State(state): State<AppState>, headers: HeaderMap, Json(request): Json<QueryRequest>, ) -> Result<Json<QueryResponse>> { enforce_api_key(&headers, &state.api_keys)?; let response = state.query.execute_query(request).await; match response { Ok(rows) => Ok(Json(rows)), Err(err) => Err(err), } } async fn translate( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<TranslateRequest>, ) -> Result<Json<TranslateResponse>> { enforce_api_key(&headers, &state.api_keys)?; let response = state.query.translate(request).await?; Ok(Json(response)) ... (clipped 1 lines) ``` </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: 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/1948/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491R116-R127'><strong>Generic internal wrap</strong></a>: Database pool acquisition and query load errors are wrapped as generic internal errors <br>without contextual details about the operation or inputs, which may limit actionable <br>debugging.<br> <details open><summary>Referred Code</summary> ```rust let plan = self.plan(&request, ast)?; let mut conn = self .pool .get() .await .map_err(|err| ServiceError::Internal(err.into()))?; let results = match plan.entity { Entity::Devices => devices::execute(&mut conn, &plan).await?, Entity::Events => events::execute(&mut conn, &plan).await?, Entity::Logs => logs::execute(&mut conn, &plan).await?, }; ``` </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 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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR103-R141'><strong>Secret exposure risk</strong></a>: The entrypoint echoes connection target details and constructs DATABASE_URL; although it <br>avoids printing passwords, it logs database target which could be sensitive in some <br>environments and lacks structured logging.<br> <details open><summary>Referred Code</summary> ```shell # Build DATABASE_URL if not explicitly provided DB_TARGET_DESC="custom DATABASE_URL" if [ -z "$SRQL_DATABASE_URL" ]; then CNPG_HOST_VALUE="${CNPG_HOST:-cnpg-rw}" CNPG_PORT_VALUE="${CNPG_PORT:-5432}" CNPG_DATABASE_VALUE="${CNPG_DATABASE:-telemetry}" CNPG_USERNAME_VALUE="${CNPG_USERNAME:-postgres}" CNPG_SSLMODE_VALUE="${CNPG_SSLMODE:-require}" CNPG_CERT_DIR_VALUE="${CNPG_CERT_DIR:-/etc/serviceradar/certs}" CNPG_ROOT_CERT_VALUE="${CNPG_ROOT_CERT:-}" if [ -z "$CNPG_ROOT_CERT_VALUE" ] && [ -n "$CNPG_CERT_DIR_VALUE" ]; then if [ -f "${CNPG_CERT_DIR_VALUE}/root.pem" ]; then CNPG_ROOT_CERT_VALUE="${CNPG_CERT_DIR_VALUE}/root.pem" fi fi CNPG_PASSWORD_VALUE="$(load_cnpg_password)" SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}" if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")" fi ... (clipped 18 lines) ``` </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/1948/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66R118-R121'><strong>Limited validation</strong></a>: The SRQL parser accepts arbitrary field names as filters and defers validation to query <br>layers, which may allow unsupported fields to pass silently in some paths and merits <br>verification of full validation coverage across entities.<br> <details open><summary>Referred Code</summary> ```rust filters.push(build_filter(raw_key, value)); } } } ``` </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"> <!-- placeholder --> <!-- /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> ___ #### Previous compliance checks <details> <summary>Compliance check up to commit <a href='https://github.com/carverauto/serviceradar/commit/66ae3e4358b091b43ca4f90cb4d96c8f2c9d4051'>66ae3e4</a></summary><br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=3>⚪</td> <td><details><summary><strong>Weak API key auth </strong></summary><br> <b>Description:</b> API key authentication relies solely on the presence and equality of the X-API-Key header <br>without secondary protections (e.g., per-user scoping or rotation enforcement), making <br>header leakage sufficient to access query endpoints.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R97-R111'>server.rs [97-111]</a></strong><br> <details open><summary>Referred Code</summary> ```rust fn enforce_api_key(headers: &HeaderMap, api_keys: &ApiKeyStore) -> Result<()> { if let Some(expected) = api_keys.current() { let provided = headers .get("x-api-key") .and_then(|value| value.to_str().ok()) .map(str::trim); if provided != Some(expected.trim()) { return Err(ServiceError::Auth); } } Ok(()) } ``` </details></details></td></tr> <tr><td><details><summary><strong>Credential exposure risk </strong></summary><br> <b>Description:</b> DATABASE_URL is constructed by embedding a percent-encoded password directly in the URL, <br>which can be exposed via process env or runtime diagnostics; consider using libpq env vars <br>or secret mounts to avoid credential exposure.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR120-R138'>entrypoint-srql.sh [120-138]</a></strong><br> <details open><summary>Referred Code</summary> ```shell SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}" if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")" fi if [ -n "$CNPG_PASSWORD_VALUE" ]; then ENCODED_PASS="$(urlencode "$CNPG_PASSWORD_VALUE")" SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}:${ENCODED_PASS}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}" else echo "Warning: CNPG password not provided; SRQL will attempt passwordless connection" >&2 SRQL_DATABASE_URL="postgresql://${CNPG_USERNAME_VALUE}@${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}?${SSL_PARAMS}" fi DB_TARGET_DESC="${CNPG_HOST_VALUE}:${CNPG_PORT_VALUE}/${CNPG_DATABASE_VALUE}" fi export SRQL_DATABASE_URL export DATABASE_URL="$SRQL_DATABASE_URL" ``` </details></details></td></tr> <tr><td><details><summary><strong>SQL injection surface</strong></summary><br> <b>Description:</b> The text filter macros pass user-supplied strings into Diesel eq/ilike clauses; while <br>Diesel parameterizes values, ensure no raw SQL fragments are constructed from user input <br>elsewhere to prevent SQL injection (verify all uses of sql! macros avoid user <br>concatenation).<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-393e3fa3d0e41741834cd7cd398a06111ab7b141ae6caca7a5dcc0e036172491R1-R66'>mod.rs [1-66]</a></strong><br> <details open><summary>Referred Code</summary> ```rust macro_rules! apply_text_filter { ($query:expr, $filter:expr, $column:expr) => {{ let __next = match $filter.op { crate::parser::FilterOp::Eq => { let value = $filter.value.as_scalar()?.to_string(); $query.filter($column.eq(value)) } crate::parser::FilterOp::NotEq => { let value = $filter.value.as_scalar()?.to_string(); $query.filter($column.ne(value)) } crate::parser::FilterOp::Like => { let value = $filter.value.as_scalar()?.to_string(); $query.filter($column.ilike(value)) } crate::parser::FilterOp::NotLike => { let value = $filter.value.as_scalar()?.to_string(); $query.filter($column.not_ilike(value)) } crate::parser::FilterOp::In => { let values = $filter.value.as_list()?.to_vec(); ... (clipped 45 lines) ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>🟡</td> <td> <details> <summary>🎫 <a href=https://github.com/carverauto/serviceradar/issues/1947>#1947</a></summary> <table width='100%'><tbody> <tr><td rowspan=9>🟢</td> <td>Rewrite SRQL query service to target PostgreSQL/CNPG using Rust and Diesel.</td></tr> <tr><td>Provide a parser to translate SRQL key:value DSL into a structured AST.</td></tr> <tr><td>Implement query builders for devices, events, and logs with filtering, ordering, limits, <br>and time ranges.</td></tr> <tr><td>Expose HTTP endpoints for health, query execution, and query translation, protected by API <br>key auth.</td></tr> <tr><td>Implement time range parsing supporting relative and absolute specifications.</td></tr> <tr><td>Implement cursor-based pagination.</td></tr> <tr><td>Provide configuration via environment variables (SRQL_*), including database URL and rate <br>limiting.</td></tr> <tr><td>Replace/remove deprecated Proton-specific fields in frontend types.</td></tr> <tr><td>Update build, packaging, and Docker/Kubernetes manifests to deploy the new Rust SRQL <br>service.</td></tr> <tr><td rowspan=4>⚪</td> <td>Validate end-to-end deployment in Kubernetes environments (base/staging/prod) with CNPG <br>connectivity.</td></tr> <tr><td>Verify API key distribution/rotation via external KV (`kvutil`) in target environments.</td></tr> <tr><td>Load/performance testing of new rate limiting, pagination, and query execution under <br>production workloads.</td></tr> <tr><td>Confirm frontend integrations against the new SRQL API and data shapes in UI flows.</td></tr> </tbody></table> </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=2>🟢</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> <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:** 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=4>⚪</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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R73-R95'><strong>Missing audit logs</strong></a>: New HTTP endpoints and API key checks do not emit audit logs for access attempts, <br>authentication outcomes, or query executions, making it unclear if critical actions are <br>recorded.<br> <details open><summary>Referred Code</summary> ```rust async fn query( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<QueryRequest>, ) -> Result<Json<QueryResponse>> { enforce_api_key(&headers, &state.api_keys)?; let response = state.query.execute_query(request).await; match response { Ok(rows) => Ok(Json(rows)), Err(err) => Err(err), } } async fn translate( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<TranslateRequest>, ) -> Result<Json<TranslateResponse>> { enforce_api_key(&headers, &state.api_keys)?; let response = state.query.translate(request).await?; ... (clipped 2 lines) ``` </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: 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/1948/files#diff-f4d3b33667f2e79a6ebe4cfff931f93c728d9a81c305ac13586e623850a504dbR71-R139'><strong>Input validation gaps</strong></a>: Filters accept raw strings for LIKE and IN without visible normalization or length limits, <br>and server returns parsed SQL without pagination limits in translate, which may require <br>additional validation and safeguards.<br> <details open><summary>Referred Code</summary> ```rust fn apply_filter<'a>(mut query: LogsQuery<'a>, filter: &Filter) -> Result<LogsQuery<'a>> { match filter.field.as_str() { "trace_id" => { query = apply_text_filter!(query, filter, col_trace_id)?; } "span_id" => { query = apply_text_filter!(query, filter, col_span_id)?; } "service_name" => { query = apply_text_filter!(query, filter, col_service_name)?; } "service_version" => { query = apply_text_filter!(query, filter, col_service_version)?; } "service_instance" => { query = apply_text_filter!(query, filter, col_service_instance)?; } "scope_name" => { query = apply_text_filter!(query, filter, col_scope_name)?; } "scope_version" => { ... (clipped 48 lines) ``` </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 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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR136-R141'><strong>Logs may expose DB</strong></a>: Entrypoint echoes database target details which may reveal infrastructure metadata in <br>container logs; also no evidence that application avoids logging sensitive request data.<br> <details open><summary>Referred Code</summary> ```shell export SRQL_DATABASE_URL export DATABASE_URL="$SRQL_DATABASE_URL" echo "SRQL listening on ${SRQL_LISTEN_HOST}:${SRQL_LISTEN_PORT} (database target: ${DB_TARGET_DESC})" exec "$@" ``` </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/1948/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66R79-R208'><strong>Unbounded inputs</strong></a>: The SRQL parser accepts arbitrary tokens, list sizes, and patterns without explicit <br>maximum lengths or field whitelisting enforcement at the parser level, which could lead to <br>denial-of-service or excessive query complexity.<br> <details open><summary>Referred Code</summary> ```rust pub fn parse(input: &str) -> Result<QueryAst> { let mut entity = None; let mut filters = Vec::new(); let mut order = Vec::new(); let mut limit = None; let mut time_filter = None; for token in tokenize(input) { let (raw_key, raw_value) = split_token(&token)?; let key = raw_key.trim().to_lowercase(); let value = parse_value(raw_value); match key.as_str() { "in" => { entity = Some(parse_entity(value.as_scalar()?)?); } "limit" => { let parsed = value .as_scalar()? .parse::<i64>() .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?; ... (clipped 109 lines) ``` </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"> <!-- placeholder --> <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> </details> <details> <summary>Compliance check up to commit <a href='https://github.com/carverauto/serviceradar/commit/49b3464f3510554cbd1e5090f7b1f3f7b31b18d9'>49b3464</a></summary><br> <table><tbody><tr><td colspan='2'><strong>Security Compliance</strong></td></tr> <tr><td rowspan=4>⚪</td> <td><details><summary><strong>Weak API key auth </strong></summary><br> <b>Description:</b> API key authentication relies on exact header match of 'x-api-key' without rate limiting <br>or optional rotation, which risks brute-force if exposed; ensure TLS and consider rate <br>limiting and secret management.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R81-R90'>server.rs [81-90]</a></strong><br> <details open><summary>Referred Code</summary> ```rust fn enforce_api_key(headers: &HeaderMap, config: &AppConfig) -> Result<()> { if let Some(expected) = &config.api_key { let provided = headers .get("x-api-key") .and_then(|value| value.to_str().ok()); if provided != Some(expected.as_str()) { return Err(ServiceError::Auth); } } ``` </details></details></td></tr> <tr><td><details><summary><strong>Info exposure in logs </strong></summary><br> <b>Description:</b> DATABASE_URL is constructed from environment values and printed partially to logs, which <br>may leak host/user/database info; ensure secrets (password) are not logged and consider <br>suppressing URL output entirely.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR103-R136'>entrypoint-srql.sh [103-136]</a></strong><br> <details open><summary>Referred Code</summary> ```shell # Build DATABASE_URL if not explicitly provided if [ -z "$SRQL_DATABASE_URL" ]; then CNPG_HOST_VALUE="${CNPG_HOST:-cnpg-rw}" CNPG_PORT_VALUE="${CNPG_PORT:-5432}" CNPG_DATABASE_VALUE="${CNPG_DATABASE:-telemetry}" CNPG_USERNAME_VALUE="${CNPG_USERNAME:-postgres}" CNPG_SSLMODE_VALUE="${CNPG_SSLMODE:-require}" CNPG_CERT_DIR_VALUE="${CNPG_CERT_DIR:-/etc/serviceradar/certs}" CNPG_ROOT_CERT_VALUE="${CNPG_ROOT_CERT:-}" if [ -z "$CNPG_ROOT_CERT_VALUE" ] && [ -n "$CNPG_CERT_DIR_VALUE" ]; then if [ -f "${CNPG_CERT_DIR_VALUE}/root.pem" ]; then CNPG_ROOT_CERT_VALUE="${CNPG_CERT_DIR_VALUE}/root.pem" fi fi CNPG_PASSWORD_VALUE="$(load_cnpg_password)" SSL_PARAMS="sslmode=${CNPG_SSLMODE_VALUE}" if [ -n "$CNPG_ROOT_CERT_VALUE" ]; then SSL_PARAMS="${SSL_PARAMS}&sslrootcert=$(urlencode "$CNPG_ROOT_CERT_VALUE")" fi ... (clipped 13 lines) ``` </details></details></td></tr> <tr><td><details><summary><strong>Broad LIKE patterns </strong></summary><br> <b>Description:</b> The DSL parser accepts list and scalar values and maps '%' to LIKE without explicit <br>escaping; while Diesel parameterizes inputs, LIKE patterns could enable broad scans or <br>unexpected matches—verify expected behavior and consider explicit wildcard policy.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66R206-R286'>parser.rs [206-286]</a></strong><br> <details open><summary>Referred Code</summary> ```rust let mut tokens = Vec::new(); let mut current = String::new(); let mut quote = None; let mut depth = 0usize; let mut escape = false; for ch in input.chars() { if escape { current.push(ch); escape = false; continue; } if let Some(q) = quote { if ch == '\\' { escape = true; continue; } if ch == q { quote = None; } ... (clipped 60 lines) ``` </details></details></td></tr> <tr><td><details><summary><strong>Secrets in env vars </strong></summary><br> <b>Description:</b> Falls back to reading DATABASE_URL from environment which may include credentials in plain <br>text; ensure secrets are injected securely and not logged elsewhere.<br> <strong><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-8a094b8b89c66adf329551634fa2c9c0e2ad9a1043f42012ad11cd3bf3f8ab6aR74-R83'>config.rs [74-83]</a></strong><br> <details open><summary>Referred Code</summary> ```rust let database_url = raw .srql_database_url .or(raw.database_url) .or_else(|| env::var("DATABASE_URL").ok()) .context("SRQL_DATABASE_URL or DATABASE_URL must be set")?; let allowed_origins = raw.srql_allowed_origins.and_then(|csv| { let trimmed: Vec<_> = csv .split(',') .filter_map(|part| { ``` </details></details></td></tr> <tr><td colspan='2'><strong>Ticket Compliance</strong></td></tr> <tr><td>🟡</td> <td> <details> <summary>🎫 <a href=https://github.com/carverauto/serviceradar/issues/1947>#1947</a></summary> <table width='100%'><tbody> <tr><td rowspan=10>🟢</td> <td>Replace the OCaml-based SRQL service with a Rust implementation using Diesel ORM targeting <br>CloudNativePG (PostgreSQL).</td></tr> <tr><td>Provide a parser to translate SRQL DSL (key:value syntax) into an AST with filters, <br>ordering, limits, and time ranges.</td></tr> <tr><td>Implement Diesel-backed query modules for devices, events, and logs, supporting field <br>filters and ordering.</td></tr> <tr><td>Expose HTTP endpoints for health, query execution, and translation to SQL, with API key <br>authentication.</td></tr> <tr><td>Implement cursor-based pagination and limit enforcement with sensible defaults and <br>maximums.</td></tr> <tr><td>Implement configuration via environment variables (SRQL_*), including DB URL, listen <br>address, API key, limits, and CORS.</td></tr> <tr><td>Add time range parsing supporting relative durations, keywords (today/yesterday), and <br>absolute ranges.</td></tr> <tr><td>Update build and packaging to use the Rust toolchain, adjust RBE image, Dockerfiles, and <br>RPM packaging.</td></tr> <tr><td>Remove Proton-specific fields from frontend types and align UI to CNPG-backed schema.</td></tr> <tr><td>Provide Diesel models and schema for devices, events, and logs with JSON serialization.</td></tr> <tr><td rowspan=4>⚪</td> <td>Verify end-to-end functionality against a live CNPG instance, including schema <br>availability and query performance.</td></tr> <tr><td>Validate API key auth and CORS behavior in deployed environments.</td></tr> <tr><td>Confirm UI components fully align with backend changes beyond the shown type deletion <br>(manual browser testing).</td></tr> <tr><td>Validate packaging and deployment across environments (base/staging/prod) including <br>systemd units and RPM install.</td></tr> </tbody></table> </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=2>🟢</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> <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:** 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=4>⚪</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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R56-R78'><strong>Missing audit logging</strong></a>: New API endpoints execute queries without explicit audit logs of requester identity, <br>action, and outcome beyond generic tracing, which may be acceptable if centralized logging <br>is configured elsewhere.<br> <details open><summary>Referred Code</summary> ```rust async fn query( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<QueryRequest>, ) -> Result<Json<QueryResponse>> { enforce_api_key(&headers, &state.config)?; let response = state.query.execute_query(request).await; match response { Ok(rows) => Ok(Json(rows)), Err(err) => Err(err), } } async fn translate( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<TranslateRequest>, ) -> Result<Json<TranslateResponse>> { enforce_api_key(&headers, &state.config)?; let response = state.query.translate(request).await?; ... (clipped 2 lines) ``` </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: 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/1948/files#diff-ad603163a35223c63c68279e53a1a4c9219b9096042a168d8a87443abaa29a58R298-R311'><strong>Partial edge handling</strong></a>: Filters fall through silently for unsupported fields (no-op) which may hide user errors <br>and lacks explicit validation feedback for unknown filter names.<br> <details open><summary>Referred Code</summary> ```rust "level" => { let value = parse_i32(filter.value.as_scalar()?)?; query = match filter.op { FilterOp::Eq => query.filter(col_level.eq(value)), FilterOp::NotEq => query.filter(col_level.ne(value)), _ => { return Err(ServiceError::InvalidRequest( "level only supports equality comparisons".into(), )) } }; } _ => {} } ``` </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 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:** <br><a href='https://github.com/carverauto/serviceradar/pull/1948/files#diff-57c82b01f92e9bd40063d0b0178c12d452771ac133f2121fb0ac008b167da367R112-R131'><strong>Potential sensitive data</strong></a>: The JSON responses include raw attributes fields (e.g., logs attributes and <br>resource_attributes) whose contents are not scrubbed and may contain sensitive data <br>depending on upstream ingestion.<br> <details open><summary>Referred Code</summary> ```rust impl LogRow { pub fn into_json(self) -> serde_json::Value { serde_json::json!({ "timestamp": self.timestamp, "trace_id": self.trace_id, "span_id": self.span_id, "severity_text": self.severity_text, "severity_number": self.severity_number, "body": self.body, "service_name": self.service_name, "service_version": self.service_version, "service_instance": self.service_instance, "scope_name": self.scope_name, "scope_version": self.scope_version, "attributes": self.attributes.clone(), "resource_attributes": self.resource_attributes, "raw_data": self.attributes.unwrap_or_default(), }) } ``` </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/1948/files#diff-2fc110913a9b9140116cb47742007161b4016b81cc0c325a2b635905c12a3811R56-R78'><strong>Input validation scope</strong></a>: The SRQL parser and query builders validate many cases, but unknown filter fields are <br>accepted as no-ops and body size/rate limits are not visible here, requiring confirmation <br>that upstream limits and auth are enforced.<br> <details open><summary>Referred Code</summary> ```rust async fn query( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<QueryRequest>, ) -> Result<Json<QueryResponse>> { enforce_api_key(&headers, &state.config)?; let response = state.query.execute_query(request).await; match response { Ok(rows) => Ok(Json(rows)), Err(err) => Err(err), } } async fn translate( State(state): State<AppState>, headers: HeaderMap, Json(request): Json<TranslateRequest>, ) -> Result<Json<TranslateResponse>> { enforce_api_key(&headers, &state.config)?; let response = state.query.translate(request).await?; ... (clipped 2 lines) ``` </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"> <!-- /compliance --update_compliance=true --> </td></tr></tbody></table> </details>
qodo-code-review[bot] commented 2025-11-17 01:23:34 +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/1948#issuecomment-3539609094
Original created: 2025-11-17T01:23:34Z

PR Code Suggestions

Latest suggestions up to 3cdc1dd

CategorySuggestion                                                                                                                                    Impact
Incremental [*]
Do not trust client-exposed env

Remove the fallback to process.env.NEXT_PUBLIC_API_KEY in getApiKey to avoid
using a client-exposed key for server-side requests.

web/src/lib/config.ts [82-85]

 // Get API key for server-side requests
 export function getApiKey(): string {
-  const raw = process.env.API_KEY || process.env.NEXT_PUBLIC_API_KEY || "";
+  const raw = process.env.API_KEY || "";
   return raw.trim();
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a security risk where a client-exposed environment variable (NEXT_PUBLIC_API_KEY) could be used for server-side authentication, which should be avoided.

High
Enforce SRQL Nginx route precedence
Suggestion Impact:The /api/query location was updated to use the ^~ modifier, ensuring correct route precedence.

code diff:

-        location /api/query {
-          proxy_pass http://serviceradar-kong:8000;
-          proxy_set_header Host $host;
-          proxy_set_header X-Real-IP $remote_addr;
-          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-          proxy_set_header X-Forwarded-Proto $scheme;
+    location ^~ /api/query {
+      proxy_pass http://serviceradar-kong:8000;
+      proxy_set_header Host $host;
+      proxy_set_header X-Real-IP $remote_addr;
+      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header X-Forwarded-Proto $scheme;

Add the ^~ modifier to the location /api/query block in the Nginx configuration
to ensure it takes precedence over the more general regex-based /api/ location
block.

k8s/demo/base/configmap.yaml [34-40]

-location /api/query {
+location ^~ /api/query {
   proxy_pass http://serviceradar-kong:8000;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   proxy_set_header X-Forwarded-Proto $scheme;
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: This is a critical fix for Nginx configuration. Without the ^~ modifier, a request for /api/query would be incorrectly handled by the subsequent regex location block, causing SRQL queries to fail. The suggestion correctly identifies and fixes this routing bug.

Medium
Fix SRQL ingress routing target
Suggestion Impact:The commit changed the ingress backend service from serviceradar-web to serviceradar-kong and updated the port from 3000 to 8000 for the /api/query path.

code diff:

-            name: serviceradar-web
+            name: serviceradar-kong
             port:
-              number: 3000
+              number: 8000

Update the ingress for /api/query to route traffic directly to serviceradar-kong
instead of serviceradar-web to simplify the request path.

k8s/demo/staging/ingress.yaml [97-103]

 - path: /api/query
   pathType: Prefix
   backend:
     service:
-      name: serviceradar-web
+      name: serviceradar-kong
       port:
-        number: 3000
+        number: 8000

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that routing /api/query through serviceradar-web is inefficient. While the current setup does proxy to Kong, the proposed change to route directly from the ingress to serviceradar-kong simplifies the request path and improves maintainability.

Low
Possible issue
Fix SRQL ingress routing
Suggestion Impact:The commit updated the ingress backend service name from serviceradar-web to serviceradar-kong and changed the port from 3000 to 8000, aligning with the suggestion.

code diff:

-            name: serviceradar-web
+            name: serviceradar-kong
             port:
-              number: 3000
+              number: 8000

Correct the ingress routing for /api/query in the staging environment. It
currently points to the web service instead of the Kong gateway, which would
cause queries to fail and bypass authentication.

k8s/demo/staging/ingress.yaml [97-103]

 - path: /api/query
   pathType: Prefix
   backend:
     service:
-      name: serviceradar-web
+      name: serviceradar-kong
       port:
-        number: 3000
+        number: 8000

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: This suggestion identifies a critical misconfiguration in the staging ingress that would route authenticated queries to the wrong service, bypassing the new SRQL backend and its authentication layer.

High
Support numeric IN/NOT IN on severity
Suggestion Impact:The commit implemented IN/NOT IN handling for severity_number, parsing list values, applying eq_any/ne_all, and updated the error message as suggested (with a minor difference of early return on empty lists).

code diff:

+            match filter.op {
+                FilterOp::Eq | FilterOp::NotEq => {
+                    let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| {
+                        ServiceError::InvalidRequest("severity_number must be an integer".into())
+                    })?;
+                    query = match filter.op {
+                        FilterOp::Eq => query.filter(col_severity_number.eq(value)),
+                        FilterOp::NotEq => query.filter(col_severity_number.ne(value)),
+                        _ => unreachable!(),
+                    };
+                }
+                FilterOp::In | FilterOp::NotIn => {
+                    let values: Vec<i32> = filter
+                        .value
+                        .as_list()?
+                        .iter()
+                        .map(|v| v.parse::<i32>())
+                        .collect::<std::result::Result<Vec<_>, _>>()
+                        .map_err(|_| {
+                            ServiceError::InvalidRequest(
+                                "severity_number list must be integers".into(),
+                            )
+                        })?;
+                    if values.is_empty() {
+                        return Ok(query);
+                    }
+                    query = match filter.op {
+                        FilterOp::In => query.filter(col_severity_number.eq_any(values)),
+                        FilterOp::NotIn => query.filter(col_severity_number.ne_all(values)),
+                        _ => unreachable!(),
+                    };
+                }
                 _ => {
                     return Err(ServiceError::InvalidRequest(
-                        "severity_number only supports equality comparisons".into(),
+                        "severity_number only supports equality and IN/NOT IN comparisons".into(),
                     ))
                 }
-            };
+            }

Add support for IN and NOT IN operations on the severity_number filter to handle
list values like severity_number:(1,2,3), aligning its behavior with other
filterable fields.

rust/srql/src/query/logs.rs [100-113]

 "severity_number" => {
-    let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| {
-        ServiceError::InvalidRequest("severity_number must be an integer".into())
-    })?;
-    query = match filter.op {
-        FilterOp::Eq => query.filter(col_severity_number.eq(value)),
-        FilterOp::NotEq => query.filter(col_severity_number.ne(value)),
+    match &filter.op {
+        FilterOp::Eq | FilterOp::NotEq => {
+            let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| {
+                ServiceError::InvalidRequest("severity_number must be an integer".into())
+            })?;
+            query = match filter.op {
+                FilterOp::Eq => query.filter(col_severity_number.eq(value)),
+                FilterOp::NotEq => query.filter(col_severity_number.ne(value)),
+                _ => unreachable!(),
+            };
+        }
+        FilterOp::In | FilterOp::NotIn => {
+            let values: Vec<i32> = filter
+                .value
+                .as_list()?
+                .iter()
+                .map(|v| v.parse::<i32>())
+                .collect::<std::result::Result<Vec<_>, _>>()
+                .map_err(|_| {
+                    ServiceError::InvalidRequest("severity_number list must be integers".into())
+                })?;
+            if !values.is_empty() {
+                query = match filter.op {
+                    FilterOp::In => query.filter(col_severity_number.eq_any(values)),
+                    FilterOp::NotIn => query.filter(col_severity_number.ne_all(values)),
+                    _ => unreachable!(),
+                };
+            }
+        }
         _ => {
             return Err(ServiceError::InvalidRequest(
-                "severity_number only supports equality comparisons".into(),
-            ))
+                "severity_number only supports equality and IN/NOT IN comparisons".into(),
+            ));
         }
-    };
+    }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that severity_number filtering lacks support for list operations (IN/NOT IN), which is inconsistent with other fields and improves feature completeness.

Medium
Sanitize RPM version correctly
Suggestion Impact:The commit replaced the simple sed substitution with the proposed RAW and SANITIZED variables and used RPM_VERSION_SANITIZED in the rpmbuild define, matching the suggested robust sanitization.

code diff:

-RUN RPM_VERSION=$(echo ${VERSION} | sed 's/-/_/g') \
+RUN RPM_VERSION_RAW="${VERSION}" \
+    && RPM_VERSION_SANITIZED="$(printf '%s' "${RPM_VERSION_RAW}" | sed -E 's/[^0-9A-Za-z.]+/./g; s/^[^0-9A-Za-z]+//; s/[.]+/./g; s/[.]$//')" \
     && rpmbuild -bb SPECS/serviceradar-${COMPONENT}.spec \
         --define "_topdir ${RPMBUILD_DIR}" \
-        --define "version ${RPM_VERSION}" \
+        --define "version ${RPM_VERSION_SANITIZED}" \
         --define "release ${RELEASE}"

Improve RPM version string sanitization to be more robust. The current method of
replacing hyphens with underscores is insufficient and may lead to invalid
version strings for rpmbuild.

docker/rpm/Dockerfile.rpm.srql [60-64]

-RUN RPM_VERSION=$(echo ${VERSION} | sed 's/-/_/g') \
+RUN RPM_VERSION_RAW="${VERSION}" \
+    && RPM_VERSION_SANITIZED="$(printf '%s' "${RPM_VERSION_RAW}" | sed -E 's/[^0-9A-Za-z.]+/./g; s/^[^0-9A-Za-z]+//; s/[.]+/./g; s/[.]$//')" \
     && rpmbuild -bb SPECS/serviceradar-${COMPONENT}.spec \
         --define "_topdir ${RPMBUILD_DIR}" \
-        --define "version ${RPM_VERSION}" \
+        --define "version ${RPM_VERSION_SANITIZED}" \
         --define "release ${RELEASE}"

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the current RPM version sanitization is not robust, but the proposed sed command is overly complex and may not be necessary if the input VERSION format is controlled.

Low
Security
Fix unsafe URL encoding for passwords
Suggestion Impact:The commit rewrote urlencode to iterate by bytes with LC_ALL=C, switched to building 'out' instead of 'output', used printf -v for hex encoding, and handled characters similarly, implementing the suggested safer UTF-8 aware encoding.

code diff:

 urlencode() {
+    # Percent-encode arbitrary bytes (safe for UTF-8 secrets)
     local input="$1"
-    local length=${#input}
-    local i=1
-    local output=""
-    while [ $i -le $length ]; do
-        char=$(printf '%s' "$input" | cut -c $i)
+    local out=""
+    local i=0
+    local char hex
+    LC_ALL=C
+    while [ $i -lt ${#input} ]; do
+        char=${input:$i:1}
         case "$char" in
             [a-zA-Z0-9.~_-])
-                output="${output}${char}"
-                ;;
-            "")
+                out="${out}${char}"
                 ;;
             *)
-                hex=$(printf '%%%02X' "'$char")
-                output="${output}${hex}"
+                printf -v hex '%%%02X' "'$char"
+                out="${out}${hex}"
                 ;;
         esac
         i=$((i + 1))
     done
-    printf '%s' "$output"
+    printf '%s' "$out"
 }

Fix the urlencode shell function to correctly handle multi-byte UTF-8 and
special characters by iterating byte-wise, preventing malformed database
connection URLs.

docker/compose/entrypoint-srql.sh [20-41]

 urlencode() {
-    local input="$1"
-    local length=${#input}
-    local i=1
-    local output=""
-    while [ $i -le $length ]; do
-        char=$(printf '%s' "$input" | cut -c $i)
-        case "$char" in
+    # Percent-encode arbitrary bytes from stdin-safe string
+    # Works correctly for UTF-8 and special characters
+    local s="$1"
+    local i c hex
+    local out=""
+    i=0
+    # Use LC_ALL=C to ensure byte-wise iteration
+    LC_ALL=C
+    while [ $i -lt ${#s} ]; do
+        c=${s:$i:1}
+        case "$c" in
             [a-zA-Z0-9.~_-])
-                output="${output}${char}"
-                ;;
-            "")
+                out="${out}${c}"
                 ;;
             *)
-                hex=$(printf '%%%02X' "'$char")
-                output="${output}${hex}"
+                printf -v hex '%%%02X' "'$c"
+                out="${out}${hex}"
                 ;;
         esac
         i=$((i + 1))
     done
-    printf '%s' "$output"
+    printf '%s' "$out"
 }

[Suggestion processed]

Suggestion importance[1-10]: 9

__

Why: This is a critical bug fix for the urlencode function, which would fail to correctly encode multi-byte UTF-8 characters or certain special characters in passwords, leading to connection failures.

High
General
Avoid global PG path pollution

Avoid globally setting PostgreSQL library paths in the RBE image. Instead, make
them optional and controlled by a build argument to prevent potential conflicts
with other build actions.

docker/Dockerfile.rbe [55-62]

 ENV CC=/usr/bin/gcc \
     CXX=/usr/bin/g++ \
     RUSTUP_HOME=/opt/rustup \
     CARGO_HOME=/opt/cargo \
-    PATH=/opt/cargo/bin:/opt/rh/gcc-toolset-13/root/usr/bin:$PATH \
-    LIBRARY_PATH=/usr/pgsql-16/lib:$LIBRARY_PATH \
-    LD_LIBRARY_PATH=/usr/pgsql-16/lib:$LD_LIBRARY_PATH \
-    PKG_CONFIG_PATH=/usr/pgsql-16/lib/pkgconfig:$PKG_CONFIG_PATH
+    PATH=/opt/cargo/bin:/opt/rh/gcc-toolset-13/root/usr/bin:$PATH
+# Optional: enable CNPG paths only when explicitly requested by builds
+ARG ENABLE_PG_PATHS=0
+ENV LIBRARY_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib:$LIBRARY_PATH" || echo "$LIBRARY_PATH") \
+    LD_LIBRARY_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib:$LD_LIBRARY_PATH" || echo "$LD_LIBRARY_PATH") \
+    PKG_CONFIG_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib/pkgconfig:$PKG_CONFIG_PATH" || echo "$PKG_CONFIG_PATH")

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out a potential issue with globally setting linker paths in a shared build image, which could cause conflicts. Making these paths optional improves the image's reusability and robustness.

Medium
Enforce positive limit during parse
Suggestion Impact:The commit added a check for parsed <= 0 and returns an InvalidRequest error with the message "limit must be a positive integer", matching the suggestion.

code diff:

+                if parsed <= 0 {
+                    return Err(ServiceError::InvalidRequest(
+                        "limit must be a positive integer".into(),
+                    ));
+                }
                 limit = Some(parsed);

Validate that the limit parameter is a positive integer during parsing to
provide earlier and clearer error feedback to the user.

rust/srql/src/parser.rs [95-101]

-for token in tokenize(input) {
-    let (raw_key, raw_value) = split_token(&token)?;
-    let key = raw_key.trim().to_lowercase();
-    let value = parse_value(raw_value);
-
-    match key.as_str() {
-        "in" => {
-            entity = Some(parse_entity(value.as_scalar()?)?);
-        }
-        "limit" => {
-            let parsed = value
-                .as_scalar()?
-                .parse::<i64>()
-                .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?;
-            limit = Some(parsed);
-        }
-        ...
+"limit" => {
+    let parsed = value
+        .as_scalar()?
+        .parse::<i64>()
+        .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?;
+    if parsed <= 0 {
+        return Err(ServiceError::InvalidRequest(
+            "limit must be a positive integer".into(),
+        ));
     }
+    limit = Some(parsed);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out that negative limits are not validated during parsing, but the impact is low because the value is clamped to a valid range later in the determine_limit function.

Low
  • Update

Previous suggestions

Suggestions up to commit 49b3464
CategorySuggestion                                                                                                                                    Impact
High-level
Refactor query builders to reduce duplication

The query building logic in rust/srql/src/query/ for devices, events, and logs
has highly duplicated apply_filter functions. This can be refactored using a
generic abstraction like a macro to reduce boilerplate and improve
maintainability.

Examples:

rust/srql/src/query/events.rs [76-314]
rust/srql/src/query/logs.rs [71-271]

Solution Walkthrough:

Before:

// in rust/srql/src/query/events.rs
fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> {
    match filter.field.as_str() {
        "id" => {
            let value = filter.value.as_scalar()?.to_string();
            query = match filter.op {
                FilterOp::Eq => query.filter(col_id.eq(value)),
                FilterOp::NotEq => query.filter(col_id.ne(value)),
                FilterOp::Like => query.filter(col_id.ilike(value)),
                // ... and so on for In, NotIn
            };
        }
        "type" => {
            let value = filter.value.as_scalar()?.to_string();
            query = match filter.op {
                FilterOp::Eq => query.filter(col_event_type.eq(value)),
                FilterOp::NotEq => query.filter(col_event_type.ne(value)),
                FilterOp::Like => query.filter(col_event_type.ilike(value)),
                // ... identical logic, different column
            };
        }
        // ... this pattern is repeated for many other fields
    }
    Ok(query)
}

After:

// Define a macro to handle the repetitive filter logic
macro_rules! apply_string_filter {
    ($query:expr, $filter:expr, $column:expr) => {
        // ... logic for Eq, NotEq, Like, NotLike, In, NotIn using the column
    };
}

// in rust/srql/src/query/events.rs
fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> {
    match filter.field.as_str() {
        "id" => {
            query = apply_string_filter!(query, filter, col_id)?;
        }
        "type" => {
            query = apply_string_filter!(query, filter, col_event_type)?;
        }
        "source" => {
            query = apply_string_filter!(query, filter, col_source)?;
        }
        // ... other fields now use the macro, greatly reducing code size
    }
    Ok(query)
}

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies significant code duplication in the apply_filter functions across three core files, and addressing this would substantially improve the new service's maintainability and extensibility.

Medium
Possible issue
Handle negated array containment filters
Suggestion Impact:The commit updated the discovery_sources filter to apply a NOT around the array containment expression when the operator is NotIn, implementing the suggested behavior.

code diff:

             let values = match &filter.value {
@@ -219,7 +166,11 @@
             }
             let expr = sql::<Bool>("coalesce(discovery_sources, ARRAY[]::text[]) @> ")
                 .bind::<Array<Text>, _>(values);
-            query = query.filter(expr);
+            query = if matches!(filter.op, FilterOp::NotIn) {
+                query.filter(not(expr))
+            } else {
+                query.filter(expr)
+            };

Implement handling for negated filters (FilterOp::NotIn) on the
discovery_sources field by wrapping the SQL expression with a NOT operator.

rust/srql/src/query/devices.rs [212-223]

 "discovery_sources" => {
     let values = match &filter.value {
         FilterValue::Scalar(v) => vec![v.to_string()],
         FilterValue::List(list) => list.clone(),
     };
     if values.is_empty() {
         return Ok(query);
     }
     let expr = sql::<Bool>("coalesce(discovery_sources, ARRAY[]::text[]) @> ")
         .bind::<Array<Text>, _>(values);
-    query = query.filter(expr);
+    
+    query = if matches!(filter.op, FilterOp::NotIn) {
+        query.filter(diesel::dsl::not(expr))
+    } else {
+        query.filter(expr)
+    };
 }
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where the negation operator for the discovery_sources filter is parsed but not implemented, leading to incorrect query results for negated filters.

Medium
Error on unsupported filter fields
Suggestion Impact:The commit changed the default match arm to return ServiceError::InvalidRequest for unsupported filter fields, matching the suggestion.

code diff:

-        _ => {}
+        other => {
+            return Err(ServiceError::InvalidRequest(format!(
+                "unsupported filter field for events: '{other}'"
+            )));
+        }

Modify the apply_filter function in events.rs to return an error for unsupported
filter fields instead of silently ignoring them.

rust/srql/src/query/events.rs [76-314]

 fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> {
     match filter.field.as_str() {
         "id" => {
             ...
         }
         "level" => {
             let value = parse_i32(filter.value.as_scalar()?)?;
             query = match filter.op {
                 FilterOp::Eq => query.filter(col_level.eq(value)),
                 FilterOp::NotEq => query.filter(col_level.ne(value)),
                 _ => {
                     return Err(ServiceError::InvalidRequest(
                         "level only supports equality comparisons".into(),
                     ))
                 }
             };
         }
-        _ => {}
+        other => {
+            return Err(ServiceError::InvalidRequest(format!(
+                "unsupported filter field for events: '{other}'"
+            )));
+        }
     }
 
     Ok(query)
 }
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that silently ignoring unknown filters is poor API design and can hide user errors, so returning an error improves robustness and provides better feedback.

Low
General
Improve graph ingestion performance
Suggestion Impact:The commit replaced the FOREACH loop with WITH + UNWIND over services and preserved the MERGE operations, implementing the suggested performance improvement.

code diff:

-  FOREACH (svc IN snap.services |
-    MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name})
-    MERGE (d)-[:RUNS_ON]->(s)
-  )
+  WITH d, snap
+  UNWIND snap.services AS svc
+  MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name})
+  MERGE (d)-[:RUNS_ON]->(s)

Replace the FOREACH clause with a more performant UNWIND clause in the Cypher
query to optimize bulk graph ingestion.

sr-architecture-and-design/prd/03-proton.md [143-152]

 SELECT *
 FROM cypher('topology_graph', $$
   UNWIND $snapshots AS snap
   MERGE (d:Device {tenant_id: snap.tenant_id, device_id: snap.device_id})
     SET d.hostname = snap.hostname, d.os = snap.os
-  FOREACH (svc IN snap.services |
-    MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name})
-    MERGE (d)-[:RUNS_ON]->(s)
-  )
+  WITH d, snap
+  UNWIND snap.services AS svc
+  MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name})
+  MERGE (d)-[:RUNS_ON]->(s)
 $$) AS (result agtype);
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a performance anti-pattern in the Cypher query example and proposes using UNWIND instead of FOREACH for better bulk ingestion performance, which is a valid best practice.

Low
Reduce health check interval

Reduce the health check interval from 30 seconds to 10 seconds for faster
failure detection and recovery in the local development environment.

docker-compose.yml [363-367]

 healthcheck:
   test: ["CMD", "curl", "-sf", "http://localhost:8080/healthz"]
-  interval: 30s
+  interval: 10s
   timeout: 5s
   retries: 5
Suggestion importance[1-10]: 4

__

Why: The suggestion correctly points out that a 30-second health check interval is long for a local development environment, and reducing it would allow for faster failure detection.

Low
Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/1948#issuecomment-3539609094 Original created: 2025-11-17T01:23:34Z --- ## PR Code Suggestions ✨ <!-- 3cdc1dd --> Latest suggestions up to 3cdc1dd <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=3>Incremental <sup><a href='https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/'>[*]</a></sup></td> <td> <details><summary>Do not trust client-exposed env</summary> ___ **Remove the fallback to <code>process.env.NEXT_PUBLIC_API_KEY</code> in <code>getApiKey</code> to avoid <br>using a client-exposed key for server-side requests.** [web/src/lib/config.ts [82-85]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-1be24fc848b0eda79de9ca091aee3521aaff483c6da940d6b4e5afa0b7c5fe7fR82-R85) ```diff // Get API key for server-side requests export function getApiKey(): string { - const raw = process.env.API_KEY || process.env.NEXT_PUBLIC_API_KEY || ""; + const raw = process.env.API_KEY || ""; return raw.trim(); } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: The suggestion correctly identifies a security risk where a client-exposed environment variable (`NEXT_PUBLIC_API_KEY`) could be used for server-side authentication, which should be avoided. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>✅ <s>Enforce SRQL Nginx route precedence</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The /api/query location was updated to use the ^~ modifier, ensuring correct route precedence. code diff: ```diff - location /api/query { - proxy_pass http://serviceradar-kong:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; + location ^~ /api/query { + proxy_pass http://serviceradar-kong:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; ``` </details> ___ **Add the <code>^~</code> modifier to the <code>location /api/query</code> block in the Nginx configuration <br>to ensure it takes precedence over the more general regex-based <code>/api/</code> location <br>block.** [k8s/demo/base/configmap.yaml [34-40]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-f4548beaa0a3a01a46971c82c5647a0f3f49eb38d66dd939d06d19018173fcd6R34-R40) ```diff -location /api/query { +location ^~ /api/query { proxy_pass http://serviceradar-kong:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } ``` `[Suggestion processed]` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: This is a critical fix for Nginx configuration. Without the `^~` modifier, a request for `/api/query` would be incorrectly handled by the subsequent regex location block, causing SRQL queries to fail. The suggestion correctly identifies and fixes this routing bug. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>✅ <s>Fix SRQL ingress routing target</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit changed the ingress backend service from serviceradar-web to serviceradar-kong and updated the port from 3000 to 8000 for the /api/query path. code diff: ```diff - name: serviceradar-web + name: serviceradar-kong port: - number: 3000 + number: 8000 ``` </details> ___ **Update the ingress for <code>/api/query</code> to route traffic directly to <code>serviceradar-kong</code> <br>instead of <code>serviceradar-web</code> to simplify the request path.** [k8s/demo/staging/ingress.yaml [97-103]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-b73659cd220fe91eaf5c98cfc0844d203477e1a985d06a87ecd16e79c3ec0ca9R97-R103) ```diff - path: /api/query pathType: Prefix backend: service: - name: serviceradar-web + name: serviceradar-kong port: - number: 3000 + number: 8000 ``` `[Suggestion processed]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies that routing `/api/query` through `serviceradar-web` is inefficient. While the current setup does proxy to Kong, the proposed change to route directly from the ingress to `serviceradar-kong` simplifies the request path and improves maintainability. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=3>Possible issue</td> <td> <details><summary>✅ <s>Fix SRQL ingress routing</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit updated the ingress backend service name from serviceradar-web to serviceradar-kong and changed the port from 3000 to 8000, aligning with the suggestion. code diff: ```diff - name: serviceradar-web + name: serviceradar-kong port: - number: 3000 + number: 8000 ``` </details> ___ **Correct the ingress routing for <code>/api/query</code> in the staging environment. It <br>currently points to the web service instead of the Kong gateway, which would <br>cause queries to fail and bypass authentication.** [k8s/demo/staging/ingress.yaml [97-103]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-b73659cd220fe91eaf5c98cfc0844d203477e1a985d06a87ecd16e79c3ec0ca9R97-R103) ```diff - path: /api/query pathType: Prefix backend: service: - name: serviceradar-web + name: serviceradar-kong port: - number: 3000 + number: 8000 ``` `[Suggestion processed]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: This suggestion identifies a critical misconfiguration in the staging ingress that would route authenticated queries to the wrong service, bypassing the new SRQL backend and its authentication layer. </details></details></td><td align=center>High </td></tr><tr><td> <details><summary>✅ <s>Support numeric IN/NOT IN on severity</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit implemented IN/NOT IN handling for severity_number, parsing list values, applying eq_any/ne_all, and updated the error message as suggested (with a minor difference of early return on empty lists). code diff: ```diff + match filter.op { + FilterOp::Eq | FilterOp::NotEq => { + let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| { + ServiceError::InvalidRequest("severity_number must be an integer".into()) + })?; + query = match filter.op { + FilterOp::Eq => query.filter(col_severity_number.eq(value)), + FilterOp::NotEq => query.filter(col_severity_number.ne(value)), + _ => unreachable!(), + }; + } + FilterOp::In | FilterOp::NotIn => { + let values: Vec<i32> = filter + .value + .as_list()? + .iter() + .map(|v| v.parse::<i32>()) + .collect::<std::result::Result<Vec<_>, _>>() + .map_err(|_| { + ServiceError::InvalidRequest( + "severity_number list must be integers".into(), + ) + })?; + if values.is_empty() { + return Ok(query); + } + query = match filter.op { + FilterOp::In => query.filter(col_severity_number.eq_any(values)), + FilterOp::NotIn => query.filter(col_severity_number.ne_all(values)), + _ => unreachable!(), + }; + } _ => { return Err(ServiceError::InvalidRequest( - "severity_number only supports equality comparisons".into(), + "severity_number only supports equality and IN/NOT IN comparisons".into(), )) } - }; + } ``` </details> ___ **Add support for <code>IN</code> and <code>NOT IN</code> operations on the <code>severity_number</code> filter to handle <br>list values like <code>severity_number:(1,2,3)</code>, aligning its behavior with other <br>filterable fields.** [rust/srql/src/query/logs.rs [100-113]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-f4d3b33667f2e79a6ebe4cfff931f93c728d9a81c305ac13586e623850a504dbR100-R113) ```diff "severity_number" => { - let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| { - ServiceError::InvalidRequest("severity_number must be an integer".into()) - })?; - query = match filter.op { - FilterOp::Eq => query.filter(col_severity_number.eq(value)), - FilterOp::NotEq => query.filter(col_severity_number.ne(value)), + match &filter.op { + FilterOp::Eq | FilterOp::NotEq => { + let value = filter.value.as_scalar()?.parse::<i32>().map_err(|_| { + ServiceError::InvalidRequest("severity_number must be an integer".into()) + })?; + query = match filter.op { + FilterOp::Eq => query.filter(col_severity_number.eq(value)), + FilterOp::NotEq => query.filter(col_severity_number.ne(value)), + _ => unreachable!(), + }; + } + FilterOp::In | FilterOp::NotIn => { + let values: Vec<i32> = filter + .value + .as_list()? + .iter() + .map(|v| v.parse::<i32>()) + .collect::<std::result::Result<Vec<_>, _>>() + .map_err(|_| { + ServiceError::InvalidRequest("severity_number list must be integers".into()) + })?; + if !values.is_empty() { + query = match filter.op { + FilterOp::In => query.filter(col_severity_number.eq_any(values)), + FilterOp::NotIn => query.filter(col_severity_number.ne_all(values)), + _ => unreachable!(), + }; + } + } _ => { return Err(ServiceError::InvalidRequest( - "severity_number only supports equality comparisons".into(), - )) + "severity_number only supports equality and IN/NOT IN comparisons".into(), + )); } - }; + } } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly identifies that `severity_number` filtering lacks support for list operations (`IN`/`NOT IN`), which is inconsistent with other fields and improves feature completeness. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>✅ <s>Sanitize RPM version correctly</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit replaced the simple sed substitution with the proposed RAW and SANITIZED variables and used RPM_VERSION_SANITIZED in the rpmbuild define, matching the suggested robust sanitization. code diff: ```diff -RUN RPM_VERSION=$(echo ${VERSION} | sed 's/-/_/g') \ +RUN RPM_VERSION_RAW="${VERSION}" \ + && RPM_VERSION_SANITIZED="$(printf '%s' "${RPM_VERSION_RAW}" | sed -E 's/[^0-9A-Za-z.]+/./g; s/^[^0-9A-Za-z]+//; s/[.]+/./g; s/[.]$//')" \ && rpmbuild -bb SPECS/serviceradar-${COMPONENT}.spec \ --define "_topdir ${RPMBUILD_DIR}" \ - --define "version ${RPM_VERSION}" \ + --define "version ${RPM_VERSION_SANITIZED}" \ --define "release ${RELEASE}" ``` </details> ___ **Improve RPM version string sanitization to be more robust. The current method of <br>replacing hyphens with underscores is insufficient and may lead to invalid <br>version strings for <code>rpmbuild</code>.** [docker/rpm/Dockerfile.rpm.srql [60-64]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-4aed257fbbf9eddfb69e5c2c023f017af5ab13107d3221efc9ebfa288b9583e3R60-R64) ```diff -RUN RPM_VERSION=$(echo ${VERSION} | sed 's/-/_/g') \ +RUN RPM_VERSION_RAW="${VERSION}" \ + && RPM_VERSION_SANITIZED="$(printf '%s' "${RPM_VERSION_RAW}" | sed -E 's/[^0-9A-Za-z.]+/./g; s/^[^0-9A-Za-z]+//; s/[.]+/./g; s/[.]$//')" \ && rpmbuild -bb SPECS/serviceradar-${COMPONENT}.spec \ --define "_topdir ${RPMBUILD_DIR}" \ - --define "version ${RPM_VERSION}" \ + --define "version ${RPM_VERSION_SANITIZED}" \ --define "release ${RELEASE}" ``` `[Suggestion processed]` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies that the current RPM version sanitization is not robust, but the proposed `sed` command is overly complex and may not be necessary if the input `VERSION` format is controlled. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=1>Security</td> <td> <details><summary>✅ <s>Fix unsafe URL encoding for passwords</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit rewrote urlencode to iterate by bytes with LC_ALL=C, switched to building 'out' instead of 'output', used printf -v for hex encoding, and handled characters similarly, implementing the suggested safer UTF-8 aware encoding. code diff: ```diff urlencode() { + # Percent-encode arbitrary bytes (safe for UTF-8 secrets) local input="$1" - local length=${#input} - local i=1 - local output="" - while [ $i -le $length ]; do - char=$(printf '%s' "$input" | cut -c $i) + local out="" + local i=0 + local char hex + LC_ALL=C + while [ $i -lt ${#input} ]; do + char=${input:$i:1} case "$char" in [a-zA-Z0-9.~_-]) - output="${output}${char}" - ;; - "") + out="${out}${char}" ;; *) - hex=$(printf '%%%02X' "'$char") - output="${output}${hex}" + printf -v hex '%%%02X' "'$char" + out="${out}${hex}" ;; esac i=$((i + 1)) done - printf '%s' "$output" + printf '%s' "$out" } ``` </details> ___ **Fix the <code>urlencode</code> shell function to correctly handle multi-byte UTF-8 and <br>special characters by iterating byte-wise, preventing malformed database <br>connection URLs.** [docker/compose/entrypoint-srql.sh [20-41]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-16cf075815de9081558fd4ee53d1138c488cd8756b43259be4a82c86dac514acR20-R41) ```diff urlencode() { - local input="$1" - local length=${#input} - local i=1 - local output="" - while [ $i -le $length ]; do - char=$(printf '%s' "$input" | cut -c $i) - case "$char" in + # Percent-encode arbitrary bytes from stdin-safe string + # Works correctly for UTF-8 and special characters + local s="$1" + local i c hex + local out="" + i=0 + # Use LC_ALL=C to ensure byte-wise iteration + LC_ALL=C + while [ $i -lt ${#s} ]; do + c=${s:$i:1} + case "$c" in [a-zA-Z0-9.~_-]) - output="${output}${char}" - ;; - "") + out="${out}${c}" ;; *) - hex=$(printf '%%%02X' "'$char") - output="${output}${hex}" + printf -v hex '%%%02X' "'$c" + out="${out}${hex}" ;; esac i=$((i + 1)) done - printf '%s' "$output" + printf '%s' "$out" } ``` `[Suggestion processed]` <details><summary>Suggestion importance[1-10]: 9</summary> __ Why: This is a critical bug fix for the `urlencode` function, which would fail to correctly encode multi-byte UTF-8 characters or certain special characters in passwords, leading to connection failures. </details></details></td><td align=center>High </td></tr><tr><td rowspan=2>General</td> <td> <details><summary>Avoid global PG path pollution</summary> ___ **Avoid globally setting PostgreSQL library paths in the RBE image. Instead, make <br>them optional and controlled by a build argument to prevent potential conflicts <br>with other build actions.** [docker/Dockerfile.rbe [55-62]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-40936cbae5822a0a5fa8016befa08eb3a7836c93328e8043dcdfb3885a6201b2R55-R62) ```diff ENV CC=/usr/bin/gcc \ CXX=/usr/bin/g++ \ RUSTUP_HOME=/opt/rustup \ CARGO_HOME=/opt/cargo \ - PATH=/opt/cargo/bin:/opt/rh/gcc-toolset-13/root/usr/bin:$PATH \ - LIBRARY_PATH=/usr/pgsql-16/lib:$LIBRARY_PATH \ - LD_LIBRARY_PATH=/usr/pgsql-16/lib:$LD_LIBRARY_PATH \ - PKG_CONFIG_PATH=/usr/pgsql-16/lib/pkgconfig:$PKG_CONFIG_PATH + PATH=/opt/cargo/bin:/opt/rh/gcc-toolset-13/root/usr/bin:$PATH +# Optional: enable CNPG paths only when explicitly requested by builds +ARG ENABLE_PG_PATHS=0 +ENV LIBRARY_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib:$LIBRARY_PATH" || echo "$LIBRARY_PATH") \ + LD_LIBRARY_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib:$LD_LIBRARY_PATH" || echo "$LD_LIBRARY_PATH") \ + PKG_CONFIG_PATH=$([ "$ENABLE_PG_PATHS" = "1" ] && echo "/usr/pgsql-16/lib/pkgconfig:$PKG_CONFIG_PATH" || echo "$PKG_CONFIG_PATH") ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out a potential issue with globally setting linker paths in a shared build image, which could cause conflicts. Making these paths optional improves the image's reusability and robustness. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>✅ <s>Enforce positive limit during parse</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit added a check for parsed <= 0 and returns an InvalidRequest error with the message "limit must be a positive integer", matching the suggestion. code diff: ```diff + if parsed <= 0 { + return Err(ServiceError::InvalidRequest( + "limit must be a positive integer".into(), + )); + } limit = Some(parsed); ``` </details> ___ **Validate that the <code>limit</code> parameter is a positive integer during parsing to <br>provide earlier and clearer error feedback to the user.** [rust/srql/src/parser.rs [95-101]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-b2edf55d1721185349ecddb2f4eacc42e0dfcae19b6c2bc638602f187da67e66R95-R101) ```diff -for token in tokenize(input) { - let (raw_key, raw_value) = split_token(&token)?; - let key = raw_key.trim().to_lowercase(); - let value = parse_value(raw_value); - - match key.as_str() { - "in" => { - entity = Some(parse_entity(value.as_scalar()?)?); - } - "limit" => { - let parsed = value - .as_scalar()? - .parse::<i64>() - .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?; - limit = Some(parsed); - } - ... +"limit" => { + let parsed = value + .as_scalar()? + .parse::<i64>() + .map_err(|_| ServiceError::InvalidRequest("invalid limit".into()))?; + if parsed <= 0 { + return Err(ServiceError::InvalidRequest( + "limit must be a positive integer".into(), + )); } + limit = Some(parsed); } ``` `[To ensure code accuracy, apply this suggestion manually]` <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion correctly points out that negative limits are not validated during parsing, but the impact is low because the value is clamped to a valid range later in the `determine_limit` function. </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> ___ #### Previous suggestions <details><summary>✅ Suggestions up to commit 49b3464</summary> <br><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=1>High-level</td> <td> <details><summary>Refactor query builders to reduce duplication</summary> ___ **The query building logic in <code>rust/srql/src/query/</code> for <code>devices</code>, <code>events</code>, and <code>logs</code> <br>has highly duplicated <code>apply_filter</code> functions. This can be refactored using a <br>generic abstraction like a macro to reduce boilerplate and improve <br>maintainability.** ### Examples: <details> <summary> <a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-ad603163a35223c63c68279e53a1a4c9219b9096042a168d8a87443abaa29a58R76-R314">rust/srql/src/query/events.rs [76-314]</a> </summary></details> <details> <summary> <a href="https://github.com/carverauto/serviceradar/pull/1948/files#diff-f4d3b33667f2e79a6ebe4cfff931f93c728d9a81c305ac13586e623850a504dbR71-R271">rust/srql/src/query/logs.rs [71-271]</a> </summary></details> ### Solution Walkthrough: #### Before: ```rust // in rust/srql/src/query/events.rs fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> { match filter.field.as_str() { "id" => { let value = filter.value.as_scalar()?.to_string(); query = match filter.op { FilterOp::Eq => query.filter(col_id.eq(value)), FilterOp::NotEq => query.filter(col_id.ne(value)), FilterOp::Like => query.filter(col_id.ilike(value)), // ... and so on for In, NotIn }; } "type" => { let value = filter.value.as_scalar()?.to_string(); query = match filter.op { FilterOp::Eq => query.filter(col_event_type.eq(value)), FilterOp::NotEq => query.filter(col_event_type.ne(value)), FilterOp::Like => query.filter(col_event_type.ilike(value)), // ... identical logic, different column }; } // ... this pattern is repeated for many other fields } Ok(query) } ``` #### After: ```rust // Define a macro to handle the repetitive filter logic macro_rules! apply_string_filter { ($query:expr, $filter:expr, $column:expr) => { // ... logic for Eq, NotEq, Like, NotLike, In, NotIn using the column }; } // in rust/srql/src/query/events.rs fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> { match filter.field.as_str() { "id" => { query = apply_string_filter!(query, filter, col_id)?; } "type" => { query = apply_string_filter!(query, filter, col_event_type)?; } "source" => { query = apply_string_filter!(query, filter, col_source)?; } // ... other fields now use the macro, greatly reducing code size } Ok(query) } ``` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies significant code duplication in the `apply_filter` functions across three core files, and addressing this would substantially improve the new service's maintainability and extensibility. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=2>Possible issue</td> <td> <details><summary>✅ <s>Handle negated array containment filters</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit updated the discovery_sources filter to apply a NOT around the array containment expression when the operator is NotIn, implementing the suggested behavior. code diff: ```diff let values = match &filter.value { @@ -219,7 +166,11 @@ } let expr = sql::<Bool>("coalesce(discovery_sources, ARRAY[]::text[]) @> ") .bind::<Array<Text>, _>(values); - query = query.filter(expr); + query = if matches!(filter.op, FilterOp::NotIn) { + query.filter(not(expr)) + } else { + query.filter(expr) + }; ``` </details> ___ **Implement handling for negated filters (<code>FilterOp::NotIn</code>) on the <br><code>discovery_sources</code> field by wrapping the SQL expression with a <code>NOT</code> operator.** [rust/srql/src/query/devices.rs [212-223]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-3202f22fff6863ed7848a129c49e2323322462b379d896d3fca2e59aa6f7b4c5R212-R223) ```diff "discovery_sources" => { let values = match &filter.value { FilterValue::Scalar(v) => vec![v.to_string()], FilterValue::List(list) => list.clone(), }; if values.is_empty() { return Ok(query); } let expr = sql::<Bool>("coalesce(discovery_sources, ARRAY[]::text[]) @> ") .bind::<Array<Text>, _>(values); - query = query.filter(expr); + + query = if matches!(filter.op, FilterOp::NotIn) { + query.filter(diesel::dsl::not(expr)) + } else { + query.filter(expr) + }; } ``` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: The suggestion correctly identifies a bug where the negation operator for the `discovery_sources` filter is parsed but not implemented, leading to incorrect query results for negated filters. </details></details></td><td align=center>Medium </td></tr><tr><td> <details><summary>✅ <s>Error on unsupported filter fields</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit changed the default match arm to return ServiceError::InvalidRequest for unsupported filter fields, matching the suggestion. code diff: ```diff - _ => {} + other => { + return Err(ServiceError::InvalidRequest(format!( + "unsupported filter field for events: '{other}'" + ))); + } ``` </details> ___ **Modify the <code>apply_filter</code> function in <code>events.rs</code> to return an error for unsupported <br>filter fields instead of silently ignoring them.** [rust/srql/src/query/events.rs [76-314]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-ad603163a35223c63c68279e53a1a4c9219b9096042a168d8a87443abaa29a58R76-R314) ```diff fn apply_filter<'a>(mut query: EventsQuery<'a>, filter: &Filter) -> Result<EventsQuery<'a>> { match filter.field.as_str() { "id" => { ... } "level" => { let value = parse_i32(filter.value.as_scalar()?)?; query = match filter.op { FilterOp::Eq => query.filter(col_level.eq(value)), FilterOp::NotEq => query.filter(col_level.ne(value)), _ => { return Err(ServiceError::InvalidRequest( "level only supports equality comparisons".into(), )) } }; } - _ => {} + other => { + return Err(ServiceError::InvalidRequest(format!( + "unsupported filter field for events: '{other}'" + ))); + } } Ok(query) } ``` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies that silently ignoring unknown filters is poor API design and can hide user errors, so returning an error improves robustness and provides better feedback. </details></details></td><td align=center>Low </td></tr><tr><td rowspan=2>General</td> <td> <details><summary>✅ <s>Improve graph ingestion performance</s></summary> ___ <details><summary><b>Suggestion Impact:</b></summary>The commit replaced the FOREACH loop with WITH + UNWIND over services and preserved the MERGE operations, implementing the suggested performance improvement. code diff: ```diff - FOREACH (svc IN snap.services | - MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name}) - MERGE (d)-[:RUNS_ON]->(s) - ) + WITH d, snap + UNWIND snap.services AS svc + MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name}) + MERGE (d)-[:RUNS_ON]->(s) ``` </details> ___ **Replace the <code>FOREACH</code> clause with a more performant <code>UNWIND</code> clause in the Cypher <br>query to optimize bulk graph ingestion.** [sr-architecture-and-design/prd/03-proton.md [143-152]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-43f4559ca42e6d93d5fd23e466b2cf3400a72816af1f9a27673c28de8262b8b5R143-R152) ```diff SELECT * FROM cypher('topology_graph', $$ UNWIND $snapshots AS snap MERGE (d:Device {tenant_id: snap.tenant_id, device_id: snap.device_id}) SET d.hostname = snap.hostname, d.os = snap.os - FOREACH (svc IN snap.services | - MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name}) - MERGE (d)-[:RUNS_ON]->(s) - ) + WITH d, snap + UNWIND snap.services AS svc + MERGE (s:Service {tenant_id: snap.tenant_id, name: svc.name}) + MERGE (d)-[:RUNS_ON]->(s) $$) AS (result agtype); ``` <details><summary>Suggestion importance[1-10]: 6</summary> __ Why: The suggestion correctly identifies a performance anti-pattern in the Cypher query example and proposes using `UNWIND` instead of `FOREACH` for better bulk ingestion performance, which is a valid best practice. </details></details></td><td align=center>Low </td></tr><tr><td> <details><summary>Reduce health check interval</summary> ___ **Reduce the health check interval from 30 seconds to 10 seconds for faster <br>failure detection and recovery in the local development environment.** [docker-compose.yml [363-367]](https://github.com/carverauto/serviceradar/pull/1948/files#diff-e45e45baeda1c1e73482975a664062aa56f20c03dd9d64a827aba57775bed0d3R363-R367) ```diff healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:8080/healthz"] - interval: 30s + interval: 10s timeout: 5s retries: 5 ``` <!-- /improve --apply_suggestion=4 --> <details><summary>Suggestion importance[1-10]: 4</summary> __ Why: The suggestion correctly points out that a 30-second health check interval is long for a local development environment, and reducing it would allow for faster failure detection. </details></details></td><td align=center>Low </td></tr> <tr><td align="center" colspan="2"> <!-- /improve_multi --more_suggestions=true --> </td><td></td></tr></tbody></table> </details>
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!2416
No description provided.