API — sentry-core
Referência da API pública do sentry-core: modelo de domínio, sinais, traits de plugin, rules engine, policy, pipeline e trackers
API — sentry-core
sentry-core é a crate de contratos: modelo de domínio, traits de plugin, rules engine, heurísticas e trackers. É pura — sem I/O, sem HTTP, sem DB — e declara #![forbid(unsafe_code)]. Toda crate de plugin depende apenas dela.
Modelo de domínio — módulo event
Event
Event é o evento normalizado, agnóstico de protocolo, que flui por todo o pipeline. Campos comuns ficam no struct; detalhes de protocolo ficam em protocol: ProtocolData.
pub struct Event {
pub id: Uuid,
pub timestamp: DateTime<Utc>,
pub source: SourceKind,
pub transport: Transport,
pub client_ip: IpAddr,
pub geo: Option<GeoInfo>,
pub asn: Option<u32>,
/* ... bytes, duração, reputação, bot ... */
pub protocol: ProtocolData,
}Acessores que fazem pattern-match no payload — heurísticas e regras usam-nos para optar por lógica de um protocolo sem assumir que todo evento é HTTP:
| Método | Retorna |
|---|---|
http() / http_mut() | Option<&HttpData> |
tcp() | Option<&TcpData> |
tls() | Option<&TlsData> |
syslog() | Option<&SyslogData> |
is_http() / protocol_kind() | bool / ProtocolKind |
Event::new cria com id/timestamp novos; Event::canonical_ip normaliza IPv4-mapeado-em-IPv6 (::ffff:a.b.c.d) para IPv4 puro — o mesmo cliente vira uma única chave em blocos, storage e CLI.
ProtocolData
ProtocolData é o enum que torna o Event extensível. Adicionar um protocolo = uma variante nova + helper em impl Event + atualizar protocol_kind().
| Variante | Payload |
|---|---|
Http(HttpData) | Requisição HTTP/1.x ou HTTP/2 |
Tcp(TcpData) | Stream TCP não-HTTP (captura, edge-tcp) |
Udp(UdpData) | Datagrama UDP (UdpData, decodifica DNS quando reconhece) |
TlsHandshake(TlsData) | Telemetria de ClientHello (SNI/JA3/JA4) |
Syslog(SyslogData) | Mensagem RFC 5424/3164 |
Raw(RawData) | Fallback (RawData) |
HttpData, UploadInfo e enums menores
HttpData carrega method/scheme/host/path/query/status/user_agent/headers/cookies/body/uploads. body só é populado por fontes inline e volta ao cap de captura após o pipeline. UploadInfo é metadado de upload (F10) — conteúdo nunca persiste; UploadKind classifica por magic bytes (Text/Image/Archive/Executable/Pdf/Unknown).
Outros tipos do módulo: TcpData (flags, payload reassemblado, fingerprint SYN, syn_count de rajada), TcpFlags, TcpStage, TlsData (sni/ja3/ja4/cipher/version/alpn), SyslogData, SourceKind, Transport, HttpMethod, GeoInfo e ReputationInfo. RawEvent é o que a trait Source emite, antes do enriquecimento; into_event promove para Event.
Sinais e vereditos — módulo analysis
Signal e SignalKind
Signal é a unidade de detecção — o "porquê" do score:
pub struct Signal {
pub kind: SignalKind,
pub weight: u8,
pub detail: Option<String>,
}SignalKind é o catálogo fechado de detecções. Pesos default (a config [scorer.weights] sobrescreve por chave, exceto posture_advisory, que é travado em 0):
| Sinal | Peso default | Emitido por |
|---|---|---|
Log4Shell | 80 | heurística |
Rce (cmd injection) | 70 | heurística |
SqlInjection | 60 | heurística |
UploadPolyglot | 60 | UPLOAD_POLYGLOT_WEIGHT |
Lfi | 50 | heurística |
KnownBadIp | 50 | KNOWN_BAD_IP_WEIGHT |
UploadExecutable | 50 | UPLOAD_EXECUTABLE_WEIGHT |
Xss | 45 | heurística |
SuspiciousLoginSuccess | 45 | SUSPICIOUS_LOGIN_SUCCESS_WEIGHT |
BadCrawler | 40 | heurística |
PathTraversal | 40 | heurística |
CredentialStuffing | 40 | CREDENTIAL_STUFFING_WEIGHT |
AuthBruteForce | 35 | AUTH_BRUTE_FORCE_WEIGHT |
ScanBehavior | 35 | SCAN_BEHAVIOR_WEIGHT |
SpoofedBot | 35 | SPOOFED_BOT_WEIGHT |
SensitivePath | 30 | heurística |
TcpScanner | 30 | heurística |
UploadTypeMismatch | 30 | UPLOAD_TYPE_MISMATCH_WEIGHT |
UploadDisallowed | 30 | UPLOAD_DISALLOWED_WEIGHT |
DirectoryBruteForce | 30 | DIRECTORY_BRUTE_FORCE_WEIGHT |
RandomScan | 25 | RANDOM_SCAN_WEIGHT |
UploadFlood | 25 | UPLOAD_FLOOD_WEIGHT |
ProtocolViolation | 25 | PROTOCOL_VIOLATION_WEIGHT (schemas sobrescrevem por policy) |
VpnProxy | 20 | VPN_PROXY_WEIGHT |
TlsSniMismatch | 20 | TLS_SNI_MISMATCH_WEIGHT |
UploadOversize | 20 | UPLOAD_OVERSIZE_WEIGHT |
ScanAttackCorrelation | 20 | SCAN_ATTACK_CORRELATION_WEIGHT |
TorExitNode | 15 | TOR_EXIT_NODE_WEIGHT |
MethodNotAllowed | 10 | RouteValidator |
SuspiciousUA (UA vazio) | 10 | heurística |
PromiscuousScanner | 10 | PROMISCUOUS_SCANNER_WEIGHT |
UnknownRoute | 8 | RouteValidator |
AnomalousPayload | 25 | [ai] signal_weight (default) |
LlmMalicious | dinâmico | score × confidence do LLM |
ExternalReputation | escalado | abuseConfidenceScore do provider |
RuleHit | 100/50/30 | short-circuit de regra, por verdict |
PostureAdvisory | 0 (travado) | postura web, nunca reavalia o visitante |
Verdict, RiskLevel, AnalysisResult e Decision
Verdict é a ação recomendada: Allow / RateLimit / Challenge / Block / Quarantine. RiskLevel agrupa o score 0–100 em cinco níveis fixos (from_score: Info 0–9, Low 10–29, Medium 30–49, High 50–74, Critical 75+).
AnalysisResult é a detecção (score + sinais + verdict pré-policy); Decision é a resposta final após policy, com override_reason e log_level opcional. RuleLogLevel controla a verbosidade de console de eventos short-circuitados (silent|info|warn|error).
Traits de plugin
Source
Implementada por toda origem de dados (source.rs):
#[async_trait]
pub trait Source: Send + Sync {
fn name(&self) -> &'static str;
async fn stream(&self) -> Result<mpsc::Receiver<RawEvent>>;
async fn shutdown(&self) -> Result<()> { Ok(()) }
}A origem é aberta dentro de stream, que devolve o receiver de um canal limitado; fechar o sender sinaliza fim de stream. Helpers do módulo: event_channel (sizing padrão) e send_or_log (push com throttle de log por fonte quando o canal enche).
Action, ActionContext e ActionDispatch
Implementada por toda resposta (action.rs):
#[async_trait]
pub trait Action: Send + Sync {
fn name(&self) -> &'static str;
async fn execute(&self, evt: &Event, decision: &Decision) -> Result<()>;
async fn execute_with_context(&self, evt: &Event, decision: &Decision, ctx: &ActionContext) -> Result<()>;
fn applies_to(&self, decision: &Decision) -> bool;
fn dispatch(&self) -> ActionDispatch;
}Implementações devem ser idempotentes (replay pós-restart é no-op, não erro). ActionContext carrega o incident_id para alertas bidirecionais (F4.5). ActionDispatch decide onde a action roda: Inline (contenção local, hot path) ou Deferred (default; I/O de rede vai para workers postergados).
ChallengeProvider
Abstração provider-agnostic de edge (challenge.rs) — espelha o LlmProvider:
#[async_trait]
pub trait ChallengeProvider: Send + Sync {
fn name(&self) -> &'static str;
async fn apply(&self, ip: IpAddr, verdict: Verdict, opts: &EdgeOptions) -> Result<()>;
}ChallengeAction é a única Action que os providers de edge exigem: ela filtra os vereditos Block/Challenge/RateLimit (pula Allow/Quarantine) e delega ao provider. EdgeMode é o sabor (block/js_challenge/managed_challenge/rate_limit) e EdgeOptions carrega TTL + modo preferido. Novo provider de edge (AWS WAF, Fastly…) = implementar a trait + um braço em daemon::build_challenge_action.
Providers de IA (crate sentry-ai)
LlmProvider
Contrato de classificação por LLM (llm.rs):
#[async_trait]
pub trait LlmProvider: Send + Sync {
fn name(&self) -> &'static str;
fn model_id(&self) -> &str;
async fn classify(&self, req: ClassifyRequest) -> anyhow::Result<ClassifyResponse>;
async fn explain(&self, req: ExplainRequest) -> anyhow::Result<String>;
}Adapters embarcados: OpenRouterProvider (default, SENTRY_LLM_KEY), OllamaProvider (local, sem chave) e MockLlmProvider. ClassifyResponse traz verdict, score, confidence e uso de tokens.
IpLookupProvider
Lookup externo de reputação por IP (F7.5, lookup.rs):
#[async_trait]
pub trait IpLookupProvider: Send + Sync {
fn name(&self) -> &'static str;
async fn check(&self, ip: IpAddr) -> Result<IpLookupResult, String>;
}AbuseIpDbLookup implementa GET /api/v2/check; o score retornado escala o peso do sinal ExternalReputation. O fork assíncrono do daemon só consulta a "banda cinza" (score acima do trigger, verdict ≠ Block), com cache e quota.
ThreatModel
O modelo local clássico fica atrás de ThreatModel — implementado por OnnxThreatModel quando a feature onnx está ligada. Detalhes de treino/export em IA.
Rules engine — módulo rules
Rule, RuleSet e SharedRuleSet
pub struct Rule {
pub id: RuleId,
pub priority: i32, // menor = avaliado antes
pub enabled: bool,
pub match_: RuleMatch,
pub action: RuleAction,
pub ttl: Option<Duration>,
pub source: RuleSource,
/* tags, log_level, created_at */
}Rule é avaliada na fast-path, antes de heurísticas e IA. RuleSet indexa regras por IP/ASN/país para lookup rápido; SharedRuleSet é o handle Arc<RwLock<>> que o hot-reload troca a quente (shared()).
RuleAction e RuleSource
RuleAction: Allow (bypass total) > Block/Challenge/RateLimit (short-circuit) > Log/Tag (anota e continua). RuleSource registra a origem (config/db/cloudflare_sync/feed/auto_learned/default_pack).
RuleMatch
RuleMatch é a condição declarativa, combinável com All/Any/Not: Ip(cidr), Asn, Country, Path{op, pattern}, Method, Header{name, op}, UserAgent, Query, Body, UploadFilename, Protocol, TlsFingerprint{ja3, ja4}, TlsSni, Ja3In(set), Reputation(tier), BotVerified, Status, Rate{count, per_secs, scope}, Time.
DSL
O texto do match = "..." no TOML é compilado por dsl::parse (recursive-descent, AND/OR/NOT/parênteses) em um RuleMatch. Erro de digitação falha em tempo de carga, não em runtime. rules_from_config compila cada [[rules.custom]] independentemente — uma expressão inválida pula só aquela regra. ReputationTier (malicious/tor/vpn_proxy/promiscuous/authorized) alimenta Reputation(tier).
Política — módulo policy
VerdictPolicy
VerdictPolicy é o mapeamento configurável detecção→ação. O default preserva o histórico (Info/Low→Allow, Medium→RateLimit, High→Challenge, Critical→Block); cada nível é remapeável no TOML e [[policy.override]] força verdict por DSL — o primeiro override que casa vence e popula Decision.override_reason.
pub fn from_config(cfg: &PolicyConfig) -> Result<Self>;
pub fn decide(&self, level: RiskLevel, evt: &Event) -> (Verdict, Option<String>);Pipeline — módulo pipeline
Pipeline
Pipeline orquestra regras → heurísticas → rotas → scorer → policy → escalada. Construção por builders encadeados:
| Método | Liga |
|---|---|
with_config | ruleset compartilhado + rotas + scorer + policy |
configure_uploads | inspeção de corpo/uploads (F10) |
with_trust | never-ban/blacklist/shadow (F7.2) |
with_rate_limiter | backend p/ condições Rate |
with_offender | escalada de reincidentes |
with_scan_tracker / with_behavior_tracker / with_correlation_tracker | detectores comportamentais |
with_bot_verifier | verificação rDNS de bots (F7.10) |
Métodos de execução e hot-reload: process (síncrono, retorna ProcessedEvent), rescore_from (re-integra sinais de forks AI/LLM/lookup — só eleva, nunca rebaixa), observe_response (fila de sinais de resposta por-IP, F3.9.1), swap_rules / swap_routes, prune_pending.
RouteValidator e RouteDef
RouteValidator valida rotas conhecidas: path fora da lista → UnknownRoute; método não suportado → MethodNotAllowed. RouteDef aceita templates (/users/{id}, trailing /*); RouteValidator::merge une config ∪ DB (dedupe por path, config vence).
Trackers de estado
Todos são por-IP com janelas deslizantes e história limitada; o daemon faz prune periódico.
| Tracker | Arquivo | O que detecta |
|---|---|---|
OffenderTracker | offender.rs | Reincidentes: strikes por janela; record_and_escalate só sobe o verdict na escada RateLimit→Challenge→Block |
ScanTracker | scan.rs | 4xx em rajada: ≥N paths distintos → RandomScan; ≥N 4xx → ScanBehavior |
BehaviorTracker | behavior.rs | Força bruta em login, credential stuffing (UAs distintos), wordlist 404, sucesso suspeito pós-falhas |
CorrelationTracker | correlation.rs | Scan de outro IP no mesmo /24, /64 ou ASN pouco antes do ataque → ScanAttackCorrelation |
BotVerifier | botverify.rs | Cache rDNS de crawlers (Verified/Spoofed/Unknown); DNS injetado via BotDnsResolver, fluxo puro em verify_with |
BlockTable | blocks.rs | Tabela compartilhada de IPs bloqueados com TTL opcional; fast-path da edge nega antes do pipeline (is_blocked, seed, reload) |
TrustSet / SharedTrustSet | trust.rs | Proxies confiáveis, never-ban, blacklist e shadow-list do [real_ip]; ranges Cloudflare embutidos (cloudflare_ranges) |
Config — módulo config
SentryConfig é o schema tipado do sentry.toml (serde; a carga com figment + overlay SENTRY_ vive no CLI). Cada seção tem seu struct (CoreConfig, EdgeConfig, UploadsConfig, PolicyConfig, …), todos reexportados na raiz da crate. A referência completa de chaves e defaults vive na seção Configuração — aqui fica só o contrato: o tipo de action é o enum ActionKind, não string, então erro de digitação falha na carga.
Heurísticas — módulo heuristics
Heuristic e HeuristicEngine
pub trait Heuristic: Send + Sync {
fn name(&self) -> &'static str;
fn gate_bit(&self) -> Option<u8> { None }
fn analyze(&self, evt: &Event, text: &DecodedHttp<'_>) -> Vec<Signal>;
}Heuristic é um detector de regex; HeuristicEngine roda todos (analyze). Performance (F5): um autômato Aho-Corasick de literais-gatilho (gate) antecipa as regex — tráfego limpo custa uma passada SIMD. As famílias embarcadas: SqlInjection, Xss, PathTraversal, Lfi, Log4Shell, CmdInjection, SensitivePath, BadCrawler, EmptyUserAgent, TcpScanner + as de upload (F10, com gate_bit() = None porque operam sobre corpo, não texto de path).
DecodedHttp
DecodedHttp carrega o path e a query URL-decodificados (%27→', +/%20→espaço) computados uma vez por evento, mais multipart/urlecoded parseados. Ao escrever nova heurística, analise text.path/text.query — nunca http.path cru, senão payloads encodados fazem bypass. (O decode acontece no helper interno http_text.)
reload_dataset_lists
pub fn reload_dataset_lists(user_agents: &[String], paths: &[String]);reload_dataset_lists mescla literais de datasets DB-backed (user_agent/path) nos gates do prefilter via ArcSwap — hot-reload sem restart (F7.7).