<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Michiel Appelman</title>
    <link>https://appelman.se/</link>
    <description>Recent content on Michiel Appelman</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Tue, 18 Apr 2023 23:03:00 +0200</lastBuildDate><atom:link href="https://appelman.se/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Setting up a Matrix Server with Cloudflare</title>
      <link>https://appelman.se/matrix-on-cloudflare/</link>
      <pubDate>Tue, 18 Apr 2023 23:03:00 +0200</pubDate>
      
      <guid>https://appelman.se/matrix-on-cloudflare/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://matrix.org/&#34;&gt;Matrix&lt;/a&gt; is an open source project and standard for instant-messaging. It&amp;rsquo;s already being used (in an adapted form, mostly) by government agencies and corporations in some parts of the world. In the past it had been a bit tedious to set up, so over the past two years or so, I&amp;rsquo;ve payed &lt;a href=&#34;https://element.io/&#34;&gt;Element&lt;/a&gt; to host a Matrix server for me. However, since I have not been using it much but was also missing some flexibility to play with the server itself, I started looking at self-hosting again.&lt;/p&gt;
&lt;p&gt;Luckily, there has been some very active development on automating those deployments on Github: &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/tree/master&#34;&gt;spantaleev/matrix-docker-ansible-deploy&lt;/a&gt;. And since the Matrix protocol itself is HTTPS-based, most of the services can actually be exposed through Cloudflare. This adds some useful security and by using Cloudflare Tunnels we can also host this anywhere: there are no dependencies on static IP addresses or poking holes in your firewall.&lt;/p&gt;
&lt;p&gt;In this guide I&amp;rsquo;ll show you how to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Connect to your server with SSH through Cloudflare Tunnel&lt;/li&gt;
&lt;li&gt;Configure Ansible and the Matrix Docker playbooks to get your first deployment going&lt;/li&gt;
&lt;li&gt;Exposing the Matrix services through Cloudflare Tunnel&lt;/li&gt;
&lt;li&gt;Setting up proper delegation and discovery with Cloudflare Workers&lt;/li&gt;
&lt;li&gt;Using Cloudflare R2 for media storage&lt;/li&gt;
&lt;li&gt;How to receive Cloudflare webhook notifications in your new Matrix server&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;notice note&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-sticky-note&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Note
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;If you haven&amp;rsquo;t noticed yet: yes, I&amp;rsquo;m very biased to using Cloudflare here. Although I work there as a Solutions Engineer, I do pay for my own Pro account and do these things for fun.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&#34;server-setup&#34;&gt;
  Server Setup
  &lt;a class=&#34;heading-link&#34; href=&#34;#server-setup&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;As we want to focus on the Matrix + Cloudflare part, I&amp;rsquo;ll not spend to much time on getting the server set up, but any major distro should work, and easiest is to use one that&amp;rsquo;s supported on the &lt;a href=&#34;https://pkg.cloudflare.com/index.html&#34;&gt;pkg.cloudflare.com&lt;/a&gt; list so you can grab &lt;code&gt;cloudflared&lt;/code&gt; from the package repository.&lt;/p&gt;
&lt;p&gt;To connect to your server through Cloudflare Tunnel, you&amp;rsquo;ll basically need to follow this tutorial: &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/use_cases/ssh/#connect-to-ssh-server-with-cloudflared-access&#34;&gt;Connect to SSH server with &lt;code&gt;cloudflared access&lt;/code&gt;&lt;/a&gt;. You&amp;rsquo;ll create a Public Hostname such as this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;ssh-public-hostname.png&#34;
         alt=&#34;Public Hostname for SSH on your server&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Public Hostname for SSH on your server&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;m also skipping a big part about getting Access Applications set up for this, BTW. So if you haven&amp;rsquo;t already, please take some time to go through:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setting up user authentication, either through a &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/identity/one-time-pin/&#34;&gt;One-time PIN&lt;/a&gt; or an &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/&#34;&gt;Identity Provider&lt;/a&gt; (Recommended).&lt;/li&gt;
&lt;li&gt;Protecting hostnames using authentication with &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/&#34;&gt;Cloudflare Access Applications&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you feel ready with that setup, you&amp;rsquo;ll need to install &lt;code&gt;cloudflared&lt;/code&gt; on your own computer (from where you normally run SSH) as well (for macOS users: &lt;code&gt;brew install cloudflared&lt;/code&gt;). By using the &lt;code&gt;ProxyCommand&lt;/code&gt; in &lt;code&gt;.ssh/config&lt;/code&gt;, you&amp;rsquo;ll be able to transparently connect to the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;Host ssh.example.com
    ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test whether that works and if you did have any ports open on your firewall for SSH towards your server, feel free to close them. All your SSH traffic will now be going over the Cloudflare Tunnel. Isn&amp;rsquo;t that a safe feeling!?&lt;/p&gt;
&lt;h2 id=&#34;connecting-with-ansible&#34;&gt;
  Connecting with Ansible
  &lt;a class=&#34;heading-link&#34; href=&#34;#connecting-with-ansible&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;First start by having a read through the &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/README.md&#34;&gt;matrix-docker-ansible-deploy docs&lt;/a&gt;. Specifically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/getting-the-playbook.md&#34;&gt;Getting the playbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook.md&#34;&gt;Configuring the Ansible playbook&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because we&amp;rsquo;ve configured our server to be reachable through Cloudflare Tunnel, we need to make some advanced adjustments to the &lt;code&gt;inventory/hosts&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;[matrix_servers]
matrix.example.com ansible_host=ssh.example.com

[matrix_servers:vars]
ansible_ssh_user=root
ansible_ssh_private_key_file=&amp;#39;~/.ssh/id_ed25519&amp;#39;
ansible_ssh_common_args=&amp;#39;-o ProxyCommand=&amp;#34;/opt/homebrew/bin/cloudflared access ssh --hostname %h&amp;#34; &amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks pretty similar to the &lt;code&gt;.ssh/config&lt;/code&gt; set up, and I also included the SSH key that the connection should use, which I also assume is something you&amp;rsquo;ve set up for your server.&lt;/p&gt;
&lt;p&gt;When this looks good you can test the connection to the server with Ansible by running the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ansible -i inventory/hosts all -m ping -v
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;configuring-the-playbook&#34;&gt;
  Configuring the Playbook
  &lt;a class=&#34;heading-link&#34; href=&#34;#configuring-the-playbook&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Most of the customization is already done by now. What&amp;rsquo;s left is putting Cloudflare Tunnel in front of the two main Matrix services, and deploying the full set of services.&lt;/p&gt;
