Getting Started with Gemini

In this post we'll take a quick dip into the gemini protocol, the .gmi file format, how you can access gemini:// sites and host your own gemini server.

Gemini is a Protocol

Compared to HTTP and HTML which are built to accomodate a great number of media types like images, video, audio and lots of interactive interfaces with JavaScript, Gemini (almost) only supports text and hyperlinks to more texts. How this text is rendered is up to the client (or gemini browser), so you can't ship a stylesheet either.

You can't decide that your text will be yellow or green, it's out of your hands. You focus on content in this privacy and bandwidth aware space. The default port for gemini servers is 1965, which I would recommend running your gemini site (capsule) on.

Gemini Clients / Browsers

There are a few "browsers" for gemini, funnily enough more for the terminal than ones with a GUI, must be a minimalist thing, for Gemini. If you want something that works cross platform, give Lagrange a try. It formats stuff in a bearable way, except for code blocks with long lines. They all look like they're from the 90s, don't be too picky.

In practise, you're on a page in your gemini browser, for example gemini://geminispace.info/ you could encounter a link to https://jonathanmh.com and it would open that in Firefox. So both clients can co-exist like magnet links or spotify playlists are opened in another application as well.

The GMI/Gemtext file format

If you're familiar with markdown, gemtext will look very familiar, some key differences are:

  • Links look like this => https://example.com A cool website
  • Links are always rendered on a new line
  • There are only three headline levels: h1, h2, h3 equivalents

If you want to mirror existing markdown content there's a great rust command line utility md2gemtext which can convert your existing content. Be aware: it's very lenient, which means that it will try to parse and convert frontmatter blocks instead of ignoring them.

Since I am using MDX on this blog I stole a few blog posts which I'll use as a content example for the server in a bit:

# Nginx 410 maps and Custom Error Page

In order to bulk retire multiple URLs of your web site at once

[...]

The full code can be found in the repository: https://github.com/JonathanMH/nginx-410-map
=> https://github.com/JonathanMH/nginx-410-map https://github.com/JonathanMH/nginx-410-map

I can convert my mdx posts relatively painlessly with the following script, but I'll need to tweak it to extract the frontmatter to a title and remove the rest:

#!/bin/bash

ls content
for document in content/*.mdx; do
	echo processing $document
	md2gemtext $document content/$(basename $document .mdx).gmi
done

Gemini Servers

Since Gemini is another protocol than HTTP it will probably not run on your regular webserver like apache or nginx. Instead there are a few different community projects to serve a gemini site.

A very simple to run server I have found is agate, which you can get via cargo: cargo install agate

Afterwards you can run a server with

agate --content content/ --addr "[::]:1965" --addr 0.0.0.0:1965 --hostname localhost --lang en-GB

where you probably should have some example gmi file in the content directory relative to where you run the command.

If you want to list your content files on your index page, you can create an empty file called .directory-listing-ok in the same directory as your content.

which will make your agate server directory look like this:

├── content
│   ├── .directory-listing-ok
│   ├── next-js-rss-with-static-site-generation.gmi
│   ├── nginx-410-maps-and-custom-error-page.gmi
│   └── processing-files-with-docker-volumes.gmi

Now if you run the server, you should see something like this:

lagrange local server

Each of the links can be clicked and it'll show the article as well:

lagrange single page

Note that Lagrange also styles the first article of the page with slight emphasis and the inline link from the markdown source is moved below the paragraph.

Should you want to run a docker container with agate, you can use this as a starting point with an alpine linux dockerfile:

FROM alpine:3.16

RUN apk --no-cache add gcc musl-dev \
	&& apk --no-cache add rust cargo

# create content directory
RUN mkdir -p /var/app/content

# copy your content directory
COPY content/ /var/app/content
RUN cargo install agate

# run your agate server in /var/app
WORKDIR /var/app
CMD ["agate", "--content path/to/content/", "--addr [::]:1965", "--addr 0.0.0.0:1965", "--lang en-GB" ]

If you have any great recommendations for blogs or content in the gemini-verse, let me know!

Tagged with: #gemini #gemtext #agate #lagrange

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!