RadioZ now on Gopher
Published on: May 30, 2026
Gopher is an old protocol for sure, but being old, it’s also very minimal, making it perfect for browsing
on low-bandwidth connections.
However, it doesn’t use HTML, instead, it uses its own, also very minimal, format called Gophermap.
Gophermap structure
!This is a title
sA link to an Audio file TAB /audio.mp3 TAB gopher.radioz.org TAB 70
||---------------------| |--------| |---------------| |--|
| Visible text Destination path Destination host Port
|
Tells the client what file type to expect - a sound file
iOrdinarry text (not a link) TAB /this TAB can.be.anything TAB 9258
||-------------------------| |------------------------------------|
| Visible text Ignored, but still required
|
Denotes plain text, as oppose to a link
As you can see, this is quite different from HTML. Even if I converted all of my markdown posts to Gophermaps
there was no way I was going to maintain two versions of every article!
Therefore, I decided to make a program that will read existing posts, automatically convert them to
Gophermaps, generate the Latest and Related sections, add the date of publication and everything else.
My goal was to have the same level of automation for Gopher as I have for the Web, so that publishing to Gopher doesn’t become a chore.
Furthermore, I wanted to be able to substitute <script> tags, which I often use to integrate various calculators to posts,
with server-side PHP scripts that will provide the same kind of functionality on Gopher.
This all led me to create a complex stack of scripts to achieve exactly what I wanted. Some of them are written by me, some of them are not.
Markdown —> Gophermap
I only found one program that can convert markdown directly to a gophermap in a reliable way, however,
I wasn’t satisfied with how it handled converting inline links to footnote links. After many more searches that yielded nothing,
an idea struck: Okay, there are no good markdown to gophermap converters, but what about markdown to gemtext converters?
For those unfamiliar, Gemtext is another "hypertext" format. It’s similar to gophermaps in terms of features.
(the main difference being that Gemtext also supports subtitles and code blocks).
I found exactly what I wanted, md2gemini nicely converted inline links to footnotes.
Now I just had to write a simple script that can convert gemtext to gophermap.
This is a lot simpler than writing a script that deals with markdown directly.
I completed the task in a couple of hours.
If you want to use the script yourself here it is. It’s built so that you can pipe output from
md2gemini directly to it:
md2gemini -l paragraph -f -a -p my-post.md | ./gemtext2gophermap - gophermap
As per the readme, this script also handles injecting gopher specific content into the final gophermap.
Generating Latest and Related sections
Now that I figured out how to convert markdown posts to gophermaps, it was time for making another script that would put all of that together.
This was hard because there is no static-site generator for Gopher.
It also didn’t help that I chose to write the script in Bash for some, yet undiscovered, reason.
A list of all the things the script had to do:
- Extract metadata from the header of a markdown file
- Put the title, author, date of publication, date of last update and a list of other languages at the beginning of every post
- Convert paths that
Astronormally handles (like../../assets) to the ones Gopher can handle - Figure out which posts should have a link in the main menu
- Generate
Latest,RelatedandFeaturedsections at the end of every post
If you want to take a look at the strange regexes I used to parse markdown and yaml,
as well as everything else mentioned in the list the script is here.
At the end, I used a Makefile to streamline the process of installing md2gemini and gemtext2gophermap,
as well as to run the aforementioned script on every post.
Supporting multiple domains
I still do not have the ability to BGP peer on Hamnet so I’m stuck with addresses they give me through vpn.hamnet.network.
This means that I need two separate domains,
one pointing to Internet addresses and another one pointing to Hamnet addresses of the server.
Doing this on the Web is easy, you just have a link like /about and the browser will preserve the domain. On Gopher, however,
every link needs to specify a domain and a port, even if those do not change.
Luckily, my server of choice automatically adds the domain and port at the end of every line.
This means that I can leave it up to the server to decide which domain a link will point to.
But how can the server know which domain a client uses? There is no Host header in Gopher. - Simple,
not only are the domains different, but IP addresses are as well, so I can have one server instance
configured for the Internet domain and bound to the Internet IPs
and another instance configured for the Hamnet domain and bound to Hamnet IPs.
Of course, this is only possible if your server has multiple IP addresses, if it doesn’t, well, you’re out of luck. (Yet another reason to push for IPv6)
Accessing RadioZ via Gopher
To access Gopher, you need a Gopher client. Common ones include:
- Lagrange (Win/Mac/Linux/Android/iOS)
- Kristall (Win/Mac/Linux/BSD)
- Gopher Browser (Windows)
- Gophie (Win/Mac/Linux)
- Bombadillo (Linux/BSD) (no-GUI)
You can also use a Gopher to Web proxy.
In any case, these are the URLs:
gopher://gopher.radioz.org (for the Internet)
gopher://hamnet.gopher.radioz.org (for Hamnet)