💾 Archived View for auragem.letz.dev › devlog › 20231001.gmi captured on 2024-07-09 at 02:06:43. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-03-21)

-=-=-=-=-=-=-

2023-10-01 Misfinmail Client and Misfin Server Projects

I have started two different projects for the misfin protocol, created by @lem (aka. @lem-two), which is an alternative to email that uses gemtext as its main message format, with some added linetypes for recognizing senders, recipients, and timestamps.

Misfin Capsule

So, I have created a spec-compliant server software at the below Gitlab:

clseibold/misfin-server

As well as a spec-compliant client that can send gemmails and read gembox files:

clseibold/misfinmail

You can find out more information about these projects on the Gitlab pages, but I will provide some details below.

My intention is to always remain spec-compliant in terms of the main protocol and how messages are sent between servers. You can always find the official spec at these two places:

Misfin Spec

Original Reference Implementation created by the Creator of Misfin

Final note, I will be starting a service very soon where people can register to have a mailbox on the AuraGem server. This is good for users that don't have the ability to run their own misfin servers. More details coming soon.

Misfin Server

A very simple misfin server in golang that can now support multiple users. Supports gemmail parsing in gemmail subdirectory as well as a new gembox (mbox for gemmail) format in the gembox subdirectory.

Note: I have also created a misfin terminal client in golang, which you can find on the misfinmail repo.

Copyright: Christian Lee Seibold

License: BSD-3-Clause

Spec-Compliant Incoming Mail Handling

