Citrine devlog #1: Converting pages

Now playing: "Boats & Birds" by Gregory and the Hawk

Hello readers! Today, I'll be starting a devlog to document my progress on Citrine [1], my project for publishing gemtext content to HTML.

Overall vision

Citrine will be a tool to help me publish my personal website. As I discussed in my previous post [2], I'm currently publishing my website for both the Gemini [3] and HTTP web protocols. I'm doing this because I want to be a part of the Gemini community, but publishing over HTTP allows me to showcase my design skills and personality better than my Gemini capsule can afford.

To do this dual-publishing, I have to write my site content in both gemtext [4] and HTML. Currently, I have to do this manually: whenever I write a post or make a site update, I draft and edit the post in gemtext, and then convert that gemtext to HTML by hand. This lets me have full control over the process to avoid errors, but it's undeniably tedious, so I would like to find a way to automate the gemtext-to-HTML conversion process as much as possible.

When I searched for existing tools for dual-publishing, I found many servers for proxying HTTP requests to Gemini, such as Gneto [5] and the Smolnet Portal project [6]. These applications would translate HTTP requests to my Gemini server, retrieve the gemtext response, convert the gemtext to HTML, and return the HTML back to the client. (I also found some web servers that can serve to both HTTP and Gemini natively, but none that were recently maintained.)

While these tools would technically meet my needs, they don't quite feel right for my workflow. For example, a few of my site pages require wholly unique layouts in gemtext and in HTML, since their HTML versions include visual elements that can't be implemented in CSS alone. Ideally, I would be able to maintain two copies of certain pages while relying on gemtext-to-HTTP translation for other simple pages.

=> /assets/img/blog/site-alternate-layouts.jpg An example of my site's content and layout in both HTML (left, displayed in Firefox) and gemtext (right, displayed in Lagrange).

I also want a tool that will let me use templating features for my gemtext and HTML documents while still handling the gemtext-to-HTML conversion gracefully. For example, my site's footer has different structures and formats in gemtext and in HTML, but similar content. In either case, I would like to reference the footer as a single template placeholder in my gemtext source content, and allow the tool to fill in either a gemtext snippet or an HTML snippet into the placeholder during rendering as needed. As far as I can tell, there aren't any existing proxy servers or other tools that would handle that scenario gracefully.

Lastly, HTTP-to-Gemini proxy servers aren't a very efficient way to serve static content. My HTTP and Gemini sites are presently being served by static file servers (nginx [7] and Agate [8], respectively) to avoid the overhead of dynamic content servers full of features that I don't need. Adding a proxy server to handle my requests would undo most of the effort I've put in to minimize my server's energy footprint.

(To be clear, proxy servers, dynamic content servers, and CMSes work great for many people! I wouldn't discourage folks from using those tools if they are helpful for getting online and onto the small web. I'm just particularly zealous about computing efficiency in my own projects.)

So, I will be making my own tool to meet my needs - Citrine! My goal for the tool is to be able to take a set of templates (HTML and gemtext), content documents (primarily gemtext), and snippets (HTML and gemtext), and generate both a set of gemtext files and a set of HTML files with their own layouts and styling. I will then be able to transfer those files to my web server to be served with nginx and Agate, possibly using a CI/CD pipeline (like how GitHub Pages works [9]).

Getting started

I've decided to try writing this project in Rust [10], a relatively new programming language similar to C or C++ but with many new features to make security easier. I haven't found an opportunity to use Rust in a client project yet, so I think that this will be a fun learning experience for me. Rust has a lot of features that I find fascinating (in a good way), like traits and borrow checking, so it's nice to get more exposure to those concepts.

So far, I've gotten most of the foundation laid out. Citrine can parse a TOML configuration to discover HTML templates, and then convert a target file to HTML using a template from that config. For example, I can convert my recent flipphone article [11] to HTML:

skylar:~/Documents/citrine (main) $ cargo run test_project > test_project/out/file1.html
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/citrine test_project`

skylar:~/Documents/citrine (main) $ cat test_project/out/file1.html | head -n 15
<!DOCTYPE html>
<html>
<head>
    <title>One year of using a flipphone</title>

    <meta property="og:title" content="One year of using a flipphone">
    <meta property="og:description" content="My experience with using a "dumbphone" as my daily driver.">
</head>
<body>
    <h1>One year of using a flipphone</h1>
<blockquote>Now playing: "Breeze Blows" by Jamie Paige</blockquote>
<p>I celebrated my one-year anniversary of replacing my iPhone with a flipphone a few weeks ago. It was an exciting milestone for me...</p>
<h2>Why use a flipphone in 2025?</h2>
<p>I started reading up on the "dumbphone" movement in the fall of 2024. At that time, I was growing less and less content...</p>
<p>Even more importantly, a growing body of research was showing me that smartphones were damaging to people's attention spans....</p>
            

I also mostly have recursive search and conversion figured out, so that Citrine can convert an entire website at once. Not bad progress for a Rust beginner!

Other thoughts

I'm excited to keep working on this! I haven't had a good personal coding project to focus on in a long time. I will plan to make Citrine available as an open-source project, but it'll be some time before I have an initial version ready. In the meantime, if you have any ideas for features to add, or know of any similar tools, let me know at me [at] skyebound [dot] gay.



Published .