4 Commits

Author SHA1 Message Date
Sharang Parnerkar
ea2a9e8a1d Fix clippy warnings and fmt issues to pass CI
All checks were successful
CI / Format (push) Successful in 23s
CI / Clippy (push) Successful in 2m50s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Format (pull_request) Successful in 2s
CI / Clippy (pull_request) Successful in 2m48s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
Replace expect/unwrap calls with safe alternatives, add Default impls
for parser structs and Toasts, fix redundant closures, collapse nested
ifs, remove unused import, and allow recursive-only-self/too-many-args
lints in compliance-graph.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 22:43:44 +01:00
Sharang Parnerkar
a22cf1595f Add SBOM enhancements, delete repo feature, and embedding build spinner
Some checks failed
CI / Format (push) Failing after 3s
CI / Clippy (push) Failing after 1m19s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Format (pull_request) Failing after 2s
CI / Clippy (pull_request) Failing after 1m18s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
- Fix SBOM display bug by removing incorrect BSON serde helpers on DateTime fields
- Add filtered/searchable SBOM list with repo, package manager, search, vuln, and license filters
- Add SBOM export (CycloneDX 1.5 / SPDX 2.3), license compliance tab, and cross-repo diff
- Add vulnerability drill-down with inline CVE details and advisory links
- Add DELETE /api/v1/repositories/{id} with cascade delete of all related data
- Add delete repository button with confirmation modal warning in dashboard
- Add spinner and progress bar for embedding builds with auto-polling status
- Install syft in agent Dockerfile for SBOM generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 00:17:14 +01:00
Sharang Parnerkar
c9dc96ad73 Run cargo fmt across all crates
Some checks failed
CI / Format (push) Successful in 2s
CI / Clippy (push) Failing after 1m23s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Clippy (pull_request) Failing after 1m18s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Format (pull_request) Successful in 3s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 23:30:26 +01:00
Sharang Parnerkar
89c30a62dd Add RAG embedding and AI chat feature
Some checks failed
CI / Security Audit (push) Has been cancelled
CI / Tests (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Format (push) Failing after 3s
Implement end-to-end RAG pipeline: AST-aware code chunking, LiteLLM
embedding generation, MongoDB vector storage with brute-force cosine
similarity fallback for self-hosted instances, and a chat API with
RAG-augmented responses. Add dedicated /chat/:repo_id dashboard page
with embedding build controls, message history, and source reference
cards.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 23:29:40 +01:00
11 changed files with 17 additions and 453 deletions

View File

@@ -37,14 +37,3 @@ GIT_CLONE_BASE_PATH=/tmp/compliance-scanner/repos
# Dashboard
DASHBOARD_PORT=8080
AGENT_API_URL=http://localhost:3001
# Keycloak (required for authentication)
KEYCLOAK_URL=http://localhost:8080
KEYCLOAK_REALM=compliance
KEYCLOAK_CLIENT_ID=compliance-dashboard
REDIRECT_URI=http://localhost:8080/auth/callback
APP_URL=http://localhost:8080
# OpenTelemetry (optional - omit to disable)
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# OTEL_SERVICE_NAME=compliance-agent

205
Cargo.lock generated
View File

@@ -167,7 +167,7 @@ dependencies = [
"sync_wrapper",
"tokio",
"tokio-tungstenite 0.28.0",
"tower 0.5.3",
"tower",
"tower-layer",
"tower-service",
"tracing",
@@ -582,18 +582,11 @@ dependencies = [
"chrono",
"hex",
"mongodb",
"opentelemetry",
"opentelemetry-appender-tracing",
"opentelemetry-otlp",
"opentelemetry_sdk",
"secrecy",
"serde",
"serde_json",
"sha2",
"thiserror 2.0.18",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
"uuid",
]
@@ -1323,7 +1316,7 @@ dependencies = [
"tokio-stream",
"tokio-tungstenite 0.27.0",
"tokio-util",
"tower 0.5.3",
"tower",
"tower-http",
"tower-layer",
"tracing",
@@ -1614,7 +1607,7 @@ dependencies = [
"tokio",
"tokio-tungstenite 0.27.0",
"tokio-util",
"tower 0.5.3",
"tower",
"tower-http",
"tracing",
"tracing-futures",
@@ -2111,12 +2104,6 @@ dependencies = [
"url",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "gloo-net"
version = "0.6.0"
@@ -3485,7 +3472,7 @@ dependencies = [
"serde_urlencoded",
"snafu",
"tokio",
"tower 0.5.3",
"tower",
"tower-http",
"tracing",
"url",
@@ -3532,98 +3519,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "opentelemetry"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 2.0.18",
"tracing",
]
[[package]]
name = "opentelemetry-appender-tracing"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e716f864eb23007bdd9dc4aec381e188a1cee28eecf22066772b5fd822b9727d"
dependencies = [
"opentelemetry",
"tracing",
"tracing-core",
"tracing-subscriber",
]
[[package]]
name = "opentelemetry-http"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed"
dependencies = [
"async-trait",
"bytes",
"http",
"opentelemetry",
"reqwest",
"tracing",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656"
dependencies = [
"futures-core",
"http",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost",
"reqwest",
"thiserror 2.0.18",
"tokio",
"tonic",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
"prost",
"tonic",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"glob",
"opentelemetry",
"percent-encoding",
"rand 0.9.2",
"serde_json",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tracing",
]
[[package]]
name = "ownedbytes"
version = "0.7.0"
@@ -3857,29 +3752,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "prost"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "psl-types"
version = "2.0.11"
@@ -4135,7 +4007,6 @@ dependencies = [
"bytes",
"cookie",
"cookie_store",
"futures-channel",
"futures-core",
"futures-util",
"http",
@@ -4159,7 +4030,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tokio-util",
"tower 0.5.3",
"tower",
"tower-http",
"tower-service",
"url",
@@ -5340,52 +5211,6 @@ dependencies = [
"winnow",
]
[[package]]
name = "tonic"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
dependencies = [
"async-trait",
"base64",
"bytes",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost",
"tokio",
"tokio-stream",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand 0.8.5",
"slab",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.5.3"
@@ -5425,7 +5250,7 @@ dependencies = [
"pin-project-lite",
"tokio",
"tokio-util",
"tower 0.5.3",
"tower",
"tower-layer",
"tower-service",
"tracing",
@@ -5497,24 +5322,6 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-opentelemetry"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444"
dependencies = [
"js-sys",
"once_cell",
"opentelemetry",
"opentelemetry_sdk",
"smallvec",
"tracing",
"tracing-core",
"tracing-log",
"tracing-subscriber",
"web-time",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.22"

View File

@@ -2,9 +2,10 @@
#[allow(clippy::expect_used)]
fn main() {
dioxus_logger::init(tracing::Level::DEBUG).expect("Failed to init logger");
#[cfg(feature = "web")]
{
dioxus_logger::init(tracing::Level::DEBUG).expect("Failed to init logger");
dioxus::web::launch::launch_cfg(
compliance_dashboard::App,
dioxus::web::Config::new().hydrate(true),
@@ -13,9 +14,6 @@ fn main() {
#[cfg(feature = "server")]
{
dotenvy::dotenv().ok();
let _telemetry_guard = compliance_core::telemetry::init_telemetry("compliance-dashboard");
compliance_dashboard::infrastructure::server_start(compliance_dashboard::App)
.map_err(|e| {
tracing::error!("Unable to start server: {e}");

View File

@@ -7,7 +7,7 @@ edition = "2021"
workspace = true
[dependencies]
compliance-core = { workspace = true, features = ["mongodb", "telemetry"] }
compliance-core = { workspace = true, features = ["mongodb"] }
compliance-graph = { path = "../compliance-graph" }
compliance-dast = { path = "../compliance-dast" }
serde = { workspace = true }

View File

@@ -1,3 +1,5 @@
use tracing_subscriber::EnvFilter;
mod agent;
mod api;
mod config;
@@ -13,9 +15,13 @@ mod webhooks;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.init();
let _telemetry_guard = compliance_core::telemetry::init_telemetry("compliance-agent");
dotenvy::dotenv().ok();
tracing::info!("Loading configuration...");
let config = config::load_config()?;

View File

@@ -9,15 +9,6 @@ workspace = true
[features]
default = ["mongodb"]
mongodb = ["dep:mongodb"]
telemetry = [
"dep:opentelemetry",
"dep:opentelemetry_sdk",
"dep:opentelemetry-otlp",
"dep:opentelemetry-appender-tracing",
"dep:tracing-opentelemetry",
"dep:tracing-subscriber",
"dep:tracing",
]
[dependencies]
serde = { workspace = true }
@@ -30,10 +21,3 @@ uuid = { workspace = true }
secrecy = { workspace = true }
bson = { version = "2", features = ["chrono-0_4"] }
mongodb = { workspace = true, optional = true }
opentelemetry = { version = "0.29", optional = true }
opentelemetry_sdk = { version = "0.29", features = ["rt-tokio"], optional = true }
opentelemetry-otlp = { version = "0.29", features = ["grpc-tonic"], optional = true }
opentelemetry-appender-tracing = { version = "0.29", optional = true }
tracing-opentelemetry = { version = "0.30", optional = true }
tracing-subscriber = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }

View File

@@ -1,8 +1,6 @@
pub mod config;
pub mod error;
pub mod models;
#[cfg(feature = "telemetry")]
pub mod telemetry;
pub mod traits;
pub use config::{AgentConfig, DashboardConfig};

View File

@@ -1,150 +0,0 @@
//! OpenTelemetry initialization for traces and logs.
//!
//! Exports traces and logs via OTLP (gRPC) when `OTEL_EXPORTER_OTLP_ENDPOINT`
//! is set. Always includes a `tracing_subscriber::fmt` layer for console output.
//!
//! Compatible with SigNoz, Grafana Tempo/Loki, Jaeger, and any OTLP-compatible
//! collector.
//!
//! # Environment Variables
//!
//! | Variable | Description | Default |
//! |---|---|---|
//! | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint (e.g. `http://localhost:4317`) | *(disabled)* |
//! | `OTEL_SERVICE_NAME` | Service name for resource | `service_name` param |
//! | `RUST_LOG` / standard `EnvFilter` | Log level filter | `info` |
use opentelemetry::global;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry::KeyValue;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_otlp::{LogExporter, SpanExporter, WithExportConfig};
use opentelemetry_sdk::{logs::SdkLoggerProvider, trace::SdkTracerProvider, Resource};
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer as _};
/// Guard that shuts down OTel providers on drop.
///
/// Must be held for the lifetime of the application. When dropped,
/// flushes and shuts down the tracer and logger providers.
pub struct TelemetryGuard {
tracer_provider: Option<SdkTracerProvider>,
logger_provider: Option<SdkLoggerProvider>,
}
impl Drop for TelemetryGuard {
fn drop(&mut self) {
if let Some(tp) = self.tracer_provider.take() {
if let Err(e) = tp.shutdown() {
eprintln!("Failed to shutdown tracer provider: {e}");
}
}
if let Some(lp) = self.logger_provider.take() {
if let Err(e) = lp.shutdown() {
eprintln!("Failed to shutdown logger provider: {e}");
}
}
}
}
fn build_resource(service_name: &str) -> Resource {
let name = std::env::var("OTEL_SERVICE_NAME").unwrap_or_else(|_| service_name.to_string());
Resource::builder()
.with_service_name(name)
.with_attributes([KeyValue::new("service.version", env!("CARGO_PKG_VERSION"))])
.build()
}
/// Initialize telemetry (tracing + logging).
///
/// If `OTEL_EXPORTER_OTLP_ENDPOINT` is set, traces and logs are exported
/// via OTLP/gRPC. Console fmt output is always enabled.
///
/// Returns a [`TelemetryGuard`] that must be held alive for the application
/// lifetime. Dropping it triggers a graceful shutdown of OTel providers.
///
/// # Panics
///
/// Panics if the tracing subscriber cannot be initialized (e.g. called twice).
pub fn init_telemetry(service_name: &str) -> TelemetryGuard {
let otel_endpoint = std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok();
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
let fmt_layer = tracing_subscriber::fmt::layer();
match otel_endpoint {
Some(ref endpoint) => {
let resource = build_resource(service_name);
// Traces
#[allow(clippy::expect_used)]
let span_exporter = SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint)
.build()
.expect("failed to create OTLP span exporter");
let tracer_provider = SdkTracerProvider::builder()
.with_batch_exporter(span_exporter)
.with_resource(resource.clone())
.build();
global::set_tracer_provider(tracer_provider.clone());
let tracer = tracer_provider.tracer(service_name.to_string());
let otel_trace_layer = OpenTelemetryLayer::new(tracer);
// Logs
#[allow(clippy::expect_used)]
let log_exporter = LogExporter::builder()
.with_tonic()
.with_endpoint(endpoint)
.build()
.expect("failed to create OTLP log exporter");
let logger_provider = SdkLoggerProvider::builder()
.with_batch_exporter(log_exporter)
.with_resource(resource)
.build();
let otel_log_layer = OpenTelemetryTracingBridge::new(&logger_provider);
// Filter to prevent telemetry-induced-telemetry loops
let otel_filter = EnvFilter::new("info")
.add_directive("hyper=off".parse().unwrap_or_default())
.add_directive("tonic=off".parse().unwrap_or_default())
.add_directive("h2=off".parse().unwrap_or_default())
.add_directive("reqwest=off".parse().unwrap_or_default());
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.with(otel_trace_layer)
.with(otel_log_layer.with_filter(otel_filter))
.init();
tracing::info!(
endpoint = endpoint.as_str(),
service = service_name,
"OpenTelemetry OTLP export enabled"
);
TelemetryGuard {
tracer_provider: Some(tracer_provider),
logger_provider: Some(logger_provider),
}
}
None => {
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.init();
tracing::info!("OpenTelemetry disabled (set OTEL_EXPORTER_OTLP_ENDPOINT to enable)");
TelemetryGuard {
tracer_provider: None,
logger_provider: None,
}
}
}
}

View File

@@ -18,7 +18,6 @@ server = [
"dioxus/router",
"dioxus/fullstack",
"compliance-core/mongodb",
"compliance-core/telemetry",
"dep:axum",
"dep:mongodb",
"dep:reqwest",

View File

@@ -17,9 +17,6 @@ services:
- "3001:3001"
- "3002:3002"
env_file: .env
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
OTEL_SERVICE_NAME: compliance-agent
depends_on:
- mongo
volumes:
@@ -32,9 +29,6 @@ services:
ports:
- "8080:8080"
env_file: .env
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
OTEL_SERVICE_NAME: compliance-dashboard
depends_on:
- mongo
- agent
@@ -49,15 +43,6 @@ services:
PREBOOT_CHROME: "true"
restart: unless-stopped
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
ports:
- "4317:4317"
- "4318:4318"
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
restart: unless-stopped
volumes:
mongo_data:
repos_data:

View File

@@ -1,52 +0,0 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
exporters:
# Log to stdout for debugging
debug:
verbosity: basic
# Configure your backend below. Examples:
#
# SigNoz:
# otlp/signoz:
# endpoint: "signoz-otel-collector:4317"
# tls:
# insecure: true
#
# Grafana Tempo (traces):
# otlp/tempo:
# endpoint: "tempo:4317"
# tls:
# insecure: true
#
# Grafana Loki (logs):
# loki:
# endpoint: "http://loki:3100/loki/api/v1/push"
#
# Jaeger:
# otlp/jaeger:
# endpoint: "jaeger:4317"
# tls:
# insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]