gmi100
Gemini protocol CLI client written in 100 lines of ANSI C.
Build run and usage
Compile with `build` script or use any C compiler linking with OpenSSl.
$ ./build # Compile on Linux $ ./gmi100 # Run with default "less -XI" pager $ ./gmi100 more # Run using "more" pager $ ./gmi100 cat # Run using "cat" as pager gmi100> gemini://geminiprotocol.net # Navigate with absolute URI gmi100> 3 # Navigate with link index from current capsule gmi100> c # Print current capsule URI gmi100> r # Refresh current capsule gmi100> u # Go "up" in URI directory path gmi100> b # Go back in browsing history gmi100> ? # Search with geminispace.info/search gmi100> !open # Run shell command on current page gmi100> q # Quit
Each time you navigate to text document the pager program will be run with that file. By default `less -XI` is used but you can provide any other in first program argument. If your pager is interactive like less the you have to exit from that pager in order to go back to gmi100 prompt and navigate to other capsule.
When non text file is visited, like an image or music then nothing will be displayed but temporary file will be created. Then you can use any shell command to do something with it. For example you can visit capsule with video and open it with mpv:
gmi100> gemini://tilde.team/~konomo/noocat.webm gmi100> !mpv
Or similar example with image and music. For example you can use `xdg-open` or `open` command to open file with default program for given MIME type.
gmi100> gemini://158.nu/images/full/158/2022-03-13-0013_v1.jpg gmi100> !xdg-open
You also can use any program on reqular text capsules. For example you decided that your defauly pager is cat but for some capsules you want to use less. Or you want to edit given page in text editor. In summary, you can open currently loaded capsule as file in any program as long as you don't navigate to other URI.
gmi100> tilde.pink gmi100> !less gmi100> !emacs gmi100> !firefox gmi100> !xdg-open
How browsing history works
Browsing history in gmi100 works differently than regular "stack" way that is commonly used in browsers and other regular modern software. It is inspired by how Emacs handles undo history. That means with the single "back" button you can go back and forward in browsing history. Also with that you will never loose any page you visited from history file and I was able to write this implementation in only few lines.
After you run the program it will open or create history .gmi100 file. Then every page you visits that is not a redirection to other page and doesn't ask you for input will be appended at the end of history file. File is never cleaned up by program itself to make history persistent between sessions but that means cleaning up browsing history is your responsibility. But this also gives you an control over history file content. You can for example append some links that you want to visit in next session to have easier access to them just by running program and pressing "b" which will navigate to last link from history file.
During browsing session typing "b" in program prompt for the first time will result in navigation to last link in history file. Then if you type "b" again it will open second to last link from history. But it will also append that link at the end. You can input "b" multiple times and it will always go back by one link in history and append it at then end of history file at the same time. Only if you decide to navigate to other page by typing URL or choosing link number you will break that cycle. Then history "pointer" will go back to the very bottom of the history file. Example:
gmi100 session pos .gmi100 history file content ================== === =============================== gmi100>------------------ --- ------------------------------- gmi100> tilde.pink --> tilde.pink ------------------ --- ------------------------------- gmi100> 2 tilde.pink --> tilde.pink/documentation.gmi ------------------ --- ------------------------------- gmi100> 2 tilde.pink tilde.pink/documentation.gmi --> tilde.pink/docs/gemini.gmi ------------------ --- ------------------------------- gmi100> b tilde.pink --> tilde.pink/documentation.gmi tilde.pink/docs/gemini.gmi tilde.pink/documentation.gmi ------------------ --- ------------------------------- gmi100> b --> tilde.pink tilde.pink/documentation.gmi tilde.pink/docs/gemini.gmi tilde.pink/documentation.gmi tilde.pink ------------------ --- ------------------------------- gmi100> 3 tilde.pink tilde.pink/documentation.gmi tilde.pink/docs/gemini.gmi tilde.pink/documentation.gmi tilde.pink --> geminiprotocol.net/
Backstory
From official Gemini FAQ page:
A basic but usable (not ultra-spartan) client should fit comfortably within 50 or so lines of code in a modern high-level language. Certainly not more than 100.
There are 3 such clients listed on official Gemini capsule among known software made by Solderpunk himself. The Go client go slightly over 100 LOC but that's ok.
When I first saw this I was convinced it's just a matter of time before someone make write such client in C. But after 2 years of waiting I decided to try myself.
Project goals:
- Write CLI client in C89
- Use only standard C libraries and OpenSSL as dependency
- Code should compile without special compiler flags
- No deliberate obfuscation like forcing things to be in single line
- Fit as many features as possible
The last point made this project a bit different from what Solderpunk did. His simple clients show how easy it is to work with Gemini. I was trying to see how much we can fit in 100 lines of C, forget about simplicity.
Initially I struggled to even fit simple TLS connection in such small space. It took me around 3 weeks of lazy slow programming but results exceeded my expectations. It turned out that it's not only achievable but also it's possible to include many convenient features like persistent browsing history, links formatting, command execution and more.
I think that final result can be called a normal C code but OFC it is very dense, hard to read and uses practices that are normally not recommended. Even tho I call it a success.
I am complete!
Ha-aaaack
Yes, you are hacked
Overflow stack
Now I'm complete
And my log you debug
This code will be mine
#include in first line
<you_brought_me_the_lib.h>
And now your shell compile
Written: 2025-11-02