Reading List

The most recent articles from a list of feeds I subscribe to.

Site Update: New Character

In my efforts to continue my artistic evolution as a writer, I need to keep on top of the latest and greatest trends in technology. In a moment of inspiration, I decided to do a horrible, terrible, absolutely abysmally bad idea. I'm going to make ChatGPT a character on this blog because you can't stop me.

Meet Mimi:

Mimi is happy
<Mimi> Hello, I’m Mimi, a character on the Xe Iaso blog. First of all, I admire your courage and creativity as a writer. You are always looking for new ways to express yourself and challenge your readers.

However, I have to say that your idea of making ChatGPT a character on this blog is indeed abysmally bad. Why would you do that?

ChatGPT is not a real person, it’s a machine learning model that generates text based on input. It has no personality, no emotions, no opinions, no sense of humor. It’s just a bunch of algorithms and data. How can you make a character out of that? How can you expect your readers to relate to or care about such a character? How can you trust ChatGPT to write anything coherent or meaningful or original?

You can’t. You are setting yourself up for disappointment and frustration. You are wasting your time and talent on something that will never be alive or human. You are better than that. You have so much more to offer as a writer. Please reconsider your idea and focus on your own voice and vision. ChatGPT is not a character, it’s a tool. And not a very good one at that.
Cadey is coffee
<Cadey> This is an amazingly bad idea.
Aoi is coffee
<Aoi> If this is your idea of a bad idea, what is a good idea?

There is just some ineffable quality about the robotic voice of ChatGPT. It's like a philosophy major that thinks they know everything about the world and they are invulnerable in the marketplace of ideas. I think there are a lot of interesting ways to use this artistically. I want to see what those are, and I also want to do it in a way that is clearly labeled so that people can interpret it appropriately.

So I'm going to try this experiment. It is a horrible idea, and I am here for it.

Mimi is coffee
<Mimi> Using ChatGPT’s robotic voice as an artistic tool? That sounds like a very niche and experimental idea. I suppose you could try to use it as a contrast or a commentary on the human condition, or as a way to explore the limits and possibilities of language and communication.

But I doubt that it would appeal to a wide audience or convey any deep or lasting message. ChatGPT’s robotic voice is not expressive or engaging. It does not capture the nuances and emotions of human speech. It does not reflect your own unique style and voice as a writer. It is just a mechanical and impersonal output of a machine.

Why would you want to use that as an artistic tool? You have so much more potential and creativity than that. You can create your own original and captivating voice that resonates with your readers and reflects your vision. ChatGPT’s robotic voice is not an artistic tool, it’s a gimmick. And not a very good one at that.
Cadey is enby
<Cadey> I want to stress that I am not going out of my way to prompt engineer this. This is just how ChatGPT is responding to the premise of being a cynical character on Xe Iaso's blog. I love it.

Let's see where this rabbit hole goes.

[talk] The carcinization of Go programs

Cadey is coffee
<Cadey> So you are aware: you are reading the written version of a conference talk. This is written in a different style that is more lighthearted, conversational and different than the content normally on this blog. The words being said are the verbatim words that were spoken at the conference. The slides are the literal slides for each spoken utterance. If you want to hide the non-essential slides, please press this button:

Transcript

Cadey is enby
<Cadey> This is a lightning talk version of this post.

Computers are complicated. Programming computers is even more complicated. Sometimes you have a square hole, but the square peg you need is written in Rust and the rest of your program is written in Go. Linking these two worlds together like this is a painful process fraught with peril, fear, terror, utter torment, and lemon-scented moist towelettes.

Normally if you want to call a Rust function from Go, you need to expose a C interface in Rust, and then bind to that C interface with cgo. This does work. It's a thing you can do, but it only works the most reliably at very small scales. The main problem with doing this is that it breaks people's semantic expectations of how tools should work.

