Categories
Uncategorized

WordPress IndieWeb setup with a block theme

I’ve been tinkering around with IndieWeb integration on this site and wanted to document some notes getting all this running with a block theme.

Theme

I’m running a mildly customized version of Pulitzer. I’ve used the Create Block Theme plugin to export my changes and have published it as a standalone child theme here.

Changes are mainly typography and header/footer changes. I’ve raised the font sizes across the board and switched to using system fonts to avoid shipping font files. Modern Font Stacks is lovely and I found this tiny plugin to pull all the families in to the font library.

I’ve also started extending Pulitzer’s existing block bindings. I’m testing this as a stand-in for Syndication Links:

Block Binding Callbacks

https://tildegit.org/ben/putz-theme/src/commit/44fff0096816d959b588b53541bbd3762b4e9fab/functions.php#L426

function putz_block_binding_callback_syndication_links
    ( array $source_args, WP_Block $block_instance, string $attribute_name ) {
  $post_id = $block_instance->context['postId'] ?? get_the_ID();
  $links = get_post_meta( $post_id, 'mf2_syndication', true);
  if ( empty( $links ) ) {
    return false;
  } else {
    return 'Also on: ' . implode( ' ', array_map( function($link) {
      $domain = wp_parse_url( $link, PHP_URL_HOST );
      return "<a href=\"$link\">$domain</a>";
    }, $links ) );
  }
}

Plugins

Relevant Plugins
PluginVersion
IndieAuthVersion 4.5.2 by IndieWeb WordPress Outreach Club
IndieBlocksVersion 0.13.0 by Jan Boddez
IndieWebVersion 4.0.5 by IndieWebCamp WordPress Outreach Club
MF2 FeedVersion 3.1.1 by IndieWeb WordPress Outreach Club
MicropubVersion 2.4.0 by IndieWeb WordPress Outreach Club
Syndication LinksVersion 4.4.20 by David Shanske
WebmentionVersion 5.3.3 by Matthias Pfefferle
WebSubVersion 3.2.1 by PubSubHubbub Team

IndieBlocks

I’m using IndieBlocks to provide microformats markup throughout my site. I’m also using the Note and Like post types. I’ve decided to keep those out of my main blog feed. Here’s the configs.

The Syndication Links plugin handles publishing the actual syndication posts, and I use the hidden links for the markup. The clickable links are currently displayed with a block binding.

IndieAuth

This requires essentially no configuration, but I did find a nasty bug in the plugin code and helped debug a bit. Logging in with IndieAuth was hanging indefinitely, getting killed by php-fpm, and throwing a 504 timeout. I’m not super familiar with the code but this is the PR that fixed it.

Shoutout to dshanske for helping me figure this one out and releasing a new plugin version with the fix!

Bridgy

I’m using both Bridgy and Bridgy Fed. I’m currently trying out POSSE for my mastodon and bluesky posts with Bridgy and publishing them as Notes here on this site. These are the publish targets I use via Syndication Links:

My solution for keeping Notes and Likes out of my main blog feed was making sure to show them in my author feed in the IndieBlocks settings and adding a rel=feed link to my author page in the header.

add_action( 'wp_head', function() { ?>
<link rel="feed" type="text/mf2+html" href="https://benharri.org/author/ben/" />
<?php } );

The other solution I tried for this was using MF2 Feed and including them as rel=alternate but Bridgy doesn’t support mf2+json feeds yet. There’s an open issue to add support for this use case.

One remaining thing that I haven’t figured out yet is pulling the syndication links out of the logs into the proper post meta. Still haven’t narrowed this down fully but it works perfectly fine when publishing with a Micropub client like Quill or IndiePass, so it feels like something to do with the block editor.

My workaround is wp-cli + jq and feels entirely unsatisfying. Hoping to get that figured out soon.

Categories
Uncategorized

Block themes are cool, actually

WordPress block themes have grown on me.

