Modelo ONNX Local
ThreatModel clássico (features → sklearn → ONNX) via ort, com retreinamento hot-swap
Modelo ONNX Local
O modelo local do Sentry é um classificador ML clássico (regressão logística)
que roda como fork assíncrono ao lado do hot path. Inferência é
local — sem rede, determinística, tipicamente sub-ms — via ort (ONNX
Runtime), atrás da feature Cargo onnx (opcional, desligada por default).
Trait ThreatModel
/// A local, deterministic, no-network threat model.
#[async_trait]
pub trait ThreatModel: Send + Sync {
fn name(&self) -> &'static str;
fn version(&self) -> &str;
async fn analyze(&self, evt: &Event) -> anyhow::Result<Vec<Signal>>;
}Definida em
sentry-ai/src/threat.rs
— o pipeline depende de um Box<dyn ThreatModel> sem puxar ort para todo
build. A implementação ONNX é
OnnxThreatModel
(feature onnx), carregada pelo daemon em
build_ai_fork
quando [ai] enabled = true. Sem a feature, o estágio fica desligado com um
warning — o resto do Sentry continua funcionando.
load
valida a largura da entrada do modelo contra FEATURE_NAMES: um modelo
treinado com outra versão das features falha no startup com instrução de
retreinar, nunca roda dessincronizado. Probabilidade de anomalia acima de
threshold emite um sinal AnomalousPayload com peso signal_weight.
Features — a fonte única de verdade
sentry-ai/src/features.rs
é o single source of truth do input do modelo: a mesma função extract
roda na inferência (fork do daemon) e no export de treino (sentry model export escreve CSV com header = FEATURE_NAMES). O script de treino valida
o header do CSV e falha alto se as pontas divergirem — treino e inferência
não podem driftar.
São 25 features, todas normalizadas para 0.0–1.0 (vetor de tamanho fixo;
eventos não-HTTP viram vetor de zeros). Resumo por grupo:
| Grupo | Features |
|---|---|
| Estrutura do path | path_len, query_len, path_depth, path_digit_ratio, path_special_ratio, path_entropy, path_encoded_count, path_dot_dot, path_null_byte |
| Tokens de ataque (sobre texto URL-decodificado + lowercase) | sqli_token_score, xss_token_score, traversal_token_score, jndi_token, cmd_token_score |
| Extensão do arquivo | has_file_ext, ext_is_script, ext_is_sensitive |
| Método HTTP | method_is_get, method_is_post |
| User-Agent | ua_len, ua_is_empty, ua_bot_token |
| Status da resposta | status_4xx, status_5xx |
| Query string | query_param_count |
Loop de retreinamento
A extração de features acontece exclusivamente em Rust — o script Python só consome os vetores exportados. O ciclo completo:
- Export —
sentry model export(handler, flags) grava um CSV (FEATURE_NAMES,label) com as features extraídas pelo Rust sobre eventos recentes do Postgres:--hours N— janela de busca (default 720 = 30 dias);--out <arquivo>— destino (defaultdataset.csv);--synthetic— dataset seed sintético sem Postgres (--rows, default 4000, metade benigna);--confirmed— labels de incidentes confirmados (evento ligado a incidente = 1, resto = 0). Sem a flag, positivos são os eventos com verdictblock/challenge/rate_limit.
- Treino —
tools/train_model.py(tools/train_model.py):StandardScaler + LogisticRegression(sklearn,class_weight="balanced") avaliado em holdout, exportado para ONNX comzipmap: false(probabilidades como tensor float plano). - Substituição — troque o arquivo apontado por
model_path(o seed commitado émodels/anomaly_v1.onnx). - Hot swap —
sentry model reload(handler) enviaNOTIFY sentry_model_changedvia Postgres; o daemon recarrega o arquivo do disco e troca o modelo a quente (listener), com o modelo vivo emArc<RwLock<Arc<dyn ThreatModel>>>. Falha de carga mantém o modelo anterior. - Status —
sentry model status(handler) mostra enabled, path, mode/trigger/threshold, se o arquivo carrega (com--features onnx) e o provider LLM ativo.
sentry model export --synthetic
python tools/train_model.py --csv dataset.csv --out models/anomaly_v1.onnx
sentry model reload # exige Postgres: o daemon escuta NOTIFY sentry_model_changed
sentry model statusO build precisa da feature: cargo build --release --features sentry-cli/onnx.
Configuração [ai]
[ai]
enabled = false
model_path = "models/anomaly_v1.onnx"
threshold = 0.7 # prob. de anomalia que emite o sinal
signal_weight = 25 # peso do AnomalousPayload (override: [scorer.weights])
mode = "fork" # fork | inline | shadow
trigger = "above_score" # above_score | always | quarantine_only
min_score = 20 # gate de score do hot path (above_score)
concurrency = 4 # inferências paralelas (fork mode)
cache_ttl_secs = 300 # cache de resultados por hash do payloadChaves e defaults em AiConfig.
O que mode/trigger/cache fazem em runtime está descrito em
Camada de IA.