summary refs log tree commit diff
path: root/www/text.causal.agency
diff options
context:
space:
mode:
Diffstat (limited to 'www/text.causal.agency')
-rw-r--r--www/text.causal.agency/.gitignore1
-rw-r--r--www/text.causal.agency/010-irc-suite.74
-rw-r--r--www/text.causal.agency/019-mailing-list.7286
-rw-r--r--www/text.causal.agency/020-c-style.7172
-rw-r--r--www/text.causal.agency/021-time-machine.7144
-rw-r--r--www/text.causal.agency/022-swans-are-dead.7164
-rw-r--r--www/text.causal.agency/023-sparse-checkout.7144
-rw-r--r--www/text.causal.agency/024-seprintf.7137
-rw-r--r--www/text.causal.agency/025-v6-pwd.7330
-rw-r--r--www/text.causal.agency/026-git-comment.7190
-rw-r--r--www/text.causal.agency/027-openbsd-linode.7202
-rw-r--r--www/text.causal.agency/028-names.781
-rw-r--r--www/text.causal.agency/029-topics.7116
-rw-r--r--www/text.causal.agency/030-discs.7114
-rw-r--r--www/text.causal.agency/031-books-2021.7127
-rw-r--r--www/text.causal.agency/032-albums-2021.7173
-rw-r--r--www/text.causal.agency/033-jorts.7485
-rw-r--r--www/text.causal.agency/034-voices.756
-rw-r--r--www/text.causal.agency/035-addendum-2021.7111
-rw-r--r--www/text.causal.agency/036-compassion.7105
-rw-r--r--www/text.causal.agency/037-care.7167
-rw-r--r--www/text.causal.agency/038-agency.785
-rw-r--r--www/text.causal.agency/039-apologies.781
-rw-r--r--www/text.causal.agency/040-sound-memory.7165
-rw-r--r--www/text.causal.agency/041-albums-2022.7185
-rw-r--r--www/text.causal.agency/042-comfort-music.762
-rw-r--r--www/text.causal.agency/043-little-blessings.778
-rw-r--r--www/text.causal.agency/Makefile51
-rw-r--r--www/text.causal.agency/colb.c16
-rw-r--r--www/text.causal.agency/feed.sh7
-rw-r--r--www/text.causal.agency/igp.149
-rw-r--r--www/text.causal.agency/igp.c143
32 files changed, 4021 insertions, 210 deletions
diff --git a/www/text.causal.agency/.gitignore b/www/text.causal.agency/.gitignore
index 8fe3acc9..66b3e637 100644
--- a/www/text.causal.agency/.gitignore
+++ b/www/text.causal.agency/.gitignore
@@ -1,3 +1,4 @@
 *.txt
+colb
 feed.atom
 igp
diff --git a/www/text.causal.agency/010-irc-suite.7 b/www/text.causal.agency/010-irc-suite.7
index b54adf3d..515a30ab 100644
--- a/www/text.causal.agency/010-irc-suite.7
+++ b/www/text.causal.agency/010-irc-suite.7
@@ -358,7 +358,7 @@ If you try any of this software
 and have feedback,
 let me know in
 .Li #ascii.town
-on freenode
+on tilde.chat
 or by email.
 And of course,
 patches are always welcome.
@@ -387,7 +387,7 @@ A small demo of scooper is hosted at
 .Aq Lk "https://causal.agency/scooper/" .
 It publicly logs the
 .Li #litterbox
-channel on freenode.
+channel on tilde.chat.
 .
 .Sh SEE ALSO
 .Bl -item -compact
