Categories
Uncategorized

Thunderbird Fonts & Colors

Saving this as a post here for my future reference.

The key to getting the “default” fonts to apply to plain-text (and all mails with the allow messages to use other fonts setting off) is also changing the fonts for Other Writing Systems.

Why is it like this? Who knows!

This answer brought to you by a semi-not-recent reddit thread.

[–]Yukness   2 points 3 years ago

The display fonts are set in the Advanced section of Language & Appearance in Options. Did you set the fonts with Fonts for-Other Writing Systems? If not, that might explain why Unicode plain text in received mail doesn’t look like what you specified.

Shoutout to /u/Yukness for this one!

Categories
Uncategorized

Matchplay Posting

I post the results from the pinball tournaments I run on our site, which usually involves copying a previous post and updating all the info by hand. I took some time this week to put together a script that reads the results from matchplay and builds a post with all the right info.

It’s not enough to publish as a git repo or anything and I just wanted to have a place to keep it so it’s here for now.

ben@odin ~> cat ~/bin/tcpp
#!/bin/sh

playersjson=$(curl -s "https://app.matchplay.events/api/tournaments/$1?includePlayers=1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json")

finalsId=$(printf %s "$playersjson" | jq -r ".data.linkedTournamentId")
standingsids=$(curl -s "https://app.matchplay.events/api/tournaments/$finalsId/standings" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    | jq -r '.[] | .playerId')

d=$(printf %s "$playersjson" | jq -r ".data.startUtc")
shortdate=$(date --date "$d" +"%B %-d")
ifpa=$(curl -s 'https://api.ifpapinball.com/v1/calendar/search?api_key=GET_YOUR_OWN&address=Traverse%20City&m=3')
ifpatourney=$(printf %s "$ifpa" | jq -r ".calendar[] | select(.start_date == \"$(date --date "$d" +"%F")\").tournament_id")

author=1
excerpt="Results for $shortdate tourney"

case $(date --date "$d" +"%a") in
    Thu)
        categories="58,54"
        ;;
    Mon)
        categories="58,52"
        ;;
    Sun)
        categories="58,51"
        author=8
        excerpt="Results for the $shortdate Belles tourney"
        ;;
    *)
        categories="4"
        ;;
esac

player1=$(printf %s "$playersjson" | jq -r ".data.players | .[] | select(.playerId == $(printf %s "$standingsids" | head -n1 | tail -n1)).name")
player2=$(printf %s "$playersjson" | jq -r ".data.players | .[] | select(.playerId == $(printf %s "$standingsids" | head -n2 | tail -n1)).name")
player3=$(printf %s "$playersjson" | jq -r ".data.players | .[] | select(.playerId == $(printf %s "$standingsids" | head -n3 | tail -n1)).name")
player4=$(printf %s "$playersjson" | jq -r ".data.players | .[] | select(.playerId == $(printf %s "$standingsids" | head -n4 | tail -n1)).name")

printf "https://tcpinball.org/wp-admin/post.php?action=edit&post="
sudo -Hu www-data wp --path=/var/www/tcpinball.org \
    post create \
    --post_author="$author" \
    --post_category="$categories" \
    --post_title="$shortdate Results" \
    --post_excerpt="$excerpt" \
    --post_status=draft \
    --porcelain \
    --post_content="<!-- wp:paragraph -->
<p>Format: Group Match Play</p>
<!-- /wp:paragraph -->

