A /16 is 65,536 addresses. Scanning even the top 1000 ports on each from one host is tens of millions of probes, and if any single source pushes that volume it gets slow, throttled, or noticed. Port scanning is the kind of broad, parallel work a fleet is built for. This post covers running pulse, the Crossfyre network engine, across nodes: how to split the range, pick a technique, detect services, pace the scan, and resume a big sweep if it breaks.
Scan only ranges you own or are explicitly authorized to assess. Port scanning is unambiguously active traffic. Confirm the IP ranges are in scope (paid engagement or a program that lists them) before you point pulse at anything.
A baseline pulse scan
pulse takes one or more targets as hosts or CIDR ranges, a port set, and a technique. Port sets can be named shorthands (top-100, top-1000) or explicit ranges and lists. The live TUI streams open ports as they are found.
# Connect scan of the top 1000 ports across a /24 you own,
# with service detection on whatever comes back open.
pulse scan \
--target 10.0.0.0/24 \
--ports top-1000 \
--technique connect \
--tasks 200 \
--timeout 1500 \
--delay 0 \
--service-detection--tasks is concurrent probes, --timeout is how long each waits for an answer (milliseconds), and --delay is an optional pause each slot takes before its next probe. The engine notes the effective rate as tasks / (delay + probe_time), so those three knobs together set how hard the scan pushes.
SYN versus connect
pulse offers two techniques, and the choice is a real trade-off, not a default to ignore.
- SYN (half-open). Sends a SYN, reads the SYN/ACK, never completes the handshake. Faster and lighter because connections are never fully established, and it leaves a smaller footprint in application logs. It needs raw-socket privileges, so the node runs it as root.
- Connect (full handshake). Completes a normal TCP connection. No special privileges required, and more reliable through some middleboxes and proxy paths, but slower and more visible because each port is a real, logged connection.
Reach for SYN on large sweeps where speed and a lighter footprint matter and the node has privileges. Use connect when you cannot run privileged, or when egress goes through a proxy chain that only carries full connections.
Split the CIDR across the fleet
The single-source bottleneck goes away when the scan does not run from a single source. Launch a port-scan workflow on Crossfyre and the control plane splits the range into discrete operations and publishes them onto a durable NATS JetStream queue. Each node pulls operations and runs the pulse daemon against its slice, so a /16 is scanned by the whole fleet at once.
- Time drops with node count. A range split across ten nodes finishes in roughly a tenth of the wall-clock time of one node, without any node scanning ten times harder.
- Load spreads across egress. Each node carries a fraction of the probes from its own address, which keeps any single source under the radar of per-source rate limits and intrusion detection.
- Egress is yours to place. Each node can route through layered proxy chains and a VPN-isolated network namespace, so probes leave from where you choose and never from the host network.
The same spread-not-speed principle from content discovery at scale applies here: add nodes at a moderate per-node rate rather than pushing one node into the range where it gets throttled or flagged.
Service detection: from open port to useful finding
An open port is a lead; the service behind it is the finding. With --service-detection, pulse probes open ports to identify the service and version, so the result is "8080: an outdated app server" rather than "8080: open." That context is what lets you triage and is what your report needs.
Service detection adds probes per open port, so it costs more time and noise. A common pattern is two passes: a fast SYN sweep to find what is open across the range, then targeted service detection on the open ports only. Results from both land in the Findings explorer, filterable by host and severity and exportable to CSV, JSON, or Markdown.
Rate control on big sweeps
On a large range the goal is the highest throughput the target network tolerates without throttling or alarms. Tune it the same empirical way every time:
- Set per-node concurrency with
--tasksand, if you need to throttle, raise--delayto slow each slot between probes. - Set
--timeoutto the network reality. Too short and you miss slow hosts as false negatives; too long and the scan crawls waiting on dead addresses. - Find the ceiling on a small subnet first, set the per-node rate below it, then add nodes to recover total throughput across the full range.
Resume a crashed sweep
Million-probe sweeps get interrupted: a node is reclaimed, a segment goes dark, you pause to retune. pulse persists scan state, so rerunning the same command continues from where it stopped instead of rescanning the whole range. Pass --fresh-start only when you want to discard saved state and begin clean.
Across the fleet it is automatic. Operations sit on the durable queue until a node completes them, so a node that dies mid-sweep has its slice redelivered to a healthy node, and nothing already scanned is repeated. Reserve-then-reconcile billing means you pay for the probes that actually ran, not for the attempt that crashed. See how scans survive a crashed node for the full picture, and meet the engines if you are new to mach, voyage, and pulse.
Enrol a few nodes and fan a port scan across an authorized range that one box could never finish in time.
Start free