# raskrebs/sonar: a port inspector that groups localhost services by repository

> Sonar is a Go CLI that lists what is listening on localhost, assigns each port to a group and a named service, and can start, tail and kill a whole project with one command. It is aimed at developers juggling several dev servers, Docker containers and Compose projects at once.

**raskrebs/sonar** — CLI tool for inspecting and managing services listening on localhost ports

- Repository: https://github.com/raskrebs/sonar
- Stars: 1,093 · Forks: 35
- Language: Go
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/raskrebs-sonar

## The port-ownership problem sonar targets

Anyone who runs three or four services at once knows the drill: something holds port 8000, `lsof -i` prints a wall of rows, and the answer is a process name you do not recognise. Sonar's premise is that a port is not interesting on its own. What matters is which project it belongs to and which service inside that project. The README states the tool shows everything listening on localhost and puts it in order: every port belongs to a group, normally the repository it was started from, and inside that group to a named service. That framing is the whole product. It is for developers on macOS, Linux or Windows who run dev servers, watchers, workers and Docker containers side by side, and who want one command to see the set and one command to stop it. It is not a network scanner for remote hosts in the general sense, although `sonar list --host user@server` does scan a remote machine over SSH.

## How the group and service model is assembled

Sonar does not require you to register anything. When you launch a command through `sonar start`, the child process inherits stdin, stdout, stderr, cwd and environment, plus `SONAR_GROUP`, `SONAR_NAME` and `SONAR_RUN_ID`, and it gets its own process group. That last detail is what makes `sonar kill` able to take down a dev server together with its watchers and workers rather than leaving orphans behind. Group resolution has a fallback chain: `--group`, else the `name` in the nearest `.sonar.yaml`, else the git root's directory name (a worktree becomes `repo@worktree`), else the name of the current directory. Service names are resolved similarly, and can be inferred from the command itself: the README gives `npm run dev` becoming `dev`, `uv run api` becoming `api`, `python -m uvicorn` becoming `uvicorn`, and `./dev.sh` becoming `dev.sh`. Ports are a hint rather than a binding. A run shows as `starting` until the port is actually listening, and the daemon uses the declared port to match the process to the port. Docker containers, Compose projects and processes you started by hand are picked up too, without any configuration. The repository layout backs this up: there is a `cmd/` directory for the CLI entry points, an `internal/` package tree, a `tray/` directory, and a `go.mod` that pulls in `github.com/spf13/cobra` for commands, `modernc.org/sqlite` for a pure-Go SQLite build, `github.com/modelcontextprotocol/go-sdk`, and `github.com/Microsoft/go-winio`, which is the Windows named-pipe dependency. The presence of an MCP SDK and a JSON Schema library (`github.com/invopop/jsonschema`, `github.com/santhosh-tekuri/jsonschema/v6`) suggests machine-readable output is treated as a first-class surface, though the README excerpt does not document an MCP server.

## Installing sonar and getting a first tree view

The README gives four install paths. On macOS and Linux, Homebrew is the shortest. Note the second command: Homebrew 6 refuses formulae from third-party taps until you trust the tap once, and the README quotes the exact error, `Error: Refusing to load formula raskrebs/sonar/sonar from untrusted tap`.

```bash
brew install raskrebs/sonar/sonar
brew trust raskrebs/sonar
```

If you prefer not to use a tap, the install script downloads the latest binary to `~/.local/bin` and adds it to your PATH if needed. On Windows there is a PowerShell equivalent.

```bash
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | bash
```

```powershell
irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex
```

Go users can install directly, and shell completions let you tab-complete port numbers rather than typing them.

```bash
go install github.com/raskrebs/sonar@latest
sonar completion zsh > "${fpath[1]}/_sonar"
```

The fastest real use is to prefix the commands in your existing `dev.sh` with `sonar start`. The README's sixty-second example looks like this, with the group name coming from the repository so nothing else needs configuring.

```bash
#!/usr/bin/env bash
sonar start --name db       --port 5432 -- docker compose up db &
sonar start --name api      --port 8000 -- uv run uvicorn app:app &
sonar start --name frontend --port 5173 -- npm run dev &
wait
```

In another terminal, `sonar list --tree` should print the project as one block with its ports nested underneath, each row showing the port, the service name, what was detected and a localhost URL. When you are finished, `sonar kill -g my-app` stops the whole project, servers, watchers and workers. If you want the process to survive the terminal, `--detach` returns immediately and writes output to `~/.config/sonar/logs/<group>/<name>.log`.

## The .sonar.yaml contract and its constraints

A project can name itself and its services in a `.sonar.yaml` at the repository root. It is optional, since sonar groups by git root without it, and the README says it is meant to be committed. The file carries a `name`, a list of `services` with `cmd`, `cwd`, `port`, `health`, `description`, `icon`, `color` and `depends_on`, and a top-level `ports` list for ports that belong to the project without a service. Two constraints are stated explicitly and both are worth reading twice. The group `name` may contain no slashes and no whitespace. And `cwd` is relative to the file and may not escape its directory. That second rule is a deliberate boundary: a committed config file cannot point a service at a path outside the repository, which matters when the file is shared with a team or checked out from an untrusted branch. The `depends_on` key implies ordering, though the README excerpt does not describe what happens when a dependency fails to start. That is the kind of gap worth testing before you rely on it for a multi-service boot sequence.

