Installation
Get graywolf running on your system
Install the Package
Download the .deb package for your architecture
(amd64 or arm64) from the
GitHub Releases
page, then install it:
sudo apt install ./graywolf_*.deb
The package installs both binaries (/usr/bin/graywolf
and /usr/bin/graywolf-modem), creates a
graywolf system user with audio and
dialout group membership, installs a hardened systemd
unit, and enables the service.
Start the Service
sudo systemctl start graywolf
The service is already enabled at boot. Check that it’s running:
sudo systemctl status graywolf
Install the Package
Download the .rpm package for your architecture
from the
GitHub Releases
page, then install it:
sudo dnf install ./graywolf_*.rpm
The package installs both binaries, creates the
graywolf system user, and installs the systemd unit.
Start the Service
sudo systemctl enable --now graywolf
Install from AUR
# With an AUR helper (e.g., yay or paru)
yay -S graywolf-aprs
Or build manually:
git clone https://aur.archlinux.org/graywolf-aprs.git
cd graywolf-aprs
makepkg -si
The PKGBUILD builds both the Rust modem and Go binary from source,
installs them to /usr/bin, and installs the systemd unit.
Start the Service
sudo systemctl enable --now graywolf-aprs
Container Image
ghcr.io/chrissnell/graywolf:latest
Docker Run
docker run -d \
--name graywolf \
--restart unless-stopped \
--device /dev/snd \
-p 8080:8080/tcp \
-v graywolf-data:/data \
ghcr.io/chrissnell/graywolf:latest \
-config /data/graywolf.db -http 0.0.0.0:8080
Audio device passthrough (--device /dev/snd) is
required for the software modem to access your sound card.
For serial PTT or GPS, pass the serial device as well
(e.g., --device /dev/ttyUSB0).
Docker Compose
services:
graywolf:
image: ghcr.io/chrissnell/graywolf:latest
container_name: graywolf
restart: unless-stopped
devices:
- /dev/snd:/dev/snd
ports:
- "8080:8080/tcp"
volumes:
- graywolf-data:/data
command: ["-config", "/data/graywolf.db", "-http", "0.0.0.0:8080"]
volumes:
graywolf-data:
docker compose up -d
Exposing Additional Ports
If you plan to use KISS TCP or AGWPE, expose those ports as well:
ports:
- "8080:8080/tcp" # Web UI
- "6700:6700/tcp" # KISS TCP
- "8000:8000/tcp" # AGWPE
Requirements
- Rust toolchain (stable, via rustup)
- Go 1.22+
- Node.js 22+ and npm (for the web UI)
- GNU Make
- ALSA development headers (
libasound2-devon Debian/Ubuntu)
Build
git clone https://github.com/chrissnell/graywolf.git
cd graywolf
# Build everything (Rust modem + Svelte UI + Go binary)
make graywolf
The binaries are written to bin/graywolf and
bin/graywolf-modem.
Cross-Compiling for ARM
To build for Raspberry Pi (aarch64), use cross:
cargo install cross
cross build --release --target aarch64-unknown-linux-gnu \
-p graywolf-modem
Install System-Wide
The repository includes a systemd unit, postinstall script, and
preremove script under packaging/. To install manually
from a source build:
# Install binaries
sudo install -m 755 bin/graywolf /usr/bin/graywolf
sudo install -m 755 bin/graywolf-modem /usr/bin/graywolf-modem
# Install the systemd unit
sudo install -m 644 packaging/systemd/graywolf.service \
/etc/systemd/system/graywolf.service
# Run the postinstall script (creates user, enables service)
sudo packaging/scripts/postinstall.sh
Then start the service:
sudo systemctl start graywolf
Makefile Targets
| Target | Description |
|---|---|
make all | Full release build (Rust + Svelte + Go) |
make release | Build Rust modem with native CPU optimizations |
make web | Build the Svelte web UI |
make graywolf | Build Go binary (triggers modem + web builds) |
make test | Run Rust tests (cargo test) |
make go-test | Run Go tests with race detector |
make go-fuzz | Run fuzz tests (AX.25, APRS parsers) |
make clean | Clean all build artifacts |
Connect to the Web UI
Once the service is running, open a browser and navigate to:
http://<your-ip>:8080
If you’re on the same machine, use
http://localhost:8080. The web UI is where you’ll
configure audio devices, radio channels, beacons, digipeater rules,
and everything else.
Set a Password
Before exposing the web UI on your network, set an admin password:
sudo graywolf auth set-password --user admin
If you installed from a package and the service is running, you
need to specify the database path explicitly:
sudo graywolf auth set-password --user admin -config /var/lib/graywolf/graywolf.db
What the Packages Install
| File | Purpose |
|---|---|
/usr/bin/graywolf |
Go application server |
/usr/bin/graywolf-modem |
Rust DSP modem |
graywolf.service |
Hardened systemd unit (filesystem, kernel, and network lockdown) |
/var/lib/graywolf/graywolf.db |
SQLite configuration database (created on first start) |
The postinstall script creates a graywolf system user
with membership in the audio group (sound card access)
and dialout group (serial port access for PTT and GPS).
Command-Line Reference
| Flag | Default | Description |
|---|---|---|
-config PATH |
./graywolf.db |
Path to the SQLite configuration database |
-modem PATH |
auto-discover | Path to the graywolf-modem binary |
-http ADDR:PORT |
127.0.0.1:8080 |
HTTP listen address for the web UI and API |
-shutdown-timeout |
10s |
Maximum graceful shutdown duration |
-flac FILE |
— | Override audio input with a FLAC file (for testing) |
-debug |
false |
Enable debug-level logging |
Subcommands
version |
Print the version string (v0.9.1-abc1234) |
auth set-password --user NAME |
Set or change the web UI password for a user |
Verify It’s Working
Check the service logs to confirm graywolf started successfully:
journalctl -u graywolf -f
You should see the modem start, audio devices initialize, and the HTTP server bind. If you see errors about missing audio devices, that’s expected — you haven’t configured any yet. Head to Audio Devices to set up your first radio channel.