🚀 Help me work seamlessly with open source sharing by sponsoring me on GitHub
Pingly inspects TLS, HTTP, QUIC, and TCP traffic as a server or Rust library.
- JA3, JA4, Akamai HTTP/2, HTTP/3, and passive TCP fingerprints
- Ordered HTTP/1 fields, HTTP/2 frames, QPACK fields, and QUIC transport parameters
- Incremental parsers and Serde round trips for saved captures
- Linux packet capture with TCP and request latency analysis
- Browser-based protocol inspection
- Automatic ACME certificates with TLS-ALPN-01 or HTTP-01
$ pingly -h
TLS and HTTP/1/2/3 fingerprint analysis server
Usage: pingly
pingly <COMMAND>
Commands:
run Run tracking server
systemd Manage the systemd service
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
$ pingly systemd -h
Manage the systemd service
Usage: pingly systemd <COMMAND>
Commands:
start Install, enable, and start the systemd service
restart Update and restart the systemd service
stop Stop the systemd service
logs Show recent systemd logs and follow new entries
status Show the systemd service status
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print helpThe Alpine image is published to ghcr.io/0x676e67/pingly. Run it locally with:
docker run --rm --name pingly \
-p 8181:8181 \
-v pingly-state:/var/lib/pingly \
ghcr.io/0x676e67/pingly:latestFor a public deployment, keep ACME data in a named volume and map port 443 for the default TLS-ALPN-01 challenge:
docker run -d --name pingly --restart unless-stopped \
-p 443:8181 \
-v pingly-state:/var/lib/pingly \
ghcr.io/0x676e67/pingly:latest run --bind 0.0.0.0:8181 \
--acme-domain pingly.us.kg \
--acme-email admin@gmail.com \
--acme-productionFor HTTP-01, also pass -p 80:8080, --acme-challenge http-01, and
--acme-http-bind 0.0.0.0:8080.
Add Pingly to your project:
[dependencies]
pingly = "0.2"The parsers work with captured protocol bytes. For example, a TLS ClientHello can span several TLS records:
use pingly::tls::ClientHello;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let bytes = std::fs::read("client-hello.bin")?;
let hello = ClientHello::parse(&bytes)?;
println!("JA3: {}", hello.ja3().hash);
println!("JA4: {}", hello.ja4().fingerprint);
Ok(())
}HTTP/3 parsing expects decrypted QUIC stream bytes; the library does not decrypt UDP packets. See the examples for incremental parsing, protocol fingerprints, and saved JSON.
Licensed under the Apache License, Version 2.0 (LICENSE).
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.