diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ca1c54..5dd9964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to a3s-observer will be documented in this file. +## [0.9.3] — soak validation + test coverage (no runtime change) + +### Tested + +- Full-stack soak validation — 15 cases, leak-free and correct under load on a prod host + an + isolated VM. Observe: steady 20 min, edge-input, a real a3s-code agent, throughput (110k ev/60s), + memory-bound (256 Mi), restart ×8, idle + heartbeat, SIGTERM, concurrent collectors, + backpressure, connection-churn. Intervene: egress, file/exec, and SSL-content guards — plus all + three running alongside the collector. +- Lib line coverage 72% → **79.6%** (`cargo llvm-cov`): adversarial SNI/DNS parser tests, the + cgroup→pod parser, the full 14-provider SNI classifier, and the v0.9.2 writer-thread path. + ## [0.9.2] — fix: output backpressure no longer stalls the event loop ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 7d346fa..ba44fbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "a3s-observer" -version = "0.9.2" +version = "0.9.3" dependencies = [ "serde", "serde_json", @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "a3s-observer-collector" -version = "0.9.2" +version = "0.9.3" dependencies = [ "a3s-observer", "a3s-observer-common", @@ -28,11 +28,11 @@ dependencies = [ [[package]] name = "a3s-observer-common" -version = "0.9.2" +version = "0.9.3" [[package]] name = "a3s-observer-ebpf" -version = "0.9.2" +version = "0.9.3" dependencies = [ "a3s-observer-common", "aya-ebpf", diff --git a/Cargo.toml b/Cargo.toml index 7de287d..6bf77a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a3s-observer" -version = "0.9.2" +version = "0.9.3" edition = "2021" license = "MIT" description = "General-purpose, language-agnostic eBPF observability for AI agents (LLM calls, tools, files, network egress)." diff --git a/README.md b/README.md index 05db340..fef6411 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,19 @@ a3s-observer → NDJSON → OTel Collector (filelog → OTLP) → your bac Rust + [Aya](https://aya-rs.dev). Validated on Linux 6.8. +## Tested + +Soak-tested under sustained load — observe on a production host, intervene in an isolated VM: + +| path | cases (all leak-free + correct under load) | +|---|---| +| **observe** | steady 20 min · edge-input · **a real a3s-code agent** · throughput 110k ev/60s · memory-bound (256 Mi) · restart ×8 · idle + heartbeat · SIGTERM · concurrent collectors · backpressure · connection-churn | +| **intervene** | egress · file/exec · SSL-content guards — and all three running alongside the collector | + +Two robustness bugs surfaced and were fixed this way: NDJSON stdout pollution (v0.9.1) and an +output-backpressure event-loop stall (v0.9.2). Lib line coverage **79.6%** (`cargo llvm-cov`) — +the untrusted SNI / DNS / cgroup parsers and the full 14-provider classifier are unit-tested. + ## Security Privileged component — see [SECURITY.md](SECURITY.md) for the disclosure policy and how to diff --git a/a3s-observer-collector/Cargo.toml b/a3s-observer-collector/Cargo.toml index 8fbb10b..fcf78d6 100644 --- a/a3s-observer-collector/Cargo.toml +++ b/a3s-observer-collector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a3s-observer-collector" -version = "0.9.2" +version = "0.9.3" edition = "2021" license = "MIT" description = "a3s-observer collector: loads the eBPF probes and exports enriched events." diff --git a/a3s-observer-common/Cargo.toml b/a3s-observer-common/Cargo.toml index 0ac04a2..68474f8 100644 --- a/a3s-observer-common/Cargo.toml +++ b/a3s-observer-common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a3s-observer-common" -version = "0.9.2" +version = "0.9.3" edition = "2021" license = "MIT" description = "Shared no_std types crossing the eBPF <-> userspace boundary for a3s-observer." diff --git a/a3s-observer-ebpf/Cargo.toml b/a3s-observer-ebpf/Cargo.toml index c08c441..40113e8 100644 --- a/a3s-observer-ebpf/Cargo.toml +++ b/a3s-observer-ebpf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a3s-observer-ebpf" -version = "0.9.2" +version = "0.9.3" edition = "2021" license = "MIT" publish = false diff --git a/src/traits.rs b/src/traits.rs index 5aeb15b..8ce1b37 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -276,6 +276,33 @@ mod tests { c.classify(Some("bedrock-runtime.us-east-1.amazonaws.com"), ip), Some(Provider::Bedrock) ); + assert_eq!( + c.classify(Some("generativelanguage.googleapis.com"), ip), + Some(Provider::Gemini) + ); + assert_eq!( + c.classify(Some("api.cohere.ai"), ip), + Some(Provider::Cohere) + ); + assert_eq!(c.classify(Some("api.x.ai"), ip), Some(Provider::XAi)); + assert_eq!(c.classify(Some("api.groq.com"), ip), Some(Provider::Groq)); + assert_eq!( + c.classify(Some("api.together.xyz"), ip), + Some(Provider::Together) + ); + assert_eq!( + c.classify(Some("api.perplexity.ai"), ip), + Some(Provider::Perplexity) + ); + assert_eq!( + c.classify(Some("api.fireworks.ai"), ip), + Some(Provider::Fireworks) + ); + assert_eq!( + c.classify(Some("openrouter.ai"), ip), + Some(Provider::OpenRouter) + ); + assert_eq!(c.classify(None, ip), None); // no SNI → unclassified assert_eq!(c.classify(Some("example.com"), ip), None); }