Project Gemini
Monday, January 16, 2023
Project Gemini is a neat modern take on the Gopher protocol. You can read the Gemini FAQ or the Gemini specification to learn more details, but the home page has a nice summary:
Gemini is a new internet protocol which
- Is heavier than gopher
- Is lighter than the web
- Will not replace either
- Strives for maximum power to weight ratio
- Takes user privacy very seriously
There are some nice Gemini clients implemented in various languages, for both the command-line and with nice user interfaces. I happen to enjoy using AV-98 and Lagrange, but many others are also great.
In a similar manner to my Gopher implementation in Factor, I recently implemented the Gemini protocol as well as a Gemini server and a Gemini user interface:
Instead of going into how the protocol or the user interface is implemented, I wanted to go over the Gemini command-line interface. In the spirit of Python’s cmd module, I contributed the command-loop vocabulary to support generic line-oriented command interpreters.
We start by making a sequence of commands that our Gemini interpreter will support:
CONSTANT: COMMANDS {
T{ command
{ name "back" }
{ quot [ drop gemini-back ] }
{ help "Go back to the previous Gemini URL." }
{ abbrevs { "b" } } }
T{ command
{ name "forward" }
{ quot [ drop gemini-forward ] }
{ help "Go forward to the next Gemini URL." }
{ abbrevs { "f" } } }
T{ command
{ name "history" }
{ quot [ drop gemini-history ] }
{ help "Display recently viewed Gemini URLs." }
{ abbrevs { "h" "hist" } } }
T{ command
{ name "less" }
{ quot [ drop gemini-less ] }
{ help "View the most recent Gemini URL in a pager." }
{ abbrevs { "l" } } }
T{ command
{ name "ls" }
{ quot [ gemini-ls ] }
{ help "List the currently available links." }
{ abbrevs f } }
T{ command
{ name "go" }
{ quot [ gemini-go ] }
{ help "Go to a Gemini URL" }
{ abbrevs { "g" } } }
T{ command
{ name "gus" }
{ quot [ drop "gemini://gus.guru/search" gemini-go ] }
{ help "Submit a query to the GUS search engine." }
{ abbrevs f } }
T{ command
{ name "up" }
{ quot [ drop gemini-up ] }
{ help "Go up one directory from the recent Gemini URL." }
{ abbrevs { "u" } } }
T{ command
{ name "url" }
{ quot [ drop gemini-url ] }
{ help "Print the most recent Gemini URL." }
{ abbrevs f } }
T{ command
{ name "reload" }
{ quot [ drop gemini-reload ] }
{ help "Reload the most recent Gemini URL." }
{ abbrevs { "r" } } }
T{ command
{ name "root" }
{ quot [ drop gemini-root ] }
{ help "Navigate to the most recent Gemini URL's root." }
{ abbrevs f } }
T{ command
{ name "shell" }
{ quot [ gemini-shell ] }
{ help "'cat' the most recent Gemini URL through a shell." }
{ abbrevs { "!" } } }
T{ command
{ name "quit" }
{ quot [ drop gemini-quit ] }
{ help "Quit the program." }
{ abbrevs { "q" "exit" } } }
}
And then we define a custom command-loop that will allow us to number the links on a Gemini page, and then by typing a number we can navigate to one of the links by detecting a “missing command”:
TUPLE: gemini-command-loop < command-loop ;
M: gemini-command-loop missing-command
over string>number [ 1 - LINKS ?nth ] [ f ] if* [
gemini-go 3drop
] [
call-next-method
] if* ;
Finally, we make a simple MAIN: word to run it:
: gemini-main ( -- )
"Welcome to Project Gemini!" "GEMINI>"
gemini-command-loop new-command-loop
COMMANDS [ over add-command ] each
run-command-loop ;
MAIN: gemini-main
You can see it in action:
$ ./factor -run=gemini.cli
Welcome to Project Gemini!
GEMINI> go gemini.circumlunar.space/news/
Official Project Gemini news feed
[1] Atom feed
2023 News
[2] 2023-01-14 - Tidying up gemini.circumlunar.space user capsules
[3] 2023-01-08 - Changing DNS server
2022 News
[4] 2022-06-20 - Three years of Gemini!
[5] 2022-01-30 - Minor specification update (0.16.1)
[6] 2022-01-22 - Mailing list archives, Atom feed for official news
[7] 2022-01-16 - Mailing list downtime, official news feed