station-manager

module
v1.0.1-0...-5a0dde2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: GPL-3.0

README

Station Manager

Station Manager

Amateur radio station management for Linux — logging, rig control and FT8 in one place, with no account to create and no internet needed to operate.

🌐 station-manager.org · 📦 Install guide · 📄 GPL-3.0-only

Station Manager runs as a local daemon (smd, written in Go) that serves a browser UI (Svelte 5 + Vite). The daemon and the UI ship as a single binary — install it, point a browser at localhost:8080, and you have a logging station. No desktop application, no cloud account, and nothing phoning home.

Status: v2 alpha. It is dogfooded daily from 7Q5MLV in Malawi and logs real contacts on real hardware, but it is early software with one primary operator. Expect rough edges, and read the install guide before depending on it.


128 contacts in 12 hours from 7Q5MLV, coloured by band, with the grey line live.


What it does

Logging

  • Fast SSB/CW entry with live callsign enrichment (QRZ.com, QRZCQ and hamnut lookups). QRZCQ XML is an opt-in fallback that uses a premium account session; enrichment is fail-soft by design — a lookup that fails degrades the entry, it never blocks the QSO.
  • Full ADIF data model, HH:MM:SS time precision, multiple logbooks.
  • Session view with in-place editing, ADIF export and session email-out.
  • Contacts map — great-circle paths with a live grey-line overlay.

FT8

  • Live decode with band activity, per-CQ beam heading, country flags and worked-before marking.
  • Answer a CQ, work a caller, or run CQ and auto-work a pile-up.
  • Occupancy view (channels or spectrum) so you pick a clear transmit slot.
  • ARRL Field Day exchanges, and reduced type-4 ladders for compound calls (PJ4/NA2AA).
  • Optional PSK Reporter spotting.


Working Japan on 15 m. Band activity carries the beam heading for every CQ; the ladder shows the exchange as it happens.

Rig control (CAT)

  • Frequency, mode, VFO and band control over serial, with rig state pushed live to the UI.
  • FT8 keys the rig through the same guarded path, with a hard stop that survives a lost link.

Getting QSOs out

  • Automatic forwarding to QRZ.com, ClubLog, and QRZCQ, per destination and configurable.
  • SM Cloud — an optional, self-hosted off-site backup of your log, so a dead disk is not a lost logbook.
  • All of it is opt-in. The local database is always the authority.

Why another logger?

Honestly: what is out there did not let me operate the way I want to. I do not generally use Windows and I do not want to use a Mac, so I was left writing it myself. Many of the existing packages work, but look out of date, cost too much, or have UIs busy enough to make setup a chore rather than a pleasure. This one is opinionated — deliberately.

It has to work without the internet. Here in Malawi the connection is not always available, and when it is, it is not always reliable. Everything that matters happens locally: logging, rig control, FT8, the database. Online logbooks are a bonus that syncs when there is a link — never a requirement.

FT8 is built in, not bolted on. No second application, no UDP bridge, no parallel logbook to reconcile afterwards: decoding, the exchange, rig control and logging are one program sharing one database. A contact worked on FT8 lands in the same log, with the same callsign enrichment and on the same map, as one worked on SSB.

Contesting is not the target today — general HF by SSB, CW and FT8 is — though support is planned, including multiple distributed stations.

Requirements

  • An RPM-based Linux with systemd: Fedora 34+, RHEL/Rocky/AlmaLinux 8+, or recent openSUSE. A Debian/Ubuntu package is on the roadmap but not yet available.
  • A web browser. That is the entire UI.
  • For CAT and FT8: a supported rig and a sound interface.

The daemon binds loopback only (127.0.0.1:8080) and keeps all state under your home directory — single user, single machine, no network exposure by default.

Install and first-run setup: docs/install.md.

Computer Aided Transceiver (CAT)

CAT is supported, but only the Yaesu FTdx10, Yaesu FT-710 and Icom IC-7300 have been tested — those are the rigs I own. Rigs are described by data files rather than code, so adding one does not need a new build. Definitions and reports from other operators are welcome.

Documentation

Licence

Station Manager is licensed under the GNU General Public License, version 3 only (GPL-3.0-only) — see LICENSE and NOTICE.

It was MIT-licensed until 2026-05-31. The move to GPL-3.0-only follows from the FT8 decode path: that capability comes from the companion library go-ft8, a WSJT-X/jt9-derived work that is GPL-3.0-only. Linking it makes the combined work a GPLv3 derivative, so the whole project adopts the same copyleft licence. The reasoning is recorded in docs/decisions/0023-relicense-to-gplv3.md and docs/licensing.md.


