SentrySentry
Architecture

Data Model

Event, ProtocolData, HttpData and uploads — Sentry's central contract

Data Model

The Event is modular by design: fields common to every origin live at the top level; protocol specifics live in the ProtocolData enum. Heuristics, rules and the scorer pattern-match on the accessor helpers and return early for variants they don't handle — no pipeline stage may assume an event is HTTP.

Event (top level)

FieldTypeDescription
idUuidStable unique event id
timestampDateTime<Utc>Observation time (ingest time, not log time)
sourceSourceKindNginx, HttpProxy, Tcp, CloudflareLogs, Syslog, EdgeTls, Synthetic
transportTransportTcp, Udp, Quic, Internal
directionDirectionInbound or Outbound
client_ipIpAddrClient IP, always canonical (IPv4-mapped becomes plain IPv4)
client_port / server_portOption<u16>Source port and observed listening port
geoOption<GeoInfo>Country/subdivision/city/lat/lon (MaxMindDB, filled by the ingestor)
asnOption<u32>Autonomous system number of the client IP
reputationOption<ReputationInfo>Tier + matching feed (F3.7, filled by the ingestor)
botOption<BotStatus>rDNS crawler-verification outcome (F7.7, filled by the pipeline)
bytes_in / bytes_outOption<u64>Bytes received/sent
duration_msOption<u64>Request/response duration when known
rawOption<String>Original record (log line, syslog message) for audit
protocolProtocolDataProtocol-specific payload

A source produces a thin RawEvent; the ingestor promotes it to Event and fills geo/ASN/reputation.

ProtocolData (protocol enum)

VariantPayloadDescription
Http(HttpData)HttpDataHTTP/1.x or HTTP/2 request observation (nginx, proxy syslog, inline edge)
Tcp(TcpData)TcpDataNon-HTTP TCP stream observation: flags, reassembled payload, stream_id, stage, SYN fingerprint (window:options:MSS:wscale) and syn_count (burst aggregation, F5)
Udp(UdpData)UdpDataUDP datagram; dns_query when it looks like DNS
TlsHandshake(TlsData)TlsDataClientHello: sni, ja3, ja4, cipher, version, alpn (F8)
Syslog(SyslogData)SyslogDataRFC 5424 message (RFC 3164 fallback): facility, severity, hostname, app-name, message
Raw(RawData)RawDataFallback for protocols without a dedicated variant

HttpData (key fields)

FieldDescription
methodOption<HttpMethod> — typed enum (Get, Post, …, Other)
scheme / hosthttp/https and the Host header value
path / query / fragmentPath without query, raw query string, fragment
statusResponse status code when observed (inline edge and proxy fill it)
user_agent / refererIdentification headers
headersEvery header captured by the source (http_* log_format tokens become kebab-case keys, enabling header.X DSL rules)
bodyRequest body bytes when the source is inline (bounded by body_capture_kb)
cookiesParsed cookies (populated by the edge)
uploadsOption<Vec<UploadInfo>> — uploaded file metadata (F10); content never persists
upstream_time_ms$upstream_response_time (F12, first value of lists)

UploadInfo / UploadKind (F10)

UploadInfo carries field_name, filename, content_type, size and kind. UploadKind classifies by magic bytes (the client declaration is only a hint): Text, Image (PNG/JPEG/GIF/WebP/BMP), Archive (ZIP/gzip/bzip2), Executable (MZ/ELF/Mach-O/shebang), Pdf, Unknown.

Accessor helpers

impl Event {
    pub fn http(&self) -> Option<&HttpData>
    pub fn tcp(&self) -> Option<&TcpData>
    pub fn tls(&self) -> Option<&TlsData>
    pub fn syslog(&self) -> Option<&SyslogData>
    pub fn protocol_kind(&self) -> ProtocolKind
}

DSL rules use protocol_kind() for protocol conditions (ProtocolKind: Http, Http3, Tcp, Udp, Tls, Other).

Golden rule: adding a new protocol

  1. Add a variant to ProtocolData.
  2. Add a helper on impl Event (e.g. pub fn my_proto(&self) -> Option<&MyData>).
  3. Update protocol_kind().
  4. Never add loose fields to the Event top level — put them in the enum.

Old consumers keep compiling (they simply ignore the new variant) and the same pipeline serves nginx today and TCP/TLS tomorrow.

Encoding normalization (heuristics)

Heuristics (SQLi, XSS, path traversal, etc.) run over the URL-decoded form of path/query (heuristics::http_text), so encoded payloads (%27 = ', + or %20 = space) cannot bypass detection. When writing new heuristics, always use http_text(http) instead of reading http.path / http.query directly.

On this page