Sentinel
Content safety monitoring for LLM applications
Sentinel sits behind your chat app, classifies every prompt and completion for harm, detects when the model's input distribution drifts from what it was trained on, and retrains + redeploys a new model version before that drift becomes an incident.
Data flow
A chat app is treated as an external black box — Sentinel never touches its prompts directly. It only sees what arrives as OpenTelemetry telemetry.
Emits OTel spans over OTLP/gRPC on :4317. Prompt/response text travels in span events, not attributes, per the GenAI semantic conventions.
Fans every trace out to Kafka and to Jaeger for live inspection.
3 partitions. A Python consumer extracts prompt/response text from each span.
POST /v1/moderations — ONNX INT8 RoBERTa, OpenAI-moderation-compatible.
Every score to classifications; harmful content (+10% of safe) to flagged_content.
Hourly PSI/JSD check against a reference window; a breach fine-tunes, quality-gates, and rolling-restarts.
See it running
Simulated chat traffic flowing through the full local stack — collector, Kafka, classifier, Postgres/Mongo, Jaeger.
Load-tested, not assumed
One config change — ORT_INTRA_THREADS 4 → 1 — measured before/after on the same 1-vCPU-limited classifier pod.
Metrics
Real numbers from the model optimization pipeline, a load/stress test against a single 1-vCPU pod, the live Prometheus surface, and the gates that decide whether a retrained model ships.
| Stage | Size | p50 latency |
|---|---|---|
| PyTorch FP32 | ~500 MB | ~110 ms |
| ONNX + O2 graph opt | ~480 MB | ~60 ms |
| ONNX + INT8 quant | ~120 MB | ~35 ms |
| Concurrency | Throughput | p50 | p99 |
|---|---|---|---|
| 1 | 26.1 req/s | 36.9 ms | — |
| 5 | 34.5 req/s | 149.0 ms | — |
| 10 | 36.3 req/s | 284.3 ms | 361.5 ms |
| Concurrency | Throughput | p50 |
|---|---|---|
| 1 | 32.6 items/s | 960.6 ms |
| 2 | 28.8 items/s | 2.19 s |
| 5 | 23.4 items/s | 6.71 s |
classifier_requests_total{endpoint,label}Request count by route and predicted labelclassifier_request_latency_seconds{endpoint}Histogram, 5ms–1s bucketsclassifier_batch_sizeHistogram of texts per batch, 1–64classifier_queue_depthGauge — requests waiting in the DynamicBatcher queueclassifier_log_errors_total{level}ERROR+ log records emittedTech stack
Every phase introduces exactly the tool that concern needs — nothing borrowed from a later phase.
Build phases
Built as a learning project — each phase fully working before the next begins. Phases 1–7 are complete; only cloud deployment remains.
Setup — Linux
One script brings up a k3d cluster and every service in it. This is the exact sequence to follow, in order.
Everything below is available through your distro's package manager or its official installer.
uv sync installs the root workspace plus the classifier's deps for local tooling.
# git clone https://github.com/VjayRam/project-sentinel.git
cd project-sentinel
uv sync --all-packages
One script does everything: creates the k3d cluster, builds and imports every image, runs terraform apply, waits for readiness, unpauses both Airflow DAGs, bootstraps a model if the registry is empty, opens every port-forward, then rolling-restarts the classifier so it loads that model.
./scripts/dev-start.sh
Press Ctrl-C to stop cleanly — only the port-forwards stop; the k3d cluster keeps running. Re-run the script any time to reconnect.
# liveness / readiness curl http://localhost:8000/health/ready # send synthetic chat traffic through the real OTLP path python scripts/simulate-traces.py --count 20 --harm-pct 0.3 # confirm it landed psql postgresql://sentinel:sentinel@localhost:5432/sentinel \ -c "SELECT label, count(*) FROM classifications GROUP BY label;"
Every port below is opened automatically by dev-start.sh via kubectl port-forward.
| Service | URL | Credentials |
|---|---|---|
| Classifier API | localhost:8000 | — |
| Classifier docs | localhost:8000/docs | — |
| Label UI | localhost:8001 | — |
| Grafana | localhost:3000 | admin / admin |
| Prometheus | localhost:9090 | — |
| Jaeger UI | localhost:16686 | — |
| Airflow UI | localhost:8090 | admin / sentinel |
| MLflow UI | localhost:5000 | — |
| MinIO console | localhost:9001 | sentinel / sentinel-minio |
Get in touch
Reach out directly — happy to talk through the architecture, the build process, or anything else.