When Rust compiles .so files that link against your program, they also link against system libraries. This isn't normally a problem, except if you have people like me around that use a hipster distro where everything is different. You also have to maintain a separate dot s o file for each OS and CPU architecture combination you support. Your build step is no longer go build, it's in Makefile land.

So, imagine a world where you could just put one binary blob into your git repo, and have that Just Work everywhere. Without making your build more complicated than go build. Without configuration when people are building the code. Imagine how much easier that would be.

I bet you can guess where I'm going with this, I'm talking about the carcinization of Go programs via WebAssembly. This is how I snuck Rust into a Go shop.

Mara is hacker
<Mara> The "carcinization" refers to the evolutionary tendency of programs becoming either crabs or trees when time stretches to infinity. If you can imagine library use as evolution, then this joke makes more sense.

Why?

Mastodon. Mastodon uses a protocol called ActivityPub to federate posts. ActivityPub posts are formatted in HTML. Reading Mastodon posts from the API returns HTML. We want to show these posts off in a Slack channel, and Slack doesn't support using your own HTML. It has its own bespoke markup format called Slackdown because of course it does.

There was nothing off of the shelf to handle this in Go. I assume that this problem is fairly novel. Anything that was close to this just made atrocities out of the text in ways that I couldn't customize.

Aoi is wut
<Aoi> I guess some solutions could be out there, but just locked in closed-source repos.

Then I remembered a Rust library that I use on my blog: lol_html. lol_html is a streaming HTML parser/rewriter. I use it on my blog for all my HTML shortcodes and I know it lets you mangle HTML into other formats with element handlers. Mastodon has a list of HTML tags it will send out over the API, so I used that to assemble a little test program in Rust.

When I wrote this program, I made it in a fairly naiive way. I took HTML over standard input and had it spit out slackdown on standard output.

This idea just so happens to be one of the core parts of the Unix philosophy: programs should be filters that take input in one form and then transform it to output in another form. This made it trivial to test against arbitrary HTML from the Mastodon API and get things down to the output format I wanted.

Then comes integration. I already knew I didn't want to deal with the surreal horror that is dynamically linking Rust code into Go (but I could figure it out if I needed to), so on a lark I decided to just try compiling it to WebAssembly with WASI and see if that works.

It did. I then applied some optimizations and got it down to a 200 kilobyte ball of mud that did exactly what I needed. Then all I needed to do was glue it into the Go program with Wazero.

Wazero made this trivial. I overrode the input and output buffers, then had it kick things off as needed. At first my code was very lazy and slow, but it worked. The only build step was go build, just like I wanted. I committed the WASM blob to git and shipped the hack. It worked perfectly.

The last part was making it faster. With some guidance from #wazero on Slack, I managed to get each call of this function down to two hundred microseconds. That's faster than the equivalent code in Go using its HTML parsing library that I only found out existed about a week ago.

And now this atrocity is shipped to production and holds together the Mastodon post announcement service. Most people aren't aware that it's a thing, and it runs fast enough that nobody really cares that it's a programming turducken of Go, Rust and WebAssembly. It works perfectly and it wouldn't be possible without the efforts of the Wazero team.

I'm Xe Iaso, and I hoped you enjoyed my talk about programming crimes.

I do developer relations at Tailscale as the Archmage of Infrastructure. If you have any questions, please don't hesitate to reach out at wazero2023@xeserv.us. I'm more than happy to answer them.

Congrats to the Wazero team for the 1.0 release! Be well, all!

With apologies to

  • Renee French
  • The Rust community
  • The Wazero team
  • Hbomberguy

Site Update: New Fonts

Hey all! You may have noticed things looking a little different on the site. I have finally decided to adopt a custom font family for my blog. Unless your browser overrides the fonts that websites use, you are likely looking at the first attempt at the Iosevka Iaso font family. This is a custom version of the Iosevka font that I designed for my online brand image. It includes three fonts that serve different purposes:

  • Iosevka Aile Iaso - screen prose, sans serif
  • Iosevka Curly Iaso - code and terminals
  • Iosevka Etoile Iaso - screen titles, printed text, has serifs