73 de 7Q5MLV

Directories

Path Synopsis
cmd
catcli command
catcli is a diagnostic CLI for poking a rig over serial and, optionally, pipeing framed responses through internal/cat for decode.
catcli is a diagnostic CLI for poking a rig over serial and, optionally, pipeing framed responses through internal/cat for decode.
civ-probe command
Command civ-probe is a developer tool for validating Icom CI-V behaviour against a real rig while building the icom_civ codec (ADR 0034).
Command civ-probe is a developer tool for validating Icom CI-V behaviour against a real rig while building the icom_civ codec (ADR 0034).
docscatalog command
ft8-capture-probe command
ft8-capture-probe is a standalone smoke utility for the live FT8 path: the internal/audio/capture device layer + the internal/ft8 slot scheduler and decoder.
ft8-capture-probe is a standalone smoke utility for the live FT8 path: the internal/audio/capture device layer + the internal/ft8 slot scheduler and decoder.
ft8-decode-file command
Command ft8-decode-file decodes one or more 12 kHz mono 16-bit WAV files through the go-ft8 decode library and prints the decoded messages.
Command ft8-decode-file decodes one or more 12 kHz mono 16-bit WAV files through the go-ft8 decode library and prints the decoded messages.
ft8-psk-probe command
ft8-psk-probe is a standalone dev/test utility for the PSK Reporter upload path (internal/pskreporter).
ft8-psk-probe is a standalone dev/test utility for the PSK Reporter upload path (internal/pskreporter).
ft8-tx-probe command
ft8-tx-probe is a standalone smoke utility for the live FT8 transmit audio path (ADR 0029 step c): the ft8 encode→modulate chain (internal/ft8) feeding the internal/audio/playback output device.
ft8-tx-probe is a standalone smoke utility for the live FT8 transmit audio path (ADR 0029 step c): the ft8 encode→modulate chain (internal/ft8) feeding the internal/audio/playback output device.
loadgen command
Command loadgen drives the daemon's POST /v1/qso endpoint flat-out for stress / profiling.
Command loadgen drives the daemon's POST /v1/qso endpoint flat-out for stress / profiling.
maintreport command
smcloud command
smcloud is the SM Cloud backup/restore service (ADR 0040 / docs/v2-design/sm-cloud-p1.md): a small HTTP API (internal/cloud/server) over a Postgres store (internal/cloud/store) that the daemon's smcloud forwarder pushes QSOs to and the restore command pulls from.
smcloud is the SM Cloud backup/restore service (ADR 0040 / docs/v2-design/sm-cloud-p1.md): a small HTTP API (internal/cloud/server) over a Postgres store (internal/cloud/store) that the daemon's smcloud forwarder pushes QSOs to and the restore command pulls from.
smd command
Package main is the entry point for smd, the Station Manager daemon.
Package main is the entry point for smd, the Station Manager daemon.
Package frontend embeds the built Station Manager SPAs into the daemon binary.
Package frontend embeds the built Station Manager SPAs into the daemon binary.
internal module
adif
Package adif handles parsing and serialisation of ADIF (Amateur Data Interchange Format) data.
Package adif handles parsing and serialisation of ADIF (Amateur Data Interchange Format) data.
api
Package api holds the daemon's HTTP handler layer.
Package api holds the daemon's HTTP handler layer.
api/httpkit
Package httpkit holds the transport-level mechanics shared by every HTTP handler surface: the JSON error envelope, the response writers, and the size-capped body readers.
Package httpkit holds the transport-level mechanics shared by every HTTP handler surface: the JSON error envelope, the response writers, and the size-capped body readers.
audio
Package audio provides CGO-free audio file I/O and FFT primitives.
Package audio provides CGO-free audio file I/O and FFT primitives.
audio/capture
The malgo/miniaudio capture backend requires CGO.
The malgo/miniaudio capture backend requires CGO.
audio/playback
Package playback wraps the public-domain miniaudio C library (via the gen2brain/malgo binding) to provide real-time audio OUTPUT for the daemon's FT8 transmit path (ADR 0029 step c).
Package playback wraps the public-domain miniaudio C library (via the gen2brain/malgo binding) to provide real-time audio OUTPUT for the daemon's FT8 transmit path (ADR 0029 step c).
bridge
Package bridge is the daemon's serial/CAT bridge subsystem (per ADR 0013 + ADR 0019).
Package bridge is the daemon's serial/CAT bridge subsystem (per ADR 0013 + ADR 0019).
buildinfo
Package buildinfo carries build-time provenance the daemon reports at runtime.
Package buildinfo carries build-time provenance the daemon reports at runtime.
cat
cloud/evidencewire
Package evidencewire is the §5 evidence-sync wire contract, shared by the SMD-side sync client (internal/evidence) and the SMC-side ingest (internal/cloud).
Package evidencewire is the §5 evidence-sync wire contract, shared by the SMD-side sync client (internal/evidence) and the SMC-side ingest (internal/cloud).
cloud/reconcile
Package reconcile is the SHARED half of the SM Cloud reconcile protocol (ADR 0040 / docs/v2-design/sm-cloud-p1.md S4): the canonical summary hash over a logbook's live rows that both ends — the cloud service (internal/cloud/server) and the daemon's reconcile routine — must compute identically for "no drift" to read as equal hashes.
Package reconcile is the SHARED half of the SM Cloud reconcile protocol (ADR 0040 / docs/v2-design/sm-cloud-p1.md S4): the canonical summary hash over a logbook's live rows that both ends — the cloud service (internal/cloud/server) and the daemon's reconcile routine — must compute identically for "no drift" to read as equal hashes.
cloud/server
Package server is the SM Cloud HTTP API (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S2): the thin transport over internal/cloud/store that the daemon's smcloud forwarder (S3), reconcile routine (S4), and restore command (S5) talk to.
Package server is the SM Cloud HTTP API (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S2): the thin transport over internal/cloud/store that the daemon's smcloud forwarder (S3), reconcile routine (S4), and restore command (S5) talk to.
cloud/store
Package store is the smcloud Postgres persistence layer (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S1): the authoritative, UUID-keyed, retentive off-site QSO store behind the SM Cloud backup/restore service.
Package store is the smcloud Postgres persistence layer (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S1): the authoritative, UUID-keyed, retentive off-site QSO store behind the SM Cloud backup/restore service.
config
Package config holds the daemon's runtime configuration — the single config.json the operator hand-edits and the daemon reads/rewrites.
Package config holds the daemon's runtime configuration — the single config.json the operator hand-edits and the daemon reads/rewrites.
database/sqlite
Package sqlite is the SQLite persistence layer for the Station Manager daemon.
Package sqlite is the SQLite persistence layer for the Station Manager daemon.
database/txutil
Package txutil contains transaction cleanup shared by database-owning packages.
Package txutil contains transaction cleanup shared by database-owning packages.
email
Package email is the daemon's general-purpose mailer.
Package email is the daemon's general-purpose mailer.
enums/bands
Package bands defines the amateur radio band constants per the ADIF spec.
Package bands defines the amateur radio band constants per the ADIF spec.
enums/dxcc
Package dxcc maps a DXCC entity's primary prefix (as hamnut emits it in `primaryDXCCPrefix`) to the ADIF DXCC entity number.
Package dxcc maps a DXCC entity's primary prefix (as hamnut emits it in `primaryDXCCPrefix`) to the ADIF DXCC entity number.
enums/modes
Package modes defines the ADIF main-mode and sub-mode catalogue used by Station Manager.
Package modes defines the ADIF main-mode and sub-mode catalogue used by Station Manager.
enums/source
Package source defines the audit-trail source enum used by qso_history.
Package source defines the audit-trail source enum used by qso_history.
enums/upload
Package upload defines the online service identifiers for QSO forwarding.
Package upload defines the online service identifiers for QSO forwarding.
enums/upload/action
Package action defines the upload queue action enum (insert, update, delete).
Package action defines the upload queue action enum (insert, update, delete).
enums/upload/failure
Package failure defines the durable classification of a terminal upload failure — WHY a qso_upload row settled `failed`, in a form recovery code can act on without parsing provider-owned, redacted error text.
Package failure defines the durable classification of a terminal upload failure — WHY a qso_upload row settled `failed`, in a form recovery code can act on without parsing provider-owned, redacted error text.
enums/upload/origin
Package origin defines the upload queue provenance enum — what CAUSED a queue entry to exist.
Package origin defines the upload queue provenance enum — what CAUSED a queue entry to exist.
enums/upload/status
Package status defines the upload queue row lifecycle states.
Package status defines the upload queue row lifecycle states.
errors
Package errors is Station Manager's operation-tagged error package.
Package errors is Station Manager's operation-tagged error package.
events
Package events is the daemon's in-memory pub/sub hub for surfacing QSO lifecycle and forwarding-outcome transitions to subscribers (the HTTP `GET /v1/events` SSE handler, and any future in-process listeners).
Package events is the daemon's in-memory pub/sub hub for surfacing QSO lifecycle and forwarding-outcome transitions to subscribers (the HTTP `GET /v1/events` SSE handler, and any future in-process listeners).
evidence
Package evidence implements the local half of the FT8 evidence archive — design §4.1 in docs/v2-design/spot-network/spot-network-design.md (first capture slice, operator decisions 2026-08-10).
Package evidence implements the local half of the FT8 evidence archive — design §4.1 in docs/v2-design/spot-network/spot-network-design.md (first capture slice, operator decisions 2026-08-10).
forwarding
Package forwarding defines the contract between the daemon's ingest and worker layers and the concrete destination plugins (QRZ, ClubLog, LoTW, ...).
Package forwarding defines the contract between the daemon's ingest and worker layers and the concrete destination plugins (QRZ, ClubLog, LoTW, ...).
forwarding/clublog
Package clublog provides the Club Log forwarder.
Package clublog provides the Club Log forwarder.
forwarding/qrz
Package qrz provides the QRZ.com forwarder.
Package qrz provides the QRZ.com forwarder.
forwarding/qrzcq
Package qrzcq provides the QRZCQ logbook forwarder.
Package qrzcq provides the QRZCQ logbook forwarder.
forwarding/smcloud
Package smcloud provides the SM Cloud forwarder (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S3) — the daemon-side backup client.
Package smcloud provides the SM Cloud forwarder (ADR 0040 / docs/v2-design/sm-cloud-p1.md, step S3) — the daemon-side backup client.
forwarding/stub
Package stub provides a test-only Forwarder implementation whose outcome is configurable via the credentials blob.
Package stub provides a test-only Forwarder implementation whose outcome is configurable via the credentials blob.
forwarding/worker
Package worker implements the per-forwarder goroutine that drains the qso_upload queue for one destination.
Package worker implements the per-forwarder goroutine that drains the qso_upload queue for one destination.
fsperm
Package fsperm carries the one private-state filesystem policy for local artifacts that hold operator/QSO data (ST-6, docs/reviews/internal-security-trust-boundary-audit.md).
Package fsperm carries the one private-state filesystem policy for local artifacts that hold operator/QSO data (ST-6, docs/reviews/internal-security-trust-boundary-audit.md).
ft8
Package ft8 is Station Manager's FT8 subsystem: it wires the go-ft8 library into the daemon and owns everything from live receive audio through decode, occupancy analysis, the SSE wire, and — operator-initiated — transmit, sequencing, and completed-QSO assembly.
Package ft8 is Station Manager's FT8 subsystem: it wires the go-ft8 library into the daemon and owns everything from live receive audio through decode, occupancy analysis, the SSE wire, and — operator-initiated — transmit, sequencing, and completed-QSO assembly.
hardware
Package hardware enumerates the host's serial ports and audio devices so the config SPA can offer the operator a pick-list of friendly labels instead of hand-typed device paths / indices — the KISS rig-profile north star (ADR 0028): the operator never types `/dev/ttyUSB0` or an audio index.
Package hardware enumerates the host's serial ports and audio devices so the config SPA can offer the operator a pick-list of friendly labels instead of hand-typed device paths / indices — the KISS rig-profile north star (ADR 0028): the operator never types `/dev/ttyUSB0` or an audio index.
inhibit
Package inhibit asks the desktop to stop idling, blanking and suspending while the station is transmitting.
Package inhibit asks the desktop to stop idling, blanking and suspending while the station is transmitting.
iocdi
Package iocdi is a lightweight IoC/DI container that wires services together using struct tags and reflection.
Package iocdi is a lightweight IoC/DI container that wires services together using struct tags and reflection.
lifecycle
Package lifecycle provides the Supervisor — the owned per-service lifecycle primitive of ADR 0070 (docs/decisions/0070-daemon-lifecycle-graph.md, design in docs/v2-design/lifecycle.md).
Package lifecycle provides the Supervisor — the owned per-service lifecycle primitive of ADR 0070 (docs/decisions/0070-daemon-lifecycle-graph.md, design in docs/v2-design/lifecycle.md).
lifecycle/orchestrator
Package orchestrator drives the runtime lifecycle transitions of ADR 0070 (docs/decisions/0070-daemon-lifecycle-graph.md, design in docs/v2-design/lifecycle.md §4).
Package orchestrator drives the runtime lifecycle transitions of ADR 0070 (docs/decisions/0070-daemon-lifecycle-graph.md, design in docs/v2-design/lifecycle.md §4).
logging
Package logging provides a thin, concurrency-safe wrapper over rs/zerolog with a structured-first API, safe lifecycle management, and file rotation.
Package logging provides a thin, concurrency-safe wrapper over rs/zerolog with a structured-first API, safe lifecycle management, and file rotation.
lookup
Package lookup defines the provider abstraction for callsign and country enrichment, per ADR 0017.
Package lookup defines the provider abstraction for callsign and country enrichment, per ADR 0017.
lookup/hamnut
Package hamnut implements the lookup.CountryProvider interface against the Hamnut /v1/call-signs/prefixes upstream.
Package hamnut implements the lookup.CountryProvider interface against the Hamnut /v1/call-signs/prefixes upstream.
lookup/qrz
Package qrz implements lookup.CallsignProvider against the QRZ.com XML API.
Package qrz implements lookup.CallsignProvider against the QRZ.com XML API.
lookup/qrzcq
Package qrzcq implements callsign enrichment through QRZCQ.com's XML API.
Package qrzcq implements callsign enrichment through QRZCQ.com's XML API.
lookup/refresher
Package refresher provides the bounded async-refresh worker that implements lookup.AsyncRefresher per ADR 0017.
Package refresher provides the bounded async-refresh worker that implements lookup.AsyncRefresher per ADR 0017.
pskreporter
Package pskreporter uploads FT8 reception reports ("spots") to PSK Reporter (https://pskreporter.info/pskdev.html) as IPFIX (RFC 5101) UDP datagrams.
Package pskreporter uploads FT8 reception reports ("spots") to PSK Reporter (https://pskreporter.info/pskdev.html) as IPFIX (RFC 5101) UDP datagrams.
qsoservice
Package qsoservice is the daemon's domain service layer.
Package qsoservice is the daemon's domain service layer.
rigserial
Package rigserial composes a rig definition's serial block (cat.RigSerial) into a transport-layer serial.Config.
Package rigserial composes a rig definition's serial block (cat.RigSerial) into a transport-layer serial.Config.
safego
Package safego provides a panic-recovering wrapper for long-lived goroutines (forwarder workers, SSE publishers, future enrichment workers).
Package safego provides a panic-recovering wrapper for long-lived goroutines (forwarder workers, SSE publishers, future enrichment workers).
securehttp
Package securehttp centralises the credential-transport and redirect policy for every SM client that carries credentials to an upstream (ST-4, docs/reviews/internal-security-trust-boundary-audit.md).
Package securehttp centralises the credential-transport and redirect policy for every SM client that carries credentials to an upstream (ST-4, docs/reviews/internal-security-trust-boundary-audit.md).
serial
Package serial is a thin, concurrency-aware wrapper around go.bug.st/serial for HF transceiver CAT control.
Package serial is a thin, concurrency-aware wrapper around go.bug.st/serial for HF transceiver CAT control.
stationevents
Package stationevents is the vocabulary of the operator-facing Station Events store (W-0020, ADR 0076 shape): the closed (category, kind) pairs migration 0009 enforces, the severity set, and the typed FACTS the daemon's producing boundaries hand to the assembly-owned recorder.
Package stationevents is the vocabulary of the operator-facing Station Events store (W-0020, ADR 0076 shape): the closed (category, kind) pairs migration 0009 enforces, the severity set, and the typed FACTS the daemon's producing boundaries hand to the assembly-owned recorder.
stationevents/recorder
Package recorder is the assembly-owned, non-blocking, bounded recorder of Station Events (W-0020, ADR 0076 §4/§5): producers hand it typed facts through the bridge and FT8 observer seams, a worker goroutine converts each fact to a row and writes it in the store's own best-effort transaction.
Package recorder is the assembly-owned, non-blocking, bounded recorder of Station Events (W-0020, ADR 0076 §4/§5): producers hand it typed facts through the bridge and FT8 observer seams, a worker goroutine converts each fact to a row and writes it in the store's own best-effort transaction.
types
Package types defines the domain types shared across the entire project.
Package types defines the domain types shared across the entire project.
utils
Package utils provides small, pure utility functions shared across the project.
Package utils provides small, pure utility functions shared across the project.
database module
enums module
listeners module
maidenhead module
Package manual embeds the built operator manual into the daemon binary.
Package manual embeds the built operator manual into the daemon binary.
pkg
enums module
errors module
utils module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL