Architecture

How graywolf is built and how packets flow through the system

This page covers graywolf’s internal design. You don’t need any of this to operate a station — it’s here for anyone curious about how things work under the hood, or for developers who want to contribute.

Components

Graywolf is two cooperating processes that communicate over a Unix socket using length-prefixed protobuf messages:

Component Overview
ComponentLanguageResponsibility
graywolf-modem Rust Audio I/O, AFSK/9600/PSK demodulation, HDLC framing, FEC encoding/decoding, PTT control, TX modulation
graywolf Go AX.25/APRS protocol handling, digipeater, iGate, beacon scheduler, KISS/AGWPE servers, TX governor, REST API, web UI
Web UI Svelte Dashboard, configuration forms, live packet logs. Built at compile time and embedded in the Go binary.
Config DB SQLite All configuration state (audio devices, channels, beacons, digi rules, iGate settings). Managed through the web UI’s REST API.

Startup Sequence

  1. The Go process starts and opens the SQLite configuration database.
  2. It launches graywolf-modem as a child process. The modem creates a Unix socket and signals readiness by writing to stdout.
  3. The Go process connects to the socket and sends configuration messages: audio devices, radio channels, and PTT settings.
  4. A StartAudio message begins audio processing. The modem starts reading from the sound card and demodulating.
  5. The HTTP server starts, serving the web UI and REST API. The station is now operational.

Packet Receive Path

When a packet arrives over the air:

  1. The Rust modem captures audio from the sound card, runs it through the AFSK demodulator (or 9600/PSK decoder), and extracts HDLC frames.
  2. Valid AX.25 frames are sent over the Unix socket to the Go process as protobuf messages.
  3. The Go process decodes the AX.25 frame and parses the APRS info field (position, message, weather, telemetry, etc.).
  4. The decoded packet is fanned out to all consumers simultaneously:
    • Packet log — recorded in the ring buffer for the web UI and API
    • Digipeater — evaluated against rules for possible retransmission
    • iGate — checked against RF filters for gating to APRS-IS
    • KISS/AGWPE clients — broadcast to connected TNC clients

Packet Transmit Path

Outbound packets can originate from several sources — the beacon scheduler, the digipeater, KISS/AGWPE clients, or the iGate (IS→RF). All transmissions flow through the TX governor:

  1. A packet is submitted to the TX governor’s priority queue. Priority order: beacon < digipeated < client (KISS/AGWPE) < iGate message.
  2. The governor checks for duplicates (same source + destination + info within 30 seconds) and per-channel rate limits.
  3. CSMA: the governor waits for DCD to clear (channel is quiet), then applies p-persistence to randomize access timing.
  4. The AX.25 frame is sent to the Rust modem over the Unix socket. The modem asserts PTT, generates the AFSK audio (with TX delay preamble), and transmits.
  5. After the frame and TX tail are sent, PTT is released.

Performance

The Rust modem is a port of the AFSK demodulator from Dire Wolf by WB2OSZ. It achieves identical decode counts on the WA8LMF test CD while running significantly faster:

=== Direwolf (atest) ===
982 packets decoded in 45.614 seconds.  34.0 x realtime

=== Graywolf (demod_bench) ===
982 packets decoded in 6.792s.  228.2 x realtime

The IPC overhead between the Rust modem and Go process is minimal — typically 10–50 μs per message round-trip. If the modem process crashes, the Go process restarts it automatically with exponential backoff.

Deployment Options

OS packages recommended Native packages for Debian/Ubuntu, RHEL/Fedora, and Arch Linux. Includes a hardened systemd service. Ideal for Raspberry Pi and dedicated station computers.
Standalone binary Download a release binary for Linux (amd64, arm64) or build from source.
Docker Container image with audio device passthrough for containerized deployments.
Build from source Clone, build, and run — requires Rust toolchain, Go 1.22+, and Node.js 22+.

Supported Protocols

ProtocolDescription
AX.25Amateur Radio Link Access Protocol (UI frames, unconnected mode)
APRSAutomatic Packet Reporting System (APRS101 specification)
KISSKeep It Simple, Stupid TNC protocol (TCP, serial, Bluetooth)
AGWPEAGW Packet Engine protocol (TCP)
APRS-ISAPRS Internet System gateway protocol (TCP, passcode auth)
HDLCHigh-Level Data Link Control with NRZI encoding
FX.25Forward error correction (interleaved convolutional code)
IL2PImproved Layer 2 Protocol (second generation FEC)