&lt;p&gt;Since we&amp;rsquo;ll use Cloudflare Tunnel as the main point of entry, we&amp;rsquo;ll follow the &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-own-webserver.md#fronting-the-integrated-reverse-proxy-webserver-with-another-reverse-proxy&#34;&gt;matrix-docker-ansible-deploy docs about using your own webserver&lt;/a&gt;. If we follow along in that article, you&amp;rsquo;ll end up with a configuration file looking like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#586e75&#34;&gt;# By default, the playbook manages its own Traefik (https://doc.traefik.io/traefik/) reverse-proxy server.&lt;/span&gt;
&lt;span style=&#34;color:#586e75&#34;&gt;# It will retrieve SSL certificates for you on-demand and forward requests to all other components.&lt;/span&gt;
&lt;span style=&#34;color:#586e75&#34;&gt;# For alternatives, see `docs/configuring-playbook-own-webserver.md`.&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_playbook_reverse_proxy_type&lt;/span&gt;: playbook-managed-traefik

&lt;span style=&#34;color:#586e75&#34;&gt;# Ensure that public urls use https&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_playbook_ssl_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;

&lt;span style=&#34;color:#586e75&#34;&gt;# Disable the web-secure (port 443) endpoint, which also disables SSL certificate retrieval&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;devture_traefik_config_entrypoint_web_secure_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;false&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;devture_traefik_container_web_host_bind_port&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;127.0.0.1:81&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;devture_traefik_config_entrypoint_web_forwardedHeaders_insecure&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;devture_traefik_additional_entrypoints_auto&lt;/span&gt;:
  - &lt;span style=&#34;color:#268bd2&#34;&gt;name&lt;/span&gt;: matrix-federation
    &lt;span style=&#34;color:#268bd2&#34;&gt;port&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;8449&lt;/span&gt;
    &lt;span style=&#34;color:#268bd2&#34;&gt;host_bind_port&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;127.0.0.1:8449&amp;#34;&lt;/span&gt;
    &lt;span style=&#34;color:#268bd2&#34;&gt;config&lt;/span&gt;: {}

&lt;span style=&#34;color:#586e75&#34;&gt;# Disable integrated Coturn server&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_coturn_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I disabled the TURN service because I have no need for audio/video calls through Matrix. Enabling TURN is possible, but then you&amp;rsquo;d have to expose your server from outside of Cloudflare, which might be a bit scary.&lt;/p&gt;
&lt;p&gt;With all of the base configuration done, have a look at the &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/installing.md&#34;&gt;installation instructions&lt;/a&gt; (or blindly run &lt;code&gt;just setup-all&lt;/code&gt; 😅).&lt;/p&gt;
&lt;h2 id=&#34;cloudflare-tunnel&#34;&gt;
  Cloudflare Tunnel
  &lt;a class=&#34;heading-link&#34; href=&#34;#cloudflare-tunnel&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While that is doing its thing (without any errors 🤞), we can proceed to expose the services through Cloudflare Tunnel. What&amp;rsquo;s important to note is that a single service on the server has to have a single hostname. This means we cannot expose the Matrix Client service (exposed locally behind port 81) and the Matrix Server service (local port 8449) both behind &lt;code&gt;matrix.example.com&lt;/code&gt;. However, we&amp;rsquo;ll trick the Matrix server into thinking that it is running on the same hostname.&lt;/p&gt;
&lt;p&gt;For that we create two Public Hostnames:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;matrix.example.com&lt;/code&gt; pointing to port 81 on the server:
&lt;figure&gt;&lt;img src=&#34;matrix-public-hostname.png&#34;
         alt=&#34;Public Hostname for Matrix where clients connect to&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Public Hostname for Matrix where clients connect to&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;matrix-fed.example.com&lt;/code&gt; pointing to port 8449 with Host header set to &lt;code&gt;matrix.example.com&lt;/code&gt;:
&lt;figure&gt;&lt;img src=&#34;matrix-fed-public-hostname.png&#34;
         alt=&#34;Public Hostname for Matrix where othere servers connect to for federation&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Public Hostname for Matrix where othere servers connect to for federation&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when a server connects to the &lt;code&gt;matrix-fed.example.com&lt;/code&gt; hostname, our Matrix server will think that everything is fine and being served a &lt;code&gt;matrix.example.com&lt;/code&gt;, because we&amp;rsquo;ve overriden the Host header in the Cloudflare Tunnel.&lt;/p&gt;
&lt;h2 id=&#34;delegation-and-discovery&#34;&gt;
  Delegation and Discovery
  &lt;a class=&#34;heading-link&#34; href=&#34;#delegation-and-discovery&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;But how will other servers know to connect to &lt;code&gt;matrix-fed.example.com&lt;/code&gt;, and not &lt;code&gt;matrix.example.com&lt;/code&gt;? Well, for that we&amp;rsquo;ll use the delegation and discovery mechanisms build in to the Matrix specification.&lt;/p&gt;
&lt;p&gt;To do proper delegation, you must use the &amp;lsquo;well-known&amp;rsquo; files at the root of your domain (&lt;code&gt;example.com&lt;/code&gt;), not an SRV record. This is because we use a delegated server and the &lt;a href=&#34;https://spec.matrix.org/v1.6/server-server-api/#server-discovery&#34;&gt;Matrix Specification&lt;/a&gt; says that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DNS is insecure (not all domains have DNSSEC), so the target of the delegation must prove that it is a valid delegate for &lt;code&gt;&amp;lt;hostname&amp;gt;&lt;/code&gt; via TLS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you already have an easy way to serve files on your main domain, you need to provide the following two files:&lt;/p&gt;
&lt;p&gt;Available under the &lt;code&gt;https://example.com/.well-known/matrix/client&lt;/code&gt; URI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;{
  &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;m.homeserver&amp;#34;&lt;/span&gt;: {
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;base_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;
  },
  &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;m.identity_server&amp;#34;&lt;/span&gt;: {
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;base_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://vector.im&amp;#34;&lt;/span&gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And vailable under the &lt;code&gt;https://example.com/.well-known/matrix/server&lt;/code&gt; URI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;{ &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;m.server&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;matrix-fed.example.com:8443&amp;#34;&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have nothing on your main domain, you can also create a directory structure &lt;code&gt;matrix-well-known/.well-known/matrix&lt;/code&gt; containing these two files and uploading them to Cloudfare Pages with &lt;code&gt;example.com&lt;/code&gt; as a custom domain.&lt;/p&gt;
&lt;h3 id=&#34;workers&#34;&gt;
  Workers
  &lt;a class=&#34;heading-link&#34; href=&#34;#workers&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;A more advanced use case, is where you already have some application running on your main (apex) domain, and can&amp;rsquo;t easily add these static files there. If that application is being proxied through Cloudflare, you can leverage Workers with custom routes (aka triggers) to dynamically reply to these requests.&lt;/p&gt;
&lt;p&gt;Because the simple nature of such a Worker, I created it through the dashboard and assigned the following route triggers to it:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;worker-routes.png&#34;
         alt=&#34;Worker Triggers, aka Routes for .well-known Matrix files&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Worker Triggers, aka Routes for .well-known Matrix files&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;This also allows me to get the same results on all my domains, without having to manually keep them in sync.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I then used the &amp;ldquo;Quick Edit&amp;rdquo; function to compose the following quick-and-dirty script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;const&lt;/span&gt; HOMESERVER_URL &lt;span style=&#34;color:#719e07&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://matrix.example.com&amp;#34;&lt;/span&gt;;