<!-- wp:table {\"hasFixedLayout\":false} -->
<figure class=\"wp-block-table\"><table><thead><tr><th>Place</th><th>Player</th></tr></thead><tbody><tr><td>1st</td><td>$player1</td></tr><tr><td>2nd</td><td>$player2</td></tr><tr><td>3rd</td><td>$player3</td></tr><tr><td>4th</td><td>$player4</td></tr></tbody></table></figure>
<!-- /wp:table -->

<!-- wp:paragraph -->
<p>See the full results on <a href=\"https://app.matchplay.events/tournaments/$1\">Match Play</a> - <a href=\"https://app.matchplay.events/tournaments/$finalsId\">Finals</a> (<a href=\"https://www.ifpapinball.com/tournaments/view.php?t=$ifpatourney\">IFPA</a>).</p>
<!-- /wp:paragraph -->
"

Results table was built by dumping the content from a previous post.

Categories
Uncategorized

Why not matrix?

Archived from https://telegra.ph/why-not-matrix-08-07 March 22, 2024
Additional links here: https://benharri.org/matrix-sucks/


haru august 7, 2023

at this point it seems like most of the tech community is familiar with matrix, the “open network for decentralized communication”. lots of projects and communities have migrated from a host of other platforms, including irc, discord and slack with the promise that their new spaces will be free forever. i first discovered matrix in 2021 and have dedicated a lot of time trying to understand exactly how it works, as well as trawling through github issues to try and understand whether we should consider matrix safe. i’ve also evaluated conduit as well as dendrite and tried a number of clients.

first of all, a quick primer on what matrix actually is. even though element market matrix as the foundation of a chat app, it’s far more complicated than that. under the hood, matrix is actually a distributed partially-replicated graph database. a matrix “room” is actually a directed acyclic graph (“dag”) made up of events which can contain things like messages, user membership states and bans and other things. servers participating in a room are eventually consistent, that is, they should replicate enough state so that everyone sees roughly the same thing.

in order to “send” something into a room, a server that is participating in a room can also try to append events onto the graph. since every participating server needs to be able to verify what they are receiving from another server actually makes sense, a set of auth rules are defined in the spec to determine when to allow or to drop certain events. events are cryptographically signed by the sending server so that other servers can check those origins too. every server participating in a room needs to perform these checks, in the hope that if all servers agree, that newly created events will be happily accepted by all participating servers.

however, over time, i have collected together quite a list of issues that i consider to be either unsolved or dangerous. without further ado, here is my list of things to consider and reasons why you might not want to use matrix:

  1. the graph is append-only by design and events that get sent into the room can not ever be guaranteed to be deleted, so a matrix room can potentially end up accumulating history endlessly. for some applications this is desirable, but for chat and other applications it removes any possibility of deniability. deleting history is a very hard problem in matrix because dropping whole events can create gaps in the graph which servers either need to complete event auth or may struggle to skip over when backfilling history.
  2. if you do want to delete something, you can send a redaction event which asks other servers very nicely to delete the content of the event, but redactions are advisory and a badly behaving server participating in a room could simply ignore the redaction request, instead holding onto the entire history. it’s basically impossible to know at the point of asking for a deletion whether this has happened or not.
  3. however, servers that choose to ignore redactions, or fail to process them for some other reason, can leak supposedly-deleted data to other servers later on. if a new server joins the room for the first time or wants to backfill history, it can ask any server in the room for help doing so, including the badly-behaved server. nothing requires a server to return the redacted skeleton event, it can opt to return the full unredacted event if it wishes. in fact this might even happen purely by accident.
  4. certain events, like membership changes, bans or pretty much any event that exercises some control over another user can’t be deleted ever as they become woven into the “auth chain” of future events, and in order for a server to independently verify each event, they need to be able to prove how the event was allowed to happen, iteratively, back to the beginning of the room. every time a user joins, leaves, changes their name or avatar, gets kicked, banned, kicks or bans someone else or changes critical room settings, traces of these actions become burned into the room history permanently.
  5. as with most places on the internet, spam is inevitable, so another fun way to attack a room is just to join hundreds or thousands of bots to the room, making the room graph very complex and difficult to compute, which in turn means that servers waste lots of cpu time and clients have lots more work to do, especially in encrypted rooms. the only way to discard all of this spam complexity is to recreate the room.
  6. even room history is a best-effort endeavor. while the room graph itself provides some causal ordering, as some events need to follow other authenticating events, it’s exceptionally hard to linearize history if you don’t know the entire history of the room partially. tiebreaks used in the server also include fields like depth and the origin timestamp, which can both be forged. so unsurprisingly, different servers can see messages arriving in a different order to each other. most of the time there is nothing you can do about this.
  7. speaking of forging things, it is also somewhat possible to insert messages into history by crafting events in the graph that refer to older ancestor events and, as long as it looks vaguely plausible (like with a sane depth and/or timestamp value), other servers will accept these events without much question and users may not be able to tell the difference if these events eventually get backfilled onto their own server’s copy of the room history.
  8. another thing that is worth noting is that end-to-end encryption in matrix is completely optional. this is pretty much required as public rooms would be extremely heavy and probably completely unusable otherwise, but the only thing trying to ensure that your dms are encrypted are clients and even they don’t have to if they don’t want to. anything in a federated room that isn’t end-to-end encrypted can end up replicated in plain-text and also end up as part of a semi-permanent history.
  9. the end-to-end encryption is also annoyingly fragile as it depends on device list updates to be delivered between servers reliably. a failure to sync these correctly will result in broken encryption in rooms where others cannot decrypt your messages and this seems to be a constant source of encryption bugs.
  10. sometimes these device list updates updates also leak information about your device, like which matrix client you are using or which operating system/platform you are on. some homeservers seem to have started trying to filter this information recently, but as always, there’s no guarantees.
  11. the entire matrix api surface is http and json. for clients and client developers this makes things nice and simple, but the federation api also uses http and json and also tries to be cryptographically secure. for signing events and requests to work, matrix expects the json to be in canonical form, except the spec doesn’t actually define what the canonical json form is strictly, so there’s every possibility different implementations will end up generating different signatures for the same event.
  12. oh wait, that actually happened. turns out that matrix homeservers written in different languages have json interoperability issues, so you might just get random events or requests that fail signature checks between different types of matrix servers because no one knows exactly how to get it right. synapse, the flagship implementation, simply relies on python’s sort_keys and calls it a day, to hilarious effect, and even other matrix devs working for element haven’t been able to figure out how to match this behavior in other implementations like dendrite.
  13. those signature checks also rely on server signing keys which can be expired and replaced, except the signing key expiry is completely arbitrary so you can simply set an expiry date for your server signing key in the past and watch as new servers are now totally unwilling to authenticate any events from your server, resulting in split-brained rooms. feeding certain key expiry information to certain servers and not others might even make for a novel eclipse attack.
  14. split-brained rooms are actually a common occurrence too, as the distributed nature of matrix requires a kind of consensus algorithm (known as “state resolution”) in order to work out what to do when a server ends up with more than one conflicting set of state. the algorithm is not watertight and relies on some preconditions (like event signatures being accurately checked across servers) so when certain edge-cases occur in this algorithm, which they often do, a “state reset” can occur which can end up kicking users out of rooms or reverting state changes. at this point state resets are an on-going meme with no solution in sight.
  15. worse than that, state resets happen quite a bit more often when servers written in different languages interoperate because of the json interoperability issues, and it’s also possible to force them to happen in some cases by joining a room, making some power actions and then setting your signing key to expire before they happened. some servers will believe the events if they passed the signature check at the time, other servers will drop the events if they learn about the key expiry first, and then chaos ensues as servers try to request state snapshots from each other, eventually breaking the room to the point that those servers may never agree again!
  16. this has happened to amusing effect more than once, as room admins and moderators have lost their powers over public rooms many times due to state resets, leaving them unable to defend themselves and their communities against spam, harassment and other types of attacks. in some cases, the core matrix team have had to try to forcefully shut down rooms on their own instances and recreate them on more than one occasion, hoping people will rejoin. the big bad Matrix HQ room has been the subject of attacks multiple times now and so have other community rooms.
  17. speaking of shutting down rooms, you can’t actually force a room to be shut down across the federation. if there are three servers in a room and one of them wants to shut down the room, there’s no way to stop the other two servers from continuing along just as they were. this is good news if you care about internet freedom, sure. this is bad news if people start to abuse instances with unsavory rooms or content. in certain legislations this could present a serious problem.
  18. speaking of moderation, this is notoriously difficult too, as moderation relies entirely on the functioning of the event auth system and breaks down if state resets happen or if someone abuses their power. this has seemingly resulted in problems with trying to moderate or clean up attacked rooms before. these changes can be extremely hard to revert, especially if there are other servers participating in the room, as the room will eventually try to converge on what the state resolution algorithm thinks the best state is, which isn’t necessarily the state that you want.
  19. pretty much any user on your homeserver can upload media to your server’s media repository at pretty much any time, and media downloads are unauthenticated by default. as a result, you can often just generate a HTTP link to the media. someone might be using your media repository as their personal sharing box at your expense and you may not even realize.
  20. you can also ask someone else’s homeserver to replicate media by asking that homeserver to return a copy of it. based on the content ID and the origin server name in the URL, the homeserver will download the media from the origin and likely cache it locally in the process. that could end up being a fun denial-of-service.
  21. in addition to that, media uploads are unverified by default. if you are running your own matrix instance, there’s a good chance that nothing is scanning that media for unsavory content (like csam or viruses) unless you have also installed a separate content scanner. this is a separate package and is not a core part of the synapse distribution, nor any other homeserver that is available today.
  22. actually the eager replication of media could end up potentially being quite a massive headache, since all it takes is for one of your users to request media from an undesirable room for your homeserver to also serve up copies of it, at which point you could become liable for hosting copies of illegal media like csam or copyrighted material without necessarily knowing it.

i want to note that this list is not completely exhaustive and i may follow up with more items at a later date. however, i have found what i believe to be quite a number of reasons to be skeptical of matrix and i would struggle very much to want to recommend it to a new community or enterprise looking for a communication platform. most of the issues here i’ve ever witnessed first hand or have been following through a series of github issues on various matrix repositories, issues which the matrix devs seem to have been largely ignoring for literal years now.

Categories
Uncategorized

Uses

Haven’t written anything here this year yet. Here’s my uses page!

Hardware

My desktop is a custom build that I put together about a year ago (Jan 2023). Here’s the part list on pcpartpicker. It’s super beefy and runs any- and everything with no complaints. Getting to this point was a little painful as the graphics card I bought was open box from Micro Center. After two RMAs, MSI ended up sending me a brand new card.

Important Specs:

  • i9-13900k
  • RTX 4080
  • 64 GB RAM

For a laptop I have a Framework 13. I’ve been super happy with that as an on-the-go device.

My phone is a Pixel 6a. It does the trick, but I’m still angry they removed the headphone jack.

I have a random collection of old laptops and desktops that I’m using as a home proxmox cluster. This site is hosted on one of the computers on that rack.

any homelab folks out there?feeling glad that electricity is relatively cheap here

ben (@benharr.is) 2023-05-15T18:13:12.830Z

For servers, I’m not even sure at this point. I think my wikipage is up to date.

Software

You can find my dotfiles here.

I use Firefox as my browser on all my devices.

I do email in Thunderbird on desktop, and Aqua Mail on my phone.

Most days, I’ll boot into windows 11 on my desktop to remote into my $dayjob development VM (the VMware remote client runs better on windows). I also have a linux dual-boot setup but I hardly bother rebooting to switch. I’ve been playing videogames quite a bit lately and haven’t taken the time to set up linux gaming on the other drive.

My laptop is running Fedora and I’m currently running KDE. GNOME is also fine. Use what you like 🙂

Text editor is vim and/or sublime text. I use Visual Studio and Jetbrains Rider for the C# I do at work.

I use fish shell as my main interactive shell, and some of the most common tools I use are ripgrep, fzf, and a bunch of aliases and shell scripts I’ve built up over the years.

For IRC, I use WeeChat. I’ve written previously about my setup. I use weechat-android on my phone to keep up to date.

Password manager is KeepassXC, and keepassdx on android. I use Syncthing for my files, including my keepass db file, and all the associated files in my pubdir (separate synced dirs).

My phone number is on jmp.chat, so I use XMPP to send and receive SMS. I run the cheogram client on my phone to integrate with the system dialer. On desktop, I tend to SMS from my weechat via bitlbee.

My font of choice is Berkeley Mono, an absolutely fantastic font. Runners up are Input Mono and Jetbrains Mono.

This site is WordPress, but it was previously in several different incarnations, starting from a very old WordPress site, then I switched at some point to bashblog. That grew tiring to deal with once you have more than a handful of posts. I switched to hugo, which I also wrote about. I guess I’m more of a GUI guy than I thought because I ended up going back to WordPress at some point last year.

Since I got deep into pinball over the last couple years, I use Match Play to run the two weekly tournaments here in TC. The tcpinball.org site is also WordPress, and I use the Jetpack plugin to publish new posts on the various social media pages we have for that.

Categories
Uncategorized

Proxmox on Hetzner Setup Notes

Installation

Basic installation over plain debian https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_12_Bookworm

Network config

Network configs derived from: https://community.hetzner.com/tutorials/install-and-configure-proxmox_ve

sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1

Proxmox host /etc/network/interfaces

This example is for the main IPv4 of 157.90.92.151 with two subnets of 157.90.196.48/28 and 162.55.142.192/28. The IPv4 gateway is derived from the existing Hetzner configs given on install.

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback
iface lo inet6 loopback

auto enp8s0
iface enp8s0 inet static
address 157.90.92.151
netmask 255.255.255.255
pointopoint 157.90.92.129
gateway 157.90.92.129

iface enp8s0 inet6 static
address 2a01:4f8:252:3e22::2
netmask 128
gateway fe80::1

auto vmbr0
iface vmbr0 inet static
address 157.90.92.151
netmask 255.255.255.255
bridge_ports none
bridge_stp off
bridge_fd 0
pre-up brctl addbr vmbr0
up ip route add 157.90.196.48/28 dev vmbr0
up ip route add 162.55.142.192/28 dev vmbr0
down ip route del 157.90.196.48/28 dev vmbr0
down ip route del 162.55.142.192/28 dev vmbr0
post-down brctl delbr vmbr0

iface vmbr0 inet6 static
address 2a01:4f8:252:3e22::2
netmask 64

The important bits here are sysctl forwarding and routing our guest subnet to vmbr0.

Also need to systemctl disable --now rpcbind.socket per Hetzner rules.

Debian guest config

Subnet: 157.90.196.48/28

auto ens18
iface ens18 inet static
address 157.90.196.48/32
# or address 157.90.196.X/32
gateway 157.90.92.151

iface ens18 inet6 static
# in this case i'm using the same ending as ipv4
address 2a01:4f8:252:3e22::48/64
gateway 2a01:4f8:252:3e22::2

/etc/apt/sources.list

deb http://mirror.hetzner.de/debian/packages bookworm main
deb http://mirror.hetzner.de/debian/packages bookworm-updates main
deb http://mirror.hetzner.de/debian/packages bookworm-backports main
deb http://mirror.hetzner.de/debian/security bookworm-security main

deb http://security.debian.org bookworm-security main

/etc/resolv.conf

These are specifically Hetzner’s internal resolvers.

nameserver 213.133.100.100
nameserver 213.133.98.98
nameserver 213.133.99.99
nameserver 2a01:4f8:0:1::add:1010
nameserver 2a01:4f8:0:1::add:9999
nameserver 2a01:4f8:0:1::add:9898
Categories
Uncategorized

Mastodon Admin Notes

This is the cron job that runs daily to do media and federation data cleanup:

#!/bin/sh

printf "%s: running cleanup tasks\n" "$(date)"
RAILS_ENV=production /home/mastodon/.rbenv/shims/ruby /home/mastodon/live/bin/tootctl media remove --days=7
RAILS_ENV=production /home/mastodon/.rbenv/shims/ruby /home/mastodon/live/bin/tootctl media remove --days=7 --remove-headers
RAILS_ENV=production /home/mastodon/.rbenv/shims/ruby /home/mastodon/live/bin/tootctl media remove --days=7 --prune-profiles
RAILS_ENV=production /home/mastodon/.rbenv/shims/ruby /home/mastodon/live/bin/tootctl media remove-orphans
RAILS_ENV=production /home/mastodon/.rbenv/shims/ruby /home/mastodon/live/bin/tootctl preview_cards remove --days=30

printf "%s: dumping db\n" "$(date)"
pg_dump -Fc mastodon_production | ssh rsync "dd of=mastodon/db.dump"

date

This is the wrapper script I use to make sure I restart all necessary processes after updates.

root@mastodon:~# cat /usr/local/bin/masto
#!/bin/sh

case $1 in
        start|restart|stop|status)
                printf "%sing mastodon services\n" "$1"
                ;;

        logs)
                exec journalctl -fu mastodon-\*
                ;;
        *)
                printf "%s: invalid action. try logs, status, start, stop, restart.\n" "$1"
                exit 1
                ;;
