Sonar is a network mapper and TCP port scanner. Give it a range of addresses and a set of ports; it finds which hosts are up, sweeps their ports through a bounded worker pool, and names the service behind each open port from its banner, its TLS certificate, or its HTTP server header. It streams results as they arrive, to a table on the command line and to a plan-position scope in the panel.
One binary with no dependencies outside the standard library. Scanning uses ordinary TCP connections, so it needs no elevated privileges and runs the same on Linux, macOS and Windows.
$ sonar scan -d -p top100 192.168.16.1-12
scanned 12 host(s), 8 up, 98 port(s) each in 13.305s
192.168.16.2 0s
PORT STATE SERVICE BANNER
80/tcp open http 200 OK, nginx/1.27.5
192.168.16.4 0s
PORT STATE SERVICE BANNER
6379/tcp open redis
192.168.16.6 0s
PORT STATE SERVICE BANNER
1883/tcp open mqtt
8 open port(s) foundsonar serve hosts a panel that draws the sweep live. Each host that answers becomes a contact placed on the
scope by round-trip latency — the faster it replies, the closer to the centre — and open ports light the
return. Selecting a contact lists its ports; the live feed shows returns as they come in.
- Targets expand into a host set — single addresses, CIDR blocks, dashed ranges, and hostnames — and the port
spec into a sorted list. Named sets like
top100andallare understood. - With
-d, each host is probed on a few common ports first. A host is up if any probe connects or is actively refused; overlarge ranges are refused before anything runs. - Live hosts and ports feed a worker pool. Each connection is classified open, closed or filtered, and a rate limit paces the sweep.
- Every open port is identified over its own connection — a banner grab, a TLS handshake for the certificate name, or a minimal request for the HTTP server header.
More detail and a component diagram are in docs/architecture.md.
| Form | Example |
|---|---|
| single address | 10.0.0.5 |
| CIDR block | 10.0.0.0/24 |
| dashed range | 10.0.0.1-32 or 10.0.0.250-10.0.1.5 |
| hostname | scanme.example.com |
| port list | -p 22,80,443 |
| port range | -p 1-1024 |
| named set | -p top100, -p all |
$ sonar scan [flags] <targets...>
-p ports: list, ranges, top100, or all (default top100)
-c concurrent connections (default 512)
-t per-port timeout (default 2s)
-d discover live hosts before scanning
-rate max connections per second (0 = unlimited)
-open report only open ports
-json print JSON instead of a table
$ sonar serve [flags]
-listen listen address (default 127.0.0.1:8080)
-token require a bearer token
-d discover before scanning by defaultFlags come before the targets. sonar serve reads SONAR_TOKEN; set a token to require it on the API and panel.
The demo stack runs Sonar on a bridge network beside a handful of services, so it can sweep them from the inside and return real banners.
$ make demo # panel on http://127.0.0.1:18120
$ make demo-downThe screenshots above are captured from this stack.
$ make build # the sonar binary
$ make test # unit tests
$ make race # tests under the race detector
$ make lint # golangci-lint: errcheck, govet, staticcheck, gosec, and moreThe parsing, worker pool and identifier carry the load-bearing tests: target and port expansion with its limits, open/closed/filtered classification and the concurrency limit over an injected dialer, and banner, HTTP and TLS identification against in-process servers.
Sonar is a diagnostic tool for networks you are authorised to test — your own hosts, your own lab, an engagement with permission. Scan only what you are allowed to scan.


