From 2e0429a7e7b080218ced1eaae300a151e6272d7b Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 10:18:18 +0000 Subject: [PATCH 01/20] Add www.alexwlchan.net as a redirect in my Caddyfile --- Caddyfile | 4 ++-- tests/test_alternate_domains.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Caddyfile b/Caddyfile index 1930c6e22..0c7d4282b 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,8 +1,8 @@ # This redirects my other domain names to my primary domain name, # to avoid diluting traffic between them. -alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk { +alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk, www.alexwlchan.net { redir https://alexwlchan.net{uri} permanent - + log { output file /var/log/caddy/alexwlchan.log diff --git a/tests/test_alternate_domains.rb b/tests/test_alternate_domains.rb index 90b962203..fb081fa11 100644 --- a/tests/test_alternate_domains.rb +++ b/tests/test_alternate_domains.rb @@ -37,7 +37,7 @@ def test_www_redirects_to_apex def test_www_http_redirects_to_apex resp = get_url('http://www.alexwlchan.net') - assert_equal resp.code, '301' + assert_equal resp.code, '308' assert_equal resp['location'], 'https://www.alexwlchan.net/' end From 29c1616c6cf0c56bac01200d9906a730fc4cacb4 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 10:41:14 +0000 Subject: [PATCH 02/20] Test certificate expiry on more of my domains --- tests/test_https_certificate_expiry.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_https_certificate_expiry.rb b/tests/test_https_certificate_expiry.rb index f714d165b..cf61cd772 100644 --- a/tests/test_https_certificate_expiry.rb +++ b/tests/test_https_certificate_expiry.rb @@ -39,4 +39,24 @@ def test_books def test_analytics assert days_to_expiry('analytics.alexwlchan.net') > 14 end + + def test_www + assert days_to_expiry('www.alexwlchan.net') > 14 + end + + def test_dotcom + assert days_to_expiry('alexwlchan.com') > 14 + end + + def test_www_dotcom + assert days_to_expiry('www.alexwlchan.com') > 14 + end + + def test_dotcouk + assert days_to_expiry('alexwlchan.co.uk') > 14 + end + + def test_www_dotcouk + assert days_to_expiry('www.alexwlchan.co.uk') > 14 + end end From cb574577415798446578d80559684a9169ee711f Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 10:48:25 +0000 Subject: [PATCH 03/20] Add top-level comments to all my tests --- tests/test_alternate_domains.rb | 9 +++++++-- tests/test_analytics.rb | 4 +++- tests/test_https_certificate_expiry.rb | 5 +++-- tests/test_mastodon.rb | 8 ++++++-- tests/test_site_is_up.rb | 4 +++- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/test_alternate_domains.rb b/tests/test_alternate_domains.rb index fb081fa11..a4f41b4a6 100644 --- a/tests/test_alternate_domains.rb +++ b/tests/test_alternate_domains.rb @@ -1,5 +1,10 @@ -# These tests check that alternative domains will redirect to my main website, -# including over HTTP and variants of alexwlchan.net. +# I have a variety of alternative domains and subdomains which all +# redirect to my main website. +# +# e.g. www.alexwlchan.net and alexwlchan.com +# +# These tests check that all of these domains are redirecting to +# my website correctly, including over HTTP and HTTPS. require 'test/unit' diff --git a/tests/test_analytics.rb b/tests/test_analytics.rb index ff592cbf2..06b8e1478 100644 --- a/tests/test_analytics.rb +++ b/tests/test_analytics.rb @@ -1,4 +1,6 @@ -# Tests for analytics.alexwlchan.net +# I run a tracking pixel and analytics dashboard at analytics.alexwlchan.net +# +# This file has tests specific to this application. require 'test/unit' diff --git a/tests/test_https_certificate_expiry.rb b/tests/test_https_certificate_expiry.rb index cf61cd772..6dc4585ac 100644 --- a/tests/test_https_certificate_expiry.rb +++ b/tests/test_https_certificate_expiry.rb @@ -1,3 +1,6 @@ +# Check that all of my HTTPS certificates are valid and not about +# to expire. + require 'date' require 'net/http' require 'test/unit' @@ -25,8 +28,6 @@ def days_to_expiry(hostname) seconds_until / (24 * 60 * 60) end -# Check that all of my HTTPS certificates are valid and not about -# to expire. class TestHttpsCertificateExpiry < Test::Unit::TestCase def test_apex assert days_to_expiry('alexwlchan.net') > 14 diff --git a/tests/test_mastodon.rb b/tests/test_mastodon.rb index f6a5f840f..224339da3 100644 --- a/tests/test_mastodon.rb +++ b/tests/test_mastodon.rb @@ -1,9 +1,13 @@ +# Tests for some routes that are used to find my Mastodon server, +# which is at social.alexwlchan.net. +# +# My handle is "@alex@alexwlchan.net", so federating servers need +# to be able to find their way to my actual server. + require 'test/unit' require_relative 'utils' -# Tests for some routes that are used by Mastodon to find -# my social.alexwlchan.net server class TestMastodon < Test::Unit::TestCase def test_host_meta resp = get_url('https://alexwlchan.net/.well-known/host-meta') diff --git a/tests/test_site_is_up.rb b/tests/test_site_is_up.rb index fe2e28998..da1ab00ce 100644 --- a/tests/test_site_is_up.rb +++ b/tests/test_site_is_up.rb @@ -1,8 +1,10 @@ +# These are vanilla uptime tests -- load a page and check that it +# gets a 200 OK with some expected text. + require 'test/unit' require_relative 'utils' -# Basic tests for my websites. class TestSite < Test::Unit::TestCase # Load the alexwlchan.net homepage def test_load_homepage From 0ff062772af11587fcd27b878847cafb636a9e53 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 11:28:13 +0000 Subject: [PATCH 04/20] Add some tests for my HTTP Security Headers --- .rubocop.yml | 4 +++ tests/run_all_tests.rb | 1 + tests/test_http_security_headers.rb | 49 +++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/test_http_security_headers.rb diff --git a/.rubocop.yml b/.rubocop.yml index 75b8beba5..2948d7e11 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -13,6 +13,10 @@ AllCops: # See https://stackoverflow.com/a/69421161/1558022 - 'vendor/**/*' +Layout/LineLength: + Exclude: + - tests/test_http_security_headers.rb + Lint/MissingSuper: Enabled: false diff --git a/tests/run_all_tests.rb b/tests/run_all_tests.rb index 7d740643b..6810ecdac 100644 --- a/tests/run_all_tests.rb +++ b/tests/run_all_tests.rb @@ -17,6 +17,7 @@ require_relative 'test_site_is_up' require_relative 'test_alternate_domains' require_relative 'test_analytics' +require_relative 'test_http_security_headers' require_relative 'test_https_certificate_expiry' require_relative 'test_mastodon' require_relative 'test_errors' diff --git a/tests/test_http_security_headers.rb b/tests/test_http_security_headers.rb new file mode 100644 index 000000000..368693ee0 --- /dev/null +++ b/tests/test_http_security_headers.rb @@ -0,0 +1,49 @@ +# Check that all of my websites have the right HTTP Security headers. + +require 'date' +require 'net/http' +require 'test/unit' + +require_relative 'utils' + +class TestHttpSecurityHeaders < Test::Unit::TestCase + def test_alexwlchan + resp = get_url('https://alexwlchan.net') + + assert_equal resp['Content-Security-Policy'], + "default-src 'self' 'unsafe-inline' https://youtube-nocookie.com https://www.youtube-nocookie.com; script-src 'self' 'unsafe-inline'; connect-src https://analytics.alexwlchan.net; img-src 'self' 'unsafe-inline' data:" + + assert_equal resp['Permissions-Policy'], + 'geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), vibrate=(), payment=()' + + assert_equal resp['Referrer-Policy'], 'no-referrer-when-downgrade' + + assert_equal resp['Strict-Transport-Security'], 'max-age=31536000; includeSubDomains' + + assert_equal resp['X-Content-Type-Options'], 'nosniff' + + assert_equal resp['X-Frame-Options'], 'ALLOWALL' + + assert_equal resp['X-Xss-Protection'], '1; mode=block' + end + + def test_books + resp = get_url('https://books.alexwlchan.net') + + assert_equal resp['Content-Security-Policy'], + "default-src ; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline' https://unpkg.com/;connect-src https://analytics.alexwlchan.net/;" + + assert_equal resp['Permissions-Policy'], + 'geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), vibrate=(), payment=()' + + assert_equal resp['Referrer-Policy'], 'no-referrer-when-downgrade' + + assert_equal resp['Strict-Transport-Security'], 'max-age=31536000; includeSubDomains' + + assert_equal resp['X-Content-Type-Options'], 'nosniff' + + assert_equal resp['X-Frame-Options'], 'DENY' + + assert_equal resp['X-Xss-Protection'], '1; mode=block' + end +end From 6486688ed64c645c06a3ad7ba70387a796d3e7c8 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 12:40:02 +0000 Subject: [PATCH 05/20] Add a step to deploy from GitHub Actions to Linode --- .github/workflows/build_site.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index 861da1ba4..2db79a069 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -131,6 +131,17 @@ jobs: SITE_URL: ${{steps.deploy_to_netlify.outputs.deploy-url}} run: echo "$SITE_URL" + - name: Deploy to Linode + uses: burnett01/rsync-deployments@7.0.1 + # if: github.ref == 'refs/heads/main' + with: + switches: -avzr --delete --exclude="" --include="" --filter="" + path: _site/ + remote_path: repos/alexwlchan.net/_site + remote_host: "178.79.134.59" + remote_user: alexwlchan + remote_key: ${{ secrets.DEPLOY_KEY }} + - name: "Run the website tests" run: ruby tests/run_all_tests.rb From eef98ee2e8f9ef08ae582c6dc479410890657680 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 12:50:09 +0000 Subject: [PATCH 06/20] Add some initial configuration with Caddy I'm serving the site from www.alexwlchan.net for testing purposes, until everything is ready to flip over my apex domain. --- Caddyfile | 74 ++++++++++++++++++++++++++++++++++++++- caddy/gone.Caddyfile | 0 caddy/redirects.Caddyfile | 0 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 caddy/gone.Caddyfile create mode 100644 caddy/redirects.Caddyfile diff --git a/Caddyfile b/Caddyfile index 0c7d4282b..37c5c1db9 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,6 +1,78 @@ +www.alexwlchan.net { + import caddy/redirects.Caddyfile + import caddy/gone.Caddyfile + + # Enable compression for responses + encode zstd gzip + + # I can set long-lived caches on all these static assets because I treat + # most files as immutable by filename -- if I modify a file, I'll upload a new + # file with a different name, so it won't be a cache hit. + # + # The one exception is CSS files, but I cache-bust those by passing a query parameter + # that includes the hash of my CSS source. + @cached { + path /favicons/* /files/* /headers/* /images/* /static/* /theme/* + } + + header @cached { + Cache-Control "public, max-age=31536000" + } + + # Add some security headers. + # I test my security headers with https://securityheaders.com/ + header { + # https://scotthelme.co.uk/hardening-your-http-response-headers/#content-security-policy + Content-Security-Policy "default-src 'self' 'unsafe-inline' https://youtube-nocookie.com https://www.youtube-nocookie.com; script-src 'self' 'unsafe-inline'; connect-src https://analytics.alexwlchan.net; img-src 'self' 'unsafe-inline' data:" + + # https://scotthelme.co.uk/a-new-security-header-feature-policy/ + # https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/ + Permissions-Policy "geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), vibrate=(), payment=()" + + # https://scotthelme.co.uk/a-new-security-header-referrer-policy/ + Referrer-Policy "no-referrer-when-downgrade" + + # https://scotthelme.co.uk/hardening-your-http-response-headers/#strict-transport-security + Strict-Transport-Security "max-age=31536000; includeSubDomains" + + # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options + X-Content-Type-Options "nosniff" + + # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options + X-Frame-Options "ALLOWALL" + + # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-xss-protection + X-Xss-Protection "1; mode=block" + } + + root * /home/alexwlchan/repos/alexwlchan.net/_site + file_server + + log { + output file /var/log/caddy/alexwlchan.log + + # This removes personally identifiable information (PII) from the logs, + # in particular: + # + # - IP addresses + # - User-Agents + # + # They're not useful to me so I might as well discard them. + format filter { + wrap json + fields { + request>remote_ip delete + request>remote_port delete + request>client_ip delete + request>headers>User-Agent delete + } + } + } +} + # This redirects my other domain names to my primary domain name, # to avoid diluting traffic between them. -alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk, www.alexwlchan.net { +alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk { redir https://alexwlchan.net{uri} permanent log { diff --git a/caddy/gone.Caddyfile b/caddy/gone.Caddyfile new file mode 100644 index 000000000..e69de29bb diff --git a/caddy/redirects.Caddyfile b/caddy/redirects.Caddyfile new file mode 100644 index 000000000..e69de29bb From ce50fa52ac2441f383079c8e6021229bfa1cbefc Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 12:54:08 +0000 Subject: [PATCH 07/20] Add the ideas-for-inclusive-events mini-site --- Caddyfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Caddyfile b/Caddyfile index 37c5c1db9..3c9ec75a9 100644 --- a/Caddyfile +++ b/Caddyfile @@ -44,6 +44,18 @@ www.alexwlchan.net { # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-xss-protection X-Xss-Protection "1; mode=block" } + + # This is a mini-site that was built separately and I've just uploaded + # the HTML files to my web server. + # + # Eventually I'd like to fold this into the main site build, but + # I'm not there yet. + # + # See https://github.com/alexwlchan/ideas-for-inclusive-events/issues/9 + handle /ideas-for-inclusive-events/* { + root * /home/alexwlchan/repos/ideas-for-inclusive-events + file_server + } root * /home/alexwlchan/repos/alexwlchan.net/_site file_server From 6bb065f7ea147787383998e6c4c2cd5297b84250 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 13:00:25 +0000 Subject: [PATCH 08/20] Add my custom error pages --- Caddyfile | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Caddyfile b/Caddyfile index 3c9ec75a9..546555d4c 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,4 +1,4 @@ -www.alexwlchan.net { +www.alexwlchan.net { import caddy/redirects.Caddyfile import caddy/gone.Caddyfile @@ -45,6 +45,19 @@ www.alexwlchan.net { X-Xss-Protection "1; mode=block" } + # If somebody gets a 404 or 410 error, respond with my custom error pages. + handle_errors 404 { + root * /home/alexwlchan/repos/alexwlchan.net/_site + rewrite * 404/index.html + file_server + } + + handle_errors 410 { + root * /home/alexwlchan/repos/alexwlchan.net/_site + rewrite * 410/index.html + file_server + } + # This is a mini-site that was built separately and I've just uploaded # the HTML files to my web server. # @@ -56,7 +69,8 @@ www.alexwlchan.net { root * /home/alexwlchan/repos/ideas-for-inclusive-events file_server } - + + # Run a static file server for anything not yet handled root * /home/alexwlchan/repos/alexwlchan.net/_site file_server From f14279a4cc42e7bc693b97e2831ca97cefee3c79 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 13:05:50 +0000 Subject: [PATCH 09/20] Remove my old Netlify config --- netlify.toml | 99 ---------------------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 netlify.toml diff --git a/netlify.toml b/netlify.toml deleted file mode 100644 index b1b13e0ff..000000000 --- a/netlify.toml +++ /dev/null @@ -1,99 +0,0 @@ -[build] - base = "" - publish = "_site/" - -# I test my security headers with https://securityheaders.com/ -[[headers]] - for = "/*" - - [headers.values] - # https://scotthelme.co.uk/hardening-your-http-response-headers/#content-security-policy - # - # - youtube-nocookie.com is used for YouTube embeds - # - analytics.alexwlchan.net is used for my tracking pixel - # - content-security-policy = """\ - default-src 'self' 'unsafe-inline' \ - https://youtube-nocookie.com https://www.youtube-nocookie.com; \ - script-src 'self' 'unsafe-inline'; \ - connect-src https://analytics.alexwlchan.net; \ - img-src 'self' 'unsafe-inline' data: \ - """ - - # https://scotthelme.co.uk/a-new-security-header-feature-policy/ - # https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/ - permissions-policy = """\ - geolocation=(), \ - midi=(), \ - notifications=(), \ - push=(), \ - sync-xhr=(), \ - microphone=(), \ - camera=(), \ - magnetometer=(), \ - gyroscope=(), \ - vibrate=(), - payment=() \ - """ - - # https://scotthelme.co.uk/a-new-security-header-referrer-policy/ - referrer-policy = "no-referrer-when-downgrade" - - # This header tells browsers to always use HTTPS for the site. - # - # See https://scotthelme.co.uk/hardening-your-http-response-headers/#strict-transport-security - strict-transport-security = "max-age=31536000; includeSubDomains" - - # This headers tells browsers that our content can't be loaded in frames. - # - # See https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options - x-content-type-options = "nosniff" - - # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options - x-frame-options = "ALLOWALL" - - # https://scotthelme.co.uk/hardening-your-http-response-headers/#x-xss-protection - x-xss-protection = "1; mode=block" - -# I can set long-lived caches on all these static assets because I treat -# most files as immutable by filename -- if I modify a file, I'll upload a new -# file with a different name, so it won't be a cache hit. -# -# The one exception is CSS files, but I cache-bust those by passing a query parameter -# that includes the hash of my CSS source. - -[[headers]] - for = "/files/*" - - [headers.values] - cache-control = "public, max-age=31536000" - -[[headers]] - for = "/images/*" - - [headers.values] - cache-control = "public, max-age=31536000" - -[[headers]] - for = "/favicons/*" - - [headers.values] - cache-control = "public, max-age=31536000" - -[[headers]] - for = "/static/*" - - [headers.values] - cache-control = "public, max-age=31536000" - -[[headers]] - for = "/headers/*" - - [headers.values] - cache-control = "public, max-age=31536000" - -[[headers]] - for = "/theme/*" - - [headers.values] - cache-control = "public, max-age=31536000" From 3b7c762b34288df5486c73b3ad73427a9626ccd2 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 13:11:12 +0000 Subject: [PATCH 10/20] Replace all my Netlify redirects with Caddy config * The `_redirects` file is split into two Caddy files * The 400 page is gone and just part of the server config now --- Caddyfile | 17 +- _config.yml | 11 - caddy/gone.Caddyfile | 114 ++++ caddy/redirects.Caddyfile | 1187 ++++++++++++++++++++++++++++++++++ src/400.md | 10 - src/_redirects | 1275 ------------------------------------- tests/test_errors.rb | 2 +- 7 files changed, 1317 insertions(+), 1299 deletions(-) delete mode 100644 src/400.md delete mode 100644 src/_redirects diff --git a/Caddyfile b/Caddyfile index 546555d4c..0f6440f77 100644 --- a/Caddyfile +++ b/Caddyfile @@ -18,7 +18,7 @@ www.alexwlchan.net { header @cached { Cache-Control "public, max-age=31536000" } - + # Add some security headers. # I test my security headers with https://securityheaders.com/ header { @@ -45,7 +45,7 @@ www.alexwlchan.net { X-Xss-Protection "1; mode=block" } - # If somebody gets a 404 or 410 error, respond with my custom error pages. + # If somebody gets a 4xx error, respond with my custom error pages. handle_errors 404 { root * /home/alexwlchan/repos/alexwlchan.net/_site rewrite * 404/index.html @@ -57,6 +57,19 @@ www.alexwlchan.net { rewrite * 410/index.html file_server } + + # If somebody is trying to look for PHP pages on my site, and WordPress + # pages in particular, they probably have nefarious goals. + # + # This is mostly automated bots -- serve them my minimal 400 Bad Request + # error rather than the complete 404 Not Found page. + @spam { + path /.env /index.php /xmlrpc.php /wp-* /blog/wp-* /cms/wp-* /shop/wp-* /site/wp-* /test/wp-* /wordpress/wp-* /wp/wp-* /wp2/wp-* /zb_system/* + } + + handle @spam { + respond "400 Bad Request" 400 + } # This is a mini-site that was built separately and I've just uploaded # the HTML files to my web server. diff --git a/_config.yml b/_config.yml index 2d4d680f7..235ede170 100644 --- a/_config.yml +++ b/_config.yml @@ -7,17 +7,6 @@ source: "src" destination: "_site" -include: - - # This file is used by Netlify to define redirects. I also use it - # to create the custom 404 page. - # - # Jekyll ignores files that start with an underscore, so I need to - # include this explicitly. - # - # See https://docs.netlify.com/routing/redirects/ - - "_redirects" - # ===================== # Build command options # ===================== diff --git a/caddy/gone.Caddyfile b/caddy/gone.Caddyfile index e69de29bb..28212f015 100644 --- a/caddy/gone.Caddyfile +++ b/caddy/gone.Caddyfile @@ -0,0 +1,114 @@ +handle /analytics/a.js { + error 410 +} + +handle /theme/style-sample { + error 410 +} + +handle /images/talks/oxford_git.jpg { + error 410 +} + +handle /images/talks/suspicious_minds.jpg { + error 410 +} + +handle /images/talks/worst_intent.png { + error 410 +} + +handle /projects/ { + error 410 +} + + + + + +# == Posts which I've removed == # + +# I took this post down in June 2023 +handle /2019/removing-the-drm-from-my-kindle-books/ { + error 410 +} + +handle /2019/08/removing-the-drm-from-my-kindle-books/ { + error 410 +} + +# I removed these posts because they were short link posts of minimal value + +handle /2014/12/war-on-christmas/ { + error 410 +} + +handle /2014/war-on-christmas/ { + error 410 +} + +handle /2015/01/bbfc-podcast/ { + error 410 +} + +handle /2015/bbfc-podcast/ { + error 410 +} + +handle /2015/01/govuk/ { + error 410 +} + +handle /2015/govuk/ { + error 410 +} + +handle /2015/01/kings-cross-problems/ { + error 410 +} + +handle /2015/kings-cross-problems/ { + error 410 +} + + + + + + + +# == Files which I've removed == # + +handle /files/boiler_instructions.pdf { + error 410 +} + +handle /files/colossus.pdf { + error 410 +} + +handle /files/ga-privacy.txt { + error 410 +} + +handle /files/rock-crushers.txt { + error 410 +} + +handle /talks/campug_git.pdf { + error 410 +} + +handle /talks/colossus_operators.jpg { + error 410 +} + +handle /talks/hypothesis_cover.png { + error 410 +} + +handle /talks/pyconuk2016_cover.png { + error 410 +} + +# == # diff --git a/caddy/redirects.Caddyfile b/caddy/redirects.Caddyfile index e69de29bb..13c8970cb 100644 --- a/caddy/redirects.Caddyfile +++ b/caddy/redirects.Caddyfile @@ -0,0 +1,1187 @@ +# Redirects for my Mastodon instance +# +# See https://jacobian.org/til/my-mastodon-instance/ +redir /.well-known/host-meta* https://social.alexwlchan.net/.well-known/host-meta permanent +redir /.well-known/webfinger* https://social.alexwlchan.net/.well-known/webfinger permanent +redir /.well-known/nodeinfo* https://social.alexwlchan.net/.well-known/nodeinfo permanent + + + +# Redirects for common RSS-like URLs, or RSS URLs I've used in the past +redir /feed /atom.xml permanent +redir /rss /atom.xml permanent +redir /feed.xml /atom.xml permanent +redir /rss.xml /atom.xml permanent +redir /feeds/all.atom.xml /atom.xml permanent + + + +# Redirects for theming/style resources +redir /images/favicon.ico /favicon.ico permanent +redir /theme/favicon.ico /favicon.ico permanent +redir /theme/favicon.png /favicon.png permanent +redir /view/img/favicon.ico /favicon.ico permanent + +redir /apple-touch-icon.png /theme/apple-touch-icon.png permanent +redir /apple-touch-icon-precomposed.png /theme/apple-touch-icon.png permanent +redir /apple-touch-icon-120x120-precomposed.png /theme/apple-touch-icon.png permanent +redir /apple-touch-icon-120x120.png /theme/apple-touch-icon.png permanent +redir /apple-touch-icon-152x152-precomposed.png /theme/apple-touch-icon.png permanent +redir /apple-touch-icon-152x152.png /theme/apple-touch-icon.png permanent + +redir /images/apple-touch-icon.png /theme/apple-touch-icon.png permanent + + + + +# == Redirects for images == # + +redir /images/2014-04-20-veil.jpg /images/2014/deathly-veil.jpg permanent +redir /images/2014-04-20-veil.png /images/2014/deathly-veil.jpg permanent +redir /images/2014/galant_leg.jpg /images/2014/P8050572.jpg permanent +redir /images/2014/standing_desk.jpg /images/2014/P8050559.jpg permanent +redir /images/2014/standing_desk_side.jpg /images/2014/P8050561.jpg permanent +redir /images/notes/resophnotes-directory.png /images/2014/resophnotes-directory.png permanent +redir /images/notes/resophnotes-styled.png /images/2014/resophnotes-styled.png permanent +redir /images/notes/resophnotes-unstyled.png /images/2014/resophnotes-unstyled.png permanent + +redir /images/2015-05-github-blue.png /images/2015/github_blue.png permanent +redir /images/2015-05-github-chart.png /images/2015/github_chart.png permanent +redir /images/2015-05-github-green.png /images/2015/github_green.png permanent +redir /images/2015-09-cortex-disabled.PNG /images/2015/cortex-disabled.PNG permanent +redir /images/2015-09-pizzaexpress-disabled.PNG /images/2015/pizzaexpress-disabled.PNG permanent +redir /images/2015-09-vexelology-disabled.PNG /images/2015/vexelology-disabled.PNG permanent +redir /images/2015-09-vexelology-enabled.PNG /images/2015/vexelology-enabled.PNG permanent + +redir /images/notes/boiler-panel.png /images/2016/boiler-panel.png permanent +redir /images/notes/tricity-sie454.png /images/2016/tricity-sie454.png permanent +redir /images/2016-01-hp-ipod-1.jpg /images/2016/hp-ipod-1.jpg permanent +redir /images/2016-09-speech-to-text.jpg /images/2016/speech-to-text.jpg permanent +redir /images/2016-09-tech-bag.jpg /images/2016/tech-bag.jpg permanent +redir /images/specktre_demo_hex.png /images/2016/specktre_demo_hex.png permanent +redir /images/specktre_demo_sq.png /images/2016/specktre_demo_sq.png permanent +redir /images/specktre_demo_tr.png /images/2016/specktre_demo_tr.png permanent +redir /images/wiki-hexagons.png /images/2016/wiki-hexagons.png permanent +redir /images/wiki-squares.png /images/2016/wiki-squares.png permanent +redir /images/wiki-triangles.png /images/2016/wiki-triangles.png permanent + +redir /images/2017/P5280917_2x.jpg /images/2017/P5280917.jpg permanent +redir /slides/pyconuk_asexuality.png /images/2017/pyconuk_asexuality.jpg permanent + +redir /images/2022/acme_1x.jpg /images/2022/acme_corporation_delicious_1x.png permanent +redir /images/2022/acme_2x.jpg /images/2022/acme_corporation_delicious_2x.png permanent +redir /images/2022/prismic-dashboard_1x.png /images/2022/prismic_dashboard_1x.png permanent +redir /images/2022/prismic-dashboard_2x.png /images/2022/prismic_dashboard_2x.png permanent + +redir /images/2018/worst-intent.png /images/cards/2018/assume-worst-intent.png permanent +redir /images/2022/10-of-cards.jpg /images/cards/2022/tin-anniversary.jpg permanent +redir /images/2022/koa-redactions-card.jpg /images/cards/2022/koa-logger-redactions.jpg permanent +redir /images/2022/bodleian.jpg /images/cards/2022/library-lookup.jpg permanent +redir /images/2022/marquee_rocket_card.jpg /images/cards/2022/marquee-rocket.jpg permanent +redir /images/2022/screenshot_card.png /images/cards/2022/screenshots.png permanent +redir /images/2022/guest_headers_card.png /images/cards/2022/no-cute.png permanent + +redir /images/pyconuk-speaking.jpg /images/projects/pyconuk-speaking.jpg permanent + +# === # + + + + + + +# == Redirects for pages which aren't attached to blog posts == + +redir /2018/03/plumbers-guide-intro/ /a-plumbers-guide-to-git/ permanent +redir /a-plumbers-guide-to-git/1 /a-plumbers-guide-to-git/1-the-git-object-store/ permanent +redir /a-plumbers-guide-to-git/1/ /a-plumbers-guide-to-git/1-the-git-object-store/ permanent +redir /a-plumbers-guide-to-git/2 /a-plumbers-guide-to-git/2-blobs-and-trees/ permanent +redir /a-plumbers-guide-to-git/2/ /a-plumbers-guide-to-git/2-blobs-and-trees/ permanent +redir /a-plumbers-guide-to-git/3 /a-plumbers-guide-to-git/3-context-from-commits/ permanent +redir /a-plumbers-guide-to-git/3/ /a-plumbers-guide-to-git/3-context-from-commits/ permanent +redir /a-plumbers-guide-to-git/4 /a-plumbers-guide-to-git/4-refs-and-branches/ permanent +redir /a-plumbers-guide-to-git/4/ /a-plumbers-guide-to-git/4-refs-and-branches/ permanent + +redir /archives /articles/ permanent +redir /archives/ /articles/ permanent +redir /archive /articles/ permanent +redir /archive/ /articles/ permanent +redir /all-posts-by-tag/ /articles/ permanent +redir /all-posts/ /articles/ permanent +redir /posts/ /articles/ permanent +redir /writing/ /articles/ permanent + +redir /about / permanent +redir /about/ / permanent + +redir /page/* /articles/ permanent + +redir /talks /tags/talks/ permanent + +redir /tag /tags/ permanent +redir /tag/ /tags/ permanent +redir /notes /til/ permanent + +# === # + + + + + +# == Redirects for individual blog posts == + +# Removing the /blog prefix +redir /blog/2012/12/hypercritical/ /2012/hypercritical/ permanent +redir /blog/2013/03/pinboard-backups/ /2013/pinboard-backups/ permanent +redir /blog/2013/03/candybar/ /2013/candybar/ permanent + +# Moving everything under /talks to dated entries +redir /talks/hypothesis-intro/ /2016/hypothesis-intro/ permanent +redir /talks/pyconuk2016 /2016/pyconuk2016/ permanent +redir /talks/qcon2017 /2017/qcon2017/ permanent + +# Moving everything under /notes to dated entries +redir /notes/custom-css-resophnotes /2014/custom-css-resophnotes/ permanent +redir /notes/custom-css-resophnotes/ /2014/custom-css-resophnotes/ permanent +redir /notes/tricity-sie454-clock /til/2016/tricity-sie454-clock/ permanent +redir /notes/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ permanent +redir /notes/worcester-28cdi-clock /til/2016/worcester-28cdi-clock/ permanent +redir /notes/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ permanent + +# Cases where the slug on the blog post changed after I'd published +redir /2016/05/safely-deleting-a-file-called-rf-/ /2016/safely-deleting-a-file-called-rf/ permanent +redir /2016/05/safely-deleting-a-file-called-rf- /2016/safely-deleting-a-file-called-rf/ permanent +redir /2017/01/a-python-interface-to-ao3 /2017/experiments-with-ao3-and-python/ permanent +redir /2017/01/a-python-interface-to-ao3/ /2017/experiments-with-ao3-and-python/ permanent +redir /2021/01/what-year-it-it/ /2021/what-year-is-it/ permanent +redir /2017/04/accessibility-at-alterconf /2017/lessons-from-alterconf/ permanent +redir /2017/04/accessibility-at-alterconf/ /2017/lessons-from-alterconf/ permanent + +# I put up a post linking to my slides, then a month later I put up +# another post with an annotated set of slides and notes. +redir /2019/09/slides-for-pycon-uk-2019/ /2019/sans-io-programming/ permanent + +# This was a broken redirect I had for a while +redir /2016/07/hypothesis-intro/ /2016/hypothesis-intro/ permanent + +# == # + + + + + +# == Redirects for files == # + +redir /maths/part-ib/geometry.pdf /files/geometry.pdf permanent +redir /maths/part-ib/linear-algebra.pdf /files/linear-algebra.pdf permanent +redir /maths/part-ib/met-top-spaces.pdf /files/met-top-spaces.pdf permanent + +redir /maths/geometry.pdf /files/geometry.pdf permanent +redir /maths/linear-algebra.pdf /files/linear-algebra.pdf permanent +redir /maths/met-top-spaces.pdf /files/met-top-spaces.pdf permanent + +# This is some changes I made in Christmas 2022 to tidy up the structure +# of the 'files' directory; I had a bunch of files which were in the +# top-level, and not in the per-year folders I'd started using for +# newer files. +redir /files/find_untagged_posts.py /files/2013/find_untagged_posts.py permanent +redir /files/untagged-posts.py /files/2013/find_untagged_posts.py permanent + +redir /files/curlparser.py /files/2014/curlparser.py permanent +redir /files/curlparser-examples.txt /files/2014/curlparser-examples.txt permanent +redir /files/open-recent-screenshots.zip /files/2014/open-recent-screenshots.zip permanent + +redir /files/gitignore_global.txt /files/2015/gitignore_global.txt permanent +redir /files/readinglist.py /files/2015/readinglist.py permanent +redir /skeletor/skeletor_jan2015.pdf /files/2015/skeletor_jan2015.pdf permanent +redir /skeletor/skeletor_jan2015.png /files/2015/skeletor_jan2015.png permanent + +redir /files/send_to_kindle.py /files/2016/send_to_kindle.py permanent +redir /skeletor/skeletor_jan2016.pdf /files/2016/skeletor_jan2016.pdf permanent +redir /skeletor/skeletor_jan2016.png /files/2016/skeletor_jan2016.png permanent +redir /skeletor/skeletor_jan2016.tex /files/2016/skeletor_jan2016.tex permanent +redir /files/tilings.py /files/2016/tilings.py permanent + +redir /files/crossness_beam.mp4 /files/2017/crossness_beam.mp4 permanent +redir /videos/crossness_beam.mp4 /files/2017/crossness_beam.mp4 permanent +redir /files/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 permanent +redir /videos/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 permanent +redir /slides/docopt/docopt_slides.pdf /files/2017/docopt_slides.pdf permanent +redir /talks/hypothesis_qcon17.pdf /files/2017/hypothesis_qcon17.pdf permanent +redir /slides/privilege_inclusion/privilege_inclusion_slides.pdf /files/2017/privilege_inclusion_slides.pdf permanent +redir /files/tweet_templates.key /files/2017/tweet_templates.key permanent +redir /files/tweet_templates.ppt /files/2017/tweet_templates.ppt permanent + +redir /slides/anti_social_media/anti_social_media_slides.pdf /files/2018/anti_social_media_slides.pdf permanent +redir /slides/assume_worst_intent.pdf /files/2018/assume_worst_intent.pdf permanent +redir /files/assume_worst_intent.pdf /files/2018/assume_worst_intent.pdf permanent +redir /talks/suspicious-minds.pdf /files/2018/suspicious-minds.pdf permanent + +redir /files/curbcut-monkigras.pdf /files/2019/curbcut-monkigras.pdf permanent +redir /files/example_loan.xlsx /files/2019/example_loan.xlsx permanent +redir /files/pyconuk_2019_sans_io.pdf /files/2019/pyconuk_2019_sans_io.pdf permanent +redir /files/wcag-black-and-white.pdf /files/2019/wcag-black-and-white.pdf permanent + +redir /skeletor/skeletor_feb2020.pdf /files/2020/skeletor_feb2020.pdf permanent +redir /skeletor/skeletor_feb2020.png /files/2020/skeletor_feb2020.png permanent +redir /skeletor/skeletor_feb2020.tex /files/2020/skeletor_feb2020.tex permanent + +# I have no idea why this particular slide is so popular and getting +# so many 404s, but adding a redirect for it. +redir /talks/hypothesis-intro/hypothesis-slide004.png /images/2016/hypothesis-intro/slide4.png permanent +redir /talks/hypothesis-intro/hypothesis-slide029.png /images/2016/hypothesis-intro/slide29.png permanent + +# === # + + + + + +# == Redirects for slides == # + +redir /slides/sans_io/sans_io.001.jpg /images/2019/sans-io-programming/slide1.jpg permanent +redir /slides/sans_io/sans_io.002.jpg /images/2019/sans-io-programming/slide2.jpg permanent +redir /slides/sans_io/sans_io.003.jpg /images/2019/sans-io-programming/slide3.jpg permanent +redir /slides/sans_io/sans_io.004.jpg /images/2019/sans-io-programming/slide4.jpg permanent +redir /slides/sans_io/sans_io.005.jpg /images/2019/sans-io-programming/slide5.jpg permanent +redir /slides/sans_io/sans_io.006.jpg /images/2019/sans-io-programming/slide6.jpg permanent +redir /slides/sans_io/sans_io.007.jpg /images/2019/sans-io-programming/slide7.jpg permanent +redir /slides/sans_io/sans_io.008.jpg /images/2019/sans-io-programming/slide8.jpg permanent +redir /slides/sans_io/sans_io.009.jpg /images/2019/sans-io-programming/slide9.jpg permanent +redir /slides/sans_io/sans_io.010.jpg /images/2019/sans-io-programming/slide10.jpg permanent +redir /slides/sans_io/sans_io.011.jpg /images/2019/sans-io-programming/slide11.jpg permanent +redir /slides/sans_io/sans_io.012.jpg /images/2019/sans-io-programming/slide12.jpg permanent +redir /slides/sans_io/sans_io.013.jpg /images/2019/sans-io-programming/slide13.jpg permanent +redir /slides/sans_io/sans_io.014.jpg /images/2019/sans-io-programming/slide14.jpg permanent +redir /slides/sans_io/sans_io.015.jpg /images/2019/sans-io-programming/slide15.jpg permanent +redir /slides/sans_io/sans_io.016.jpg /images/2019/sans-io-programming/slide16.jpg permanent +redir /slides/sans_io/sans_io.017.jpg /images/2019/sans-io-programming/slide17.jpg permanent +redir /slides/sans_io/sans_io.018.jpg /images/2019/sans-io-programming/slide18.jpg permanent +redir /slides/sans_io/sans_io.019.jpg /images/2019/sans-io-programming/slide19.jpg permanent +redir /slides/sans_io/sans_io.020.jpg /images/2019/sans-io-programming/slide20.jpg permanent +redir /slides/sans_io/sans_io.021.jpg /images/2019/sans-io-programming/slide21.jpg permanent +redir /slides/sans_io/sans_io.022.jpg /images/2019/sans-io-programming/slide22.jpg permanent +redir /slides/sans_io/sans_io.023.jpg /images/2019/sans-io-programming/slide23.jpg permanent +redir /slides/sans_io/sans_io.024.jpg /images/2019/sans-io-programming/slide24.jpg permanent +redir /slides/sans_io/sans_io.025.jpg /images/2019/sans-io-programming/slide25.jpg permanent +redir /slides/sans_io/sans_io.026.jpg /images/2019/sans-io-programming/slide26.jpg permanent +redir /slides/sans_io/sans_io.027.jpg /images/2019/sans-io-programming/slide27.jpg permanent +redir /slides/sans_io/sans_io.028.jpg /images/2019/sans-io-programming/slide28.jpg permanent +redir /slides/sans_io/sans_io.029.jpg /images/2019/sans-io-programming/slide29.jpg permanent +redir /slides/sans_io/sans_io.030.jpg /images/2019/sans-io-programming/slide30.jpg permanent +redir /slides/sans_io/sans_io.031.jpg /images/2019/sans-io-programming/slide31.jpg permanent +redir /slides/sans_io/sans_io.032.jpg /images/2019/sans-io-programming/slide32.jpg permanent + +redir /slides/hypothesis-intro/hypothesis-intro.000.png /images/2016/hypothesis-intro/slide0.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.001.png /images/2016/hypothesis-intro/slide1.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.002.png /images/2016/hypothesis-intro/slide2.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.003.png /images/2016/hypothesis-intro/slide3.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.004.png /images/2016/hypothesis-intro/slide4.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.005.png /images/2016/hypothesis-intro/slide5.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.006.png /images/2016/hypothesis-intro/slide6.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.007.png /images/2016/hypothesis-intro/slide7.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.008.png /images/2016/hypothesis-intro/slide8.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.009.png /images/2016/hypothesis-intro/slide9.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.010.png /images/2016/hypothesis-intro/slide10.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.011.png /images/2016/hypothesis-intro/slide11.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.012.png /images/2016/hypothesis-intro/slide12.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.013.png /images/2016/hypothesis-intro/slide13.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.014.png /images/2016/hypothesis-intro/slide14.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.015.png /images/2016/hypothesis-intro/slide15.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.016.png /images/2016/hypothesis-intro/slide16.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.017.png /images/2016/hypothesis-intro/slide17.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.018.png /images/2016/hypothesis-intro/slide18.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.019.png /images/2016/hypothesis-intro/slide19.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.020.png /images/2016/hypothesis-intro/slide20.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.021.png /images/2016/hypothesis-intro/slide21.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.022.png /images/2016/hypothesis-intro/slide22.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.023.png /images/2016/hypothesis-intro/slide23.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.024.png /images/2016/hypothesis-intro/slide24.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.025.png /images/2016/hypothesis-intro/slide25.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.026.png /images/2016/hypothesis-intro/slide26.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.027.png /images/2016/hypothesis-intro/slide27.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.028.png /images/2016/hypothesis-intro/slide28.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.029.png /images/2016/hypothesis-intro/slide29.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.030.png /images/2016/hypothesis-intro/slide30.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.031.png /images/2016/hypothesis-intro/slide31.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.032.png /images/2016/hypothesis-intro/slide32.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.033.png /images/2016/hypothesis-intro/slide33.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.034.png /images/2016/hypothesis-intro/slide34.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.035.png /images/2016/hypothesis-intro/slide35.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.036.png /images/2016/hypothesis-intro/slide36.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.037.png /images/2016/hypothesis-intro/slide37.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.038.png /images/2016/hypothesis-intro/slide38.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.039.png /images/2016/hypothesis-intro/slide39.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.040.png /images/2016/hypothesis-intro/slide40.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.041.png /images/2016/hypothesis-intro/slide41.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.042.png /images/2016/hypothesis-intro/slide42.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.043.png /images/2016/hypothesis-intro/slide43.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.044.png /images/2016/hypothesis-intro/slide44.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.045.png /images/2016/hypothesis-intro/slide45.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.046.png /images/2016/hypothesis-intro/slide46.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.047.png /images/2016/hypothesis-intro/slide47.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.048.png /images/2016/hypothesis-intro/slide48.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.049.png /images/2016/hypothesis-intro/slide49.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.050.png /images/2016/hypothesis-intro/slide50.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.051.png /images/2016/hypothesis-intro/slide51.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.052.png /images/2016/hypothesis-intro/slide52.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.053.png /images/2016/hypothesis-intro/slide53.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.054.png /images/2016/hypothesis-intro/slide54.png permanent +redir /slides/hypothesis-intro/hypothesis-intro.055.png /images/2016/hypothesis-intro/slide55.png permanent + +redir /slides/docopt/docopt.001.png /images/2017/ode-to-docopt/slide1.png permanent +redir /slides/docopt/docopt.002.png /images/2017/ode-to-docopt/slide2.png permanent +redir /slides/docopt/docopt.003.png /images/2017/ode-to-docopt/slide3.png permanent +redir /slides/docopt/docopt.004.png /images/2017/ode-to-docopt/slide4.png permanent +redir /slides/docopt/docopt.005.png /images/2017/ode-to-docopt/slide5.png permanent +redir /slides/docopt/docopt.006.png /images/2017/ode-to-docopt/slide6.png permanent +redir /slides/docopt/docopt.007.png /images/2017/ode-to-docopt/slide7.png permanent +redir /slides/docopt/docopt.008.png /images/2017/ode-to-docopt/slide8.png permanent +redir /slides/docopt/docopt.009.png /images/2017/ode-to-docopt/slide9.png permanent +redir /slides/docopt/docopt.010.png /images/2017/ode-to-docopt/slide10.png permanent +redir /slides/docopt/docopt.011.png /images/2017/ode-to-docopt/slide11.png permanent +redir /slides/docopt/docopt.012.png /images/2017/ode-to-docopt/slide12.png permanent +redir /slides/docopt/docopt.013.png /images/2017/ode-to-docopt/slide13.png permanent +redir /slides/docopt/docopt.014.png /images/2017/ode-to-docopt/slide14.png permanent + +redir /slides/ols_afterthought/ols_afterthought.001.png /images/2020/inclusion-cant-be-an-afterthought/slide1.png permanent +redir /slides/ols_afterthought/ols_afterthought.002.jpg /images/2020/inclusion-cant-be-an-afterthought/slide2.jpg permanent +redir /slides/ols_afterthought/ols_afterthought.003.jpg /images/2020/inclusion-cant-be-an-afterthought/slide3.jpg permanent +redir /slides/ols_afterthought/ols_afterthought.004.jpg /images/2020/inclusion-cant-be-an-afterthought/slide4.jpg permanent +redir /slides/ols_afterthought/ols_afterthought.005.png /images/2020/inclusion-cant-be-an-afterthought/slide5.png permanent +redir /slides/ols_afterthought/ols_afterthought.006.png /images/2020/inclusion-cant-be-an-afterthought/slide6.png permanent +redir /slides/ols_afterthought/ols_afterthought.007.jpg /images/2020/inclusion-cant-be-an-afterthought/slide7.jpg permanent +redir /slides/ols_afterthought/ols_afterthought.008.jpg /images/2020/inclusion-cant-be-an-afterthought/slide8.jpg permanent +redir /slides/ols_afterthought/ols_afterthought.009.png /images/2020/inclusion-cant-be-an-afterthought/slide9.png permanent +redir /slides/ols_afterthought/ols_afterthought.010.png /images/2020/inclusion-cant-be-an-afterthought/slide10.png permanent +redir /slides/ols_afterthought/ols_afterthought.011.png /images/2020/inclusion-cant-be-an-afterthought/slide11.png permanent + +redir /slides/privilege_inclusion/privilege_inclusion.001.png /images/2017/pyconuk-2017-privilege-inclusion/slide1.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.003.png /images/2017/pyconuk-2017-privilege-inclusion/slide3.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.004.png /images/2017/pyconuk-2017-privilege-inclusion/slide4.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.005.png /images/2017/pyconuk-2017-privilege-inclusion/slide5.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.006.png /images/2017/pyconuk-2017-privilege-inclusion/slide6.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.007.png /images/2017/pyconuk-2017-privilege-inclusion/slide7.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.008.png /images/2017/pyconuk-2017-privilege-inclusion/slide8.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.009.png /images/2017/pyconuk-2017-privilege-inclusion/slide9.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.010.png /images/2017/pyconuk-2017-privilege-inclusion/slide10.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.011.png /images/2017/pyconuk-2017-privilege-inclusion/slide11.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.012.png /images/2017/pyconuk-2017-privilege-inclusion/slide12.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.015.png /images/2017/pyconuk-2017-privilege-inclusion/slide15.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.016.png /images/2017/pyconuk-2017-privilege-inclusion/slide16.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.019.png /images/2017/pyconuk-2017-privilege-inclusion/slide19.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.020.png /images/2017/pyconuk-2017-privilege-inclusion/slide20.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.021.png /images/2017/pyconuk-2017-privilege-inclusion/slide21.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.022.png /images/2017/pyconuk-2017-privilege-inclusion/slide22.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.024.png /images/2017/pyconuk-2017-privilege-inclusion/slide24.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.025.png /images/2017/pyconuk-2017-privilege-inclusion/slide25.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.026.png /images/2017/pyconuk-2017-privilege-inclusion/slide26.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.027.png /images/2017/pyconuk-2017-privilege-inclusion/slide27.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.028.png /images/2017/pyconuk-2017-privilege-inclusion/slide28.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.029.png /images/2017/pyconuk-2017-privilege-inclusion/slide29.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.030.png /images/2017/pyconuk-2017-privilege-inclusion/slide30.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.031.png /images/2017/pyconuk-2017-privilege-inclusion/slide31.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.032.png /images/2017/pyconuk-2017-privilege-inclusion/slide32.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.033.png /images/2017/pyconuk-2017-privilege-inclusion/slide33.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.035.png /images/2017/pyconuk-2017-privilege-inclusion/slide35.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.036.png /images/2017/pyconuk-2017-privilege-inclusion/slide36.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.037.png /images/2017/pyconuk-2017-privilege-inclusion/slide37.png permanent +redir /slides/privilege_inclusion/privilege_inclusion.040.png /images/2017/pyconuk-2017-privilege-inclusion/slide40.png permanent + +redir /slides/curbcut_monkigras/curbcut_monkigras.001.jpg /images/2019/monki-gras-the-curb-cut-effect/slide1.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.002.jpg /images/2019/monki-gras-the-curb-cut-effect/slide2.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.003.jpg /images/2019/monki-gras-the-curb-cut-effect/slide3.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.004.jpg /images/2019/monki-gras-the-curb-cut-effect/slide4.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.005.jpg /images/2019/monki-gras-the-curb-cut-effect/slide5.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.006.jpg /images/2019/monki-gras-the-curb-cut-effect/slide6.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.007.png /images/2019/monki-gras-the-curb-cut-effect/slide7.png permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.008.png /images/2019/monki-gras-the-curb-cut-effect/slide8.png permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.009.png /images/2019/monki-gras-the-curb-cut-effect/slide9.png permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.010.png /images/2019/monki-gras-the-curb-cut-effect/slide10.png permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.011.jpg /images/2019/monki-gras-the-curb-cut-effect/slide11.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.012.jpg /images/2019/monki-gras-the-curb-cut-effect/slide12.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.013.jpg /images/2019/monki-gras-the-curb-cut-effect/slide13.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.014.jpg /images/2019/monki-gras-the-curb-cut-effect/slide14.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.015.jpg /images/2019/monki-gras-the-curb-cut-effect/slide15.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.016.jpg /images/2019/monki-gras-the-curb-cut-effect/slide16.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.017.jpg /images/2019/monki-gras-the-curb-cut-effect/slide17.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.018.jpg /images/2019/monki-gras-the-curb-cut-effect/slide18.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.019.jpg /images/2019/monki-gras-the-curb-cut-effect/slide19.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.020.jpg /images/2019/monki-gras-the-curb-cut-effect/slide20.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.021.jpg /images/2019/monki-gras-the-curb-cut-effect/slide21.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.023.jpg /images/2019/monki-gras-the-curb-cut-effect/slide23.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.024.jpg /images/2019/monki-gras-the-curb-cut-effect/slide24.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.025.jpg /images/2019/monki-gras-the-curb-cut-effect/slide25.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.026.jpg /images/2019/monki-gras-the-curb-cut-effect/slide26.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.027.jpg /images/2019/monki-gras-the-curb-cut-effect/slide27.jpg permanent +redir /slides/curbcut_monkigras/curbcut_monkigras.028.jpg /images/2019/monki-gras-the-curb-cut-effect/slide28.jpg permanent + +redir /slides/assume_worst_intent/assume_worst_intent.001.png /images/2018/assume-worst-intent/slide1.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.002.png /images/2018/assume-worst-intent/slide2.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.003.png /images/2018/assume-worst-intent/slide3.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.004.png /images/2018/assume-worst-intent/slide4.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.005.png /images/2018/assume-worst-intent/slide5.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.006.png /images/2018/assume-worst-intent/slide6.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.007.png /images/2018/assume-worst-intent/slide7.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.008.png /images/2018/assume-worst-intent/slide8.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.009.png /images/2018/assume-worst-intent/slide9.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.010.png /images/2018/assume-worst-intent/slide10.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.011.png /images/2018/assume-worst-intent/slide11.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.012.jpeg /images/2018/assume-worst-intent/slide12.jpg permanent +redir /slides/assume_worst_intent/assume_worst_intent.013.png /images/2018/assume-worst-intent/slide13.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.014.png /images/2018/assume-worst-intent/slide14.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.015.png /images/2018/assume-worst-intent/slide15.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.016.png /images/2018/assume-worst-intent/slide16.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.017.png /images/2018/assume-worst-intent/slide17.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.018.png /images/2018/assume-worst-intent/slide18.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.019.png /images/2018/assume-worst-intent/slide19.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.020.png /images/2018/assume-worst-intent/slide20.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.021.png /images/2018/assume-worst-intent/slide21.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.022.png /images/2018/assume-worst-intent/slide22.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.023.png /images/2018/assume-worst-intent/slide23.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.024.png /images/2018/assume-worst-intent/slide24.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.025.png /images/2018/assume-worst-intent/slide25.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.026.png /images/2018/assume-worst-intent/slide26.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.027.png /images/2018/assume-worst-intent/slide27.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.028.png /images/2018/assume-worst-intent/slide28.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.029.png /images/2018/assume-worst-intent/slide29.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.030.png /images/2018/assume-worst-intent/slide30.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.031.png /images/2018/assume-worst-intent/slide31.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.032.png /images/2018/assume-worst-intent/slide32.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.033.png /images/2018/assume-worst-intent/slide33.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.034.png /images/2018/assume-worst-intent/slide34.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.035.png /images/2018/assume-worst-intent/slide35.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.036.png /images/2018/assume-worst-intent/slide36.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.037.png /images/2018/assume-worst-intent/slide37.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.038.png /images/2018/assume-worst-intent/slide38.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.039.png /images/2018/assume-worst-intent/slide39.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.040.png /images/2018/assume-worst-intent/slide40.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.041.png /images/2018/assume-worst-intent/slide41.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.042.png /images/2018/assume-worst-intent/slide42.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.043.png /images/2018/assume-worst-intent/slide43.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.044.png /images/2018/assume-worst-intent/slide44.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.045.png /images/2018/assume-worst-intent/slide45.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.046.png /images/2018/assume-worst-intent/slide46.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.047.png /images/2018/assume-worst-intent/slide47.png permanent +redir /slides/assume_worst_intent/assume_worst_intent.048.png /images/2018/assume-worst-intent/slide48.png permanent + +redir /slides/suspicious-minds/suspicious-minds.001.jpg /images/2018/suspicious-minds/slide1.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.002.jpg /images/2018/suspicious-minds/slide2.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.003.png /images/2018/suspicious-minds/slide3.png permanent +redir /slides/suspicious-minds/suspicious-minds.004.png /images/2018/suspicious-minds/slide4.png permanent +redir /slides/suspicious-minds/suspicious-minds.005.png /images/2018/suspicious-minds/slide5.png permanent +redir /slides/suspicious-minds/suspicious-minds.006.png /images/2018/suspicious-minds/slide6.png permanent +redir /slides/suspicious-minds/suspicious-minds.007.png /images/2018/suspicious-minds/slide7.png permanent +redir /slides/suspicious-minds/suspicious-minds.008.png /images/2018/suspicious-minds/slide8.png permanent +redir /slides/suspicious-minds/suspicious-minds.009.png /images/2018/suspicious-minds/slide9.png permanent +redir /slides/suspicious-minds/suspicious-minds.010.png /images/2018/suspicious-minds/slide10.png permanent +redir /slides/suspicious-minds/suspicious-minds.011.jpg /images/2018/suspicious-minds/slide11.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.012.png /images/2018/suspicious-minds/slide12.png permanent +redir /slides/suspicious-minds/suspicious-minds.013.png /images/2018/suspicious-minds/slide13.png permanent +redir /slides/suspicious-minds/suspicious-minds.014.png /images/2018/suspicious-minds/slide14.png permanent +redir /slides/suspicious-minds/suspicious-minds.015.png /images/2018/suspicious-minds/slide15.png permanent +redir /slides/suspicious-minds/suspicious-minds.016.png /images/2018/suspicious-minds/slide16.png permanent +redir /slides/suspicious-minds/suspicious-minds.017.jpg /images/2018/suspicious-minds/slide17.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.018.jpg /images/2018/suspicious-minds/slide18.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.019.png /images/2018/suspicious-minds/slide19.png permanent +redir /slides/suspicious-minds/suspicious-minds.020.png /images/2018/suspicious-minds/slide20.png permanent +redir /slides/suspicious-minds/suspicious-minds.021.png /images/2018/suspicious-minds/slide21.png permanent +redir /slides/suspicious-minds/suspicious-minds.022.png /images/2018/suspicious-minds/slide22.png permanent +redir /slides/suspicious-minds/suspicious-minds.023.png /images/2018/suspicious-minds/slide23.png permanent +redir /slides/suspicious-minds/suspicious-minds.024.png /images/2018/suspicious-minds/slide24.png permanent +redir /slides/suspicious-minds/suspicious-minds.025.png /images/2018/suspicious-minds/slide25.png permanent +redir /slides/suspicious-minds/suspicious-minds.026.png /images/2018/suspicious-minds/slide26.png permanent +redir /slides/suspicious-minds/suspicious-minds.027.png /images/2018/suspicious-minds/slide27.png permanent +redir /slides/suspicious-minds/suspicious-minds.028.png /images/2018/suspicious-minds/slide28.png permanent +redir /slides/suspicious-minds/suspicious-minds.029.jpg /images/2018/suspicious-minds/slide29.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.030.jpg /images/2018/suspicious-minds/slide30.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.031.png /images/2018/suspicious-minds/slide31.png permanent +redir /slides/suspicious-minds/suspicious-minds.032.png /images/2018/suspicious-minds/slide32.png permanent +redir /slides/suspicious-minds/suspicious-minds.033.png /images/2018/suspicious-minds/slide33.png permanent +redir /slides/suspicious-minds/suspicious-minds.034.png /images/2018/suspicious-minds/slide34.png permanent +redir /slides/suspicious-minds/suspicious-minds.035.png /images/2018/suspicious-minds/slide35.png permanent +redir /slides/suspicious-minds/suspicious-minds.036.png /images/2018/suspicious-minds/slide36.png permanent +redir /slides/suspicious-minds/suspicious-minds.037.jpg /images/2018/suspicious-minds/slide37.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.038.jpg /images/2018/suspicious-minds/slide38.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.039.png /images/2018/suspicious-minds/slide39.png permanent +redir /slides/suspicious-minds/suspicious-minds.040.png /images/2018/suspicious-minds/slide40.png permanent +redir /slides/suspicious-minds/suspicious-minds.041.png /images/2018/suspicious-minds/slide41.png permanent +redir /slides/suspicious-minds/suspicious-minds.042.png /images/2018/suspicious-minds/slide42.png permanent +redir /slides/suspicious-minds/suspicious-minds.043.png /images/2018/suspicious-minds/slide43.png permanent +redir /slides/suspicious-minds/suspicious-minds.044.png /images/2018/suspicious-minds/slide44.png permanent +redir /slides/suspicious-minds/suspicious-minds.045.png /images/2018/suspicious-minds/slide45.png permanent +redir /slides/suspicious-minds/suspicious-minds.046.jpg /images/2018/suspicious-minds/slide46.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.046.jpeg /images/2018/suspicious-minds/slide46.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.047.png /images/2018/suspicious-minds/slide47.png permanent +redir /slides/suspicious-minds/suspicious-minds.048.jpeg /images/2018/suspicious-minds/slide48.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.049.jpeg /images/2018/suspicious-minds/slide49.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.049.jpg /images/2018/suspicious-minds/slide49.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.050.png /images/2018/suspicious-minds/slide50.png permanent +redir /slides/suspicious-minds/suspicious-minds.051.jpg /images/2018/suspicious-minds/slide51.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.051.jpeg /images/2018/suspicious-minds/slide51.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.052.jpg /images/2018/suspicious-minds/slide52.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.052.jpeg /images/2018/suspicious-minds/slide52.jpg permanent +redir /slides/suspicious-minds/suspicious-minds.053.png /images/2018/suspicious-minds/slide53.png permanent + +redir /slides/anti_social_media/anti_social_media.001.png /images/2018/anti-social-media/slide1.png permanent +redir /slides/anti_social_media/anti_social_media.002.png /images/2018/anti-social-media/slide2.png permanent +redir /slides/anti_social_media/anti_social_media.004.png /images/2018/anti-social-media/slide4.png permanent +redir /slides/anti_social_media/anti_social_media.005.png /images/2018/anti-social-media/slide5.png permanent +redir /slides/anti_social_media/anti_social_media.006.png /images/2018/anti-social-media/slide6.png permanent +redir /slides/anti_social_media/anti_social_media.007.png /images/2018/anti-social-media/slide7.png permanent +redir /slides/anti_social_media/anti_social_media.008.png /images/2018/anti-social-media/slide8.png permanent +redir /slides/anti_social_media/anti_social_media.009.png /images/2018/anti-social-media/slide9.png permanent +redir /slides/anti_social_media/anti_social_media.010.png /images/2018/anti-social-media/slide10.png permanent +redir /slides/anti_social_media/anti_social_media.011.png /images/2018/anti-social-media/slide11.png permanent +redir /slides/anti_social_media/anti_social_media.012.png /images/2018/anti-social-media/slide12.png permanent +redir /slides/anti_social_media/anti_social_media.013.png /images/2018/anti-social-media/slide13.png permanent +redir /slides/anti_social_media/anti_social_media.014.png /images/2018/anti-social-media/slide14.png permanent +redir /slides/anti_social_media/anti_social_media.015.png /images/2018/anti-social-media/slide15.png permanent +redir /slides/anti_social_media/anti_social_media.016.png /images/2018/anti-social-media/slide16.png permanent +redir /slides/anti_social_media/anti_social_media.017.png /images/2018/anti-social-media/slide17.png permanent +redir /slides/anti_social_media/anti_social_media.018.png /images/2018/anti-social-media/slide18.png permanent +redir /slides/anti_social_media/anti_social_media.019.png /images/2018/anti-social-media/slide19.png permanent +redir /slides/anti_social_media/anti_social_media.020.png /images/2018/anti-social-media/slide20.png permanent +redir /slides/anti_social_media/anti_social_media.021.png /images/2018/anti-social-media/slide21.png permanent +redir /slides/anti_social_media/anti_social_media.022.png /images/2018/anti-social-media/slide22.png permanent +redir /slides/anti_social_media/anti_social_media.023.png /images/2018/anti-social-media/slide23.png permanent +redir /slides/anti_social_media/anti_social_media.025.png /images/2018/anti-social-media/slide25.png permanent +redir /slides/anti_social_media/anti_social_media.026.png /images/2018/anti-social-media/slide26.png permanent +redir /slides/anti_social_media/anti_social_media.027.png /images/2018/anti-social-media/slide27.png permanent +redir /slides/anti_social_media/anti_social_media.028.png /images/2018/anti-social-media/slide28.png permanent +redir /slides/anti_social_media/anti_social_media.031.png /images/2018/anti-social-media/slide31.png permanent +redir /slides/anti_social_media/anti_social_media.032.png /images/2018/anti-social-media/slide32.png permanent +redir /slides/anti_social_media/anti_social_media.033.png /images/2018/anti-social-media/slide33.png permanent +redir /slides/anti_social_media/anti_social_media.034.png /images/2018/anti-social-media/slide34.png permanent +redir /slides/anti_social_media/anti_social_media.036.png /images/2018/anti-social-media/slide36.png permanent +redir /slides/anti_social_media/anti_social_media.037.png /images/2018/anti-social-media/slide37.png permanent +redir /slides/anti_social_media/anti_social_media.038.png /images/2018/anti-social-media/slide38.png permanent +redir /slides/anti_social_media/anti_social_media.039.png /images/2018/anti-social-media/slide39.png permanent +redir /slides/anti_social_media/anti_social_media.042.png /images/2018/anti-social-media/slide42.png permanent +redir /slides/anti_social_media/anti_social_media.043.png /images/2018/anti-social-media/slide43.png permanent +redir /slides/anti_social_media/anti_social_media.044.png /images/2018/anti-social-media/slide44.png permanent +redir /slides/anti_social_media/anti_social_media.045.png /images/2018/anti-social-media/slide45.png permanent +redir /slides/anti_social_media/anti_social_media.046.png /images/2018/anti-social-media/slide46.png permanent +redir /slides/anti_social_media/anti_social_media.047.png /images/2018/anti-social-media/slide47.png permanent +redir /slides/anti_social_media/anti_social_media.048.png /images/2018/anti-social-media/slide48.png permanent +redir /slides/anti_social_media/anti_social_media.049.png /images/2018/anti-social-media/slide49.png permanent +redir /slides/anti_social_media/anti_social_media.051.png /images/2018/anti-social-media/slide51.png permanent +redir /slides/anti_social_media/anti_social_media.052.png /images/2018/anti-social-media/slide52.png permanent +redir /slides/anti_social_media/anti_social_media.054.png /images/2018/anti-social-media/slide54.png permanent +redir /slides/anti_social_media/anti_social_media.055.png /images/2018/anti-social-media/slide55.png permanent +redir /slides/anti_social_media/anti_social_media.056.png /images/2018/anti-social-media/slide56.png permanent +redir /slides/anti_social_media/anti_social_media.057.png /images/2018/anti-social-media/slide57.png permanent +redir /slides/anti_social_media/anti_social_media.059.png /images/2018/anti-social-media/slide59.png permanent +redir /slides/anti_social_media/anti_social_media.061.png /images/2018/anti-social-media/slide61.png permanent +redir /slides/anti_social_media/anti_social_media.062.png /images/2018/anti-social-media/slide62.png permanent +redir /slides/anti_social_media/anti_social_media.063.png /images/2018/anti-social-media/slide63.png permanent +redir /slides/anti_social_media/anti_social_media.064.png /images/2018/anti-social-media/slide64.png permanent +redir /slides/anti_social_media/anti_social_media.065.png /images/2018/anti-social-media/slide65.png permanent +redir /slides/anti_social_media/anti_social_media.066.png /images/2018/anti-social-media/slide66.png permanent +redir /slides/anti_social_media/anti_social_media.067.png /images/2018/anti-social-media/slide67.png permanent +redir /slides/anti_social_media/anti_social_media.068.png /images/2018/anti-social-media/slide68.png permanent +redir /slides/anti_social_media/anti_social_media.070.png /images/2018/anti-social-media/slide70.png permanent +redir /slides/anti_social_media/anti_social_media.072.png /images/2018/anti-social-media/slide72.png permanent +redir /slides/anti_social_media/anti_social_media.073.png /images/2018/anti-social-media/slide73.png permanent +redir /slides/anti_social_media/anti_social_media.075.png /images/2018/anti-social-media/slide75.png permanent +redir /slides/anti_social_media/anti_social_media.076.png /images/2018/anti-social-media/slide76.png permanent +redir /slides/anti_social_media/anti_social_media.079.png /images/2018/anti-social-media/slide79.png permanent +redir /slides/anti_social_media/anti_social_media.080.png /images/2018/anti-social-media/slide80.png permanent +redir /slides/anti_social_media/anti_social_media.081.png /images/2018/anti-social-media/slide81.png permanent +redir /slides/anti_social_media/anti_social_media.082.png /images/2018/anti-social-media/slide82.png permanent +redir /slides/anti_social_media/anti_social_media.084.png /images/2018/anti-social-media/slide84.png permanent +redir /slides/anti_social_media/anti_social_media.085.png /images/2018/anti-social-media/slide85.png permanent +redir /slides/anti_social_media/anti_social_media.086.png /images/2018/anti-social-media/slide86.png permanent +redir /slides/anti_social_media/anti_social_media.087.png /images/2018/anti-social-media/slide87.png permanent +redir /slides/anti_social_media/anti_social_media.088.png /images/2018/anti-social-media/slide88.png permanent +redir /slides/anti_social_media/anti_social_media.089.png /images/2018/anti-social-media/slide89.png permanent + +# === # + + + + + +# == Removing the month from post slugs at the start of 2023 == # +redir /2012/12/hypercritical/ /2012/hypercritical/ permanent +redir /2013/01/zero/ /2013/zero/ permanent +redir /2013/02/darwin/ /2013/darwin/ permanent +redir /2013/03/candybar/ /2013/candybar/ permanent +redir /2013/03/pinboard-backups/ /2013/pinboard-backups/ permanent +redir /2013/03/timezone-bug/ /2013/timezone-bug/ permanent +redir /2013/05/rss-podcasts-tumblr/ /til/2013/rss-podcasts-tumblr/ permanent +redir /2013/08/google-maps/ /2013/google-maps/ permanent +redir /2013/08/untagged-tumblr-posts/ /2013/untagged-tumblr-posts/ permanent +redir /2013/11/textmate-quick-look/ /2013/textmate-quick-look/ permanent +redir /2014/04/veil/ /2014/veil/ permanent +redir /2014/05/part-ia-exams/ /2014/part-ia-exams/ permanent +redir /2014/05/part-ii-advice/ /2014/part-ii-advice/ permanent +redir /2014/06/instapaper-urls/ /2014/instapaper-urls/ permanent +redir /2014/06/site-updates/ /2014/site-updates/ permanent +redir /2014/06/skeletor/ /2014/skeletor/ permanent +redir /2014/06/untagged-tumblr-posts-redux/ /2014/untagged-tumblr-posts-redux/ permanent +redir /2014/07/latex-alpha/ /2014/latex-alpha/ permanent +redir /2014/07/overcast/ /2014/overcast/ permanent +redir /2014/08/alfred-screenshots/ /2014/alfred-screenshots/ permanent +redir /2014/08/textexpander-amazon-affiliates/ /2014/textexpander-amazon-affiliates/ permanent +redir /2014/08/untagged-tumblr-updates/ /2014/untagged-tumblr-updates/ permanent +redir /2014/09/404-pages/ /2014/404-pages/ permanent +redir /2014/09/new-standing-desk/ /2014/new-standing-desk/ permanent +redir /2014/10/notes-on-tumblr/ /2014/notes-on-tumblr/ permanent +redir /2014/10/virtualfish/ /2014/virtualfish/ permanent +redir /2014/11/custom-css-resophnotes/ /2014/custom-css-resophnotes/ permanent +redir /2014/11/ranged-strings/ /2014/ranged-strings/ permanent +redir /2014/12/acronyms/ /2014/acronyms/ permanent +redir /2014/12/golems/ /2014/golems/ permanent +redir /2014/12/imessage-export/ /2014/imessage-export/ permanent +redir /2014/12/kitchen-sink-security/ /2014/kitchen-sink-security/ permanent +redir /2014/12/mathis-cars/ /2014/mathis-cars/ permanent +redir /2014/12/pelican-linkposts/ /2014/pelican-linkposts/ permanent +redir /2014/12/skeletors-all-the-way-down/ /2014/skeletors-all-the-way-down/ permanent +redir /2015/02/1password/ /2015/1password/ permanent +redir /2015/02/flask-notes/ /2015/flask-notes/ permanent +redir /2015/02/lists/ /2015/lists/ permanent +redir /2015/02/swift-let/ /2015/swift-let/ permanent +redir /2015/03/exam-advice/ /2015/exam-advice/ permanent +redir /2015/03/pygmentizr/ /2015/pygmentizr/ permanent +redir /2015/03/python-firewall/ /2015/python-firewall/ permanent +redir /2015/05/github-contributions/ /2015/github-contributions/ permanent +redir /2015/05/nvalt-and-marked/ /2015/nvalt-and-marked/ permanent +redir /2015/05/one-step-paste-simulator/ /2015/one-step-paste-simulator/ permanent +redir /2015/05/raft-algorithm/ /2015/raft-algorithm/ permanent +redir /2015/06/git-info-exclude/ /2015/git-info-exclude/ permanent +redir /2015/06/persistent-ipython-notebooks/ /2015/persistent-ipython-notebooks/ permanent +redir /2015/06/safer-file-copying/ /2015/safer-file-copying/ permanent +redir /2015/07/exit-traps-in-bash/ /2015/exit-traps-in-bash/ permanent +redir /2015/08/untagged-tumblr-v2/ /2015/untagged-tumblr-v2/ permanent +redir /2015/09/effective-python/ /2015/effective-python/ permanent +redir /2015/09/http2-by-stealth/ /2015/http2-by-stealth/ permanent +redir /2015/09/spotlight-suggestions/ /2015/spotlight-suggestions/ permanent +redir /2015/10/getting-started-testing/ /2015/getting-started-testing/ permanent +redir /2015/11/backups-and-docker/ /2015/backups-and-docker/ permanent +redir /2015/11/bbc-micro-bit/ /2015/bbc-micro-bit/ permanent +redir /2015/11/beyond-pep8/ /2015/beyond-pep8/ permanent +redir /2015/11/export-urls-from-safari-reading-list/ /2015/export-urls-from-safari-reading-list/ permanent +redir /2015/11/operation-fortitude/ /2015/operation-fortitude/ permanent +redir /2015/11/quick-shell-docker/ /2015/quick-shell-docker/ permanent +redir /2015/12/pretty-print/ /2015/pretty-print/ permanent +redir /2015/12/simpler-syndication/ /2015/simpler-syndication/ permanent +redir /2016/01/fusion-drive/ /2016/fusion-drive/ permanent +redir /2016/01/harry-potter-ipod/ /2016/harry-potter-ipod/ permanent +redir /2016/01/skeletor-2015/ /2016/skeletor-2015/ permanent +redir /2016/02/how-i-use-textexpander-to-curb-my-language/ /2016/how-i-use-textexpander-to-curb-my-language/ permanent +redir /2016/02/saved-by-the-prompt/ /2016/saved-by-the-prompt/ permanent +redir /2016/03/backup-paranoia/ /2016/backup-paranoia/ permanent +redir /2016/03/exclusive-create-python/ /2016/exclusive-create-python/ permanent +redir /2016/03/itunes-images-with-alfred/ /2016/itunes-images-with-alfred/ permanent +redir /2016/04/hiding-the-youtube-search-bar/ /2016/hiding-the-youtube-search-bar/ permanent +redir /2016/04/regexes-are-code/ /2016/regexes-are-code/ permanent +redir /2016/05/finding-404s-in-apache-logs/ /2016/finding-404s-in-apache-logs/ permanent +redir /2016/05/os-x-hates-textmate/ /2016/os-x-hates-textmate/ permanent +redir /2016/05/python-smtplib-and-fastmail/ /2016/python-smtplib-and-fastmail/ permanent +redir /2016/05/safely-deleting-a-file-called-rf/ /2016/safely-deleting-a-file-called-rf/ permanent +redir /2016/06/hypothesis-intro/ /2016/hypothesis-intro/ permanent +redir /2016/06/introduction-to-property-based-testing/ /2016/introduction-to-property-based-testing/ permanent +redir /2016/06/reading-web-pages-on-my-kindle/ /2016/reading-web-pages-on-my-kindle/ permanent +redir /2016/06/toothbrush-subscriptions/ /2016/toothbrush-subscriptions/ permanent +redir /2016/07/chasing-redirects-and-url-shorteners/ /2016/chasing-redirects-and-url-shorteners/ permanent +redir /2016/07/clearing-disk-space-on-os-x/ /2016/clearing-disk-space-on-os-x/ permanent +redir /2016/08/clean-up-directories/ /2016/clean-up-directories/ permanent +redir /2016/08/dealing-with-query-strings/ /2016/dealing-with-query-strings/ permanent +redir /2016/08/is-a-url-from-tumblr/ /2016/is-a-url-from-tumblr/ permanent +redir /2016/09/please-use-aspell/ /2016/please-use-aspell/ permanent +redir /2016/09/pyconuk2016/ /2016/pyconuk2016/ permanent +redir /2016/09/silence-is-golden/ /2016/silence-is-golden/ permanent +redir /2016/09/speech-to-text/ /2016/speech-to-text/ permanent +redir /2016/09/travelling-tech-bag/ /2016/travelling-tech-bag/ permanent +redir /2016/10/a-shell-alias-for-tallying/ /2016/a-shell-alias-for-tallying/ permanent +redir /2016/10/the-a-stands-for-asexual/ /2016/the-a-stands-for-asexual/ permanent +redir /2016/10/tiling-the-plane-with-pillow/ /2016/tiling-the-plane-with-pillow/ permanent +redir /2016/10/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ permanent +redir /2016/10/wallpapers-with-pillow/ /2016/wallpapers-with-pillow/ permanent +redir /2016/10/why-i-use-pytest/ /2016/why-i-use-pytest/ permanent +redir /2016/10/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ permanent +redir /2016/11/low-tech-productivity/ /2016/low-tech-productivity/ permanent +redir /2016/11/you-should-use-keyring/ /2016/you-should-use-keyring/ permanent +redir /2016/12/slack-history/ /2016/slack-history/ permanent +redir /2016/12/strings-are-terrible/ /2016/strings-are-terrible/ permanent +redir /2017/01/experiments-with-ao3-and-python/ /2017/experiments-with-ao3-and-python/ permanent +redir /2017/01/scrape-logged-in-ao3/ /2017/scrape-logged-in-ao3/ permanent +redir /2017/02/backup-your-goodreads/ /2017/backup-your-goodreads/ permanent +redir /2017/02/backup-your-instapaper/ /2017/backup-your-instapaper/ permanent +redir /2017/03/extensions-in-python-markdown/ /2017/extensions-in-python-markdown/ permanent +redir /2017/03/qcon2017/ /2017/qcon2017/ permanent +redir /2017/04/lessons-from-alterconf/ /2017/lessons-from-alterconf/ permanent +redir /2017/06/crossness-pumping-station/ /2017/crossness-pumping-station/ permanent +redir /2017/07/backing-up-pinboard-archives/ /2017/backing-up-pinboard-archives/ permanent +redir /2017/07/listing-s3-keys/ /2017/listing-s3-keys/ permanent +redir /2017/07/soundcloud-backups/ /2017/soundcloud-backups/ permanent +redir /2017/09/lazy-reading-in-python/ /2017/lazy-reading-in-python/ permanent +redir /2017/09/ode-to-docopt/ /2017/ode-to-docopt/ permanent +redir /2017/09/useful-git-commands/ /2017/useful-git-commands/ permanent +redir /2017/10/control-centre/ /2017/control-centre/ permanent +redir /2017/10/latex-underlines/ /2017/latex-underlines/ permanent +redir /2017/10/lightning-talks/ /2017/lightning-talks/ permanent +redir /2017/10/overengineering/ /2017/overengineering/ permanent +redir /2017/10/pip-tools/ /2017/pip-tools/ permanent +redir /2017/10/pronunciation-peeves/ /2017/pronunciation-peeves/ permanent +redir /2017/10/pyconuk-2017-resources/ /2017/pyconuk-2017-resources/ permanent +redir /2017/10/requests-hooks/ /2017/requests-hooks/ permanent +redir /2017/10/tweets-in-keynote/ /2017/tweets-in-keynote/ permanent +redir /2017/11/a-plumbers-guide-to-git/ /2017/a-plumbers-guide-to-git/ permanent +redir /2017/11/asking-about-gender/ /2017/asking-about-gender/ permanent +redir /2017/11/dont-tap-the-mic/ /2017/dont-tap-the-mic/ permanent +redir /2017/11/fetching-cloudwatch-logs/ /2017/fetching-cloudwatch-logs/ permanent +redir /2017/11/five-years-of-witch/ /2017/five-years-of-witch/ permanent +redir /2017/11/pruning-git-branches/ /2017/pruning-git-branches/ permanent +redir /2017/11/pyconuk-2017-privilege-inclusion/ /2017/pyconuk-2017-privilege-inclusion/ permanent +redir /2017/12/armed-police/ /2017/armed-police/ permanent +redir /2017/12/building-your-repo/ /2017/building-your-repo/ permanent +redir /2018/01/downloading-sqs-queues/ /2018/downloading-sqs-queues/ permanent +redir /2018/01/ips-for-documentation/ /2018/ips-for-documentation/ permanent +redir /2018/01/listing-s3-keys-redux/ /2018/listing-s3-keys-redux/ permanent +redir /2018/02/working-from-home/ /2018/working-from-home/ permanent +redir /2018/03/a-plumbers-guide-to-git/ /2018/a-plumbers-guide-to-git/ permanent +redir /2018/03/continuous-releases/ /2018/continuous-releases/ permanent +redir /2018/03/overnight-bag/ /2018/overnight-bag/ permanent +redir /2018/04/24-hours/ /2018/24-hours/ permanent +redir /2018/04/anti-social-media/ /2018/anti-social-media/ permanent +redir /2018/04/s3-shortcuts/ /2018/s3-shortcuts/ permanent +redir /2018/05/ascii-bar-charts/ /2018/ascii-bar-charts/ permanent +redir /2018/05/beware-logged-errors/ /2018/beware-logged-errors/ permanent +redir /2018/05/google-duplex/ /2018/google-duplex/ permanent +redir /2018/07/icloud-calendars/ /2018/icloud-calendars/ permanent +redir /2018/07/imac-accessory/ /2018/imac-accessory/ permanent +redir /2018/07/leaking-my-ssh-keys/ /2018/leaking-my-ssh-keys/ permanent +redir /2018/07/travel-instructions/ /2018/travel-instructions/ permanent +redir /2018/08/do-not-distract-while-driving/ /2018/do-not-distract-while-driving/ permanent +redir /2018/08/finding-slow-builds-in-travis/ /2018/finding-slow-builds-in-travis/ permanent +redir /2018/08/inclusive-conferences/ /2018/inclusive-conferences/ permanent +redir /2018/08/maps-for-pyconuk/ /2018/maps-for-pyconuk/ permanent +redir /2018/08/no-more-tumblr-redirects/ /2018/no-more-tumblr-redirects/ permanent +redir /2018/08/parallel-scan-scanamo/ /2018/parallel-scan-scanamo/ permanent +redir /2018/08/selective-sudo-on-travis/ /2018/selective-sudo-on-travis/ permanent +redir /2018/09/assume-worst-intent/ /2018/assume-worst-intent/ permanent +redir /2018/09/content-warnings/ /2018/content-warnings/ permanent +redir /2018/09/error-logging-in-lambdas/ /2018/error-logging-in-lambdas/ permanent +redir /2018/09/lessons-in-signage/ /2018/lessons-in-signage/ permanent +redir /2018/09/pyconuk-2018/ /2018/pyconuk-2018/ permanent +redir /2018/09/suspicious-minds/ /2018/suspicious-minds/ permanent +redir /2018/10/finatra-404/ /2018/finatra-404/ permanent +redir /2018/10/gender-recognition-act/ /2018/gender-recognition-act/ permanent +redir /2018/10/horstmann-electric-7/ /til/2018/horstmann-electric-7/ permanent +redir /2018/10/unscrupulous-time-travel/ /2018/unscrupulous-time-travel/ permanent +redir /2018/11/aberdulais-waterfall/ /2018/aberdulais-waterfall/ permanent +redir /2018/11/book-recommendations/ /2018/book-recommendations/ permanent +redir /2018/11/finding-unsubscribed-sns-topics/ /2018/finding-unsubscribed-sns-topics/ permanent +redir /2018/12/backing-up-tumblr/ /2018/backing-up-tumblr/ permanent +redir /2018/12/getting-credentials-for-an-assumed-iam-role/ /2018/getting-credentials-for-an-assumed-iam-role/ permanent +redir /2018/12/iterating-in-fixed-size-chunks/ /2018/iterating-in-fixed-size-chunks/ permanent +redir /2018/12/reading-a-utf8-encoded-csv/ /2018/reading-a-utf8-encoded-csv/ permanent +redir /2019/01/debugging-a-stuck-terraform-plan/ /2019/debugging-a-stuck-terraform-plan/ permanent +redir /2019/01/monki-gras-the-curb-cut-effect/ /2019/monki-gras-the-curb-cut-effect/ permanent +redir /2019/01/notes-from-you-got-this/ /2019/notes-from-you-got-this/ permanent +redir /2019/01/oyster-tracking/ /2019/oyster-tracking/ permanent +redir /2019/01/paul-rothe-and-sons/ /2019/paul-rothe-and-sons/ permanent +redir /2019/01/sunlight/ /2019/sunlight/ permanent +redir /2019/02/checking-jekyll-sites-with-htmlproofer/ /2019/checking-jekyll-sites-with-htmlproofer/ permanent +redir /2019/02/inclusive-events-redux/ /2019/inclusive-events-redux/ permanent +redir /2019/02/working-with-large-s3-objects/ /2019/working-with-large-s3-objects/ permanent +redir /2019/03/atomic-cross-filesystem-moves-in-python/ /2019/atomic-cross-filesystem-moves-in-python/ permanent +redir /2019/03/creating-a-github-action-to-auto-merge-pull-requests/ /2019/creating-a-github-action-to-auto-merge-pull-requests/ permanent +redir /2019/03/finding-the-latest-screenshot-in-macos-mojave/ /2019/finding-the-latest-screenshot-in-macos-mojave/ permanent +redir /2019/03/forth-bridge/ /2019/forth-bridge/ permanent +redir /2019/04/getting-a-transcript-of-a-talk-from-youtube/ /2019/getting-a-transcript-of-a-talk-from-youtube/ permanent +redir /2019/04/how-i-back-up-my-computer/ /2019/how-i-back-up-my-computer/ permanent +redir /2019/04/reversing-a-tco-url-to-the-original-tweet/ /2019/reversing-a-tco-url-to-the-original-tweet/ permanent +redir /2019/04/some-tips-for-conferences/ /2019/some-tips-for-conferences/ permanent +redir /2019/05/creating-a-locking-service-in-a-scala-type-class/ /2019/creating-a-locking-service-in-a-scala-type-class/ permanent +redir /2019/05/falsehoods-programmers-believe-about-unix-time/ /2019/falsehoods-programmers-believe-about-unix-time/ permanent +redir /2019/05/finding-unused-variables-in-a-terraform-module/ /2019/finding-unused-variables-in-a-terraform-module/ permanent +redir /2019/05/first-thoughts-on-trans-inclusion/ /2019/first-thoughts-on-trans-inclusion/ permanent +redir /2019/06/a-jekyll-filter-for-obfuscating-email-addresses/ /2019/a-jekyll-filter-for-obfuscating-email-addresses/ permanent +redir /2019/06/acorn-on-the-command-line/ /2019/acorn-on-the-command-line/ permanent +redir /2019/06/cycle-tracking-isn-t-just-for-women/ /2019/cycle-tracking-isn-t-just-for-women/ permanent +redir /2019/06/getting-cover-images-from-mobi-ebooks/ /2019/getting-cover-images-from-mobi-ebooks/ permanent +redir /2019/06/reading-a-chinese-dictionary/ /2019/reading-a-chinese-dictionary/ permanent +redir /2019/06/regenerating/ /2019/regenerating/ permanent +redir /2019/07/creating-preview-thumbnails-of-pdf-documents/ /2019/creating-preview-thumbnails-of-pdf-documents/ permanent +redir /2019/07/finding-divisors-with-python/ /2019/finding-divisors-with-python/ permanent +redir /2019/07/listing-s3-keys/ /2019/listing-s3-keys/ permanent +redir /2019/07/section-28/ /2019/section-28/ permanent +redir /2019/07/ten-braille-facts/ /2019/ten-braille-facts/ permanent +redir /2019/08/finding-tint-colours-with-k-means/ /2019/finding-tint-colours-with-k-means/ permanent +redir /2019/09/github-code-search-with-de-duplication/ /2019/github-code-search-with-de-duplication/ permanent +redir /2019/09/streaming-large-s3-objects/ /2019/streaming-large-s3-objects/ permanent +redir /2019/09/triangular-coordinates-in-svg/ /2019/triangular-coordinates-in-svg/ permanent +redir /2019/09/unpacking-compressed-archives-in-scala/ /2019/unpacking-compressed-archives-in-scala/ permanent +redir /2019/10/adventures-with-concurrent-futures/ /2019/adventures-with-concurrent-futures/ permanent +redir /2019/10/digital-preservation-at-wellcome-collection/ /2019/digital-preservation-at-wellcome-collection/ permanent +redir /2019/10/religious-holidays/ /2019/religious-holidays/ permanent +redir /2019/10/rough-edges-of-filecmp/ /2019/rough-edges-of-filecmp/ permanent +redir /2019/10/sans-io-programming/ /2019/sans-io-programming/ permanent +redir /2019/11/aws-costs-graph/ /2019/aws-costs-graph/ permanent +redir /2019/11/fixing-terraform-module-sources/ /2019/fixing-terraform-module-sources/ permanent +redir /2019/11/my-scanning-setup/ /2019/my-scanning-setup/ permanent +redir /2019/11/saving-a-copy-of-a-tweet-by-typing-twurl/ /2019/saving-a-copy-of-a-tweet-by-typing-twurl/ permanent +redir /2019/12/november-scripts/ /2019/november-scripts/ permanent +redir /2019/12/spreadsheet-functions/ /2019/spreadsheet-functions/ permanent +redir /2019/12/yaml-impossible/ /2019/yaml-impossible/ permanent +redir /2020/01/deletion-canary/ /2020/deletion-canary/ permanent +redir /2020/01/excluding-lots-of-folders-in-backblaze/ /2020/excluding-lots-of-folders-in-backblaze/ permanent +redir /2020/01/finding-the-bottlenecks-in-an-ecs-cluster/ /2020/finding-the-bottlenecks-in-an-ecs-cluster/ permanent +redir /2020/01/pride-valknuts/ /2020/pride-valknuts/ permanent +redir /2020/01/tex-dockerfile/ /2020/tex-dockerfile/ permanent +redir /2020/01/uk-stations-map/ /2020/uk-stations-map/ permanent +redir /2020/02/a-remote-controlled-oven-is-a-safety-nightmare/ /2020/a-remote-controlled-oven-is-a-safety-nightmare/ permanent +redir /2020/02/adjusting-the-dominant-colour-of-an-image/ /2020/adjusting-the-dominant-colour-of-an-image/ permanent +redir /2020/02/archival-storage-service/ /2020/archival-storage-service/ permanent +redir /2020/02/versioning-a-bagit-bag/ /2020/versioning-a-bagit-bag/ permanent +redir /2020/03/adding-non-breaking-spaces-with-jekyll/ /2020/adding-non-breaking-spaces-with-jekyll/ permanent +redir /2020/03/finding-the-size-of-your-s3-buckets/ /2020/finding-the-size-of-your-s3-buckets/ permanent +redir /2020/03/inclusion-cant-be-an-afterthought/ /2020/inclusion-cant-be-an-afterthought/ permanent +redir /2020/03/rich-enough-to-make-bad-choices/ /2020/rich-enough-to-make-bad-choices/ permanent +redir /2020/03/sick-leave/ /2020/sick-leave/ permanent +redir /2020/03/stripey-flag-wallpapers/ /2020/stripey-flag-wallpapers/ permanent +redir /2020/04/adventures-in-embodiment/ /2020/adventures-in-embodiment/ permanent +redir /2020/04/comparing-json-in-scala/ /2020/comparing-json-in-scala/ permanent +redir /2020/04/complex-failures/ /2020/complex-failures/ permanent +redir /2020/04/downloading-files-with-python/ /2020/downloading-files-with-python/ permanent +redir /2020/04/exploring-an-unknown-sql-server/ /2020/exploring-an-unknown-sql-server/ permanent +redir /2020/04/getting-word-count-stats-for-my-blog/ /2020/getting-word-count-stats-for-my-blog/ permanent +redir /2020/04/good-information-is-a-privilege/ /2020/good-information-is-a-privilege/ permanent +redir /2020/04/storing-language-vocabulary-as-a-graph/ /2020/storing-language-vocabulary-as-a-graph/ permanent +redir /2020/04/survivors-guilt/ /2020/survivors-guilt/ permanent +redir /2020/04/thinking-about-gender/ /2020/thinking-about-gender/ permanent +redir /2020/04/using-dynamodb-as-a-calculator/ /2020/using-dynamodb-as-a-calculator/ permanent +redir /2020/05/downloading-the-ao3-fics-that-i-ve-saved-in-pinboard/ /2020/downloading-the-ao3-fics-that-i-ve-saved-in-pinboard/ permanent +redir /2020/05/getting-every-item-from-a-dynamodb-table-with-python/ /2020/getting-every-item-from-a-dynamodb-table-with-python/ permanent +redir /2020/05/give-your-audience-time-to-react/ /2020/give-your-audience-time-to-react/ permanent +redir /2020/05/how-long-is-my-data/ /2020/how-long-is-my-data/ permanent +redir /2020/05/human-friendly-dates-in-javascript/ /2020/human-friendly-dates-in-javascript/ permanent +redir /2020/05/illustrating-lifecycle-transitions-in-amazon-s3/ /2020/illustrating-lifecycle-transitions-in-amazon-s3/ permanent +redir /2020/05/letter-to-my-mp-about-liz-truss-and-medical-treatment-for-trans-youth/ /2020/letter-to-my-mp-about-liz-truss-and-medical-treatment-for-trans-youth/ permanent +redir /2020/05/make-it-safe-to-admit-mistakes/ /2020/make-it-safe-to-admit-mistakes/ permanent +redir /2020/05/moving-messages-between-sqs-queues/ /2020/moving-messages-between-sqs-queues/ permanent +redir /2020/05/reflecting-on-bad-life-choices/ /2020/reflecting-on-bad-life-choices/ permanent +redir /2020/05/sachsenhausen/ /2020/sachsenhausen/ permanent +redir /2020/05/social-media-as-a-growth-culture-for-opinions/ /2020/social-media-as-a-growth-culture-for-opinions/ permanent +redir /2020/05/the-friends-i-lost-along-the-way/ /2020/the-friends-i-lost-along-the-way/ permanent +redir /2020/06/always-read-your-contract/ /2020/always-read-your-contract/ permanent +redir /2020/06/archive-monocultures-considered-harmful/ /2020/archive-monocultures-considered-harmful/ permanent +redir /2020/06/fat-shaming-in-the-good-place/ /2020/fat-shaming-in-the-good-place/ permanent +redir /2020/06/finding-the-months-between-two-dates-in-python/ /2020/finding-the-months-between-two-dates-in-python/ permanent +redir /2020/06/large-things-living-in-cold-places/ /2020/large-things-living-in-cold-places/ permanent +redir /2020/06/using-applescript-to-open-a-url-in-private-browsing-in-safari/ /2020/using-applescript-to-open-a-url-in-private-browsing-in-safari/ permanent +redir /2020/07/changing-the-accent-colour-of-icns-icons/ /2020/changing-the-accent-colour-of-icns-icons/ permanent +redir /2020/07/getting-a-markdown-link-to-a-window-in-safari/ /2020/getting-a-markdown-link-to-a-window-in-safari/ permanent +redir /2020/07/how-to-do-parallel-downloads-with-youtube-dl/ /2020/how-to-do-parallel-downloads-with-youtube-dl/ permanent +redir /2020/07/running-concurrent-try-functions-in-scala/ /2020/running-concurrent-try-functions-in-scala/ permanent +redir /2020/07/what-does-d-match-in-a-regex/ /2020/what-does-d-match-in-a-regex/ permanent +redir /2020/07/why-do-programming-languages-have-a-main-function/ /2020/why-do-programming-languages-have-a-main-function/ permanent +redir /2020/08/s3-keys-are-not-file-paths/ /2020/s3-keys-are-not-file-paths/ permanent +redir /2020/08/s3-prefixes-are-not-directories/ /2020/s3-prefixes-are-not-directories/ permanent +redir /2020/08/using-fuzzy-string-matching-to-find-duplicate-tags/ /2020/using-fuzzy-string-matching-to-find-duplicate-tags/ permanent +redir /2020/09/two-python-functions-for-getting-cloudtrail-events/ /2020/two-python-functions-for-getting-cloudtrail-events/ permanent +redir /2020/09/using-qlmanage-to-create-thumbnails-on-macos/ /2020/using-qlmanage-to-create-thumbnails-on-macos/ permanent +redir /2020/10/a-new-readme-for-docstore/ /2020/a-new-readme-for-docstore/ permanent +redir /2020/10/a-sprinkling-of-azure/ /2020/a-sprinkling-of-azure/ permanent +redir /2020/10/how-do-i-use-my-iphone-cameras/ /2020/how-do-i-use-my-iphone-cameras/ permanent +redir /2020/10/the-importance-of-good-error-messages/ /2020/the-importance-of-good-error-messages/ permanent +redir /2020/10/til-using-git-check-ignore-to-debug-your-gitignore/ /til/2020/using-git-check-ignore-to-debug-your-gitignore/ permanent +redir /2020/11/a-python-function-to-ignore-a-path-with-git-info-exclude/ /2020/a-python-function-to-ignore-a-path-with-git-info-exclude/ permanent +redir /2020/11/copying-images-from-docker-hub-to-amazon-ecr/ /2020/copying-images-from-docker-hub-to-amazon-ecr/ permanent +redir /2020/11/how-i-read-non-fiction-books/ /2020/how-i-read-non-fiction-books/ permanent +redir /2020/11/maths-is-about-facing-ambiguity-not-avoiding-it/ /2020/maths-is-about-facing-ambiguity-not-avoiding-it/ permanent +redir /2020/11/non-technical-users/ /2020/non-technical-users/ permanent +redir /2020/11/remembering-if-a-details-element-was-opened/ /2020/remembering-if-a-details-element-was-opened/ permanent +redir /2020/12/creating-short-lived-temporary-roles-for-experimenting-with-aws-iam-policy-documents/ /2020/creating-short-lived-temporary-roles-for-experimenting-with-aws-iam-policy-documents/ permanent +redir /2021/01/kempisbot/ /2021/kempisbot/ permanent +redir /2021/01/what-year-is-it/ /2021/what-year-is-it/ permanent +redir /2021/02/digital-verification/ /2021/digital-verification/ permanent +redir /2021/02/screaming-in-the-cloud/ /2021/screaming-in-the-cloud/ permanent +redir /2021/03/an-applescript-to-toggle-voice-control/ /2021/an-applescript-to-toggle-voice-control/ permanent +redir /2021/03/inner-outer-strokes-svg/ /2021/inner-outer-strokes-svg/ permanent +redir /2021/03/rainbow-hearts/ /2021/rainbow-hearts/ permanent +redir /2021/04/coloured-squares/ /2021/coloured-squares/ permanent +redir /2021/04/detect-private-browsing/ /2021/detect-private-browsing/ permanent +redir /2021/04/s3-progress-bars/ /2021/s3-progress-bars/ permanent +redir /2021/04/secure-input/ /2021/secure-input/ permanent +redir /2021/04/unified-search/ /2021/unified-search/ permanent +redir /2021/06/s3-deprecates-bittorrent/ /2021/s3-deprecates-bittorrent/ permanent +redir /2021/06/visualising-journal/ /2021/visualising-journal/ permanent +redir /2021/07/a-wise-choice-of-test-strings/ /2021/a-wise-choice-of-test-strings/ permanent +redir /2021/07/listing-deleted-secrets/ /2021/listing-deleted-secrets/ permanent +redir /2021/07/useful-github-searches/ /2021/useful-github-searches/ permanent +redir /2021/08/finding-misconfigured-or-dangling-cloudwatch-alarms/ /2021/finding-misconfigured-or-dangling-cloudwatch-alarms/ permanent +redir /2021/08/ignore-lots-of-folders-in-spotlight/ /2021/ignore-lots-of-folders-in-spotlight/ permanent +redir /2021/08/markdown-image-syntax/ /2021/markdown-image-syntax/ permanent +redir /2021/09/cloud-costs-report/ /2021/cloud-costs-report/ permanent +redir /2021/09/debugging-eventbridge-cron/ /2021/debugging-eventbridge-cron/ permanent +redir /2021/09/editing-toolbar/ /2021/editing-toolbar/ permanent +redir /2021/09/non-commuting-strings/ /2021/non-commuting-strings/ permanent +redir /2021/09/perfect-planks/ /2021/perfect-planks/ permanent +redir /2021/09/safari-15-favicons/ /2021/safari-15-favicons/ permanent +redir /2021/09/septembrse/ /2021/septembrse/ permanent +redir /2021/10/console-copying/ /2021/console-copying/ permanent +redir /2021/10/how-do-you-work-with-non-engineers/ /2021/how-do-you-work-with-non-engineers/ permanent +redir /2021/10/oboe-of-optozorax/ /2021/oboe-of-optozorax/ permanent +redir /2021/10/original-photos-filename/ /2021/original-photos-filename/ permanent +redir /2021/10/readmes-for-open-science/ /2021/readmes-for-open-science/ permanent +redir /2021/10/redacting-pdfs/ /2021/redacting-pdfs/ permanent +redir /2021/11/dominant-colours/ /2021/dominant-colours/ permanent +redir /2021/12/2021-in-reading/ /2021/2021-in-reading/ permanent +redir /2021/12/os-sep/ /2021/os-sep/ permanent +redir /2021/12/rust-errors/ /2021/rust-errors/ permanent +redir /2021/12/slashes/ /2021/slashes/ permanent +redir /2022/01/animated-artichokes/ /2022/animated-artichokes/ permanent +redir /2022/01/rusty-shelves/ /2022/rusty-shelves/ permanent +redir /2022/02/no-cause-for-alarm/ /2022/no-cause-for-alarm/ permanent +redir /2022/02/route53/ /2022/route53/ permanent +redir /2022/02/safari-tabs/ /2022/safari-tabs/ permanent +redir /2022/02/two-twitter-cards/ /2022/two-twitter-cards/ permanent +redir /2022/04/checking-with-curl/ /2022/checking-with-curl/ permanent +redir /2022/04/lorenz-wheels/ /2022/lorenz-wheels/ permanent +redir /2022/04/supposedly-simple-image-layout/ /2022/supposedly-simple-image-layout/ permanent +redir /2022/05/carbon-monoxide/ /2022/carbon-monoxide/ permanent +redir /2022/05/dominant-web-colours/ /2022/dominant-web-colours/ permanent +redir /2022/05/rust-on-glitch/ /2022/rust-on-glitch/ permanent +redir /2022/06/alfred-to-github/ /2022/alfred-to-github/ permanent +redir /2022/06/forgotten-secrets/ /2022/forgotten-secrets/ permanent +redir /2022/06/imaginary-numbers/ /2022/imaginary-numbers/ permanent +redir /2022/06/new-archive/ /2022/new-archive/ permanent +redir /2022/07/martian-plaque/ /2022/martian-plaque/ permanent +redir /2022/07/saturn-v/ /2022/saturn-v/ permanent +redir /2022/07/screenshots/ /2022/screenshots/ permanent +redir /2022/08/blink-diffs/ /2022/blink-diffs/ permanent +redir /2022/08/buildkite-deployments/ /2022/buildkite-deployments/ permanent +redir /2022/08/circle-party/ /2022/circle-party/ permanent +redir /2022/08/egyptian-mixtape/ /2022/egyptian-mixtape/ permanent +redir /2022/08/no-cute/ /2022/no-cute/ permanent +redir /2022/08/screenshots-go-gangbusters/ /2022/screenshots-go-gangbusters/ permanent +redir /2022/08/spaaaaaace/ /2022/spaaaaaace/ permanent +redir /2022/08/strict-jinja/ /2022/strict-jinja/ permanent +redir /2022/09/graph-generative-art/ /2022/graph-generative-art/ permanent +redir /2022/09/maths-cross-stitch/ /2022/maths-cross-stitch/ permanent +redir /2022/09/moomin-mathematics/ /2022/moomin-mathematics/ permanent +redir /2022/09/nextjs-props/ /2022/nextjs-props/ permanent +redir /2022/09/rust-1-64/ /2022/rust-1-64/ permanent +redir /2022/10/accessibility-fixes/ /2022/accessibility-fixes/ permanent +redir /2022/10/circle-experiments/ /2022/circle-experiments/ permanent +redir /2022/10/iterative-project-management/ /2022/iterative-project-management/ permanent +redir /2022/10/library-lookup/ /2022/library-lookup/ permanent +redir /2022/10/snapped-elastic/ /2022/snapped-elastic/ permanent +redir /2022/11/bure-valley/ /2022/bure-valley/ permanent +redir /2022/11/changing-the-macos-accent-colour/ /2022/changing-the-macos-accent-colour/ permanent +redir /2022/11/koa-logger-redactions/ /2022/koa-logger-redactions/ permanent +redir /2022/11/obsidian-plugin/ /2022/obsidian-plugin/ permanent +redir /2022/11/tin-anniversary/ /2022/tin-anniversary/ permanent +redir /2022/11/tweet-alt-text/ /2022/tweet-alt-text/ permanent +redir /2022/12/2022-in-reading/ /2022/2022-in-reading/ permanent +redir /2022/12/cursor-confirmed/ /2022/cursor-confirmed/ permanent +redir /2022/12/live-text-script/ /2022/live-text-script/ permanent +redir /2022/12/marquee-rocket/ /2022/marquee-rocket/ permanent +redir /2022/12/print-sbt/ /2022/print-sbt/ permanent +redir /2022/12/prismic-validation/ /2022/prismic-validation/ permanent + +# === # + + + + + + +# == And the old per-month archives == # + +redir /2012/12/ /articles/ permanent +redir /2013/01/ /articles/ permanent +redir /2013/02/ /articles/ permanent +redir /2013/03/ /articles/ permanent +redir /2013/05/ /articles/ permanent +redir /2013/08/ /articles/ permanent +redir /2013/11/ /articles/ permanent +redir /2014/04/ /articles/ permanent +redir /2014/05/ /articles/ permanent +redir /2014/06/ /articles/ permanent +redir /2014/07/ /articles/ permanent +redir /2014/08/ /articles/ permanent +redir /2014/09/ /articles/ permanent +redir /2014/10/ /articles/ permanent +redir /2014/11/ /articles/ permanent +redir /2014/12/ /articles/ permanent +redir /2015/01/ /articles/ permanent +redir /2015/02/ /articles/ permanent +redir /2015/03/ /articles/ permanent +redir /2015/05/ /articles/ permanent +redir /2015/06/ /articles/ permanent +redir /2015/07/ /articles/ permanent +redir /2015/08/ /articles/ permanent +redir /2015/09/ /articles/ permanent +redir /2015/10/ /articles/ permanent +redir /2015/11/ /articles/ permanent +redir /2015/12/ /articles/ permanent +redir /2016/01/ /articles/ permanent +redir /2016/02/ /articles/ permanent +redir /2016/03/ /articles/ permanent +redir /2016/04/ /articles/ permanent +redir /2016/05/ /articles/ permanent +redir /2016/06/ /articles/ permanent +redir /2016/07/ /articles/ permanent +redir /2016/08/ /articles/ permanent +redir /2016/09/ /articles/ permanent +redir /2016/10/ /articles/ permanent +redir /2016/11/ /articles/ permanent +redir /2016/12/ /articles/ permanent +redir /2017/01/ /articles/ permanent +redir /2017/02/ /articles/ permanent +redir /2017/03/ /articles/ permanent +redir /2017/04/ /articles/ permanent +redir /2017/06/ /articles/ permanent +redir /2017/07/ /articles/ permanent +redir /2017/09/ /articles/ permanent +redir /2017/10/ /articles/ permanent +redir /2017/11/ /articles/ permanent +redir /2017/12/ /articles/ permanent +redir /2018/01/ /articles/ permanent +redir /2018/02/ /articles/ permanent +redir /2018/03/ /articles/ permanent +redir /2018/04/ /articles/ permanent +redir /2018/05/ /articles/ permanent +redir /2018/07/ /articles/ permanent +redir /2018/08/ /articles/ permanent +redir /2018/09/ /articles/ permanent +redir /2018/10/ /articles/ permanent +redir /2018/11/ /articles/ permanent +redir /2018/12/ /articles/ permanent +redir /2019/01/ /articles/ permanent +redir /2019/02/ /articles/ permanent +redir /2019/03/ /articles/ permanent +redir /2019/04/ /articles/ permanent +redir /2019/05/ /articles/ permanent +redir /2019/06/ /articles/ permanent +redir /2019/07/ /articles/ permanent +redir /2019/08/ /articles/ permanent +redir /2019/09/ /articles/ permanent +redir /2019/10/ /articles/ permanent +redir /2019/11/ /articles/ permanent +redir /2019/12/ /articles/ permanent +redir /2020/01/ /articles/ permanent +redir /2020/02/ /articles/ permanent +redir /2020/03/ /articles/ permanent +redir /2020/04/ /articles/ permanent +redir /2020/05/ /articles/ permanent +redir /2020/06/ /articles/ permanent +redir /2020/07/ /articles/ permanent +redir /2020/08/ /articles/ permanent +redir /2020/09/ /articles/ permanent +redir /2020/10/ /articles/ permanent +redir /2020/11/ /articles/ permanent +redir /2020/12/ /articles/ permanent +redir /2021/01/ /articles/ permanent +redir /2021/02/ /articles/ permanent +redir /2021/03/ /articles/ permanent +redir /2021/04/ /articles/ permanent +redir /2021/06/ /articles/ permanent +redir /2021/07/ /articles/ permanent +redir /2021/08/ /articles/ permanent +redir /2021/09/ /articles/ permanent +redir /2021/10/ /articles/ permanent +redir /2021/11/ /articles/ permanent +redir /2021/12/ /articles/ permanent +redir /2022/01/ /articles/ permanent +redir /2022/02/ /articles/ permanent +redir /2022/04/ /articles/ permanent +redir /2022/05/ /articles/ permanent +redir /2022/06/ /articles/ permanent +redir /2022/07/ /articles/ permanent +redir /2022/08/ /articles/ permanent +redir /2022/09/ /articles/ permanent +redir /2022/10/ /articles/ permanent +redir /2022/11/ /articles/ permanent +redir /2022/12/ /articles/ permanent + +# === # + + + + + +# == Ensure that dated posts are "hackable" == # +# +# i.e. that you can delete bits off the end of the URL and still get +# somewhere useful. + +redir /til/2024/ /til/ permanent +redir /til/2023/ /til/ permanent +redir /til/2022/ /til/ permanent +redir /til/2021/ /til/ permanent +redir /til/2020/ /til/ permanent +redir /til/2019/ /til/ permanent +redir /til/2018/ /til/ permanent +redir /til/2016/ /til/ permanent +redir /til/2013/ /til/ permanent + +redir /2024/ /articles/ permanent +redir /2023/ /articles/ permanent +redir /2022/ /articles/ permanent +redir /2021/ /articles/ permanent +redir /2020/ /articles/ permanent +redir /2019/ /articles/ permanent +redir /2018/ /articles/ permanent +redir /2017/ /articles/ permanent +redir /2016/ /articles/ permanent +redir /2015/ /articles/ permanent +redir /2014/ /articles/ permanent +redir /2013/ /articles/ permanent +redir /2012/ /articles/ permanent + +# === # + + + + + +# == Misc redirects == # + +redir /files/2024/looped_squares.html https://looped-squares.glitch.me +redir /files/2024/looped_squares/ https://looped-squares.glitch.me +redir /files/2024/looped_squares https://looped-squares.glitch.me + +# === # + + + + + +# == Redirects for reshuffling in April 2024 == # +# +# In April 2024 I moved some of my old posts into the TIL section, so +# their URLs changed. Make sure they get redirected! +# +# I also removed some old posts which I didn't think were worth keeping +# around, and want them to get a 410 Gone. + +redir /2013/rss-podcasts-tumblr/ /til/2013/rss-podcasts-tumblr/ permanent + +redir /exams/ /2015/technical-exams/ permanent + +redir /2023/mechanize-ssl/ /til/2023/mechanize-ssl/ permanent +redir /2020/til-using-git-check-ignore-to-debug-your-gitignore/ /til/2020/using-git-check-ignore-to-debug-your-gitignore/ permanent +redir /2018/horstmann-electric-7/ /til/2018/horstmann-electric-7/ permanent +redir /2016/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ permanent +redir /2016/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ permanent + +# === # + + + + +# == Redirects for reshuffling in October 2024 == # +# +# In October 2024 I rejigged stuff as part of redoing the tag pages, including +# changing some tag names. They aren't super commonly uses, but I did find +# a few broken links in Google to fix. + +redir /tags/amazon-s3/ /tags/aws/amazon-s3/ permanent + +# === # diff --git a/src/400.md b/src/400.md deleted file mode 100644 index 55fe9e9c7..000000000 --- a/src/400.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: 400 Bad Request -layout: compress ---- -{% comment %} - This is only served to malicious crawlers, e.g. people looking for - /wp-login.php, so keep it as small as possible! -{% endcomment %} - -400 Bad Request diff --git a/src/_redirects b/src/_redirects deleted file mode 100644 index 002f6b00d..000000000 --- a/src/_redirects +++ /dev/null @@ -1,1275 +0,0 @@ -# This proxies to the inclusive events site in the background, -# presenting it at this sub-path. -# -# See https://answers.netlify.com/t/support-guide-can-i-deploy-multiple-repositories-in-a-single-site/179 -/ideas-for-inclusive-events/* https://ideas-for-inclusive-events.netlify.app/:splat 200 - - -# == Redirects for common RSS-like URLs, or RSS URLs I've used in the past == # - -/feed /atom.xml -/rss /atom.xml -/feed.xml /atom.xml -/rss.xml /atom.xml -/feeds/all.atom.xml /atom.xml - -# === # - - - - -# === Redirects for my Masto.host instance # -# -# See https://jacobian.org/til/my-mastodon-instance/ - -/.well-known/host-meta* https://social.alexwlchan.net/.well-known/host-meta:splat 301 -/.well-known/webfinger* https://social.alexwlchan.net/.well-known/webfinger:splat 301 -/.well-known/nodeinfo* https://social.alexwlchan.net/.well-known/nodeinfo:splat 301 - -# === # - - - - -# == Redirects for theming/style resources == # - -/images/favicon.ico /favicon.ico -/theme/favicon.ico /favicon.ico -/theme/favicon.png /favicon.png -/view/img/favicon.ico /favicon.ico - -/apple-touch-icon.png /theme/apple-touch-icon.png -/apple-touch-icon-precomposed.png /theme/apple-touch-icon.png -/apple-touch-icon-120x120-precomposed.png /theme/apple-touch-icon.png -/apple-touch-icon-120x120.png /theme/apple-touch-icon.png -/apple-touch-icon-152x152-precomposed.png /theme/apple-touch-icon.png -/apple-touch-icon-152x152.png /theme/apple-touch-icon.png - -/images/apple-touch-icon.png /theme/apple-touch-icon.png - -/analytics/a.js /410/ 410 - -/theme/style-sample /410/ 410 - -# === # - - - - - -# == Redirects for images == # - -/images/2014-04-20-veil.jpg /images/2014/deathly-veil.jpg -/images/2014-04-20-veil.png /images/2014/deathly-veil.jpg -/images/2014/galant_leg.jpg /images/2014/P8050572.jpg -/images/2014/standing_desk.jpg /images/2014/P8050559.jpg -/images/2014/standing_desk_side.jpg /images/2014/P8050561.jpg -/images/notes/resophnotes-directory.png /images/2014/resophnotes-directory.png -/images/notes/resophnotes-styled.png /images/2014/resophnotes-styled.png -/images/notes/resophnotes-unstyled.png /images/2014/resophnotes-unstyled.png - -/images/2015-05-github-blue.png /images/2015/github_blue.png -/images/2015-05-github-chart.png /images/2015/github_chart.png -/images/2015-05-github-green.png /images/2015/github_green.png -/images/2015-09-cortex-disabled.PNG /images/2015/cortex-disabled.PNG -/images/2015-09-pizzaexpress-disabled.PNG /images/2015/pizzaexpress-disabled.PNG -/images/2015-09-vexelology-disabled.PNG /images/2015/vexelology-disabled.PNG -/images/2015-09-vexelology-enabled.PNG /images/2015/vexelology-enabled.PNG - -/images/notes/boiler-panel.png /images/2016/boiler-panel.png -/images/notes/tricity-sie454.png /images/2016/tricity-sie454.png -/images/2016-01-hp-ipod-1.jpg /images/2016/hp-ipod-1.jpg -/images/2016-09-speech-to-text.jpg /images/2016/speech-to-text.jpg -/images/2016-09-tech-bag.jpg /images/2016/tech-bag.jpg -/images/specktre_demo_hex.png /images/2016/specktre_demo_hex.png -/images/specktre_demo_sq.png /images/2016/specktre_demo_sq.png -/images/specktre_demo_tr.png /images/2016/specktre_demo_tr.png -/images/wiki-hexagons.png /images/2016/wiki-hexagons.png -/images/wiki-squares.png /images/2016/wiki-squares.png -/images/wiki-triangles.png /images/2016/wiki-triangles.png - -/images/2017/P5280917_2x.jpg /images/2017/P5280917.jpg -/slides/pyconuk_asexuality.png /images/2017/pyconuk_asexuality.jpg - -/images/2022/acme_1x.jpg /images/2022/acme_corporation_delicious_1x.png -/images/2022/acme_2x.jpg /images/2022/acme_corporation_delicious_2x.png -/images/2022/prismic-dashboard_1x.png /images/2022/prismic_dashboard_1x.png -/images/2022/prismic-dashboard_2x.png /images/2022/prismic_dashboard_2x.png - -/images/2018/worst-intent.png /images/cards/2018/assume-worst-intent.png -/images/2022/10-of-cards.jpg /images/cards/2022/tin-anniversary.jpg -/images/2022/koa-redactions-card.jpg /images/cards/2022/koa-logger-redactions.jpg -/images/2022/bodleian.jpg /images/cards/2022/library-lookup.jpg -/images/2022/marquee_rocket_card.jpg /images/cards/2022/marquee-rocket.jpg -/images/2022/screenshot_card.png /images/cards/2022/screenshots.png -/images/2022/guest_headers_card.png /images/cards/2022/no-cute.png - -/images/pyconuk-speaking.jpg /images/projects/pyconuk-speaking.jpg - -/images/talks/oxford_git.jpg /410/ 410 -/images/talks/suspicious_minds.jpg /410/ 410 -/images/talks/worst_intent.png /410/ 410 - -# === # - - - - - -# == Redirects for pages which aren't attached to blog posts == - -/2018/03/plumbers-guide-intro/ /a-plumbers-guide-to-git/ -/a-plumbers-guide-to-git/1 /a-plumbers-guide-to-git/1-the-git-object-store/ -/a-plumbers-guide-to-git/1/ /a-plumbers-guide-to-git/1-the-git-object-store/ -/a-plumbers-guide-to-git/2 /a-plumbers-guide-to-git/2-blobs-and-trees/ -/a-plumbers-guide-to-git/2/ /a-plumbers-guide-to-git/2-blobs-and-trees/ -/a-plumbers-guide-to-git/3 /a-plumbers-guide-to-git/3-context-from-commits/ -/a-plumbers-guide-to-git/3/ /a-plumbers-guide-to-git/3-context-from-commits/ -/a-plumbers-guide-to-git/4 /a-plumbers-guide-to-git/4-refs-and-branches/ -/a-plumbers-guide-to-git/4/ /a-plumbers-guide-to-git/4-refs-and-branches/ - -/archives /articles/ -/archives/ /articles/ -/archive /articles/ -/archive/ /articles/ -/all-posts-by-tag/ /articles/ -/all-posts/ /articles/ -/posts/ /articles/ -/writing/ /articles/ - -/about / -/about/ / - -/page/* /articles/ - -/talks /tags/talks/ - -/tag /410/ 410 -/tag/ /410/ 410 -/notes /410/ 410 - -# === # - - - - - -# == Redirects for individual blog posts == - -# Removing the /blog prefix -/blog/2012/12/hypercritical/ /2012/hypercritical/ -/blog/2013/03/pinboard-backups/ /2013/pinboard-backups/ -/blog/2013/03/candybar/ /2013/candybar/ - -# Moving everything under /talks to dated entries -/talks/hypothesis-intro/ /2016/hypothesis-intro/ -/talks/pyconuk2016 /2016/pyconuk2016/ -/talks/qcon2017 /2017/qcon2017/ - -# Moving everything under /notes to dated entries -/notes/custom-css-resophnotes /2014/custom-css-resophnotes/ -/notes/custom-css-resophnotes/ /2014/custom-css-resophnotes/ -/notes/tricity-sie454-clock /til/2016/tricity-sie454-clock/ -/notes/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ -/notes/worcester-28cdi-clock /til/2016/worcester-28cdi-clock/ -/notes/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ - -# Cases where the slug on the blog post changed after I'd published -/2016/05/safely-deleting-a-file-called-rf-/ /2016/safely-deleting-a-file-called-rf/ -/2016/05/safely-deleting-a-file-called-rf- /2016/safely-deleting-a-file-called-rf/ -/2017/01/a-python-interface-to-ao3 /2017/experiments-with-ao3-and-python/ -/2017/01/a-python-interface-to-ao3/ /2017/experiments-with-ao3-and-python/ -/2021/01/what-year-it-it/ /2021/what-year-is-it/ -/2017/04/accessibility-at-alterconf /2017/lessons-from-alterconf/ -/2017/04/accessibility-at-alterconf/ /2017/lessons-from-alterconf/ - -# I put up a post linking to my slides, then a month later I put up -# another post with an annotated set of slides and notes. -/2019/09/slides-for-pycon-uk-2019/ /2019/sans-io-programming/ - -# This was a broken redirect I had for a while -/2016/07/hypothesis-intro/ /2016/hypothesis-intro/ - -# I took this post down in June 2023 -/2019/removing-the-drm-from-my-kindle-books/ /410/ 410 - -# === # - - - - - -# == Redirects for files == # - -/maths/part-ib/geometry.pdf /files/geometry.pdf -/maths/part-ib/linear-algebra.pdf /files/linear-algebra.pdf -/maths/part-ib/met-top-spaces.pdf /files/met-top-spaces.pdf - -/maths/geometry.pdf /files/geometry.pdf -/maths/linear-algebra.pdf /files/linear-algebra.pdf -/maths/met-top-spaces.pdf /files/met-top-spaces.pdf - -# This is some changes I made in Christmas 2022 to tidy up the structure -# of the 'files' directory; I had a bunch of files which were in the -# top-level, and not in the per-year folders I'd started using for -# newer files. -/files/find_untagged_posts.py /files/2013/find_untagged_posts.py -/files/untagged-posts.py /files/2013/find_untagged_posts.py - -/files/curlparser.py /files/2014/curlparser.py -/files/curlparser-examples.txt /files/2014/curlparser-examples.txt -/files/open-recent-screenshots.zip /files/2014/open-recent-screenshots.zip - -/files/gitignore_global.txt /files/2015/gitignore_global.txt -/files/readinglist.py /files/2015/readinglist.py -/skeletor/skeletor_jan2015.pdf /files/2015/skeletor_jan2015.pdf -/skeletor/skeletor_jan2015.png /files/2015/skeletor_jan2015.png - -/files/send_to_kindle.py /files/2016/send_to_kindle.py -/skeletor/skeletor_jan2016.pdf /files/2016/skeletor_jan2016.pdf -/skeletor/skeletor_jan2016.png /files/2016/skeletor_jan2016.png -/skeletor/skeletor_jan2016.tex /files/2016/skeletor_jan2016.tex -/files/tilings.py /files/2016/tilings.py - -/files/crossness_beam.mp4 /files/2017/crossness_beam.mp4 -/videos/crossness_beam.mp4 /files/2017/crossness_beam.mp4 -/files/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 -/videos/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 -/slides/docopt/docopt_slides.pdf /files/2017/docopt_slides.pdf -/talks/hypothesis_qcon17.pdf /files/2017/hypothesis_qcon17.pdf -/slides/privilege_inclusion/privilege_inclusion_slides.pdf /files/2017/privilege_inclusion_slides.pdf -/files/tweet_templates.key /files/2017/tweet_templates.key -/files/tweet_templates.ppt /files/2017/tweet_templates.ppt - -/slides/anti_social_media/anti_social_media_slides.pdf /files/2018/anti_social_media_slides.pdf -/slides/assume_worst_intent.pdf /files/2018/assume_worst_intent.pdf -/files/assume_worst_intent.pdf /files/2018/assume_worst_intent.pdf -/talks/suspicious-minds.pdf /files/2018/suspicious-minds.pdf - -/files/curbcut-monkigras.pdf /files/2019/curbcut-monkigras.pdf -/files/example_loan.xlsx /files/2019/example_loan.xlsx -/files/pyconuk_2019_sans_io.pdf /files/2019/pyconuk_2019_sans_io.pdf -/files/wcag-black-and-white.pdf /files/2019/wcag-black-and-white.pdf - -/skeletor/skeletor_feb2020.pdf /files/2020/skeletor_feb2020.pdf -/skeletor/skeletor_feb2020.png /files/2020/skeletor_feb2020.png -/skeletor/skeletor_feb2020.tex /files/2020/skeletor_feb2020.tex - -/files/boiler_instructions.pdf /410/ 410 -/files/colossus.pdf /410/ 410 -/files/ga-privacy.txt /410/ 410 -/files/rock-crushers.txt /410/ 410 -/talks/campug_git.pdf /410/ 410 -/talks/colossus_operators.jpg /410/ 410 -/talks/hypothesis_cover.png /410/ 410 -/talks/pyconuk2016_cover.png /410/ 410 - -# I have no idea why this particular slide is so popular and getting -# so many 404s, but adding a redirect for it. -/talks/hypothesis-intro/hypothesis-slide004.png /images/2016/hypothesis-intro/slide4.png -/talks/hypothesis-intro/hypothesis-slide029.png /images/2016/hypothesis-intro/slide29.png - -# === # - - - - -# == Redirects for slides == # - -/slides/sans_io/sans_io.001.jpg /images/2019/sans-io-programming/slide1.jpg -/slides/sans_io/sans_io.002.jpg /images/2019/sans-io-programming/slide2.jpg -/slides/sans_io/sans_io.003.jpg /images/2019/sans-io-programming/slide3.jpg -/slides/sans_io/sans_io.004.jpg /images/2019/sans-io-programming/slide4.jpg -/slides/sans_io/sans_io.005.jpg /images/2019/sans-io-programming/slide5.jpg -/slides/sans_io/sans_io.006.jpg /images/2019/sans-io-programming/slide6.jpg -/slides/sans_io/sans_io.007.jpg /images/2019/sans-io-programming/slide7.jpg -/slides/sans_io/sans_io.008.jpg /images/2019/sans-io-programming/slide8.jpg -/slides/sans_io/sans_io.009.jpg /images/2019/sans-io-programming/slide9.jpg -/slides/sans_io/sans_io.010.jpg /images/2019/sans-io-programming/slide10.jpg -/slides/sans_io/sans_io.011.jpg /images/2019/sans-io-programming/slide11.jpg -/slides/sans_io/sans_io.012.jpg /images/2019/sans-io-programming/slide12.jpg -/slides/sans_io/sans_io.013.jpg /images/2019/sans-io-programming/slide13.jpg -/slides/sans_io/sans_io.014.jpg /images/2019/sans-io-programming/slide14.jpg -/slides/sans_io/sans_io.015.jpg /images/2019/sans-io-programming/slide15.jpg -/slides/sans_io/sans_io.016.jpg /images/2019/sans-io-programming/slide16.jpg -/slides/sans_io/sans_io.017.jpg /images/2019/sans-io-programming/slide17.jpg -/slides/sans_io/sans_io.018.jpg /images/2019/sans-io-programming/slide18.jpg -/slides/sans_io/sans_io.019.jpg /images/2019/sans-io-programming/slide19.jpg -/slides/sans_io/sans_io.020.jpg /images/2019/sans-io-programming/slide20.jpg -/slides/sans_io/sans_io.021.jpg /images/2019/sans-io-programming/slide21.jpg -/slides/sans_io/sans_io.022.jpg /images/2019/sans-io-programming/slide22.jpg -/slides/sans_io/sans_io.023.jpg /images/2019/sans-io-programming/slide23.jpg -/slides/sans_io/sans_io.024.jpg /images/2019/sans-io-programming/slide24.jpg -/slides/sans_io/sans_io.025.jpg /images/2019/sans-io-programming/slide25.jpg -/slides/sans_io/sans_io.026.jpg /images/2019/sans-io-programming/slide26.jpg -/slides/sans_io/sans_io.027.jpg /images/2019/sans-io-programming/slide27.jpg -/slides/sans_io/sans_io.028.jpg /images/2019/sans-io-programming/slide28.jpg -/slides/sans_io/sans_io.029.jpg /images/2019/sans-io-programming/slide29.jpg -/slides/sans_io/sans_io.030.jpg /images/2019/sans-io-programming/slide30.jpg -/slides/sans_io/sans_io.031.jpg /images/2019/sans-io-programming/slide31.jpg -/slides/sans_io/sans_io.032.jpg /images/2019/sans-io-programming/slide32.jpg - -/slides/hypothesis-intro/hypothesis-intro.000.png /images/2016/hypothesis-intro/slide0.png -/slides/hypothesis-intro/hypothesis-intro.001.png /images/2016/hypothesis-intro/slide1.png -/slides/hypothesis-intro/hypothesis-intro.002.png /images/2016/hypothesis-intro/slide2.png -/slides/hypothesis-intro/hypothesis-intro.003.png /images/2016/hypothesis-intro/slide3.png -/slides/hypothesis-intro/hypothesis-intro.004.png /images/2016/hypothesis-intro/slide4.png -/slides/hypothesis-intro/hypothesis-intro.005.png /images/2016/hypothesis-intro/slide5.png -/slides/hypothesis-intro/hypothesis-intro.006.png /images/2016/hypothesis-intro/slide6.png -/slides/hypothesis-intro/hypothesis-intro.007.png /images/2016/hypothesis-intro/slide7.png -/slides/hypothesis-intro/hypothesis-intro.008.png /images/2016/hypothesis-intro/slide8.png -/slides/hypothesis-intro/hypothesis-intro.009.png /images/2016/hypothesis-intro/slide9.png -/slides/hypothesis-intro/hypothesis-intro.010.png /images/2016/hypothesis-intro/slide10.png -/slides/hypothesis-intro/hypothesis-intro.011.png /images/2016/hypothesis-intro/slide11.png -/slides/hypothesis-intro/hypothesis-intro.012.png /images/2016/hypothesis-intro/slide12.png -/slides/hypothesis-intro/hypothesis-intro.013.png /images/2016/hypothesis-intro/slide13.png -/slides/hypothesis-intro/hypothesis-intro.014.png /images/2016/hypothesis-intro/slide14.png -/slides/hypothesis-intro/hypothesis-intro.015.png /images/2016/hypothesis-intro/slide15.png -/slides/hypothesis-intro/hypothesis-intro.016.png /images/2016/hypothesis-intro/slide16.png -/slides/hypothesis-intro/hypothesis-intro.017.png /images/2016/hypothesis-intro/slide17.png -/slides/hypothesis-intro/hypothesis-intro.018.png /images/2016/hypothesis-intro/slide18.png -/slides/hypothesis-intro/hypothesis-intro.019.png /images/2016/hypothesis-intro/slide19.png -/slides/hypothesis-intro/hypothesis-intro.020.png /images/2016/hypothesis-intro/slide20.png -/slides/hypothesis-intro/hypothesis-intro.021.png /images/2016/hypothesis-intro/slide21.png -/slides/hypothesis-intro/hypothesis-intro.022.png /images/2016/hypothesis-intro/slide22.png -/slides/hypothesis-intro/hypothesis-intro.023.png /images/2016/hypothesis-intro/slide23.png -/slides/hypothesis-intro/hypothesis-intro.024.png /images/2016/hypothesis-intro/slide24.png -/slides/hypothesis-intro/hypothesis-intro.025.png /images/2016/hypothesis-intro/slide25.png -/slides/hypothesis-intro/hypothesis-intro.026.png /images/2016/hypothesis-intro/slide26.png -/slides/hypothesis-intro/hypothesis-intro.027.png /images/2016/hypothesis-intro/slide27.png -/slides/hypothesis-intro/hypothesis-intro.028.png /images/2016/hypothesis-intro/slide28.png -/slides/hypothesis-intro/hypothesis-intro.029.png /images/2016/hypothesis-intro/slide29.png -/slides/hypothesis-intro/hypothesis-intro.030.png /images/2016/hypothesis-intro/slide30.png -/slides/hypothesis-intro/hypothesis-intro.031.png /images/2016/hypothesis-intro/slide31.png -/slides/hypothesis-intro/hypothesis-intro.032.png /images/2016/hypothesis-intro/slide32.png -/slides/hypothesis-intro/hypothesis-intro.033.png /images/2016/hypothesis-intro/slide33.png -/slides/hypothesis-intro/hypothesis-intro.034.png /images/2016/hypothesis-intro/slide34.png -/slides/hypothesis-intro/hypothesis-intro.035.png /images/2016/hypothesis-intro/slide35.png -/slides/hypothesis-intro/hypothesis-intro.036.png /images/2016/hypothesis-intro/slide36.png -/slides/hypothesis-intro/hypothesis-intro.037.png /images/2016/hypothesis-intro/slide37.png -/slides/hypothesis-intro/hypothesis-intro.038.png /images/2016/hypothesis-intro/slide38.png -/slides/hypothesis-intro/hypothesis-intro.039.png /images/2016/hypothesis-intro/slide39.png -/slides/hypothesis-intro/hypothesis-intro.040.png /images/2016/hypothesis-intro/slide40.png -/slides/hypothesis-intro/hypothesis-intro.041.png /images/2016/hypothesis-intro/slide41.png -/slides/hypothesis-intro/hypothesis-intro.042.png /images/2016/hypothesis-intro/slide42.png -/slides/hypothesis-intro/hypothesis-intro.043.png /images/2016/hypothesis-intro/slide43.png -/slides/hypothesis-intro/hypothesis-intro.044.png /images/2016/hypothesis-intro/slide44.png -/slides/hypothesis-intro/hypothesis-intro.045.png /images/2016/hypothesis-intro/slide45.png -/slides/hypothesis-intro/hypothesis-intro.046.png /images/2016/hypothesis-intro/slide46.png -/slides/hypothesis-intro/hypothesis-intro.047.png /images/2016/hypothesis-intro/slide47.png -/slides/hypothesis-intro/hypothesis-intro.048.png /images/2016/hypothesis-intro/slide48.png -/slides/hypothesis-intro/hypothesis-intro.049.png /images/2016/hypothesis-intro/slide49.png -/slides/hypothesis-intro/hypothesis-intro.050.png /images/2016/hypothesis-intro/slide50.png -/slides/hypothesis-intro/hypothesis-intro.051.png /images/2016/hypothesis-intro/slide51.png -/slides/hypothesis-intro/hypothesis-intro.052.png /images/2016/hypothesis-intro/slide52.png -/slides/hypothesis-intro/hypothesis-intro.053.png /images/2016/hypothesis-intro/slide53.png -/slides/hypothesis-intro/hypothesis-intro.054.png /images/2016/hypothesis-intro/slide54.png -/slides/hypothesis-intro/hypothesis-intro.055.png /images/2016/hypothesis-intro/slide55.png - -/slides/docopt/docopt.001.png /images/2017/ode-to-docopt/slide1.png -/slides/docopt/docopt.002.png /images/2017/ode-to-docopt/slide2.png -/slides/docopt/docopt.003.png /images/2017/ode-to-docopt/slide3.png -/slides/docopt/docopt.004.png /images/2017/ode-to-docopt/slide4.png -/slides/docopt/docopt.005.png /images/2017/ode-to-docopt/slide5.png -/slides/docopt/docopt.006.png /images/2017/ode-to-docopt/slide6.png -/slides/docopt/docopt.007.png /images/2017/ode-to-docopt/slide7.png -/slides/docopt/docopt.008.png /images/2017/ode-to-docopt/slide8.png -/slides/docopt/docopt.009.png /images/2017/ode-to-docopt/slide9.png -/slides/docopt/docopt.010.png /images/2017/ode-to-docopt/slide10.png -/slides/docopt/docopt.011.png /images/2017/ode-to-docopt/slide11.png -/slides/docopt/docopt.012.png /images/2017/ode-to-docopt/slide12.png -/slides/docopt/docopt.013.png /images/2017/ode-to-docopt/slide13.png -/slides/docopt/docopt.014.png /images/2017/ode-to-docopt/slide14.png - -/slides/ols_afterthought/ols_afterthought.001.png /images/2020/inclusion-cant-be-an-afterthought/slide1.png -/slides/ols_afterthought/ols_afterthought.002.jpg /images/2020/inclusion-cant-be-an-afterthought/slide2.jpg -/slides/ols_afterthought/ols_afterthought.003.jpg /images/2020/inclusion-cant-be-an-afterthought/slide3.jpg -/slides/ols_afterthought/ols_afterthought.004.jpg /images/2020/inclusion-cant-be-an-afterthought/slide4.jpg -/slides/ols_afterthought/ols_afterthought.005.png /images/2020/inclusion-cant-be-an-afterthought/slide5.png -/slides/ols_afterthought/ols_afterthought.006.png /images/2020/inclusion-cant-be-an-afterthought/slide6.png -/slides/ols_afterthought/ols_afterthought.007.jpg /images/2020/inclusion-cant-be-an-afterthought/slide7.jpg -/slides/ols_afterthought/ols_afterthought.008.jpg /images/2020/inclusion-cant-be-an-afterthought/slide8.jpg -/slides/ols_afterthought/ols_afterthought.009.png /images/2020/inclusion-cant-be-an-afterthought/slide9.png -/slides/ols_afterthought/ols_afterthought.010.png /images/2020/inclusion-cant-be-an-afterthought/slide10.png -/slides/ols_afterthought/ols_afterthought.011.png /images/2020/inclusion-cant-be-an-afterthought/slide11.png - -/slides/privilege_inclusion/privilege_inclusion.001.png /images/2017/pyconuk-2017-privilege-inclusion/slide1.png -/slides/privilege_inclusion/privilege_inclusion.003.png /images/2017/pyconuk-2017-privilege-inclusion/slide3.png -/slides/privilege_inclusion/privilege_inclusion.004.png /images/2017/pyconuk-2017-privilege-inclusion/slide4.png -/slides/privilege_inclusion/privilege_inclusion.005.png /images/2017/pyconuk-2017-privilege-inclusion/slide5.png -/slides/privilege_inclusion/privilege_inclusion.006.png /images/2017/pyconuk-2017-privilege-inclusion/slide6.png -/slides/privilege_inclusion/privilege_inclusion.007.png /images/2017/pyconuk-2017-privilege-inclusion/slide7.png -/slides/privilege_inclusion/privilege_inclusion.008.png /images/2017/pyconuk-2017-privilege-inclusion/slide8.png -/slides/privilege_inclusion/privilege_inclusion.009.png /images/2017/pyconuk-2017-privilege-inclusion/slide9.png -/slides/privilege_inclusion/privilege_inclusion.010.png /images/2017/pyconuk-2017-privilege-inclusion/slide10.png -/slides/privilege_inclusion/privilege_inclusion.011.png /images/2017/pyconuk-2017-privilege-inclusion/slide11.png -/slides/privilege_inclusion/privilege_inclusion.012.png /images/2017/pyconuk-2017-privilege-inclusion/slide12.png -/slides/privilege_inclusion/privilege_inclusion.015.png /images/2017/pyconuk-2017-privilege-inclusion/slide15.png -/slides/privilege_inclusion/privilege_inclusion.016.png /images/2017/pyconuk-2017-privilege-inclusion/slide16.png -/slides/privilege_inclusion/privilege_inclusion.019.png /images/2017/pyconuk-2017-privilege-inclusion/slide19.png -/slides/privilege_inclusion/privilege_inclusion.020.png /images/2017/pyconuk-2017-privilege-inclusion/slide20.png -/slides/privilege_inclusion/privilege_inclusion.021.png /images/2017/pyconuk-2017-privilege-inclusion/slide21.png -/slides/privilege_inclusion/privilege_inclusion.022.png /images/2017/pyconuk-2017-privilege-inclusion/slide22.png -/slides/privilege_inclusion/privilege_inclusion.024.png /images/2017/pyconuk-2017-privilege-inclusion/slide24.png -/slides/privilege_inclusion/privilege_inclusion.025.png /images/2017/pyconuk-2017-privilege-inclusion/slide25.png -/slides/privilege_inclusion/privilege_inclusion.026.png /images/2017/pyconuk-2017-privilege-inclusion/slide26.png -/slides/privilege_inclusion/privilege_inclusion.027.png /images/2017/pyconuk-2017-privilege-inclusion/slide27.png -/slides/privilege_inclusion/privilege_inclusion.028.png /images/2017/pyconuk-2017-privilege-inclusion/slide28.png -/slides/privilege_inclusion/privilege_inclusion.029.png /images/2017/pyconuk-2017-privilege-inclusion/slide29.png -/slides/privilege_inclusion/privilege_inclusion.030.png /images/2017/pyconuk-2017-privilege-inclusion/slide30.png -/slides/privilege_inclusion/privilege_inclusion.031.png /images/2017/pyconuk-2017-privilege-inclusion/slide31.png -/slides/privilege_inclusion/privilege_inclusion.032.png /images/2017/pyconuk-2017-privilege-inclusion/slide32.png -/slides/privilege_inclusion/privilege_inclusion.033.png /images/2017/pyconuk-2017-privilege-inclusion/slide33.png -/slides/privilege_inclusion/privilege_inclusion.035.png /images/2017/pyconuk-2017-privilege-inclusion/slide35.png -/slides/privilege_inclusion/privilege_inclusion.036.png /images/2017/pyconuk-2017-privilege-inclusion/slide36.png -/slides/privilege_inclusion/privilege_inclusion.037.png /images/2017/pyconuk-2017-privilege-inclusion/slide37.png -/slides/privilege_inclusion/privilege_inclusion.040.png /images/2017/pyconuk-2017-privilege-inclusion/slide40.png - -/slides/curbcut_monkigras/curbcut_monkigras.001.jpg /images/2019/monki-gras-the-curb-cut-effect/slide1.jpg -/slides/curbcut_monkigras/curbcut_monkigras.002.jpg /images/2019/monki-gras-the-curb-cut-effect/slide2.jpg -/slides/curbcut_monkigras/curbcut_monkigras.003.jpg /images/2019/monki-gras-the-curb-cut-effect/slide3.jpg -/slides/curbcut_monkigras/curbcut_monkigras.004.jpg /images/2019/monki-gras-the-curb-cut-effect/slide4.jpg -/slides/curbcut_monkigras/curbcut_monkigras.005.jpg /images/2019/monki-gras-the-curb-cut-effect/slide5.jpg -/slides/curbcut_monkigras/curbcut_monkigras.006.jpg /images/2019/monki-gras-the-curb-cut-effect/slide6.jpg -/slides/curbcut_monkigras/curbcut_monkigras.007.png /images/2019/monki-gras-the-curb-cut-effect/slide7.png -/slides/curbcut_monkigras/curbcut_monkigras.008.png /images/2019/monki-gras-the-curb-cut-effect/slide8.png -/slides/curbcut_monkigras/curbcut_monkigras.009.png /images/2019/monki-gras-the-curb-cut-effect/slide9.png -/slides/curbcut_monkigras/curbcut_monkigras.010.png /images/2019/monki-gras-the-curb-cut-effect/slide10.png -/slides/curbcut_monkigras/curbcut_monkigras.011.jpg /images/2019/monki-gras-the-curb-cut-effect/slide11.jpg -/slides/curbcut_monkigras/curbcut_monkigras.012.jpg /images/2019/monki-gras-the-curb-cut-effect/slide12.jpg -/slides/curbcut_monkigras/curbcut_monkigras.013.jpg /images/2019/monki-gras-the-curb-cut-effect/slide13.jpg -/slides/curbcut_monkigras/curbcut_monkigras.014.jpg /images/2019/monki-gras-the-curb-cut-effect/slide14.jpg -/slides/curbcut_monkigras/curbcut_monkigras.015.jpg /images/2019/monki-gras-the-curb-cut-effect/slide15.jpg -/slides/curbcut_monkigras/curbcut_monkigras.016.jpg /images/2019/monki-gras-the-curb-cut-effect/slide16.jpg -/slides/curbcut_monkigras/curbcut_monkigras.017.jpg /images/2019/monki-gras-the-curb-cut-effect/slide17.jpg -/slides/curbcut_monkigras/curbcut_monkigras.018.jpg /images/2019/monki-gras-the-curb-cut-effect/slide18.jpg -/slides/curbcut_monkigras/curbcut_monkigras.019.jpg /images/2019/monki-gras-the-curb-cut-effect/slide19.jpg -/slides/curbcut_monkigras/curbcut_monkigras.020.jpg /images/2019/monki-gras-the-curb-cut-effect/slide20.jpg -/slides/curbcut_monkigras/curbcut_monkigras.021.jpg /images/2019/monki-gras-the-curb-cut-effect/slide21.jpg -/slides/curbcut_monkigras/curbcut_monkigras.023.jpg /images/2019/monki-gras-the-curb-cut-effect/slide23.jpg -/slides/curbcut_monkigras/curbcut_monkigras.024.jpg /images/2019/monki-gras-the-curb-cut-effect/slide24.jpg -/slides/curbcut_monkigras/curbcut_monkigras.025.jpg /images/2019/monki-gras-the-curb-cut-effect/slide25.jpg -/slides/curbcut_monkigras/curbcut_monkigras.026.jpg /images/2019/monki-gras-the-curb-cut-effect/slide26.jpg -/slides/curbcut_monkigras/curbcut_monkigras.027.jpg /images/2019/monki-gras-the-curb-cut-effect/slide27.jpg -/slides/curbcut_monkigras/curbcut_monkigras.028.jpg /images/2019/monki-gras-the-curb-cut-effect/slide28.jpg - -/slides/assume_worst_intent/assume_worst_intent.001.png /images/2018/assume-worst-intent/slide1.png -/slides/assume_worst_intent/assume_worst_intent.002.png /images/2018/assume-worst-intent/slide2.png -/slides/assume_worst_intent/assume_worst_intent.003.png /images/2018/assume-worst-intent/slide3.png -/slides/assume_worst_intent/assume_worst_intent.004.png /images/2018/assume-worst-intent/slide4.png -/slides/assume_worst_intent/assume_worst_intent.005.png /images/2018/assume-worst-intent/slide5.png -/slides/assume_worst_intent/assume_worst_intent.006.png /images/2018/assume-worst-intent/slide6.png -/slides/assume_worst_intent/assume_worst_intent.007.png /images/2018/assume-worst-intent/slide7.png -/slides/assume_worst_intent/assume_worst_intent.008.png /images/2018/assume-worst-intent/slide8.png -/slides/assume_worst_intent/assume_worst_intent.009.png /images/2018/assume-worst-intent/slide9.png -/slides/assume_worst_intent/assume_worst_intent.010.png /images/2018/assume-worst-intent/slide10.png -/slides/assume_worst_intent/assume_worst_intent.011.png /images/2018/assume-worst-intent/slide11.png -/slides/assume_worst_intent/assume_worst_intent.012.jpeg /images/2018/assume-worst-intent/slide12.jpg -/slides/assume_worst_intent/assume_worst_intent.013.png /images/2018/assume-worst-intent/slide13.png -/slides/assume_worst_intent/assume_worst_intent.014.png /images/2018/assume-worst-intent/slide14.png -/slides/assume_worst_intent/assume_worst_intent.015.png /images/2018/assume-worst-intent/slide15.png -/slides/assume_worst_intent/assume_worst_intent.016.png /images/2018/assume-worst-intent/slide16.png -/slides/assume_worst_intent/assume_worst_intent.017.png /images/2018/assume-worst-intent/slide17.png -/slides/assume_worst_intent/assume_worst_intent.018.png /images/2018/assume-worst-intent/slide18.png -/slides/assume_worst_intent/assume_worst_intent.019.png /images/2018/assume-worst-intent/slide19.png -/slides/assume_worst_intent/assume_worst_intent.020.png /images/2018/assume-worst-intent/slide20.png -/slides/assume_worst_intent/assume_worst_intent.021.png /images/2018/assume-worst-intent/slide21.png -/slides/assume_worst_intent/assume_worst_intent.022.png /images/2018/assume-worst-intent/slide22.png -/slides/assume_worst_intent/assume_worst_intent.023.png /images/2018/assume-worst-intent/slide23.png -/slides/assume_worst_intent/assume_worst_intent.024.png /images/2018/assume-worst-intent/slide24.png -/slides/assume_worst_intent/assume_worst_intent.025.png /images/2018/assume-worst-intent/slide25.png -/slides/assume_worst_intent/assume_worst_intent.026.png /images/2018/assume-worst-intent/slide26.png -/slides/assume_worst_intent/assume_worst_intent.027.png /images/2018/assume-worst-intent/slide27.png -/slides/assume_worst_intent/assume_worst_intent.028.png /images/2018/assume-worst-intent/slide28.png -/slides/assume_worst_intent/assume_worst_intent.029.png /images/2018/assume-worst-intent/slide29.png -/slides/assume_worst_intent/assume_worst_intent.030.png /images/2018/assume-worst-intent/slide30.png -/slides/assume_worst_intent/assume_worst_intent.031.png /images/2018/assume-worst-intent/slide31.png -/slides/assume_worst_intent/assume_worst_intent.032.png /images/2018/assume-worst-intent/slide32.png -/slides/assume_worst_intent/assume_worst_intent.033.png /images/2018/assume-worst-intent/slide33.png -/slides/assume_worst_intent/assume_worst_intent.034.png /images/2018/assume-worst-intent/slide34.png -/slides/assume_worst_intent/assume_worst_intent.035.png /images/2018/assume-worst-intent/slide35.png -/slides/assume_worst_intent/assume_worst_intent.036.png /images/2018/assume-worst-intent/slide36.png -/slides/assume_worst_intent/assume_worst_intent.037.png /images/2018/assume-worst-intent/slide37.png -/slides/assume_worst_intent/assume_worst_intent.038.png /images/2018/assume-worst-intent/slide38.png -/slides/assume_worst_intent/assume_worst_intent.039.png /images/2018/assume-worst-intent/slide39.png -/slides/assume_worst_intent/assume_worst_intent.040.png /images/2018/assume-worst-intent/slide40.png -/slides/assume_worst_intent/assume_worst_intent.041.png /images/2018/assume-worst-intent/slide41.png -/slides/assume_worst_intent/assume_worst_intent.042.png /images/2018/assume-worst-intent/slide42.png -/slides/assume_worst_intent/assume_worst_intent.043.png /images/2018/assume-worst-intent/slide43.png -/slides/assume_worst_intent/assume_worst_intent.044.png /images/2018/assume-worst-intent/slide44.png -/slides/assume_worst_intent/assume_worst_intent.045.png /images/2018/assume-worst-intent/slide45.png -/slides/assume_worst_intent/assume_worst_intent.046.png /images/2018/assume-worst-intent/slide46.png -/slides/assume_worst_intent/assume_worst_intent.047.png /images/2018/assume-worst-intent/slide47.png -/slides/assume_worst_intent/assume_worst_intent.048.png /images/2018/assume-worst-intent/slide48.png - -/slides/suspicious-minds/suspicious-minds.001.jpg /images/2018/suspicious-minds/slide1.jpg -/slides/suspicious-minds/suspicious-minds.002.jpg /images/2018/suspicious-minds/slide2.jpg -/slides/suspicious-minds/suspicious-minds.003.png /images/2018/suspicious-minds/slide3.png -/slides/suspicious-minds/suspicious-minds.004.png /images/2018/suspicious-minds/slide4.png -/slides/suspicious-minds/suspicious-minds.005.png /images/2018/suspicious-minds/slide5.png -/slides/suspicious-minds/suspicious-minds.006.png /images/2018/suspicious-minds/slide6.png -/slides/suspicious-minds/suspicious-minds.007.png /images/2018/suspicious-minds/slide7.png -/slides/suspicious-minds/suspicious-minds.008.png /images/2018/suspicious-minds/slide8.png -/slides/suspicious-minds/suspicious-minds.009.png /images/2018/suspicious-minds/slide9.png -/slides/suspicious-minds/suspicious-minds.010.png /images/2018/suspicious-minds/slide10.png -/slides/suspicious-minds/suspicious-minds.011.jpg /images/2018/suspicious-minds/slide11.jpg -/slides/suspicious-minds/suspicious-minds.012.png /images/2018/suspicious-minds/slide12.png -/slides/suspicious-minds/suspicious-minds.013.png /images/2018/suspicious-minds/slide13.png -/slides/suspicious-minds/suspicious-minds.014.png /images/2018/suspicious-minds/slide14.png -/slides/suspicious-minds/suspicious-minds.015.png /images/2018/suspicious-minds/slide15.png -/slides/suspicious-minds/suspicious-minds.016.png /images/2018/suspicious-minds/slide16.png -/slides/suspicious-minds/suspicious-minds.017.jpg /images/2018/suspicious-minds/slide17.jpg -/slides/suspicious-minds/suspicious-minds.018.jpg /images/2018/suspicious-minds/slide18.jpg -/slides/suspicious-minds/suspicious-minds.019.png /images/2018/suspicious-minds/slide19.png -/slides/suspicious-minds/suspicious-minds.020.png /images/2018/suspicious-minds/slide20.png -/slides/suspicious-minds/suspicious-minds.021.png /images/2018/suspicious-minds/slide21.png -/slides/suspicious-minds/suspicious-minds.022.png /images/2018/suspicious-minds/slide22.png -/slides/suspicious-minds/suspicious-minds.023.png /images/2018/suspicious-minds/slide23.png -/slides/suspicious-minds/suspicious-minds.024.png /images/2018/suspicious-minds/slide24.png -/slides/suspicious-minds/suspicious-minds.025.png /images/2018/suspicious-minds/slide25.png -/slides/suspicious-minds/suspicious-minds.026.png /images/2018/suspicious-minds/slide26.png -/slides/suspicious-minds/suspicious-minds.027.png /images/2018/suspicious-minds/slide27.png -/slides/suspicious-minds/suspicious-minds.028.png /images/2018/suspicious-minds/slide28.png -/slides/suspicious-minds/suspicious-minds.029.jpg /images/2018/suspicious-minds/slide29.jpg -/slides/suspicious-minds/suspicious-minds.030.jpg /images/2018/suspicious-minds/slide30.jpg -/slides/suspicious-minds/suspicious-minds.031.png /images/2018/suspicious-minds/slide31.png -/slides/suspicious-minds/suspicious-minds.032.png /images/2018/suspicious-minds/slide32.png -/slides/suspicious-minds/suspicious-minds.033.png /images/2018/suspicious-minds/slide33.png -/slides/suspicious-minds/suspicious-minds.034.png /images/2018/suspicious-minds/slide34.png -/slides/suspicious-minds/suspicious-minds.035.png /images/2018/suspicious-minds/slide35.png -/slides/suspicious-minds/suspicious-minds.036.png /images/2018/suspicious-minds/slide36.png -/slides/suspicious-minds/suspicious-minds.037.jpg /images/2018/suspicious-minds/slide37.jpg -/slides/suspicious-minds/suspicious-minds.038.jpg /images/2018/suspicious-minds/slide38.jpg -/slides/suspicious-minds/suspicious-minds.039.png /images/2018/suspicious-minds/slide39.png -/slides/suspicious-minds/suspicious-minds.040.png /images/2018/suspicious-minds/slide40.png -/slides/suspicious-minds/suspicious-minds.041.png /images/2018/suspicious-minds/slide41.png -/slides/suspicious-minds/suspicious-minds.042.png /images/2018/suspicious-minds/slide42.png -/slides/suspicious-minds/suspicious-minds.043.png /images/2018/suspicious-minds/slide43.png -/slides/suspicious-minds/suspicious-minds.044.png /images/2018/suspicious-minds/slide44.png -/slides/suspicious-minds/suspicious-minds.045.png /images/2018/suspicious-minds/slide45.png -/slides/suspicious-minds/suspicious-minds.046.jpg /images/2018/suspicious-minds/slide46.jpg -/slides/suspicious-minds/suspicious-minds.046.jpeg /images/2018/suspicious-minds/slide46.jpg -/slides/suspicious-minds/suspicious-minds.047.png /images/2018/suspicious-minds/slide47.png -/slides/suspicious-minds/suspicious-minds.048.jpeg /images/2018/suspicious-minds/slide48.jpg -/slides/suspicious-minds/suspicious-minds.049.jpeg /images/2018/suspicious-minds/slide49.jpg -/slides/suspicious-minds/suspicious-minds.049.jpg /images/2018/suspicious-minds/slide49.jpg -/slides/suspicious-minds/suspicious-minds.050.png /images/2018/suspicious-minds/slide50.png -/slides/suspicious-minds/suspicious-minds.051.jpg /images/2018/suspicious-minds/slide51.jpg -/slides/suspicious-minds/suspicious-minds.051.jpeg /images/2018/suspicious-minds/slide51.jpg -/slides/suspicious-minds/suspicious-minds.052.jpg /images/2018/suspicious-minds/slide52.jpg -/slides/suspicious-minds/suspicious-minds.052.jpeg /images/2018/suspicious-minds/slide52.jpg -/slides/suspicious-minds/suspicious-minds.053.png /images/2018/suspicious-minds/slide53.png - -/slides/anti_social_media/anti_social_media.001.png /images/2018/anti-social-media/slide1.png -/slides/anti_social_media/anti_social_media.002.png /images/2018/anti-social-media/slide2.png -/slides/anti_social_media/anti_social_media.004.png /images/2018/anti-social-media/slide4.png -/slides/anti_social_media/anti_social_media.005.png /images/2018/anti-social-media/slide5.png -/slides/anti_social_media/anti_social_media.006.png /images/2018/anti-social-media/slide6.png -/slides/anti_social_media/anti_social_media.007.png /images/2018/anti-social-media/slide7.png -/slides/anti_social_media/anti_social_media.008.png /images/2018/anti-social-media/slide8.png -/slides/anti_social_media/anti_social_media.009.png /images/2018/anti-social-media/slide9.png -/slides/anti_social_media/anti_social_media.010.png /images/2018/anti-social-media/slide10.png -/slides/anti_social_media/anti_social_media.011.png /images/2018/anti-social-media/slide11.png -/slides/anti_social_media/anti_social_media.012.png /images/2018/anti-social-media/slide12.png -/slides/anti_social_media/anti_social_media.013.png /images/2018/anti-social-media/slide13.png -/slides/anti_social_media/anti_social_media.014.png /images/2018/anti-social-media/slide14.png -/slides/anti_social_media/anti_social_media.015.png /images/2018/anti-social-media/slide15.png -/slides/anti_social_media/anti_social_media.016.png /images/2018/anti-social-media/slide16.png -/slides/anti_social_media/anti_social_media.017.png /images/2018/anti-social-media/slide17.png -/slides/anti_social_media/anti_social_media.018.png /images/2018/anti-social-media/slide18.png -/slides/anti_social_media/anti_social_media.019.png /images/2018/anti-social-media/slide19.png -/slides/anti_social_media/anti_social_media.020.png /images/2018/anti-social-media/slide20.png -/slides/anti_social_media/anti_social_media.021.png /images/2018/anti-social-media/slide21.png -/slides/anti_social_media/anti_social_media.022.png /images/2018/anti-social-media/slide22.png -/slides/anti_social_media/anti_social_media.023.png /images/2018/anti-social-media/slide23.png -/slides/anti_social_media/anti_social_media.025.png /images/2018/anti-social-media/slide25.png -/slides/anti_social_media/anti_social_media.026.png /images/2018/anti-social-media/slide26.png -/slides/anti_social_media/anti_social_media.027.png /images/2018/anti-social-media/slide27.png -/slides/anti_social_media/anti_social_media.028.png /images/2018/anti-social-media/slide28.png -/slides/anti_social_media/anti_social_media.031.png /images/2018/anti-social-media/slide31.png -/slides/anti_social_media/anti_social_media.032.png /images/2018/anti-social-media/slide32.png -/slides/anti_social_media/anti_social_media.033.png /images/2018/anti-social-media/slide33.png -/slides/anti_social_media/anti_social_media.034.png /images/2018/anti-social-media/slide34.png -/slides/anti_social_media/anti_social_media.036.png /images/2018/anti-social-media/slide36.png -/slides/anti_social_media/anti_social_media.037.png /images/2018/anti-social-media/slide37.png -/slides/anti_social_media/anti_social_media.038.png /images/2018/anti-social-media/slide38.png -/slides/anti_social_media/anti_social_media.039.png /images/2018/anti-social-media/slide39.png -/slides/anti_social_media/anti_social_media.042.png /images/2018/anti-social-media/slide42.png -/slides/anti_social_media/anti_social_media.043.png /images/2018/anti-social-media/slide43.png -/slides/anti_social_media/anti_social_media.044.png /images/2018/anti-social-media/slide44.png -/slides/anti_social_media/anti_social_media.045.png /images/2018/anti-social-media/slide45.png -/slides/anti_social_media/anti_social_media.046.png /images/2018/anti-social-media/slide46.png -/slides/anti_social_media/anti_social_media.047.png /images/2018/anti-social-media/slide47.png -/slides/anti_social_media/anti_social_media.048.png /images/2018/anti-social-media/slide48.png -/slides/anti_social_media/anti_social_media.049.png /images/2018/anti-social-media/slide49.png -/slides/anti_social_media/anti_social_media.051.png /images/2018/anti-social-media/slide51.png -/slides/anti_social_media/anti_social_media.052.png /images/2018/anti-social-media/slide52.png -/slides/anti_social_media/anti_social_media.054.png /images/2018/anti-social-media/slide54.png -/slides/anti_social_media/anti_social_media.055.png /images/2018/anti-social-media/slide55.png -/slides/anti_social_media/anti_social_media.056.png /images/2018/anti-social-media/slide56.png -/slides/anti_social_media/anti_social_media.057.png /images/2018/anti-social-media/slide57.png -/slides/anti_social_media/anti_social_media.059.png /images/2018/anti-social-media/slide59.png -/slides/anti_social_media/anti_social_media.061.png /images/2018/anti-social-media/slide61.png -/slides/anti_social_media/anti_social_media.062.png /images/2018/anti-social-media/slide62.png -/slides/anti_social_media/anti_social_media.063.png /images/2018/anti-social-media/slide63.png -/slides/anti_social_media/anti_social_media.064.png /images/2018/anti-social-media/slide64.png -/slides/anti_social_media/anti_social_media.065.png /images/2018/anti-social-media/slide65.png -/slides/anti_social_media/anti_social_media.066.png /images/2018/anti-social-media/slide66.png -/slides/anti_social_media/anti_social_media.067.png /images/2018/anti-social-media/slide67.png -/slides/anti_social_media/anti_social_media.068.png /images/2018/anti-social-media/slide68.png -/slides/anti_social_media/anti_social_media.070.png /images/2018/anti-social-media/slide70.png -/slides/anti_social_media/anti_social_media.072.png /images/2018/anti-social-media/slide72.png -/slides/anti_social_media/anti_social_media.073.png /images/2018/anti-social-media/slide73.png -/slides/anti_social_media/anti_social_media.075.png /images/2018/anti-social-media/slide75.png -/slides/anti_social_media/anti_social_media.076.png /images/2018/anti-social-media/slide76.png -/slides/anti_social_media/anti_social_media.079.png /images/2018/anti-social-media/slide79.png -/slides/anti_social_media/anti_social_media.080.png /images/2018/anti-social-media/slide80.png -/slides/anti_social_media/anti_social_media.081.png /images/2018/anti-social-media/slide81.png -/slides/anti_social_media/anti_social_media.082.png /images/2018/anti-social-media/slide82.png -/slides/anti_social_media/anti_social_media.084.png /images/2018/anti-social-media/slide84.png -/slides/anti_social_media/anti_social_media.085.png /images/2018/anti-social-media/slide85.png -/slides/anti_social_media/anti_social_media.086.png /images/2018/anti-social-media/slide86.png -/slides/anti_social_media/anti_social_media.087.png /images/2018/anti-social-media/slide87.png -/slides/anti_social_media/anti_social_media.088.png /images/2018/anti-social-media/slide88.png -/slides/anti_social_media/anti_social_media.089.png /images/2018/anti-social-media/slide89.png - -# === # - - - - - -# == Removing the month from post slugs at the start of 2023 == # -/2012/12/hypercritical/ /2012/hypercritical/ -/2013/01/zero/ /2013/zero/ -/2013/02/darwin/ /2013/darwin/ -/2013/03/candybar/ /2013/candybar/ -/2013/03/pinboard-backups/ /2013/pinboard-backups/ -/2013/03/timezone-bug/ /2013/timezone-bug/ -/2013/05/rss-podcasts-tumblr/ /til/2013/rss-podcasts-tumblr/ -/2013/08/google-maps/ /2013/google-maps/ -/2013/08/untagged-tumblr-posts/ /2013/untagged-tumblr-posts/ -/2013/11/textmate-quick-look/ /2013/textmate-quick-look/ -/2014/04/veil/ /2014/veil/ -/2014/05/part-ia-exams/ /2014/part-ia-exams/ -/2014/05/part-ii-advice/ /2014/part-ii-advice/ -/2014/06/instapaper-urls/ /2014/instapaper-urls/ -/2014/06/site-updates/ /2014/site-updates/ -/2014/06/skeletor/ /2014/skeletor/ -/2014/06/untagged-tumblr-posts-redux/ /2014/untagged-tumblr-posts-redux/ -/2014/07/latex-alpha/ /2014/latex-alpha/ -/2014/07/overcast/ /2014/overcast/ -/2014/08/alfred-screenshots/ /2014/alfred-screenshots/ -/2014/08/textexpander-amazon-affiliates/ /2014/textexpander-amazon-affiliates/ -/2014/08/untagged-tumblr-updates/ /2014/untagged-tumblr-updates/ -/2014/09/404-pages/ /2014/404-pages/ -/2014/09/new-standing-desk/ /2014/new-standing-desk/ -/2014/10/notes-on-tumblr/ /2014/notes-on-tumblr/ -/2014/10/virtualfish/ /2014/virtualfish/ -/2014/11/custom-css-resophnotes/ /2014/custom-css-resophnotes/ -/2014/11/ranged-strings/ /2014/ranged-strings/ -/2014/12/acronyms/ /2014/acronyms/ -/2014/12/golems/ /2014/golems/ -/2014/12/imessage-export/ /2014/imessage-export/ -/2014/12/kitchen-sink-security/ /2014/kitchen-sink-security/ -/2014/12/mathis-cars/ /2014/mathis-cars/ -/2014/12/pelican-linkposts/ /2014/pelican-linkposts/ -/2014/12/skeletors-all-the-way-down/ /2014/skeletors-all-the-way-down/ -/2014/12/war-on-christmas/ /410/ 410 -/2015/01/bbfc-podcast/ /410/ 410 -/2015/01/govuk/ /410/ 410 -/2015/01/kings-cross-problems/ /410/ 410 -/2015/02/1password/ /2015/1password/ -/2015/02/flask-notes/ /2015/flask-notes/ -/2015/02/lists/ /2015/lists/ -/2015/02/swift-let/ /2015/swift-let/ -/2015/03/exam-advice/ /2015/exam-advice/ -/2015/03/pygmentizr/ /2015/pygmentizr/ -/2015/03/python-firewall/ /2015/python-firewall/ -/2015/05/github-contributions/ /2015/github-contributions/ -/2015/05/nvalt-and-marked/ /2015/nvalt-and-marked/ -/2015/05/one-step-paste-simulator/ /2015/one-step-paste-simulator/ -/2015/05/raft-algorithm/ /2015/raft-algorithm/ -/2015/06/git-info-exclude/ /2015/git-info-exclude/ -/2015/06/persistent-ipython-notebooks/ /2015/persistent-ipython-notebooks/ -/2015/06/safer-file-copying/ /2015/safer-file-copying/ -/2015/07/exit-traps-in-bash/ /2015/exit-traps-in-bash/ -/2015/08/untagged-tumblr-v2/ /2015/untagged-tumblr-v2/ -/2015/09/effective-python/ /2015/effective-python/ -/2015/09/http2-by-stealth/ /2015/http2-by-stealth/ -/2015/09/spotlight-suggestions/ /2015/spotlight-suggestions/ -/2015/10/getting-started-testing/ /2015/getting-started-testing/ -/2015/11/backups-and-docker/ /2015/backups-and-docker/ -/2015/11/bbc-micro-bit/ /2015/bbc-micro-bit/ -/2015/11/beyond-pep8/ /2015/beyond-pep8/ -/2015/11/export-urls-from-safari-reading-list/ /2015/export-urls-from-safari-reading-list/ -/2015/11/operation-fortitude/ /2015/operation-fortitude/ -/2015/11/quick-shell-docker/ /2015/quick-shell-docker/ -/2015/12/pretty-print/ /2015/pretty-print/ -/2015/12/simpler-syndication/ /2015/simpler-syndication/ -/2016/01/fusion-drive/ /2016/fusion-drive/ -/2016/01/harry-potter-ipod/ /2016/harry-potter-ipod/ -/2016/01/skeletor-2015/ /2016/skeletor-2015/ -/2016/02/how-i-use-textexpander-to-curb-my-language/ /2016/how-i-use-textexpander-to-curb-my-language/ -/2016/02/saved-by-the-prompt/ /2016/saved-by-the-prompt/ -/2016/03/backup-paranoia/ /2016/backup-paranoia/ -/2016/03/exclusive-create-python/ /2016/exclusive-create-python/ -/2016/03/itunes-images-with-alfred/ /2016/itunes-images-with-alfred/ -/2016/04/hiding-the-youtube-search-bar/ /2016/hiding-the-youtube-search-bar/ -/2016/04/regexes-are-code/ /2016/regexes-are-code/ -/2016/05/finding-404s-in-apache-logs/ /2016/finding-404s-in-apache-logs/ -/2016/05/os-x-hates-textmate/ /2016/os-x-hates-textmate/ -/2016/05/python-smtplib-and-fastmail/ /2016/python-smtplib-and-fastmail/ -/2016/05/safely-deleting-a-file-called-rf/ /2016/safely-deleting-a-file-called-rf/ -/2016/06/hypothesis-intro/ /2016/hypothesis-intro/ -/2016/06/introduction-to-property-based-testing/ /2016/introduction-to-property-based-testing/ -/2016/06/reading-web-pages-on-my-kindle/ /2016/reading-web-pages-on-my-kindle/ -/2016/06/toothbrush-subscriptions/ /2016/toothbrush-subscriptions/ -/2016/07/chasing-redirects-and-url-shorteners/ /2016/chasing-redirects-and-url-shorteners/ -/2016/07/clearing-disk-space-on-os-x/ /2016/clearing-disk-space-on-os-x/ -/2016/08/clean-up-directories/ /2016/clean-up-directories/ -/2016/08/dealing-with-query-strings/ /2016/dealing-with-query-strings/ -/2016/08/is-a-url-from-tumblr/ /2016/is-a-url-from-tumblr/ -/2016/09/please-use-aspell/ /2016/please-use-aspell/ -/2016/09/pyconuk2016/ /2016/pyconuk2016/ -/2016/09/silence-is-golden/ /2016/silence-is-golden/ -/2016/09/speech-to-text/ /2016/speech-to-text/ -/2016/09/travelling-tech-bag/ /2016/travelling-tech-bag/ -/2016/10/a-shell-alias-for-tallying/ /2016/a-shell-alias-for-tallying/ -/2016/10/the-a-stands-for-asexual/ /2016/the-a-stands-for-asexual/ -/2016/10/tiling-the-plane-with-pillow/ /2016/tiling-the-plane-with-pillow/ -/2016/10/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ -/2016/10/wallpapers-with-pillow/ /2016/wallpapers-with-pillow/ -/2016/10/why-i-use-pytest/ /2016/why-i-use-pytest/ -/2016/10/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ -/2016/11/low-tech-productivity/ /2016/low-tech-productivity/ -/2016/11/you-should-use-keyring/ /2016/you-should-use-keyring/ -/2016/12/slack-history/ /2016/slack-history/ -/2016/12/strings-are-terrible/ /2016/strings-are-terrible/ -/2017/01/experiments-with-ao3-and-python/ /2017/experiments-with-ao3-and-python/ -/2017/01/scrape-logged-in-ao3/ /2017/scrape-logged-in-ao3/ -/2017/02/backup-your-goodreads/ /2017/backup-your-goodreads/ -/2017/02/backup-your-instapaper/ /2017/backup-your-instapaper/ -/2017/03/extensions-in-python-markdown/ /2017/extensions-in-python-markdown/ -/2017/03/qcon2017/ /2017/qcon2017/ -/2017/04/lessons-from-alterconf/ /2017/lessons-from-alterconf/ -/2017/06/crossness-pumping-station/ /2017/crossness-pumping-station/ -/2017/07/backing-up-pinboard-archives/ /2017/backing-up-pinboard-archives/ -/2017/07/listing-s3-keys/ /2017/listing-s3-keys/ -/2017/07/soundcloud-backups/ /2017/soundcloud-backups/ -/2017/09/lazy-reading-in-python/ /2017/lazy-reading-in-python/ -/2017/09/ode-to-docopt/ /2017/ode-to-docopt/ -/2017/09/useful-git-commands/ /2017/useful-git-commands/ -/2017/10/control-centre/ /2017/control-centre/ -/2017/10/latex-underlines/ /2017/latex-underlines/ -/2017/10/lightning-talks/ /2017/lightning-talks/ -/2017/10/overengineering/ /2017/overengineering/ -/2017/10/pip-tools/ /2017/pip-tools/ -/2017/10/pronunciation-peeves/ /2017/pronunciation-peeves/ -/2017/10/pyconuk-2017-resources/ /2017/pyconuk-2017-resources/ -/2017/10/requests-hooks/ /2017/requests-hooks/ -/2017/10/tweets-in-keynote/ /2017/tweets-in-keynote/ -/2017/11/a-plumbers-guide-to-git/ /2017/a-plumbers-guide-to-git/ -/2017/11/asking-about-gender/ /2017/asking-about-gender/ -/2017/11/dont-tap-the-mic/ /2017/dont-tap-the-mic/ -/2017/11/fetching-cloudwatch-logs/ /2017/fetching-cloudwatch-logs/ -/2017/11/five-years-of-witch/ /2017/five-years-of-witch/ -/2017/11/pruning-git-branches/ /2017/pruning-git-branches/ -/2017/11/pyconuk-2017-privilege-inclusion/ /2017/pyconuk-2017-privilege-inclusion/ -/2017/12/armed-police/ /2017/armed-police/ -/2017/12/building-your-repo/ /2017/building-your-repo/ -/2018/01/downloading-sqs-queues/ /2018/downloading-sqs-queues/ -/2018/01/ips-for-documentation/ /2018/ips-for-documentation/ -/2018/01/listing-s3-keys-redux/ /2018/listing-s3-keys-redux/ -/2018/02/working-from-home/ /2018/working-from-home/ -/2018/03/a-plumbers-guide-to-git/ /2018/a-plumbers-guide-to-git/ -/2018/03/continuous-releases/ /2018/continuous-releases/ -/2018/03/overnight-bag/ /2018/overnight-bag/ -/2018/04/24-hours/ /2018/24-hours/ -/2018/04/anti-social-media/ /2018/anti-social-media/ -/2018/04/s3-shortcuts/ /2018/s3-shortcuts/ -/2018/05/ascii-bar-charts/ /2018/ascii-bar-charts/ -/2018/05/beware-logged-errors/ /2018/beware-logged-errors/ -/2018/05/google-duplex/ /2018/google-duplex/ -/2018/07/icloud-calendars/ /2018/icloud-calendars/ -/2018/07/imac-accessory/ /2018/imac-accessory/ -/2018/07/leaking-my-ssh-keys/ /2018/leaking-my-ssh-keys/ -/2018/07/travel-instructions/ /2018/travel-instructions/ -/2018/08/do-not-distract-while-driving/ /2018/do-not-distract-while-driving/ -/2018/08/finding-slow-builds-in-travis/ /2018/finding-slow-builds-in-travis/ -/2018/08/inclusive-conferences/ /2018/inclusive-conferences/ -/2018/08/maps-for-pyconuk/ /2018/maps-for-pyconuk/ -/2018/08/no-more-tumblr-redirects/ /2018/no-more-tumblr-redirects/ -/2018/08/parallel-scan-scanamo/ /2018/parallel-scan-scanamo/ -/2018/08/selective-sudo-on-travis/ /2018/selective-sudo-on-travis/ -/2018/09/assume-worst-intent/ /2018/assume-worst-intent/ -/2018/09/content-warnings/ /2018/content-warnings/ -/2018/09/error-logging-in-lambdas/ /2018/error-logging-in-lambdas/ -/2018/09/lessons-in-signage/ /2018/lessons-in-signage/ -/2018/09/pyconuk-2018/ /2018/pyconuk-2018/ -/2018/09/suspicious-minds/ /2018/suspicious-minds/ -/2018/10/finatra-404/ /2018/finatra-404/ -/2018/10/gender-recognition-act/ /2018/gender-recognition-act/ -/2018/10/horstmann-electric-7/ /til/2018/horstmann-electric-7/ -/2018/10/unscrupulous-time-travel/ /2018/unscrupulous-time-travel/ -/2018/11/aberdulais-waterfall/ /2018/aberdulais-waterfall/ -/2018/11/book-recommendations/ /2018/book-recommendations/ -/2018/11/finding-unsubscribed-sns-topics/ /2018/finding-unsubscribed-sns-topics/ -/2018/12/backing-up-tumblr/ /2018/backing-up-tumblr/ -/2018/12/getting-credentials-for-an-assumed-iam-role/ /2018/getting-credentials-for-an-assumed-iam-role/ -/2018/12/iterating-in-fixed-size-chunks/ /2018/iterating-in-fixed-size-chunks/ -/2018/12/reading-a-utf8-encoded-csv/ /2018/reading-a-utf8-encoded-csv/ -/2019/01/debugging-a-stuck-terraform-plan/ /2019/debugging-a-stuck-terraform-plan/ -/2019/01/monki-gras-the-curb-cut-effect/ /2019/monki-gras-the-curb-cut-effect/ -/2019/01/notes-from-you-got-this/ /2019/notes-from-you-got-this/ -/2019/01/oyster-tracking/ /2019/oyster-tracking/ -/2019/01/paul-rothe-and-sons/ /2019/paul-rothe-and-sons/ -/2019/01/sunlight/ /2019/sunlight/ -/2019/02/checking-jekyll-sites-with-htmlproofer/ /2019/checking-jekyll-sites-with-htmlproofer/ -/2019/02/inclusive-events-redux/ /2019/inclusive-events-redux/ -/2019/02/working-with-large-s3-objects/ /2019/working-with-large-s3-objects/ -/2019/03/atomic-cross-filesystem-moves-in-python/ /2019/atomic-cross-filesystem-moves-in-python/ -/2019/03/creating-a-github-action-to-auto-merge-pull-requests/ /2019/creating-a-github-action-to-auto-merge-pull-requests/ -/2019/03/finding-the-latest-screenshot-in-macos-mojave/ /2019/finding-the-latest-screenshot-in-macos-mojave/ -/2019/03/forth-bridge/ /2019/forth-bridge/ -/2019/04/getting-a-transcript-of-a-talk-from-youtube/ /2019/getting-a-transcript-of-a-talk-from-youtube/ -/2019/04/how-i-back-up-my-computer/ /2019/how-i-back-up-my-computer/ -/2019/04/reversing-a-tco-url-to-the-original-tweet/ /2019/reversing-a-tco-url-to-the-original-tweet/ -/2019/04/some-tips-for-conferences/ /2019/some-tips-for-conferences/ -/2019/05/creating-a-locking-service-in-a-scala-type-class/ /2019/creating-a-locking-service-in-a-scala-type-class/ -/2019/05/falsehoods-programmers-believe-about-unix-time/ /2019/falsehoods-programmers-believe-about-unix-time/ -/2019/05/finding-unused-variables-in-a-terraform-module/ /2019/finding-unused-variables-in-a-terraform-module/ -/2019/05/first-thoughts-on-trans-inclusion/ /2019/first-thoughts-on-trans-inclusion/ -/2019/06/a-jekyll-filter-for-obfuscating-email-addresses/ /2019/a-jekyll-filter-for-obfuscating-email-addresses/ -/2019/06/acorn-on-the-command-line/ /2019/acorn-on-the-command-line/ -/2019/06/cycle-tracking-isn-t-just-for-women/ /2019/cycle-tracking-isn-t-just-for-women/ -/2019/06/getting-cover-images-from-mobi-ebooks/ /2019/getting-cover-images-from-mobi-ebooks/ -/2019/06/reading-a-chinese-dictionary/ /2019/reading-a-chinese-dictionary/ -/2019/06/regenerating/ /2019/regenerating/ -/2019/07/creating-preview-thumbnails-of-pdf-documents/ /2019/creating-preview-thumbnails-of-pdf-documents/ -/2019/07/finding-divisors-with-python/ /2019/finding-divisors-with-python/ -/2019/07/listing-s3-keys/ /2019/listing-s3-keys/ -/2019/07/section-28/ /2019/section-28/ -/2019/07/ten-braille-facts/ /2019/ten-braille-facts/ -/2019/08/finding-tint-colours-with-k-means/ /2019/finding-tint-colours-with-k-means/ -/2019/08/removing-the-drm-from-my-kindle-books/ /410/ 410 -/2019/09/github-code-search-with-de-duplication/ /2019/github-code-search-with-de-duplication/ -/2019/09/streaming-large-s3-objects/ /2019/streaming-large-s3-objects/ -/2019/09/triangular-coordinates-in-svg/ /2019/triangular-coordinates-in-svg/ -/2019/09/unpacking-compressed-archives-in-scala/ /2019/unpacking-compressed-archives-in-scala/ -/2019/10/adventures-with-concurrent-futures/ /2019/adventures-with-concurrent-futures/ -/2019/10/digital-preservation-at-wellcome-collection/ /2019/digital-preservation-at-wellcome-collection/ -/2019/10/religious-holidays/ /2019/religious-holidays/ -/2019/10/rough-edges-of-filecmp/ /2019/rough-edges-of-filecmp/ -/2019/10/sans-io-programming/ /2019/sans-io-programming/ -/2019/11/aws-costs-graph/ /2019/aws-costs-graph/ -/2019/11/fixing-terraform-module-sources/ /2019/fixing-terraform-module-sources/ -/2019/11/my-scanning-setup/ /2019/my-scanning-setup/ -/2019/11/saving-a-copy-of-a-tweet-by-typing-twurl/ /2019/saving-a-copy-of-a-tweet-by-typing-twurl/ -/2019/12/november-scripts/ /2019/november-scripts/ -/2019/12/spreadsheet-functions/ /2019/spreadsheet-functions/ -/2019/12/yaml-impossible/ /2019/yaml-impossible/ -/2020/01/deletion-canary/ /2020/deletion-canary/ -/2020/01/excluding-lots-of-folders-in-backblaze/ /2020/excluding-lots-of-folders-in-backblaze/ -/2020/01/finding-the-bottlenecks-in-an-ecs-cluster/ /2020/finding-the-bottlenecks-in-an-ecs-cluster/ -/2020/01/pride-valknuts/ /2020/pride-valknuts/ -/2020/01/tex-dockerfile/ /2020/tex-dockerfile/ -/2020/01/uk-stations-map/ /2020/uk-stations-map/ -/2020/02/a-remote-controlled-oven-is-a-safety-nightmare/ /2020/a-remote-controlled-oven-is-a-safety-nightmare/ -/2020/02/adjusting-the-dominant-colour-of-an-image/ /2020/adjusting-the-dominant-colour-of-an-image/ -/2020/02/archival-storage-service/ /2020/archival-storage-service/ -/2020/02/versioning-a-bagit-bag/ /2020/versioning-a-bagit-bag/ -/2020/03/adding-non-breaking-spaces-with-jekyll/ /2020/adding-non-breaking-spaces-with-jekyll/ -/2020/03/finding-the-size-of-your-s3-buckets/ /2020/finding-the-size-of-your-s3-buckets/ -/2020/03/inclusion-cant-be-an-afterthought/ /2020/inclusion-cant-be-an-afterthought/ -/2020/03/rich-enough-to-make-bad-choices/ /2020/rich-enough-to-make-bad-choices/ -/2020/03/sick-leave/ /2020/sick-leave/ -/2020/03/stripey-flag-wallpapers/ /2020/stripey-flag-wallpapers/ -/2020/04/adventures-in-embodiment/ /2020/adventures-in-embodiment/ -/2020/04/comparing-json-in-scala/ /2020/comparing-json-in-scala/ -/2020/04/complex-failures/ /2020/complex-failures/ -/2020/04/downloading-files-with-python/ /2020/downloading-files-with-python/ -/2020/04/exploring-an-unknown-sql-server/ /2020/exploring-an-unknown-sql-server/ -/2020/04/getting-word-count-stats-for-my-blog/ /2020/getting-word-count-stats-for-my-blog/ -/2020/04/good-information-is-a-privilege/ /2020/good-information-is-a-privilege/ -/2020/04/storing-language-vocabulary-as-a-graph/ /2020/storing-language-vocabulary-as-a-graph/ -/2020/04/survivors-guilt/ /2020/survivors-guilt/ -/2020/04/thinking-about-gender/ /2020/thinking-about-gender/ -/2020/04/using-dynamodb-as-a-calculator/ /2020/using-dynamodb-as-a-calculator/ -/2020/05/downloading-the-ao3-fics-that-i-ve-saved-in-pinboard/ /2020/downloading-the-ao3-fics-that-i-ve-saved-in-pinboard/ -/2020/05/getting-every-item-from-a-dynamodb-table-with-python/ /2020/getting-every-item-from-a-dynamodb-table-with-python/ -/2020/05/give-your-audience-time-to-react/ /2020/give-your-audience-time-to-react/ -/2020/05/how-long-is-my-data/ /2020/how-long-is-my-data/ -/2020/05/human-friendly-dates-in-javascript/ /2020/human-friendly-dates-in-javascript/ -/2020/05/illustrating-lifecycle-transitions-in-amazon-s3/ /2020/illustrating-lifecycle-transitions-in-amazon-s3/ -/2020/05/letter-to-my-mp-about-liz-truss-and-medical-treatment-for-trans-youth/ /2020/letter-to-my-mp-about-liz-truss-and-medical-treatment-for-trans-youth/ -/2020/05/make-it-safe-to-admit-mistakes/ /2020/make-it-safe-to-admit-mistakes/ -/2020/05/moving-messages-between-sqs-queues/ /2020/moving-messages-between-sqs-queues/ -/2020/05/reflecting-on-bad-life-choices/ /2020/reflecting-on-bad-life-choices/ -/2020/05/sachsenhausen/ /2020/sachsenhausen/ -/2020/05/social-media-as-a-growth-culture-for-opinions/ /2020/social-media-as-a-growth-culture-for-opinions/ -/2020/05/the-friends-i-lost-along-the-way/ /2020/the-friends-i-lost-along-the-way/ -/2020/06/always-read-your-contract/ /2020/always-read-your-contract/ -/2020/06/archive-monocultures-considered-harmful/ /2020/archive-monocultures-considered-harmful/ -/2020/06/fat-shaming-in-the-good-place/ /2020/fat-shaming-in-the-good-place/ -/2020/06/finding-the-months-between-two-dates-in-python/ /2020/finding-the-months-between-two-dates-in-python/ -/2020/06/large-things-living-in-cold-places/ /2020/large-things-living-in-cold-places/ -/2020/06/using-applescript-to-open-a-url-in-private-browsing-in-safari/ /2020/using-applescript-to-open-a-url-in-private-browsing-in-safari/ -/2020/07/changing-the-accent-colour-of-icns-icons/ /2020/changing-the-accent-colour-of-icns-icons/ -/2020/07/getting-a-markdown-link-to-a-window-in-safari/ /2020/getting-a-markdown-link-to-a-window-in-safari/ -/2020/07/how-to-do-parallel-downloads-with-youtube-dl/ /2020/how-to-do-parallel-downloads-with-youtube-dl/ -/2020/07/running-concurrent-try-functions-in-scala/ /2020/running-concurrent-try-functions-in-scala/ -/2020/07/what-does-d-match-in-a-regex/ /2020/what-does-d-match-in-a-regex/ -/2020/07/why-do-programming-languages-have-a-main-function/ /2020/why-do-programming-languages-have-a-main-function/ -/2020/08/s3-keys-are-not-file-paths/ /2020/s3-keys-are-not-file-paths/ -/2020/08/s3-prefixes-are-not-directories/ /2020/s3-prefixes-are-not-directories/ -/2020/08/using-fuzzy-string-matching-to-find-duplicate-tags/ /2020/using-fuzzy-string-matching-to-find-duplicate-tags/ -/2020/09/two-python-functions-for-getting-cloudtrail-events/ /2020/two-python-functions-for-getting-cloudtrail-events/ -/2020/09/using-qlmanage-to-create-thumbnails-on-macos/ /2020/using-qlmanage-to-create-thumbnails-on-macos/ -/2020/10/a-new-readme-for-docstore/ /2020/a-new-readme-for-docstore/ -/2020/10/a-sprinkling-of-azure/ /2020/a-sprinkling-of-azure/ -/2020/10/how-do-i-use-my-iphone-cameras/ /2020/how-do-i-use-my-iphone-cameras/ -/2020/10/the-importance-of-good-error-messages/ /2020/the-importance-of-good-error-messages/ -/2020/10/til-using-git-check-ignore-to-debug-your-gitignore/ /til/2020/using-git-check-ignore-to-debug-your-gitignore/ -/2020/11/a-python-function-to-ignore-a-path-with-git-info-exclude/ /2020/a-python-function-to-ignore-a-path-with-git-info-exclude/ -/2020/11/copying-images-from-docker-hub-to-amazon-ecr/ /2020/copying-images-from-docker-hub-to-amazon-ecr/ -/2020/11/how-i-read-non-fiction-books/ /2020/how-i-read-non-fiction-books/ -/2020/11/maths-is-about-facing-ambiguity-not-avoiding-it/ /2020/maths-is-about-facing-ambiguity-not-avoiding-it/ -/2020/11/non-technical-users/ /2020/non-technical-users/ -/2020/11/remembering-if-a-details-element-was-opened/ /2020/remembering-if-a-details-element-was-opened/ -/2020/12/creating-short-lived-temporary-roles-for-experimenting-with-aws-iam-policy-documents/ /2020/creating-short-lived-temporary-roles-for-experimenting-with-aws-iam-policy-documents/ -/2021/01/kempisbot/ /2021/kempisbot/ -/2021/01/what-year-is-it/ /2021/what-year-is-it/ -/2021/02/digital-verification/ /2021/digital-verification/ -/2021/02/screaming-in-the-cloud/ /2021/screaming-in-the-cloud/ -/2021/03/an-applescript-to-toggle-voice-control/ /2021/an-applescript-to-toggle-voice-control/ -/2021/03/inner-outer-strokes-svg/ /2021/inner-outer-strokes-svg/ -/2021/03/rainbow-hearts/ /2021/rainbow-hearts/ -/2021/04/coloured-squares/ /2021/coloured-squares/ -/2021/04/detect-private-browsing/ /2021/detect-private-browsing/ -/2021/04/s3-progress-bars/ /2021/s3-progress-bars/ -/2021/04/secure-input/ /2021/secure-input/ -/2021/04/unified-search/ /2021/unified-search/ -/2021/06/s3-deprecates-bittorrent/ /2021/s3-deprecates-bittorrent/ -/2021/06/visualising-journal/ /2021/visualising-journal/ -/2021/07/a-wise-choice-of-test-strings/ /2021/a-wise-choice-of-test-strings/ -/2021/07/listing-deleted-secrets/ /2021/listing-deleted-secrets/ -/2021/07/useful-github-searches/ /2021/useful-github-searches/ -/2021/08/finding-misconfigured-or-dangling-cloudwatch-alarms/ /2021/finding-misconfigured-or-dangling-cloudwatch-alarms/ -/2021/08/ignore-lots-of-folders-in-spotlight/ /2021/ignore-lots-of-folders-in-spotlight/ -/2021/08/markdown-image-syntax/ /2021/markdown-image-syntax/ -/2021/09/cloud-costs-report/ /2021/cloud-costs-report/ -/2021/09/debugging-eventbridge-cron/ /2021/debugging-eventbridge-cron/ -/2021/09/editing-toolbar/ /2021/editing-toolbar/ -/2021/09/non-commuting-strings/ /2021/non-commuting-strings/ -/2021/09/perfect-planks/ /2021/perfect-planks/ -/2021/09/safari-15-favicons/ /2021/safari-15-favicons/ -/2021/09/septembrse/ /2021/septembrse/ -/2021/10/console-copying/ /2021/console-copying/ -/2021/10/how-do-you-work-with-non-engineers/ /2021/how-do-you-work-with-non-engineers/ -/2021/10/oboe-of-optozorax/ /2021/oboe-of-optozorax/ -/2021/10/original-photos-filename/ /2021/original-photos-filename/ -/2021/10/readmes-for-open-science/ /2021/readmes-for-open-science/ -/2021/10/redacting-pdfs/ /2021/redacting-pdfs/ -/2021/11/dominant-colours/ /2021/dominant-colours/ -/2021/12/2021-in-reading/ /2021/2021-in-reading/ -/2021/12/os-sep/ /2021/os-sep/ -/2021/12/rust-errors/ /2021/rust-errors/ -/2021/12/slashes/ /2021/slashes/ -/2022/01/animated-artichokes/ /2022/animated-artichokes/ -/2022/01/rusty-shelves/ /2022/rusty-shelves/ -/2022/02/no-cause-for-alarm/ /2022/no-cause-for-alarm/ -/2022/02/route53/ /2022/route53/ -/2022/02/safari-tabs/ /2022/safari-tabs/ -/2022/02/two-twitter-cards/ /2022/two-twitter-cards/ -/2022/04/checking-with-curl/ /2022/checking-with-curl/ -/2022/04/lorenz-wheels/ /2022/lorenz-wheels/ -/2022/04/supposedly-simple-image-layout/ /2022/supposedly-simple-image-layout/ -/2022/05/carbon-monoxide/ /2022/carbon-monoxide/ -/2022/05/dominant-web-colours/ /2022/dominant-web-colours/ -/2022/05/rust-on-glitch/ /2022/rust-on-glitch/ -/2022/06/alfred-to-github/ /2022/alfred-to-github/ -/2022/06/forgotten-secrets/ /2022/forgotten-secrets/ -/2022/06/imaginary-numbers/ /2022/imaginary-numbers/ -/2022/06/new-archive/ /2022/new-archive/ -/2022/07/martian-plaque/ /2022/martian-plaque/ -/2022/07/saturn-v/ /2022/saturn-v/ -/2022/07/screenshots/ /2022/screenshots/ -/2022/08/blink-diffs/ /2022/blink-diffs/ -/2022/08/buildkite-deployments/ /2022/buildkite-deployments/ -/2022/08/circle-party/ /2022/circle-party/ -/2022/08/egyptian-mixtape/ /2022/egyptian-mixtape/ -/2022/08/no-cute/ /2022/no-cute/ -/2022/08/screenshots-go-gangbusters/ /2022/screenshots-go-gangbusters/ -/2022/08/spaaaaaace/ /2022/spaaaaaace/ -/2022/08/strict-jinja/ /2022/strict-jinja/ -/2022/09/graph-generative-art/ /2022/graph-generative-art/ -/2022/09/maths-cross-stitch/ /2022/maths-cross-stitch/ -/2022/09/moomin-mathematics/ /2022/moomin-mathematics/ -/2022/09/nextjs-props/ /2022/nextjs-props/ -/2022/09/rust-1-64/ /2022/rust-1-64/ -/2022/10/accessibility-fixes/ /2022/accessibility-fixes/ -/2022/10/circle-experiments/ /2022/circle-experiments/ -/2022/10/iterative-project-management/ /2022/iterative-project-management/ -/2022/10/library-lookup/ /2022/library-lookup/ -/2022/10/snapped-elastic/ /2022/snapped-elastic/ -/2022/11/bure-valley/ /2022/bure-valley/ -/2022/11/changing-the-macos-accent-colour/ /2022/changing-the-macos-accent-colour/ -/2022/11/koa-logger-redactions/ /2022/koa-logger-redactions/ -/2022/11/obsidian-plugin/ /2022/obsidian-plugin/ -/2022/11/tin-anniversary/ /2022/tin-anniversary/ -/2022/11/tweet-alt-text/ /2022/tweet-alt-text/ -/2022/12/2022-in-reading/ /2022/2022-in-reading/ -/2022/12/cursor-confirmed/ /2022/cursor-confirmed/ -/2022/12/live-text-script/ /2022/live-text-script/ -/2022/12/marquee-rocket/ /2022/marquee-rocket/ -/2022/12/print-sbt/ /2022/print-sbt/ -/2022/12/prismic-validation/ /2022/prismic-validation/ - -# === # - -# == And the old per-month archives == # - -/2012/12/ /articles/ -/2013/01/ /articles/ -/2013/02/ /articles/ -/2013/03/ /articles/ -/2013/05/ /articles/ -/2013/08/ /articles/ -/2013/11/ /articles/ -/2014/04/ /articles/ -/2014/05/ /articles/ -/2014/06/ /articles/ -/2014/07/ /articles/ -/2014/08/ /articles/ -/2014/09/ /articles/ -/2014/10/ /articles/ -/2014/11/ /articles/ -/2014/12/ /articles/ -/2015/01/ /articles/ -/2015/02/ /articles/ -/2015/03/ /articles/ -/2015/05/ /articles/ -/2015/06/ /articles/ -/2015/07/ /articles/ -/2015/08/ /articles/ -/2015/09/ /articles/ -/2015/10/ /articles/ -/2015/11/ /articles/ -/2015/12/ /articles/ -/2016/01/ /articles/ -/2016/02/ /articles/ -/2016/03/ /articles/ -/2016/04/ /articles/ -/2016/05/ /articles/ -/2016/06/ /articles/ -/2016/07/ /articles/ -/2016/08/ /articles/ -/2016/09/ /articles/ -/2016/10/ /articles/ -/2016/11/ /articles/ -/2016/12/ /articles/ -/2017/01/ /articles/ -/2017/02/ /articles/ -/2017/03/ /articles/ -/2017/04/ /articles/ -/2017/06/ /articles/ -/2017/07/ /articles/ -/2017/09/ /articles/ -/2017/10/ /articles/ -/2017/11/ /articles/ -/2017/12/ /articles/ -/2018/01/ /articles/ -/2018/02/ /articles/ -/2018/03/ /articles/ -/2018/04/ /articles/ -/2018/05/ /articles/ -/2018/07/ /articles/ -/2018/08/ /articles/ -/2018/09/ /articles/ -/2018/10/ /articles/ -/2018/11/ /articles/ -/2018/12/ /articles/ -/2019/01/ /articles/ -/2019/02/ /articles/ -/2019/03/ /articles/ -/2019/04/ /articles/ -/2019/05/ /articles/ -/2019/06/ /articles/ -/2019/07/ /articles/ -/2019/08/ /articles/ -/2019/09/ /articles/ -/2019/10/ /articles/ -/2019/11/ /articles/ -/2019/12/ /articles/ -/2020/01/ /articles/ -/2020/02/ /articles/ -/2020/03/ /articles/ -/2020/04/ /articles/ -/2020/05/ /articles/ -/2020/06/ /articles/ -/2020/07/ /articles/ -/2020/08/ /articles/ -/2020/09/ /articles/ -/2020/10/ /articles/ -/2020/11/ /articles/ -/2020/12/ /articles/ -/2021/01/ /articles/ -/2021/02/ /articles/ -/2021/03/ /articles/ -/2021/04/ /articles/ -/2021/06/ /articles/ -/2021/07/ /articles/ -/2021/08/ /articles/ -/2021/09/ /articles/ -/2021/10/ /articles/ -/2021/11/ /articles/ -/2021/12/ /articles/ -/2022/01/ /articles/ -/2022/02/ /articles/ -/2022/04/ /articles/ -/2022/05/ /articles/ -/2022/06/ /articles/ -/2022/07/ /articles/ -/2022/08/ /articles/ -/2022/09/ /articles/ -/2022/10/ /articles/ -/2022/11/ /articles/ -/2022/12/ /articles/ - -# === # - - - - -# == Redirects for spam == # -# -# Netlify Analytics highlights the most frequent "Not Found" resources, -# but it's populated by people attempting to hack/spam my site. -# -# This serves them a 400 Bad Request page, and should get them out of this list. - -/.env /400/ 400 -/xmlrpc.php /400/ 400 -/shop/wp-* /400/ 400 -/test/wp-* /400/ 400 -/wp-* /400/ 400 -/blog/wp-* /400/ 400 -/cms/wp-* /400/ 400 -/site/wp-* /400/ 400 -/wordpress/wp-* /400/ 400 -/wp/wp-* /400/ 400 -/wp2/wp-* /400/ 400 -/index.php* /400/ 400 -/zb_system/* /400/ 400 - -# === # - - - - - -# == Ensure that dated posts are "hackable" == # -# -# i.e. that you can delete bits off the end of the URL and still get -# somewhere useful. - -/projects/ /410/ 410 - -/til/2024/ /til/ -/til/2023/ /til/ -/til/2022/ /til/ -/til/2021/ /til/ -/til/2020/ /til/ -/til/2019/ /til/ -/til/2018/ /til/ -/til/2016/ /til/ -/til/2013/ /til/ - -/2024/ /articles/ -/2023/ /articles/ -/2022/ /articles/ -/2021/ /articles/ -/2020/ /articles/ -/2019/ /articles/ -/2018/ /articles/ -/2017/ /articles/ -/2016/ /articles/ -/2015/ /articles/ -/2014/ /articles/ -/2013/ /articles/ -/2012/ /articles/ - -# === # - - - - -# == Ensure that non-primary domains redirect to the main domain == # - -https://alexwlchan.com/login/* https://alexwlchan.net/:splat 200 -https://alexwlchan.co.uk/login/* https://alexwlchan.net/:splat 200 -https://www.alexwlchan.net/login/* https://alexwlchan.net/:splat 200 - -# === # - - - - - -# == Misc redirects == # - -/files/2024/looped_squares.html https://looped-squares.glitch.me -/files/2024/looped_squares/ https://looped-squares.glitch.me -/files/2024/looped_squares https://looped-squares.glitch.me - -# === # - - - - -# == Redirects for reshuffling in April 2024 == # -# -# In April 2024 I moved some of my old posts into the TIL section, so -# their URLs changed. Make sure they get redirected! -# -# I also removed some old posts which I didn't think were worth keeping -# around, and want them to get a 410 Gone. - -/2013/rss-podcasts-tumblr/ /til/2013/rss-podcasts-tumblr/ - -/2015/bbfc-podcast/ /410/ 410 -/2015/govuk/ /410/ 410 -/2015/kings-cross-problems/ /410/ 410 -/2014/war-on-christmas/ /410/ 410 - -/exams/ /2015/technical-exams/ - -/2023/mechanize-ssl/ /til/2023/mechanize-ssl/ -/2020/til-using-git-check-ignore-to-debug-your-gitignore/ /til/2020/using-git-check-ignore-to-debug-your-gitignore/ -/2018/horstmann-electric-7/ /til/2018/horstmann-electric-7/ -/2016/worcester-28cdi-clock/ /til/2016/worcester-28cdi-clock/ -/2016/tricity-sie454-clock/ /til/2016/tricity-sie454-clock/ - -# === # - - - - -# == Redirects for reshuffling in October 2024 == # -# -# In October 2024 I rejigged stuff as part of redoing the tag pages, including -# changing some tag names. They aren't super commonly uses, but I did find -# a few broken links in Google to fix. - -/tags/amazon-s3/ /tags/aws/amazon-s3/ - -# === # - - - - - -# This uses Netlify's custom 404 error page handling -# See https://docs.netlify.com/routing/redirects/redirect-options/#custom-404-page-handling -/* /404/ 404 diff --git a/tests/test_errors.rb b/tests/test_errors.rb index 9d84e37ce..8c44016f3 100644 --- a/tests/test_errors.rb +++ b/tests/test_errors.rb @@ -34,6 +34,6 @@ def test_wp_login_is_400 resp = get_url('https://alexwlchan.net/wp-login.php') assert_equal resp.code, '400' - assert_equal resp.body.strip, '