&lt;span style=&#34;color:#268bd2&#34;&gt;const&lt;/span&gt; IDENTITY_SERVER_URL &lt;span style=&#34;color:#719e07&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://vector.im&amp;#34;&lt;/span&gt;;
&lt;span style=&#34;color:#268bd2&#34;&gt;const&lt;/span&gt; FEDERATION_SERVER &lt;span style=&#34;color:#719e07&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;matrix-fed.example.com:8443&amp;#34;&lt;/span&gt;;

&lt;span style=&#34;color:#268bd2&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#719e07&#34;&gt;default&lt;/span&gt; {
  &lt;span style=&#34;color:#268bd2&#34;&gt;async&lt;/span&gt; fetch(request, env) {
    &lt;span style=&#34;color:#268bd2&#34;&gt;const&lt;/span&gt; path &lt;span style=&#34;color:#719e07&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#719e07&#34;&gt;new&lt;/span&gt; URL(request.url).pathname;
    &lt;span style=&#34;color:#719e07&#34;&gt;switch&lt;/span&gt; (path) {
      &lt;span style=&#34;color:#719e07&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;/.well-known/matrix/client&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#719e07&#34;&gt;:&lt;/span&gt;
        &lt;span style=&#34;color:#719e07&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#719e07&#34;&gt;new&lt;/span&gt; Response(
          &lt;span style=&#34;color:#586e75&#34;&gt;`{&amp;#34;m.homeserver&amp;#34;: {&amp;#34;base_url&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;${&lt;/span&gt;HOMESERVER_URL&lt;span style=&#34;color:#2aa198&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#586e75&#34;&gt;&amp;#34;},&amp;#34;m.identity_server&amp;#34;: {&amp;#34;base_url&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;${&lt;/span&gt;IDENTITY_SERVER_URL&lt;span style=&#34;color:#2aa198&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#586e75&#34;&gt;&amp;#34;}}`&lt;/span&gt;
        );
      &lt;span style=&#34;color:#719e07&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;/.well-known/matrix/server&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#719e07&#34;&gt;:&lt;/span&gt;
        &lt;span style=&#34;color:#719e07&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#719e07&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#586e75&#34;&gt;`{&amp;#34;m.server&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;${&lt;/span&gt;FEDERATION_SERVER&lt;span style=&#34;color:#2aa198&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#586e75&#34;&gt;&amp;#34;}`&lt;/span&gt;);
      &lt;span style=&#34;color:#719e07&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#719e07&#34;&gt;:&lt;/span&gt;
        &lt;span style=&#34;color:#719e07&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#719e07&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;Invalid request&amp;#34;&lt;/span&gt;);
    }
  },
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Simple enough! And it does the job without interfering with the rest of my websites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Congratulations! You now should have a fully functional Matrix server! To celebrate, feel free to &lt;a href=&#34;https://matrix.to/#/@michiel:appelman.cloud&#34;&gt;send me a message on Matrix&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Also take a look at enabling &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-email.md&#34;&gt;sending emails through a relay server&lt;/a&gt;. I used the service provided by my VPS hoster &lt;a href=&#34;https://www.transip.nl/vps/VPSXS/&#34;&gt;TransIP&lt;/a&gt;. This is especially useful to verify email addresses associated to your Matrix identity.&lt;/p&gt;
&lt;p&gt;Finish setting up your server and make sure you update the services every once in a while &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/maintenance-upgrading-services.md&#34;&gt;according to the docs&lt;/a&gt;!&lt;/p&gt;
&lt;div class=&#34;notice tip&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-lightbulb-o&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Tip
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;Please continue reading if you want to learn about setting up R2 and Webhook notifications in Cloudflare, but note that these are paid features.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&#34;r2-for-media-storage&#34;&gt;
  R2 for Media Storage
  &lt;a class=&#34;heading-link&#34; href=&#34;#r2-for-media-storage&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Another fun thing I tried is to use Cloudflare&amp;rsquo;s S3-compatible storage called R2 as a media store. This is very easy to setup with &lt;code&gt;matrix-docker-ansible-deploy&lt;/code&gt; as well, by following the &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-synapse-s3-storage-provider.md&#34;&gt;instructions in the docs&lt;/a&gt; but adapting to R2:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_config_bucket&lt;/span&gt;: matrix-media
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_config_region_name&lt;/span&gt;: auto
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_config_endpoint_url&lt;/span&gt;: https://xxxxxxxxxxx.r2.cloudflarestorage.com
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_config_access_key_id&lt;/span&gt;: xxxxxxxxxxx
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_synapse_ext_synapse_s3_storage_provider_config_secret_access_key&lt;/span&gt;: xxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the region is set to &lt;code&gt;auto&lt;/code&gt;, but the other variables should be self explanatory if you follow the &lt;a href=&#34;https://developers.cloudflare.com/r2/get-started/#2-create-a-bucket&#34;&gt;R2 documenation&lt;/a&gt; and create an &lt;a href=&#34;https://developers.cloudflare.com/r2/api/s3/tokens/&#34;&gt;&lt;em&gt;Edit&lt;/em&gt; API token&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;webhook-notifications&#34;&gt;
  Webhook Notifications
  &lt;a class=&#34;heading-link&#34; href=&#34;#webhook-notifications&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you are a paying Cloudflare customer, you&amp;rsquo;re able to configure &lt;a href=&#34;https://developers.cloudflare.com/fundamentals/notifications/create-notifications/configure-webhooks/&#34;&gt;webhook notifications&lt;/a&gt; in your dashboard. I desperately wanted to use this feature, so my next step was to get them delivered in my new Matrix server. For this, we&amp;rsquo;ll need to enable the &amp;lsquo;generic&amp;rsquo; webhooks in the &lt;a href=&#34;https://matrix-org.github.io/matrix-hookshot/latest/hookshot.html&#34;&gt;Hookshot&lt;/a&gt; bridge:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_generic_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For more info on setting this up, see the &lt;a href=&#34;https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-bridge-hookshot.md&#34;&gt;matrix-docker-ansible-deploy docs for hookshot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After you rerun the deploy command (&lt;code&gt;just setup-all&lt;/code&gt;) your Matrix server should be back up. Open your Element client, invite the bot in to a room where you want to receive the notifications, and make sure to give it Mod powers. Then send it a message containing &lt;code&gt;!hookshot webhook Cloudflare&lt;/code&gt; to the room. If everything went well, you should now receive a private message from the Hookshot bot containing a URL that you can then use in the Cloudflare dashboard.&lt;/p&gt;
