Gritty's Blog

Goal: Setup Forgejo on my VPS and be able to use SSL with it


  • I had no idea how to get Forgejo to use SSL
  • I didn’t use a wildcard with letsencrypt for my domain so wasn’t going to work
  • I needed to figure out how to get a subdirectory to proxy SSL to my http server via nginx (i.e.,* forwarded to


2 pieces of information were key:

  1. Having nginx proxy all SSL requests to the forgejo http server

  2. Finding the right nginx location directive to catch all /git/* requests correctly


This assumes you have a newly installed Forgejo server running on your VPS at port 3000 and running under systemd. See Forgejo installation / admin docs for details.

Configure Nginx

Edit your Nginx config. I’m just using the default at:

$ vim /etc/nginx/sites-enabled/default

And add something like this inside your server block (preferably near the top so it’s the first match):

location ~/git(?.*)$ {

proxysetheader Host $host;

proxysetheader X-Real-IP $remote_addr;

proxysetheader X-Forwarded-For $remote_addr;

proxysetheader X-Forwarded-Proto: https;

proxysetheader X-Url-Scheme: https;




  • The ‘location’ defines a regular expression to catch ALL requests beginning with ‘/git’ and assigns it to a variable ‘sub_dir’ (which is used on the last line to pass to the Forgejo server
  • the next 3 lines setup the proxy headers
  • X-Forwarded-Proto – this is the key line, wherein nginx will proxy https requests to the forgejo server
  • proxypass – this is location of your Forgejo server. Notice that the $subdir is appended. If you don’t do this, Forgejo will fail to load anything but the bare-bones HTML page (no images, etc.)

Restart Nginx

$ sudo systemctl restart nginx

Configure forgejo for the subdirectory

$ sudo vim /etc/forgejo/app.ini

And change ‘ROOT_URL’ to point to your subdirectory


Restart forgejo

$ sudo systemctl restart forgejo

The End

That’s it, you should now be able to navigate to


and see forgejo loaded with https://

Go ahead and make that admin user (you didn’t do that over http did you?)


[Server Fault](

[Nginx Location Examples](

[SO Question on SSL with Nginx Reverse Proxy](

I realized recently that I needed to have some kind of backup for my VPS (virtual private server, i.e., a hosting service), and I had a Raspberry Pi 4 sitting at home doing nothing, so why not use my Pi and rsync?

Overview of Steps

  1. Install sshd on the VPS
  2. Setup SSH Keys
  3. Test the ssh login
  4. Install rsync on the VPS
  5. Create and Test the Script
  6. Add to crontab

Step 1: Install SSH on your server / VPS

There's a bunch of tutorials out there to do this, so I won't repeat here. See here

Step 2: Setup SSH Keys

Again, here's another tutorial to avoid using passwords. This is important so that your cron job can run automatically without input Setting Up SSH Keys

Step 3: Test SSH Login

If you run

$ ssh <user>@<yourserver>

You should be automatically logged into your system without the need for a password

Step 4: Installing rsync (if not already present)

Setting up rsync

Step 5: Create and Test the Script

Using rsync

rsync -aAX --delete --log-file=/home/pi/rsync.log <user>@<server>:/remote/path/to/backup/ /local/path/on/pi/

NOTE: before you blindly use this script, note that the “—delete” option is there. What this will do is remove files from the backup if they're deleted from the source (rsync keeps all files by default). If you're not careful it'll delete files you may want to keep. I suggest adding the “-n” option to do a dry-run and check the output

NOTE 2: the “—log-file=/home/pi/rsync.log” will log all output to the file specified when cron runs it (below)

Step 5a: Create Script

$ touch ~/.local/bin/
$ chmod +x ~/.local/bin/
$ vim ~/.local/bin/

And drop this in there:

rsync -aAX --delete --log-file=/home/pi/rsync.log <user>@<server>:/remote/path/to/backup/ /local/path/on/pi/

Step 6: add to user's crontab

$ crontab -e

and add this line (if you want it to run a 0200, daily) 0 02 * * * /home/pi/.local/bin/

The End

That's it. If you followed along, you should be backing up your VPS files to your Raspberry Pi (or other machine) at 0200 daily, with logs going to rsync.log in the home directory.

I wanted to implement Gemini-Mention on my Gemini capsule but I didn't want to use the existing scripts out there (Go, Lua, or Bash). Since I am most comfortable in Python, I just created my own.

If you're not familiar with Gemini-Mention, it's an RFC that defines a way for a person writing a reply gemlog (a gemlog replying to another gemlog) to be able to notify the original gemlog owner about the reply. Aside from aggregators like Cosmos and backlinks provided by some search providers, there's no real way to know if someone is replying to your gemlog. Gemini-Mention is there to hopefully solve that problem, even if it's a manual way of doing so.

Seen on my capsule: gemini:// (you'll need a Gemini browser, like LaGrange )

Disclaimer, I'm a hobby programmer so there's definitely a better way to do this, but it gets the job done for me. Code follows.

#!/usr/bin/env python3

# gemini-mention
# by Gritty
# Requires: ignition

import os
import sys
import re
import ignition
import fileinput
import json
from urllib.parse import unquote

# Gemini-Mention

ERROR: Sorry, there was an issue fetching the URL you listed.  Please ensure that:
* It is percent-encoded
* Is a proper gemini:// URL

=> mention?submit Submit another link

# Gemini-Mention

=> mention?submit Submit a link

TLDR: Use the link above to notify me of a reply to one of my gemlogs.

## What is gemini-mention?
Gemini-mention alerts a capsule owner of a gemlog reply.  This is voluntary and done for the courtesy of the gemlog owner to which they are replying.

It is defined by an RFC created by @Bacardi55:
=> Gemini Mention RFC (HTTPS)  

### Usage
Submit a percent encoded gemini URL to the following link:
> gemini://<percent_encoded_url_of_reply>

This capsule will then scan that page for links back to this capsule and log them for the capsule owner.  In the future I (Gritty) plan to post these replies to the bottom of their respective gemlogs.


# Gemini-Mention

Matching backlinks / mentions:

FOOTER = '''
=> ../index.gmi Home
=> ../gemlog/index.gmi Gemlogs (non-technical)
=> ../tech-gemlog/index.gmi Technical Gemlogs

def process_json(srcUrl, mentions):
    # expects source url that mentions this site
    # expections list of urls mentioned by the source url
    # make sure json file exists with at least one dummy entry
    # i.e., { "test" : "value"}
    with open("mentions.json", "r+") as file:
        # load json file
        dataDict = json.load(file)
        # create or update entry
        dataDict[srcUrl] = mentions
        # write the data, truncating file first
        json.dump(dataDict, file, indent = 2)

def printPage(text, header=True, footer=True):
    if header:
        print("20 text/gemini; charset=utf-8", end = "\r\n")
    if footer:

#### Main program starts here ####

qString = os.getenv('QUERY_STRING')

if qString:
    if qString == 'submit':
        # Prompt user for a URL that mentions a page on this capsule
        print("10 Enter a mention URL. \r\n")
        # Everything else should (hopefully) be a URL to parse
        URL = unquote(qString)
        response = ignition.request(URL) 
        if not response.success():
            page =
            pattern = r"^(=>) *(gemini://|tech-gemlog)[\S]*) *(.*$)" 
            regex = re.compile(pattern, re.MULTILINE)
            matches = re.findall(regex, page)
            if matches == []:
                printPage("No matches to this capsule found")
                # process the matches
                mentionedUrls = []
                pageText = "Matched URLs\n"
                for item in matches:
                    if item[1] not in mentionedUrls:
                        pageText += f"* {item[1]} \n"
                process_json(URL, mentionedUrls)
                pageText += "URLs / mentions Recorded\n"
                pageText += "=>mention?submit Submit another"

    # We have an empty query string, display instructions

CGI is alive and kicking on the #geminiProtocol, for sure.

The formative years of my life were in the 1990s, back when Common Gateway Interface, or CGI, was all the rage. I remember seeing URLs like and seeing how it generated dynamic content, but since I didn't have access to a web server like that back then, I never really got into CGI programming. After I started doing web programming, it was Javascript, Javascript, Javascript, and AJAX to save the day and goodbye old, non-secure, heavy CGI scripts.

So as you can imagine, when I stumbled across Gemini around January of 2022, I was very surprised to see that a lot of the servers being created for people to use had CGI support for creating dynamic content. At first I was baffled, but it really does make a lot of sense for a text-focused protocol such as Gemini:

  • Most capsules are low traffic so spinning up these processes, while resource intensive by today's standards, aren't really too much of an issue for Gemini
  • Gemini is purposely limited, and as such, things like AJAX calls to a Gemini server just aren't supported
  • CGI has been around for a while, and is fairly accessible to professional and hobby coders alike

I think the beauty of CGI scripts is that it lowers the barrier for novice programmers to make an interactive capsule (gemini site) and practice their coding skills. Want to make an old school guestbook using Bash scripts and SQLite? Go for it! Want to make a commenting system using Python? Sure. No problem. It's the simplicity that I like, which is core to the theme of Gemini.

Documentation, however, on how to make a CGI script specifically for Gemini was lacking, so I ended up making my own HOWTOs on doing that. I even convinced Solderpunk to put a link on their official Gemini FAQ that goes over creating your own capsule and introducing you to CGI programming.

Again, if you have a Gemini browser – like LaGrange – you can see my HOWTO here: gemini://

On the #gemini protocol there is one really prominent content aggregator (think RSS feed) that's been active over the several years of the life of Gemini: Antenna.

Antenna basically works this way: a person writes a gemlog (a blog on Gemini), and then updates an index page that's a list of links to the logs with the dates and titles. A user then submits the that index page to Antenna and it ingests it into a database and displays everyone's entries in chronological order. So instead of having an aggregator poll a list of sites periodically, a user will submit their list. In doing so, it puts the onus on the user to aggregate their content, and ensures that Antenna isn't polling dead gemlogs for months on end.

Well, for a few days I noticed (as well as the community), that Antenna was down. There was a post about it on the new BBS (bulletin board system), and it was mentioned that someone could theoretically create another Antenna. I don't know why I wanted to do this for the community, but I suddenly had to do it.

Now, I have two small kids at home so personal time is the most premium of commodities, and that free time is usually spent relaxing because I'm too tired to do anything else, but sometimes I get this idea in my head and I have to do it. So, I set out making my own version of Antenna after someone pointed me to the code repo.

Much to my chagrin, the instructions were way out of date from the actual code, but luckily it was written in Python and it gave me an opportunity to brush back up on my Python skills. After much digging around and seeing how it works, I finally got my version of Antenna up and running...and if I remember correctly it was about two evenings of trying to get things working.

I posted up on the BBS that I got it working and for folks to try it out. The creator of Gemini, Solderpunk, replied and asked if I would try and theme it so eventually I came up with Deep Space Network Antenna, named after the NASA program. I also changed the allowable posts to be non-technical. The problem with the last part was that most people only had 1 index page for all their gemlogs and in order to not submit technical gemlogs alongside non-technical ones, you'd have to have two. I went to BBS to get opinions on this and there were of course some on both sides, but in general, I decided to leave it up to the capsule owner (gemlog writer) to do this separation.

For a while it was pretty dead on DSN Antenna, but now some folks have started posting to it, and Skyjake (the creator of the best Gemini browser, LaGrange, and gemini's first BBS) added it to Cosmos, his super aggregator, which is an aggregator of aggregators, designed to catch replies between gemlog posts. So it seems fairly official now, and I'm happy about that.

If you have a Gemini browser, you can check it out here: gemini://

I've been spending a lot of time on the #gemini protocol this past year, and I'm writing this post on my normal blog so that the search engine crawlers index this page for those that may be interested in it. While I know there are other introductory pages – which I'll link here as well – I feel like this is my contribution on getting the message out. If you happen to be on my gopher:// mirror of this site, just simply drop in https:// instead on a normal browser.

What is the Gemini Protocol?

Gemini FAQ on Circumlunar

Why use Gemini?

A lot of folks are disenfranchised with how the web is today. This is a mix of new and old internet users alike. As you can imagine, the old-heads that saw the internet blossom and loved the promise of a great new world, are now disenfranchised and looking to escape back to what the internet used to be: controlled by individuals, not by corporations greedily collecting your data so they can make money off of you. There's also a surprising group of younger internet citizens that grew up with a cellphone in their hand and internet all around them. They, too, it seems, see the insidious side of the internet and wish to connect with people, not endless status updates.

The second group, which also intersects with the first, are the people who like the cleanliness of text-only presentation. These are the folks that love command line and using text files. They probably use a todo.txt as well.

In a world of flashy websites, content can easily be lacking and overlooked. In the world of Gopher and Gemini, however, content is king because if you have no content there's really nothing to post.

Another group are the ones that love to blog, but they, too, felt lost at sea in the glut of the internet. Since gemini and gopher are much smaller, the interactions are admittedly less, but they are more meaningful because they are real people who stopped by to read your 5000 word #gemlog (gemini's version of a blog), and it struck a chord with them. If they take the time to fire up their email program to write you a response, you can guarantee it means a bit more than the comment section on most internet blogs.

Another reason people join is the clear lack of algorithms feeding you content. Almost everything in Gemini is pull versus push. What this means is that you have to find content instead of it being firehosed to your screen. And why would you want this? It slows you down, and it makes you the curator of your own reading lists. Part of the fun of the old internet, they say, was that you had to DIG to find content, and that the digging was part of best things of the internet then, because when you found a gem or goldmine, it meant that much more to you. That's not to say there aren't any push capabilities; there are at least 4 search engines that I know about, and several content aggregators (think #RSS feeds), and a social microblog site called Station. While these are technically centralizing parts of Gemini (and there have been many discussions about this on Gemini, both pro and con), I think they provide a modicum of convenience while sidestepping most of the bad parts of the highly centralized web.

Generally though, Gemini is just not what most people are looking for, and that's okay. It's draw, for me, is the fact that it's a small (but far from exclusive) community of people, and not big corporations and algorithms.

What can I do on Gemini?

Gemlogs (Gemini Blogs)

A lot of people on Gemini write long-form #gemlogs, which are just Gemini versions of Blogs. Gemini, however, is NOT just a blogosphere on a lesser-known internet protocol, but it does make up a lot of the content on there. When I was first joining Gemini I wasn't much of a writer, but after a while I felt encouraged to write down a few thoughts here and there. People use their gemlogs for a whole host of things that they would use a blog. such as: * Journaling thoughts, talking about your day, etc. * Note taking – this is pretty popular for those people that need a technical reference for themselves and others while the figure out a system or coding, etc.

You can subscribe to gemlogs if they are in the proper gemlog format or if the owner put the logs into an Atom feed

gemlog format for subscribing

Tinylog (microblog)

Gemini also has a version of microblogging called #tinylogs. These are shorter-form posts that don't warrant a full-fledged post. Think Twitter or Mastodon, but stripped waaayyy down to the basics. If you have your own site (or capsule as they are called in Gemini), you can start your own in a single text file, or there's a site called Station that is the closest thing Gemini has to a social network. Station is a microblog-style site that lets you post tinylogs and to like, comment, and subscribe to other people's tinylogs. Gemini is so small that the front page feed only gets a few updates a day and is easily digestable.

If you make your own tinylog on your website, there's a way you can subscribe to other tinylogs and view them as a timeline (otherwise you have to constantly check the tinylogs of others that you like). The two programs are called #gtl and #lace.

Tinylog format / RFC Tinlylog tools


SpellBinding My favorite all-time game on Gemini is #Spellbinding. It's a word game where you are given seven letters in a circle with one letter in the middle, and you must make as many words as possible using the center letter. words must be 4+ letters.

you'll need a Gemini browser to access the link below: gemini://

Wordo A wordle clone for Gemini gemini://

There are also some MUDs and other things I haven't explored yet.

Getting Started

If you're interested in getting started, here's my quickstart:

Get a browser

Lagrange. Just use this, it's the best one out there for mobile and desktop Lagrange Gemini Browser

Find some content

Point your browser to: * Antenna – content aggregator: gemini:// * Cosmos – content super aggregator: gemini:// * – search engine: gemini://

Setup a capsule SmolPub

Other Getting Started Guides

Awesome Gemini – Links Gemini Quickstart


Not a lot of folks use the finger command or protocol these days; however, I've been delving into the #smallweb / smolweb (i.e., the Gemini Protocol) for the past year and noticed a few people have revived this ancient protocol for status updates. I figured I'd give it a shot, and I thought it would be simple, but the lack of guidance really hindered me for a bit.

If you're wondering, efingerd / fingerd (the original) simply enables people outside of your own linux machine to use the finger command, just remotely.


Finger / efingerd is not installed on Ubuntu 22.10 by default so you have to install it:

$ sudo apt-get install finger
$ sudo apt-get install efingerd

Other finger daemons

This post only goes over efingerd, but if you want to see what other finger daemons are available:

$ apt search fingerd

Enabling efingerd

On Ubuntu 22.10, the install script should automatically update your /etc/inetd.conf file with the necessary configuration. One thing I learned is that these old programs are launched by another wrapper daemon, namely tcpd. I really tried getting cfingerd to work with systemd, xinetd, and inetd, but I kept getting errors, so I went with efingerd in the end.

If you check out /etc/inetd.conf, you'll see this line added:

:INFO: Info services
finger   stream  tcp  nowait   efingerd  /usr/sbin/tcpd   /usr/sbin/efingerd 

which I edited to:

:INFO: Info services
finger   stream  tcp  nowait   root  /usr/sbin/tcpd   /usr/sbin/efingerd -u -t 5  
  • -u prevents users from using the executable ~/.efingerd file
  • -t 5 sets the connection timeout
  • I also changed user to root (which most other finger daemons run as) so that each user's home directory could be read by the bash scripts, otherwise the user efingerd can't see into those.

Enabling the Service

$ sudo systemctl inetd restart

Checking the service

$ sudo journalctl -f -u inetd


$ sudo systemctl status inetd

Configuring efingerd

make all files in /etc/efingerd executeable

sudo chmod +x /etc/efingerd/

edit them – there are a few test commands in there... check out the manpage for what they do

I pretty much got rid of everything (finger shows users logged in and their IP addresses) and crafted my own scripts inside the files to read the user's directory for the .plan, .project. and .pgpkey files, and display them if they exist.

Open port in firewall

$ sudo ufw enable 79/tcp

Testing efingerd

try to finger your server:

finger @<your server>

(responds with whatever is in “list”)


finger <user>@<your server>

(responds with whatever is in “luser”)

Addendum – happynetbox

I reached out to the owner of happynetbox [1], a web front-end to finger where you can sign up for your own account and have a finger account purely through the web. I found this site because of ~ruario's posting on #antenna where they were testing if antenna would allow a finger:// submission. Anyways, the owner responded to me and said they used a Node project on github as a baseline for that entire site, which, might actally be easier than setting up 30 year old finger programs on today's systems. The site can be found here: * Simple Node.js finger server * Happynetbox

After some fighting with the “simple” install, I finally have a working Writefreely instance on my server with SSL.