Spent a couple hours this weekend sprucing up tcpinball.org. Found this lovely block theme and it all came together quite nicely.

Had a strange bug where the Navigation block stopped working on certain pages. Didn’t find the actual cause but it fixed itself when I disabled the Gutenberg plugin.

Categories
Uncategorized

DNSSEC wasn’t worth it

In reply to Calling Time on DNSSEC? by Geoff Huston.

[… we] estimate that DNSSEC validation is performed around 1% of the time, given the DNS query profile of today’s data

I run my own authoritative nameservers and have had a slight nagging feeling that I should’ve enabled DNSSEC years ago. It’s been on my perpetual to-do list but I’ve never gotten around to it. I’ve definitely caused some outages trying to get DNSSEC to work.

Came across this article and it confirms that my procrastination was pretty OK in this specific case.

Categories
Uncategorized

Bluesky PDS Without Docker

Here’s how I got a self-hosted PDS (personal data server) running without docker.

This can be useful if you want to run the PDS on an existing machine or just don’t like docker. I came up with these steps by emulating what the installer script does.

My setup uses nginx and a wildcard TLS cert for my PDS domain.

Get the code

Clone the PDS repo

$ git clone https://github.com/bluesky-social/pds

Set up nginx

I use certbot to issue wildcard certs for my domains. See my wildcard cert script here. Note that you will need to set up credentials for your nameservers. I’m not aware of a way in nginx to issue certs on-demand like the example caddy config does.

Here’s my nginx config for the PDS.

server {
listen 80;
server_name hellthread.pro *.hellthread.pro;
return 302 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name hellthread.pro *.hellthread.pro;
ssl_certificate /etc/letsencrypt/live/hellthread.pro/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hellthread.pro/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

location / {
include proxy_params;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:3002;
}
}

Configure your .env file

Adjust the top 4 options, filling in your domain and generating keys with the following commands:

Use this for the JWT_SECRET:

$ openssl rand --hex 16

Use this twice to generate the admin password and rotation key:

$ openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32
PDS_HOSTNAME="your domain here"
PDS_JWT_SECRET="generated secret"
PDS_ADMIN_PASSWORD="generated key"
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="another generated key"

PDS_DATA_DIRECTORY=./data
PDS_BLOBSTORE_DISK_LOCATION=./data/blocks
PDS_DID_PLC_URL=https://plc.directory
PDS_BSKY_APP_VIEW_URL=https://api.bsky.app
PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app
PDS_REPORT_SERVICE_URL=https://mod.bsky.app
PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac
PDS_CRAWLERS=https://bsky.network
LOG_ENABLED=true
NODE_ENV=production
PDS_PORT=3002

Run the PDS

Be sure to install the dependencies:

$ cd service
$ pnpm install --production --frozen-lockfile
$ mkdir -p data/blocks

This is the systemd setup I use to run the PDS. Add your own user unit with the following steps:

$ mkdir -p ~/.config/systemd/user
$ $EDITOR ~/.config/systemd/user/pds.service
# copy in the example below and adjust as needed
$ systemctl --user daemon-reload
$ systemctl --user enable --now pds

pds.service:

[Unit]
Description=atproto personal data server

[Service]
WorkingDirectory=/home/ben/workspace/pds/service
ExecStart=/usr/bin/node --enable-source-maps index.js
Restart=on-failure
EnvironmentFile=/home/ben/workspace/pds/service/.env

[Install]
WantedBy=default.target

View the logs from journalctl like this:

$ journalctl --user --output=cat --follow --unit pds | jq

You can run the pdsadmin commands by setting the PDS_ENV_FILE variable like this:

ben@odin ~/w/p/pdsadmin (main)> PDS_ENV_FILE=../service/.env bash account.sh list
Handle Email DID
ben.hellthread.pro ben@hellthread.pro did:plc:g5isluhi3wkw557ucarjgtgy

Update

To update your PDS, use git pull in the directory you cloned it in. Then update dependencies in the service subdirectory and restart the unit:

$ cd pds
$ git pull
$ cd service
$ pnpm install --production --frozen-lockfile
$ systemctl --user restart pds
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

WordPress with sqlite3

Update: there’s a proposal in WordPress core to merge sqlite support: https://make.wordpress.org/core/2023/04/19/status-update-on-the-sqlite-project/. The recommended way to use sqlite is currently by using the official plugin: https://wordpress.org/plugins/sqlite-database-integration/.


Running WordPress with sqlite is quick, easy, and can be much less system administration load as it eliminates the need for a separate database process.

Here’s how to run WordPress with sqlite using aaemnnosttv’s drop-in.

Set it up

  1. download https://wordpress.org/latest.tar.gz
  2. extract it into your webroot (something like /var/www)
  3. download db.php and add it to /var/www/yoursite/wp-content/
  4. follow the normal setup instructions but skip the database fields
  5. profit????

nginx config

Adjust configs as needed. Here’s an example.

snippets/ssl/benharri.org includes the block from certbot that points to the right cert and key.

server {
  listen 80;
  server_name benharri.org;
  return 307 https://$server_name$request_uri;
}

server {
  listen 443 ssl;
  server_name benharri.org;
  include snippets/ssl/benharri.org;
  index index.php index.html;
  root /var/www/benharri.org;
  client_max_body_size 100M;
  include /var/www/benharri.org/nginx.conf; #w3tc caching

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }

  location ~* wp-config.php {
    deny all;
  }

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_intercept_errors on;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
  }

  location ~ /\.ht {
    deny all;
  }
}
Categories
Uncategorized

Hugo blog rewrite

I finally got sick of some outstanding bashblog bugs and decided to rewrite my blog with hugo

Why fix something that ain’t broken?

There are several major bugs in bashblog that I haven’t had the time or interest to fix. Luckily, I have my blog source in a git repo, so the messed-up files are relatively easy to fix up.

My theory is that the cognitive load of dealing with those bugs has been preventing me from writing any new posts… (among other current events).

Plus I guess I just needed a project!

The hard part

It was a bit of a struggle to get my blogposts converted over to a proper format.

bashblog maintains the tags with a plaintext link line at the bottom of the file and keeps track of modify dates on the filesystem using date -r and touch -t to keep them up to date.

This makes it hard to work with the blog in any place other than where it’s already deployed live. Additionally, parsing the tags to another format was a bit of a headache…

I ended up writing a little script to pull the timestamp from bashblog’s generated html since I no longer had the original file metadata.

Time to write more

I’m always tempted to play with the publishing process instead of actually writing.

We’ll see how well I can avoid the temptation now!

Categories
Uncategorized

Tools and services

Inspired by tomasino’s recent post, I’d like to detail some of the stuff that I rent and use.

Recurring Costs

Some of these are monthly, others are annual

  • server rental – $135/month, 64gb model, 2yr contract discount
  • proxmox – my preferred hypervisor. I find it useful enough and worth paying the annual license to use the official package mirrors – $80/year
  • domains – includes tilde-related ones and my personal ones (about 10 or so) – about 30 domains totaling $500/year
  • offsite backups at rsync.net – I use the borg offering for a discount since versioning is handled by my backup software – between $60-$70/year

My out of pocket expenses are a little over $2k per year.

Note that I use the server and backups for personal stuff in addition to tilde related things.

The current expenses are totally manageable as is, but if you’ve got some spare cash and want to pitch in towards tilde hosting costs, I have a donate page.

Software I run, use, and maintain

These are all free/open source.

Operating systems

Platforms / software

These are things that I run and maintain for tilde.team and the tildeverse

Our own software

There are also a handful of other sites that we’ve written collaboratively among tildeverse peeps:

See the tildeverse org and team org on tildegit for more projects 🙂

This has diverged a bit from my costs into all the stuff that I do. I suppose that means that the main cost is my time and energy. In general, these things are hands-off in the day-to-day and only take time when there are updates or something breaks.