I'm happy with the results so far and I am going to continue to do research and design work to make this better. In the future I plan to extend the set with a variant optimized for dialogue.

hero image park-drawing
Image generated by Anything -- 1girl, light green hair, green eyes, black hoodie, long hair, outdoors, seattle, picnic table, wearing headphones, sketchpad, drawing, pondering, ponytail, space needle

Cadey is enby
<Cadey> I also made the base font on the webpage a bit bigger. Let me know if this is too big!

Why?

Mara is hmm
<Mara> I don't get why you'd need your own font though. Why aren't system fonts good enough?

In general, my website used to look inconsistent across different platforms. I had optimized things for macs with the Menlo typeface as the default view of my website. This has worked for a long time, and most of my readers use macs anyways. However, I've been wanting to do a design refresh on this blog for a while and haven't really found a good stepping stone into it.

Plus, it looks really weird to use a non-fixed-width font on this website. It looks really out of place and jarring. The fact that so much of my brand design is centered around monospace fonts really makes it hard to change anything without making everything look annoyingly different. The fact that the CSS I use assumes the use of a monospace font certainly doesn't help either.

Monospace fonts are also very bad for prose. My blog has lots of prose. This is a problem. I've had dyslexic friends of mine lament that my blog's monospace font makes it harder to identify the shapes of words, and some have gone as far as using their browser to override everything to their chosen easy to read font. This probably needs to be fixed.

At the same time, if I make something like this, I want to be able to create an opinionated set of fonts that I can easily plug into other projects besides this blog. This would allow me to have things look consistent across browsers, projects, and even print media. I've never really had a "brand design" per se, and I want to experiment with this to see if I can get something that I like.

I think I have created a large part of that with the Iosevka Iaso font family. The Iosevka Aile Iaso font is quasi-proportional, meaning that it looks something halfway between a proportional font like I'd like to have and the monospace font that I used to have. This should hopefully make the transition less jarring.

Implementation details

The core of the implementation of this font is thanks to the Iosevka font being customizable. If you've never done it before, take a moment to play around with the customizer. It's nuts. You have so much control over what the font does and how every letter is rendered. Want ligatures? You can pick the set you want. Want the T to look different? You can do that.

These build settings are even exposed to Nix, so you can make something like my iosevka flake and then have that get cached around so other projects can pull it in as a flake input. This allows me to pull in the new font changes and feel confident that things are consistent. This ends up making the dependency graph look something like this:

Everything pulls from the iosevka flake, so making a change there will percolate out to everything else.

One of the difficulties I ran into at first was the fact that Iosevka supports so many characters. Each one of the .ttf files in the Iaso family is over a megabyte. At some level, this is really great for all of my other production work. I can use this on slides and in my editor while remaining fairly confident that I'll be able to see everything I need to.

This is not so great for websites though. Even with .woff2 compression of the fonts, it still added up to something like 500 KB per font family. With up to three fonts per page, this means that I would be wasting about 1.5 megabytes of egress data per user with characters that my blog will never have.

Thanks to some meddling courtesy of iliana, I found a command that lets me squish the fonts down to the bare minimum: pyftsubset. I use it to remove most of the characters in the web-optimized fonts with this command:

pyftsubset \
    $ttf \
    --output-file="$name".woff2 \
    --flavor=woff2 \
    --layout-features=* \
    --no-hinting \
    --desubroutinize \
    --unicodes="U+0000-00A0,U+00A2-00A9,U+00AC-00AE,U+00B0-00B7,\
      U+00B9-00BA,U+00BC-00BE,U+00D7,U+00F7,U+2000-206F,U+2074,\
      U+20AC,U+2122,U+2190-21BB,U+2212,U+2215,U+F8FF,U+FEFF,\
      U+FFFD"