400 Bad Request

' + assert_equal resp.body.strip, '400 Bad Request' end end From e67cf5838e2b70232aadef49e595ccfe93459210 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 20:50:14 +0000 Subject: [PATCH 11/20] Now run Caddy with my primary domain --- Caddyfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Caddyfile b/Caddyfile index 0f6440f77..c37eb6a50 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,4 +1,4 @@ -www.alexwlchan.net { +alexwlchan.net { import caddy/redirects.Caddyfile import caddy/gone.Caddyfile @@ -111,7 +111,7 @@ www.alexwlchan.net { # This redirects my other domain names to my primary domain name, # to avoid diluting traffic between them. -alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk { +alexwlchan.com, www.alexwlchan.com, alexwlchan.co.uk, www.alexwlchan.co.uk, www.alexwlchan.net { redir https://alexwlchan.net{uri} permanent log { From 03c80903737d90be7eac6badd944f65033347338 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:04:12 +0000 Subject: [PATCH 12/20] Fix a couple of failing tests --- tests/test_alternate_domains.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_alternate_domains.rb b/tests/test_alternate_domains.rb index a4f41b4a6..0eb832dab 100644 --- a/tests/test_alternate_domains.rb +++ b/tests/test_alternate_domains.rb @@ -15,7 +15,7 @@ class TestAlternateDomains < Test::Unit::TestCase def test_http_redirects_to_https resp = get_url('http://alexwlchan.net') - assert_equal resp.code, '301' + assert_equal resp.code, '308' assert_equal resp['location'], 'https://alexwlchan.net/' end @@ -24,7 +24,7 @@ def test_http_redirects_to_https def test_http_redirects_to_https_with_path resp = get_url('http://alexwlchan.net/contact/') - assert_equal resp.code, '301' + assert_equal resp.code, '308' assert_equal resp['location'], 'https://alexwlchan.net/contact/' end From 294e8088b56ae6a773280ffbab478cc5fa21e49f Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:12:52 +0000 Subject: [PATCH 13/20] Remove a link to the no-longer extant /400/ page --- src/_posts/2023/2023-03-05-filtering-netlify-analytics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_posts/2023/2023-03-05-filtering-netlify-analytics.md b/src/_posts/2023/2023-03-05-filtering-netlify-analytics.md index 277b9756d..d4788f813 100644 --- a/src/_posts/2023/2023-03-05-filtering-netlify-analytics.md +++ b/src/_posts/2023/2023-03-05-filtering-netlify-analytics.md @@ -34,18 +34,18 @@ But a lot of the hits are from malicious bots, crawling the web for vulnerable P Since I don't run PHP, I'm not worried about them breaking anything; these errors are just noise. To filter these requests out of the table, I've created a bunch of redirects [in my `_redirects` file][redirects]. -I'm matching on a handful of these patterns using wildcards, and sending them all to [/400/](/400/) with an HTTP 400 status code: +I'm matching on a handful of these patterns using wildcards, and sending them all to an HTTP 400 page with an HTTP 400 status code: ``` /wp-* /400/ 400 - + /blog/wp-* /400/ 400 /cms/wp-* /400/ 400 /site/wp-* /400/ 400 /wordpress/wp-* /400/ 400 /wp/wp-* /400/ 400 /wp2/wp-* /400/ 400 - + /index.php* /400/ 400 ``` From eba6b70a6b072ea4c293ba544e7f84d9e45e5a5a Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:29:14 +0000 Subject: [PATCH 14/20] Update the linter to work with the new redirects file --- scripts/linter.rb | 10 +++++----- scripts/linting/caddy_redirects.rb | 14 ++++++++++++++ .../linting/check_all_urls_are_hackable.rb | 4 ++-- scripts/linting/netlify_redirects.rb | 19 ------------------- 4 files changed, 21 insertions(+), 26 deletions(-) create mode 100644 scripts/linting/caddy_redirects.rb delete mode 100644 scripts/linting/netlify_redirects.rb diff --git a/scripts/linter.rb b/scripts/linter.rb index 4eda09609..240e59940 100644 --- a/scripts/linter.rb +++ b/scripts/linter.rb @@ -7,7 +7,7 @@ require_relative '../src/_plugins/pillow/get_image_info' -require_relative 'linting/netlify_redirects' +require_relative 'linting/caddy_redirects' require_relative 'linting/logging' require_relative 'linting/check_all_urls_are_hackable' @@ -100,12 +100,12 @@ def check_yaml_front_matter(src_dir) # This ensures that any redirects I create are working. It doesn't mean # I can't forget to create a redirect, but it does mean I won't create # a redirect that points to another broken page. -def check_netlify_redirects(dst_dir) - info('Checking Netlify redirect rules...') +def check_redirects(dst_dir) + info('Checking Caddy redirect config...') bad_lines = [] - parse_netlify_redirects("#{dst_dir}/_redirects").each do |redirect| + parse_caddy_redirects.each do |redirect| # A couple of special cases that I don't worry about. next if redirect[:source] == '/ideas-for-inclusive-events/*' next if redirect[:target].start_with? 'https://' @@ -156,5 +156,5 @@ def report_errors(errors) check_writing_has_been_archived(src_dir) check_yaml_front_matter(src_dir) -check_netlify_redirects(html_dir) +check_redirects(html_dir) check_all_urls_are_hackable(html_dir) diff --git a/scripts/linting/caddy_redirects.rb b/scripts/linting/caddy_redirects.rb new file mode 100644 index 000000000..ceecfd461 --- /dev/null +++ b/scripts/linting/caddy_redirects.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +def parse_caddy_redirects + File.readlines('caddy/redirects.Caddyfile').each.with_index(1) + .filter { |line, _| line.start_with? 'redir' } + .map do |line, lineno| + { + lineno:, + line:, + source: line.strip.split[1], + target: line.strip.split[2] + } + end +end diff --git a/scripts/linting/check_all_urls_are_hackable.rb b/scripts/linting/check_all_urls_are_hackable.rb index 2ffd182a4..11ea6f0be 100644 --- a/scripts/linting/check_all_urls_are_hackable.rb +++ b/scripts/linting/check_all_urls_are_hackable.rb @@ -16,7 +16,7 @@ require 'pathname' require_relative 'logging' -require_relative 'netlify_redirects' +require_relative 'caddy_redirects' # Given a path, return a list of all its parent directories. # @@ -57,7 +57,7 @@ def check_all_urls_are_hackable(dst_dir) # The goal is to have two sets of URLs without trailing slashes, # e.g. {'/writing', '/til'} # - redirects = parse_netlify_redirects("#{dst_dir}/_redirects").to_set { |r| r[:source].chomp('/') } + redirects = parse_caddy_redirects.to_set { |r| r[:source].chomp('/') } folders_with_index_html = Dir.glob("#{dst_dir}/**/index.html").map { |p| File.dirname(p).gsub(dst_dir, '') } # Work out all the URLs that somebody could "hack" their way towards. diff --git a/scripts/linting/netlify_redirects.rb b/scripts/linting/netlify_redirects.rb deleted file mode 100644 index 7df2bb035..000000000 --- a/scripts/linting/netlify_redirects.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -# Parse the Netlify `_redirects` file. -# -# See https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file -# -def parse_netlify_redirects(path) - File.readlines(path).each.with_index(1) - .reject { |line, _| line.start_with? '#' } - .reject { |line, _| line.strip.empty? } - .map do |line, lineno| - { - lineno:, - line:, - source: line.strip.split[0], - target: line.strip.split[1] - } - end -end From 13130ae910d099ef6a0d38059c3bbd1a9a682785 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:31:13 +0000 Subject: [PATCH 15/20] Remove the "Deploy to Netlify" steps --- .github/workflows/build_site.yml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index 2db79a069..751f33c38 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -110,27 +110,6 @@ jobs: echo "There is no _drafts folder, so nothing to publish!" fi - - name: Deploy to Netlify - id: deploy_to_netlify - uses: nwtgck/actions-netlify@v3.0 - with: - publish-dir: "_site" - production-branch: main - github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - deploy-message: "Deploy from GitHub Actions" - enable-pull-request-comment: true - overwrites-pull-request-comment: true - netlify-config-path: netlify.toml - env: - NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} - NETLIFY_SITE_ID: "da37a488-4df9-4cc2-b267-947179af20bd" - timeout-minutes: 1 - - - name: Print Netlify Deploy URL - env: - SITE_URL: ${{steps.deploy_to_netlify.outputs.deploy-url}} - run: echo "$SITE_URL" - - name: Deploy to Linode uses: burnett01/rsync-deployments@7.0.1 # if: github.ref == 'refs/heads/main' From 3a90986b424ca95956a59b936265f615f42fbba6 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:45:57 +0000 Subject: [PATCH 16/20] Print the progress + replace the SSH deploy key --- .github/workflows/build_site.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index 751f33c38..4f10ff783 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -114,7 +114,7 @@ jobs: uses: burnett01/rsync-deployments@7.0.1 # if: github.ref == 'refs/heads/main' with: - switches: -avzr --delete --exclude="" --include="" --filter="" + switches: -avzr --progress --delete --exclude="" --include="" --filter="" path: _site/ remote_path: repos/alexwlchan.net/_site remote_host: "178.79.134.59" From d5721910f9eca6a1eab90f990c161bb62f9bb97a Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 21:51:10 +0000 Subject: [PATCH 17/20] Do a test ping from GitHub Actions --- .github/workflows/build_site.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index 4f10ff783..b72789810 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -110,6 +110,10 @@ jobs: echo "There is no _drafts folder, so nothing to publish!" fi + - name: Test you can see the Linode + run: + ping -c 1 178.79.134.59 + - name: Deploy to Linode uses: burnett01/rsync-deployments@7.0.1 # if: github.ref == 'refs/heads/main' From 82f60aceee19c79dce6f8d5e3b0cfe26b724bc5a Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 22:11:25 +0000 Subject: [PATCH 18/20] Only deploy to Linode from pull requests --- .github/workflows/build_site.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index b72789810..2aaee1957 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -116,7 +116,7 @@ jobs: - name: Deploy to Linode uses: burnett01/rsync-deployments@7.0.1 - # if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main' with: switches: -avzr --progress --delete --exclude="" --include="" --filter="" path: _site/ From 472591e711fa0228aa92b693a57b1bafe0d6fa31 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 22:11:48 +0000 Subject: [PATCH 19/20] Add a script to sync my local dir and my Linode --- scripts/sync_with_linode.sh | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100755 scripts/sync_with_linode.sh diff --git a/scripts/sync_with_linode.sh b/scripts/sync_with_linode.sh new file mode 100755 index 000000000..ce065dcae --- /dev/null +++ b/scripts/sync_with_linode.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# This script can be used to synchronise my local build with the +# _site folder on GitHub. +# +# There are two options: +# +# $ sync_with_linode.sh pull +# This pulls the latest code from Linode to the local _site folder. +# +# $ sync_with_linode.sh push +# This replaces the site on the Linode with my local _site folder. +# This will only push production builds of the site. +# + +set -o errexit +set -o nounset + +if (( $# != 1 )) +then + echo "Usage: $0 push|pull" >&2 + exit 1 +fi + +case $1 in + "pull") + SOURCE="alexwlchan@harmonia.linode:repos/alexwlchan.net/_site/" + TARGET="_site" + ;; + + "push") + if ! grep -q '' _site/index.html + then + echo "Will not push development build of site!" >&2 + exit 1 + fi + + SOURCE="_site" + TARGET="alexwlchan@harmonia.linode:repos/alexwlchan.net/_site/" + ;; + + *) + echo "Usage: $0 push|pull" >&2 + exit 1 + ;; +esac + +rsync \ + --archive \ + --compress \ + --verbose \ + --recursive \ + --progress \ + --delete \ + "$SOURCE" "$TARGET" From 2a6dbec8ae969f01846e27e2ce37a451a9de4515 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Wed, 30 Oct 2024 22:14:58 +0000 Subject: [PATCH 20/20] We only need to test for Linode connectivity if we're deploying --- .github/workflows/build_site.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_site.yml b/.github/workflows/build_site.yml index 2aaee1957..d4270f919 100644 --- a/.github/workflows/build_site.yml +++ b/.github/workflows/build_site.yml @@ -111,6 +111,7 @@ jobs: fi - name: Test you can see the Linode + if: github.ref == 'refs/heads/main' run: ping -c 1 178.79.134.59