diff --git a/.bin/cdrip2flac b/.bin/cdrip2flac
new file mode 100755
index 0000000..80b0c42
--- /dev/null
+++ b/.bin/cdrip2flac
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -xe
+alias flac_encode="flac -e --best --delete-input-file"
+doas chown sdk /dev/rcd0* /dev/cd0*
+cdio cdrip
+for i in *.wav;
+do
+ flac_encode "$i" -o "${i%%.wav}.flac";
+done
+picard *.flac
+cdio eject
diff --git a/.bin/create-torrent b/.bin/create-torrent
new file mode 100755
index 0000000..a66809a
--- /dev/null
+++ b/.bin/create-torrent
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# source https://github.com/aria2/aria2/issues/663
+
+export PATH=$HOME/.cargo/bin:$PATH
+[ ! -f $HOME/.cargo/bin/imdl ] \
+ && cargo install -f imdl
+set -x
+imdl torrent create --input "$1"
+imdl torrent show --input "$1.torrent"
+imdl torrent verify --input "$1.torrent" --content "$1"
+imdl torrent link --input "$1.torrent"
diff --git a/.bin/cvs-update b/.bin/cvs-update
new file mode 100755
index 0000000..ac6b937
--- /dev/null
+++ b/.bin/cvs-update
@@ -0,0 +1,6 @@
+#!/bin/sh -ex
+
+CVSROOT="sdk@cvs.openbsd.org:/cvs"
+export CVSROOT
+
+doas -u sdk cvs -d $CVSROOT -z1 -q up -Pd -A $@
diff --git a/.bin/dexec_man b/.bin/dexec_man
new file mode 100755
index 0000000..712850b
--- /dev/null
+++ b/.bin/dexec_man
@@ -0,0 +1,11 @@
+#!/bin/sh
+. $HOME/.bin/_config
+
+SEL=$(man -k any= | $DMENU_CMD -l 10 -p "Man")
+
+[ -z "$SEL" ] && exit 0
+
+N=$(echo "$SEL" | cut -d"(" -f2 | cut -d")" -f1)
+M=$(echo "$SEL" | cut -d"(" -f1 | cut -d"," -f1)
+
+sterm -e man -s $N $M
diff --git a/.bin/fb-getip.sh b/.bin/fb-getip.sh
new file mode 100755
index 0000000..221cfcb
--- /dev/null
+++ b/.bin/fb-getip.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# needs: curl, yq (xq)
+# usage: fb-getip.sh [ip address]
+#
+# Endpoints & Services:
+# http://fritz.box:49000/tr64desc.xml
+# AVM TR064 Documentation:
+# https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AVM_TR-064_first_steps.pdf
+# https://www.broadband-forum.org/technical/download/TR-064.pdf
+
+FBHOST="fritz.box"
+[ ! -z "$1" ] && FBHOST="$1"
+
+# Variable parts, which can be looked up in the TR-064 documentation
+SERVICE=WANIPConnection
+ACTION=GetExternalIPAddress
+
+# Endpoints supported by the fritzbox, can be looked up at the
+# "Endpoint & Services" URL.
+ENDPOINT=WANIPConn1
+
+# SOAP protocol payload (https://en.wikipedia.org/wiki/SOAP)
+# This is the language the fritzbox is "speaking". So we create this
+# payload with the appropriage Service, Action variables.
+PAYLOAD=\
+'
+
+
+
+
+'
+
+# We use curl to send the payload with the appropriate soap+upnp header
+# to the fritzbox and receive the SOAP response.
+#
+# Header explanation:
+#
+# - Header: Content-Type - describes that we send format text/xml with utf8 encoding.
+# See HTTP Specification:
+# https://www.rfc-editor.org/rfc/rfc9110.html#name-content-type
+# (Chapter 8.3)
+#
+# - Header: SoapAction... - it's redundant with the payload information.
+# See SoapAction specification:
+# https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
+# (Chapter 6.1.1)
+#
+# - SoapAction:... - This is "urn:schemas-upnp-org:
+# Each has it's own specifcation.
+# WANIPConnection Service Specification:
+# https://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v1-Service.pdf
+# (Chapter 2.1)
+
+RESPONSE="$(curl -s "http://$FBHOST:49000/igdupnp/control/$ENDPOINT" \
+ -H "Content-Type: text/xml; charset=utf-8" \
+ -H "SoapAction:urn:schemas-upnp-org:service:$SERVICE:1#$ACTION" \
+ -d "$PAYLOAD")"
+
+# The SOAP response is ugly XML (just like the requst), so we use "xq",
+# which converts XML to JSON and allows us to operate on it with "jq"
+# syntax.
+# yq/xq documentation: https://kislyuk.github.io/yq
+# jq documentation: https://jqlang.github.io/jq/manual
+# Tutorial: https://www.ashbyhq.com/blog/engineering/jq-and-yq
+#
+# (once understood, these tools are _very_ powerful! - worth to learn)
+
+# This "case" is only there so we can do a different kind parsing for
+# other endpoints. Like when the ACTION variable is changed the parsing
+# needs to be adapted.
+case "$ACTION" in
+ GetExternalIPAddress) printf '%s' "$RESPONSE" \
+ | xq -r '."s:Envelope"."s:Body"."u:GetExternalIPAddressResponse".NewExternalIPAddress'
+ ;;
+ *) printf '%s' "$RESPONSE"
+ ;;
+esac
diff --git a/.bin/format-exfat.sh b/.bin/format-exfat.sh
new file mode 100755
index 0000000..f6e2fcb
--- /dev/null
+++ b/.bin/format-exfat.sh
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+
+_dev="$1"
+
+[ -z $_dev ] && printf "usage: %s \n" "$(basename $0)" && exit 2
+[ $(id -u) -gt 0 ] && printf "you need superuser rights\n" && exit 2
+
+dmesg | grep "$_dev" | grep -A1 scsibus | tail -n 2
+
+printf "Format ${_dev} [y/N]? "
+read
+case $REPLY in
+ [yY]) ;;
+ *) exit 0; ;;
+esac
+
+printf "Overwriting first MBs with zeros:"
+dd of=/dev/r${_dev}c if=/dev/zero bs=1M count=1 > /dev/null 2>&1
+printf " ✅\n"
+
+printf "Creating exFAT / NTFS partition:"
+echo "edit 0\n07\n\n2048\n*\nw\nq\n" | fdisk -e "$_dev" > /dev/null 2>&1
+printf " ✅\n"
+
+printf "Creating exFAT file system (this may take a while)...\n"
+mkexfatfs -s 2048 "/dev/${_dev}i"
+printf "Creating exFAT file system: ✅\n"
+
+printf "Mount:\ndoas mount.exfat /dev/%si /mnt\n" "$_dev"
diff --git a/.bin/format-fat32.sh b/.bin/format-fat32.sh
new file mode 100755
index 0000000..b9e4aa5
--- /dev/null
+++ b/.bin/format-fat32.sh
@@ -0,0 +1,29 @@
+#!/bin/sh -e
+
+_dev="$1"
+
+[ -z $_dev ] && printf "usage: %s \n" "$(basename $0)" && exit 2
+[ $(id -u) -gt 0 ] && printf "you need superuser rights\n" && exit 2
+
+dmesg | grep "$_dev" | grep -A1 scsibus | tail -n 2
+
+printf "Format ${_dev} [y/N]? "
+read
+case $REPLY in
+ [yY]) ;;
+ *) exit 0; ;;
+esac
+
+printf "Overwriting first MBs with zeros:"
+dd of=/dev/r${_dev}c if=/dev/zero bs=1M count=1 > /dev/null 2>&1
+printf " ✅\n"
+
+printf "Creating FAT32 partition:"
+echo "edit 0\n0B\n\n1024\n*\nw\nq\n" | fdisk -e "$_dev" > /dev/null 2>&1
+printf " ✅\n"
+
+printf "Creating FAT32 file system (this may take a while)...\n"
+newfs_msdos -F32 -b65536 "${_dev}i"
+printf "Creating FAT32 file system: ✅\n"
+
+printf "Mount:\ndoas mount_msdos /dev/%si /mnt\n" "$_dev"
diff --git a/.bin/got-notes b/.bin/got-notes
new file mode 100755
index 0000000..c1ed481
--- /dev/null
+++ b/.bin/got-notes
@@ -0,0 +1,23 @@
+#!/bin/cat
+
+init: got init luakit.git
+download: got clone git@github.com:luakit/luakit luakit.git
+checkout: got checkout luakit.git luakit
+commit: got commit
+diff changes: got diff
+
+diff branch main with patch: got diff main patch
+
+list branches: got branch -l
+switch/create branch: got branch testbranch
+
+interactive revert: got revert -p -R .
+
+rebase current branch on top of master: got update -b master
+
+add file: got add
+
+repo info: got info
+
+import from CVS:
+got import -r /var/git/src.git -I CVS -I obj /tmp/src
diff --git a/.bin/lddcopy b/.bin/lddcopy
new file mode 100755
index 0000000..76a1c28
--- /dev/null
+++ b/.bin/lddcopy
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# FIXME: does not work with space in filenames or directories
+# (can be fixed by | while read ... the ldd output, but.. why?)
+# FIXME: directory permissions are not copied
+# (can be fixed with stat -f '%p %u %g' . and walk the path with mkdir -m ...)
+
+##
+## USAGE
+##
+
+_usage() {
+ echo "usage: mkchroot [-xn] [-u or executable] directory"
+ echo "options: -u - runs for all executables found in /bin/ directories"
+ echo " -x - copy stuff required to connect to X"
+ echo " -n - copy stuff required for networking"
+}
+
+##
+## HANDLE ARGUMENTS
+##
+
+while getopts xnuh arg
+do
+ case $arg in
+ x) _with_x=1 ;; # creates /tmp with X connection
+ n) _with_network=1 ;; # copies resolv.conf
+ u) _update_mode=1 ;; # updates the existing directory
+ h) _usage; exit 0 ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+if [ -z "$1" ] && [ -z "$_update_mode" ]
+then
+ _usage
+ exit 1
+fi
+
+if [ -z "$2" ] && [ -z "$_update_mode" ]
+then
+ _usage
+ exit 1
+fi
+
+if [ "$_update_mode" == "1" ]
+then
+ _indir="$1"
+else
+ _infile="$1"
+ _indir="$2"
+fi
+
+###
+### FUNCTIONS
+###
+
+_ldd_extract() {
+ for _file in $(ldd "$1" | awk '/\// { print $7 }' | xargs)
+ do
+ _name="$(basename "$_file")"
+ _dir="$2/$(dirname "$_file" | cut -b2-)"
+ if [ ! -d "$_dir" ]
+ then
+ echo "mkdir: $_dir"
+ mkdir -p "$_dir"
+ fi
+ if [ ! -f "$_dir/$_name" ]
+ then
+ echo "copy: $_file -> $_dir/$_name"
+ cp -af "$_file" "$_dir/$_name"
+ else
+ echo "skip: $_file -> $_dir/$_name (existing)"
+ fi
+ done
+}
+
+_find_binfile() {
+ # remove basedir
+ _f=$(echo "$1" | sed "s|${1%%/*}||g")
+ _ofile="$(readlink -f "$_f" 2> /dev/null)"
+ echo $_ofile
+}
+
+###
+### MAIN PROGRAM
+###
+
+if [ -z "$_update_mode" ]
+then
+ _ldd_extract "$_infile" "$_indir"
+fi
+
+if [ "$_update_mode" == "1" ]
+then
+ _binfiles="$(find "$_indir" -type f -path "*/bin/*")"
+ for _binfile in $_binfiles
+ do
+ _infile=
+ _infile=$(_find_binfile "$_binfile")
+ if [ -z "$_infile" ]
+ then
+ echo "skip: $_binfile -> $_infile (not found)"
+ else
+ _ldd_extract "$_infile" "$_indir"
+ fi
+ done
+fi
diff --git a/.bin/luakit-formfiller b/.bin/luakit-formfiller
new file mode 100755
index 0000000..4c3bea6
--- /dev/null
+++ b/.bin/luakit-formfiller
@@ -0,0 +1,2 @@
+#!/bin/sh
+sterm "vim $HOME/.local/share/luakit/forms.lua" &
diff --git a/.bin/myxmenu b/.bin/myxmenu
new file mode 100755
index 0000000..ad35c40
--- /dev/null
+++ b/.bin/myxmenu
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+get_catgirl() {
+ cd ~/.config/catgirl && ls * | xargs -n1 \
+ | while read line
+ do
+ print "\t$line\ttexec \"catgirl $line\""
+ done
+}
+IRC="$(get_catgirl)"
+
+xmenu -i < 0.20, # How long to wait before giving up on server response, in seconds
+ 'debug' => 1, # Debug output?
+ 'top' => 3, # How many entries to return?
+ 'protocol' => 'tcp', # tcp, udp, icmp, et al
+ 'port' => 'http', # (used in getservbyname sub)
+);
+
+# hash to store servers and response times to protocol/port requests
+my %serverstats;
+
+# create HTTP::Tiny instance
+my $http = HTTP::Tiny->new;
+
+# Get a list of all current OpenBSD FTP servers
+my $response = $http->get('http://ftp.eu.openbsd.org/pub/OpenBSD/ftplist');
+die "Failed!\n" unless $response->{success};
+
+# Iterate through server list and get TCP/80 response time in ms
+foreach my $line (split("\n", $response->{content})) {
+ if ($line !~ /(cdn)/) {
+ if ($line =~ /(http:\/\/)(.+?)(\/\S+)/) {
+ my $response = &httping($2);
+ $serverstats{$1.$2.$3} = $response if ($response);
+ }
+ }
+}
+
+# Sort & print servers by response time in ms
+my $i = 0;
+foreach my $key (sort {$serverstats{$a} <=> $serverstats{$b} } keys %serverstats) {
+ $i++;
+ print "$key\n";
+ last if $i eq $config{'top'};
+}
+
+#
+# Ping TCP/80 and return response time or 0 if unresponsive + some diagnostic output
+#
+
+sub httping ($) {
+ my $host = shift;
+ my $ping = Net::Ping->new($config{'protocol'});
+ $ping->hires();
+ $ping->{port_num} = getservbyname($config{'port'}, $config{'protocol'});
+ print ("Trying $host... ") if $config{'debug'};
+ my ($retval, $duration, $ip) = $ping->ping($host, $config{'timeout'});
+ $ping->close();
+ $duration = int($duration * 1000);
+
+ if ($retval) {
+ print ("$duration ms\n") if $config{'debug'};
+ return $duration;
+ } else {
+ print ("unresponsive\n") if $config{'debug'};;
+ return 0;
+ }
+}
diff --git a/.bin/subsonic b/.bin/subsonic
new file mode 100755
index 0000000..346b0c2
--- /dev/null
+++ b/.bin/subsonic
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+# subsonic
+alias subsonic-cli="\subsonic-cli -c $HOME/.subsonic-cli.conf"
+
+subsonic-play() {
+ # $1 songId
+ for song in $@
+ do
+ echo $song > /tmp/.playing
+ subsonic-cli stream -p id $song \
+ | mpv --force-window=no --loop=no - || exit 0
+ done
+}
+
+subsonic-random() {
+ while true;
+ do
+ echo "Fetching moar dataaa.."
+ subsonic-play $(subsonic-cli getRandomSongs \
+ | jq -r '.randomSongs[][].id')
+ done
+}
+
+subsonic-favorites() {
+ while true;
+ do
+ echo "Fetching moar dataaa.."
+ subsonic-play $(subsonic-cli getStarred \
+ | jq -r '.starred[][].id' \
+ | sort -r)
+ done
+}
+
+subsonic-search() {
+ {
+ echo "|ALBUMID|ID|ALBUM|ARTIST|TITLE|GENRE|"
+ printf "|%s|%s|%s|%s|%s|%s|\n" "--------------------------------"\
+ "--------------------------------"\
+ "--------" "--------" "--------" "--------"
+ subsonic-cli search3 \
+ -p songcount 200 \
+ -p query "$@" \
+ | jq -r '.searchResult3[][]|select(.contentType!=null)|[.albumId,.id,.album,.title,.artist,.genre]' \
+ | tr -d '", ' \
+ | awk '/^\[/ { S++ } \
+ /^\]/ { S-- } \
+ S==0 { NR=0 } \
+ NR==2 { AID=$0 } \
+ NR==3 { ID=$0 } \
+ NR==4 { ALBUM=$0 } \
+ NR==5 { TITLE=$0 } \
+ NR==6 { ARTIST=$0 } \
+ NR==7 { GENRE=$0 } \
+ NR==0 { printf("|%s|%s|%s|%s|%s|%s|\n", AID, ID, ALBUM, ARTIST, TITLE, GENRE) }'
+ } | column -s'|' -t
+}
+
+subsonic-star() {
+ [ -f /tmp/.playing ] \
+ && subsonic-cli star -p id $(cat /tmp/.playing | tail -1) \
+ || echo bruh.
+}
+
+subsonic-unstar() {
+ [ -f /tmp/.playing ] \
+ && subsonic-cli unstar -p id $(cat /tmp/.playing | tail -1) \
+ || echo bruh.
+}
+
+subsonic-download() {
+ # $1 albumId
+ NAME=$(subsonic-cli getAlbum -p id $1 \
+ | jq -r '.album.artist,.album.name' | xargs | tr ' ' '-')
+ printf "Downloading to %s/subsonic-download/%s.zip\n" "$HOME" "$NAME"
+ mkdir -p "$HOME/subsonic-download"
+ subsonic-cli download -p id $1 | pv > "$HOME/subsonic-download/$NAME.zip"
+}
+
+if [ -z $1 ]
+then
+ cat <<"EOF"
+ usage: subsonic
+
+ play - play song with id
+ download - download album with id
+ [un]star - [un]star currently playing song
+ search - show song/album for term
+ favorites - play starred songs
+ random - play random songs
+
+EOF
+else
+ subsonic-$1 $2
+fi
+
+
diff --git a/.bin/xdg-open b/.bin/xdg-open
new file mode 120000
index 0000000..a8f5f6a
--- /dev/null
+++ b/.bin/xdg-open
@@ -0,0 +1 @@
+nnn.sh
\ No newline at end of file