&lt;h3 id=&#34;deleting-a-webhook&#34;&gt;
  Deleting a Webhook
  &lt;a class=&#34;heading-link&#34; href=&#34;#deleting-a-webhook&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;One thing I ran into was that there is &lt;a href=&#34;https://github.com/matrix-org/matrix-hookshot/issues/116&#34;&gt;no easy way to delete a webhook once created&lt;/a&gt;. For that you need Hookshot&amp;rsquo;s &lt;a href=&#34;https://matrix-org.github.io/matrix-hookshot/latest/advanced/provisioning.html&#34;&gt;provisioning API&lt;/a&gt;. Opening up the &lt;code&gt;host_vars&lt;/code&gt; YAML file again to add it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_provisioning_enabled&lt;/span&gt;: &lt;span style=&#34;color:#cb4b16&#34;&gt;true&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_provisioning_port&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;9002&lt;/span&gt; &lt;span style=&#34;color:#586e75&#34;&gt;# default port&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_provisioning_secret&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;XXXXXX&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_provisioning_internal&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;/v1&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_provisioning_endpoint&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://hookshot.example.com{{ matrix_hookshot_provisioning_internal }}&amp;#34;&lt;/span&gt;

&lt;span style=&#34;color:#268bd2&#34;&gt;matrix_hookshot_container_http_host_bind_ports&lt;/span&gt;:
  - &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;127.0.0.1:9002:9002&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then run the setup again with &lt;code&gt;just setup-all&lt;/code&gt;. While that runs, go to the Cloudflare Zero Trust dashboard, and add a Public Hostname to the Cloudflare Tunnel, and remember to also protect it with an Access Application.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;hookshot-public-hostname.png&#34;
         alt=&#34;Public Hostname for the Hookshot provisioning API&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Public Hostname for the Hookshot provisioning API&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;When all that&amp;rsquo;s done, you&amp;rsquo;ll be able to run curl commands using &lt;code&gt;cloudflared&lt;/code&gt; providing your Matrix user identity in the query and the provisioning secret in the Authorization header:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;cloudflared access curl &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;https://hookshot.example.com/v1/&lt;/span&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;$ROOM_ID&lt;/span&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;/connections?userId=%40user%3Aexample.com&amp;#34;&lt;/span&gt; -H &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span style=&#34;color:#268bd2&#34;&gt;$PROV_SECRET&lt;/span&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Running an IPv6-only webserver</title>
      <link>https://appelman.se/ipv6-only-server/</link>
      <pubDate>Fri, 29 Jul 2022 17:00:00 +0200</pubDate>
      
      <guid>https://appelman.se/ipv6-only-server/</guid>
      <description>&lt;p&gt;Recently more and more hosters have started to offer IPv6-only virtual private servers (VPSs) that give you your own dedicated server at a major discount:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.transip.nl/vps/sandboxvps/&#34;&gt;TransIP - SandboxVPS&lt;/a&gt; – €2,50/month (ex. VAT)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://v6node.com/&#34;&gt;v6Node.com&lt;/a&gt; – €9/&lt;strong&gt;year&lt;/strong&gt; - recently featured on &lt;a href=&#34;https://news.ycombinator.com/item?id=32271917&#34;&gt;Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.hetzner.com/cloud&#34;&gt;Hetzner&lt;/a&gt; – disable IPv4 for a server and start from €3,62/month&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://store.pcextreme.nl/cart/preselect/server/compute_standard_1gb_ipv6_only&#34;&gt;PCextreme&lt;/a&gt; – €1,00/month&lt;/li&gt;
&lt;li&gt;Possibly a lot more, see for some examples the HN thread linked above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have not been blessed with IPv6 connectivity at home, that might look pretty useless. How do you connect to the server to manage it with SSH? How do you actually connect to the web application you want to host? Luckily there is a way around this by using Cloudflare Tunnel and SSH Access.&lt;/p&gt;
&lt;h2 id=&#34;choosing-the-right-server&#34;&gt;
  Choosing the right server
  &lt;a class=&#34;heading-link&#34; href=&#34;#choosing-the-right-server&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The easiest way to get Cloudflare Tunnel installed is by using a distribution and version for which Cloudflare releases pre-built packages of Cloudflare Tunnel (aka &lt;code&gt;cloudflared&lt;/code&gt;). An up-to-date complete list can be found on the &lt;a href=&#34;https://pkg.cloudflare.com/&#34;&gt;package repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also pick any other distribution and take care of the installation of the &lt;code&gt;cloudflared&lt;/code&gt; executable yourself as described on the GitHub page: &lt;a href=&#34;https://github.com/cloudflare/cloudflared#installing-cloudflared&#34;&gt;cloudflare/cloudflared&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;notice note&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-sticky-note&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Note
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;The next steps assume you have a &lt;a href=&#34;https://www.cloudflare.com/sign-up&#34;&gt;Cloudflare Free account&lt;/a&gt; with a domain onboarded, and have done the initial setup of your &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/setup/&#34;&gt;Zero Trust dashboard&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&#34;cloudflare-tunnel&#34;&gt;
  Cloudflare Tunnel
  &lt;a class=&#34;heading-link&#34; href=&#34;#cloudflare-tunnel&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Open the &lt;a href=&#34;https://dash.teams.cloudflare.com/&#34;&gt;Cloudflare Zero Trust dashboard&lt;/a&gt;, navigate to &lt;strong&gt;Access &amp;gt; Tunnels&lt;/strong&gt; and click &lt;strong&gt;Create New Tunnel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can pick a name and will then get to the following screen where you can copy paste the command to install the &lt;code&gt;cloudflared&lt;/code&gt; package based on your OS:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;1.png&#34;
         alt=&#34;Connecting the connector&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Connecting the connector&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The bottom part of the screen will auto-refresh to show you any connectors that have been found to connect to Cloudflare, but this probably won&amp;rsquo;t happen yet.&lt;/p&gt;
&lt;p&gt;To get the connector to connect to Cloudflare using IPv6-only, we need to change the way the service is started. For this change the systemd unit file located at &lt;code&gt;/etc/systemd/system/cloudflared.service&lt;/code&gt; to include the &lt;code&gt;--edge-ip-version 6&lt;/code&gt; option in the &lt;code&gt;ExecStart&lt;/code&gt; command. It should look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;[Unit]
Description=cloudflared
After=network.target

[Service]
TimeoutStartSec=&lt;span style=&#34;color:#2aa198&#34;&gt;0&lt;/span&gt;
Type=notify
ExecStart=/usr/bin/cloudflared --edge-ip-version &lt;span style=&#34;color:#2aa198&#34;&gt;6&lt;/span&gt; --no-autoupdate tunnel run --token xxxxx
Restart=on-failure
RestartSec=&lt;span style=&#34;color:#2aa198&#34;&gt;5&lt;/span&gt;s

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart the service and you should be seeing a connection pop up in the Cloudflare Zero Trust dashboard. Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;add-an-ssh-service&#34;&gt;
  Add an SSH Service
  &lt;a class=&#34;heading-link&#34; href=&#34;#add-an-ssh-service&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;On the next screen you will configure a Public Hostname for the SSH server that&amp;rsquo;s running on the server. Simply choose a hostname as part of your Cloudflare domain and point it to SSH on the localhost:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;2.png&#34;
         alt=&#34;Set a public hostname for the SSH application&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Set a public hostname for the SSH application&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;That&amp;rsquo;s it! Finish the configuration for the Tunnel by clicking &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;notice note&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-sticky-note&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Note
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;Another cool thing about Cloudflare Tunnel is that it will setup an outbound connection to the Cloudflare Edge. This means you will not have to open any ports towards your IPv6-only server with your provider! Not for SSH, nor for HTTP(S) traffic.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&#34;protect-the-ssh-application&#34;&gt;
  Protect the SSH Application
  &lt;a class=&#34;heading-link&#34; href=&#34;#protect-the-ssh-application&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Now head over to the &lt;strong&gt;Access &amp;gt; Applications&lt;/strong&gt; screen to add a new &amp;lsquo;Self-hosted&amp;rsquo; application for your &lt;code&gt;vps-ssh.example.com&lt;/code&gt; public hostname:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;3.png&#34;
         alt=&#34;Setup an Access Application to protect the SSH service&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Setup an Access Application to protect the SSH service&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the &lt;strong&gt;Identity providers&lt;/strong&gt; section you can always choose &lt;strong&gt;One-time PIN&lt;/strong&gt; or get Okta onboarded first (did you know they have a &lt;a href=&#34;https://developer.okta.com/signup/&#34;&gt;free Developer sign-up&lt;/a&gt;?). This will take care of the authentication of the users.&lt;/p&gt;
