Last updated:2023-08-29
For newcomers, the most frequently asked questions are, "how do I make a gemlog?" and "how do I make my own capsule?" There are a bunch of guides around, but this one's mine so that I can point folks to it when they ask.
I'm not a professional but I've researched all the items below, so feel free to provide me feedback via email.
----------------------------------
There are three general options:
Personally I've done all three but mostly the first two. I bought a Raspberry Pi 4 and hooked it up to my home network. It's a bit trickier to get your DNS to point to it but it's absolutely doable. I then migrated over to a Digital Ocean $6 USD VPS just so I wasn't opening my home network to some kind of attack vector I didn't know about. I've had no complaints with them thus far and $6 USD /mo is worth it.
I also have a capsule on Tilde.Team, but it only points to my primary capsule on my VPS. Points to consider about using a hosting service:
If using a VPS, I believe most come with static IPs.
If using your own hardware where your home IP can change, you can use a dynamic dns service (e.g., dyndns or duckdns), or run a script that updates your domain name (see section 1.7.1).
For my VPS I picked Ubuntu, but feel free to install whatever you're comfortable with; people serve from all sorts of OSs (BSD, etc.).
I'm no security professional but I did do my research.
Generally, I:
[1] Basic security steps (for Pi but works for most distros)
[2] SSH Key login setup on Digital Ocean
NOTE: Make sure you don't lock yourself out of your server with fail2ban! Luckily I did this on my RPI and was able to log in locally to fix it.
Personally, I hand-craft my gemini capsule because I don't update all that often so it's not that much maintenance; however, there a quite a few Static Site Generators (SSGs) out there for Gemini. I've known geminauts to retrofit HTML SSGs to output both gemtext and HTML so they can mirror their blogs and gemlogs. I've not tried this.
If you choose to hand-code, simply make a directory in your unprivileged user's $HOME directory and start planning out the structure. Oftentimes the server will dictate some of this (i.e., CGI vs static content), so it's best to consult the instructions there before diving into this part.
See Section 2.1 "Creating Your First Gemlog" below.
I haven't tried any of these, but here are some SSGs for Gemini:
gssg - creates gemlogs, index pages, and atom feeds
Picking a good server can be daunting, especially if it's your first one. I suggest using an easy static server (i.e., doesn't support dynamic content via CGI) like Agate to get started.
➢ Note: The guide sets the time limit of the self-signed certificate to expire in 1 year, I suggest 5+, or even 100. This keeps you from having to generate a new one all the time and folks wondering if something happened with your capsule. (See Section 1.5.2 for more on generating server certificates)
I've also tried Molly Brown (by @solderpunk) and am currently using GmCapsule (by @skyjake). I like GmCapsule the best thus far as it also includes Titan support
Gemini uses TLS to make secure connections between the client and server. This is mandatory, and actually a point of contention for some geminauts and also critics of the protocol. As it's a text-oriented protocol, some argue that TLS makes everything too complex (especially so on older / limited hardware), but personally I think this is a good trade-off for security, and it really doesn't add an unbearable amount of complexity from what I've seen. However, this did give way for a slimmer, non-TLS version of Gemini called Spartan.
Despite the above, if you want to run a server you're going to need to generate a certificate. Most certificates are "self-signed" certificates because they aren't backed by what's known as a trusted Certificate Authority (CA)...it's only you signing your certificate. "What's the use in that?" you may ask, well, Gemini operates on what's known as Trust On First Use (TOFU), wherein the first time you visit a server, you immediately trust the certificate and store its fingerprint for future comparision. Each time you request something from that server, it checks to see if the certificate changes, and if it has, almost all clients will throw a warning. This is to prevent a man-in-the-middle attack where a new server masquerades as the one you're actually trying to contact.
See section 4.5.6 in the Gemini FAQ for discussion on TOFU
Why doesn't Gemini mandate CAs? This is actually addressed in the official Gemini FAQ:
As mentioned above there are a couple of ways to create a certificate:
If you want a free CA, most people use letsencrypt (link below), and simply copy their certs over to their gemini server (which should be run as an unprivileged user). Note that these certificates expire every 3 months and will cause a certificate change alert for your users - this is enough for most operators to skip using letsencrypt and just use a really long expiry on a self-signed certificate. I won't go over using letsencrypt as their website does a good job, especially if you're using their certbot, but once it's done you just have to navigate to "/etc/letsencrypt/live/<yoursite>/" to find the keys.
As for self-signed certificates, there are a few guides out there but the easiest way is to use Solderpunk's gemcert program to help you create one:
Note: As per the Gemcert site, it creates certificates for both your primary domain (e.g., example.com), and also a wildcard for any subdomains that you might make in the future (i.e., *.example.com). This will be important if your gemini site runs from, say, gemini.example.com
Note 2: As mentioned above, it's recommended to have your self-signed cert expire at a date way in the future, like 100+ years.
Gemcert Usage (for example, using GmCapsule):
$ ./gemcert --server --domain localhost $ mv localhost.crt cert.pem $ mv localhost.key key.pem $ mv *.pem ~/.cert/
Explanation:
Note: You can use a dynamic dns service (see section 1.1.1), but I recommend you buy a domain.
Note 2: Tilde servers may offer subdomains for free
There are a lot of ways to do this and most are straightforward. I went with Njalla because they:
This is actually not that bad if you're using a VPS, and using a home server isn't too bad either. You have to add what's called an "A Record" that links your domain name to the static IP of your VPS.
The basic approach is to run a script that sends your current public IP address to your nameserver on a recurring basis.
Considerations / Steps
Crontab setting for every 12 hours:
0 */12 * * * <location/to/script>
Njalla - setting up dynamic DNS
Forwarding ports on your home router
Setting static IPs on your home router / network
Crontab guru (helps create crontab entries)
Not only is content king on Gemini...it's the only thing. You don't have to be a voluminous writer on Gemini but it certainly helps to have an idea of what you want to say. You could, theorectically, just hang out on #Station and #BBS, but the heart and soul of Gemini is the long-form sharing of thoughts and ideas with other geminauts.
Personally, I'm not much of a writer, nor do I have that much time to do so, but something about Gemini made me want to write. I would actually catch myself thinking "this would be a good gemlog topic" on occasion, and then would write it out later. Some people post multiple times a day, and others - like myself - write a couple times a month... and THAT'S OKAY. The point here is to have good, well thought-out (or maybe stream-of-consciousness, if that's your thing) gemlog entries that others can consume.
I'll go over the manual way of writing your gemlog, as most static site generators do this for you. The exact format is pretty simple and is covered on circumlunar, the offical capsule of Gemini. Now, you can format your capsule and gemlog however you want, but if you want someone to be able to subscribe to it via their favorite reader, you'll have to comply with gmisub, or gemini subscriptions specification. See link below.
You'll also want to be familar with gemtext, which I won't cover here because the official docs cover it well enough:
A quick introduction to gemtext markup (on Circumlunar)
Your starter folder structure may look something like this:
gemini/ ├─ cgi-bin/ ├─ gemlog/ │ ├─ drafts/ │ ├─ 20230811-hello-world.gmi │ ├─ atom-feed.xml │ ├─ index.gmi │ ├─ template.gmi ├─ index.gmi
A few things to note about this layout:
Per the gmisub specification, you need a few things:
=> gemlog/20230811-hello-world.gmi 2023-08-11 - Hello World!
That is:
If you do the above, all gemlog readers will be able to subscribe to your page, and you will also be able to submit your entries to Antenna and DSN Antenna
Why use Atom feeds instead of RSS? @Solderpunk describes it here:
To be honest, if you wanted to skip generating an Atom feed in favor of gmisub, you can absolutely do that. I believe most people generate Atom feeds in order to have a standardized format, and one that can be used in clients that don't support gmisub. It also supports more precise created/updated timestamps for each entry, if that's something you need. It's also an official standard, which helps.
I personally use @solderpunk's Gemfeed tool because it's just a python script that scrapes your "gemlog/" directory and generates an atom feed for you. You can do this automatically with a cron job, but I don't update a lot so I trigger it manually whenever I make an udpate. I also put my helper scripts in "gemini/scripts/" directory.
I made a "generate_atom.sh" script in my scripts/ directory:
#!/bin/bash # taken from https://tildegit.org/solderpunk/gemfeed BASE_URL="gemini://gemini.smallweb.space/gemlog/" LOG_DIR="/home/gemini/gemini/gemini.smallweb.space/gemlog/" python3 /home/gemini/gemini/scripts/gemfeed.py -b ${BASE_URL} -d ${LOG_DIR} -a "Gritty" -e "gritty@smallweb.space" -n 20
In here:
And that's pretty much it for Atom feeds.
Think of Twitter (now, "X") posts when you think of a tinylog, except all your posts are in one gmi file with timestamps.
For this I'm going to steal from an early gemlog of mine, where I was discovering microblogging on Gemini. There are a few microblog formats on Gemini, but tinylog is the most widely adopted, and is the one you want to use.
Microblogging on Gemini (by Gritty)
Tinylog has an (unofficial but widely used) RFC set out for it to define a common structure. Although "[t]he original idea and most "rules" comes from Drew/uoou/Friendo['s]" Lace script, Bacardi55 has formally defined the structure which allows for easier aggregation.
Tinylog:
I want to emphasize the first point - while twtxt is plaintext, Tinylogs are Gemtext, and thus can be read by *any* Gemini client natively, in whatever native format that client renders (i.e., Lagrange rendering vs Kristall, for example).
Tinylog RFC on Codeberg by Bacardi55
Lace script - interleaves microblogs
Station social microblogging platform
BBS - Bulletin Board System for Gemini - supports tinylogs natively on a per-user basis
Bacardi55's list of known tinylogs
If you follow the RFC, creating a tinylog is straightforward - it's just a .gmi file with a particular format. Each entry is separated by a timestamp.
Here's a snapshot of the top of mine at the time of this writing:
# Gritty's tinylog Thoughts too small for a normal gemlog author: @gritty@gemini.smallweb.space ## 2023-08-11 00:41 UTC I wonder if 2FA / totp could theoretically be used on Gemini for logins... ## 2023-08-09 16:27 UTC I updated my gemroll list with new resources on gemini and added a HOWTO for creating your own capsule => ../HOWTO/managing-your-own-capsule.gmi Managing your own capsule
In the above:
And that's it.
Tip: if SSH'ing into your capsule to update your tinylog is time consuming, you can make a CGI script to update from your phone / browser. Check the CGI section.
As mentioned in the sections above, you can use the GTL tool on the commandline to get a Twitter-like microblog interface. It's a TUI (Terminal User Inferface) that stitches together the tinylogs you subscribe to into a timeline. The reason this is necessary, is that unlike Twitter (now "X"), all tinylogs are flat files hosted on a variety of servers and not in a central database (thus, out of order). You can also edit your own tinylog and reply to others within the program.
That being said, if you're like me, you're on your phone most of the time and not logging into a console to fire up GTL. The neat thing about GTL is that you can make it output .gmi files to your capsule on a recurring basis through a cron job. This way, you can just use your phone to navigate to a normal gemini file that has your subscriptions already in a timeline format.
Once you have GTL installed somewhere and have subscribed to a few tinylogs, just edit your crontab:
crontab -e
here's my entry:
0 */6 * * * /home/gemini/bin/gtl --mode gemini --limit 55 > /home/gemini/gemini/gemini.smallweb.space/tinylog-agg.gmi
Let's break this down:
Now all you have to do is link to this file from somewhere, like your landing page on your capsule.
And that's it, you can now read your tinylog timeline from any gemini browser.
Here's a few reasons you may want to install an aggregator on your capsule:
Whatever your reason, there are several to pick from. Here are a few:
Personally I use comitium since I read a gemlog a while back comparing a few (I didn't save the link), and I liked the features. Here's an excerpt from the author, @nytpu:
There are many Gemini aggregators out there, and yet not one does everything I want it to. I want an aggregator that:
1. Works with Gemini and Gopher (and possibly http)
2. Supports Atom, RSS, and Gemini feeds (and possibly JSON feeds)
3. Can watch a page for changes
4. Simple and easy setup & usage
All of the aggregators always have really cool and unique features, and yet all that I've found don't meet one or more of these basic (IMO) criterion. Hence, going and writing my own.
Also, it's nice if it doesn't tie me to a specific browser/service; i.e. trivially self-hostable, ideally as CGI or static pages that can use an existing server setup rather than needing vhosting and routes.
The quickstart guide is really good and will do exactly what it says:
Food for thought on aggregators and centralized services
Are We Rebuilding the Centralized Web Minus Tracking? (by @bacardi55)
I've been around the web since the mid-1990's and even then I didn't understand or use CGI all that much; it was foreign to me until, ironically, 2022 - a date in which CGI should be considered the way of the dodo. But this is Gemini, and we are harkening back to the simpler times. CGI is "well documented" on the web, but it wasn't really that understandable to me and how exactly to use it on a Gemini server.
A few resources helped me get on the right track:
A Sample CGI Application (by @tomasino)
Using SCGI to serve dynamic content over the Gemini protocol (covers CGI and SCGI)
I highly suggest reading the SCGI article above, and then watch Tomasino's videos, they're quite helpful.
TLDR: CGI apps are scripts/program (Python, Bash, etc.) on your server that output gemtext. CGI starts and ends a program for every call, where SCGI keeps a server up and running.
I am using GmCapsule for the following examples. Please consult your server's documentation on how to setup CGI. I don't want to repeat too much of the User Manual but I'll cover some general guidelines.
GmCapsule supports virtual hosts so it expects a certain directory structure with the name of your domain. For both the static content and CGI directories, you will need to have a subdirectory with the domain you are referencing so the server knows which content goes to which site when it receives a request for a certain domain. Please see the example from my capsule below:
~gemini/ ├─ gemini/ │ ├─ gemini.smallweb.space/ │ │ ├─ <static content> │ ├─ cgi-bin/ │ │ ├─ gemini.smallweb.space/ │ │ │ ├─ <dynamic content / scripts>
As you can see above, both static and dynamic (CGI) content goes under a domain-named subdirectory (gemini.smallweb.space, in my case)
The above must be configured in GmCapsule's config file. It's a simple ini-style file that sits in the home directory of the user running the server in a file named ~/.gmcapsulerc.
Here's mine:
; By default, configuration is read from ~/.gmcapsulerc. Use the -c option ; to specify some other configuration file. [server] host = gemini.smallweb.space port = 1965 certs = /home/gemini/.certs [static] root = /home/gemini/gemini [cgi] bin_root = /home/gemini/gemini/cgi-bin
Download above .gmcapsulerc (you'll need to rename)
From the above, GmCapsule will look for subdirectories defined in "host" at the defined [static] and [cgi] root directories. For CGI, any executable file in the cgi-bin directory will be executed if called by the browser.
--Location of Files--
From the example, my static content is available at:
gemini://gemini.smallweb.space/
and dynamic content is located at
gemini://gemini.smallweb.space/
Notice that you don't need a subdirectory called "cgi-bin" or something similar. This has the advantage of being able to reference static content from your cgi scripts/directory, if similarly named (e.g., an /Antenna directory in both static and cgi-bin)
Next up is to see if it works (see next section)
GmCapsule Homepage (with examples)
After you have your CGI configured it's time to test it out. Note that if you're on a shared server you just need to consult the documentation or server owner for the location where user CGI scripts are served (if at all).
Let's start with a hello world bash script.
cd cgi-bin/
touch hello
vim hello
And save the following:
#!/bin/sh printf "20 text/gemini; charset=utf-8\r\n" printf "# Hello World! \n" printf "## I got CGI working \n"
Finally, make it executable:
chmod +x hello
If all goes well you can see this script (per our example) at:
gemini://gemini.smallweb.space/hello
(I don't have the example script at that location)
-- Breakdown of Script --
In the above we start with a "she-bang" and then the location to the shell that's going to execute the code below the she-bang. You can change this to /bin/bash if you wanted or even /usr/bin/python3 if you wanted to use python instead (which we use in later examples).
Per the gemini spec, a browser expects a "20" response if everything is OK and you want to display text to the user (see the link below to reference the spec). The next 2 lines simply print out gemtext, which in this case a level 1 and level 2 header.
-- Errors --
If all does not go well, check your configurations, paths, and directory and file naming. Also make sure to make your script executable.
Gemini Specification (see section 3)
Nearly all servers that implement CGI pass along a defined set of CGI variables that your programs can access in order to make decisions and process dynamic content. This can be a client's certificate, data from the user for a type "10" input (see Gemini spec for 10/input), or the user's current path.
The CGI spec defines particular variables but your server may have a subset of these or even new ones (such as Titan). GmCapsule defines these:
-- Accessing these in your code (Python) --
To access in Python:
os.getenv('<env_var')
for example:
os.getenv('QUERY_STRING')
which gets the query string passed to the script.
If you want to see what these all do, just make a script to print them out:
#!/usr/bin/python3 import os print("20 text/gemini\r") print ("Client cert hashes:", os.getenv('REMOTE_IDENT'))
After that, it's figuring out what you want to do with all of this. For me, I made a tinylog updater (see other HOWTO).
Skyjake has examples using Titan with GmCapsule:
If you've read some of my BBS / Station posts or through my capsule, you'll know that I don't have much time to login via terminal to my capsule much, so content updates to my site should ideally be through my phone. I had no easy way to update my tinylog (even Termux is cumbersome), and so I decided to just have a cert-restricted tinylog update script for my capsule.
As this was my first real project I relied heavily on Tomasino's starter project to get me going:
A Sample CGI Application (by @tomasino)
The general idea is to:
Taking the lead from Tomasino, I created a "helpers.py" file that housed all my common routines that may be reused in other parts of my capsule. You can see in the file (below), but here are the stubs:
This checks for, and gets the client certificate
Error if wrong cert presented
simply grab the query string, if it exists
print the "20" response
Temporary redirect to url (code 30)
cert required (send code 60)
prompt for input (code 10)
simply return the path environment variable
read in a text file (such as a tinylog)
-- The Update Script --
I won't go line-by-line on how this works here (maybe in a gemlog post), but you can see the script I use here:
-- Basic breakdown of the code --
And that's pretty much it
I took the "thanks" idea from @Morgan. The basic idea is that visual responses like thumbs up or down on posts or comments can skew someone's perception of an article, post, or comment. What's important is that the author gets direct feedback about their post without the readers seeing it.
Again, I'll post the code and briefly go over how it works here:
-- Overview --
gemini://gemini.smallweb.space/thanks?page=/gemlog/20230804-DSN-note.gmi&feedback=thanks
What this script doesn't do is limit how many times you can click a link... so someone could spam a bunch of negative feedback if they wanted.
Currently I have to log into my capsule to view that JSON file, but I plan on making a certificate-restricted page so I can easily see the updates for myself.
Currently I'm not using Titan on my capsule but I do want to use it to make gemlog posts from my phone. This is a future project of mine. Currently, the best source to get a helloworld Titan example running on GmCapsule is from its homepage:
GmCapsule example from @skyjake
Example CGI script by @skyjake on adding, modifying, and deleting files via Titan:
If anyone has additional examples on using Titan that they want to share, please feel free to reach out to me (gritty@smallweb.space)