Liking cljdoc? Tell your friends :D

Paclo

cljdoc Clojars Project

Paclo is a Clojure library for packet capture (PCAP) I/O, BPF filtering, and data-first packet processing. It provides a small public API plus optional decode hooks for DNS/TLS workflows.

If cljdoc or Clojars returns 404 right after publishing, indexing can take several minutes.

Project status

Install

deps.edn (recommended: Clojars)

{:deps
 {org.clojars.nanto/paclo {:mvn/version "1.0.1"}}}

deps.edn (Git tag)

{:deps
 {org.clojars.nanto/paclo
  {:git/url "https://github.com/nantes-rfli/paclo.git"
   :git/tag "v1.0.1"}}}

If your environment requires Java class prep for git dependencies, run once in your consumer project:

clojure -X:deps prep

Quick start

(require '[paclo.core :as core])

;; Capture 10 UDP/53 packets
(->> (core/packets {:device "en0"
                    :filter (core/bpf [:and [:udp] [:port 53]])
                    :timeout-ms 50})
     (take 10)
     doall)

Run examples

Development examples are under dev/examples and loaded with :dev.

ExampleWhat it showsOutputTypical command
benchPCAP read performance smokeednclojure -M:dev -m examples.bench
dns-summaryDNS summary rowsedn/jsonlclojure -M:dev:dns-ext -m examples.dns-summary trace.pcap
pcap-filterFilter + write + metadataedn/jsonlclojure -M:dev -m examples.pcap-filter in.pcap out.pcap 'udp and port 53' 60 jsonl --async
pcap-statsPacket and endpoint statsedn/jsonlclojure -M:dev -m examples.pcap-stats in.pcap 'udp and port 53' 10 jsonl
flow-topnTop flows (unidir/bidir)edn/jsonlclojure -M:dev -m examples.flow-topn in.pcap 'udp and port 53' 10 --async
dns-rttDNS RTT pairs/stats/qstatsedn/jsonlclojure -M:dev:dns-ext -m examples.dns-rtt in.pcap 'udp and port 53' 50 stats --async
dns-topnDNS top-N by groupedn/jsonl/csvclojure -M:dev:dns-ext -m examples.dns-topn in.pcap _ 20 rcode edn
dns-qpsDNS bucketed QPS/bytesedn/jsonl/csvclojure -M:dev:dns-ext -m examples.dns-qps in.pcap _ 1000 rcode edn
tls-sni-scanTLS ClientHello SNI top-Nedn/jsonlclojure -M:dev -m examples.tls-sni-scan in.pcap 'tcp and port 443' 10 jsonl

Notes:

  • For repository-local runs, DNS examples require -M:dev:dns-ext.
  • When used as a library dependency, DNS extension namespaces are included in the published artifact.
  • Optional positional arguments can be skipped with _.
  • format defaults to edn if omitted.
  • Bundled sample PCAPs: test/resources/dns-sample.pcap, test/resources/dns-synth-small.pcap, test/resources/tls-sni-sample.pcap.

dns-ext alias quick check

clojure -M:dev:dns-ext -e "(require 'paclo.dns.decode) (println :dns-ext-ok)"

CLI reference (stable v1.0)

dns-topn

clojure -M:dev:dns-ext -m examples.dns-topn test/resources/dns-sample.pcap
clojure -M:dev:dns-ext -m examples.dns-topn in.pcap _ 20 qname-suffix csv --punycode-to-unicode
clojure -M:dev:dns-ext -m examples.dns-topn in.pcap _ 20 alpn edn _ --alpn-join

dns-qps

clojure -M:dev:dns-ext -m examples.dns-qps test/resources/dns-sample.pcap
clojure -M:dev:dns-ext -m examples.dns-qps in.pcap _ 200 qname edn --max-buckets 100000 --warn-buckets-threshold 50000 --emit-empty-per-key
clojure -M:dev:dns-ext -m examples.dns-qps in.pcap _ 500 rrtype jsonl --async --async-mode dropping --async-buffer 256

dns-rtt

clojure -M:dev:dns-ext -m examples.dns-rtt in.pcap
clojure -M:dev:dns-ext -m examples.dns-rtt in.pcap 'udp and port 53' 50 pairs _ edn _ --server 1.1.1.1:53
clojure -M:dev:dns-ext -m examples.dns-rtt in.pcap 'udp and port 53' 20 qstats p95 jsonl --client 192.168.4.28

pcap-stats

clojure -M:dev -m examples.pcap-stats in.pcap
clojure -M:dev -m examples.pcap-stats in.pcap 'udp and port 53' 10 jsonl

flow-topn

clojure -M:dev -m examples.flow-topn in.pcap 'udp and port 53' 10 bidir bytes jsonl
clojure -M:dev -m examples.flow-topn in.pcap 'udp or tcp' 10 bidir bytes edn --async --async-mode dropping --async-timeout-ms 1000

pcap-filter

clojure -M:dev -m examples.pcap-filter in.pcap out.pcap
clojure -M:dev -m examples.pcap-filter in.pcap out-dns.pcap 'udp and port 53' 0 jsonl
clojure -M:dev -m examples.pcap-filter /tmp/large.pcap /tmp/out.pcap --async --async-mode dropping --async-buffer 16

tls-sni-scan

clojure -M:dev -m examples.tls-sni-scan in.pcap
clojure -M:dev -m examples.tls-sni-scan in.pcap 'tcp and port 443' 10 jsonl

For full argument tables and behavior details, see docs/usage.md.

Public API surface (v1.0)

NamespacePublic functionsNotes
paclo.corebpf, packets, write-pcap!, list-devicesUser-facing API
paclo.decode-extregister!, unregister!, installed, apply!Post-decode hook API

Internal namespaces (paclo.pcap, paclo.parse, paclo.proto.*) are not part of the compatibility contract.

Compatibility matrix

LayerSupportedCI gate status (2026-02-23)Notes
Clojure1.12.xRequiredBaseline in deps.edn is 1.12.1
JDK17, 21RequiredCompatibility jobs run both
OSmacOS, LinuxRequiredmacos-latest and Linux runners
CPUx86_64, arm64Requiredarm64 runner: ubuntu-24.04-arm
Babashka1.12.xCheckedCI validates bb --version
libpcapSystem packageCheckedLinux uses libpcap-dev; macOS uses system pcap

Performance baselines

Reference measurements (:xform = drop packets shorter than 60 bytes):

SamplePacketsdecode?=falsedecode?=true
test/resources/dns-sample.pcap411.1ms13.3ms
synthetic 50k (/tmp/paclo-mid-50k.pcap)50,000273.7ms879.9ms
synthetic 100k (/tmp/bench-100k.pcap)100,000398.5ms1291.7ms

Environment: macOS 14.4 / Intel i7-8700B / JDK 21.

Perf gate:

clojure -M:perf-gate

Current thresholds: warn=1000ms, fail=1200ms.

Security scan (NVD)

CI runs Dependency Audit with NVD_API_TOKEN.

Local reproduction:

NVD_API_TOKEN=<token> clojure -M:nvd dev/nvd-clojure.edn "$(clojure -Spath -A:dev:dns-ext)"

Documentation

  • Docs index: docs/README.md
  • Usage guide: docs/usage.md
  • Decode extensions: docs/extensions.md
  • Public API contract: docs/cljdoc-api-contract.md
  • Migration guide: docs/migration-0.4-to-1.0.md
  • Roadmap: docs/ROADMAP.md

Maintainer notes

Clojars publish automation is defined in .github/workflows/publish.yml. Required repository secrets:

  • CLOJARS_USERNAME
  • CLOJARS_PASSWORD

FAQ

Q. Live capture returns Permission denied. A. On Linux/macOS, packet capture may require root privileges or specific libpcap permissions.

Q. What happens with :decode? true on parse errors? A. Packet decode failures do not throw. Each packet includes :decode-error instead.

Q. Is this optimized for large captures? A. Yes for streaming workflows, but use BPF + :xform aggressively to reduce allocations.

License

MIT

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close