&lt;p&gt;Next you will have to define the authorization for the application. For example, you want to limit access only to certain email addresses or members of specific Okta groups:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;4.png&#34;
         alt=&#34;Configure an authentication policy&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Configure an authentication policy&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And finally on the next screen you can scroll down and enable browser-rendering for the SSH session so that you can connect without any terminal.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;5.png&#34;
         alt=&#34;Enable browser rendering&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Enable browser rendering&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;connect-from-a-terminal&#34;&gt;
  Connect from a terminal
  &lt;a class=&#34;heading-link&#34; href=&#34;#connect-from-a-terminal&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Connecting through a browser can be nice a last-resort, but most times an actual terminal is much more comfortable. Based on the &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/tutorials/ssh/#connect-from-a-client-machine&#34;&gt;Cloudflare docs&lt;/a&gt; we can now try to connect to the just configured SSH application through a terminal.&lt;/p&gt;
&lt;p&gt;First install &lt;code&gt;cloudflared&lt;/code&gt; on your local machine, eg. with &lt;code&gt;brew&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;brew install cloudflare/cloudflare/cloudflared
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Second, edit your &lt;code&gt;~/.ssh/config&lt;/code&gt; file to include a Host for your new VPS SSH application with a special proxy command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;Host vps
  Hostname vps-ssh.example.com
  ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also be sure to include the right &lt;code&gt;IdentityFile&lt;/code&gt;s that your VPS expecting, and you should be good to go! Running &lt;code&gt;ssh vps&lt;/code&gt; should now open a browser window where you should be able to authenticate and then be redirected back to your terminal.&lt;/p&gt;
&lt;p&gt;With all of that setup, you now have your own webserver available through Cloudflare, reachable over IPv4 and IPv6, but just connected to IPv6. When you&amp;rsquo;re ready to add your first web applications, go back into the &lt;strong&gt;Access &amp;gt; Tunnels &amp;gt; VPS Tunnel&lt;/strong&gt; configuration and add a new Public Hostname that points to the port on localhost where the application is running. Have fun!&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;6.png&#34;
         alt=&#34;Create a new public hostname for the HTTP application&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Create a new public hostname for the HTTP application&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;further-reading&#34;&gt;
  Further Reading
  &lt;a class=&#34;heading-link&#34; href=&#34;#further-reading&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Connect another application: &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/#2-connect-an-application&#34;&gt;https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/#2-connect-an-application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Protecting your web applications: &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/&#34;&gt;https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An old but golden tutorial of Cloudflare&amp;rsquo;s CTO: &lt;a href=&#34;https://blog.cloudflare.com/ssh-raspberry-pi-400-cloudflare-tunnel-auditable-terminal/&#34;&gt;SSHing to my Raspberry Pi 400 from a browser, with Cloudflare Tunnel and Auditable Terminal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Ditching QWERTY</title>
      <link>https://appelman.se/ditching-qwerty/</link>
      <pubDate>Wed, 16 Feb 2022 10:00:00 +0100</pubDate>
      
      <guid>https://appelman.se/ditching-qwerty/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m not sure what typing level you were when you started your career. Still, I think it is safe to say that I was not at a level that prepared me for working life primarily spent at a keyboard. Even more so, of course, when the pandemic hit.&lt;/p&gt;
&lt;p&gt;What I mean by that is that I could not touch-type. Well, not with more than two fingers, because that is what I taught myself over the years without any formal training. So when the pandemic was in full swing, I decided to make the most of my time spent behind said keyboard and teach myself proper typing using the &lt;a href=&#34;https://colemak.com/&#34;&gt;Colemak&lt;/a&gt; layout. I did not settle on Colemak immediately, though, and if you&amp;rsquo;re on the fence about it, I will take you through my journey.&lt;/p&gt;
&lt;h2 id=&#34;what-to-learn&#34;&gt;
  What to learn?
  &lt;a class=&#34;heading-link&#34; href=&#34;#what-to-learn&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Since I taught myself some poor man&amp;rsquo;s touch typing on regular QWERTY keyboards, I knew I would have to unlearn some elaborate muscle memory. At the same time, I knew QWERTY was not the only layout and that it definitely did not become a &lt;em&gt;de facto&lt;/em&gt; standard because of its ergonomics. So I decided that since I started from scratch anyway, I would choose the appropriate layout for my needs. I have to warn you, though: you can (and probably will) get lost in a very deep rathole of keyboard layouts, with &lt;a href=&#34;https://colemak.org/&#34;&gt;one of the layouts&lt;/a&gt; having amassed an &lt;a href=&#34;https://viralintrospection.wordpress.com/2010/09/06/a-different-philosophy-in-designing-keyboard-layouts/&#34;&gt;even more passionate following&lt;/a&gt; than the other.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re even half-serious about choosing a new layout, you will end up on &lt;a href=&#34;http://mkweb.bcgsc.ca/carpalx/&#34;&gt;carpalx&lt;/a&gt;, where you will read up on stroke paths, hand alternations, triads, and much more. Those values combined for all the different layouts result in a particular score that makes one of the layouts the &lt;a href=&#34;http://mkweb.bcgsc.ca/carpalx/?full_optimization&#34;&gt;best (QFMLWY)&lt;/a&gt; and another the worst (no, that&amp;rsquo;s not QWERTY, but &lt;a href=&#34;http://mkweb.bcgsc.ca/carpalx/?worst_layout&#34;&gt;TNWMLC&lt;/a&gt;, an anti-optimized layout with its own fashion line). Then you learn that the guy behind carpalx also collaborated on &lt;a href=&#34;https://decorrespondent.nl/1783/we-kunnen-48-procent-sneller-typen-waarom-doen-we-het-dan-niet/135797038564-b6670f86&#34;&gt;an article&lt;/a&gt; on the Dutch journalistic website &lt;em&gt;De Correspondent&lt;/em&gt; to find the best Dutch layout. And you (or at least I, since I am Dutch) start to throw that in the comparison.&lt;/p&gt;
