Concepts & Architecture

Workspaces, sessions, the Go backend, and how the pieces fit.

Traffic Jam is three cooperating processes plus one database. Understanding the split — what runs where, and where state lives — makes the rest of the documentation easier to navigate.

The three layers

LayerTechnologyResponsibility
Desktop shellElectron (electron/main.cjs)Spawns and supervises the Go API, allocates a loopback port, generates the capability token, serves the frontend over the traffic-jam://app scheme
FrontendReact + Vite (TypeScript)All UI: capture library, analysis tabs, API lab, replay dialogs. Talks to the backend only over HTTP
API backendGo (server/, wired with uber-go/fx)Parsing, TLS decryption, replay, storage. Exposes a JSON REST API under /api

In the packaged macOS app the shell starts the bundled traffic-jam-api binary on a free 127.0.0.1 port. In development you run the two halves yourself: npm run dev:api starts Go on 127.0.0.1:8790, and npm run dev:web starts Vite, which proxies /api to http://127.0.0.1:8790. See /docs/en/getting-started/installation/.

Workspaces, sessions, and requests

The data model is a small hierarchy:

  • Capture folder (workspace) — a directory you register to organize evidence. Folders nest; sessions are filed into them. Managed through PUT/PATCH/DELETE /api/folders.
  • Capture session — one imported capture: a pcap/pcapng file (with optional TLS keylog) or a HAR file. A session carries parse stats, warnings, TLS fingerprints, and recovered certificates.
  • Request/response exchange — a single HTTP transaction reassembled from the capture, with ordered headers, bodies, timing, and the TLS fingerprint (JA3/JA4, ciphers, ALPN, SNI) attached to its connection.

The frontend lists sessions via GET /api/sessions (summaries only) and lazily loads full details with GET /api/sessions/{id}. The desktop shell’s workspace tabs — Captures, Browser capture, Android, API lab, Timeline, Compare, TLS, Workbench — are views over this same data.

How the frontend talks to the backend

Every call goes through src/shared/api/client.ts, which prefixes apiBaseUrl and adds one header when a token is configured:

X-Traffic-Jam-Token: <capability token>

The backend resolves the base URL and token from the Electron preload bridge (window.trafficJam) in desktop builds, or from VITE_TRAFFIC_JAM_API_BASE_URL / VITE_TRAFFIC_JAM_API_TOKEN in the web build.

The API security middleware (withAPISecurity in handler.go) enforces two independent checks on every request:

  1. Origin allowlist. A request carrying an Origin header must come from an allowed origin or it is rejected with HTTP 403. Always allowed: the desktop origin traffic-jam://app, localhost, and loopback IPs (127.0.0.1, ::1). Additional exact origins can be added with the comma-separated TRAFFIC_JAM_ALLOWED_ORIGINS env var.
  2. Capability token. When TRAFFIC_JAM_API_TOKEN is set, the request must present the same value in X-Traffic-Jam-Token (compared in constant time) or it is rejected with HTTP 401. The standalone dev API has no token by default; the desktop shell generates a cryptographically random 32-byte token on every launch and passes it only to its own backend and renderer.
Env var (backend)DefaultPurpose
TRAFFIC_JAM_ADDR127.0.0.1:8790Listen address
TRAFFIC_JAM_API_TOKENempty (no token required)Capability token; empty disables the check
TRAFFIC_JAM_ALLOWED_ORIGINSemptyExtra exact browser origins beyond loopback/desktop
TRAFFIC_JAM_DB_PATH.traffic-jam/traffic-jam-v2.sqlite3SQLite database file
TRAFFIC_JAM_CAPTURE_DIR.traffic-jam/live-capturesLive-capture artifact directory
TRAFFIC_JAM_MAX_UPLOAD_BYTES4 GiBUpload size cap

[!NOTE] The API binds to loopback only. It is a local analysis tool, not a network service; do not point TRAFFIC_JAM_ADDR at a public interface.

The capture pipeline

Importing a capture (POST /api/import, or /api/import-har for HAR) runs entirely in-process — TShark is not required:

  1. Read packets. gopacket/pcapgo iterate the pcap/pcapng file.
  2. Reassemble TCP streams. Segments are grouped by flow and reordered; capture gaps are reported as warnings. UDP on 443/8443 is flagged as QUIC/HTTP3, which cannot be decrypted with TLS keylogs.
  3. Decrypt TLS. TLS 1.2/1.3 records are decrypted using keys from an external NSS keylog file or from a pcapng Decryption Secrets Block. The ClientHello/ServerHello are parsed for JA3/JA4 and certificate extraction.
  4. Parse HTTP. The plaintext stream is parsed into request/response exchanges.
  5. Persist. The session, exchanges, fingerprints, and certificates are written to SQLite.

Live capture instead drives mitmdump (and tshark for packet-level capture) as child processes; see /docs/en/capture/.

SQLite persistence

All durable state lives in one SQLite database (modernc.org/sqlite, single writer connection). Imported sessions survive API restarts. The tables:

TableStores
capture_sessions, capture_foldersImported captures and their folder hierarchy
traffic_exchangesReassembled request/response pairs
tls_fingerprints, tls_certificatesClientHello fingerprints and recovered certs
replay_historySent replays and their responses
endpoint_annotationsReview status, tags, notes per endpoint
client_certsUploaded mTLS client certificates (PEM/PKCS#12)
decode_scripts, decode_pipelinesCustom decode/encode scripts and per-endpoint pipelines
fingerprint_profiles, canary_checks, canary_runsVersioned fingerprint snapshots and canary drift checks
protobuf_schemas, protobuf_bindingsUploaded .proto schemas and endpoint bindings
route_model_rulesRoute-catalog modeling rules

The API lab

The API lab (the api-re feature, workspace tab API lab) is the reverse-engineering surface. It reads the selected capture sessions and layers on a route catalog, global search, signature analysis, decode pipelines, single and bulk replay (capped at 50 requests per batch by the backend), replay-chain variables, and JWT tooling. Replay and its safety model are covered in /docs/en/replay/; the investigation and findings workflow in /docs/en/analysis/ and /docs/en/security/; exporting a standalone Go collector in /docs/en/collector/.

[!WARNING] Features such as alg=none test tokens, credential A/B replay, and mutation probes are for authorized testing of systems you have permission to assess.

Where data lives on disk

  • Web/dev: .traffic-jam/traffic-jam-v2.sqlite3 under the working directory, with live-capture artifacts in .traffic-jam/live-captures (or beside a custom database).
  • Desktop app: the SQLite database sits in Electron’s user-data directory (traffic-jam-v2.sqlite3); open it via Capture → Open Traffic Jam Data Folder.

Environment variables and file formats are collected in /docs/en/reference/.

Traffic Jam documentation. Built with Hugo.