pat: a cross-platform Winlink client for amateur radio email
A cross-platform Winlink client written in Go
At a glance
- What is it?
- Pat is a Go implementation of a Winlink 2000 client with a command line interface and a mobile-friendly web interface, speaking AX.25, telnet, PACTOR and ARDOP over both Winmor gateways and peer to peer links, and it is also the sandbox application for the wl2k-go library it is built on. It suits amateur radio operators who want to send and receive Winlink mail from a laptop, a Raspberry Pi or a phone, and the practical constraint is that the radio side needs a modem and an antenna whatever the client is written in.
- Who is it for?
- Adopt Pat if you operate amateur radio and want a Winlink client that runs on Linux, macOS, Windows or Android, with both a terminal interface and a browser interface over the same core. Do not adopt it if you are not a licensed operator on the amateur bands, since every mode it speaks is a radio mode, and check first which transport you actually have, because the Winmor, PACTOR, ARDOP and AX.25 paths each need different hardware and a gateway for the Winmor ones.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 31 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A Winlink client that is also a library test bed
Pat is a cross platform Winlink client with basic messaging capabilities, and the README is clear that it has a second job.
It is the primary sandbox and prototype application for the wl2k-go project, which is the library it is built on, and it provides both a command line interface and a responsive, mobile-friendly web interface.
That framing explains the shape of the repository. The top level has `api/`, `app/`, `cfg/`, `cli/`, `internal/`, `main.go` and `web/`, so the protocol work lives in wl2k-go and this repository is the application on top of it. It also explains the release history: v1.0.0 on 2026-04-19, after v0.19.2 on 2025-12-20 and v0.19.1 on 2025-09-13, so the project reached a 1.0 after a long run of 0.x releases.
The project is written in Go, MIT licensed, and it is mainly developed for Linux, but is also known to run on OS X, Windows and Android. The last push was on 2026-09-22 and the homepage is getpat.io. The topic tags name the protocols and platforms it cares about: ardop, ax25, ham-radio, pactor, vara, winlink.
The go.mod file requires Go 1.24.0 and declares a `tool` directive for golang.org/x/vuln/cmd/govulncheck, which is a recent addition to Go's module tooling for pinning a tool dependency. The dependency list includes wl2k-go at v1.0.1, gorilla/mux and gorilla/websocket for the web side, cronexpr for the scheduled command syntax, liner for the terminal interface, pflag for the git style command line, and two protocol implementations from other authors, `github.com/n8jja/Pat-Vara` for VARA and `github.com/harenber/ptc-go/v2` for PACTOR.
What a session actually looks like
The example in the README is a complete session transcript, and it shows the interaction model better than any description would.
martinhpedersen@duo:~$ pat interactive
> listen winmor,telnet-p2p,ax25
2015/02/03 10:33:10 Listening for incoming traffic (winmor,telnet-p2p,ax25)...
> connect winmor:///LA3F
2015/02/03 10:34:28 Connecting to winmor:LA3F...
2015/02/03 10:34:33 Connected to WINMOR:LA3F
RMS Trimode 1.3.3.0 Follo.SE Oslo. Pactor & Winmor Hybrid Gateway
LA5NTA has 117 minutes remaining with LA3F
[WL2K-2.8.4.8-B2FWIHJM$]
Wien CMS via LA3F >
>FF
FC EM FOYNU8AKXX59 260 221 0
F> 68
1 proposal(s) received
Accepting FOYNU8AKXX59
Receiving [//WL2K test til linux] [offset 0]
>FF
FQ
Waiting for remote node to close the connection...
> _Several things fall out of that. The `interactive` subcommand gives a prompt where you type `listen` with a comma separated list of modes and then `connect` with a gateway path in the form `winmor:///LA3F`, and the gateway announces itself, in this case a hybrid Pactor and Winmor gateway run by RMS Trimode. The session shows a mailbox interaction, an FF command that appears to be the transfer command, a proposal, an acceptance, and the transfer of a message with an offset.
The `listen` command taking several modes at once is the feature the list of supported protocols points at. AX.25, telnet, PACTOR and ARDOP are all supported, and Pat can listen for P2P connections using multiple modes concurrently. For a ham radio operator that is the difference between a client that only works when the software happens to match the other end's software, and one that can hear a station on any of the common digital modes.
The transcript also shows the metadata you get. There is a protocol version string, the gateway name and its location, and a note of how many minutes remain with the peer, which for a gateway with a usage limit is information you need before you start a long transfer.
Radio control, location reports and scheduled commands
The feature list is a mix of client features and radio features, and the radio-facing ones are what make it more than a mail client.
Rig control uses hamlib, which is the standard library for talking to amateur radio transceivers, so Pat can set the frequency and mode and key the radio as part of a session. That is the feature that makes a digital mode client usable rather than a toy.
Position reports can be posted with a location from local GPS, from browser location or from manual entry. Each of those three sources is a different deployment: a Pi with a GPS receiver attached, a browser-based client asking the phone for its position, or an operator typing a grid square. The three-way choice is what makes the same client work on a fixed station, a mobile one and a portable one.
Scheduled commands use a CRON-like syntax, and the examples given are QSY and connect. QSY is the amateur radio term for changing frequency, so the intent is clear: you can have the rig follow a schedule, moving to a different band or to a different remote node at a set time, without anyone at the keyboard.
Then the client features. There is a message composer and reader giving basic mailbox functionality, and image attachments are auto-shrunk, which is a practical necessity rather than a nicety given the size limits of the protocols involved.
The web side is a built-in http-server with a mobile friendly interface, and the command line is described as git style, which in this context means a subcommand per action with a consistent flag set, the same convention the pflag dependency and the command names in the transcript follow.
The experimental feature worth knowing about is gzip message compression, added as an experimental B2F extension. It is implemented as a backwards compatible alternative to the ancient LZHUF compression, it is enabled by default, and sessions between two Pat nodes, or other software supporting this B2F extension, will use gzip compression when transferring messages. The backwards compatibility is the point, since the ancient LZHUF scheme is what the other end of the link is likely to understand.
Running it in a container and the packaging around it
The Dockerfile and the compose file show how the project expects to be deployed for the web interface, and there is more packaging than most projects of this kind carry.
The image is a two stage build on the Alpine golang base. The builder stage installs git and the CA certificates, copies the module files and runs `go mod download`, then copies the source and builds the binary. The runtime stage is `FROM scratch`, meaning no operating system at all, with the CA certificates copied in from the builder so TLS still works, and the binary placed at `/bin/pat`.
The runtime configuration is careful about where state lives. The image sets `XDG_CONFIG_HOME`, `XDG_DATA_HOME` and `XDG_STATE_HOME` all to `/app`, and the compose file mounts a `./docker-data` directory there, so the configuration and the mailbox data survive a container restart. The listener address is set with `PAT_HTTPADDR=:8080` and the port is exposed, and the compose file maps 8080 to the host. The entrypoint is `/bin/pat` and the default command is `http`, so the container runs the web interface rather than the terminal client.
The container runs as `USER 65534:65534`, the nobody user, and there is a comment in the Dockerfile about making sure there is a `/tmp` directory with the correct permissions of 01777, which is the standard trick for a scratch image where the base filesystem has no such directory.
Outside the container, the repository has a `debian/` directory for a system package, an `osx/` directory for the macOS build, a `man/` directory for the manual pages, a `share/` directory, and a `make.bash` build script. There are also `sighup_unix.go` and `sighup_windows.go` at the root, which is how the process reloads its configuration when it is sent a hangup signal, and the difference between the two files is exactly the platform difference in how that signal is delivered.
The docker-compose file is minimal by comparison, a single service built from the Dockerfile with the data volume and the port, and the image name `la5nta/pat`.
The upstream projects Pat borrows from
The acknowledgements section is unusually specific, and it is the clearest indication of how much of this is other people's work.
The JNOS developers are thanked for the properly maintained lzhuf implementation, as well as the original author Haruyasu Yoshizaki. The paclink-unix team, Nicholas S. Castellano N2QZ and others, are thanked as the reference implementation. The Amateur Radio Safety Foundation, Inc. is credited for the Winlink 2000 project. And F6FBB Jean-Paul ROUBELAT is credited for the FBB forwarding protocol.
The last one is the most interesting, because FBB is the mechanism by which a packet station relays a radio message to the internet-connected side, and Pat implements the forwarding protocol rather than inventing a transport of its own. The same pattern holds for the compression: the ancient LZHUF scheme that gzip is offered as an alternative to is JNOS's, and the Winlink 2000 protocol itself is the Amateur Radio Safety Foundation's work, with the Windows client and the reference implementation from paclink-unix and the protocol documentation from the JNOS project.
So Pat's own contribution is the Go implementation and the client experience: the command line, the web interface, the cross-platform support and the concurrency across multiple listening modes.
The disclaimer at the end is the kind of thing a careful project states precisely. Pat and wl2k-go are not affiliated with The Winlink Development Team nor the Winlink 2000 project, and the link to winlink.org is given so you can find the actual organisation.
The copyright is held by Martin Hebnes Pedersen LA5NTA, who is also the author of the wl2k-go library, and the contributors list is given with call signs, which is the convention in amateur radio, with eighteen names including AB3E, DL1THM, HB9GPA, KI7RMJ, LA3QMA, N2YGK, VE7GNU and WY2K. The project is MIT licensed, and the README carries a Liberapay patrons badge, so it takes direct donations alongside the GPL-style patreon page.
Editorial conclusion
Adopt Pat if you operate amateur radio and want a Winlink client that runs on Linux, macOS, Windows or Android, with both a terminal interface and a browser interface over the same core. Do not adopt it if you are not a licensed operator on the amateur bands, since every mode it speaks is a radio mode, and check first which transport you actually have, because the Winmor, PACTOR, ARDOP and AX.25 paths each need different hardware and a gateway for the Winmor ones.
Frequently asked questions
What is the Pat Winlink client?
It is a cross platform Winlink client with basic messaging capabilities, written in Go, that provides both a command line interface and a responsive mobile-friendly web interface. It is the primary sandbox and prototype application for the wl2k-go library project.
Which radio modes and transports does Pat support?
AX.25, telnet, PACTOR and ARDOP support, over Winmor gateways and telnet peer to peer links, and it can listen for P2P connections using multiple modes concurrently. Rig control uses hamlib, and scheduled commands use a CRON-like syntax for actions such as QSY or connect.
How do I run Pat as a web server in Docker?
The image exposes port 8080 with PAT_HTTPADDR set to :8080, runs as the nobody user and stores state under /app through the XDG environment variables, so the compose file mounts ./docker-data there and maps 8080 to the host. The container's default command is http, which starts the web interface.
What is the gzip experiment in Pat?
Gzip message compression has been added as an experimental B2F extension, implemented as a backwards compatible alternative to the ancient LZHUF compression. It is enabled by default, and sessions between two Pat nodes, or other software supporting the extension, will use gzip compression when transferring messages.
What projects does Pat build on?
The JNOS developers for the maintained lzhuf implementation and Haruyasu Yoshizaki as the original author, the paclink-unix team as the reference implementation, the Amateur Radio Safety Foundation for the Winlink 2000 project, and F6FBB Jean-Paul ROUBELAT for the FBB forwarding protocol. Pat and wl2k-go are not affiliated with the Winlink Development Team.
Is Pat still being maintained?
The repository is not archived and the last push was on 2026-09-22. The release history shows v1.0.0 from 2026-04-19 after v0.19.2 from 2025-12-20, and the project is MIT licensed with the homepage at getpat.io.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/la5nta-pat)Was this useful?