&lt;p&gt;I will not judge you if, by this time, you have built an Excel with a dozen or so layouts, their total efforts for different languages, etc. Probably also around this time, perhaps instigated by that very Excel, you will have a momentary flashback of a kiss. Or, on second thought, a KISS – Keep It Simple, Stupid! You realize that you have started to optimize prematurely, which, according to scholars, is where evil lives.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;colemak.png&#34;
         alt=&#34;Colemak layout&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Colemak layout&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;At this point, I made a quick decision to start with Colemak. It already is a massive improvement over QWERTY and also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leaves a lot of punctuation keys in the exact location – great for coding&lt;/li&gt;
&lt;li&gt;It is quite popular and therefore supported quite well on various touch-type learning tools&lt;/li&gt;
&lt;li&gt;It is supported out-of-the-box by macOS and iPadOS – this makes it super easy to switch to and from&lt;/li&gt;
&lt;li&gt;Apparently also isn&amp;rsquo;t too bad for Dutch!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;notice note&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-sticky-note&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Note
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;If you are using Windows, there is &lt;a href=&#34;https://www.change.org/p/microsoft-add-colemak-as-a-pre-installed-keyboard-layout-to-windows&#34;&gt;a petition to get Colemak supported&lt;/a&gt; out-of-the-box as well.&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&#34;how-to-learn&#34;&gt;
  How to learn?
  &lt;a class=&#34;heading-link&#34; href=&#34;#how-to-learn&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There is, of course, only one trick to learning anything, and that is repetition. I made an effort to practice for at least 15 minutes every day. Those 15 minutes turned out to be a bit of a minimum, but also a maximum: practising any longer would negatively affect my accuracy increase my frustration, resulting in even lower accuracy. &lt;a href=&#34;https://keybr.com&#34;&gt;keybr.com&lt;/a&gt; keeps a nice log of your lessons:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;keybr-calendar.png&#34;
         alt=&#34;Typing learing calendar&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Typing learing calendar&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You see here that I kept at it for a couple of weeks and was practising a lot less after about four months. The good news is that by that time, I felt confident enough to switch to Colemak full-time! Practising as I went with business email, chat, etc. Everything was now done without looking at the keyboard and using all my fingers.&lt;/p&gt;
&lt;p&gt;I do have to admit that I am not a fast typer. After a year of typing with Colemak, my top words-per-minute is 44. You can see progress in the graph below, but it&amp;rsquo;s not stellar by any measure. But honestly, I&amp;rsquo;m OK with that: it still feels great to type so effortlessly.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;keybr-graph.png&#34;
         alt=&#34;Typing speed graph&#34; loading=&#34;lazy&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Typing speed increase. Green is speed, red is accuracy, and purple is the number of letters.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Regarding tools, I initially started with &lt;a href=&#34;https://www.gnu.org/software/gtypist/&#34;&gt;GNU Typist&lt;/a&gt; but later switched to &lt;a href=&#34;https://keybr.com&#34;&gt;keybr.com&lt;/a&gt; mentioned earlier. The great thing about it is that you can set the Colemak layout to be emulated instead of changing input sources on your OS. This allows you to quickly get some practice. The way it teaches you is excellent but brutal: it will focus on a couple of keys initially and let you practice until it measures that you are comfortable with them. Then it adds new keys to the mix, coming back every time to a specific key when it measures you&amp;rsquo;re losing confidence in it.&lt;/p&gt;
&lt;p&gt;Whatever tool you decide to use, a typical recommendation is to use one that scores you primarily based on accuracy and then speed. As I have found out, your speed increase will be limited if you&amp;rsquo;re not accurate enough. Because, yeah, no matter how fast you can press backspace, that doesn&amp;rsquo;t count towards your WPM.&lt;/p&gt;
&lt;h2 id=&#34;remapping-hjkl&#34;&gt;
  Remapping h/j/k/l
  &lt;a class=&#34;heading-link&#34; href=&#34;#remapping-hjkl&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When you have taken the plunge, have spent some hours on &lt;a href=&#34;https://keybr.com&#34;&gt;keybr.com&lt;/a&gt; and are reasonably comfortable with the new layout, there will come a time when you run into challenges. For me, that was in the way to navigate in Vim/Neovim using &lt;kbd&gt;h&lt;/kbd&gt;/&lt;kbd&gt;j&lt;/kbd&gt;/&lt;kbd&gt;k&lt;/kbd&gt;/&lt;kbd&gt;l&lt;/kbd&gt;. Colemak moves the last three of those keys to different positions on the keyboard that are not intuitive at all. One easy solution is to use the arrow keys on your keyboard instead of &lt;kbd&gt;h&lt;/kbd&gt;/&lt;kbd&gt;j&lt;/kbd&gt;/&lt;kbd&gt;k&lt;/kbd&gt;/&lt;kbd&gt;l&lt;/kbd&gt; and forget about it. However, I grew pretty accustomed to the location of those keys, as they are in a great position on the home-row.&lt;/p&gt;
