A proxy server that translates from gemini servers to gopher clients.
  • Common Lisp 100%
Find a file
Jason McBrayer b50e099cd5 Normalize unicode characters before sending to gopher
This is intended to help with the case where the UTF-8 text from
Gemini maps cleanly to ISO Latin-1, and the Gopher client supports
Latin-1. Slightly experimental.
2023-03-04 11:28:02 -05:00
.gitignore Substantially working; have to fix translated links 2023-01-01 14:23:09 -05:00
cosmarmot.asd Normalize unicode characters before sending to gopher 2023-03-04 11:28:02 -05:00
cosmarmot.ros Add a startup script 2023-02-17 12:32:27 -05:00
cosmarmot.service Add systemd service file 2023-02-24 08:50:39 -05:00
LICENSE.md Add copyright notices and license. 2023-02-17 20:20:45 -05:00
package.lisp Some automatic formatting 2023-02-21 22:18:55 -05:00
pages.lisp Some automatic formatting 2023-02-21 22:18:55 -05:00
README.org Forgot a step in README 2023-02-25 15:39:37 -05:00
server.lisp Normalize unicode characters before sending to gopher 2023-03-04 11:28:02 -05:00
util.lisp Improve logging and error handling 2023-02-21 22:22:40 -05:00

Cosmarmot - a Gopher to Gemini gateway

Cosmarmot is a Gemini gateway for Gopher clients, written in Common Lisp. It runs as a server and accepts Gopher requests, translating them to Gemini requests and retrieving them. It renders gemtext pages as gophermaps, which is kind of a sin, but usually works pretty well.

Motivation

Although Gemini's simple protocol and easily to parse plain text markup seem naturally suited to retrocomputing, many older computers are unable to cope with TLS for one reason or another: either they are actually too slow to handle the encryption, or they are not capable of running an operating system with up-to-date cryptography libraries. However, most, if not all, of these machines are capable of running a Gopher client. Cosmarmot can be run on any TLS-capable computer (such as an old laptop or a single board computer) to fetch Gemini content for Gopher clients on its local network.

Usage

Installation

These installation instructions will use Roswell to set up and run Cosmarmot. You can definitely set it up without Roswell, if you have a basic familiarity with asdf and quicklisp.

  1. If you are going to be running a Cosmarmot server reachable from the internet, you should create a user specifically for running Cosmarmot, with no access to anywhere other than its home directory.
  2. Install Roswell system-wide, following the instructions on the Roswell website.
  3. As the user that will be running Cosmarmot, run 'ros setup' to set up your Roswell environment and Common Lisp compiler and runtime.
  4. As the user that will be running Cosmarmot, clone the 'gemtext' and 'cl-gemini-client' projects from this site into .roswell/local-projects/. You can clone them to someplace else that ASDF knows about, but that's the easiest. You should also probably put cosmarmot there and run it from there.
  5. Edit the 'cosmarmot.ros' script, changing the :host and :port keyword arguments to the values you need. The :host keyword will be used both for picking an address for the server to bind to, and to construct proxied URLs, so if you are opening your server up to the internet, it needs to be a resolvable hostname or a public IP address. If you're not running it as root (which you shouldn't be), you will need to run it on a port other than 70 (I recommend 7070), or grant that user capabilities allowing them to listen on low ports. That's beyond the scope of this README.

Running the server

There is a systemd service file provided, which starts Cosmarmot from the Roswell script, running as the user you installed it as - you will have to customize the user and paths. If your system does not use systemd and you want to run Cosmarmot as a system service, you'll have to write an init script appropriate for your system. The systemd service file is set up to enable running Cosmarmot on port 70 as a regular user, and also to disallow most filesystem access. If you are running it from a user home directory, you'll have to relax the protections on /home.

If you are simply running Cosmarmot as a single user for your own use, you can just run the 'cosmarmot.ros' script from a terminal, or screen, or tmux.