## Where sonar stops being the right tool

Sonar is a session tool, not a supervisor. If you need a service to restart after a crash, run at boot, or stay up across reboots, this is the wrong layer; systemd units, launchd plists or a container restart policy are the right ones, and sonar will merely observe the ports they hold. The `--detach` mode is described as returning immediately and writing output to a log file under `~/.config/sonar/logs/`, which is output capture, not supervision. There is also a visibility boundary. Desktop apps and system services that happen to listen, including Figma, Discord, Spotify, ControlCenter, macOS `.app` bundles and `/System/Library/` daemons, are hidden unless you pass `-a`. That default is sensible, but it means a first-time user who is hunting a mystery port on 3000 may need `-a` before the answer appears. Finally, the health check surface is HTTP-oriented: `sonar list --health` performs HTTP health checks, and the `.sonar.yaml` example uses paths like `/healthz`. A service that speaks gRPC or a raw TCP protocol on its port will not get a meaningful health signal from that flag.

## How it differs from lsof, netstat and Docker tooling

The obvious alternative is `lsof -i` or `netstat -tulpn`, and the difference is not the port list, which those tools already give you accurately. The difference is attribution. `lsof` reports a PID and a process name; it has no concept of a project, so three services from the same repository look like three unrelated rows. Sonar's group is derived from the git root unless you override it, which is what makes `sonar kill -g my-app` possible as a single operation. Docker's own tooling is the other comparison point, and here the difference runs the other way: `docker compose down` is authoritative for a Compose project because it knows the desired state, whereas sonar infers state from what is listening. If you only ever run one Compose project, `docker compose up` and `docker compose down` cover most of the ground and sonar adds relatively little. Sonar earns its place when the set spans Compose, a bare `npm run dev`, and a hand-started process, because no single existing tool sees all three as one project.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-09-10. Releases are frequent and small: v0.6.4 on 2026-09-07, v0.6.5 later the same day, and v0.6.6 on 2026-09-10. That cadence suggests a project still settling its surface rather than one in maintenance mode, and it also means the CLI flags are the part most likely to move. The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are included; that is the general shape of the licence text, and anything beyond it is a question for your own legal review rather than something this article can settle. Upgrade cost is low if you install through Homebrew or the install script, since both fetch the latest binary, and the install script accepts `SONAR_VERSION` if you want to pin a release. The larger cost is social rather than technical: the `.sonar.yaml` is meant to be committed, so every service definition becomes shared team surface that needs review when ports or commands change. The README also notes that examples marked `# check` are executed against a fresh build by `scripts/readme-check.sh` on every CI run, which is a reasonable guard against documentation drift for the command examples themselves.

## Conclusion

Adopt sonar if you routinely run several dev servers, Compose projects and hand-started processes at once and want one tree view plus a single kill switch, and if you are willing to wrap your start commands in sonar start. Do not adopt it if you need a long-running supervisor, a cross-machine inventory, or a tool that works without installing a binary on each host. Before committing a .sonar.yaml, verify that its cwd values stay inside the repository, since the README states cwd may not escape the file's directory, and check that the port numbers you declare match what your commands actually bind.

## FAQ

### What is raskrebs/sonar and what is it used for?

It is a CLI tool, written in Go, for inspecting and managing services listening on localhost ports. It assigns each port to a group, normally the repository it was started from, and to a named service inside that group, so you can list a project as a tree, wait for it, tail it, or stop it with one command.

### How do I install raskrebs/sonar?

The README gives four routes: Homebrew with `brew install raskrebs/sonar/sonar`, an install script that downloads the latest binary to `~/.local/bin`, a PowerShell script on Windows, and `go install github.com/raskrebs/sonar@latest`. Homebrew 6 additionally requires `brew trust raskrebs/sonar` before it will load the formula.

### Does raskrebs/sonar work without a configuration file?

Yes. The README states that grouping by git root works without a `.sonar.yaml`, and that Docker containers, Compose projects and processes started by hand are picked up without any configuration. The `.sonar.yaml` is optional and is used to name the group and its services explicitly.

### How do I stop everything a project started with raskrebs/sonar?

Use `sonar kill -g my-app`, substituting your group name. Because each child launched by `sonar start` gets its own process group, the kill takes down the whole tree, including watchers and workers, rather than just the top-level process.

### Can raskrebs/sonar scan a remote machine?

The README lists `sonar list --host user@server` as a way to scan a remote machine over SSH. Nothing in the excerpt describes setting up a long-running agent on the remote side, so treat it as an on-demand SSH scan rather than continuous monitoring.

## Sources

- [Issues](https://github.com/raskrebs/sonar/issues)
- [License: MIT](https://github.com/raskrebs/sonar/blob/main/LICENSE)
- [raskrebs/sonar on GitHub](https://github.com/raskrebs/sonar)
- [README](https://github.com/raskrebs/sonar/blob/main/README.md)
- [Releases](https://github.com/raskrebs/sonar/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/raskrebs-sonar