&lt;p&gt;Then I remembered the concept of a layer on a keyboard that allows you to configure keys to act in a different way when you&amp;rsquo;re holding down a particular key. The &lt;kbd&gt;Shift&lt;/kbd&gt; key can be seen as a modifier that activates the &lt;em&gt;Capitals and Punctuation&lt;/em&gt; layer. And of course, there are the &lt;kbd&gt;Ctrl&lt;/kbd&gt;, &lt;kbd&gt;Alt&lt;/kbd&gt; and &lt;kbd&gt;Cmd&lt;/kbd&gt; keys. Those seemed too far from the home row, and I chose to use the &lt;kbd&gt;Escape&lt;/kbd&gt; key as a modifier to emulate arrow-key presses when the &lt;kbd&gt;h&lt;/kbd&gt;/&lt;kbd&gt;j&lt;/kbd&gt;/&lt;kbd&gt;k&lt;/kbd&gt;/&lt;kbd&gt;l&lt;/kbd&gt; keys are pressed.&lt;/p&gt;
&lt;div class=&#34;notice tip&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-lightbulb-o&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Tip
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;For more inspiration, take a look at the &lt;a href=&#34;https://dreymar.colemak.org/layers-extend.html&#34;&gt;Extend layer&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;To remap these keys, you will need to use &lt;a href=&#34;https://karabiner-elements.pqrs.org/&#34;&gt;Karabiner Elements&lt;/a&gt;. With it, you can first do a simple modification to map &lt;kbd&gt;Caps Lock&lt;/kbd&gt; to &lt;kbd&gt;Escape&lt;/kbd&gt; (you didn&amp;rsquo;t think I would be using the regular &lt;kbd&gt;Escape&lt;/kbd&gt; key, did you!?):&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;simple-mod.png&#34; alt=&#34;Caps Lock to Escape remap&#34;&gt;&lt;/p&gt;
&lt;p&gt;Then the tricky part comes where we need to add some &lt;em&gt;complex modifications&lt;/em&gt;. &lt;a href=&#34;https://codeberg.org/michielappelman/dotfiles/src/branch/master/config/karabiner/assets/complex_modifications/escape-to-c-o-c.json&#34;&gt;My config is available in my dotfiles repo&lt;/a&gt; and does a couple of things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Map &lt;kbd&gt;Escape&lt;/kbd&gt; to &lt;kbd&gt;&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Cmd&lt;/kbd&gt;+&lt;kbd&gt;Option&lt;/kbd&gt;&lt;/kbd&gt; (also known as a Hyper-key), but only when held down: when there is a single press, the key will act as a regular &lt;kbd&gt;Escape&lt;/kbd&gt; key.&lt;/li&gt;
&lt;li&gt;Map Hyper + &lt;kbd&gt;h&lt;/kbd&gt;/&lt;kbd&gt;j&lt;/kbd&gt;/&lt;kbd&gt;k&lt;/kbd&gt;/&lt;kbd&gt;l&lt;/kbd&gt; to &lt;kbd&gt;Left&lt;/kbd&gt;/&lt;kbd&gt;Down&lt;/kbd&gt;/&lt;kbd&gt;Up&lt;/kbd&gt;/&lt;kbd&gt;Right&lt;/kbd&gt; keys&lt;/li&gt;
&lt;li&gt;Map Hyper + &lt;kbd&gt;y&lt;/kbd&gt;/&lt;kbd&gt;u&lt;/kbd&gt;/&lt;kbd&gt;i&lt;/kbd&gt;/&lt;kbd&gt;o&lt;/kbd&gt; to &lt;kbd&gt;Home&lt;/kbd&gt;/&lt;kbd&gt;PgDwn&lt;/kbd&gt;/&lt;kbd&gt;PgUp&lt;/kbd&gt;/&lt;kbd&gt;End&lt;/kbd&gt; keys&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just to give you an idea, the first remap looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;manipulators&amp;#34;&lt;/span&gt;: [
  { &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;from&amp;#34;&lt;/span&gt;: {
      &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;key_code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;escape&amp;#34;&lt;/span&gt;,
      &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;modifiers&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;optional&amp;#34;&lt;/span&gt;: [ &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;any&amp;#34;&lt;/span&gt; ]}
    },
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;to&amp;#34;&lt;/span&gt;: [
      {&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;key_code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;left_shift&amp;#34;&lt;/span&gt;,
       &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;modifiers&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;left_control&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;left_command&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;left_option&amp;#34;&lt;/span&gt;]}
    ],
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;to_if_alone&amp;#34;&lt;/span&gt;: [{&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;key_code&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;escape&amp;#34;&lt;/span&gt;}],
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#2aa198&#34;&gt;&amp;#34;basic&amp;#34;&lt;/span&gt;
}]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to use this remap config, download it from my repository and put it in the &lt;code&gt;~/.config/karabiner/assets/complex_modifications/&lt;/code&gt; folder. The three remap configurations listed above can then be chosen in the Karabiner settings:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;complex-mod.png&#34; alt=&#34;Complex modification settings&#34;&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve really come to enjoy this configuration for Vim, but the best thing is that I can now use the same Vim-like keys to navigate everywhere on my system: browsing in Firefox, writing in Word and calculating in Excel. Give it a try, and let me know what you think on &lt;a href=&#34;https://twitter.com/michielappelman&#34;&gt;Twitter&lt;/a&gt;!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Pictures of Japan</title>
      <link>https://appelman.se/japan-pictures/</link>
      <pubDate>Sun, 31 May 2015 16:27:45 +0100</pubDate>
      
      <guid>https://appelman.se/japan-pictures/</guid>
      <description>&lt;div class=&#34;notice info&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-exclamation-circle&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Info
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;This is an older post that I imported from my previous blog.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;So today I&amp;rsquo;ve finally finished sorting, selecting and editing the pictures I took during my vacation in Japan. Me and my girlfriend were there for three weeks during the 2015 Sakura season.&lt;/p&gt;
&lt;p&gt;You can view the full album on &lt;a href=&#34;https://www.flickr.com/photos/michielappelman/sets/72157653789506535&#34;&gt;my Flickr page&lt;/a&gt;, but below you&amp;rsquo;ll find some of my favorites.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;business-district&#34;&gt;
  Business District
  &lt;a class=&#34;heading-link&#34; href=&#34;#business-district&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In Tokyo we stayed in a nice hotel in the middle of a busy business area. One of those neighbourhoods where the evenings during the week are more exciting than the weekends. Every restaurant is full of hard working &lt;a href=&#34;http://en.wikipedia.org/wiki/Salaryman&#34;&gt;salarymen&lt;/a&gt; letting of some steam.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/18316300461&#34; title=&#34;8 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c4.staticflickr.com/8/7756/18316300461_041ebc922b_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;8&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;early-bloom&#34;&gt;
  Early bloom
  &lt;a class=&#34;heading-link&#34; href=&#34;#early-bloom&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Walking around Tokyo we stumbled across some cherry blossom trees that were already in full bloom, but most of them were still very much closed. This particular tree didn&amp;rsquo;t seem to have made up its mind and had some flowers that were already opening. Overall the &lt;em&gt;sakura&lt;/em&gt; season started a bit early but as usual was very unpredictable. Luckily for us we caught the whole show during our stay.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/18314923955&#34; title=&#34;54 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c2.staticflickr.com/8/7757/18314923955_a094cfc1a8_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;54&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chillin&#34;&gt;
  Chillin&#39;
  &lt;a class=&#34;heading-link&#34; href=&#34;#chillin&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Near Hiroshima we visited one of the biggest &lt;em&gt;torii&lt;/em&gt; in Japan just of the Miyajima island. Here the deer have grown accustomed to the steady flow of tourists and they &lt;strong&gt;will&lt;/strong&gt; attack for food. This one seemed pretty chilled out, though. Probably it already stole a big bag of sweets from an unsuspecting &lt;em&gt;gaijin&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/18311029552&#34; title=&#34;71 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c1.staticflickr.com/9/8851/18311029552_176bdc669e_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;71&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;japanese-jazz&#34;&gt;
  Japanese Jazz
  &lt;a class=&#34;heading-link&#34; href=&#34;#japanese-jazz&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;One of the most fun evenings in Japan was hanging out in this small Jazz bar situated along the popular Pontochō alley. These guys were awesome and the lady behind the bar made us feel right at home. Definitely recommended: the Stardust Club.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/17694342803&#34; title=&#34;91 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c2.staticflickr.com/8/7776/17694342803_925e1e7d45_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;91&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;climbing&#34;&gt;
  Climbing
  &lt;a class=&#34;heading-link&#34; href=&#34;#climbing&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;South of Kyoto on the Kii peninsula is an old trail called Daimon-zaka. It leads up on a big old mountain towards a quiet temple (especially compared to the tourist craziness in Kyoto) and is lined with trees that are hundreds of years old. It&amp;rsquo;s crazy to think old pelgrim monks already made the same trip and saw the same trees during the Edo-period. When you reach the top you are greeted by this beautiful view of a pagoda with the &lt;em&gt;Nachi no taki&lt;/em&gt; waterfall behind it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/17694397153&#34; title=&#34;128 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c1.staticflickr.com/1/548/17694397153_d079369458_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;128&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;watch-out-for-bears&#34;&gt;
  Watch out for bears
  &lt;a class=&#34;heading-link&#34; href=&#34;#watch-out-for-bears&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;This is the old &lt;em&gt;Nakasendo&lt;/em&gt; trail with a sign pointing towards one of the two post towns where it runs in between. Well, it used to be longer and was used as a route between Kyoto and Tokyo back in the old days, but nowadays this part is the only piece that is left as it was back then. Along the way you find several bells that can be rung to scare off any bears. We didn&amp;rsquo;t see any, so I guess they worked…&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/18127243628&#34; title=&#34;151 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c4.staticflickr.com/8/7751/18127243628_4a67b88c56_h.jpg&#34;  loading=&#34;lazy&#34; alt=&#34;151&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;grand-re-opening&#34;&gt;
  Grand Re-opening
  &lt;a class=&#34;heading-link&#34; href=&#34;#grand-re-opening&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Apparently we were in luck because Himeji castle — one of the very view castles that&amp;rsquo;s original — had been re-opened just a couple of weeks before we arrived. Unfortunately for us, Himeji castle is one of the most loved castles in Japan and because &lt;em&gt;sakura&lt;/em&gt; was in full bloom when we were in the area, half of Japan was also there… The inside of the castle wasn&amp;rsquo;t very spectacular but I did love the outside!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/18315105795&#34; title=&#34;184 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c1.staticflickr.com/9/8870/18315105795_6c84fab907_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;184&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;birthday-view&#34;&gt;
  Birthday View
  &lt;a class=&#34;heading-link&#34; href=&#34;#birthday-view&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;A couple of days before we left I had my birthday and we booked a beautiful traditional Japanese hotel with a view from the roof on Mt. Fuji. The view was a bit blocked by the clouds, but we were still lucky to see part of it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/michielappelman/17692239304&#34; title=&#34;197 by Michiel Appelman, on Flickr&#34;&gt;&lt;img src=&#34;https://c1.staticflickr.com/1/447/17692239304_0643f5e8f5_h.jpg&#34; loading=&#34;lazy&#34; alt=&#34;197&#34;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>USB to Serial on Yosemite</title>
      <link>https://appelman.se/usb-to-serial-on-yosemite/</link>
      <pubDate>Mon, 16 Mar 2015 11:27:45 +0100</pubDate>
      
      <guid>https://appelman.se/usb-to-serial-on-yosemite/</guid>
      <description>&lt;div class=&#34;notice warning&#34;&gt;
  &lt;div class=&#34;notice-title&#34;&gt;
    &lt;i class=&#34;fa fa-exclamation-triangle&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Warning
  &lt;/div&gt;
  &lt;div class=&#34;notice-content&#34;&gt;This is an older post that I imported from my previous blog. I have not validated whether the procedure below is still relevant for Yosemite, but it most certainly will not be for newer macOS releases!&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;There have been numerous blog posts written on getting USB to Serial adapters working under Mac OS X — and specifically Yosemite. However, none of them gave me the absolute answer on how to get it working perfectly. And when a colleague of mine asked me to help him get his working on his Mac a couple of weeks ago, it seemed all was lost for him as well.&lt;/p&gt;