The first time you run Cosmarmot, it will download its dependencies using quicklisp, so might start up a bit slowly.

Using with a Gopher client

The Gopher client does not need to support proxies; Gemini pages can be fetched with the hostname or IP address of the Cosmarmot server, with a specially-constructed selector.

In URL form, you would use:

gopher://cosmarmot-host:port/1/gemini-host/gemini-path

In Gopher clients that don't support URLs, you need to use cosmarmot-host as the host, cosmarmot-port as the port, 1 as the item type, and /gemini-host/gemini-path as the selector. You may need to enter this in a dialog, or write a gophermap with a line with those elements.

Internally, Cosmarmot will rewrite Gemini URLs into Gopher items that go through Cosmarmot. Binary files and text files that are not recognized as gemtext will be passed through unchanged (though future support for downscaling and converting images is planned).

Limitations

Content-type guessing

Gophermaps need to be able to tell the client in advance what kind of media to expect (text file, subdirectory, image, etc.). This makes sense for Gopher, because Gophermaps are generally either written by hand (and the author knows what's on the other side of the link), or are indices of local directories (and the server can probe or guess their file types). A gemtext file does not have that information available, however. Like with HTTP, links don't have file type information; Gemini requests return the MIME type of the file as part of their response.

This means that when constructing a gophermap from a gemtext page, it is not always possible to guess the right item type for a particular line. If the URL of the gemtext link ends in a filename with an extension, Cosmarmot will guess based on that. If it looks like a directory, it will be handled as gemtext. For now, Cosmarmot will also handle anything it can't look up as text/gemini (on the basis that links to directories in Gemini URLs may not end in "/"), but this may change in the future.

In practice, I have not noticed any problems with this, but I'm sure someone will run into it.

Cosmarmot does not yet use every well-known item type (like Mac BinHex or uuencoded binary) even when they're appropriate. This is planned to be added.

Error handling

Gopher has no means to signal an error, which makes it somewhat harder to report Gemini errors back to a Gopher client. For type 0 and 1 items, Cosmarmot could report Gemini errors as a constructed text file or gophermap, but it does not yet do this.

Unicode

Most Gemini pages are in UTF-8. Many Gopher clients do not understand UTF-8. For pages in a Latin-alphabet language, this is rarely that significant, because UTF-8 is a superset of the ISO-8859-1 Latin-1 codepage. Some punctuation and emoji will show up as garbage, and that's fine, and may remind you of the bad old days where every script used its own codepage, and sometimes more than one, and you had to guess which one was correct. It's a bigger problem for, e.g., Cyrillic or CJK text. If your client supports UTF-8 (either a modern client, or a very naive old client on a modern terminal emulator), then you should be fine.

This is somewhat solvable for older clients: the Gemini response should include the main language of the page, and it would be possible to convert the document to an appropriate legacy codepage. But this will require some refactoring to implement, and also implementing it will probably ruin things for people using modern Gopher clients.

Input requests

Currently, Cosmarmot does not handle 1x responses (input requests) from Gemini. Handling them is complicated by the same impedance mismatch between Gopher item types and Gemini responses that affects content types. In the future, Cosmarmot will probably handle 1x responses by creating an interstitial Gopher page with the prompt and a type 7 item.

Client issues

Very old gopher clients

Some gopher clients are old enough not to support every common item type. In particular, some are even old enough not to support the "I" (informational) item type, which Cosmarmot uses to represent plain, preformatted, or quoted lines in gemtext. Such a client will only see the links from the gemtext. In the future, I plan to put a link to the raw original gemtext in each rendered gophermap, for the sake of such clients.

TurboGopher

The latest version of TurboGopher seems to ignore port numbers in Gopher selectors. If you're not running Cosmarmot on port 70 (I've been running it on 7070 in testing), you'll have to edit every link to make them work.

Lynx

Lynx does not seem to understand HTML items with a selector of the form

URL: http://foo.example.org/bar.html