The server follows the spec in adding the sender and timestamp to the gemmail file, as per the sections below, from the spec (gemini://misfin.org/specification.gmi):

Sender lines should be added by the server when saving or retransmitting a message. If a message is forwarded, the original sender line should be preserved and sent alongside, so the final recipient will see both senders:
- misfin spec, section 4.1, "Sender line"

and...

Like sender lines, timestamp lines should be added by the receiving mailserver, and only sent if forwarding a message, in which case they should be left as-is.
- misfin spec, section 4.3, "Timestamp line"

Creating a Cert

Certs will be created when you use the `init` and `make mailbox` commands. More info on these below, in the Running the server section.

Private key of users should be kept to the user, so the server does not store them. Instead, the server stores the certificate information (hostname, mailbox name, full name/blurb, and fingerprint). This is particularly useful on public access multi-user/timesharing systems (like pubnixes).

For the server certificate and mailinglist certificates, the private keys are stored on the server so that the server can use them to make requests to other misfin servers. Mailinglist certs will be stored in the configured mailboxes directory.

Building the server

go build -o . ./...

Note: Upon releases, pre-compiled binary files are added as artifacts. To view these, go to `Deploy > Releases` in the sidebar on Gitlab.

Running the server

Initialize the server and create your server certificate (which acts as a CA for all of the mailboxes) by running the `init` command:

misfin-server init

It will interactively guide you through setting up your server and cert, as well as ask you for a config_filepath where your configuration file is saved. If you do not go with the default location, then this config_filepath must be passed to every command from here on.

You can start the server with the `serve` command. Give it your config_filepath that you set during the init step. If you do not, then the default of `./misfinserver.conf` is used.

misfin-server serve [config_filepath]

Or you can add mailboxes with this command. Give it your config_filepath that you set during the init step. If you do not, then the default of `./misfinserver.conf` is used.

misfin-server make mailbox [config_filepath]

Currently, you must run the `make mailbox` command while the server is not running.

Finally, you can change the config using the `config` command. You can configure the following fields: `port`, `bind-address`, `server-cert`, `mailbox-dir`.

Creating a mailinglist

You can run the `make mailinglist` command like below and it will guide you through creating a mailinglist.

misfin-server make mailinglist [config_filepath]

The command will prompt you for a mailbox name and a full name (aka. blurb). It will then generate the key, which will be stored on the mailingserver so that messages sent to the mailinglist can be forwarded.

To subscribe to a mailinglist, send a message with the subject of "Subscribe" to the mailinglist address. The body of the message is ignored.

Importing gembox and gemmail packages

This repository offers three useful packages for creating both misfin servers and clients. The `misfin_client` package will implement a simple client that can send requests to a misfin server and receive the response. The `gemmail` package handles parsing gemmail files (as per the misfin spec), and allows you to make changes (like prepend a sender or timestamp) to the gemmail. The `gembox` package is a parser for the gembox file format outlined in the Gembox section below. It is a flat file of multiple gemmails separated by a separator line. The gembox package will split these up into multiple gemmails and wrap around the `gemmail` package.

You should be able to import the misfin_client, gembox, and gemmail packages into your own project like so:

import "gitlab.com/clseibold/misfin-server/misfin_client"
import "gitlab.com/clseibold/misfin-server/gembox"
import "gitlab.com/clseibold/misfin-server/gemmail"

Gemmail Parser

The gemmail parser (in the `gemmail` subdirectory) will parse both the spec-specific gemmail linetypes as well as all gemtext linetypes. Gemtext linetypes are placed in a separate array (`GemMail.GeminiLines`) of the GemMail struct to distinguish them from gemmail linetypes.

Call the `String()` function on a GemMail variable to convert the gemmail back into a string for saving. You can also append Senders, Receivers, and Timestamps to the GemMail before converting it back into a string.

The Gemmail Format (From the Spec)

The gemmail format is like a gemtext file, with three new linetypes. The three new linetypes are as follows:

Lastly, a message's subject is always assumed to be the first gemtext level-1 heading (the first line starting with `#`). All other lines should be interpreted as per the Gemini gemtext spec.

If you are forwarding a gemmail, keep all sender lines. Each receiving mailserver will add a sender line for each sender.

Gembox (.gembox or .gmbox file) and Gembox parser

The Gembox format was something I created to be a single-file storage of multiple gemmails, inspired by the concept of mbox files in Multics and Unix, and other Operating Systems.

Gemboxes are gemmails with the divider `<=====\n` appearing on a separate line in-between each gemmail. This divider never appears at the beginning or end of a gembox file (they are always between two gemmails). When you receive mail, the mail will be appended to the gembox file and will be printed to the terminal.

The Gembox parser (in the `gembox` subdirectory) will simply split a multi-line string based on the divider linetype `<=====\n` to get each individual gemmail. Then, each gemmail will be passed to the Gemmail parser mentioned above. The GemBox struct contains a slice of GemMails.

Call the `String()` function on a GemBox variable to convert the gembox back into a string for saving. You can also append GemMails to the GemBox before converting it back into a string.

The divider was chosen because it starts with a gemmail linetype (the sender line, using the `<` character), however `=====` is an invalid address. This means readers that don't support gembox will either fail on the line or ignore it if they are trying to parse addresses correctly. The intention is that this hopefully introduces some backwards compatibility, however, there may be some other way of doing this better. I am not set on the specific divider that I have chosen, so feedback is welcome.

Misfin Client Library (under `misfin_client` package)

The `misfin_client` package is a client library for misfin that was heavily based off of [makeworld's go-gemini client library](https://github.com/makew0rld/go-gemini). The ISC license for makeworld's client library can be found in `LICENSE-CLIENT`. Additions and modifications by me are licensed with BSD-3-Clause.

Below is a simple example of a client that sends a message to `clseibold@auragem.letz.dev`:

package main

import (
    "fmt"
    "os"
    "gitlab.com/clseibold/misfin-server/misfin_client"
)

func main() {
    message := "# Test\nThis is a test gemmail!"
    certFile, _ := os.ReadFile("mailbox.pem")
    client := misfin_client.Client{}
    resp, err := client.SendWithCert("misfin://clseibold@auragem.letz.dev", certFile, certFile, message)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%d %s\n", resp.Status, resp.Meta) // Should print `20 <fingerprint>` if server is running
}

Misfinmail Client

A misfin mail terminal client written in golang and modelled after the Multics mail commands. It will let you send gemmails using your sender cert and read gemmails from a gembox.

A feature that I am excited about having added is the ability to add a gemini fetch address for your gembox, where the command will fetch your gembox from gemini using the address, with the gemini client cert set to your sender cert. The fetched gembox will then be downloaded to your configured gembox filepath.

For those who have gemini servers, you can setup a route that serves your gembox and verifies that the client cert's fingerprint (SHA256) is the same as your sender cert's fingerprint. This is also a useful feature for those who want to become misfin mail hosters. You just need to provide the user with this fetch address.

For server hosters who choose not to use the gembox format to store gemmails on their server, you can create a route that loads all of the gemmails on the server and constructs, on-the-fly, them into the gembox format. To do this, you simply need to concatenate the gemmails onto each other, separating them with a line of `<=====`. Should be fairly simple to program.

I am considering adding a new configuration option to this misfin client to use a folder that contains individual gemmail files instead, but this is not something currently in the program.

Copyright: Christian Lee Seibold<br>

License: BSD-3-Clause

Building/Installing

You can get precompiled binaries for x64 Windows and Linux on the [Releases Page](https://gitlab.com/clseibold/misfinmail/-/releases).

go build .

If you have golang, you can instead install the program using the following command:

go install gitlab.com/clseibold/misfinmail/v2@latest

It will install to the bin directory in your GOPATH. Make sure `$GOPATH/bin` is put into the PATH environment variable to be able to run the command.

Running

On first run of the program, it will create a `misfinmail.conf` configuration file in your Home Directory (%USERPROFILE% on Windows). It will then prompt you for the path to your sender certificate and the path to your gembox. These will be placed into the config file and loaded on each run of the command.

It will also prompt if you want to set a gemini fetch address. This feature will fetch your gembox from the gemini address, using your sender cert as the gemini client cert, and save the gembox to your configured gembox path. This fetch will happen on every run of the command to ensure data is up to date.

Currently, the fetch command does not keep track of last message fetched, which means if you delete anything in your local gembox, the results will not be saved as the fetch will always overwrite your local gembox with the contents of the gembox from the fetch address. I'm currently considering ways of improving this situation.

If you run your own gemini server, you can setup a gemini fetch address by making a route to your gembox which requires a client certificate, and the client certificate SHA256 hash should match your sender cert's SHA256 hash (called the `fingerprint` in misfin docs).

Help

misfinmail help

Send Mail

Send mail with this command, providing the address. If you exclude the optional message argument, the command will prompt you for the subject and message body. The message body can be multiline. End the message body with a single `.` by itself on the last line.

misfinmail send address [message]

You can specify a certificate using the `--cert cert_path` flag. This will temporarily override the default certificate specified in the `~/misfinmail.conf` configuration file.

Read from Gembox

This command will read from the gembox configured in your configuration file. You can provide the command an optional number argument to start at that message number.

misfinmail read [num]

You can specify a gembox using the `--gembox` flag. This will read from the specified gembox instead of the configured default gembox filepath.

misfinmail read --gembox archive.gembox

This command will print out each gemmail and prompt you for input on whether you want to delete that gemmail from your gembox or not. The commands are as follows:

You can also specify the `--list` command to list out a summary of the gemmail in your gembox.

Fetch Command

This command will download a gembox from the configured gemini fetch address to the configured gembox filepath.

misfinmail fetch

You can pass the `--gembox` flag to specify a different gembox filepath from the configured default. The example below will fetch from the gemini fetch address to the archive.gembox file. The program *will not* change the gembox configured as the default in your configuration file.

misfinmail fetch --gembox archive.gembox

You can also pass in the `--address` flag to specify a gemini fetch address different from the configured default. The example below will fetch from the specified gemini address and store the gembox in the archive.gembox file.

misfinmail fetch --gembox archive.gembox --address gemini://auragem.letz.dev/mail/user/gembox

Configuration

You can modify your configuration file anytime by using the `config` command.

misfinmail config field value

The current possible fields are:

The `gembox` and `sender-cert` fields take paths as their values. The `gembox-fetch` field takes a gemini address as its value.

Contact Me

You can contact me via misfin at the address `clseibold@auragem.letz.dev`.