Skip to content

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.

huginn-netTCP, HTTP, TLS
↓ Uses ↓
huginn-net-tcpTCP analysis
huginn-net-httpHTTP analysis
huginn-net-tlsTLS analysis
↓ TCP & HTTP use ↓
huginn-net-dbp0f signatures

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.

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"] }
FeatureDescription
fullAlias for syn + syn-ack + mtu + uptime
synTCP SYN OS fingerprinting (client → server)
syn-ackTCP SYN+ACK OS fingerprinting (server → client)
mtuMTU extraction from the TCP MSS option
uptimeUptime estimation from TCP timestamps
jsonserde::Serialize on all output types

Match tiers are Specific, Generic, and Fuzzy. See Matching. Parsed fields you can match yourself: ObservableTcp, ObservableMtu, ObservableUptime.

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"] }
FeatureDescription
fullAlias for p0f-request + p0f-response + akamai
p0f-requestHTTP request fingerprinting (browser detection, Accept-Language, header order)
p0f-responseHTTP response fingerprinting (web server detection)
akamaiStandalone Akamai HTTP/2 fingerprint extractor (Http2FingerprintExtractor)
jsonserde::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.

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 offers
huginn-net-tls = { version = "{{huginn-net-tls}}", features = ["full"] }
FeatureDescription
fullAlias for stable-v1
stable-v1Adds JA4_s1 / JA4_rs1 (huginn-only): JA4 minus S1_SESSION_EXTENSIONS so resumption does not split the key
jsonserde::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.

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 protocols
huginn-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.

  • Need everything? → Use huginn-net
  • Only OS detection? → Use huginn-net-tcp and huginn-net-db
  • Only browser detection? → Use huginn-net-http and huginn-net-db
  • Only TLS analysis? → Use huginn-net-tls
  • Custom signatures? → Use huginn-net-db together with huginn-net-tcp or huginn-net-http