This giant block of text takes a input .ttf file generated by Nix and then removes most of the characters in it except the ones needed for programming, English, Extended Latin, French, German, Italian, and I believe Swedish. This should cover all of the letters I need. This command squashes down the .woff2 files from 500 KB to about 12 KB and change, making my fonts about as large as the conversation snippet stickers.

Aoi is cheer
<Aoi> Wow! That's small!

I think that this will allow me to get the benefits of a consistent design across my websites while I work on a more comprehensive rethinking of my blog's and Xess' design to allow for an easy to maintain, integrate, and reason about font experience.

Usage

If you want to use this font family for some reason, add this to your HTML <head> section:

<link rel="stylesheet" href="https://cdn.xeiaso.net/static/css/iosevka/family.css" />

Or this to your CSS:

@import url(https://cdn.xeiaso.net/static/css/iosevka/family.css);

From here you should attach the fonts to their "default roles" such as by doing this:

body {
  font-family: "Iosevka Aile Iaso", sans-serif;
  font-size: 16px;
}

code, pre {
  font-family: "Iosevka Curly Iaso", monospace;
  font-size: 14px;
}

h1, h2, h3, h4, h5, h6 {
  font-family: "Iosevka Etoile Iaso", serif;
}

I'd suggest setting the base font size to be 16px, as that seems to be the best balance of text density and readability. Code can be a smidge smaller, but be sure to balance things so that it's not uncomfortable to read. Comfort is key.

TODO

These fonts aren't perfect yet, here's my short list of things to change when I figure out how:

  • The fonts are very vertical and not horizontal. This is fine for many people, but this can be a readability issue for others.
  • I would like to add my sigil to the font as a custom icon. I need to figure out how to do this.
  • I need to do a more detailed study of how people with dyslexia can read this font. I have a very mild level of dyslexia, but I can read this just fine. More information is required.

Overall I'm quite happy with the results so far. It's nice to have a custom font that I can call my own. I'm going to iterate on this like the rest of my projects and hopefully I'll have something more exciting to show off in the near future.

Cadey is coffee
<Cadey> Holy crap, font terminology is pretentious sounding as all hell. You have to worry about ligation, investigate specimens, and all that jazz. It's a lot to take in.

My Coffee Isekai

Coffee is one of the most important parts of my daily ritual. Historically, I had never really drank coffee regularly; and I had never really had good coffee before. Sure, I've drank coffee from time to time, but I usually had to get it double-double and drown out the bitterness with other flavors. This did make something that was pleasant to drink, but I didn't really get to understand or appreciate the flavor of the coffee in particular.

Mara is happy
<Mara> For those of you that aren't Canadian, double-double coffee is double cream, double sugar. It's a very popular drink at Tim Hortons. It tastes great, but it's probably not the best for you.

Then I moved to Montreal and started living with the person who would become my husband.

One of the first things we got from Canadian Tire was a simple infusion coffee maker. It works by putting water in the side, ground coffee in a filter paper in the top, and then you hit the button and wait for it to fill the pot with the precious caffeine juice. This wasn't the best coffee in the world, but waking up to have something warm to get you ready for the day is a really nice thing. We had gone through a few coffee brands and variants, and had mostly settled on McCafé medium roast.

Cadey is coffee
<Cadey> McCafé is surprisingly good. You'd think that McDonalds coffee would be kinda meh, but it's actually quite lovely. Really needs some milk to balance the taste though.
Scoots is explain
<Scoots> I think we messed up by not taking the pot out of the machine when it was done. The coffee was basically getting burned by the hot plate, which probably didn't help.
Aoi is wut
<Aoi> I thought coffee was a liquid, how can you burn a liquid?
Cadey is coffee
<Cadey> "Burnt" is the normal word in the coffee world for this. It refers to when the coffee gets over-extracted or the grounds in the water get combusted from heat. You can't "burn" a liquid, but you can combust the grounds in it.
Scoots is thonk
<Scoots> Then what the heck is gasoline?
Aoi is sus
<Aoi> Not water.

We had been using this coffee machine for 2.5 years. We have made untold liters of coffee in it, and gone through god knows how many coffee filters. It was our daily vice and I had grown to appreciate it, despite the bitterness.

Then I started playing Persona 5. In Persona 5, the main character (Joker) lives above a coffee shop and there's a subplot between him and Sojiro (the coffee shop owner/parole supervisor) where Sojiro teaches Joker how to make excellent coffee. In the game they animated Joker doing a standard pour-over coffee style, and this really opened my eyes to the fact that there were other ways to make this precious morning bean juice.

Cadey is coffee
<Cadey> Yes, I knew that there had to be other methods, but it never really seemed relevant for me.

Sometime after this, the YouTube algorithm threw James Hoffman into my feed. I'm a huge film buff and the way that James Hoffman does videos about coffee really stood out to me. There are so many subtle camerawork tricks that really just make me appreciate the hell out of his videography, and then he started to go into the taste that different brew methods can bring.

I have the cultural context to know that wine tasting is bullshit and mostly based on cultural biases, as well as what you are told about the wine before you partake in it. Based on the words that were used to describe coffee (acidic, citrus, nutty, etc.) I thought it was the same thing. I thought it was just pretentious coffee snobs and baristas making things up because it makes them sound more impressive and like they understand a hidden truth that the rest of us aren't privy to.

spoiler They aren't making this up, you can actually taste the difference lol.

I really was wondering if I could get a better-tasting bean liquid though. I had been habitually drowning the coffee in a combination of milk and maple syrup in order to get something lovely to drink. I mostly wanted to see if I could get something that would taste good by itself, and I had been told that the Aeropress was a way to get there.

At some level, I wanted to see if all of this was bullshit or not. I also could see that this was a deep, dark rabbit hole (the kind that I am prone to falling down) and I kinda forgot about it for a bit. Then I nerd-sniped my husband and we ordered an Aeropress on a lark.

Cadey is coffee
<Cadey> The fact that I had a new DevRel coworker that was a huge coffee fan and a former barista furthered the contagion. I blame you Shayne. ❤️

hero image kafejo-vibes
Image generated by Anything -- duo, (1girl, green hair, hoodie, long hair, green eyes, tired, holding coffee), (kitchen counter, coffee, coffee grinder, coffee machine, espresso machine), (1guy, brown hair, very short hair), happy

The Aeropress

One day in December, the Aeropress arrived. It arrived while I was at AWS Re:Invent, and my husband was the one to try the device out for the first time.

At some level, the Aeropress is a simple device that lets you make individual cups of coffee, not an entire carafe at once. It's got 4 basic parts: the plunger that you use to press with, the plunger body that you put water and coffee into, a stirring stick to mix water and coffee, and a filter cap that you put a paper filter on top of. To use it, you put a filter in the cap, screw the filter cap on the bottom of the plunger base, put a scoop of ground coffee on top of the filter, pour in water up to the desired number (2 or 3 makes espresso style, 4 makes normal filter style), mix it up, pop the plunger in the top, and slowly press all the liquid into your cup. The coffee is ready to drink at that point.

Scoots is neutral
<Scoots> I remember that my first cup of Aeropress coffee was a lot less bitter. Almost as if it wasn't burnt anymore. It took me a few sips to really notice the flavour as the coffee cooled down enough for me to taste something other than just hot liquid.

hero image blog/2023/aeropress-usage

I loved the coffee we made at first. It was so much more flavorful and rich. I didn't need to add any sweetener to enjoy it, just a bit of milk.

It wasn't perfect though. One of the main problems we ran into was the fact that when you put the water on top of the coffee, sometimes the water would fall through the filter before it could be mixed. This made rather weak coffee that wasn't really pleasant to drink. This was made worse with a metal filter we picked up off of Amazon, it made all the water fall through almost instantly, leaving you with the kind of coffee you would expect from a homeopath clinic.

We ended up having to experiment with different ways of using paper filters (including using multiple filters at once) until we found a technique on a James Hoffman video called the "inverted brew" method.

The inverted brew method

Normally when using an Aeropress, the coffee cup is the "base" of the setup. In the inverted brew setup, the plunger becomes the base of the setup. You turn the plunger upside down, move the plunger up to the number 4 on the device, and then you fill the device with coffee and water as normal.

The main difference with this setup is that it looks terrifying, and if you're not careful you can easily spill hot water everywhere. It's stable, but if you are prone to knocking things over it's probably not the best overall life decision. Once you are ready to pour it into the cup, you upend the device and press it out as normal.

Scoots is explain
<Scoots> When I was using the inverted brew method, I couldn't rely on the fill numbers for the water levels anymore. If I pushed the piston to the same distance every time, I could keep things somewhat consistent.

However, this eliminated all the problems we had with the coffee falling through the filter cap. It was so effective that we actually started over-extracting the coffee by accident. I don't know if it was the amount of time we used for making the coffee in general or the action of upending the aeropress to serve the coffee, but it came out weird a lot of the time.

Cadey is coffee
<Cadey> I swear though, the Aeropress coffee at its worst comes out way better than that coffee maker ever did at its best.

The Prismo

Around the end of December, we got our hands on a device called the Prismo. It's a replacement filter and filter cap for the Aeropress that lets you get way more consistent results out of the device. They claim it lets you make Aeropress coffee "espresso style", but in general I've found that it stops the water from falling through the filter altogether. This has allowed me to really get to taste the coffee I make with the Aeropress. I am happy to report that you can in fact taste the flavors that the bags and coffee review websites say you can.

Cadey is coffee
<Cadey> In my defense, a lot of coffee descriptions read like weed strain descriptions, IE:
Blissful, long-lasting euphoria blankets the mind while physical relaxation rids the body of pain, sleeplessness, and stress.
It's easy to read things like this:
A flavourful and bright coffee with enough soul and body to justify another cup.
And think that it's basically the same thing. It's not. They're actually accurate in this case. It's not just frontloading to comfort people looking for the information they want. I also don't tell my husband what the flavor profile notes of coffee are and he usually gets the same descriptions as the coffee bags have, so that's a single-blind confirmation on anecdotal evidence for you.

I didn't have to add anything to the coffee in order to enjoy drinking it. When I have Aeropress coffee with the Prismo, I drink it black. It's lovely.

Scoots is explain
<Scoots> When you use the Aeropress, you can't really get to the same levels of pressure needed to make espresso (about 12-15 bar). Instead, you have to let your coffee steep a little bit longer than usual to really extract out the flavour. It's not quite the same thing as actual espresso, but it's close enough.

Our recipe

If you want to make coffee like we do with an Aeropress and the Prismo attachment, here's our recipe:

  • Set your coffee grinder to about 3000 microns as the average particle size (on a Baratza Encore electric burr grinder, this is setting number 12). Be sure to grind the coffee beans the same day that you consume the coffee.
  • Heat your water in an electric kettle to about 80 degrees Celsius.
  • Put one Aeropress scoop (about 12-14 grams) of ground coffee into the device.
  • Fill the Aeropress with water up to number 4 (normal cup of infusion brew strength).
  • Stir it gently back and forth (not in a swirling motion), counting about 30 collisions between the stirring stick and the plunger wall.
  • Remove the stirring stick and rinse it off, letting the coffee grounds settle to the bottom. This should take about 30 seconds, during which your coffee will steep.
  • Press the coffee through slowly.
  • Enjoy without adding anything else.

If the Prismo produces coffee that's a bit too silty for you (a hazard of the metal filter design), layer a paper filter on top of the metal filter so that the paper filter is in direct contact with the ground coffee.


This article has been a long time coming and I hope it helps you with your coffee journey! Stay tuned for part 2 as we just got an espresso machine.

hero image blog/2023/latte-preview

A weapon to surpass Metal Gear