esac

exec systemctl $1 mastodon-web \
        mastodon-streaming \
        mastodon-sidekiq@default \
        mastodon-sidekiq@pull \
        mastodon-sidekiq@push \
        mastodon-sidekiq@mailers \
        mastodon-sidekiq@scheduler \
        mastodon-sidekiq@ingress

The mastodon-sidekiq@.service units were added to address queues backing up and is essentially the same as the default unit file but with the -q queue name parameter added.

Categories
Uncategorized

Keep it Simple

I run two weekly pinball tournaments here in Traverse City (https://tcpinball.org/) which we submit to the IFPA for global rankings.

The IFPA charges a $1 per player fee to be “sanctioned”. This fee is generously sponsored by each location that we run tournaments at. Thinking it’d be easy enough to pull back the player counts later on, I put the fee payments on my credit card and figured I’d get reimbursed later on.

This is what the stats page shows on IFPA, which is not useful to me, but it shows I’ve paid $568 as of this date in fees. I need to know how much each location owes me.

I spent probably 4 hours yesterday digging through the API docs for matchplay and the IFPA directly trying to get a count of players by location.

I ended up writing some code which didn’t quite solve it either. The list of tournaments that it gives back doesn’t include the player count, meaning I have to make another API call to get the player details for each tournament. I got rate-limited almost immediately just testing it.

While the data I need might not be accessible via the APIs, it’s right here on my TD page in a nice little table. Queue another hour of trying to parse the HTML with several different tools.

The brain blast came as I was eating dinner. Stop trying to parse it and just paste it into a spreadsheet. Works immediately. Save as a CSV and import to a sqlite db.

sqlite> .mode csv
sqlite> .import ifpa.csv ifpa
sqlite> select sum(players) from ifpa where tournament like '%coin slot%';
436
sqlite> select sum(players) from ifpa where tournament like '%right brain%';
148

I could’ve figured out how to match this up with a calculated column in the spreadsheet but the important feature there was just parsing the HTML from my clipboard.

Major props to spreadsheets and sqlite yet again!

Categories
Uncategorized

WordPress block theme fragment offset

I just solved a slight visual bug on a wordpress site (tcpinball.org) with id permalinks.

I recently added made the site header sticky on there, which started covering the heading you jumped to when clicking a fragment id.

Here’s the solution:

[id] {
    scroll-margin-top: 80px;
}

You can add this CSS snippet in the block editor with the “Additional CSS” option in the global styles panel.

Easy fix without editing any theme or plugin files!

Categories
Uncategorized

My desktop rig

I bought all the parts for this back in March at Micro Center.

i9-13900k, RTX 4080, 64 gb RAM. Runs everything on highest settings.

pc part picker listing

Categories
Uncategorized

git diff-highlight

Diff output from git can be hard to read. Luckily there’s a nice tool bundled with git that can help us out.

Enter diff-highlight, a little perl script found in git’s contrib directory.

From its own documentation:

[diff-highlight] post-processes the line-oriented diff, finds pairs of lines, and highlights the differing segments.

diff-highlight is shipped in a default git install but it needs to be bundled and added to your $PATH. Here’s how to do it on debian:

$ sudo make -C /usr/share/doc/git/contrib/diff-highlight
$ sudo ln -s /usr/share/doc/git/contrib/diff-highlight/diff-highlight /usr/local/bin/

Now you can pipe git’s diff output to to diff-highlight to get a better view of what actually changed.

git diff | diff-highlight

Optionally, you can configure git to use it all the time. Add the following to your ~/.gitconfig:

[pager]
log = diff-highlight | less
show = diff-highlight | less
diff = diff-highlight | less

See the documentation for more usage tips!