Installing GopherTrunk on macOS
Five minutes from a fresh download to a working gophertrunk sdr list.
GopherTrunk on macOS is a single static binary that talks to RTL-SDR
dongles through IOKit — no kext, no librtlsdr, no Homebrew formula
to chase.
Requirements: macOS 12 (Monterey) or later, on Apple Silicon or Intel. The binaries link against system APIs (e.g.
SecTrustCopyCertificateChain) that first shipped in macOS 12, so earlier releases (Big Sur 11 and older) abort at launch withdyld: Symbol not found— see Troubleshooting.
1. Download the tarball
Go to the GopherTrunk releases page and grab the asset matching your CPU:
gophertrunk-<version>-darwin-arm64.tar.gz # Apple Silicon (M1 / M2 / M3 / M4)
gophertrunk-<version>-darwin-amd64.tar.gz # Intel Macs
If you’d rather curl it, see the one-liner under the downloads page macOS quick-start.
Verify the download against
SHA256SUMSbefore installing — see the verify section on the downloads page for the exactshasum -a 256 -cinvocation.
2. Install the binary
Extract and place gophertrunk somewhere on PATH. The conventional
spot for a single-binary command-line tool on macOS is
/usr/local/bin (Intel) or /opt/homebrew/bin (Apple Silicon, if you
use Homebrew); either works:
tar xzf gophertrunk-<version>-darwin-arm64.tar.gz
cd gophertrunk-<version>-darwin-arm64
sudo install -m 0755 gophertrunk /usr/local/bin/gophertrunk
The tarball also bundles config.example.yaml, README.md, and
LICENSE. We’ll come back to the config in step 5.
3. Clear the Gatekeeper quarantine (one-time, every download)
Builds are unsigned — the first time you run an un-signed, quarantined binary, macOS will refuse with “cannot be opened because the developer cannot be verified.” Two ways to clear it:
Easy: right-click gophertrunk in Finder → Open → confirm in
the dialog. macOS remembers your approval for that binary.
CLI: strip the quarantine xattr directly:
sudo xattr -dr com.apple.quarantine /usr/local/bin/gophertrunk
Re-do this every time you upgrade — a fresh download re-attaches the
com.apple.quarantine xattr.
No driver swap needed. Unlike Windows (Zadig → WinUSB) and Linux (DVB blacklist + udev rule), macOS lets user-space claim USB devices via IOKit without rebinding the kernel driver. Plug in the dongle and go.
4. Verify everything works
Open Terminal and run:
gophertrunk version
gophertrunk sdr list
sdr list should print one line per attached dongle with its driver,
index, serial, product string, and (when populated) tuner + gain
ladder. The plain command only reads USB descriptors, so the TUNER
and gains columns stay blank — pass --probe to open each device
just long enough to enumerate them:
gophertrunk sdr list --probe
If you see no SDR devices found and the dongle is plugged in:
- Check
system_profiler SPUSBDataType | grep -A 4 RTL2838shows the dongle (typically0x0bda:0x2838). - If you have SDR++, GQRX, or another RTL-SDR app open, close it — IOKit hands the device to whoever claims it first.
- Some USB-C hubs power-cycle low-power devices aggressively; plug the dongle directly into the Mac or into a powered hub.
See hardware.md for the full
matrix of supported tuners and dongles.
5. Configure and start the daemon
The tarball includes config.example.yaml. Drop a copy at
~/Library/Application Support/GopherTrunk/config.yaml and edit
it — the daemon walks $GOPHERTRUNK_CONFIG →
~/Library/Application Support/GopherTrunk/config.yaml →
~/Documents/GopherTrunk/config.yaml → ./config.yaml and loads
the first one it finds, so no -config flag is needed:
mkdir -p ~/Library/Application\ Support/GopherTrunk
cp config.example.yaml ~/Library/Application\ Support/GopherTrunk/config.yaml
${EDITOR:-nano} ~/Library/Application\ Support/GopherTrunk/config.yaml
gophertrunk run
On startup the daemon prints config: loaded <path> so you can
confirm it picked the right file. Override discovery any time with
-config <path> or by exporting GOPHERTRUNK_CONFIG.
If you keep more than one config in that directory (e.g.
config.yaml + prod.yaml), gophertrunk run prints a numbered
menu and asks which to load. Non-interactive launches (launchd,
cron) auto-pick the first match with a stderr warning; pin a
specific file via -config or GOPHERTRUNK_CONFIG for those.
Logs stream to the terminal. Press Ctrl+C to stop cleanly.
Run as a launchd service
For a long-running setup, register GopherTrunk as a per-user
LaunchAgent so it starts at login and respawns on crash. Drop the
following at ~/Library/LaunchAgents/org.gophertrunk.daemon.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.gophertrunk.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/gophertrunk</string>
<string>run</string>
<string>-config</string>
<string>/Users/YOUR_USER/.config/gophertrunk/config.yaml</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/YOUR_USER/Library/Logs/gophertrunk.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOUR_USER/Library/Logs/gophertrunk.err.log</string>
</dict>
</plist>
Then load it:
launchctl load ~/Library/LaunchAgents/org.gophertrunk.daemon.plist
launchctl start org.gophertrunk.daemon
tail -f ~/Library/Logs/gophertrunk.log
For a system-wide daemon (starts at boot, not login), put the plist
under /Library/LaunchDaemons/ and sudo launchctl bootstrap
system/<path> instead. Note that LaunchDaemons run as root by
default — set UserName in the plist to drop privileges.
Uninstall
launchctl unload ~/Library/LaunchAgents/org.gophertrunk.daemon.plist 2>/dev/null || true
rm -f ~/Library/LaunchAgents/org.gophertrunk.daemon.plist
sudo rm -f /usr/local/bin/gophertrunk
rm -rf ~/.config/gophertrunk
rm -f ~/Library/Logs/gophertrunk.log ~/Library/Logs/gophertrunk.err.log
Recordings under your call-log directory are left alone — remove them manually if you want a clean slate.
Troubleshooting
| Symptom | Likely cause |
|---|---|
cannot be opened because the developer cannot be verified |
Quarantine xattr still attached — re-run the xattr -dr from step 3, or right-click → Open. |
dyld: Symbol not found: _SecTrustCopyCertificateChain (then zsh: abort) |
Your macOS is older than 12 (Monterey). GopherTrunk binaries require macOS 12 or later — check with sw_vers and update, or run on a supported machine. |
command not found: gophertrunk |
Binary isn’t on PATH — re-check step 2, or run from the install path directly. |
sdr list prints nothing |
Another RTL-SDR app (SDR++, GQRX) is holding the device — close it and retry. |
sdr list still prints nothing with every other SDR app closed |
Re-run with RTLSDR_DEBUG_USB=1 gophertrunk sdr list 2> trace.log. Attach trace.log, sw_vers, and system_profiler SPUSBDataType \| grep -A 10 "Vendor ID" to a new issue — the trace tells us which IOKit class matched and which properties were readable. |
usb: claim interface failed |
Same — IOKit hands the dongle to whoever opened it first. |
usb: bulk-IN not active / usb: transfer aborted during probe or bring-up, especially with two dongles |
IOKit aborted a control transfer mid-bring-up (kIOReturnAborted). Bring-up now resets and retries this transient automatically; if it survives every retry it’s usually marginal USB power or bus contention. Give each dongle its own powered port — avoid bus-powered hubs and daisy-chained extensions — and retry gophertrunk sdr list --probe. |
| Audio plays as silence | audio.enabled: false by default — set true in config. CoreAudio is the default backend on Darwin. |
LaunchAgent says Load failed: 5: Input/output error |
plist syntax error — plutil -lint ~/Library/LaunchAgents/org.gophertrunk.daemon.plist will show the line. |
For anything else: open an issue at
https://github.com/MattCheramie/GopherTrunk/issues with the
gophertrunk version output and the first few lines of the daemon
log (tail ~/Library/Logs/gophertrunk.err.log if running under
launchd).