diff --git a/www/text.causal.agency/019-mailing-list.7 b/www/text.causal.agency/019-mailing-list.7
new file mode 100644
index 00000000..b3490a94
--- /dev/null
+++ b/www/text.causal.agency/019-mailing-list.7
@@ -0,0 +1,286 @@
+.Dd March  4, 2021
+.Dt MAILING-LIST 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Mailing List
+.Nd a small-scale approach
+.
+.Sh DESCRIPTION
+When I initially published
+some software I expected
+other people to use,
+I just asked that patches
+be mailed directly to me,
+but I figured that
+if more people were interested,
+it would be better
+to have a mailing list.
+Unfortunately
+email software,
+mailing list options in particular,
+are quite daunting.
+I wanted a light-weight option
+that would require me to host
+as little software as possible.
+.
+.Pp
+My regular email is hosted by Fastmail,
+and I poked around its settings
+to see what I could do.
+It turns out Fastmail lets you
+configure address aliases to
+.Dq also send to all contacts in
+a contacts group.
+That's a mailing list!
+I created a group called
+.Dq List
+and an alias called
+.Mt list@causal.agency
+configured to deliver to that group.
+So it's really just an alias
+for my regular address
+that happens to also
+deliver to another group of people.
+.
+.Pp
+It's easier to just configure
+and manage one mailing list,
+so what I do is ask patches and feedback
+to be sent to
+.Mt list+catgirl@causal.agency ,
+for example.
+Fastmail treats any
+.Ar +suffix
+the same as the base address,
+but the full address can be used
+by subscribers to filter mail by topic
+if they wish.
+.
+.Pp
+To subscribe someone to the list,
+I add their contact to the group.
+For a long time I was planning
+to write some software
+to manage these subscriptions.
+It should be possible
+to process subscription requests from IMAP
+and manipulate the contact group with CardDAV.
+When I went to start implementing this,
+however,
+I found CardDAV (and WebDAV in general)
+completely inscrutable.
+It's the kind of protocol
+that is split across like 20
+different RFCs
+and you can't understand anything
+by just reading
+the one you actually care about.
+So I've given up on that
+and will keep manually subscribing people
+on request.
+.
+.Pp
+The only thing missing, then,
+is a way for people to read
+mail sent to the list
+while they aren't subscribed.
+All the existing
+mailing list archive software
+I know of
+expects to have the mail locally,
+but I'd rather keep all my mail in IMAP.
+First,
+in order to make sure
+I keep a complete archive
+of the mailing list in IMAP,
+I added a small amount
+of Sieve code
+to my Fastmail filters configuration:
+.Bd -literal -offset indent
+if address :matches ["To", "Cc"] "list*@causal.agency" {
+	fileinto :copy :flags "\e\eSeen" "INBOX.List";
+}
+.Ed
+.
+.Pp
+Sieve is a small standard language
+specifically for filtering mail.
+This bit of code matches
+anything sent to the list
+and adds a copy of it
+(the original is going into my inbox)
+to the
+.Dq List
+folder
+and marks the copy as read.
+.
+.Pp
+With a pristine IMAP mailbox
+to export from,
+I wrote a new archive generator.
+It's called
+.Xr bubger 1
+kirg (have it in a way).
+My goal was to render directly from IMAP
+and produce only static files as output,
+making it not only easy to serve,
+but also to run in one place
+and copy the files elsewhere.
+That's important to me
+because it has access to my email,
+so I'd rather run it
+on my local network and
+.Xr rsync 1
+its output into The Cloud.
+The static files are in
+HTML, Atom and mboxrd formats.
+.
+.Pp
+The architecture of
+.Xr bubger 1
+is that for each piece of mail,
+identified by its UID in the mailbox,
+HTML and Atom fragments
+are exported along with the mboxrd.
+Those fragments are then stitched together
+using the IMAP SORT and THREAD extensions
+to make full pages and feeds
+for each thread.
+The fragments act as a cache
+for subsequent runs.
+.
+.Pp
+I admit I did some
+pretty questionable things
+to achieve this.
+Namely,
+I wrote a small string templating engine in C.
+I use it to produce the HTML
+and XML for Atom,
+as well as to generate URLs
+and paths.
+I'm really happy with how it works, actually.
+This is also where
+I really started using
+one of my favourite C hacks:
+.Bd -literal -offset indent
+#define Q(...) #__VA_ARGS__
+.Ed
+.
+.Pp
+I quote all my HTML/XML templates
+with this and it's lovely.
+.
+.Pp
+I've been working on
+.Xr bubger 1
+on and off for almost a year now,
+and it's been interesting.
+I learned a lot about how email
+works from having to deal with
+all the ways a message can be.
+Thankfully a lot of that dealing
+is done by the IMAP server.
+.
+.Pp
+As for running it,
+I initially just ran it with
+.Xr cron 8 ,
+and that's still a good way to go.
+To hook it up to
+.Xr rsync 1 ,
+pipe it like so:
+.Bd -literal -offset indent
+bubger -C list [...] | rsync -a --files-from=- list remote:list
+.Ed
+.
+.Pp
+Later,
+I got a little annoyed
+with having to wait
+for the next run
+if I wanted to link
+to some mail I just received.
+I added an option
+to use IMAP IDLE
+to wait for new mail continuously
+and I started running it
+under my process supervisor,
+.Xr catsitd 8 .
+.
+.Pp
+The setup is a little more complex
+to feed the list of updated files to
+.Xr rsync 1 .
+I added the
+.Xr catsit-watch 1
+utility to run a command
+when a file changes,
+and in my
+.Xr catsit.conf 5
+I have the following:
+.Bd -literal -offset indent
+bubger	~/.local/libexec/bubger
+rsync	catsit-watch -i -f ~/list/UIDNEXT ~/.local/libexec/rsync
+.Ed
+.
+.Pp
+The
+.Pa ~/.local/libexec/bubger
+script runs
+.Xr bubger 1 ,
+writing the list of updated paths to
+.Pa ~/list/FILES :
+.Bd -literal -offset indent
+exec bubger -i -C ~/list [...] >~/list/FILES
+.Ed
+.
+.Pp
+And the
+.Pa ~/.local/libexec/rsync
+script gets run each time a
+.Xr bubger 1
+update completes
+.Po
+.Pa UIDNEXT
+is always the last file written
+.Pc
+and copies the listed files
+to the remote host:
+.Bd -literal -offset indent
+exec rsync -a --files-from=$HOME/list/FILES ~/list remote:list
+.Ed
+.
+.Pp
+I haven't tagged any
+.Xr bubger 1
+releases yet
+because it hasn't gotten
+a huge amount of testing,
+and I'm not sure anyone but me
+would even want to use it.
+But I'm happy
+with how it's working right now,
+so I might tag 1.0 soon
+just for fun.
+.
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Lk https://causal.agency/list/
+.It
+.Lk https://git.causal.agency/bubger/about
+.It
+.Lk https://git.causal.agency/catsit/about
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh BUGS
+Almost every time
+I try to type
+.Dq mailing list
+I instead type
+.Dq mailist list .
diff --git a/www/text.causal.agency/020-c-style.7 b/www/text.causal.agency/020-c-style.7
new file mode 100644
index 00000000..9816dbc3
--- /dev/null
+++ b/www/text.causal.agency/020-c-style.7
@@ -0,0 +1,172 @@
+.Dd March 16, 2021
+.Dt C-STYLE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm C Style
+.Nd a rough description
+.
+.Sh DESCRIPTION
+This is a rough description
+of the style in which I write C,
+since it's uncommon
+but some people seem to like it.
+I don't have any hard rules,
+it just needs to look right.
+.
+.Ss Superficialities
+I use tabs
+and they're set to 4 characters wide
+in my editor.
+I keep my lines shorter than 80 columns,
+which I enforce by
+not resizing my terminal's width.
+I use block indentation only,
+meaning I write long function calls
+like this:
+.Bd -literal -offset indent
+fprintf(
+    imap.w, "%s UID THREAD %s UTF-8 %s\er\en",
+    Atoms[thread], algo, search
+);
+.Ed
+.Pp
+Anything that can be sorted
+should be sorted,
+with trailing commas where possible.
+This and block indentation
+make for simpler diffs.
+.Pp
+I either write single-line ifs
+or always use braces.
+I put parentheses
+around ternary expressions.
+I use camelCase
+for functions and variables,
+and PascalCase for types and constants.
+When an acronym appears
+in an identifier,
+it's in either all lower case
+or all upper case.
+The despicable SCREAMING_SNAKE_CASE
+is reserved for macros.
+I don't set globals or statics to zero
+since that is already the default.
+I don't compare against zero or NULL
+unnecessarily.
+.
+.Ss \&No side-effects in control flow
+I never write a function call
+with side-effects
+inside the condition of an if statement.
+I find this makes following the
+.Dq happy path
+through functions
+much easier.
+I write things like this:
+.Bd -literal -offset indent
+pidFile = open(pidPath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
+if (pidFile < 0) err(EX_CANTCREAT, "%s", pidPath);
+
+error = flock(pidFile, LOCK_EX | LOCK_NB);
+if (error && errno != EWOULDBLOCK) err(EX_IOERR, "%s", pidPath);
+if (error) errx(EX_CANTCREAT, "%s: file is locked", pidPath);
+.Ed
+.Pp
+I do write side-effects
+inside for and while statement heads,
+since that's generally expected.
+For some reason
+I like to write the constant first
+if I'm comparing the result of an assignment
+with a side-effect.
+.Bd -literal -offset indent
+for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line)
+.Ed
+.
+.Ss Paragraphs
+I leave blank lines
+between logical chunks of
+.Dq things happening .
+This is usually between side-effects
+with their related error handling,
+or between groups of closely related side-effects.
+I try to keep variable declarations
+glued to the top of the bit of code
+they're used in.
+.
+.Ss Leading break
+I've mentioned this previously.
+I write my switch statement breaks
+before each case label.
+Doing this aligns nicely,
+and being in the habit
+means I always avoid
+accidental fallthrough.
+.Bd -literal -offset indent
+switch (opt) {
+    break; case 'a': append = 1;
+    break; case 'd': delay = strtol(optarg, NULL, 10);
+    break; case 'f': watch(kq, optarg);
+    break; case 'i': init = 1;
+    break; default: return EX_USAGE;
+}
+.Ed
+.
+.Ss Function type definitions
+Function types are always typedef'd,
+and it's the function type itself
+that is defined,
+not a function pointer type!
+I put the typedef above any functions
+that are supposed to be of that type
+so it's clear what the pattern is.
+.Bd -literal -offset indent
+typedef void Action(struct Service *service);
+Action *fn = NULL;
+.Ed
+.
+.Ss Constants
+I prefer enums over #defines
+for integer constants,
+and static const strings over #defines
+unless I want to do concatenation.
+.Bd -literal -offset indent
+enum { Cap = 1024 };
+.Ed
+.Pp
+I avoid the preprocessor
+wherever possible,
+with the notable exception of X macros,
+which I've talked about previously.
+Doing things in the actual language
+makes for easier debugging.
+.
+.Ss Organization
+I usually use only one header file
+in each project.
+The dependency is easy to declare
+and the complete rebuild
+when the header changes
+isn't a problem for small projects.
+Unless it's a single-file program,
+I name the file which contains main
+something generic,
+since the name of the project
+isn't relevant to its function.
+I name functions like
+.Ar nounVerb ,
+and all the functions for a 
+.Ar noun
+are defined in
+.Pa noun.c .
+Not really to do with C,
+but I always put a FILES section
+in my README pages
+to briefly describe
+the layout of the code
+for anyone looking to
+read or make changes to it.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/021-time-machine.7 b/www/text.causal.agency/021-time-machine.7
new file mode 100644
index 00000000..93d35c1e
--- /dev/null
+++ b/www/text.causal.agency/021-time-machine.7
@@ -0,0 +1,144 @@
+.Dd April 25, 2021
+.Dt TIME-MACHINE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Time Machine
+.Nd an awful one
+.
+.Sh DESCRIPTION
+If, like me,
+you have a Raspberry Pi 3 at home
+that you've just upgraded to
+.Fx 13.0
+which has a hard drive
+from an old laptop
+attached to it by USB adapter
+with ZFS on it
+and you want to
+use that as a Time Machine
+backup destination
+over SMB using
+.Xr samba 8 ,
+despite
+.Xr samba 8
+being awful software
+and using ZFS on a system
+with only 1 GB of RAM
+being a terrible idea,
+this is how to do it.
+.
+.Pp
+In
+.Pa /usr/local/etc/smb4.conf :
+.Bd -literal -offset indent
+[global]
+vfs objects = zfsacl catia fruit streams_xattr
+fruit:metadata = stream
+fruit:model = Macmini
+
+[TimeMachine]
+read only = no
+path = /media/zhdd/backup/TimeMachine
+fruit:time machine = yes
+fruit:time machine max size = 250G
+.Ed
+.
+.Pp
+The important thing here is
+.Sy zfsacl
+in the vfs objects list.
+Most pages will tell you about the others,
+but without
+.Sy zfsacl
+Time Machine will just fail to
+create the backup
+and not provide any useful error.
+I'm not actually sure if the
+.Sy fruit:metadata
+setting is required,
+but a bunch of pages recommend it.
+The
+.Sy fruit:model
+just makes it look nice in Finder.
+The rest creates an SMB share called
+.Dq TimeMachine
+that macOS will be willing to use.
+You can limit the size of the share that
+.Xr samba 8
+reports so that Time Machine
+doesn't fill up the whole drive.
+.
+.Pp
+The other important thing to do
+is to create some swap space.
+When I first tried backing up
+to this share,
+it stopped after a while
+because
+.Xr smbd 8
+got killed
+when there was nowhere to swap pages to.
+A wiki page told me to
+create swap on ZFS like this:
+.Bd -literal -offset indent
+zfs create -V 2G \e
+	-o org.freebsd:swap=on \e
+	-o checksum=off \e
+	-o compression=off \e
+	-o dedup=off \e
+	-o sync=disabled \e
+	-o primarycache=none \e
+	zhdd/swap
+swapon /dev/zvol/zhdd/swap
+.Ed
+.
+.Pp
+To be fair to
+.Xr samba 8 ,
+most of the memory
+is being used by the ZFS ARC
+.Po
+which you can see in
+.Xr top 1
+.Pc ,
+but
+.Xr smbd 8
+still seems to be using
+far more memory than is reasonable.
+It's interesting seeing processes
+with 0 RES in
+.Xr htop 1
+because they're all being swapped out
+while the ARC takes half the available RAM.
+And having to wait for my shell
+to be paged back in when I quit
+.Xr htop 1 .
+.
+.Pp
+Anyway,
+as expected this whole thing
+is terribly slow.
+On my initial backup,
+I'm currently at 26.49 GB
+of 104.22 GB
+with an estimate of 8 hours remaining.
+Normally transfer time estimates
+are wildly inaccurate,
+but I think in this case it's right.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Sh BUGS
+.Fx
+doesn't seem to want to mount
+the ZFS volumes on the hard-drive-over-USB
+automatically at boot.
+I have to
+.Xr zpool-import 8
+the drive manually each time.
+I don't know if there's a workaround for this,
+but I don't have anything essential
+to the system on the drive,
+and it doesn't need to reboot often.
diff --git a/www/text.causal.agency/022-swans-are-dead.7 b/www/text.causal.agency/022-swans-are-dead.7
new file mode 100644
index 00000000..8664e886
--- /dev/null
+++ b/www/text.causal.agency/022-swans-are-dead.7
@@ -0,0 +1,164 @@
+.Dd May  5, 2021
+.Dt SWANS-ARE-DEAD 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Swans Are Dead
+.Nd album by Swans
+.
+.Sh DESCRIPTION
+Swans Are Dead
+is the best Swans album.
+Among my favourites are
+Soundtracks for the Blind,
+To Be Kind
+and Love of Life,
+but Swans Are Dead
+is the one I come back to
+most consistently.
+I'm always in the mood
+to listen to these tunes.
+.
+.Pp
+It's interesting to me
+that I enjoy it so much,
+I think because I had the expectation
+that live albums
+are not of the same quality
+as studio albums,
+but that's just completely untrue
+in the case of Swans.
+The performances are excellent
+and the recording is
+for the most part perfect.
+The album feels live,
+without any distracting deficiencies
+of live recording
+that would take you out
+of just enjoying the music.
+.
+.Bl -ohang
+.It Dq Feel Happiness
+This track feels kind of special
+since it's the only song on the album
+that was never released
+as part of another project.
+I absolutely love this format of song.
+It's like 10 minutes of build
+before any lyrics happen,
+which you only get after
+the wave of the first part
+of the song collapses.
+It bookends the first disc nicely with
+.Dq Blood Promise,
+I think,
+which is sort of the reverse.
+.
+.It Dq Blood On Yr Hands
+This is such a great start
+to the Jarboe-focused
+section of the black disc.
+A cappella apart from the hum
+of the equipment on stage,
+I love this vocal performance.
+I sing this song,
+terribly,
+in the shower.
+The lack of instrumental
+seems to make it stick in my mind even more.
+.
+.It Dq I Crawled
+This is another great vocal performance
+by Jarboe.
+It's so much more dynamic and intense
+than the version of this song
+released much earlier on Young God
+with Gira's vocals.
+I remember seeing a bad comment
+somewhere online
+from someone who couldn't stand
+any Swans song Jarboe sang on.
+They must have never heard
+this version of
+.Dq I Crawled.
+Incredible.
+.
+.It Dq Blood Promise
+My favourite track on
+Swans Are Dead,
+by far.
+I had actually never heard of
+.Dq The Whiffenpoof Song
+until I looked up
+the recording they use
+to introduce this song
+and indicate it's the last of the show.
+Anyway,
+this track highlights
+what makes Swans live albums
+so interesting.
+This performance of the song
+has evolved so much
+from the studio recording on
+The Great Annihilator.
+They share the same lyrics,
+but the earlier version is only 4:15,
+to the live version's fifteen and a half minutes!
+And it sucks me in the whole time.
+As the song winds down
+you can hear an audience member yell,
+.Dq Don't stop!
+and I agree.
+.
+.It Dq The Sound
+One of my all-time favourite songs.
+It's the one that got me to listen to
+Soundtracks for the Blind,
+and might've gotten me into Swans altogether.
+I don't quite remember
+what order I started listening to things in.
+This version of it is great.
+I don't think I could choose
+between this and the studio recording.
+There are just
+two ways to enjoy it.
+I love how frantic the guitars get
+at the height of this track.
+.
+.It Dq I See Them All Lined Up
+This version of the song
+is way more harsh
+than the version on Soundtracks.
+It loses some contrast
+between the verses
+and the explosions of sound
+punctuating them,
+it just hits hard
+the whole time.
+.
+.It Dq Yum Yab
+An absolute banger.
+The drums sound so good on this
+and they really get me moving.
+The whole thing is delightfully nasty and fun.
+.El
+.
+.Pp
+Everything else on the album
+is good too,
+of course,
+I just don't have as much to say.
+There's almost two and a half hours of music
+on this thing!
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I want to try writing
+about different kinds of things here,
+and this is my first attempt
+at doing so.
+There's more music
+I want to write about,
+and maybe some other
+non-computer topics.
diff --git a/www/text.causal.agency/023-sparse-checkout.7 b/www/text.causal.agency/023-sparse-checkout.7
new file mode 100644
index 00000000..925bc043
--- /dev/null
+++ b/www/text.causal.agency/023-sparse-checkout.7
@@ -0,0 +1,144 @@
+.Dd June  9, 2021
+.Dt SPARSE-CHECKOUT 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Sparse Checkout
+.Nd a cool git feature
+.
+.Sh DESCRIPTION
+I was going to write a post about
+.Xr git-subtree 1
+(and I still plan to!)
+but while talking about it
+with a friend
+I came across another command:
+.Xr git-sparse-checkout 1 .
+I got pretty excited because
+I already had a use case for it.
+.
+.Pp
+.Xr git-sparse-checkout 1
+does pretty much what it sounds like.
+It lets you only have
+a subset of files in the repository actually
+.Dq checked out .
+This is really useful
+for huge respositories
+where you are only interested in
+some part of it.
+Any operation touching the working tree
+is much faster because
+it can skip all the files you don't care about.
+.
+.Pp
+My use case is with the
+.Fx
+.Xr ports 7
+tree,
+which recently moved to git
+and contains almost 14 thousand files.
+Working with the whole repository
+was super painful.
+.Xr git-status 1 ,
+which I run as a habit
+when my shell is idle,
+would take dozens of seconds
+to check the whole working tree
+and report back.
+(I didn't get any real time measurements
+before enabling
+.Xr git-sparse-checkout 1 ,
+and I'm not about to disable it now,
+since it'd have to check out
+all those files again.)
+I'm only actually working on
+a small handful of ports,
+so all that work is wasted.
+Time to turn on sparse checkout:
+.Bd -literal -offset indent
+git sparse-checkout init --cone
+.Ed
+.
+.Pp
+The
+.Fl \-cone
+option here
+(which I keep reading as
+.Dq clone
+because it's git)
+restricts the kinds of patterns
+you can use to select files to check out,
+but makes the calculation more efficient.
+Basically it means you can only select
+paths along with everything below them,
+which I think is pretty much
+always what you want anyway.
+Enabling sparse checkout
+can take quite a while
+because it has to do a lot of un-checking-out.
+I should mention
+that you can pass
+.Fl \-sparse
+to
+.Xr git-clone 1
+to avoid ever checking out
+the whole tree.
+.
+.Pp
+The default selection when you run
+.Cm init
+is to check out all the files
+at the root of the repository,
+but none of the subdirectories.
+For
+.Xr ports 7 ,
+I also want to check out
+the shared scripts and Makefiles:
+.Bd -literal -offset indent
+git sparse-checkout add Keywords Mk Templates Tools
+.Ed
+.
+.Pp
+And then I can selectively check out
+just the ports I'm working on:
+.Bd -literal -offset indent
+git sparse-checkout add irc/catgirl irc/pounce
+.Ed
+.
+.Pp
+After enabling sparse checkout,
+.Xr git-status 1
+takes what I'd call
+a normal amount of time.
+I also did this on
+a couple-weeks-out-of-date copy of the
+.Xr ports 7
+tree,
+and when I ran
+.Xr git-pull 1
+it was also really quick,
+because it didn't have to bother
+updating all those files
+I'm not interested in.
+It still downloads all the git objects,
+of course,
+and you can just add any new paths you need
+to the sparse checkout list.
+My disk usage also went down
+by about a gigabyte.
+.
+.Pp
+I'm super pleased to discover this part of git,
+because it makes working with huge
+and/or monorepo-style repositories
+so much more feasible!
+You can see how I came across it,
+since
+.Xr git-subtree 1
+is also a useful tool for monorepos.
+Stay tuned for that post,
+I guess :)
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/024-seprintf.7 b/www/text.causal.agency/024-seprintf.7
new file mode 100644
index 00000000..d1af2e1a
--- /dev/null
+++ b/www/text.causal.agency/024-seprintf.7
@@ -0,0 +1,137 @@
+.Dd June 12, 2021
+.Dt SEPRINTF 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm seprintf
+.Nd an snprintf alternative
+.
+.Sh SYNOPSIS
+.Ft "char *"
+.Fn seprintf "char *ptr" "char *end" "const char *fmt" "..."
+.
+.Sh DESCRIPTION
+While discussing string building in C recently,
+mcf pointed out
+.Xr seprint 2
+from Plan 9,
+and it kind of blew my mind.
+I had implemented my own function in
+.Xr catgirl 1
+for building up strings using
+.Xr snprintf 3
+and a struct containing
+pointer, length and capacity,
+but it felt out of place.
+.Fn seprintf
+(I add the
+.Dq f ,
+Plan 9 doesn't)
+is a much simpler
+and more
+.Dq C-like
+interface with really nice usage patterns.
+.
+.Pp
+The obvious difference from
+.Xr snprintf 3
+is that
+.Fn seprintf
+takes an
+.Fa end
+pointer
+rather than a size.
+This means you need only calculate it
+once for each buffer,
+rather than subtracting
+the running length from the buffer size.
+.Fn seprintf Ap s
+return value is a pointer
+to the terminating null
+of the string it wrote,
+so you can pass that back in
+to continue appending
+to the same buffer.
+.
+.Pp
+I'm not sure of the exact behaviour on Plan 9,
+but my implementation indicates truncation occurred
+by returning the
+.Fa end
+pointer.
+That makes it both easy to check,
+and perfectly fine to keep calling
+.Fn seprintf
+anyway.
+It just won't write anything if
+.Fa ptr
+==
+.Fa end .
+.
+.Pp
+In the case of formatting failure
+(which should be prevented by
+compile-time format string checking,
+but should still be considered),
+.Fn seprintf
+returns
+.Dv NULL .
+I'm again not sure if this matches Plan 9.
+I like this a lot better than
+.Xr snprintf 3
+returning -1,
+because an unchecked
+.Dv NULL
+is likely to quickly cause a crash,
+while blindly adding
+.Xr snprintf 3 Ap s
+return value
+to your running length
+is a non-obvious logic error.
+.
+.Sh EXAMPLES
+Here's an example of what some code using
+.Fn seprintf
+might look like:
+.Bd -literal -offset indent
+char buf[4096];
+char *ptr = buf, *end = &buf[sizeof(buf)];
+ptr = seprintf(ptr, end, "argv: ");
+for (int i = 1; i < argc; ++i) {
+	ptr = seprintf(
+		ptr, end, "%s%s",
+		(i > 1 ? ", " : ""), argv[i]
+	);
+}
+if (ptr == end) errx(1, "truncation occurred :(");
+.Ed
+.
+.Pp
+And here is the very short implementation of it against
+.Xr vsnprintf 3
+which I copy into my project header files:
+.Bd -literal -offset indent
+#include <stdarg.h>
+#include <stdio.h>
+static inline char *
+seprintf(char *ptr, char *end, const char *fmt, ...)
+	__attribute__((format(printf, 3, 4)));
+static inline char *
+seprintf(char *ptr, char *end, const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	int n = vsnprintf(ptr, end - ptr, fmt, ap);
+	va_end(ap);
+	if (n < 0) return NULL;
+	if (n > end - ptr) return end;
+	return ptr + n;
+}
+.Ed
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Another short one before
+.Xr git-subtree 1 .
+I just think this function
+is really neat.
diff --git a/www/text.causal.agency/025-v6-pwd.7 b/www/text.causal.agency/025-v6-pwd.7
new file mode 100644
index 00000000..90bfd6ac
--- /dev/null
+++ b/www/text.causal.agency/025-v6-pwd.7
@@ -0,0 +1,330 @@
+.Dd September  1, 2021
+.Dt V6-PWD 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm V6 pwd
+.Nd deciphering old code
+.
+.Sh DESCRIPTION
+We were talking about
+.Xr wall 1
+on IRC
+and how long it had been annoying users.
+My manual page says
+.Xr wall 1
+appeared in
+.At v6 ,
+which means that
+.Xr wall 1
+has been annoying users for 46 years!
+.
+.Pp
+The Wikipedia page links to the source for
+.At v6 ,
+so I was curious to see how the very first
+.Xr wall 1
+was implemented.
+It's not that surprising,
+except that it is hardcoded
+to handle only 50 logins,
+and it forks to write to each tty,
+waiting one second between each.
+I think the forking must be to avoid
+any of the terminals being opened
+from becoming the controlling terminal
+of the original
+.Xr wall 1
+process.
+.
+.Pp
+Then I started looking
+at some of the other source files
+and found the implementation of
+.Xr pwd 1 ,
+which was surprising.
+There's no
+.Xr getcwd 3
+function
+(the earlier form of which,
+.Xr getwd 3 ,
+appeared in
+.Bx 4.0 ) ,
+so
+.Xr pwd 1
+has to figure out
+the path to the working directory itself.
+It took me a while to figure out how it works.
+.
+.Pp
+To make it easy to talk about,
+I'm just going to include the whole thing here:
+.Bd -literal
+char dot[] ".";
+char dotdot[] "..";
+char root[] "/";
+char name[512];
+int file, off -1;
+struct statb {int devn, inum, i[18];}x;
+struct entry { int jnum; char name[16];}y;
+
+main() {
+	int n;
+
+loop0:
+	stat(dot, &x);
+	if((file = open(dotdot,0)) < 0) prname();
+loop1:
+	if((n = read(file,&y,16)) < 16) prname();
+	if(y.jnum != x.inum)goto loop1;
+	close(file);
+	if(y.jnum == 1) ckroot();
+	cat();
+	chdir(dotdot);
+	goto loop0;
+}
+ckroot() {
+	int i, n;
+
+	if((n = stat(y.name,&x)) < 0) prname();
+	i = x.devn;
+	if((n = chdir(root)) < 0) prname();
+	if((file = open(root,0)) < 0) prname();
+loop:
+	if((n = read(file,&y,16)) < 16) prname();
+	if(y.jnum == 0) goto loop;
+	if((n = stat(y.name,&x)) < 0) prname();
+	if(x.devn != i) goto loop;
+	x.i[0] =& 060000;
+	if(x.i[0] != 040000) goto loop;
+	if(y.name[0]=='.')if(((y.name[1]=='.') && (y.name[2]==0)) ||
+				(y.name[1] == 0)) goto pr;
+	cat();
+pr:
+	write(1,root,1);
+	prname();
+}
+prname() {
+	if(off<0)off=0;
+	name[off] = '\en';
+	write(1,name,off+1);
+	exit();
+}
+cat() {
+	int i, j;
+
+	i = -1;
+	while(y.name[++i] != 0);
+	if((off+i+2) > 511) prname();
+	for(j=off+1; j>=0; --j) name[j+i+1] = name[j];
+	off=i+off+1;
+	name[i] = root[0];
+	for(--i; i>=0; --i) name[i] = y.name[i];
+}
+.Ed
+.
+.Pp
+First, some syntax trivia:
+it seems you don't need
+.Sy =
+to give globals values.
+I guess that makes sense.
+I also noticed that
+it avoids giving
+.Va inum
+and
+.Va jnum
+the same name.
+I think that's because in old C,
+struct field names all shared the same namespace.
+The last difference I noticed
+is the operator
+.Sy =&
+rather than
+.Sy &= .
+Honestly I think the former makes more sense,
+but I can see that the one we have now
+is less ambiguous.
+.
+.Pp
+To get
+.Fn prname
+and
+.Fn cat
+out of the way,
+it's building up a path from the bottom.
+At first I thought it must be
+starting at the end of its buffer
+and moving back as it adds components,
+but no,
+it moves the entire path-so-far over
+every time it adds a new component
+onto the front.
+.Fn cat
+is just a bunch of manual string copying.
+It also gives up
+if the new component
+would make the path longer than 511 characters.
+Fair enough.
+.
+.Pp
+So how does it build up the path?
+The loop in
+.Fn main
+first calls
+.Xr stat 2
+on the current directory
+.Pa \&.
+in order to get its inode number.
+I love that
+.Vt struct statb
+is just declared at the top of this file.
+Clearly this code predates the C preprocessor.
+.
+.Pp
+It then opens the parent directory
+.Pa ..
+and reads directory entries from it.
+The inner loop is looking for
+a directory entry with the same inode number
+as the current directory,
+to figure out what the current directory is called.
+Curiously,
+it reads 16-byte directory entries,
+despite declaring a larger struct.
+The preprocessor can't be invented soon enough.
+.
+.Pp
+Once it finds the matching directory entry,
+it adds the name of the entry
+onto the front of the path,
+changes directory to
+.Pa ..
+and starts over.
+It stops when the current directory
+has an inode number of 1,
+which must be the root of a file system,
+but then it does something else.
+It took me a while to decipher what
+.Fn ckroot
+is doing.
+.
+.Pp
+The loop in
+.Fn main
+stops when it gets to the root
+of a file system,
+but that's not necessarily
+.Pa / .
+I think what
+.Fn ckroot
+is doing is trying to figure out
+where that file system is mounted.
+It starts by checking the device number
+that the current directory is on.
+Or really it calls
+.Xr stat 2
+on the name of the directory entry that
+.Fn main
+just found,
+which I think must be
+.Pa \&.
+at this point anyway since it's at a root.
+.
+.Pp
+Anyway,
+it then changes directory to and opens
+.Pa /
+and starts reading directory entries from that,
+calling
+.Xr stat 2
+on each of them
+and checking for a matching device number.
+I think this implies that file systems
+can only be mounted in
+.Pa /
+and not at any lower level,
+at least not if you want
+.Xr pwd 1
+to understand it.
+I'm not sure what the check for
+an inode number of 0 is skipping over
+in this loop.
+Some kind of special entry in
+.Pa /
+perhaps.
+.
+.Pp
+Once it finds an entry
+with a matching device number,
+it checks the flags
+to make sure the entry is a directory.
+It does so with hardcoded constants,
+but it seems they haven't changed
+in all these years.
+According to
+.Xr stat 2 ,
+040000 is
+.Dv S_IFDIR .
+The number of file types
+clearly has grown since then though,
+since
+.Dv S_IFMT
+is now 0170000 rather than 060000.
+.
+.Pp
+I think the reason it checks
+that the entry is a directory
+is because if it actually is
+on the root file system already,
+then any regular file
+would have a matching device number.
+If the entry is indeed a directory,
+it then checks if the entry is
+.Pa \&.
+or
+.Pa \&.. ,
+which indicates that it really is already at
+.Pa / .
+If it's not,
+it adds the mount point that it found
+to the front of the path.
+.
+.Pp
+Finally,
+it prints
+.Pa /
+followed by the path it built up.
+If it failed at any point before that,
+it would print the path it had built so far
+with no leading
+.Pa / .
+Better than nothing!
+.
+.Pp
+So that's how I think
+.Xr pwd 1
+works in
+.At v6 .
+It was a fun puzzle to work through,
+and it was interesting to see
+the assumptions it makes.
+How simple things were back then...
+Actually I find it really cool
+that code from 1975
+can still be read and understood
+using knowledge of modern C and UNIX-likes.
+.
+.Sh SEE ALSO
+.Lk https://minnie.tuhs.org/cgi-bin/utree.pl?file=V6
+.Pp
+.Pa pwd.c
+appears in
+.Pa V6/usr/source/s2 .
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I regret saying in two previous posts
+what I planned to write next,
+because this is still not that.
diff --git a/www/text.causal.agency/026-git-comment.7 b/www/text.causal.agency/026-git-comment.7
new file mode 100644
index 00000000..fefb497e
--- /dev/null
+++ b/www/text.causal.agency/026-git-comment.7
@@ -0,0 +1,190 @@
+.Dd September 10, 2021
+.Dt GIT-COMMENT 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm git-comment
+.Nd add comments from commit messages
+.
+.Sh SYNOPSIS
+.Nm git comment
+.Op Fl \-all
+.Op Fl \-comment-start Ar string
+.Op Fl \-comment-lead Ar string
+.Op Fl \-comment-end Ar string
+.Op Fl \-min-group Ar lines
+.Op Fl \-min-repeat Ar lines
+.Op Fl \-no-repeat
+.Op Fl \-pretty Ar format
+.Op Ar options ...
+.Op Fl \-
+.Ar file
+.
+.Sh DESCRIPTION
+The
+.Nm
+command
+adds comments to a file
+showing the commit messages
+which last modified
+each group of lines.
+By default only commit messages with bodies
+and which modified groups of at least 2 lines
+are added.
+Each comment contains
+the abbreviated commit hash
+and the commit summary,
+followed by the commit body.
+.
+.Pp
+.Nm
+accepts all the options of
+.Xr git-blame 1
+in addition to the following:
+.Bl -tag -width Ds
+.It Fl \-all
+Include all commit messages.
+The default is to include
+only commit messages with bodies
+(lines after the summary).
+.
+.It Fl \-comment-start Ar string
+Start comments with
+.Ar string .
+The default is the value of
+.Cm comment.start
+or
+.Ql /* .
+.
+.It Fl \-comment-lead Ar string
+Continue comments with the leading
+.Ar string .
+The default is the value of
+.Cm comment.lead
+or
+.Ql " *" .
+.
+.It Fl \-comment-end Ar string
+End comments with
+.Ar string .
+The default is the value of
+.Cm comment.end
+or
+.Ql " */" .
+.
+.It Fl \-min-group Ar lines
+Add comments only for groups of at least
+.Ar lines .
+The default is 2 lines.
+.
+.It Fl \-min-repeat Ar lines
+Avoid repeating a comment
+if it occurred in the last
+.Ar lines .
+The default is 30 lines.
+.
+.It Fl \-no-repeat
+Avoid repeating comments entirely.
+.
+.It Fl \-pretty Ar format
+Set the pretty-print format
+to use for commit messages.
+The default is the value of
+.Cm comment.pretty
+or
+.Ql format:%h\ %s%n%n%-b .
+See
+.Xr git-show 1 .
+.El
+.
+.Sh EXAMPLES
+For files with
+.Ql #
+comments:
+.Bd -literal -offset indent
+git config comment.start '#'
+git config comment.lead '#'
+git config comment.end ''
+.Ed
+.
+.Pp
+Add as many comments as possible:
+.Bd -literal -offset indent
+git comment --all --min-group 1 --min-repeat 1
+.Ed
+.
+.Pp
+Some examples of output from
+.Xr catgirl 1 :
+.Bd -literal
+/* 347e2b4 Don't apply uiThreshold to Network and Debug
+ *
+ * Messages don't really need to be hidden from <network> and I think
+ * it could be confusing. Debug messages are all Cold so everything
+ * would be hidden, and I want to keep them that way so that <debug>
+ * doesn't clutter the status line needlessly.
+ */
+if (id == Network || id == Debug) {
+	window->thresh = Cold;
+} else {
+	window->thresh = uiThreshold;
+}
+
+/* b4c26a2 Measure timestamp width using ncurses
+ *
+ * This allows for non-ASCII characters in timestamps, and simplifies
+ * things by including the trailing space in the width.
+ */
+int y;
+char buf[TimeCap];
+struct tm *time = localtime(&(time_t) { -22100400 });
+size_t len = strftime(buf, sizeof(buf), uiTime.format, time);
+if (!len) errx(EX_CONFIG, "invalid timestamp format: %s", uiTime.format);
+waddstr(main, buf);
+waddch(main, ' ');
+getyx(main, y, uiTime.width);
+(void)y;
+
+/* 43b1dba Restore toggling ignore with M--
+ *
+ * So that pressing M-- repeatedly maintains the previous behavior.
+ */
+if (n < 0 && window->thresh == Ice) {
+	window->thresh = Cold;
+} else {
+	window->thresh += n;
+}
+
+/* 1891c77 Preserve colon from previous tab-complete
+ *
+ * This fixes the case when pinging multiple nicks and one of them needs to
+ * be cycled through.
+ */
+bool colon = (tab.len >= 2 && buf[tab.pos + tab.len - 2] == L':');
+.Ed
+.
+.Sh SEE ALSO
+.Lk https://git.causal.agency/src/tree/bin/git-comment.pl
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Pp
+In case it's unclear,
+this is a
+.Xr git 1
+subcommand I wrote.
+Did you know you can add new
+.Xr git 1
+subcommands just by
+adding executables named
+.Pa git-*
+to somewhere in
+.Ev PATH ?
+.
+.Pp
+This is also,
+I think,
+my third Perl script ever.
+It's an interestingly shaped language.
+Quite neat.
diff --git a/www/text.causal.agency/027-openbsd-linode.7 b/www/text.causal.agency/027-openbsd-linode.7
new file mode 100644
index 00000000..9f40de42
--- /dev/null
+++ b/www/text.causal.agency/027-openbsd-linode.7
@@ -0,0 +1,202 @@
+.Dd September 26, 2021
+.Dt OPENBSD-LINODE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Installing OpenBSD on Linode
+.Nd a guide
+.
+.Sh DESCRIPTION
+I've been thinking for a while
+about moving my servers to Linode,
+and also about moving them to
+.Ox .
+I actually originally got into
+.Fx
+(and from there,
+.Ox )
+only because DigitalOcean
+started offering it as a
+.Dq droplet
+image.
+I've been running those servers fine for years,
+but now I prefer to run
+.Ox ,
+and some recent DigitalOcean outages
+had me thinking about it more,
+so I'm giving it a shot.
+.
+.Pp
+As an aside,
+running
+.Ox
+on DigitalOcean
+is not really a good option.
+It seems more awkward to install your own OS there,
+and if you do,
+I've heard that IPv6 won't work
+because they don't know how to run SLAAC.
+Also,
+now that I've used
+the Linode control panel and LISH a bit,
+DigitalOcean kind of feels like a toy
+in comparison.
+.
+.Pp
+Here's what I did to install
+.Ox
+on Linode:
+.Bl -enum
+.It
+Create a Linode with the
+.Dq Choose a Distribution
+box blank.
+.
+.It
+Under the Storage tab,
+create a disk called
+.Dq miniroot
+of type raw
+with size 8 MB.
+This will hold the install image.
+.
+.It
+Create another disk called
+.Dq root
+of type raw
+using the remaining available storage.
+.
+.It
+Boot the Linode in rescue mode
+from the option in the three-dots menu
+next to
+.Dq Power On .
+Attach
+.Dq miniroot
+to
+.Pa /dev/sda .
+.
+.It
+Log into the LISH console
+and obtain the install image:
+.Bd -literal
+curl -O https://cdn.openbsd.org/pub/OpenBSD/6.9/amd64/miniroot69.img
+dd if=miniroot69.img of=/dev/sda
+.Ed
+.Pp
+Power off the Linode.
+.
+.It
+Under the Configurations tab,
+create a configuration called
+.Dq install
+in full virtualization mode.
+Paravirtualization works fine once installed,
+but for some reason the installer
+can't see the root disk
+without full virtualization.
+Under boot settings,
+select direct disk.
+Attach
+.Dq root
+to
+.Pa /dev/sda ,
+.Dq miniroot
+to
+.Pa /dev/sdb
+and set the root device to
+.Pa /dev/sdb .
+.
+.It
+Create a similar configuration called
+.Dq boot
+but using paravirtualiztion
+and without
+.Dq miniroot
+attached.
+Set the root device to
+.Pa /dev/sda .
+.
+.It
+Boot the
+.Dq install
+configuration,
+launch the LISH console
+and switch to Glish.
+It's possible
+to have the installer use serial console,
+but it requires entering commands
+at the boot prompt
+before the timeout,
+and I never managed it.
+If you do manage it,
+run:
+.Bd -literal
+stty com0 9600
+set tty com0
+boot
+.Ed
+.
+.It
+Proceed through the
+.Ox
+installer.
+When asked to
+change the default console to com0,
+answer yes
+so that regular LISH will work.
+Power off the Linode.
+.
+.It
+Boot the
+.Dq boot
+configuration
+and log in to LISH.
+Since the installer configured networking
+in full virtualization,
+rename the file to the paravirtualized interface:
+.Bd -literal
+mv /etc/hostname.em0 /etc/hostname.vio0
+.Ed
+.Pp
+In order to get the right public IPv6 address,
+disable privacy extensions
+by changing the inet6 line of
+.Pa hostname.vio0
+to:
+.Bd -literal
+inet6 autoconf -temporary -soii
+.Ed
+.
+.It
+Bring networking up
+and run
+.Xr syspatch 8
+since
+.Pa rc.firsttime
+couldn't do it:
+.Bd -literal
+sh /etc/netstart
+syspatch
+.Ed
+.
+.It
+Reboot.
+.El
+.
+.Pp
+I guess I'll be slowly moving things over
+to the new servers
+for the next little while.
+With any luck the next post here
+will not say
+.Fx
+in its header!
+.
+.Sh SEE ALSO
+I learned the basic idea
+of how to do this from
+.Lk https://www.subgeniuskitty.com/notes/openbsd_on_linode .
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/028-names.7 b/www/text.causal.agency/028-names.7
new file mode 100644
index 00000000..de47c074
--- /dev/null
+++ b/www/text.causal.agency/028-names.7
@@ -0,0 +1,81 @@
+.Dd October 30, 2021
+.Dt NAMES 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Names
+.Nd three types
+.
+.Sh DESCRIPTION
+There are (at least) three
+different types of names
+a person has.
+.
+.Pp
+First, there are normie names.
+These are names usually made up
+of several words
+each of which is capitalized.
+Most people have one of these,
+but it's possible to have more.
+They're names that might appear on
+various types of Documents.
+A
+.Dq legal name
+(dubious)
+is a normie name,
+but normie names need not be
+.Dq legal
+(dubious).
+I list this category first
+not because it's more important,
+but because it is by far the most boring.
+.
+.Pp
+Next, there are Real Names.
+Most people have at least a few
+and will probably go through
+different ones over time.
+Your Real Names are anything people
+use to refer to you.
+On the internet these are often not capitalized.
+Sometimes that is the only distinction
+between a Real Name
+and a normie name.
+.
+.Pp
+There was a period of time
+when I was playing a lot of TF2
+and not really leaving my apartment.
+I had set my steam name to
+.Dq gluten product
+(yeah, from that dril tweet)
+and I talked in the game's voice chat
+quite a bit.
+Naturally other Gamers in voice chat
+called me
+.Dq gluten
+and at some point I realized
+that over the span of months
+I had been refered to as
+.Dq gluten
+more often than any other name.
+So that was a Real Name of mine.
+People used it and I responded to it.
+.
+.Pp
+Last, there are the True Names.
+The kind of name that knowledge of
+gives one power over a person.
+I don't think any humans
+know their own True Names,
+but I do believe they exist.
+It's possible that other animals
+know theirs.
+It's probably best not to know though, right?
+I think if I knew mine
+I would always worry
+about accidentally revealing it.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/029-topics.7 b/www/text.causal.agency/029-topics.7
new file mode 100644
index 00000000..d071eb67
--- /dev/null
+++ b/www/text.causal.agency/029-topics.7
@@ -0,0 +1,116 @@
+.Dd January  8, 2022
+.Dt TOPICS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Topics
+.Nd a bit of a mess
+.
+.Sh DESCRIPTION
+Shortly after my last post
+I started writing another one
+but I never finished it.
+I don't think I had enough to say,
+or if I did it meant going into
+a whole extra thing.
+I also had a list in my mind
+of other things to write about,
+but inspiration hasn't really struck
+for any of them.
+I'm currently in the mood
+to write something anyway,
+so I'm just going to write a bit
+about the topics I have in mind.
+I may or may not ever
+write more about any of them.
+.
+.Pp
+The post I had started writing
+(twice, actually)
+was about voices.
+I like them a lot
+and I'm fascinated by them.
+The problem is
+I don't actually have much
+to say about it
+without getting into Gender,
+which as I say is a whole extra thing,
+and not something I've written about here before.
+.
+.Pp
+When I started writing here,
+I didn't want to blog about
+personal topics or LGBTQ stuff.
+But more recently
+I want to move away from
+only writing about computers.
+Or maybe away from
+writing about computers entirely.
+There are more interesting things,
+but I don't have experience
+writing about them.
+Yet,
+I should say.
+.
+.Pp
+I'm honestly still not sure
+if writing about gender here
+is at all a good idea.
+But it turns out to feel like
+a bit of a prerequisite
+for other things.
+I find gender perception
+in particular
+to be fascinating.
+It's interesting.
+It's neat.
+And I don't know if I can
+write anything coherent about it.
+.
+.Pp
+Related to that,
+I've been thinking of writing
+about how the pandemic
+has had a strangely positive effect
+on my life.
+Or at least,
+I've made a lot of positive changes
+during it.
+I'm in a better place emotionally now
+than ever before,
+and that obviously runs counter
+to most people's experiences.
+Additionally with that positive outlook
+I want to write about the meaning
+of my domain name.
+I'm proud of it.
+.
+.Pp
+This week the topic of fetish
+has been on my mind.
+That actually feels
+a bit less risky
+to write about than gender.
+And it may honestly be more interesting.
+I don't know.
+There's not enough sex
+on computer blogs,
+or whatever this is.
+Although my main ideas
+are not about sex at all.
+.
+.Pp
+Just this turned out to be
+harder to write than I thought it would be.
+I think I want to populate this space
+with more short posts like the previous one.
+I wrote that while very sleepy
+after 3 AM though,
+and I don't exactly
+want to repeat that regularly.
+We'll see.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Listening to Kate Bush \(em Hounds of Love.
diff --git a/www/text.causal.agency/030-discs.7 b/www/text.causal.agency/030-discs.7
new file mode 100644
index 00000000..df73a750
--- /dev/null
+++ b/www/text.causal.agency/030-discs.7
@@ -0,0 +1,114 @@
+.Dd January  8, 2022
+.Dt DISCS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Desert Island Discs
+.Nd we're doing three in this one
+.
+.Sh DESCRIPTION
+In typical fashion
+I'm going to write about something
+completely different instead.
+Something short and simple.
+I got thinking about this
+after reading a little interview thing
+this week.
+The question is
+which three albums would you want to have
+if you were stranded on a desert island.
+What could you listen to
+for the rest of time?
+It's surprisingly easy
+to take this question very seriously.
+.
+.Pp
+My immediate thought was
+.Em Music for 18 Musicians.
+I've literally said this about it
+in conversation before.
+That's an album
+I'd want to have on a desert island.
+I find it incredibly soothing,
+almost hypnotic.
+I really do feel like
+I could listen to it forever.
+And then maybe I could finally determine
+which of its eleven sections
+is the best.
+.
+.Pp
+My next thought was
+.Em Soundtracks for the Blind .
+We already know I'm a huge SWANS fan.
+Despite what I've written about
+.Em Swans Are Dead ,
+I instead jumped to SFTB.
+I still think that
+.Em Dead
+has better tunes,
+but
+.Em Soundtracks
+is definitely the better cohesive album.
+It has such atmosphere and mood on it.
+Like
+.Em 18 ,
+it's an album that sucks me in.
+Also,
+either SWANS album
+is an economical choice
+in this hypothetical
+since they're each 2 hours and 20 minutes long.
+.
+.Pp
+Choosing a third album is a lot harder.
+There's so much other music I like
+and only one slot left.
+There's no other single album
+that stands out above the rest
+like the previous two,
+for me.
+.Em Wildlife ,
+maybe?
+Or 
+.Em Jane Doe ?
+Perhaps a classic like
+.Em Aeroplane ,
+or a boomer classic like
+.Em The Wall .
+But would I really want to
+listen to any of those
+to the exclusion of everything else?
+They're too mood-dependent.
+.
+.Pp
+Then I realized the perfect choice
+for third album.
+.Em Mouth Moods .
+A mashup album is the perfect wildcard,
+and
+.Em Moods
+is just fun as hell to listen to.
+I get songs from it stuck in my head
+instead of the originals.
+The final track,
+.Em Shit ,
+always gets me moving.
+It's a masterpiece.
+.
+.Bl -enum
+.It
+Steve Reich Ensemble \(em
+.Em Music for 18 Musicians
+.It
+SWANS \(em
+.Em Soundtracks for the Blind
+.It
+Neil Cicierega \(em
+.Em Mouth Moods
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Listening to Steve Reich Ensemble \(em Music for 18 Musicians.
diff --git a/www/text.causal.agency/031-books-2021.7 b/www/text.causal.agency/031-books-2021.7
new file mode 100644
index 00000000..d7b46f17
--- /dev/null
+++ b/www/text.causal.agency/031-books-2021.7
@@ -0,0 +1,127 @@
+.Dd January 12, 2022
+.Dt BOOKS-2021 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Books 2021
+.Nd a review, I guess
+.
+.Sh DESCRIPTION
+In 2021 I read 26 books.
+Finished the 26th right on December 31st.
+It's not a lot but it's more than last year.
+Here are the ones I loved
+(in the order I read them).
+I will avoid spoilers,
+of course.
+.
+.Ss Network Effect by Martha Wells
+I've been reading the
+.Em Murderbot Diaries
+series for a while.
+They're fun stories.
+I liked this full-length novel entry a lot.
+I guess it felt like it had more room
+for the characters to develop.
+This is probably when I started
+asking my friends if they'd read it
+because I wanted to talk about
+Murderbot gender vibes.
+.Pp
+You may like if: you're trans.
+.
+.Ss The Once and Future Witches by Alix E. Harrow
+Um,
+it's about witches!
+One of them has the same name as me.
+Kind of has some similar vibes to
+.%T The Future of Another Timeline ,
+which was my favourite book I read in 2020.
+.Pp
+You may like if: you like women.
+.
+.Ss A Desolation Called Peace by Arkady Martine
+I was so excited for this sequel to
+.%T A Memory Called Empire ,
+another previous favourite
+and something I've been wanting more of.
+I kinda wish there was more fucking in it though honestly.
+.Pp
+You may like if: you like women.
+.
+.Ss Piranesi by Susanna Clarke
+Really something different.
+It turned out to be a different story
+than I expected
+from reading the first few pages.
+.Pp
+You may like if: you like statues, I guess?
+.
+.Ss A Psalm for the Wild-Built by Becky Chambers
+Ok yes I do give 3/3 stars
+to every Becky Chambers book.
+They're so fucking good.
+I'm looking forward to
+more entries in this novella series.
+(Also I'm currently reading
+the fourth
+.Em Wayfarers
+book
+and loving it too!)
+.Pp
+You may like if: your pronouns are they/them <3
+.
+.Sh HONOURABLE MENTIONS
+.Ss Her Body and Other Parties by Carmen Maria Machado
+I really enjoyed the short story
+.Dq Especially Heinous: 272 Views of Law & Order SVU
+in this collection.
+It goes on a bit too long
+but the format is unique.
+You can read that one online,
+actually.
+.
+.Ss The Hobbit by J. R. R. Tolkien
+Yeah I hadn't read this until last year.
+I borrowed it after marathoning
+the extended editions of the
+.%T Lord of the Rings
+trilogy during a heat wave.
+As I said at the time,
+pretty good for something
+written by a man
+like a hundred years ago.
+Kind of hilarious that women
+just don't exist
+in the world of
+.%T The Hobbit .
+.
+.Ss Earthlings by Sayaka Murata
+Pretty fucking wild.
+I'd recommend it,
+but I have to say it
+.Em extremely
+needs a child sexual abuse content warning on it.
+.
+.Ss Six Months, Three Days, Five Others by Charlie Jane Anders
+A surprising number of these short stories
+are actual stories!
+They have beginnings,
+middles
+and ends!
+.
+.Ss The City in the Middle of the Night by Charlie Jane Anders
+It's got some
+.Em Xenogenesis
+series vibes.
+Sophie is a goddamn lesbian idiot though
+and she never even realizes it.
+.
+.Sh SEE ALSO
+.Lk https://git.causal.agency/src/tree/txt/books.txt
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Listening to
+.Em Ear Massage with Latex Gloves 100% Sensitivity 40 minute (No Talking) .
diff --git a/www/text.causal.agency/032-albums-2021.7 b/www/text.causal.agency/032-albums-2021.7
new file mode 100644
index 00000000..72c1d0d2
--- /dev/null
+++ b/www/text.causal.agency/032-albums-2021.7
@@ -0,0 +1,173 @@
+.Dd January 13, 2022
+.Dt ALBUMS-2021 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Albums 2021
+.Nd a review
+.
+.Sh DESCRIPTION
+Every year I create a new playlist
+in iTunes
+(Music dot app, whatever)
+for the albums I listen to that year.
+It's usually embarrassingly short.
+I don't listen to new music
+as much as I'd like,
+and usually only one or two
+are actually from the current year.
+Not that the playlist
+is limited to new (to me) music.
+If I get really into an album
+I've heard before,
+more than before,
+I also add it to the list.
+Anyway,
+this is a review
+of my 2021 albums playlist.
+.
+.Ss Black Country, New Road \(em For the First Time (2021)
+I first heard the single
+.Em Sunglasses
+from someone sharing it on IRC,
+and I loved it,
+so I was looking forward to this album.
+What a let down though.
+The version of
+.Em Sunglasses
+on the album is just plain worse
+than the single version.
+I still got some decent listening
+out of the album,
+but that just sours it for me.
+.Pp
+Favourite track:
+.Em Track X .
+.
+.Ss Black Dresses \(em Forever \&In Your Heart (2021)
+I fucking love Black Dresses.
+.Em Peaceful as Hell
+is one of my all-time favourite albums.
+I'm glad they put out another one
+after it looked like they wouldn't.
+The sounds are just so good.
+Exactly what my ears crave.
+The texture of it
+tickles my brain clit.
+.Pp
+Favourite tracks:
+.Em Waiting42moro ,
+.Em Mistake .
+.
+.Ss Low \(em Drums and Guns (2007)
+I've long loved the song
+.Em Breaker
+and its music video,
+but I only listened to the album
+it's on last year.
+Something I didn't realize,
+I guess because I usually pulled up
+the music video
+without headphones on,
+is how aggressively this album
+uses stereo panning.
+Vocals are generally
+panned hard right throughout,
+with much of the instrumentation
+panned centre or hard left.
+It's bold
+and it really works for me.
+I especially love the vocal harmony on
+.Em Breaker
+all the way on the opposite channel.
+Bring back stereo separation!
+.Pp
+Favourite tracks:
+.Em Breaker ,
+.Em Murderer ,
+.Em Violent Past .
+.
+.Ss The Armed \(em Ultrapop (2021)
+I have to admit
+I didn't actually listen to this one much.
+I listened to the previous album,
+.Em Only Love ,
+a lot in 2020.
+I think this album is good,
+but I'll probably only really get into it
+in some future year.
+.
+.Ss Lingua Ignota \(em Caligula (2019)
+Dear lord,
+why did I wait so long
+to listen to this one.
+I had heard
+.Em "Do You Doubt Me Traitor"
+back when it came out,
+but somehow I didn't realize
+just how much this album
+would be my shit.
+Fucking incredible vocals.
+Lovely sometimes minimal,
+sometimes extreme
+instrumentals
+and exquisite percussion.
+The sound of,
+I believe,
+a lightbulb rolling around on the floor on
+.Em Fragrant
+is such an interesting addition.
+.Pp
+Favourite tracks:
+.Em "Do You Doubt Me Traitor" ,
+.Em "Fragrant Is My Many Flower'd Crown" ,
+.Em "If the Poison Won't Take You My Dogs Will" .
+.
+.Ss Black Dresses \(em LOVE AND AFFECTION FOR STUPID LITTLE BITCHES (2019)
+I wanted even more Black Dresses
+and fortunately there was still more
+I hadn't yet listened to!
+I've already gushed about Black Dresses
+so I'll spare you.
+They're so good though.
+.Pp
+Favourite tracks:
+.Em STATIC ,
+.Em HERTZ ,
+.Em MY HEART BEATS OUT OF TIME .
+.
+.Ss Barenaked Ladies \(em All Their Greatest Hits: Disc One 1991-2001
+What?
+Yeah,
+late last year I decided to revisit BNL.
+My parents listened to them a lot
+when I was growing up,
+and I liked them too.
+The first show I ever went to was the
+.Dq Barenaked for the Holidays
+tour with my parents.
+It turns out
+I still think their '90s stuff
+is pretty darn good!
+Steven Page is really a great singer.
+This is also the first time
+I'm listening to these tunes
+with fancy headphones
+and it sounds great.
+Honestly
+.Em The Old Apartment
+can totally compete
+with the favourites
+I've accumulated more recently.
+\&'90s alt rock was good actually?
+.Pp
+Favourite tracks:
+.Em The Old Apartment ,
+.Em Brian Wilson ,
+.Em What a Good Boy ,
+.Em Too Little Too Late .
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Listening to all my favourite tracks :)
diff --git a/www/text.causal.agency/033-jorts.7 b/www/text.causal.agency/033-jorts.7
new file mode 100644
index 00000000..001f877c
--- /dev/null
+++ b/www/text.causal.agency/033-jorts.7
@@ -0,0 +1,485 @@
+.Dd February  2, 2022
+.Dt JORTS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Introducing Jorts
+.Nd june's ports
+.
+.Sh DESCRIPTION
+Alright so I've gone off the deep end,
+maybe.
+After continual frustration with MacPorts
+culminating in not being able to install
+.Xr nvi 1
+on my work MacBook,
+I have just gone ahead
+and started my own personal ports tree
+for macOS.
+After a couple of weeks,
+I have 32 ports in my tree
+and only two remaining requested ports
+installed from MacPorts.
+.
+.Pp
+I set out with a couple ideas in mind:
+.Bl -bullet
+.It
+This will be my own personal ports tree.
+It only has to work for me.
+Since I'm using it on both
+my personal Intel MacBook Pro
+still running Catalina
+and my work M1 MacBook Pro
+running Monterey,
+it is at least that portable.
+.
+.It
+It's ok to rely on
+system libraries and tools
+provided by macOS.
+I'm not creating a distro,
+so it doesn't need to be totally isolated.
+This lets me skip really annoying things
+like compiler toolchains.
+.
+.It
+Sources get vendored,
+either from release tarballs
+or with
+.Xr git-subtree 1 .
+This allows totally pain-free
+local patching,
+and boy has this paid off.
+I can just do what I need to do
+to get the thing to build how I want
+and commit it in git like anything else.
+.Pp
+It also means that the tree itself
+is entirely self-contained
+and doesn't rely on any external sources
+or network access.
+Honestly with some old and obscure software
+it feels like upstream could disappear at any moment,
+so this gives me peace of mind too.
+.Pp
+Another advantage of vendoring upstream sources
+is that all of the code installed on my system
+(in
+.Pa /usr/local
+anyway)
+is easily inspected,
+much like
+.Pa /usr/src
+on a BSD.
+This can be super useful for debugging
+or just for reference.
+.
+.It
+Produce simple package tarballs.
+They're just the contents of
+.Ev DESTDIR
+after a staged install.
+They get installed for real
+by untarring them in
+.Pa / .
+They can then be uninstalled
+(or upgraded)
+by removing the paths contained
+in the tarball from the system.
+.
+.It
+Track installed packages with symbolic links
+to specific package tarballs.
+Keep old tarballs around for rollbacks.
+This means I can see what's installed
+with plain old
+.Xr ls 1 !
+.Bd -literal
+$ ls */Installed
+\&...
+libretls/Installed          toilet/Installed
+mandoc/Installed            tree/Installed
+
+$ ls -l toilet/Installed
+lrwxr-xr-x  1 root  staff  19 17 Jan 21:45 toilet/Installed -> toilet-0.3~1.tar.gz
+.Ed
+.
+.It
+Use
+.Xr bmake 1 .
+It's scrutable.
+It also knows how to bootstrap itself
+pretty well.
+Since
+.Xr bmake 1
+is itself a port in my tree
+that would require
+.Xr bmake 1
+to build and install,
+I wrote a small
+.Pa Bootstrap
+shell script
+to install
+.Xr bmake 1
+.Dq manually
+then use that
+.Xr bmake 1
+to build and install its own port.
+It also requires a bit of care
+when upgrading the
+.Xr bmake 1
+port since macOS
+rather doesn't like a binary
+deleting itself while it's running.
+.
+.It
+No GNU software.
+I simply refuse to do it.
+To that end,
+prefer configuring/building with
+.Xr cmake 1
+where at all possible.
+I fell into this early on
+since I originally just wanted to install
+.Xr nvi 1
+and
+.Sy lichray/nvi2
+is a better upstream source these days
+that uses
+.Xr cmake 1 .
+.Pp
+With a port and support for
+.Xr cmake 1
+in
+.Pa Port.mk ,
+I can make changes to
+.Pa CMakeLists.txt
+files without issue.
+I can also vendor upstreams
+directly from git
+rather than having to find
+release tarballs with generated
+.Pa configure
+scripts and so on.
+When I need to make changes
+to the build systems of projects using autotools,
+I either have to have autotools installed
+(from outside my tree)
+or painstakingly reflect my edits by hand
+in the generated files,
+both of which suck hard.
+.El
+.
+.Pp
+Ok so that's actually quite a number of ideas.
+But they have come together
+into something surprisingly usable
+surprisingly quickly!
+Like I said,
+this is only intended to be
+my own personal ports tree,
+but I hope that some of these ideas
+are interesting
+and maybe inspire others
+to explore similar approaches.
+.
+.Pp
+But wait,
+I'm not done yet!
+There are some other interesting things
+that I came up with along the way,
+and also some complaints
+about some upstreams,
+but I'll try to keep those to a minimum.
+.
+.Pp
+So it turns out that dependencies are hard.
+Who knew?
+It's easy enough to enforce
+direct dependencies
+at build time
+by just checking for the required
+.Pa Installed
+symlinks.
+It's less straightforward
+to do this recursively,
+which you need if
+you want to be able to say,
+.Do
+Install
+.Xr nvi
+for me!
+.Dc
+and get
+.Xr ncurses 3 ,
+.Xr cmake 1
+and
+.Xr pkgconf 1
+installed first
+if they aren't already.
+.
+.Pp
+Rather than trying to do all that in
+.Xr bmake 1 ,
+I wrote a shell script called
+.Pa Plan ,
+which itself produces a shell script.
+Given a list of ports
+to install or upgrade,
+it recursively gathers their dependencies
+and feeds them to
+.Xr tsort 1 ,
+which is a neat utility
+which topologically sorts a graph.
+In other words,
+it determines the order
+in which the graph of dependencies
+should be installed.
+The
+.Pa Plan
+script produces a list of
+.Xr bmake 1
+commands to make that happen
+on standard output,
+which can be piped to
+.Xr sh 1 .
+So,
+the way to say the above is:
+.Bd -literal -offset ident
+$ ./Plan -j4 nvi | sh -e
+.Ed
+.
+.Pp
+Now,
+what's missing from this approach
+is the ability to automatically
+uninstall no-longer-needed dependencies.
+It's something I've criticized Homebrew for lacking
+and one of the reasons I started using MacPorts,
+so it's somewhat ironic that
+my own system lacks it as well.
+However,
+I don't think it's much of a problem,
+since I'm only packaging
+what I actually want installed
+in the first place.
+On my personal computer,
+I have all 32 of my ports installed,
+and I expect that to continue.
+I can always keep using MacPorts
+to install things I only intend
+to use temporarily.
+.
+.Pp
+Another thing I was slightly concerned about
+from the beginning was disk usage.
+I think the benefits of vendoring sources
+far outweigh the cost in storage,
+but it would be nice to at least minimize that cost.
+Previously,
+I wrote about
+.Xr git-sparse-checkout 1 ,
+which allows you to only have certain paths
+checked out in your git working tree.
+Since port sources aren't always interesting
+and only
+.Em required
+while actually building the port,
+it makes sense to not have them always checked out.
+.
+.Pp
+Rather than manipulate
+.Xr git-sparse-checkout 1
+myself,
+I added support for it
+directly into
+.Pa Port.mk .
+If sparse checkout is enabled,
+building a port will automatically
+add its source tree to the checkout list,
+and cleaning that port will
+remove it from the list.
+At rest,
+only the port system itself
+and the package tarballs
+need to be present on the file system.
+.
+.Pp
+It turns out that upstream
+build system behaviour
+is super inconsistent,
+even among projects using
+the same tools.
+I started collecting a list of checks
+to perform on the output of my port builds
+to make sure they didn't do anything weird.
+They live in
+.Pa Check.sh ,
+which gets run
+when a package tarball is created.
+The current list of checks is:
+.Bl -bullet
+.It
+Check for directories not included by
+.Ev PACKAGE_DIRS .
+In other words,
+make sure the port isn't
+trying to install anything
+outside of
+.Pa /usr/local .
+Sometimes this makes sense,
+though,
+which is what
+.Ev PACKAGE_DIRS
+is for.
+.It
+Check for references to PWD,
+i.e. the build directory.
+This can mean the build
+didn't understand
+.Ev PREFIX
+and
+.Ev DESTDIR
+correctly,
+or that it built with debug info.
+.It
+Check for binaries without manuals.
+If your software installs an executable in
+.Pa bin
+but not a manual page,
+your software is incomplete!
+Sometimes this just means
+I missed an extra documentation install target.
+.It
+Check for dynamic linking to outside objects.
+In other words,
+if something ended up linking to
+a library installed by MacPorts
+rather than the one from
+.Nm jorts
+or macOS.
+.It
+Check for dynamic linking
+to system libraries
+.Nm jorts
+provides instead.
+Similar to the last one,
+if both macOS and
+.Nm jorts
+provide a library,
+check that ports link with the latter.
+.It
+Check for scripts with outside interpreters.
+This is analogous to the linking checks
+but for scripts,
+checking that their shebang lines
+refer to interpreters installed
+by macOS or
+.Nm jorts .
+.El
+.
+.Pp
+A number of my ports
+still fail some of these checks,
+but I have fixed a lot of problems
+the script called out.
+.
+.Pp
+Speaking of problem ports...
+git's build system is truly awful.
+I'm sorry,
+it's just really disappointing.
+On the upside though,
+I did manage to patch it
+to use
+.Xr asciidoctor 1
+directly to generate manual pages
+from asciidoc source,
+rather than generating docbook or whatever
+then converting that.
+One less build dependency!
+I also fixed up curl's
+.Pa CMakeLists.txt
+(which I guess are normally only used on Windows)
+to build and install documentation properly.
+And I got libcaca's Cocoa driver working again!
+Very important to be able to run
+.Xr cacafire 1
+in a Cocoa window.
+.
+.Pp
+Shout out to SDL2,
+which didn't require any patching
+or extra options beyond
+.Ev USE_CMAKE=yes .
+Model upstream.
+.
+.Pp
+Some other odds and ends:
+I like being able to name ports how I want
+(for example,
+.Sy ag )
+and use my own port version convention,
+using
+.Ql +
+to append VCS revisions
+and
+.Ql ~
+to append port revisions.
+I don't think those are likely
+to ever clash with upstream versioning schemes.
+Not that I even need to follow upstream versioning.
+There is no reason the version number of
+.Xr dash 1
+should start with a zero.
+.
+.Pp
+Speaking of versions,
+a big downside of maintaining your own ports tree
+is that you actually need to update it.
+Thankfully,
+once I packaged
+.Xr curl 1
+and
+.Xr jq 1
+(which needs a new release dammit,
+it's been 4 years and the build is broken
+on macOS),
+I could use the Repology API
+to check if I'm behind everyone else.
+Far more reliable than
+trying to automate checking upstreams
+for new versions.
+That lives in the
+.Pa Outdated
+shell script.
+.
+.Pp
+Phew!
+I wrote a lot about this.
+It feels a little self-indulgent,
+but I've had fun working on this
+and want to share.
+If anyone else tries anything similar,
+or is weird enough to give
+.Nm jorts
+a try themselves,
+I'd love to hear about it!
+.
+.Sh SEE ALSO
+.Lk https://git.causal.agency/jorts/
+.Pp
+.Lk https://youtu.be/Sx3ORAO1Y6s
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+Listening to
+.Em Arcade Fire \(em Arcade Fire (EP) ,
+.Em Arcade Fire \(em The Suburbs .
+.Pp
+Typed on a brand new
+Leopold FC660M
+with Cherry MX Red switches.
+Lovely keyboard.
diff --git a/www/text.causal.agency/034-voices.7 b/www/text.causal.agency/034-voices.7
new file mode 100644
index 00000000..4990295d
--- /dev/null
+++ b/www/text.causal.agency/034-voices.7
@@ -0,0 +1,56 @@
+.Dd March  5, 2022
+.Dt VOICES 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Voices
+.Nd more kinds of them
+.
+.Sh DESCRIPTION
+Welcome to the third time
+I started writing this post!
+I think the first time
+was after watching a jan Misali video
+that had clips of audio interviews in it.
+It got me thinking about
+how interesting it was
+to hear someone's voice
+without knowing anything else about them.
+.
+.Pp
+That's pretty much all I managed to write
+the first two times I started this.
+If I get past this next sentence,
+then I can probably finish the post.
+What stopped me was that
+all my thoughts and feelings about voices
+are influenced by being trans
+(and being a fan of other trans people),
+and I thought,
+.Dq I don't write about that here,
+but why don't I?
+I don't have to come out to my blog.
+.
+.Pp
+So really what I have been wanting to say is this:
+every trans woman's voice that I have heard
+has sounded genuinely wonderful to me.
+Especially if you're reading this
+and we've been on a voice call before.
+I know,
+voices are the object of so much self-consciousness,
+but I really wish they didn't have to be.
+Most of us do not sound like cis women
+and to me that is fine.
+Good, actually.
+Trans women sound like trans women.
+As a voice appreciator,
+I am so happy to hear more kinds.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I've been watching some Vektroid streams lately,
+and I love her voice.
+It was another thing
+reminding me to write this.
diff --git a/www/text.causal.agency/035-addendum-2021.7 b/www/text.causal.agency/035-addendum-2021.7
new file mode 100644
index 00000000..262f2178
--- /dev/null
+++ b/www/text.causal.agency/035-addendum-2021.7
@@ -0,0 +1,111 @@
+.Dd March 18, 2022
+.Dt ADDENDUM-2021 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Addendum 2021
+.Nd missed music
+.
+.Sh DESCRIPTION
+I just realized that I totally forgot
+some important music for me from last year
+in my Albums 2021 post,
+because it wasn't in my playlist.
+Last year I watched
+the Berserk anime from 1997,
+and its soundtrack is incredible.
+.
+.Pp
+Actually the only reason
+I started watching it at all
+was because of the music.
+I was watching the wayneradiotv stream,
+.Do
+Mon repas durant un temps de tristesse;
+un pizza je n'oublierai jamais
+.Dc
+and I was mesmerized by the Guts theme.
+I had to find out what it was from.
+This was also around the time
+that Kentaro Miura died
+so people were really talking about it.
+Anyway just hearing
+that part of the soundtrack
+got me to start watching the anime,
+since you can find it all on youtube.
+.
+.Pp
+The anime in general did not disappoint.
+Actually it's really fucking good,
+and so is the rest of the soundtrack.
+The title sequence and credits tracks
+are so good that I let them play
+every episode even though
+I watched the series over only like 2 days.
+.
+.Pp
+I absolutely love whatever genre this stuff is.
+Is '90s anime intros its own genre?
+Something about combining
+acoustic and electric guitars,
+maybe.
+I'm also fond of
+the poorly written english lyrics.
+They're poetic in a distinctive way.
+I feel the same about
+that Shinsei Kamattechan
+song that was used for the credits of
+Attack on Titan season 2.
+Honestly awesome to write lyrics
+in a second language you haven't mastered.
+.
+.Pp
+So,
+the intro track,
+.Em Tell Me Why .
+First off,
+that sword sound effect
+near the beginning rules.
+Put that in more songs.
+What I really can't get enough of
+on this track are
+the quiet shouty vocals
+a bit off to the left
+during the chorus.
+It's such a cool idea
+to have clean lead vocals
+and shouting in the background.
+.
+.Pp
+And the credits track,
+.Em "Waiting So Long" .
+That first low note is so good.
+This is really a perfect credits song
+for the atmosphere of the show.
+It's creeping.
+The dual vocals
+the whole way through
+are such an interesting texture.
+Both of these tracks
+have really cool vocal sounds.
+And that dirty final guitar chord
+is a great sound to end on.
+.
+.Sh SEE ALSO
+These aren't great quality uploads
+but this stuff is sadly hard to find.
+.Bl -tag -width Ds
+.It "Guts"
+.Lk https://youtu.be/vZa0Yh6e7dw
+.It "Earth"
+.Lk https://youtu.be/5iAViNf9Z4Y
+.It "Penpals \(em Tell Me Why"
+.Lk https://youtu.be/I2rV8oKWSdM
+.It "Silver Fins \(em Waiting So Long"
+.Lk https://youtu.be/70GD2SBCq64
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+.Dq I like swinging my sword in battle.
diff --git a/www/text.causal.agency/036-compassion.7 b/www/text.causal.agency/036-compassion.7
new file mode 100644
index 00000000..9d0d887d
--- /dev/null
+++ b/www/text.causal.agency/036-compassion.7
@@ -0,0 +1,105 @@
+.Dd March 31, 2022
+.Dt COMPASSION 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Compassion
+.Nd better world fiction
+.
+.Sh DESCRIPTION
+Recently I watched the film
+.Em Margarita With a Straw .
+I'm not sure how to feel
+about some aspects of it,
+but it tries to do a lot,
+and I was still thinking about it
+a couple days later.
+.
+.Pp
+What really sticks out about it,
+to me,
+is that it is
+better world fiction,
+for lack of a better term.
+It's a film about two characters
+with disabilities,
+but it doesn't play into tropes.
+There's no big dramatic scene
+where a character gets treated unfairly.
+It doesn't really happen.
+In the world of the movie,
+most people are accepting,
+patient
+and compassionate.
+That's not to say
+there is no conflict.
+The film is just telling a different story.
+.
+.Pp
+The story takes place
+in a better world.
+Or maybe it takes place
+in a world that exists
+within our own,
+hidden between the worse parts.
+It's wonderfully subversive.
+Because I went into the film
+expecting at least one deeply upsetting
+scene of discrimination.
+What else would you expect
+of a story like this one,
+right?
+But instead of being upset,
+I was warmed.
+It was so nice to see
+the characters work through
+their own problems
+surrounded by simple kindness.
+And when it was over,
+I was left wanting
+to move our world
+closer to that one.
+.
+.Pp
+That's what I love about this kind of fiction.
+It's why I love the books of Becky Chambers so much.
+They give me hope,
+and guidance.
+I count the
+.Em Murderbot Diaries
+series in this as well,
+which shows a sort of bad world,
+and an alternative.
+I think it's so important
+to see the good that exists
+and the good that could exist.
+Rather than something to fight against,
+these stories show something to fight for.
+A more compassionate world.
+.
+.Pp
+I know,
+one person can't change the world.
+But they can change their own world,
+and the worlds of those around them.
+And slowly,
+good things can spread.
+I'll strive to be
+more patient,
+more understanding,
+more compassionate,
+and I hope you will too.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Pp
+I can't help but worry,
+when I write something like this,
+that someone I know will read it
+and think that I'm lying
+because I've hurt them.
+If that's the case,
+I am sorry,
+and I promise
+I am trying to do better.
diff --git a/www/text.causal.agency/037-care.7 b/www/text.causal.agency/037-care.7
new file mode 100644
index 00000000..052a4727
--- /dev/null
+++ b/www/text.causal.agency/037-care.7
@@ -0,0 +1,167 @@
+.Dd April  3, 2022
+.Dt CARE 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Care
+.Nd trans stuff in Montreal
+.
+.Sh DESCRIPTION
+This kind of info
+is frustratingly hard to find
+even from support orgs
+and the like.
+I think it's unlikely
+that anyone in my blog's audience
+is also someone who needs this info,
+and my blog isn't easy to find either,
+but I want to at least
+make it available somewhere.
+Really this is just like
+the posts where I figure out
+how to do something with a computer
+then I write it down.
+.
+.Pp
+Prices obviously change,
+by which I mean they inevitably go up,
+but I'm gonna give the amounts I paid
+in 2021\(en2022.
+Also if you want more details
+about any of this
+please email me.
+I will be happy to tell you all about it.
+.
+.Ss Medication
+I get HRT through
+Dr. Gabrielle Landry
+at La Clinique A,
+which is a private clinic.
+I've done everything over the phone.
+After the first consultation,
+I signed an informed consent form
+and had a prescription the next day,
+which I could start
+after I got an initial blood test.
+The information I found
+said to contact a specific person
+at the clinic with a direct phone number,
+which is what I did.
+Email me if you want that number.
+.
+.Pp
+I paid $300 for the first consult,
+$195 for the first followup,
+and $75 for further followups.
+I think annual appointments
+are more expensive
+than the followups.
+I've been getting blood tests done at a CLSC,
+which is free.
+On the public drug insurance plan,
+I paid $30-$35
+for my prescriptions
+as my dosage increased.
+I have private insurance now
+that entirely covers prescriptions,
+so I'm not sure what I'd be paying
+for my current prescription
+on the public plan.
+.
+.Ss Hair removal
+I tried laser hair removal,
+for longer than I should have.
+It was a waste of time and money.
+Do not believe any arguments about
+its convenience over electrolysis.
+.
+.Pp
+I've started getting electrolysis done
+with Dimi.
+Again,
+feel free to email me for contact info.
+He is very good and can do long sessions.
+I really don't find it very painful,
+which I think is partly my own pain tolerance
+and partly good equipment and skill.
+I've also found that taking acetaminophen beforehand
+and dressing warmly to keep my body relaxed help.
+I've paid $85 for hour-long sessions
+and $160 for two-hour sessions.
+I'm still early in treatment,
+but I'm really happy with the results so far!
+.
+.Ss Sex & name change
+The form for this is
+.Do
+Application to Change the Sex Designation
+of a Person 18 Years of Age and Over
+.Dc
+from the
+.Em Directeur de l'\('etat civil .
+It's self-ID,
+but you have to get it signed by
+someone you know
+and a commissioner for oaths.
+Julien at P10 is qualified for that
+and was super nice.
+We did it over Zoom.
+It's a free service,
+so I made a donation to P10.
+.
+.Pp
+I paid $144 to file mine
+but it's now FREE
+the first time you do it.
+Also $17 to mail it.
+Surprisingly,
+I got an acknowledgment letter
+.Po
+just saying they got it
+and would start looking at it
+.Dq shortly
+.Pc
+like a week and a half
+after I mailed the application.
+My cheque was cashed
+39 days after the date
+on the acknowledgment,
+and I got a
+.Dq favourable decision
+a week later.
+It takes another 30 days
+to get the certificate of change,
+after which you can
+order a new birth certificate
+and RAMQ will (slowly) send you a form
+to get a new card.
+In all it took about 4 months
+from when I mailed the application
+to having ID with my name on it.
+.
+.Ss Therapy
+I'm not seeking therapy
+for gender specifically,
+but I would like to find a good therapist
+that's aware of it.
+I'll update this
+if I find one.
+.
+.Ss Piercings
+Ok I know this isn't trans-specific
+but at least for me getting piercings
+was gender-affirming.
+Cuz I got nipple piercings lol.
+Anyway,
+I went to Mauve.
+They're super nice,
+really know what they're doing,
+and their website has lots of info.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Pp
+If somehow you did find this useful,
+I'd love for you to email me
+and let me know how things went for you.
diff --git a/www/text.causal.agency/038-agency.7 b/www/text.causal.agency/038-agency.7
new file mode 100644
index 00000000..f99a070b
--- /dev/null
+++ b/www/text.causal.agency/038-agency.7
@@ -0,0 +1,85 @@
+.Dd April 14, 2022
+.Dt AGENCY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Agency
+.Nd origin of a name
+.
+.Sh DESCRIPTION
+When I registered this domain name,
+it was aspirational.
+Intentionally so.
+I wanted a new domain
+for a new identity,
+and I was thinking about personhood.
+That's what causal agency means.
+.
+.Pp
+It really was aspirational
+for me at the time.
+I spent a lot of time
+wishing I could be a person,
+because I didn't feel like one.
+I didn't feel real,
+like everyone else was.
+I didn't have any power
+over my own life.
+Things just happened to me,
+and I watched.
+There wasn't really a
+.Dq me
+there.
+The world was something that happened
+but that I couldn't interact with.
+I felt like that
+for most of my life.
+.
+.Pp
+But at some point
+I decided that,
+even if I wasn't now,
+one day I hoped to be an actual real life person.
+Like most programmers
+I am dreadful at naming things,
+so I didn't come up
+with this clever domain name
+myself.
+I typed
+.Dq person
+into some thesaurus,
+and it gave back
+.Dq causal agent ,
+and I realized
+agency is a TLD now.
+.
+.Pp
+Maybe it's a little dramatic
+to label myself with the thing
+I didn't think I had.
+But who knows,
+maybe it helped.
+Because it took a few years,
+but I did become a person.
+I feel real now.
+I can change my own life
+and the world around me.
+I have causal agency.
+.
+.Pp
+I am really proud of this domain name.
+I'm proud to put it on everything I make.
+Every instance of it
+is a reminder
+that I did what I set out to do,
+and that I'm still doing it.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.
+.Pp
+If anything in this post resonates with you,
+I want you to know that,
+whatever you think you can't do,
+it is possible,
+and you'll get there one day.
diff --git a/www/text.causal.agency/039-apologies.7 b/www/text.causal.agency/039-apologies.7
new file mode 100644
index 00000000..1b15076a
--- /dev/null
+++ b/www/text.causal.agency/039-apologies.7
@@ -0,0 +1,81 @@
+.Dd September 19, 2022
+.Dt APOLOGIES 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Apologies
+.Nd making them
+.
+.Sh DESCRIPTION
+Apologies are very important to me.
+Unfortunately
+I've only recently realized
+how valuable they are.
+I've tried to think about
+what makes a good apology,
+since it's not something
+I was ever taught.
+This is the advice
+I came up with for myself,
+on how to apologize.
+.
+.Bl -enum
+.It
+Make the apology.
+This is the most important part.
+If you feel guilty
+for something you've done,
+or think you might have hurt someone,
+apologize.
+Even if they don't need an apology,
+saying sorry won't hurt.
+And start with that.
+Literally say
+.Dq I'm sorry .
+Sometimes people forget that.
+.Pp
+On the other side,
+if you've been hurt by someone,
+and you trust them,
+let them know.
+Give them a chance to apologize.
+People don't always realize
+they've made a mistake.
+.
+.It
+Explain what you did wrong.
+I think it's important
+for the other person
+to know you understand
+how you've messed up.
+Really think about this!
+It's what will help you learn.
+If you know you've hurt someone
+but you're not sure why,
+you can try asking them.
+Take their answer seriously.
+.
+.It
+Don't make excuses.
+Do not talk about yourself.
+Don't even mention
+how you were feeling stressed that day,
+or whatever.
+It's not relevant.
+We all make mistakes,
+we all have bad days.
+.
+.It
+Commit to doing better.
+Try to learn from your mistakes.
+Say it won't happen again.
+Literally say
+.Dq I won't do that again .
+And then try your hardest to make that true.
+An apology is a commitment,
+not something you're done with
+once you've said it.
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/040-sound-memory.7 b/www/text.causal.agency/040-sound-memory.7
new file mode 100644
index 00000000..c995de08
--- /dev/null
+++ b/www/text.causal.agency/040-sound-memory.7
@@ -0,0 +1,165 @@
+.Dd November 14, 2022
+.Dt SOUND-MEMORY 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm Sound Memory
+.Nd associations
+.
+.Sh DESCRIPTION
+.Ss Talking Heads \(em "Remain In Light"
+The first time I gave this album a serious listen
+was when I was going for several-hour walks
+at 4 in the morning in,
+I think,
+fall 2020.
+I would stay up all night,
+go out walking at 4am
+for a couple hours,
+come home,
+eat
+.Dq breakfast
+and go to sleep.
+I listened to this album
+walking on completely empty
+big city streets
+in the dark.
+.
+.Ss Buffy Sainte-Marie \(em Up Where We Belong
+I started listening to this album
+after hearing it many mornings
+walking into the cafe on my block
+back in 2019.
+I could tell Vincent was working
+if I heard this when I opened the door.
+.
+.Ss Molasses \(em Trilogie: Toil & Peaceful Life
+I listened to this when I had 8am classes
+in CEGEP.
+In particular my first semester philosophy course,
+which was in the forum.
+I usually got there even earlier
+because of how the bus schedules worked out.
+There was another girl in my class,
+who I always sat next to,
+who also got there early,
+but we never spoke outside of class.
+.
+.Ss Arcade Fire \(em Funeral
+This album just feels like walking outside
+in fresh snow in early winter,
+you know?
+.
+.Ss Molasses \(em Trouble at Jinx Hotel
+I listened to this when I was looking for an apartment.
+I specifically remember listening to it
+walking down Clark toward my new place
+to pick up my keys.
+.
+.Ss Arcade Fire \(em Neon Bible
+The song
+.Dq "No Cars Go"
+is strongly associated for me
+with my earliest gender feelings.
+It's how I date when I first
+started to feel like something was wrong.
+The Suburbs was released in 2010,
+so I was probably listening to Neon Bible
+in 2011.
+Ten years between that
+and coming out.
+.
+.Ss "Do Make Say Think" \(em "You You're a History In Rust"
+I remember hearing
+.Dq "A Tender History In Rust"
+for the first time
+at the office of my first job.
+Me and my coworkers stayed late,
+probably on a Friday night,
+drinking free tech startup booze.
+.
+.Ss mewithoutYou \(em It's All Crazy! It's All False! It's All a Dream! It's Alright
+I exclusively listened to this album
+on a high school trip to Europe.
+Every morning when we got on the bus,
+I heard
+.Dq Every Thought a Thought of You
+and every night before bed
+I listened to
+.Dq The King Beetle on a Coconut Estate .
+.
+.Ss Arcade Fire \(em The Suburbs
+I listened to this album a tonne
+when I was playing
+Minecraft and Urban Terror
+with my online friends
+while I was in high school.
+In particular I remember
+a backyard shed World of Padman map
+and the apartments Minecraft world.
+.
+.Ss Arcade Fire \(em Reflektor
+I associate
+.Dq Afterlife
+with the walk between Laurier metro
+and my first job,
+in the winter.
+Must've just been how the timing worked out
+with my commute at the time.
+.
+.Ss Swans \(em To Be Kind
+I listened to this on one of my playthroughs
+of Half-Life 2.
+In particular I associate
+.Dq Bring the Sun / Toussaint L'Ouverture
+with the Water Hazard chapter.
+.
+.Ss Wrekmeister Harmonies \(em Light Falls
+For a while I put this on whenever I
+left my apartment to go somewhere
+and it was already dark,
+so probably winter.
+.
+.Ss St. Vincent \(em MASSEDUCTION
+This,
+along with the next one,
+I think were all I listened to
+on a family vacation
+to Quebec City and New Brunswick
+some years ago.
+.
+.Ss SOPHIE \(em Oil of Every Pearl's Un-Insides
+Many hours on the road
+on that family vacation.
+Two albums on repeat.
+.
+.Ss Julia Holter \(em Aviary
+This is another album
+I listened to when I was taking
+walks at 4am.
+I wasn't in a good place.
+Yet.
+.
+.Ss Beep Test \(em Laugh Track
+A tape from the first act
+at one of my favourite shows
+I've ever been to,
+at La Sotterenea
+in Suoni 2019.
+I wish I had been out already.
+.
+.Ss The Armed \(em Only Love
+The third of the albums I listened to
+on those dark walks.
+I listened to it loud,
+this album's mixing needs it.
+.
+.Ss Eliza Kavtion \(em The Rez That Summer
+A favourite local artist.
+I remember vividly the first time
+I heard her play,
+opening for Wrekmeister Harmonies
+at La Vitrola in 2018.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/041-albums-2022.7 b/www/text.causal.agency/041-albums-2022.7
new file mode 100644
index 00000000..48bd3c3d
--- /dev/null
+++ b/www/text.causal.agency/041-albums-2022.7
@@ -0,0 +1,185 @@
+.Dd December 21, 2022
+.Dt ALBUMS-2022 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm albums 2022
+.Nd review
+.
+.Sh DESCRIPTION
+it's the year-end review
+of albums I listened to.
+same as last year,
+I added any albums I got into
+this year to a playlist.
+I've actually done that
+every year since 2018.
+maybe I'll review
+those old playlists some time.
+.
+.Ss ZHAOZE \(em SUMMER INSECTS TALK ABOUT ICE (2021)
+it's a five-and-a-half-minute album!
+you can loop it however long you want.
+it's really lovely.
+.Pp
+favourite track:
+ON HORSEBACK, TO FARAWAY
+.
+.Ss KATE BUSH \(em HOUNDS OF LOVE (1985)
+first of all I do not watch that one show.
+I've known that track for a while actually.
+I mean I probably first heard the CHROMATICS cover.
+but anyway,
+I think someone mentioned this album
+on IRC at just the right time
+and I put it on.
+the second half really shines tbh.
+love a concept album.
+.Pp
+favourite tracks:
+RUNNING UP THAT HILL,
+HOUNDS OF LOVE,
+AND DREAM OF SHEEP,
+WATCHING YOU WITHOUT ME,
+THE MORNING FOG.
+.
+.Ss GODSPEED YOU! BLACK EMPEROR \(em ALL LIGHTS FUCKED ON THE HAIRY AMP DROOLING (1994)
+didn't expect to hear this probably ever.
+still wild that it finally got uploaded.
+and to be honest I'm a little mad
+that it's actually good.
+like yeah it's not a godspeed album
+but it holds up as a tape on its own.
+it's the kind of shit I listen to.
+also can't believe some people
+still thought it was fake.
+like have you not heard
+any other efrim menuck projects?
+.Pp
+favourite tracks:
+$13.13,
+DIMINISHING SHINE,
+DADMOMDADDY,
+333 FRAMES PER SECOND,
+ALL ANGELS GONE.
+.
+.Ss BLACK DRESSES \(em FORGET YOUR OWN FACE (2022)
+woops I think I only listened to this like twice.
+will need to revisit it later for sure.
+I'll like it.
+.
+.Ss BACKXWASH \(em I LIE HERE BURIED WITH MY RINGS AND MY DRESSES (2021)
+only got into this album
+after hearing it live this summer.
+was the first show I went to in years
+and it was really fucking good.
+gotta listen to this shit loud.
+sampling godspeed for a beat fucks.
+honestly back to back bangers.
+.Pp
+favourite tracks:
+I LIE HERE BURIED WITH MY RINGS AND MY DRESSES,
+TERROR PACKETS,
+SONG OF SINNERS,
+BURN TO ASHES.
+.
+.Ss PHILIP GLASS ENSEMBLE \(em EINSTEIN ON THE BEACH (1979)
+actually just the knee plays
+because I can't be bothered
+listening to all of it.
+and I'm embarrassed by how much
+I enjoy this avant-garde bullshit.
+like ok just sing repeating numbers at me
+and my brain is happy.
+what is this?
+my kink?
+anyway I also have kind of an obsession
+with the
+.Dq story of love
+in knee 5.
+I fucking hate it.
+but it's delivered so well.
+and that violin though!
+.Pp
+favourite tracks:
+KNEE 1,
+KNEE 5.
+.
+.Ss KANYE WEST \(em YEEZUS (2013)
+ok look I listened to this
+before recent events.
+what the fuck.
+it's a really good album though?
+pretty sure I listened to it
+because bound 2 kept getting in my head,
+because of that minecraft parody parody
+wayne did ages ago.
+.Pp
+favourite tracks:
+BLACK SKINHEAD,
+HOLD MY LIQUOR,
+BLOOD ON THE LEAVES,
+BOUND 2.
+.
+.Ss FLYING RACCOON SUIT \(em AFTERGLOW (2021)
+I've listened to the whole album
+a few times
+but I'm mostly just here
+for the title track.
+this also happened to be
+dropped in IRC at just the right time.
+good ska-punk-type shit.
+and I like lisps ok.
+.Pp
+favourite track:
+AFTERGLOW.
+.
+.Ss RAMSHACKLE GLORY \(em LIVE THE DREAM (2011)
+one of those albums
+I don't know why I took so long
+to get to.
+I've been listening to johnny hobo
+since I was like in high school.
+ramshackle is a little more hopeful
+and I love that.
+your heart is a muscle the size of your fist.
+keep on loving.
+keep on fighting.
+.Pp
+favourite tracks:
+WE ARE ALL COMPOST IN TRAINING,
+NEVER COMING HOME,
+YOUR HEART IS A MUSCLE THE SIZE OF YOUR FIST.
+.
+.Ss LES RALLIZES D\('ENUD\('ES \(em THE OZ TAPES (2022)
+a pleasant surprise in someone's playlist.
+lately I've been listening to this
+in the metro to or from electrolysis.
+it's good listening for that.
+bold to have two versions
+of the same 24-minute song
+on the same release.
+.Pp
+favourite tracks:
+A SHADOW ON OUR JOY,
+THE LAST ONE_1970 (ver.2).
+.
+.Ss LINGUA IGNOTA \(em SINNER GET READY (2021)
+another I'm only getting into
+after hearing it live.
+just last sunday actually.
+was a good show.
+people will go wild
+to hear a cover live for real.
+.Pp
+favourite tracks:
+I WHO BEND THE TALL GRASSES,
+PENNSYLVANIA FURNACE,
+PERPETUAL FLAME OF CENTRALIA.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I started writing this
+before I saw LINGUA IGNOTA.
+good thing I waited.
diff --git a/www/text.causal.agency/042-comfort-music.7 b/www/text.causal.agency/042-comfort-music.7
new file mode 100644
index 00000000..445e04c3
--- /dev/null
+++ b/www/text.causal.agency/042-comfort-music.7
@@ -0,0 +1,62 @@
+.Dd February 23, 2024
+.Dt COMFORT-MUSIC 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm comfort music
+.Nd feel better
+.
+.Sh DESCRIPTION
+it's been a while.
+and I'm on almost no sleep
+and haven't eaten a real meal
+since noon.
+which is a state I've written
+at least a couple posts in before,
+so what better time
+to return to what has apparently
+become this blog's format:
+lists of some music I like.
+.
+.Pp
+this is a list of music that comforts me.
+.
+.Bl -bullet
+.It
+knee play 5, from einstein on the beach.
+I like the organ and the counting and the cadence of the story.
+.It
+low \(em words.
+and I'm tired.
+.It
+godspeed you! black emperor \(em storm.
+this is like my original comfort music.
+been listening to it since I was teenage.
+the grooves are worn deep in my mind.
+.It
+set fire to flames \(em love song for 15 ontario (w/ singing police car).
+I like how it ends.
+.It
+va, from the beginner's guide.
+I think that's the whole point.
+though maybe it's too sad
+to be truly comforting.
+.It
+undertale, from undertale.
+what can I say?
+.It
+wrekmeister harmonies \(em covered in blood from invisible wounds.
+I find quite a bit of the album comforting really.
+I'm picking this one because I like the cadence
+of the lyrics.
+.It
+lingua ignota \(em pennsylvania furnace and perpetual flame of centralia.
+these are really my go to in recent times.
+I like waiting for the next line.
+.El
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
+.Pp
+I don't think I've said anything
+very interesting here.
diff --git a/www/text.causal.agency/043-little-blessings.7 b/www/text.causal.agency/043-little-blessings.7
new file mode 100644
index 00000000..957c6289
--- /dev/null
+++ b/www/text.causal.agency/043-little-blessings.7
@@ -0,0 +1,78 @@
+.Dd March 24, 2024
+.Dt LITTLE-BLESSINGS 7
+.Os "Causal Agency"
+.
+.Sh NAME
+.Nm little blessings
+.Nd life's
+.
+.Sh DESCRIPTION
+today I went out to go around.
+run some errands and do some shopping.
+along the way I was given
+several of life's little blessings.
+.
+.Pp
+while walking on ste-cath
+between berri and complexe desjardins,
+there was a somewhat disheveled man
+walking in the same direction and singing.
+he had a beautiful voice.
+he was singing a sad song in french,
+and he sung it well and enunciated every word.
+.
+.Pp
+in the mcdonald's at complexe desjardins,
+while waiting for my order,
+there were what appeared to be
+a teenager and her younger brother,
+who must have been
+looking at the display of
+current happy meal toys.
+the teenager was playing smash or pass,
+to the amusement of the younger one.
+they got ice cream
+and ate it across the room from me downstairs.
+.
+.Pp
+later,
+taking the 24 home from atwater
+carrying my new vacuum cleaner,
+the bus got lost.
+I think the driver missed the stop
+and tried to compensate
+by turning north onto peel
+and stopping there.
+but then he had to keep going up peel.
+he turned right onto docteur-penfield,
+which just brings you further up the mountain.
+when it met des pins,
+he turned left and pulled over,
+asking for guidance over the radio.
+we got moving again,
+back towards peel.
+that's how I ended up
+on a 24
+.Dq sherbrooke
+east,
+facing west on des pins.
+it was actually quite scenic.
+and amusing.
+I was in no rush.
+.
+.Pp
+after getting back onto sherbrooke,
+the bus had to take another detour,
+this one planned.
+so my ride on the 24,
+which normally only drives on sherbrooke,
+ended up going on peel,
+docteur-penfield,
+des pins,
+de bleury,
+ren\('e-l\('evesque
+and saint-laurent.
+it was a very exciting bus trip.
+.
+.Sh AUTHORS
+.An june Aq Mt june@causal.agency
diff --git a/www/text.causal.agency/Makefile b/www/text.causal.agency/Makefile
index d44e3f77..a8683a20 100644
--- a/www/text.causal.agency/Makefile
+++ b/www/text.causal.agency/Makefile
@@ -1,7 +1,4 @@
-WEBROOT ?= /usr/local/www/text.causal.agency
-LIBEXEC ?= /usr/local/libexec
-
-CFLAGS += -Wall -Wextra
+WEBROOT = /var/www/text.causal.agency
 
 TXTS += 001-make.txt
 TXTS += 002-writing-mdoc.txt
@@ -21,23 +18,49 @@ TXTS += 015-reusing-tags.txt
 TXTS += 016-using-openbsd.txt
 TXTS += 017-unpasswords.txt
 TXTS += 018-operating-systems.txt
+TXTS += 019-mailing-list.txt
+TXTS += 020-c-style.txt
+TXTS += 021-time-machine.txt
+TXTS += 022-swans-are-dead.txt
+TXTS += 023-sparse-checkout.txt
+TXTS += 024-seprintf.txt
+TXTS += 025-v6-pwd.txt
+TXTS += 026-git-comment.txt
+TXTS += 027-openbsd-linode.txt
+TXTS += 028-names.txt
+TXTS += 029-topics.txt
+TXTS += 030-discs.txt
+TXTS += 031-books-2021.txt
+TXTS += 032-albums-2021.txt
+TXTS += 033-jorts.txt
+TXTS += 034-voices.txt
+TXTS += 035-addendum-2021.txt
+TXTS += 036-compassion.txt
+TXTS += 037-care.txt
+TXTS += 038-agency.txt
+TXTS += 039-apologies.txt
+TXTS += 040-sound-memory.txt
+TXTS += 041-albums-2022.txt
+TXTS += 042-comfort-music.txt
+TXTS += 043-little-blessings.txt
 
-all: ${TXTS}
+all: colb ${TXTS}
 
-.SUFFIXES: .7 .txt
+.SUFFIXES: .7 .fmt .txt
 
 .7.txt:
-	mandoc -T utf8 $< | col -bx > $@
+	mandoc -T utf8 $< | ./colb > $@
+	touch -m -r $< $@
+
+.fmt.txt:
+	fmt $< | sed '1,/^$$/d' > $@
+	touch -m -r $< $@
 
-feed.atom: feed.sh ${TXTS}
+feed.atom: feed.sh colb ${TXTS}
 	sh feed.sh > feed.atom
 
 clean:
-	rm -f ${TXTS} feed.atom igp
+	rm -f colb ${TXTS} feed.atom
 
-install: ${TXTS} feed.atom
+install: colb ${TXTS} feed.atom
 	install -p -m 644 ${TXTS} feed.atom ${WEBROOT}
-
-install-igp: igp
-	install igp ${LIBEXEC}
-	install -p -m 644 igp.c ${WEBROOT}
diff --git a/www/text.causal.agency/colb.c b/www/text.causal.agency/colb.c
new file mode 100644
index 00000000..5faabc3a
--- /dev/null
+++ b/www/text.causal.agency/colb.c
@@ -0,0 +1,16 @@
+#include <locale.h>
+#include <stdio.h>
+#include <wchar.h>
+int main(void) {
+	setlocale(LC_CTYPE, "en_US.UTF-8");
+	wint_t next, prev = WEOF;
+	while (WEOF != (next = getwchar())) {
+		if (next == L'\b') {
+			prev = WEOF;
+		} else {
+			if (prev != WEOF) putwchar(prev);
+			prev = next;
+		}
+	}
+	if (prev != WEOF) putwchar(prev);
+}
diff --git a/www/text.causal.agency/feed.sh b/www/text.causal.agency/feed.sh
index f45bd326..71bbf662 100644
--- a/www/text.causal.agency/feed.sh
+++ b/www/text.causal.agency/feed.sh
@@ -23,10 +23,13 @@ encode() {
 	' "$@"
 }
 
-for txt in *.txt; do
+set -- *.txt
+shift $(( $# - 20 ))
+for txt; do
 	entry="${txt%.txt}.7"
+	test -f "$entry" || entry="${txt%.txt}.fmt"
 	date=$(grep '^[.]Dd' "$entry" | cut -c 5-)
-	title=$(grep '^[.]Nm' "$entry" | cut -c 5- | encode)
+	title=$(grep -m 1 '^[.]Nm' "$entry" | cut -c 5- | encode)
 	summary=$(grep '^[.]Nd' "$entry" | cut -c 5- | encode)
 	published=$(date -ju -f '%B %d, %Y %T' "${date} 00:00:00" '+%FT%TZ')
 	mtime=$(stat -f '%m' "$entry")
diff --git a/www/text.causal.agency/igp.1 b/www/text.causal.agency/igp.1
deleted file mode 100644
index ccfaeaa6..00000000
--- a/www/text.causal.agency/igp.1
+++ /dev/null
@@ -1,49 +0,0 @@
-.Dd January 14, 2021
-.Dt IGP 1
-.Os
-.
-.Sh NAME
-.Nm igp
-.Nd insane gopher posse
-.
-.Sh SYNOPSIS
-.Nm
-.Op Fl h Ar host
-.Op Fl p Ar port
-.Ar directory
-.
-.Sh DESCRIPTION
-The
-.Nm
-utility is a simple
-Internet Gopher server
-which can be started by
-.Xr inetd 8 .
-It serves directory listings
-and files under
-.Ar directory .
-.
-.Pp
-The arguments are as follows:
-.Bl -tag -width Ds
-.It Fl h Ar host
-Set the host used in directory listings.
-The default is the local host name.
-.It Fl p Ar port
-Set the port used in directory listeings.
-The default is port 70.
-.El
-.
-.Sh STANDARDS
-.Rs
-.%A B. Alberti
-.%A F. Anklesaria
-.%A D. Johnson
-.%A P. Lindner
-.%A M. McCahill
-.%A D. Torrey
-.%T The Internet Gopher Protocol
-.%I IETF
-.%R RFC 1436
-.%D March 1993
-.Re
diff --git a/www/text.causal.agency/igp.c b/www/text.causal.agency/igp.c
deleted file mode 100644
index d7db2b28..00000000
--- a/www/text.causal.agency/igp.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sysexits.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifdef __FreeBSD__
-#include <sys/capsicum.h>
-#endif
-
-static int compar(const void *_a, const void *_b) {
-	const struct dirent *a = _a;
-	const struct dirent *b = _b;
-	if (a->d_type != b->d_type) {
-		return (a->d_type > b->d_type) - (a->d_type < b->d_type);
-	}
-	return strcmp(a->d_name, b->d_name);
-}
-
-int main(int argc, char *argv[]) {
-	int error;
-	const char *host = NULL;
-	const char *port = "70";
-	for (int opt; 0 < (opt = getopt(argc, argv, "h:p:"));) {
-		switch (opt) {
-			break; case 'h': host = optarg;
-			break; case 'p': port = optarg;
-			break; default:  return EX_USAGE;
-		}
-	}
-	if (optind == argc) return EX_USAGE;
-	if (!host) {
-		static char buf[256];
-		error = gethostname(buf, sizeof(buf));
-		if (error) abort();
-		host = buf;
-	}
-
-	const char *path = argv[optind];
-	int root = open(path, O_RDONLY | O_DIRECTORY);
-	if (root < 0) err(EX_NOINPUT, "/");
-
-#ifdef __FreeBSD__
-	cap_rights_t cap;
-	error = cap_enter()
-		|| cap_rights_limit(STDIN_FILENO, cap_rights_init(&cap, CAP_READ))
-		|| cap_rights_limit(STDOUT_FILENO, cap_rights_init(&cap, CAP_WRITE))
-		|| cap_rights_limit(STDERR_FILENO, &cap)
-		|| cap_rights_limit(
-			root, cap_rights_init(&cap, CAP_PREAD, CAP_FSTATAT, CAP_FSTATFS)
-		);
-	if (error) abort();
-#else
-#warning "This is completely insecure without capsicum(4)!"
-#endif
-
-	char buf[1024];
-	if (!fgets(buf, sizeof(buf), stdin)) return EX_PROTOCOL;
-	char *ptr = buf;
-	char *sel = strsep(&ptr, "\t\r\n");
-	if (sel[0] == '/') sel++;
-
-	int fd = (sel[0] ? openat(root, sel, O_RDONLY) : root);
-	if (fd < 0) err(EX_NOINPUT, "%s", sel);
-
-	struct stat stat;
-	error = fstat(fd, &stat);
-	if (error) err(EX_IOERR, "%s", sel);
-	if (!(stat.st_mode & (S_IFREG | S_IFDIR))) {
-		errx(EX_NOINPUT, "%s: Not a file or directory", sel);
-	}
-
-	if (stat.st_mode & S_IFREG) {
-#ifdef __FreeBSD__
-		error = sendfile(fd, STDOUT_FILENO, 0, 0, NULL, NULL, 0);
-		if (!error) return EX_OK;
-#endif
-		char buf[4096];
-		for (ssize_t len; 0 < (len = read(fd, buf, sizeof(buf)));) {
-			fwrite(buf, len, 1, stdout);
-		}
-		return EX_OK;
-	}
-
-	DIR *dir = fdopendir(fd);
-	if (!dir) err(EX_IOERR, "%s", sel);
-
-	size_t len = 0;
-	size_t width = 0;
-	static struct dirent ents[4096];
-	for (struct dirent *ent; len < 4096 && (ent = readdir(dir));) {
-		if (ent->d_name[0] == '.') continue;
-		if (ent->d_type != DT_REG && ent->d_type != DT_DIR) continue;
-		if (ent->d_namlen > width) width = ent->d_namlen;
-		ents[len++] = *ent;
-	}
-
-	qsort(ents, len, sizeof(ents[0]), compar);
-	for (size_t i = 0; i < len; ++i) {
-		char mtime[26] = "";
-		if (ents[i].d_type == DT_REG) {
-			error = fstatat(fd, ents[i].d_name, &stat, 0);
-			if (error) err(EX_IOERR, "%s/%s", sel, ents[i].d_name);
-			ctime_r(&stat.st_mtime, mtime);
-			mtime[24] = '\0';
-		}
-		printf(
-			"%c%-*s  %s\t%s%s%s\t%s\t%s\r\n",
-			(ents[i].d_type == DT_DIR ? '1' : '0'),
-			(int)width, ents[i].d_name, mtime,
-			sel, (sel[0] ? "/" : ""), ents[i].d_name, host, port
-		);
-	}
-
-	printf("i-- \t\t%s\t%s\r\n", host, port);
-	printf("0Served by IGP (AGPLv3)\tigp.c\ttext.causal.agency\t70\r\n");
-	printf(".\r\n");
-}