Ecosystem
Five crates. huginn-net calls the protocol crates. TCP and HTTP call huginn-net-db when you want a label from p0f.fp. TLS does not.
Modular Architecture
Section titled “Modular Architecture”Crates Overview
Section titled “Crates Overview”huginn-net
Section titled “huginn-net”Runs TCP, HTTP, and TLS in one FingerprintResult. Use it when you want more than one protocol.
full enables db and every analysis this version ships. db already depends on huginn-net-db. Add that crate yourself only with huginn-net-tcp or huginn-net-http.
[dependencies]huginn-net = { version = "{{huginn-net}}", features = ["full"] }Add json to serialize all output types with serde_json:
huginn-net = { version = "{{huginn-net}}", features = ["full", "json"] }Each tcp-* / http-* feature adds a field on FingerprintResult and skips that parser when the feature is off. tls-stable-v1 adds JA4_s1 / JA4_rs1. full does not enable huginn-net-http’s akamai feature. That fingerprint is Http2FingerprintExtractor, called by you, not a field on FingerprintResult.
Live capture and PCAP both go through the same analyzer. With db off, construct it with HuginnNet::new_observable. The raw signatures and JA4 are still there. OS, browser, and server labels are not.
ObservableTcp, ObservableHttpRequest, ObservableHttpResponse, and ObservableTlsClient are the parsed fields if you match them yourself.
huginn-net-tcp
Section titled “huginn-net-tcp”SYN, SYN+ACK, MTU, and uptime. OS labels need huginn-net-db with the tcp feature, passed in with .with_matcher(...). Without a matcher you get the signature string and no OS name.
[dependencies]huginn-net-tcp = { version = "{{huginn-net-tcp}}", features = ["full"] }huginn-net-db = { version = "{{huginn-net-db}}", features = ["tcp"] }| Feature | Description |
|---|---|
full | Alias for syn + syn-ack + mtu + uptime |
syn | TCP SYN OS fingerprinting (client → server) |
syn-ack | TCP SYN+ACK OS fingerprinting (server → client) |
mtu | MTU extraction from the TCP MSS option |
uptime | Uptime estimation from TCP timestamps |
json | serde::Serialize on all output types |
Match tiers are Specific, Generic, and Fuzzy. See Matching. Parsed fields you can match yourself: ObservableTcp, ObservableMtu, ObservableUptime.
huginn-net-http
Section titled “huginn-net-http”HTTP requests, HTTP responses, and Akamai HTTP/2. Browser and server labels need huginn-net-db with the http feature. Without a matcher you get the signature and no name.
[dependencies]huginn-net-http = { version = "{{huginn-net-http}}", features = ["full"] }huginn-net-db = { version = "{{huginn-net-db}}", features = ["http"] }| Feature | Description |
|---|---|
full | Alias for p0f-request + p0f-response + akamai |
p0f-request | HTTP request fingerprinting (browser detection, Accept-Language, header order) |
p0f-response | HTTP response fingerprinting (web server detection) |
akamai | Standalone Akamai HTTP/2 fingerprint extractor (Http2FingerprintExtractor) |
json | serde::Serialize on all output types |
akamai builds the HTTP/2 fingerprint from SETTINGS, WINDOW_UPDATE, PRIORITY, and HEADERS. Http2FingerprintExtractor keeps pseudo-header order when TLS is terminated elsewhere. Parsed fields: ObservableHttpRequest, ObservableHttpResponse.
huginn-net-tls
Section titled “huginn-net-tls”JA4 from a TLS ClientHello on TCP (t prefix). QUIC (q) and DTLS are not parsed. No database.
[dependencies]# Core JA4. Computed from the ClientHello, no database.huginn-net-tls = "{{huginn-net-tls}}"
# With stable fingerprints (JA4_s1 / JA4_rs1) and everything this version offershuginn-net-tls = { version = "{{huginn-net-tls}}", features = ["full"] }| Feature | Description |
|---|---|
full | Alias for stable-v1 |
stable-v1 | Adds JA4_s1 / JA4_rs1 (huginn-only): JA4 minus S1_SESSION_EXTENSIONS so resumption does not split the key |
json | serde::Serialize on all output types |
Output is JA4, JA4_r, JA4_o, and JA4_ro, built with tls-parser. stable-v1 adds JA4_s1 / JA4_rs1 (JA4S1.md).
Versions that are actually parsed: TLS 1.0–1.3 and SSL 3.0. The JA4 version token has an SSL 2.0 code (s2). Those handshakes are not parsed. GREASE values are dropped (RFC 8701). SNI and ALPN go into the prefix. The parsed ClientHello is ObservableTlsClient.
huginn-net-db
Section titled “huginn-net-db”Parses p0f.fp and implements the TCP and HTTP matchers. Depend on it when you use huginn-net-tcp or huginn-net-http on their own. huginn-net already pulls it in through db.
# Both protocolshuginn-net-db = { version = "{{huginn-net-db}}", features = ["full"] }
# TCP only (no HTTP parser embedded)huginn-net-db = { version = "{{huginn-net-db}}", features = ["tcp"] }
# HTTP only (no TCP parser embedded)huginn-net-db = { version = "{{huginn-net-db}}", features = ["http"] }A field that does not fit rejects the signature. Tiers are Specific, Generic, and Fuzzy (TCP only). HTTP hits are exact. Plug the matcher in with SharedTcpSignatureMatcher or SharedHttpSignatureMatcher and .with_matcher(...). Database (both protocols in one type) exists only when tcp and http are both enabled.
How labels and Params are produced is documented under Matching.
The version in each snippet is that crate’s current release on crates.io.
Which Crate Should I Use?
Section titled “Which Crate Should I Use?”Decision Guide
Section titled “Decision Guide”- Need everything? → Use
huginn-net - Only OS detection? → Use
huginn-net-tcpandhuginn-net-db - Only browser detection? → Use
huginn-net-httpandhuginn-net-db - Only TLS analysis? → Use
huginn-net-tls - Custom signatures? → Use
huginn-net-dbtogether withhuginn-net-tcporhuginn-net-http
