Importing Captures
pcap/pcapng and HAR import, TLS decryption with keylogs, and session persistence.
Traffic Jam reads packet captures and browser HAR exports into a session you can compare, search, and replay. Parsing happens entirely in the Go backend with an in-process gopacket parser — capture bytes and key material are never handed to a child process, and TShark is not required for import.
Supported file formats
| Kind | Extensions | Notes |
|---|---|---|
| Packet capture | .pcap, .pcapng, .cap | Classic pcap and pcapng, detected by magic bytes |
| Compressed capture | .pcap.gz, .pcapng.gz | gzip-wrapped; decompressed transparently |
| HAR | .har | HAR 1.2 as exported by browser DevTools |
| TLS key log | .sslkeylog, .keylog, .keys, .log, .txt | NSS keylog format (see below) |
The backend distinguishes pcapng from classic pcap by the section-header magic (0a 0d 0d 0a) and transparently unwraps gzip before parsing, so you can drop either form in as-is.
Import a capture
- Open the capture intake (the import panel on the workspace home).
- Choose a capture file, choose a whole folder, or drag files onto the window. Global drag-and-drop works anywhere in the app.
- If a keylog is present, pair it with its capture (see the next section), then confirm the import.
Each capture becomes its own session. Progress is shown in a toast; on success the toast offers an Open timeline action. HAR files are imported through the same intake and are routed to a separate endpoint automatically.
[!TIP] You can also hit the API directly:
POST /api/import(multipart fieldspcap, optionalkeylog,name,folderPath) for captures andPOST /api/import-har(fieldhar) for HAR. The default upload ceiling is 4 GiB per request; override it withTRAFFIC_JAM_MAX_UPLOAD_BYTES.
Decrypt TLS traffic with a keylog
Encrypted HTTP/1 and HTTP/2 sessions are decrypted when you supply an NSS key log file — the format written by the SSLKEYLOGFILE environment variable. Point the target client at a writable file before you capture:
export SSLKEYLOGFILE="$HOME/tls-keys.log"
# run the browser / curl / app under test, then capture
Firefox and Chromium honor SSLKEYLOGFILE directly; curl (built against NSS or a supporting TLS stack) does too. Attach the resulting file to the capture in the intake, or pass it as the keylog multipart field. The parser reads the standard three-column lines (LABEL client_random secret), skips # comments, and merges an external keylog with any secrets embedded in the capture.
Supported secret labels:
| TLS version | Keylog labels consumed |
|---|---|
| TLS 1.3 | CLIENT_HANDSHAKE_TRAFFIC_SECRET, SERVER_HANDSHAKE_TRAFFIC_SECRET, CLIENT_TRAFFIC_SECRET_0, SERVER_TRAFFIC_SECRET_0 |
| TLS 1.2 | CLIENT_RANDOM (master secret) |
TLS 1.3 AES-128-GCM, AES-256-GCM, and ChaCha20-Poly1305 suites are supported, as are TLS 1.2 AEAD suites (GCM and ChaCha20-Poly1305). If a stream has a matching secret but still fails to open, the import reports a per-stream decryption warning.
pcapng Decryption Secrets Blocks
If your capture tool embeds secrets directly — Wireshark’s “Export TLS session keys” or a capture written while SSLKEYLOGFILE was active — Traffic Jam reads the pcapng Decryption Secrets Block (DSB) and needs no separate keylog file. The gopacket pcapng reader skips blocks it does not understand, so the backend scans for DSBs (block type 0x0000000a, secrets type TLSK) separately and merges those keys with any external keylog you also provide.
How captures are parsed
The parser reassembles each TCP stream by sequence number, warns on capture gaps, and then classifies the payload:
- HTTP/2 is identified by the connection preface (
PRI * HTTP/2.0…), not by ALPN — ALPN lists every protocol the client offered, which misclassifies HTTP/1.1 connections when a browser also offeredh2. Frames are decoded with an HTTP/2 framer and an HPACK decoder;SETTINGSframes are captured too. - HTTP/1 requests and responses are parsed with Go’s
net/httpreaders, preserving header order and bodies. - HTTP CONNECT tunnels are detected at the head of a stream, split off, and the tunneled traffic is parsed on its own.
Bodies are decompressed according to Content-Encoding (gzip, deflate/zlib, brotli, zstd). Alongside the exchanges, the parser extracts TLS ClientHello fingerprints (JA3/JA3Full, cipher suites, extensions, supported groups, signature algorithms, SNI, ALPN) and the server certificate chain.
Session persistence
Imported sessions are stored in SQLite, so they survive API restarts. The default database is .traffic-jam/traffic-jam-v2.sqlite3; set TRAFFIC_JAM_DB_PATH to relocate it. Sessions, exchanges, TLS fingerprints, and certificates are kept in dedicated tables (capture_sessions, traffic_exchanges, tls_fingerprints, tls_certificates), and the original pcap/keylog paths are recorded so a session can be traced back to its source files. Deleting a session cascades to its rows.
Import multiple captures
Drop or select several captures at once and each is imported as a separate session in the same workspace. When you supply captures and keylogs together, the intake auto-pairs each capture with a keylog by filename stem — an exact stem match wins, otherwise a keylog in the same directory is used. Folder import walks the directory recursively and applies the same matching. See /docs/en/getting-started/installation/ for the runtime requirements and /docs/en/analysis/ for comparing the resulting sessions.
Limitations
| Area | Behavior |
|---|---|
| WebSocket | The Upgrade: websocket handshake is detected and flagged (101 response or Connection: upgrade), but WebSocket message frames are not decoded. A warning reports the number of upgrades found. |
| QUIC / HTTP3 | UDP flows on port 443/8443 are detected and a warning is emitted. QUIC cannot be decrypted with TLS keylogs — disable QUIC/HTTP3 in the client to capture that traffic as TLS-over-TCP. |
| TCP gaps | Streams with missing segments are reassembled best-effort and flagged; affected messages may be incomplete. |
For reverse-engineering the APIs inside an imported session, continue to /docs/en/api-re/.