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 with dyld: 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 SHA256SUMS before installing — see the verify section on the downloads page for the exact shasum -a 256 -c invocation.

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 RTL2838 shows the dongle (typically 0x0bda: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).