diff --git a/deploy/README.md b/deploy/README.md
deleted file mode 100644
index c3f746ea..00000000
--- a/deploy/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Various ways to deploy GoatCounter; see the subdirectories for details.
-
-- alpine – Set up an Alpine Linux machine.
diff --git a/deploy/alpine/README.md b/deploy/alpine/README.md
deleted file mode 100644
index d3c1368b..00000000
--- a/deploy/alpine/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-This sets up a basic GoatCounter installation on Alpine Linux, using SQLite.
-
-This is also available as a ["StackScript" for Linode][s]; If you don't have a
-Linode account yet then [consider using my "referral URL"][r] and I'll get some
-cash back from Linode :-)
-
-It should be fine to run this more than once; and can be used to upgrade to a
-newer version.
-
-You can set the version to use with `GOATCOUNTER_VERSION`; this needs to be a
-release on GitHub:
-
- $ GOATCOUNTER_VERSION=v2.0.0 ./goatcounter-alpine.sh
-
-You can create additional sites with:
-
- $ cd /home/goatcounter
- $ ./bin/goatcounter db create site -vhost example.com -user.email me@example.com
-
-Files are stored in `/home/goatcounter`; see `/var/log/goatcounter/current` for
-logs; and you can configure the flags in `/etc/conf.d/goatcounter`
-
-[s]: https://cloud.linode.com/stackscripts/659823
-[r]: https://www.linode.com/?r=7acaf75737436d859e785dd5c9abe1ae99b4387e
diff --git a/deploy/alpine/goatcounter-alpine.sh b/deploy/alpine/goatcounter-alpine.sh
deleted file mode 100755
index d20d2f4b..00000000
--- a/deploy/alpine/goatcounter-alpine.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/bin/sh
-#
-#
-#
-#
-#
-# This will set up an Alpine Linux machine; environment variables:
-# GOATCOUNTER_DOMAIN Domain you'll be hosting GoatCounter on
-# GOATCOUNTER_EMAIL Your email address
-# GOATCOUNTER_PASSWORD Password to access GoatCounter
-# GOATCOUNTER_VERSION GoatCounter version (default: v2.4.1).
-#
-# This is available as a "StackScript" to deploy GoatCounter on a Linode VPS:
-# https://cloud.linode.com/stackscripts/659823
-#
-# If you don't have a Linode account yet then consider using my "referral URL"
-# and I'll get some cash back from Linode :-)
-# https://www.linode.com/?r=7acaf75737436d859e785dd5c9abe1ae99b4387e
-#
-# This script's source at the GoatCounter repo is:
-# https://github.com/arp242/goatcounter/blob/master/deploy/alpine
-#
-# It should be fine to run this more than once; and can be used to upgrade to a
-# newer version.
-#
-# Files are stored in /home/goatcounter; see /var/log/goatcounter for logs; and
-# you can configure the flags in /etc/conf.d/goatcounter.
-#
-# You can create additional sites with
-#
-# $ cd /home/goatcounter
-# $ ./bin/goatcounter db create site [..]
-#
-# Please report any bugs, problems, or other issues on the GoatCounter issue
-# tracker, or email me at martin@goatcounter.com
-#
-
-# GoatCounter version to set up.
-v=${GOATCOUNTER_VERSION:-"v2.4.0"}
-
-
-set -eu
-
-if [ -z "${GOATCOUNTER_DOMAIN:-}" ]; then
- printf 2>&1 'Must set a domain; see the script header for usage.\n'
- exit 1
-fi
-if [ -z "${GOATCOUNTER_EMAIL:-}" ]; then
- printf 2>&1 'Must set a email; see the script header for usage.\n'
- usage
- exit 1
-fi
-if [ -z "${GOATCOUNTER_PASSWORD:-}" ]; then
- printf 2>&1 'Must set a password; see the script header for usage.\n'
- usage
- exit 1
-fi
-
-# Required packages.
-apk add tzdata
-
-# Setup user and group.
-grep -q '^goatcounter:' /etc/group || addgroup -S goatcounter
-grep -q '^goatcounter:' /etc/passwd || adduser -s /sbin/nologin -DS -G goatcounter goatcounter
-
-# Get latest version if it doesn't exist yet.
-mkdir -p /home/goatcounter/bin
-dst="/home/goatcounter/bin/goatcounter-$v"
-if [ ! -f "$dst" ]; then
- curl -L \
- "https://github.com/arp242/goatcounter/releases/download/$v/goatcounter-$v-linux-amd64.gz" |
- gzip -d > "$dst"
-fi
-chmod a+x "$dst"
-setcap 'cap_net_bind_service=+ep cap_sys_chroot=+ep' "$dst"
-ln -sf "$dst" "/home/goatcounter/bin/goatcounter"
-
-# Set up site; this may fail if the site already exists, which is fine.
-cd /home/goatcounter
-./bin/goatcounter db create site -createdb -vhost "$GOATCOUNTER_DOMAIN" -user.email "$GOATCOUNTER_EMAIL" -user.password "$GOATCOUNTER_PASSWORD" ||:
-chown -R goatcounter:goatcounter db
-
-# Set up log directory.
-mkdir -p /var/log/goatcounter
-chown goatcounter:goatcounter /var/log/goatcounter
-
-# Install, enable, and start service.
-cat << EOF > /etc/init.d/goatcounter
-#!/sbin/openrc-run
-
-name="goatcounter"
-description="GoatCounter web analytics"
-
-command=/home/goatcounter/bin/goatcounter
-directory=/home/goatcounter
-command_args="serve -listen \${GOATCOUNTER_LISTEN:-:443} -db \${GOATCOUNTER_DB:-sqlite+./db/goatcounter.sqlite3} \${GOATCOUNTER_OPTS:--automigrate}"
-command_user="goatcounter:goatcounter"
-command_background="yes"
-pidfile="/run/\${RC_SVCNAME}.pid"
-output_log="/var/log/\${RC_SVCNAME}/current"
-error_log="/var/log/\${RC_SVCNAME}/current"
-
-start_pre() {
- # Make sure this is correct after updates etc.
- setcap 'cap_net_bind_service=+ep cap_sys_chroot=+ep' "\$(readlink "\$command")"
-}
-
-depend() {
- use net
- use dns
- after firewall
-}
-EOF
-
-cat << EOF > /etc/conf.d/goatcounter
-# The uncommented values are the defaults.
-
-# Listen on all addresses.
-#GOATCOUNTER_LISTEN=:443
-
-# Location of SQLite3 database file or PostgreSQL connection. GoatCounter is
-# started from /home/goatcounter.
-#GOATCOUNTER_DB="sqlite+./db/goatcounter.sqlite3"
-
-# If you use PostgreSQL then URI-type connector is recommended, as OpenRC can't
-# deal well with spaces; for example:
-#GOATCOUNTER_DB="postgresql+/run/postgresql/goatcounter?sslmode=disable"
-
-
-# Other flags to add. See "goatcounter help serve".
-#GOATCOUNTER_ARGS="-automigrate"
-EOF
-
-chmod a+x /etc/init.d/goatcounter
-rc-update add goatcounter ||:
-rc-service goatcounter start
diff --git a/docs/plans.md b/docs/plans.md
deleted file mode 100644
index 07176142..00000000
--- a/docs/plans.md
+++ /dev/null
@@ -1,70 +0,0 @@
-This document describes long-term goals for GoatCounter.
-
-Right now, GoatCounter just "counts" who visits what page. This is a good start
-but I'd like to add a few more advanced features to give better insight. The
-end-goal is to give meaningful analytics (instead of just "vanity analytics")
-while still maintaining reasonable and expected levels of privacy (i.e. it won't
-be possible to connect data to a real person, or to see everything someone did
-on your website ever).
-
-A high-level overview, in order of what needs to be done:
-
-- There are various "plumbing" tasks that need to be done, like supporting
- multiple users, making it easier to self-host an instance (https server,
- ACME implementation which doesn't depend on shell scripts, better DB
- migrations/management), and a few performance issues.
-
-- In my own usage I notice I miss various basic features, such as searching
- paths, or the ability to add tracking for "who is clicking on my
- {Patreon,Sign up} button?", or the ability to view dates in the local
- timezone (it's all UTC now for simplicity).
-
-- Setting up a good and highly reliable hosted option; I feel this is
- important because in reality, most people are just not going to self-host
- GoatCounter. Even people who have the required technical skills often have
- better stuff to do than maintain their own. This is an important reason
- for the popularity of Google Analytics: it's free and easy to set up.
-
- Right now everything runs on a simple $20/month Linode VPS and free Open
- Source Netlify account for CDN. It works well enough for now, but needs
- some more work to ensure better reliability; there have been two problems
- already due to simple config errors, both of which would have been caught
- and fixed very quickly with better monitoring/testing of the live site in
- place. Backups are also a bit ad-hoc right now by downloading them to my
- laptop every day. In other words, it all needs some work.
-
- The "self-hosted" option will always remain a first-class citizen.
-
-- Better/more flexible charts; right now it's essentially just one view (per
- hour), and it would be helpful to have a per-day view too, or to easily
- compare the current month to the previous.
-
- I feel this is an essential part of what's missing from a lot of existing
- "simple" solution, which just give you "you had 5k visitors this month".
- That's all great, but it doesn't really tell you stuff like "you had a 9%
- growth this month", or "you had a 3% growth from source A, and a 18%
- growth from source B".
-
-- Give more flexible insight outside of "number of visits per page". A lot
- of use cases revolve around sales rather than page views, and supporting
- complex workflows/insights is what make tools like Matomo and GA so
- complex. The trick is here is to strike a good balance between being easy
- to use/add technically, giving meaningful data, and still being easy to
- use for the average person.
-
-- Make a slightly more advanced "tracker" which allows giving a bit more
- insight in the path visitors take (e.g. "30% of people never complete the
- signup form", or "25% of people who enter via /foo.html go to
- "/bar.html").
-
- This gives more insight in what kind of pages are effective at driving
- conversion, and also enabling insight in how a change/redesign affects the
- effectiveness of a site.
-
- The trick here is doing it without pervasive tracking. Fathom does this by
- setting a temporary 30-minute cookie, for example, although I'd like to
- explore alternatives which doesn't store *any* user ID.
-
-Out of scope are things like highly advanced data analysis, user
-identifiable tracking, "real time visitor information", or generally
-covering every single use case.
diff --git a/docs/sbom.md b/docs/sbom.md
deleted file mode 100644
index 5b3b4720..00000000
--- a/docs/sbom.md
+++ /dev/null
@@ -1,83 +0,0 @@
-Software Bill of Materials
-==========================
-
-Maintained here as part of the [NGI0 PET Fund](https://nlnet.nl/PET/)
-application.
-
-Backend
--------
-
-Direct dependencies:
-
-| Name | License | Why? |
-| ---- | ------- | ----- |
-| code.soquee.net/otp | BSD-2-Clause | Generate tokens for MFA |
-| github.com/bmatcuk/doublestar/v3 | MIT | -exclude 'glob:..' flag in `goatcounter import`. |
-| github.com/boombuler/barcode | MIT | Generating the QR code for MFA |
-| github.com/go-chi/chi | MIT | HTTP routing |
-| github.com/google/uuid | BSD-3-Clause | Generate UUIDs for sessions. |
-| github.com/mattn/go-sqlite3 | MIT | SQLite database support |
-| github.com/monoculum/formam | Apache-2.0 | Decode HTTP forms to Go structs. |
-| github.com/oschwald/geoip2-golang | ISC | Get location from IP address. |
-| github.com/oschwald/maxminddb-golang | ISC | Get Location from IP address. |
-| github.com/russross/blackfriday | BSD-2-Clause | Some pages are in Markdown |
-| github.com/teamwork/reload | MIT | Automatically reload |
-| golang.org/x/crypto | BSD-3-Clause | Hash passwords, create TLS certs for ACME. |
-| golang.org/x/image | BSD-3-Clause | Create PNG version of the "visitor counter". |
-| golang.org/x/net | BSD-3-Clause | Generate and validate TOTP tokens. |
-| golang.org/x/sync | BSD-3-Clause | x/sync/singleflight |
-| zgo.at/blackmail | MIT | Send emails. |
-| zgo.at/errors | MIT | More convenient errors. |
-| zgo.at/follow | MIT | "goatcounter import -follow" |
-| zgo.at/gadget | MIT | Get browser and system name and name from User-Agent. |
-| zgo.at/guru | MIT | Errors with a status code. |
-| zgo.at/isbot | MIT | Detect bots. |
-| zgo.at/json | MIT | encoding/json with ,readonly tag |
-| zgo.at/tz | MIT | Present timezones in dropdown nicely. |
-| zgo.at/z18n | MIT | Translations and localisation |
-| zgo.at/zcache | MIT | In-memory caching. |
-| zgo.at/zdb | MIT | Database access layer |
-| zgo.at/zhttp | MIT | HTTP tools |
-| zgo.at/zli | MIT | CLI conveniences |
-| zgo.at/zlog | MIT | Logging library. |
-| zgo.at/zstd | MIT | Extensions to stdlib. |
-| zgo.at/zvalidate | MIT | Validate values |
-
-Testing dependencies:
-
-| Name | License | Why? |
-| ---- | ------- | ----- |
-| github.com/PuerkitoBio/goquery | BSD 3-Clause | Used in tests to check the HTML |
-| golang.org/x/tools | BSD-3-Clause | Linting tools. |
-| honnef.co/go/tools | MIT | Linting tools. |
-
-Indirect transient dependencies; not these may not actually be used/compiled in:
-
-| Name | License |
-| ---- | ------- |
-| github.com/BurntSushi/toml | MIT |
-| github.com/andybalholm/cascadia | BSD-2-Clause |
-| github.com/davecgh/go-spew | ISC |
-| github.com/fsnotify/fsnotify | BSD-3-Clause |
-| github.com/go-sql-driver/mysql | MPL-2.0 |
-| github.com/kisielk/gotool | MIT |
-| github.com/lib/pq | MIT |
-| github.com/pmezard/go-difflib | BSD-3-Clause |
-| github.com/stretchr/objx | MIT |
-| github.com/stretchr/testify | MIT |
-| github.com/yuin/goldmark | MIT |
-| golang.org/x/mod | BSD-3-Clause |
-| golang.org/x/sys | BSD-3-Clause |
-| golang.org/x/term | BSD-3-Clause |
-| golang.org/x/text | BSD-3-Clause |
-| golang.org/x/xerrors | BSD-3-Clause |
-| gopkg.in/check.v1 | BSD-2-Clause |
-| gopkg.in/yaml.v3 | Apache-2.0 |
-
-Frontend:
-
-| Name | License | Why? |
-| ---- | ------- | ---- |
-| jQuery | MIT | It's just easier than the DOM. |
-| Pikaday | MIT | Date picker; native browser date pickers don't actually work all that well (even in 2020), and this provides a much better UX. |
-| Dragula | MIT | Drag & drop; native browser functionality isn't broadly supported. |
diff --git a/docs/sessions.md b/docs/sessions.md
deleted file mode 100644
index e69de29b..00000000