Setting Up a Gemini Capsule

Sometimes, while reading news in my RSS feeds, I stumble upon an article that awakens in me an irresistible desire to get my hands dirty and start working on something I previously didn’t know, to see where that leads me.

Recently, I read an article about the Gemini Protocol, and I decided to dive in and create a Gemini capsule on my own.

What is Gemini?

Gemini is an internet protocol designed in 2019, offering a contemporary twist on the early days of the online world. It represents a simplified alternative to other modern web protocols, omitting trackers, scripting, and elaborate styling in favor of an easily parsed markup language.

Moreover, Gemini prioritizes security through built-in encryption and emphasizes privacy, again, no trackers involved.

I view Gemini as a cool spot where I enjoy interacting with friendly folks and simply relaxing.

However, it is not intended to replace the web as we know it, I quote the FAQ on the official web site

Gemini is not intended to replace either Gopher or the web, but to co-exist peacefully alongside them as one more option which people can freely choose to use if it suits them

The full specification of the protocol isn’t quite as extensive. I mean, the FAQ I mentioned earlier is actually longer than this specification! I gave it a quick read, but at this point, I don’t feel quite up to implementing it from scratch. It would be a really cool project though.

I’ll leave you to explore the official website yourself if you’re interested; there are numerous resources available there.

So, let’s make a capsule?

A “capsule” is the term used in Gemini to refer to a “Gemini Website,” or more precisely, a “Gemini set of pages.”

Firstly, we need to familiarize ourselves with the “gemtext” markup language, which bears some resemblance to Markdown, although there are notable differences. For instance, in a gemtext file, you cannot create an inline link as you would in Markdown; instead, you must write a new line containing only the link itself.

For my capsule, I decide to write a Python script to transform the MarkDown source file of Hexo into .gmi file for Gemini.

I use the md2gemini library to handle a significant portion of the task, and some classic string manipulations for the specific file format of Hexo, such as header information.

The script looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import os
from pathlib import Path
from md2gemini import md2gemini

# ===== UPDATE THESE VALUES ===== #
host = "gemini://localhost/"
author = "blchrd"
# =============================== #

root_gmi = "public_gmi"
blog_posts = []


def create_index_page(blog_post_list):
print(f"Create index links list in {root_gmi}/index.gmi")
blog_post_links = ""
for blog_post in blog_post_list:
blog_post_links += f"=> {blog_post_list}"

gemini_index = blog_post_links

with open(f"{root_gmi}/index.gmi",'w') as wf:
wf.write(gemini_index)


def transform_markdown_into_gemtext(source, file_dest, blog_post=True):
with open(source, "r") as f:
md_content = f.read()
gemini = md2gemini(md_content, links="at-end", plain=True)
with open(dest, 'w') as wf:
wf.write(gemini)
blog_posts.append(f'{host}{file_dest} {formated_date}: {title}')


Path(root_gmi).mkdir(parents=True, exist_ok=True)
transform_markdown_into_gemtext('source/about/index.md', 'about.gmi', blog_post=False)
for root, dirs, files in os.walk('source/_posts'):
for file in files:
transform_markdown_into_gemtext(f'{root}/{file}', file.replace('.md', '.gmi'))

blog_posts.sort(reverse=True)
create_index_page(blog_posts)

This is a stripped-down version of it. The full script I use is on this repository. Now we have all our gmi files, ready to be serve.

Files ok, now the server

There are numerous server software options available for Gemini, developed in various programming languages—I’m not sure if I mentioned this, but it could be a really fun project. So, you have a variety of choices. Among these, I personally opted for the one I had heard about the most, Agate (written in Rust). Once you’ve downloaded the binary, a straightforward command in the terminal and you’re all set.

1
agate --content "path/to/public_gmi" --addr 127.0.0.1:1965 --lang en-US --hostname localhost`

And voilà, your server is up and running locally. All that’s required at this point is a Gemini client to test whether everything is displaying correctly.

I never really browse the geminispace before starting to play with it. So, I went to the software page of Gemini’s website, and look at all the client, same as the server, a lot of it, in a diverse language and… ok, I put in my project idea to develop client and maybe server software for Gemini, that look so cool.

In the end, I go for Amfora, a terminal client for Gemini, who work like a charm.

And finally, I can test it, and it looks good (the Gemini client is on the left terminal, and on the right is the server):

Screenshot of terminal

Let’s go serve this to the world

I began by installing Agate on my server, and then attempted to serve it using the same command line as before:

1
agate --content "/path/to/public_gmi" --addr 0.0.0.0:1965 --lang en-US --hostname blchrd.eu

That didn’t work; it displayed ‘Permission denied’ for certificate creation. It attempted to create in the default directory. I attempted the magical word ‘sudo’, but then it responded with ‘agate: command not found’.

So, I forced the certificate path to one with the appropriate permissions:

1
agate --content "/path/to/public_gmi" --addr 0.0.0.0:1965 --lang en-US --hostname blchrd.eu --certs /path/to/certificates

And it’s working! You can now access my gemlog (the term for “blog” in the geminispace) entries using the Gemini protocol at the address gemini://blchrd.eu.

Conclusion

I won’t deny it, the most enjoyable aspect was undoubtedly writing the script. It also constituted a significant part of the process, as I utilized established Gemini server and client tools for this endeavor.

However, I take pleasure in delving into the technical details, and I might eventually write my own Gemini client and server. I’ve been exploring the code of various implementations, including the ones I’ve been using, and I’m keen to dive into this further.