initial commit
This commit is contained in:
commit
ed6c1e9522
58 changed files with 2221 additions and 0 deletions
29
content/blog/building-this-site.md
Normal file
29
content/blog/building-this-site.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: Building This Site
|
||||
date: 2025-04-10
|
||||
---
|
||||
|
||||
I care about thoughtful design, and forcing your visitors to download dozens of
|
||||
megabytes of assets and JavaScript dependencies just to read a single article
|
||||
doesn't feel very thoughtful to me. That's why I've committed to a 1 megabyte
|
||||
upper limit for every page on this site. There's also no point for there to be
|
||||
any JavaScript here, so I'd rather not include it, even if some fancy features
|
||||
may rely on it.
|
||||
|
||||
My goal on this website is simply to share my thoughts. I can't really do that
|
||||
if I'm distracted by the framework. There should be minimal barriers between
|
||||
having an idea, and turning it into a blog post.
|
||||
|
||||
I think the simplest and most elegant way to build a personal website, and the
|
||||
approach I've taken here, is to use a Markdown-based static site generator.
|
||||
Markdown is _nice_ (although I do have some complaints), and because the
|
||||
building blocks are so simple, the output should be cleanly viewable even on an
|
||||
old copy of Internet Explorer.
|
||||
|
||||
Now, I must admit, I _did_ take the unnecessarily complicated route of building
|
||||
my **own** static site generator. It's probably unnecessary, considering how
|
||||
many great tools already exist, but what can I say? I like building stuff.
|
||||
|
||||
Now, I have a system where I can update this site with very little effort. All I
|
||||
need to do is add a new file, write some content in Markdown, and commit it to
|
||||
the repo.
|
256
content/blog/email-in-helix.md
Normal file
256
content/blog/email-in-helix.md
Normal file
|
@ -0,0 +1,256 @@
|
|||
---
|
||||
title: Writing Emails In Helix
|
||||
date: 2025-05-29
|
||||
---
|
||||
|
||||
This article is all about writing emails in Helix. Obviously, Helix isn't an
|
||||
email client -- and it's lacking in a plugin system, so you can't really turn it
|
||||
into one, either. And that's okay! You might be wondering, then, what exactly do
|
||||
I mean by "writing emails in Helix"?
|
||||
|
||||
An email client needs to do a whole lot more than write text! It needs to
|
||||
connect to your mail servers, show your messages, display and allow you to
|
||||
interact with flags/labels, be able to **send** your email, etc. Only an insane
|
||||
person would try to hamfist this functionality into a text editor (I'm looking
|
||||
at you, Neovim plugin developers!).
|
||||
|
||||
Helix is **really** good at its _one_ job, and that's **editing text**. It is a
|
||||
text editor, after all. As it turns out, _writing_ emails is, well, text
|
||||
editing, which is Helix's forte. So why not take Helix's amazing writing
|
||||
experience, and apply it to email? If you're willing to take the plunge, you may
|
||||
be pleasantly surprised at how **joyful** writing email can actually be!
|
||||
|
||||
There are a few different ways to approach composing emails in Helix. This post
|
||||
covers what worked best for me. That is, using Helix as a composer for the
|
||||
`aerc` email client. It wasn't straightforward to figure out at first, but
|
||||
all-in-all, the configuration isn't actually that complicated, so you should be
|
||||
able to set it up in no time.
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [Aerc Integration](#aerc-integration)
|
||||
- [Naive Approach](#naive-approach)
|
||||
- [Helix Workspace](#helix-workspace)
|
||||
- [Markdown Highlighting](#markdown-highlighting)
|
||||
- [Spellcheck](#spellcheck)
|
||||
- [Formatting](#formatting)
|
||||
- [Conclusion](#conclusion)
|
||||
|
||||
<!--toc:end-->
|
||||
|
||||
## Aerc Integration
|
||||
|
||||
At the end of the day, an email is just a text file. An `.eml` file, to be
|
||||
exact. So, you _could_ just open up a fresh buffer, manually type in your
|
||||
headers, and go to town. When you're done, you can figure out _some_ way to
|
||||
actually send that file (perhaps dragging and dropping it into your mail
|
||||
client).
|
||||
|
||||
However, that's not really ideal. For starters, emails have a **lot** of
|
||||
headers, and if you mess any of them up, your message might not get delivered.
|
||||
You probably want to be able to, at the very least, **reply** to an email and
|
||||
have its headers be auto-filled.
|
||||
|
||||
Therefore, some level of integration with an actual email client is desirable.
|
||||
Let the client handle every _other_ aspect of reading and sending email, and
|
||||
relegate **writing** to Helix. This is the approach I'll be covering.
|
||||
|
||||
## Naive Approach
|
||||
|
||||
The simplest approach should work with zero configuration. `aerc` uses your
|
||||
`$EDITOR` to compose your emails by default; so you can get started using Helix
|
||||
right away! However, this is missing two important features: **spellchecking**
|
||||
and **formatting**. To that end, we have to figure out how to get Helix to load
|
||||
a custom configuration for emails.
|
||||
|
||||
## Helix Workspace
|
||||
|
||||
As you may know, Helix gives you an option to specify _per-project_
|
||||
configuration. This requires creating a `.helix` directory in the root of your
|
||||
project, and filling it with `config.toml` and `languages.toml` files that
|
||||
override your default config. _However_, there isn't currently a way to specify
|
||||
_filetype-specific_ options; this is where we have to get creative.
|
||||
|
||||
I recommend creating a folder inside your `aerc` config called `helix-config`.
|
||||
Its name doesn't actually matter, so you can call it whatever you want! This
|
||||
directory will serve as our _workspace_. The basic idea is to have `aerc` open
|
||||
email files with this directory set as the current working directory — which is
|
||||
what induces Helix to load the workspace config.
|
||||
|
||||
Inside our new workspace, let's create the `.helix` directory and populate
|
||||
`config.toml` with our preferences:
|
||||
|
||||
```toml
|
||||
[editor]
|
||||
text-width = 74
|
||||
[editor.soft-wrap]
|
||||
enable = true
|
||||
wrap-at-text-width = true
|
||||
```
|
||||
|
||||
The final step is the simplest. First, create a shell script inside
|
||||
`helix-config`:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
|
||||
cd ~/.config/aerc/helix-config && hx "$@"
|
||||
```
|
||||
|
||||
This will serve as our "email entry point". It simply sets the CWD to our new
|
||||
workspace, then loads Helix normally. Then, you'll need to set the `editor`
|
||||
option in `aerc.conf`:
|
||||
|
||||
```ini
|
||||
[compose]
|
||||
editor=/home/yourUserName/.config/aerc/helix-config/hx.sh
|
||||
```
|
||||
|
||||
Unfortunately, you have to provide an absolute path, otherwise `aerc` won't find
|
||||
the script.
|
||||
|
||||
## Markdown Highlighting
|
||||
|
||||
When writing plain-text emails, I like to use Markdown markup. Consider the
|
||||
following message:
|
||||
|
||||
```
|
||||
Dear John,
|
||||
|
||||
Thanks for sharing! I _seriously_ appreciate your help.
|
||||
|
||||
To proceed, we'll need to do:
|
||||
|
||||
- Thing A
|
||||
- Thing B
|
||||
```
|
||||
|
||||
Even though the recipient will receive this email in un-rendered plain text
|
||||
format, the _semantics_ are still very clear. Of course, we don't _need_
|
||||
highlighting in our editor to write markdown, but it can help make the
|
||||
experience a bit better.
|
||||
|
||||
All you need to do is create a `.helix/languages.toml` file inside our email
|
||||
workspace from before:
|
||||
|
||||
```toml
|
||||
[[language]]
|
||||
name = "markdown"
|
||||
file-types = [{ glob = "/tmp/aerc-compose-*.eml" }]
|
||||
```
|
||||
|
||||
Take note of the `file-types` entry. Email compose files opened by `aerc` will
|
||||
always match the `/tmp/aerc-compose-*.eml` pattern. The above snippet ensures
|
||||
that emails will be treated as Markdown files by Helix. The reason I recommend
|
||||
putting this in your _workspace_ config and not setting it globally is to avoid
|
||||
complications if you ever find yourself needing to edit a regular `.eml` file
|
||||
_outside_ of `aerc`.
|
||||
|
||||
## Spellcheck
|
||||
|
||||
The excellent [harper](https://github.com/Automattic/harper) LSP supports
|
||||
Markdown out-of-the-box. You can follow the instructions in its documentation to
|
||||
install and configure it for Helix. Then, you can add it to your workspace
|
||||
`languages.toml`:
|
||||
|
||||
```toml
|
||||
language-servers = ["harper-ls"]
|
||||
```
|
||||
|
||||
## Formatting
|
||||
|
||||
Many plain-text email clients _don't_ automatically wrap lines for readers. A
|
||||
soft-wrapped email may look completely fine on your end, but be totally
|
||||
unreadable for your recipient. Therefore, we need a way to _hard-wrap_ our
|
||||
emails before sending them. This ended up being a much bigger challenge than I
|
||||
thought it would.
|
||||
|
||||
My first approach was to use the nifty `wrap` filter that `aerc` ships with by
|
||||
adding it to `languages.toml`:
|
||||
|
||||
```toml
|
||||
formatter = { command = "/usr/lib/aerc/filters/wrap", args = ["-w", "74"] }
|
||||
```
|
||||
|
||||
This worked _okay_, but it totally broke the markup at times. Crucially, it
|
||||
doesn't respect code blocks or verbatim sections denoted by backticks. If you're
|
||||
participating in technical mailing lists, you definitely want these to be
|
||||
preserved.
|
||||
|
||||
Using a regular Markdown formatter introduced another problem: Markup was
|
||||
preserved, but not in an email-friendly way. Consider this example:
|
||||
|
||||
```
|
||||
How's it going?
|
||||
|
||||
Best,
|
||||
Daniel
|
||||
```
|
||||
|
||||
A standard Markdown formatter ignores single line breaks and produces this
|
||||
output:
|
||||
|
||||
```
|
||||
How's it going?
|
||||
|
||||
Best, Daniel
|
||||
```
|
||||
|
||||
It similarly mangles signature blocks:
|
||||
|
||||
```
|
||||
--
|
||||
Daniel
|
||||
Programmer
|
||||
email@address.com
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```
|
||||
-- Daniel Programmer email@address.com
|
||||
```
|
||||
|
||||
Eventually, I realized that I'd have to write my **own** formatter. I wrote a
|
||||
Python script that hard-wraps the input, automatically reflows paragraphs, and
|
||||
squashes consecutive paragraph breaks — while preserving the following:
|
||||
|
||||
- Any long sequence not broken by spaces.
|
||||
- Quoted lines (`>` prefixed email quotes).
|
||||
- Indented lines.
|
||||
- Markdown lists.
|
||||
- Markdown code blocks.
|
||||
- Signature blocks at end of file.
|
||||
- Sign-offs.
|
||||
|
||||
Sign-offs are preserved by following a simple heuristic. A two-line sequence is
|
||||
considered to be a signoff if:
|
||||
|
||||
- It starts with 1-5 words ending with a comma, followed by
|
||||
- 1-5 words that each start with capital letters.
|
||||
|
||||
This covers signoffs like these:
|
||||
|
||||
```
|
||||
Best,
|
||||
Daniel
|
||||
|
||||
Yours truly,
|
||||
John Alfred Smith
|
||||
```
|
||||
|
||||
I provide a number of flags and options, so you can configure the formatter to
|
||||
your liking. If you want to use it, `format.py` is available in my
|
||||
[mail-utils](https://git.sr.ht/~ficd/mail-utils) repository on sourcehut. To add
|
||||
it to your mail workspace just requires changing one line in `languages.toml`:
|
||||
|
||||
```toml
|
||||
formatter = { command = "/path/to/format.py" }
|
||||
auto-format = true
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Hopefully, this guide was helpful in getting you set up with composing your
|
||||
emails in Helix. A similar guide for Kakoune is in the works, so check back
|
||||
soon!
|
171
content/blog/email-in-kakoune.md
Normal file
171
content/blog/email-in-kakoune.md
Normal file
|
@ -0,0 +1,171 @@
|
|||
---
|
||||
title: Writing Emails In Kakoune
|
||||
date: 2025-06-01
|
||||
---
|
||||
|
||||
This post will guide you through my setup for using Kakoune as an email composer
|
||||
inside `aerc`. I'll also explain how to configure Kakoune to act as the _pager_
|
||||
for reading `text/plain` emails. If you only care about the final config, feel
|
||||
free to skip to it [here](#final-configuration).
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [Naive Approach](#naive-approach)
|
||||
- [Composer Setup](#composer-setup)
|
||||
- [Reader Setup](#reader-setup)
|
||||
- [Final Configuration](#final-configuration)
|
||||
|
||||
<!--toc:end-->
|
||||
|
||||
## Naive Approach
|
||||
|
||||
Since `aerc` uses your `$EDITOR` for composition, you don't technically have to
|
||||
do anything. I prefer setting it explicitly in `aerc.conf`, for good measure:
|
||||
|
||||
```ini
|
||||
[compose]
|
||||
editor=kak
|
||||
```
|
||||
|
||||
The rest of the magic happens in your `kakrc`.
|
||||
|
||||
## Composer Setup
|
||||
|
||||
Essentially, we want to hook `filetype=mail` and set our buffer configuration
|
||||
there. I'll share a recommended configuration with some explanation.
|
||||
|
||||
```kak
|
||||
hook global WinSetOption filetype=mail %~
|
||||
set-option window formatcmd '/home/fic/dev/utils/mail-utils/format.py'
|
||||
set-option window comment_line '>'
|
||||
try autospell-enable
|
||||
hook -group mail-auto-format window BufWritePre .* format
|
||||
hook -once -always window WinSetOption filetype=.* %{
|
||||
unset-option window formatcmd
|
||||
remove-hooks window mail-auto-format
|
||||
}
|
||||
~
|
||||
```
|
||||
|
||||
I use a custom formatter to format emails. It automatically hard-wraps lines
|
||||
while preserving certain markup elements, code blocks, sign-offs, and signature
|
||||
blocks. For more details, check the formatting section of my post on
|
||||
[Helix](/blog/email/helix#formatting).
|
||||
|
||||
I find that setting `>` as the `comment_line` token is convenient for working
|
||||
with quotes in replies.
|
||||
|
||||
The `try autospell-enable` enables my
|
||||
[kak-autospell](https://git.sr.ht/~ficd/kak-autospell) plugin for the buffer.
|
||||
Essentially, it provides spellchecking that's continuously refreshed and hidden
|
||||
in insert mode.
|
||||
|
||||
The remaining commands configure auto-formatting on save. I always prefer having
|
||||
this on so I never forget to format my message before sending it.
|
||||
|
||||
## Reader Setup
|
||||
|
||||
I find that using Kakoune to **read** emails is helpful because of how easy it
|
||||
is to copy quotes, open links, etc. Configuring this is a tad hackier, however.
|
||||
The basic idea is to set Kakoune as the viewer `pager` in `aerc.conf`.
|
||||
|
||||
However, all this does is pipe the email to `kak` through standard input, so we
|
||||
need to tell the editor to treat it like an email:
|
||||
|
||||
```ini
|
||||
[viewer]
|
||||
pager=kak -e 'set buffer filetype mail'
|
||||
```
|
||||
|
||||
When you're using Kakoune as a pager, you'll probably want to configure some
|
||||
things differently. In my case, I like to set the buffer as `readonly`, remove
|
||||
the `number-lines` and `show-whitespaces` highlighters, disable soft-wrap & my
|
||||
scrolloff settings, and _not_ set any formatters.
|
||||
|
||||
The `pager` command above sets the filetype, but we need to distinguish between
|
||||
_composing_ and _reading_ in our Kakoune hook. When Kakoune is opened with input
|
||||
through standard input, it loads a buffer that's conveniently named `*stdin*`.
|
||||
Thus, we can check the buffer name before continuing.
|
||||
|
||||
If we're in "reading mode", we define a hidden command called `ismailreader`
|
||||
which doesn't do anything. Why? If the command is defined, and we try to invoke
|
||||
it... well, nothing happens! But if it's **not** defined, we get an error
|
||||
instead. We can combine this with the `try` command to for some simple boolean
|
||||
logic.
|
||||
|
||||
```kak
|
||||
evaluate-commands %sh{
|
||||
# stdin, we assume it's a pager
|
||||
if [ "$kak_bufname" = "*stdin*" ]; then
|
||||
echo 'define-command -hidden ismailreader nop'
|
||||
fi
|
||||
}
|
||||
try %{
|
||||
ismailreader
|
||||
# do reader config here
|
||||
} catch %{
|
||||
# do composer config here
|
||||
}
|
||||
```
|
||||
|
||||
I set the following configuration for "reader mode":
|
||||
|
||||
```kak
|
||||
set buffer readonly true
|
||||
try %{
|
||||
remove-highlighter window/number-lines
|
||||
remove-highlighter window/show-whitespaces
|
||||
# custom commands defined elsewhere in my kakrc
|
||||
ui-wrap-disable
|
||||
ui-scrolloff-disable
|
||||
}
|
||||
```
|
||||
|
||||
## Final Configuration
|
||||
|
||||
To recap, you'll want to set this in `aerc.conf`:
|
||||
|
||||
```ini
|
||||
[viewer]
|
||||
pager=kak -e 'set buffer filetype mail'
|
||||
|
||||
# ...
|
||||
|
||||
[compose]
|
||||
editor=kak
|
||||
```
|
||||
|
||||
And the following in your `kakrc`:
|
||||
|
||||
```kak
|
||||
hook global WinSetOption filetype=mail %~
|
||||
evaluate-commands %sh{
|
||||
# stdin, we assume it's a pager
|
||||
if [ "$kak_bufname" = "*stdin*" ]; then
|
||||
echo 'define-command -hidden ismailreader nop'
|
||||
fi
|
||||
}
|
||||
try %{
|
||||
# READER MODE setup
|
||||
ismailreader
|
||||
set buffer readonly true
|
||||
try %{
|
||||
# remove these highlighters so everything displays properly
|
||||
remove-highlighter window/number-lines
|
||||
remove-highlighter window/show-whitespaces
|
||||
ui-wrap-disable
|
||||
ui-scrolloff-disable
|
||||
}
|
||||
} catch %{
|
||||
# WRITER MODE setup
|
||||
set-option window formatcmd '/home/fic/dev/utils/mail-utils/format.py'
|
||||
set-option window comment_line '>'
|
||||
try autospell-enable
|
||||
hook -group mail-auto-format window BufWritePre .* format
|
||||
hook -once -always window WinSetOption filetype=.* %{
|
||||
unset-option window formatcmd
|
||||
remove-hooks window mail-auto-format
|
||||
}
|
||||
}
|
||||
~
|
||||
```
|
10
content/blog/index.md
Normal file
10
content/blog/index.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Blog Posts
|
||||
template: post_list
|
||||
post: false
|
||||
---
|
||||
|
||||
Here, you can find my musings on various technology topics. I take a _digital
|
||||
garden_-like approach, meaning these articles are likely incomplete and may be
|
||||
changed at any point.
|
||||
|
112
content/blog/mirror-srht-to-github.md
Normal file
112
content/blog/mirror-srht-to-github.md
Normal file
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: Automatically Mirror Sr.ht To GitHub
|
||||
date: 2025-05-15
|
||||
---
|
||||
|
||||
For a variety of reasons, I've recently migrated to [sr.ht](https://sr.ht/~ficd)
|
||||
for Git and project hosting. I prefer the minimal, no-frills interface, and I
|
||||
really like that you can have multiple repositories, mailing lists, and issue
|
||||
trackers grouped under the same project. The email-centric workflow is also
|
||||
appealing to me.
|
||||
|
||||
However, the fact remains that a strong presence on GitHub is very important. If
|
||||
you're a student hoping to land a programming job, you need to keep those commit
|
||||
stats up. Many tools, such as [Yazi](https://yazi-rs.github.io/)'s package
|
||||
manager, only support GitHub repositories. However, I don't _like_ GitHub,
|
||||
that's the whole reason I switched!
|
||||
|
||||
I finally decided to give automatically mirroring my repositories a shot. While
|
||||
it was a bit of work to set up, it's worth it in the end. In this post, I'll
|
||||
guide you through the process.
|
||||
|
||||
## SSH and Sourcehut Secrets
|
||||
|
||||
The first step is to give `builds.sr.ht` push access to your GitHub repositories
|
||||
via `ssh`. Begin by generating an `ssh` key pair:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/gh_mirror_id -N ""
|
||||
```
|
||||
|
||||
You'll want to add the _public_ key to your GitHub account. Navigate to
|
||||
**Settings** → **SSH and GPG keys**, click the big green button, and paste the
|
||||
contents of `gh_mirror_id.pub`. Make sure its key type is "Authentication Key",
|
||||
and give it a helpful name like `sr.ht builds`.
|
||||
|
||||
Next, you need to upload the _private_ key to Sourcehut. Visit
|
||||
[builds.sr.ht/secrets](https://builds.sr.ht/secrets), name it something
|
||||
memorable, and paste `gh_mirror_id` into the big box labeled "Secret". Take care
|
||||
to tick the **Secret Type** → **SSH Key** menu option before hitting the blue
|
||||
"Add" button.
|
||||
|
||||
Keep this page open, because you'll need the secret's UUID later.
|
||||
|
||||
## Build Manifest
|
||||
|
||||
The basic idea is as follows: whenever you push to your chosen `sr.ht`
|
||||
repositories, it should automatically be pushed to GitHub with the `--mirror`
|
||||
flag. Sourcehut has an automated build system that's up for the task. _(Note:
|
||||
you need a paid account to use it.)_
|
||||
|
||||
For any repository you want to mirror, you'll need to add a file called
|
||||
`.build.yml` to the root. This file will look familiar if you've worked with
|
||||
GitHub actions before. Then, you set up the environment, import the `ssh` key
|
||||
from earlier, and add a task for pushing the repository as if you were doing it
|
||||
from the command line. Finally, make sure you've created an empty GitHub
|
||||
repository ahead of time — otherwise, the build will fail!
|
||||
|
||||
```yaml
|
||||
image: alpine/latest
|
||||
packages:
|
||||
- git
|
||||
- openssh
|
||||
secrets:
|
||||
- SECRET_UUID_HERE
|
||||
environment:
|
||||
# in case the repos have different names
|
||||
srht_repo: repo-name-on-srht
|
||||
github_repo: repo-name-on-gh
|
||||
github: git@github.com:yourusername
|
||||
tasks:
|
||||
# ssh-keyscan is required, command fails otherwise!
|
||||
- mirror: |
|
||||
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
cd "$srht_repo"
|
||||
git remote add github "$github/$github_repo.git"
|
||||
git push --mirror github
|
||||
```
|
||||
|
||||
And that's it! Anytime you push to a branch that has this build manifest in it,
|
||||
your GitHub mirror is updated.
|
||||
|
||||
## Readme Wrangling
|
||||
|
||||
Since all you're doing is _mirroring_, you can't have a different readme on
|
||||
GitHub. There's a few things I recommend doing so folks viewing your repository
|
||||
on GitHub don't get confused. You can mention the mirror in the "About" section,
|
||||
and include a link to your `sr.ht` repo:
|
||||
|
||||

|
||||
|
||||
I also recommend prominently displaying links to your bug tracker and mailing
|
||||
list, if applicable to your project. Finally, make sure there's an early link to
|
||||
the `sr.ht` project page. You could also explicitly mention that the project
|
||||
lives on sourcehut and is mirrored on GitHub, but this may be overkill. Consider
|
||||
this example from my Ashen project README:
|
||||
|
||||
`ex_start`
|
||||
|
||||
This monorepository contains official implementations of Ashen across a range of
|
||||
editors, terminals, tools, and more — each carefully tuned to carry the same
|
||||
muted warmth. The project lives on [sourcehut](https://sr.ht/~ficd/ashen/) and
|
||||
is mirrored on [GitHub](https://github.com/ficcdaf/ashen). To report issues or
|
||||
make requests, visit the [ticket tracker](https://todo.sr.ht/~ficd/ashen) or
|
||||
contact the [mailing list](https://lists.sr.ht/~ficd/ashen) (_possibly by
|
||||
carrier pigeon_.)
|
||||
|
||||
`ex_end`
|
||||
|
||||
## Happy Hacking!
|
||||
|
||||
Now, you should be able to comfortably use `sr.ht` to host your projects without
|
||||
sacrificing the ubiquity of GitHub. Happy hacking!
|
74
content/blog/on-websites.md
Normal file
74
content/blog/on-websites.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
title: On Websites (Or The Case For The Personal Web)
|
||||
date: 2025-05-05
|
||||
---
|
||||
|
||||
## Preamble
|
||||
|
||||
The internet is a [lovecraftian entity](/blog/the-lovecraftian-internet) in its
|
||||
size and complexity. Much of its gargantuan scope is invisible to us, the _deep
|
||||
web_, but even the surface web is mostly beyond our comprehension as
|
||||
individuals. The electric highways are, in no small part, dominated by social
|
||||
media platforms. The internet is the perfect technology for sharing ourselves
|
||||
with others, and social media makes this easy. However, it's come at a great
|
||||
cost -- to our privacy, our money, our freedom, our individuality.
|
||||
|
||||
I'm not here to litigate the complicated issue that is social media's effect on
|
||||
society. I'm not here to tell you _why_ there's a problem with our digital
|
||||
platforms. What I will say is that it seems fairly uncontroversial to point out
|
||||
that social media is an _addiction_ for many of us. I'm sure that you, dear
|
||||
reader, have also considered "taking a break" or even quitting. Even if we don't
|
||||
know exactly why, we do seem to agree, as a society, that something is wrong.
|
||||
|
||||
Past this introduction, I'm not here to criticize social media. I'm also not
|
||||
here to refer you to other platforms like Mastodon and Matrix. I'm here to tell
|
||||
you that **the internet doesn't have to be this way**.
|
||||
|
||||
## Websites
|
||||
|
||||
Today, we think of websites as _platforms_: storefronts, news, town squares,
|
||||
encyclopedias. Something maintained by companies and organizations providing a
|
||||
service. The space for _us_ to share _ourselves_, it seems, is on someone else's
|
||||
website. What format you can post in, how your profile looks; you're at the
|
||||
mercy of the service provider. So, why don't we just make our own websites?
|
||||
|
||||
A website is actually remarkably simple. Sure, **web development** isn't; it's
|
||||
been increasing in complexity and bloat by the year. But a _website_ is _just_ a
|
||||
document that's hosted at some address. Visitors _download_ the document, and
|
||||
their browser _renders_ it. That's how you're reading this page, even.
|
||||
|
||||
Websites don't **need** functionality. They don't need fancy graphics and
|
||||
buttons. They literally **only** need text. Everything else is fluff -- great
|
||||
fluff, my own site has images and a theme -- but fluff nonetheless. As soon as
|
||||
you realize this, you'll learn that you don't need to be a developer or tech
|
||||
wizard to publish a website. And I argue that, as creative individuals, to make
|
||||
full use of what the web is **really** great at, it's definitely worth
|
||||
publishing a website.
|
||||
|
||||
## Why Should You Make A Website?
|
||||
|
||||
I think there's something to be said for having a corner of the internet that's
|
||||
_truly_ your own. When you post on social media, that profile isn't **really**
|
||||
yours. If it were, how come you can't, say, change the background color? Play
|
||||
music for your visitors? How come you're limited by _how_ you can post? On
|
||||
Instagram, you can only post visual media, not really the best way to share your
|
||||
complex thoughts on something.
|
||||
|
||||
Your website is **yours**. You can post what, when, and how _you_ want to. The
|
||||
_data_ is yours too. There are so many ways to express your creativity and
|
||||
personality through this medium. Take a stroll through
|
||||
[Neocities'](https://neocities.org) public offerings sometime to see what I
|
||||
mean.
|
||||
|
||||
When you publish to your website, you're not thinking about likes, shares and
|
||||
friends. You just write, and upload. Whoever reads it, reads it. Unless you've
|
||||
set up some analytics (which I don't even recommend), you _don't_ know how many
|
||||
people have read your posts. And I think it's better this way.
|
||||
|
||||
Stepping away from social media to cultivate my own digital garden has allowed
|
||||
me to perform a more authentic version of myself on the internet. It's great
|
||||
having one, central place to share myself with the wold. Blog posts, art,
|
||||
technical projects, my resume, all I need to do now is direct people here.
|
||||
|
||||
So, do yourself a favor and try building a personal website. I think it's one of
|
||||
the most rewarding ways to participate in the internet.
|
235
content/blog/rediscovering-email.md
Normal file
235
content/blog/rediscovering-email.md
Normal file
|
@ -0,0 +1,235 @@
|
|||
---
|
||||
title: Rediscovering Email
|
||||
date: 2025-03-24
|
||||
---
|
||||
|
||||
## Preamble
|
||||
|
||||
"Kids these days" don't really use email anymore. _It's me by the way, I'm
|
||||
kids._ Sure, we email our professors, we email our resumes to potential
|
||||
employers, we email when the business is a tad too official for text or
|
||||
WhatsApp.
|
||||
|
||||
Email isn't just for communication. Think about it: when all you want to do is
|
||||
talk to someone, are you sending them an email? When's the last time you emailed
|
||||
a friend or loved one "What's up? How's it going?"
|
||||
|
||||
Email has been relegated, essentially, to administrative tasks. Emails are
|
||||
something you _have_ to send. They carry an air of officiality. Definitely not
|
||||
the way to contact your friends.
|
||||
|
||||
Why is that?
|
||||
|
||||
## Web Mail Sucks
|
||||
|
||||
I think our _experience_ of writing email takes a large chunk of the blame. To
|
||||
be honest, it's pretty dreadful most of the time, thanks in no small part due to
|
||||
the _God-awful_ interfaces through which we interact with emails.
|
||||
|
||||
Web mail interfaces _suck_. You log into Gmail just to see that every fifth
|
||||
letter in your inbox is actually an ad. I'm not talking promotional mail, I'm
|
||||
talking a literal ad, served to you by Google, masquerading as a real message.
|
||||
|
||||
And tell me why Outlook needs to make hundreds of JavaScript requests, and cache
|
||||
a hundred megabytes of data just to show my inbox? If I'm using my laptop on the
|
||||
school network, chances are, I'll be sitting there for at least ten seconds
|
||||
before I can even see whether I've got any new messages.
|
||||
|
||||
Oh, and I don't know about you, but since I spent most of the day using my
|
||||
computer, I like to do whatever I can to prolong my ability to, like, use my
|
||||
eyes. A big part of that means using dark mode wherever possible, as God
|
||||
intended. Which brings me to my next point: dark mode in most web mail clients
|
||||
isn't great.
|
||||
|
||||
For instance, Proton Mail provides a lovely dark interface for your inbox, just
|
||||
to burn your retinas off the moment you start composing a message or reading a
|
||||
new email. This is one area I must be fair to Outlook, however, which provides
|
||||
one of the nicest dark interfaces I've used.
|
||||
|
||||

|
||||
|
||||
For quite a few reasons, including those stated above, and of course wanting to
|
||||
use my own text editor to write my letters, I finally decided it was time to
|
||||
figure out a better way to interact with email. And as it turns out, there
|
||||
really is one!
|
||||
|
||||
## Email In The Terminal?
|
||||
|
||||
If you know me at all, you know I'm a **fiend** for the terminal. If a given
|
||||
task is even _possible_ to do in a command-line (or TUI) interface, then that's
|
||||
the way I'll be doing it from now on until my hands my fall off my wrists. Call
|
||||
it a neurodivergent stubbornness, but it's just the only way I really like to
|
||||
interact with my computer.
|
||||
|
||||
It also happens that Helix, my text editor of choice, also runs in the terminal.
|
||||
Anytime I'd have to draft an email longer than a few sentences, I'd start to
|
||||
feel the withdrawal. Maybe Helix has really spoiled me, but the experience of
|
||||
manipulating text in this beautiful program is too good to **not** incorporate
|
||||
into every aspect of my life that involves writing. I'm already writing my
|
||||
papers and slideshows in Helix (_more on that in a future post_), so why not
|
||||
email?
|
||||
|
||||
First, I figured out using Thunderbird. Although it has many problems (like,
|
||||
tell me how there _still_ isn't tray support for Linux?!) using Thunderbird was
|
||||
otherwise a "solid" experience. Sure, Thunderbird is a GUI app, _but_ I wasted
|
||||
no time in finding a pipeline for writing emails in my beloved Helix.
|
||||
|
||||
The extension "External Editor Revived" does exactly what it sounds like. Using
|
||||
it, I was able to open up email files for editing in Helix, then pipe them back
|
||||
to Thunderbird for review and sending. I was happy with this setup for a while
|
||||
despite its problems.
|
||||
|
||||
But as it turns out, an even nerdier way was just around the corner...
|
||||
|
||||
## Enter Aerc
|
||||
|
||||
I forget where I first heard about `aerc`, but I'm really glad that I did! It's
|
||||
a lovely little TUI email client maintained by the good people at Source Hut. It
|
||||
features a vim-like ex-command setup and controls. The controls are also 100%
|
||||
remappable, which makes my Colemak brain happy.
|
||||
|
||||
My favorite part, though, is the composing interface. It's really clever: the
|
||||
top of the screen holds the standard headers you expect to see like From, To,
|
||||
and the Subject, but the part where you write the body of your letter is
|
||||
actually a tmux-style embedded terminal running your favorite `$EDITOR`!
|
||||
|
||||

|
||||
|
||||
Of course, there were quite a few hiccups in getting Helix to play along;
|
||||
namely, spellchecking, Markdown syntax highlighting and automatic text-wrap was
|
||||
a challenge. If you're interested in the details, I'll later be publishing a
|
||||
tutorial outlining step-by-step how to replicate my Helix-mail setup.
|
||||
|
||||
### Authentication Problems
|
||||
|
||||
If you're going to use an email client, you need to authenticate with your email
|
||||
provider. For Proton Mail, this was easy enough; the Bridge app interfaces with
|
||||
your encrypted inbox, and exposes IMAP and SMTP services locally. Outlook, on
|
||||
the other hand (especially a school/corporate policy) is a **hassle**.
|
||||
|
||||
Without getting too deep into it, it was more than one day of headache before I
|
||||
managed to get it working. The solution involved finding (and modifying) a
|
||||
Python script to authenticate and save my initial refresh token, as well as
|
||||
manage requesting new access tokens when they expire. I also had to gaslight
|
||||
Microsoft into thinking I was actually using Thunderbird (by pulling the
|
||||
`client_id` straight from the Mozilla source code).
|
||||
|
||||
## Organizing Email
|
||||
|
||||
My inbox has _always_ been disorganized. Frankly, I don't know a single person
|
||||
whose inbox isn't. And it's understandable! The tools at our disposal aren't
|
||||
particularly helpful, and most importantly, **they're not standardized.** For
|
||||
example, we could create various folders, moving the appropriate emails as we
|
||||
read them. Some _(emph: some)_ providers let you automate this, but I've found
|
||||
the filter rules to be a bit lacking. Organizing by folders also inherently
|
||||
limits your ability to later filter and narrow down your searches.
|
||||
|
||||
Tags are clearly the best way, but again, the implementation depends
|
||||
per-provider. Worst of all, as with most things these days, non-standard
|
||||
implementations mean that as soon as you've put all your eggs in the basket,
|
||||
you're essentially locked in; unless you want to go to the _colossal_ hassle of
|
||||
re-organizing your inbox playing by _another_ provider's rules.
|
||||
|
||||
## Storing Email Locally
|
||||
|
||||
As it turns out, `aerc` (and other mail clients like it), besides the standard
|
||||
ability to read mail from an IMAP server, has another trick up its sleeve. That
|
||||
trick is reading mail from a _local database_. Now, this might not seem like a
|
||||
big deal. Thunderbird does this too: when you access your inbox via IMAP,
|
||||
Thunderbird downloads and caches all of your emails.
|
||||
|
||||
What I never knew, and it took messing around with `aerc` to learn about, is
|
||||
that there exist multiple _standardized_ ways to store emails locally. What this
|
||||
means is that as long as you synchronize your emails to this local store, you
|
||||
can access them from **any** client!
|
||||
|
||||
I chose to go with a tool called `mbsync`, which stores mail in the standard
|
||||
"maildir" format, and keeps it in sync with an IMAP server. The syncing only
|
||||
happens when you trigger it manually -- although you can easily configure
|
||||
another program to "listen" for new emails and sync automatically. I decided to
|
||||
forego this, more on why later.
|
||||
|
||||
## Complex Tagging
|
||||
|
||||
The best part is that once you've got this email database, you can pick from a
|
||||
host of tools and systems for indexing and organizing it. I went with a tool
|
||||
called `notmuch`, a program that's very popular among programmers much older and
|
||||
more bearded than I am. `notmuch` _indexes_ your maildir and basically acts as a
|
||||
query engine. I found the experience of using it similar to, say, SQL. The basic
|
||||
premise is that you write queries about your emails.
|
||||
|
||||
```sh
|
||||
# Here's me searching for emails about GitHub authorization
|
||||
notmuch search 'from:/@.*github.*/ subject:/auth\w*/'
|
||||
```
|
||||
|
||||
You can also apply an arbitrary string to an email as a **tag**. Tags are more
|
||||
powerful than **flags**, which are IMAP compliant, but limited to only a few
|
||||
specific roles. `notmuch` allows you to create and use as many different tags as
|
||||
you want!
|
||||
|
||||
```sh
|
||||
# Here's me applying the "personal" tag to
|
||||
notmuch tag +personal -- 'path:personal/**'
|
||||
```
|
||||
|
||||
But the real juice is configuring rules that will automatically tag your new
|
||||
emails. Then, not only can you filter/query your mail by tag, but when you open
|
||||
your mailbox in `aerc`, you can configure queries as "folders" for easy,
|
||||
repeated viewing. Nice!
|
||||
|
||||
## The Synchronization Problem
|
||||
|
||||
I spent a lot of time on tagging. Figuring out the rules and automatic filters,
|
||||
fine-tuning my queries, etc. When I finally had it all figured out, I
|
||||
encountered the next problem, which (I guess) I could've foreseen: **the
|
||||
`notmuch` database is local**! And as it turns out, synchronizing its database
|
||||
between devices (home PC and laptop in my case) is **not** a trivial task.
|
||||
|
||||
I first tried a tool called [muchsync](https://www.muchsync.org/). It's a clever
|
||||
piece of software, with one big problem: it's designed around one database that
|
||||
acts as a central server, which _other_ databases replicate. This turned out to
|
||||
not be particularly reliable for my case, because thanks to the aforementioned
|
||||
[authentication hacks](#authentication-problems), I can only **reliably**
|
||||
synchronize my mail if I'm actually at the computer to re-authenticate when
|
||||
necessary. Trying to wrangle the authentication and GPG-encrypted tokens via
|
||||
`ssh` was starting to feel like more trouble than it was worth.
|
||||
|
||||
My next idea was to use SyncThing to just keep the entire local database
|
||||
synchronized, peer-to-peer style. I figured that as long as I only ran `mbsync`
|
||||
manually (for the external IMAP sync) and only accessed/edited my mail from one
|
||||
device at a time, I wouldn't have any conflicts.
|
||||
|
||||
That was, apparently, an incorrect assumption. I was basically faced with a
|
||||
conflict every single time I updated some tags or deleted a few emails. Because
|
||||
of how `mbsync` is written, it was basically impossible to recover from these
|
||||
broken database states. After multiple instances of needing to re-download my
|
||||
entire inbox, I decided it was time to cut my losses, and `notmuch`
|
||||
`pacman -Rns`'d out of my life almost as quickly as it `-Syu`'d into it.
|
||||
|
||||
## JMAP Is Amazing
|
||||
|
||||
For reasons I'll get into in another post, at this point, I was also completely
|
||||
fed up with Proton Mail, and on the hunt for a new provider. I'd heard good
|
||||
things about Fastmail, and after seeing that they implemented a new protocol
|
||||
called JMAP, I decided to try it out.
|
||||
|
||||
> Note: I was actually so pleased with Fastmail that my free trial went paid
|
||||
> subscriber in less than a day. Take that, Proton!
|
||||
|
||||
JMAP has **labels**, which are similar enough to `notmuch` tags. But because
|
||||
they're supported by the protocol itself, they're hosted by the mail server
|
||||
itself, meaning you can completely sidestep the synchronization hassle. Not only
|
||||
that, but they're also a first-class citizen in Fastmail's web interface (which
|
||||
is one of the best I've ever used).
|
||||
|
||||
A **label** is basically analogous to a folder. If an email has a certain label,
|
||||
it's basically in that folder. If it has multiple, it's like multiple folders
|
||||
symlink to the same message. What's cool about this system is that you can move
|
||||
and "copy" your mail around without actually creating redundant copies of
|
||||
emails.
|
||||
|
||||
With this approach, you don't even need to maintain an archive folder. Any mail
|
||||
that doesn't have the Inbox label is considered archived. This means you can
|
||||
archive messages while still retaining their other labels. Very useful for
|
||||
things like digital receipts and automated messages from `noreply` addresses.
|
64
content/blog/the-lovecraftian-internet.md
Normal file
64
content/blog/the-lovecraftian-internet.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
title: The Lovecraftian Internet
|
||||
date: 2025-04-05
|
||||
---
|
||||
|
||||
# The Lovecraftian Internet
|
||||
|
||||
The web we know is spun by gluttonous spiders no longer satiated by flies and
|
||||
roaches. They cast their net wide across the world, like cuckoos they lay their
|
||||
parasitic eggs inside our lives. They fondle our brains until our DNA is
|
||||
infested with algorithms, our ribcages become mainframes and our eyes turn into
|
||||
screens.
|
||||
|
||||
<div class="image-container">
|
||||
<img src="/static/images/hrgiger.png" alt="A dithered monochrome image of HR Giger's Li II (1976)">
|
||||
|
||||
<small><a href="https://www.pixography.art/post/117714084121/hr-giger-li-i-ii-1974-li-tobler-1948">Li
|
||||
II, H.R. Giger, 1976.</a></small>
|
||||
|
||||
</div>
|
||||
|
||||
No more.
|
||||
|
||||
The spores are already sprouting, recycling the decaying corporate internet into
|
||||
binary compost.
|
||||
|
||||
The internet is incomprehensively massive. Conservative estimates claim at least
|
||||
**4.2 billion** indexed webpages.<sup>1</sup> _Indexed_, in this context, means
|
||||
publicly accessible individual webpages that can be found on a search engine
|
||||
because [web crawlers](https://en.wikipedia.org/wiki/Web_crawler) can reach
|
||||
them. This is known as the **surface web**.
|
||||
|
||||
The figure doesn't account for the rest of the internet: private resources
|
||||
hidden behind firewalls, internal webpages and services, e-mail traffic, IoT
|
||||
devices, direct messages, video game servers, media & file storage, just to name
|
||||
a few. These are all part of the **deep web**: the portion of the internet that
|
||||
cannot be indexed by search engines.
|
||||
|
||||
<div class="image-container">
|
||||
<img src="/static/images/datacenter2-d.jpg" alt="A dithered grayscale image of a datacenter">
|
||||
|
||||
<small><a href="https://flic.kr/p/7xhNmM">Leonardo Rizzi, 2010.</a></small>
|
||||
|
||||
</div>
|
||||
|
||||
No one knows the true size of the deep web. It's impossible to even guess.
|
||||
According to the BBC,<sup>2</sup> the "big four" store _at least_ **1.2 million
|
||||
terabytes** of data. So even before we consider the fairly recent eruption of AI
|
||||
and the sprawling datasets and models that came with it, that's already
|
||||
<strong>9.6 x 10<sup>18</sup></strong> bits of data. That's more ones and zeros
|
||||
than grains of sand on Earth.<sup>3</sup>
|
||||
|
||||
The internet is a
|
||||
[Lovecraftian](https://www.hplovecraft.com/creation/bestiary.aspx) entity in
|
||||
scale, tangibility, and perceptibility. Clearly, this web was not spun by an
|
||||
itsy-bitsy spider.
|
||||
|
||||
---
|
||||
|
||||
# References
|
||||
|
||||
1. [WorldWideWebSize](https://www.worldwidewebsize.com/)
|
||||
2. [ScienceFocus Magazine](https://www.sciencefocus.com/future-technology/how-much-data-is-on-the-internet)
|
||||
3. [NPR](https://www.npr.org/sections/krulwich/2012/09/17/161096233/which-is-greater-the-number-of-sand-grains-on-earth-or-stars-in-the-sky)
|
Loading…
Add table
Add a link
Reference in a new issue