&lt;p&gt;We followed the instructions on a couple of different web pages without any success and finally we just copied the driver I had running to his Mac and everything was working all of a sudden! Magic.&lt;/p&gt;
&lt;p&gt;The driver I&amp;rsquo;m using took me a while to get right as well when I first started using a Mac some years ago. But since then it hadn&amp;rsquo;t failed me. Until of course Yosemite came along and required drivers to be signed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;com.apple.kextd[19]: ERROR: invalid signature for com.prolific.driver.PL2303, will not load
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Luckily this was easily solved by running the following command and rebooting:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;sudo nvram boot-args=&amp;#34;kext-dev-mode=1&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-correct-driver&#34;&gt;
  The Correct Driver
  &lt;a class=&#34;heading-link&#34; href=&#34;#the-correct-driver&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Basically, there are two drivers available on the Internet for the Prolific USB to Serial chipset. The first is the Open Source version available on &lt;a href=&#34;https://github.com/mpepping/osx-pl2303/&#34;&gt;GitHub&lt;/a&gt; and the second is from a website called &lt;a href=&#34;http://plugable.com/drivers/prolific/&#34;&gt;Plugable&lt;/a&gt;. As far as I can see the results are mixed for any of the adapters, but I&amp;rsquo;m using the latter.&lt;/p&gt;
&lt;p&gt;When I first downloaded and installed this driver, it didn&amp;rsquo;t work until I made some changes in the &lt;code&gt;Info.plist&lt;/code&gt; file included in the driver. To do so, I first unloaded the driver and opened the Info file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;sudo kextunload /System/Library/Extensions/ProlificUsbSerial.kext
sudo vi /System/Library/Extensions/ProlificUsbSerial.kext/Contents/Info.plist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In it you will find the &lt;code&gt;067B_2304&lt;/code&gt; key which includes a certain &lt;code&gt;idProduct&lt;/code&gt; and &lt;code&gt;idVendor&lt;/code&gt;. These settings are the ones I had to change to reflect the following values:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:2;-o-tab-size:2;tab-size:2&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;067B_2304&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                                       
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;                                                                     
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleIdentifier&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                          
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.prolific.driver.PL2303&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;                            
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOClass&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                                     
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com_prolific_driver_PL2303&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;                            
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;IOProviderClass&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                             
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;IOUSBInterface&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;                                        
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;bConfigurationValue&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                         
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;1&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;                                                   
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;bInterfaceNumber&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                            
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;0&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;                                                   
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idProduct&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                                   
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;8296&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;                                                
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;idVendor&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;                                                    
        &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;24969&lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;                                               
    &lt;span style=&#34;color:#268bd2&#34;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And when finished you can reload the kext-file: &lt;code&gt;sudo kextload /System/Library/Extensions/ProlificUsbSerial.kext&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;tested-devices&#34;&gt;
  Tested Devices
  &lt;a class=&#34;heading-link&#34; href=&#34;#tested-devices&#34;&gt;
    &lt;i class=&#34;fa fa-link&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;
  &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The known-working USB to Serial adapters for this driver are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sitecom USB to Serial Cable 0,6m &lt;a href=&#34;http://www.sitecomlearningcentre.com/products/cn-104v4002/usb-to-serial-cable-0-6m&#34;&gt;CN-104&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Trendnet USB to Serial Converter &lt;a href=&#34;http://www.trendnet.com/products/proddetail.asp?prod=150_TU-S9&#34;&gt;TU-S9&lt;/a&gt; (Version v1.xR)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please let me know &lt;a href=&#34;https://twitter.com/michielappelman&#34;&gt;on Twitter&lt;/a&gt; if it works for your device as well, or if you have any other questions.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
