#gemini #client #command-line #actor #client-certificate #aims #experimental

bin+lib trotter

Trotter 🎠 is an experimental crate that aims to make writing Gemini clients fun and easy

11 releases (2 stable)

1.0.1 Mar 31, 2024
0.6.0 Nov 21, 2023
0.5.2 Nov 18, 2023
0.4.0 Nov 16, 2023
0.1.0 Nov 11, 2023

#1 in #aims

Download history 45/week @ 2024-02-22 18/week @ 2024-02-29 14/week @ 2024-03-07 7/week @ 2024-03-14 333/week @ 2024-03-28 59/week @ 2024-04-04 212/week @ 2024-04-11 13/week @ 2024-04-18 11/week @ 2024-04-25

239 downloads per month
Used in 3 crates (2 directly)


820 lines

🎠 Trotter

Trotter is an experimental crate that aims to make interacting with gemini servers fun and easy.

This crate comes with the trot command-line program that exposes most of its functionality. Install it with the command cargo install --features cli trotter.

There's also Fluffer, a crate for writing gemini server apps.

😊 Requests

Trotter seeks to be a feature-complete gemini client library, while also being easy to use.


If you're only here to send a quick request, refer to the ergonomic trot and trot_in methods.

async fn main() {
    trotter::trot("geminiprotocol.net") // gemini:// prefix and root slash can be elided

    trotter::trot_in("localhost/input", "notice me!")


For more-detailed requests, you can use an Actor.

You can use the builder pattern to easily attach a user agent and client certificate to the actor.

Once you've built an Actor, you can call Actor::get to send a request with it.

use trotter::{Actor, UserAgent};

async fn main() {
    let owo = Actor::default()

🤖 User-agents

Did you know there's a version of the robots.txt standard for gemini? (robots.txt for Gemini)

Trotter has robots functionality built-in. Once you set your user-agent, you will receive a RobotDenied error if you try to access a page you are disallowed from.

I strongly suggest you do this if you're using Trotter for a project that depends on other peoples' content.

🌕 Titan

Titan is a sister-protocol to gemini that allows clients to upload files. (read more)

To use titan, construct a Titan object, and pass it to the Actor::upload method.

use trotter::{Actor, Titan};

async fn main() -> anyhow::Result<()> {
    let g = Actor::default()
            Titan {
                content:  "Example content :DDDDDDDDDDDDDDDDDDDD".into(),
                mimetype: "text/plain".into(),
                token:    None,


🎁 Responses

Once you receive a structured Response, you can either weed through it yourself, or rely on the helper functions it implements to preform common operations.

📖 Parsing

Trotter also provides tools for parsing gemtext.

use trotter::parse::Gemtext;

fn main() {
    let txt = "# 💎
## Is
### So
> effing
* dope
=> /path/to/somewhere i can take u there
``` alt text goes here
Here's a table
| The | Best |
| 😘  | 😪   |

    let gemtext = Gemtext::parse(txt);




If you have access to a posix shell with openssl installed, you can define the following functions to easily generate and inspect x509 certificates.

certgen() { [ -f "${1:?usage: certgen [domain]}.key" ] || [ -f "$1.crt" ] || ( openssl req -new -subj "/CN=$1" -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -days 3650 -nodes -out "$1.crt" -keyout "$1.key" && printf '📜 Cert generated\n' ) ;}
certinfo(){ openssl x509 -noout -text < "${1:?usage: certinfo [file]}" ;}


For now, I want this to be a helpful tool for automating gemini requests. But ultimately, I would like for it to be robust enough to write a complete client with.

  • Write response to file
  • Get response as gemtext
  • robots.txt support
  • Custom errors
  • Cli binary 👀
  • Server certificates
  • Tofu store directory
  • Byte read/write timeout


~244K SLoC