<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>remy sharp's b:log</title>
    <atom:link href="https://remysharp.com/feed.xml" rel="self" type="application/rss+xml"></atom:link>
    <link>https://remysharp.com</link>
    <description>About [code] and all that jazz</description>
    <image>
      <title>remy sharp's b:log</title>
      <link>https://remysharp.com</link>
      <url>https://remysharp.com/images/avatar-125.jpg</url>
      <width>125</width>
      <height>125</height>
    </image>
    <lastBuildDate>Tue, 24 Feb 2026 00:00:00 +0000</lastBuildDate>
    <managingEditor>remy@remysharp.com (Remy Sharp)</managingEditor>
    <webMaster>remy@remysharp.com (Remy Sharp)</webMaster>
    <language>en</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <generator>Honestly, I don't know what most of these tags do, I copied a lot of them from other people's web site ¯\_(ツ)_/¯</generator>
    <item>
      <title>Rivers of London (Rivers of London, #1) [book]</title>
      <guid isPermaLink="false">rivers-of-london</guid>
      <link>https://remysharp.com/books/2026/rivers-of-london</link>
      <pubDate>Tue, 24 Feb 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[Vivid voice, fun and dark and original.
I wasn't sure what I was getting in to reading Rivers of London (recommended multiple times on socials), especially as I kept finding the teens/young adult book cover for it (reminding me of the multiple covers the Harry Potter books had) - but I decided to jump in.
I quickly got sucked into the book and the narrative style that Aaronovitch used. For some reason I had Millie Bobby Brown as the voice which caused me a little confusion when I learn that the protagonist and narrator is called Peter Grant (I really didn't read the blurb) - but decided that she could still run the inner monologue.
The Rivers of London is a very London and British book (which I always especially enjoy because I can vividly see the places, hear the accents and usually feel the weather) that's a mix of modern copper plod story with fantastical (and certainly for me: original) characters from gods to vampires to ghosts and wizards.
There's a good balance of funny and witting against the (some times) gruesome and frankly pretty terribly bad things that make for the primary investigation for the protagonist, Grant, to work through.
Really fun stuff - I can definitely see myself returning for more in this series.]]></description>
      <content:encoded><![CDATA[<p>Vivid voice, fun and dark and original.</p>
<p>I wasn't sure what I was getting in to reading Rivers of London (recommended multiple times on socials), especially as I kept finding the teens/young adult book cover for it (reminding me of the multiple covers the Harry Potter books had) - but I decided to jump in.</p>
<p>I quickly got sucked into the book and the narrative style that Aaronovitch used. For some reason I had Millie Bobby Brown as the voice which caused me a little confusion when I learn that the protagonist and narrator is called Peter Grant (I really didn't read the blurb) - but decided that she could still run the inner monologue.</p>
<p>The Rivers of London is a very London and British book (which I always especially enjoy because I can vividly see the places, hear the accents and usually feel the weather) that's a mix of modern copper plod story with fantastical (and certainly for me: original) characters from gods to vampires to ghosts and wizards.</p>
<p>There's a good balance of funny and witting against the (some times) gruesome and frankly pretty terribly bad things that make for the primary investigation for the protagonist, Grant, to work through.</p>
<p>Really fun stuff - I can definitely see myself returning for more in this series.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2026/rivers-of-london">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>git recent: what branch did I work on? [blog]</title>
      <guid isPermaLink="false">git-recent</guid>
      <link>https://remysharp.com/2026/02/12/git-recent</link>
      <pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[Mega short blog post, mostly for me to remember, but also might be useful to you.
In a project I'll often work on and move around different branches throughout the day, and as the years wear on it's rather dulled my memory - that's to say, I quickly forget what branch I was working on!]]></description>
      <content:encoded><![CDATA[
<p>Mega short blog post, mostly for me to remember, but also might be useful to you.</p>
<p>In a project I'll often work on and move around different branches throughout the day, and as the years wear on it's rather dulled my memory - that's to say, I quickly forget what branch I was working on!</p>
<h2><code>git recent</code></h2>
<p>I created a <code>git</code> command line alias that helps my failing memory. What it does is list all the local branches, oldest to newest with the relative time and the name:</p>
<pre><code>[alias]
  recent = !git for-each-ref --sort=committerdate --format='%(committerdate:relative) %(refname:short)' refs/heads/ | tail -10
</code></pre>
<p>It's a bit cumbersome with the <code>!git</code> part at the start, but it's because I want to limit it the most recent 10 results <em>and</em> I want it with the newest at the bottom.</p>
<pre><code>$ git recent
3 weeks ago fix/ai-gen-docs
2 weeks ago feat/not-available-not-404
2 weeks ago fix/bulk-download-timeout
8 days ago fix/real-null-in-bulk
8 days ago fix/real-404
2 days ago feat/bulk-schema
2 days ago fix/transcript-endpoint-potential-null
2 days ago fix/sanitise-sql-input
21 hours ago main
20 hours ago feat/api-analytics
</code></pre>
<p>Memory win for me. Neato.</p>
<p><em>Originally published on <a href="https://remysharp.com/2026/02/12/git-recent">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Try text scaling support [link]</title>
      <guid isPermaLink="false">2026-02-03-d5be6744</guid>
      <link>https://remysharp.com/links/2026-02-03-d5be6744</link>
      <pubDate>Tue, 03 Feb 2026 11:01:24 +0000</pubDate>
      <description><![CDATA[I've had a larger phone because I got fed up with squinting at my screen, but because of that I also have the OS level font bumped to 115% (and I know Jule, my wife, has it bumped to 125%). This meta tag will use the system font size to adjust the font. Importantly, Josh also points out that the default font size shouldn't be touched: Source: www.joshtumath.uk#heading-tip-1:-don't-override-the-initial-font-size /By Josh Tumath, via Jeremy Keith]]></description>
      <content:encoded><![CDATA[
<p>I've had a larger phone because I got fed up with squinting at my screen, but because of that I also have the OS level font bumped to 115% (and I know Jule, my wife, has it bumped to 125%). This meta tag will use the system font size to adjust the font. Importantly, Josh also points out that the default font size shouldn't be touched: <em>Source: <a href="https://www.joshtumath.uk/posts/2026-01-27-try-text-scaling-support-in-chrome-canary/">www.joshtumath.uk</a></em>#heading-tip-1:-don't-override-the-initial-font-size /By Josh Tumath, via Jeremy Keith</p>
<p><em>Originally published on <a href="https://remysharp.com/links/2026-02-03-d5be6744">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Bear Head (Dogs of War, #2) [book]</title>
      <guid isPermaLink="false">bear-head</guid>
      <link>https://remysharp.com/books/2026/bear-head</link>
      <pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[Really enjoyed this follow up to Dogs of War.
I accidentally read Dogs of War in 2018 - I never went looking for it and loved it. Equally, I can't remember when I found out about Bear Head but I knew I wanted to read it if it existed in the same universe. So finally I get around to it.
Overall well written, well drawn characters with distinctive voices in a universe that's really not that hard to conceive. I've read a few of Tchaikovsky's books now and as much as I enjoy them, for some reason it does take me quite a stretch to read the books (this one being just ~315 pages and took me a month) - not sure what's up with that.
The story is told from three protagonist's perspective: Honey, the bear from Dogs of War - once soldier, now an academic arguing for rights for others. Jimmy, the &quot;grunt worker&quot; placed on Mars to prepare it for the colonies that plan to settle and Carole (Springer…I want to say), the PA and &quot;collared&quot; assistant to the antagonist Warner S. Thompson.
Similarly with Dogs of War, Bear Head discusses self and identity (can a person be entirely digital?), freedom and slavery, and the untouchable super rich and their power.
There's some pretty horrible scenes around human slavery where people are compelled to perform, both involuntary physically but also compelled to think in certain ways (i.e. being submissive by design).
With a lot of what I'm reading lately, I can't help but associate it with today's real life events (in 2025-2026), and thinking of the corruption throughout politics we're seeing today - though I can see how this book being published in 2021 aligns fairly well with Trump's first regime from '17-21. Perhaps it's that backdrop that makes me a slow reader!]]></description>
      <content:encoded><![CDATA[<p>Really enjoyed this follow up to Dogs of War.</p>
<p>I <em>accidentally</em> read <a href="https://remysharp.com/books/2018/dogs...">Dogs of War</a> in 2018 - I never went looking for it and loved it. Equally, I can't remember when I found out about Bear Head but I knew I wanted to read it if it existed in the same universe. So finally I get around to it.</p>
<p>Overall well written, well drawn characters with distinctive voices in a universe that's really not that hard to conceive. I've read a few of Tchaikovsky's books now and as much as I enjoy them, for some reason it does take me quite a stretch to read the books (this one being just ~315 pages and took me a month) - not sure what's up with that.</p>
<p>The story is told from three protagonist's perspective: Honey, the bear from Dogs of War - once soldier, now an academic arguing for rights for others. Jimmy, the &quot;grunt worker&quot; placed on Mars to prepare it for the colonies that plan to settle and Carole (Springer…I want to say), the PA and &quot;collared&quot; assistant to the antagonist Warner S. Thompson.</p>
<p>Similarly with Dogs of War, Bear Head discusses self and identity (can a person be entirely digital?), freedom and slavery, and the untouchable super rich and their power.</p>
<p>There's some pretty horrible scenes around human slavery where people are compelled to perform, both involuntary physically but also compelled to <em>think</em> in certain ways (i.e. being submissive by design).</p>
<p>With a lot of what I'm reading lately, I can't help but associate it with today's real life events (in 2025-2026), and thinking of the corruption throughout politics we're seeing today - though I can see how this book being published in 2021 aligns fairly well with Trump's first regime from '17-21. Perhaps it's that backdrop that makes me a slow reader!</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2026/bear-head">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>JS Bin down in 2026 [blog]</title>
      <guid isPermaLink="false">js-bin-down-in-2026</guid>
      <link>https://remysharp.com/2026/02/02/js-bin-down-in-2026</link>
      <pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[January 27th I got an email notification saying that JS Bin had become unavailable. Then next day real life human beings were asking what's going on. By 11pm on the 30th the last of the issues were resolved.
Earlier today Jake asked me: what went wrong?
Fucking, everything.]]></description>
      <content:encoded><![CDATA[
<p>January 27th I got an email notification saying that JS Bin had become unavailable. Then next day real life human beings were asking what's going on. By <a href="https://github.com/jsbin/jsbin/issues/3583#issuecomment-3826171066">11pm on the 30th</a> the last of the issues were resolved.</p>
<p>Earlier today <a href="https://jakearchibald.com/">Jake</a> asked me: <em>what went wrong?</em></p>
<p><strong>Fucking, everything.</strong></p>
<h2>TL;DR</h2>
<p>I get it, this is a big wordy blog post. I got carried away and enjoyed telling the story.</p>
<p>The short version is: CloudFlare, probably best to upgrade everything to latest software (which I couldn't do), 520 status can actually be a mismatched TLS exchange between CloudFlare and the origin. Oh, and don't lean on LLMs too hard when the shit is really hitting the fan - try to take a big step back and make sure you take stock.</p>
<p>Otherwise, here's the too-long, do-read version -</p>
<h2>On being in maintenance mode</h2>
<p>For the last 5 ish/maybe quite a few years or more, JS Bin has run in a fairly automated maintenance mode. There's usually little flurries of wobbles that need my attention every 3/6 months. JS Bin is coming on to 18 years old, which is geriatric by web standards.</p>
<p>Typically it's <a href="https://remysharp.com/2015/09/15/jsbin-toxic-part-2">dodgy content</a> on the site that I need to put banning in place, sometimes it's take down requests that come through from Amazon (where JS Bin is hosted), sometimes it's a memory exception that takes me a little longer to recover from.</p>
<p>You can see from the last 11 years of status checks, there's been outages but nothing quite like what happened this time around (ignoring that big one on the far left…):</p>
<figure><img src="https://remysharp.com/images/jsbin-status-chart.avif" alt="A chart starting from 2014 with spots throughout showing when JS Bin was down" decoding="async"><figcaption>In 2017 the entire server fell over and need to be replaced entirely. Then this were quiet-ish, then…we have the red wall</figcaption></figure>
<p>The larger hours when the server goes down (as I found out recently) is memory running out on the machine, and the machine will respond by pretty much collapsing. The result of which is that I can't connect to the machine via ssh to attempt restore it. It usually requires a forced reboot via the AWS console.</p>
<p>Then there's the odd occasion that even reboots from the AWS console, a literal &quot;turn it on and off again&quot;, doesn't fly…</p>
<h2>It wouldn't come back from a reboot</h2>
<p>This latest outage just wouldn't come back from a reboot. I would trigger the reboot and then wait on the console attempting to connect over SSH so I could get some eyes on the situation but even then I couldn't get it.</p>
<p>JS Bin was rebooting and immediately locking up. So this tells me there's some external pressure on the machine that is not easing up.</p>
<p>The only option I have to hand is to shutdown the machine entirely for an hour or so, to let whatever is clawing at the door to go away for a bit.</p>
<p>When I eventually looked at the Cloud Watch logs, it was clear there was something absolutely smashing at the walls (and door, yes, I mix my metaphors). The amount of inbound network traffic is unprecedented for jsbin. From the chart below, it shows normal usage in the days prior, a trickle of network inbound traffic by comparison to what I was now seeing.</p>
<p>The dips that happen after the cliff edge of monitoring is the machine giving up and being unresponsive. From the chart, I can see that even the short outage wasn't enough to get this beast off jsbin's back.</p>
<figure><img src="https://remysharp.com/images/cloud-watch-chart.avif" alt="The Cloud Watch network traffic logs showing a huge increase of network inbound traffic, going from a typical 1mb to 100mb" decoding="async"><figcaption>The 100mb peak <em>incoming</em> traffic was the real problem.</figcaption></figure>
<h2>Killing the appropriate process</h2>
<p>When I finally managed to get into the machine, the first stop was <code>syslog</code> to find out what was responsible for the crash (or rather the symptom, not the cause, not yet).</p>
<p>I quickly found the garbage collection dump and stacktrace of node running out of memory. First thing's first: let's kill the offending process rather than having a full shit-the-bed approach.</p>
<pre><code># /etc/sysctl.conf and reload with `sudo sysctl -p`
vm.oom_kill_allocating_task=1
</code></pre>
<p>That would mean node (which was saturating all the memory) would be killed rather than anything the system could get it's hands on - which usually meant I couldn't ssh into the machine.</p>
<p>This change wouldn't stop things getting sluggish on the machine but it would mean that I could continue to diagnose even whilst the traffic was bombarding the server (albeit with a very slow terminal responding).</p>
<p>I could see the CPU usage was very, very high, and I could see node steadily increasing it's memory footprint (I use <code>htop</code>).</p>
<p>CPU usage of 100% is…okay because it means I'm using all the available process, but it leaves zero headroom and rather conspicuous that it wasn't dipping at any point.</p>
<p>Then chatgpt suggested upgrading node, which was weird because I'd never told Chatgpt what version of node I was running…</p>
<h2>Side quest: node is <em>really</em> old</h2>
<p>As with running in maintenance and things being in a status quo, I hadn't really touched node…at all.</p>
<p>As it happens, jsbin had been running on node 7 (not even &quot;stable&quot; node 8) for, I imagine, well over a decade.</p>
<p>To me, out of the blue, Chatgpt suggested I could upgrade node. This is a decent suggestion but at no point did I tell Chatgpt what version of node I was running.</p>
<p>On me querying, Chatgpt told me that <em>I had told the AI</em> which was a straight up lie.</p>
<p>After more prodding than I'd like, eventually it turned out that the version of node was in my terminal and at some point during the nginx tuning (in the next section), Chatgpt had helped itself to what was on the screen.</p>
<p>I've no idea if it has access to the scroll back history - I've got to assume it doesn't, but still pretty uncool.</p>
<p>I don't tend to use the &quot;application&quot; thing that Chatgpt has (I normally use the browser interface) but this definitely taught me a lesson: if there's sensitive data <em>anywhere</em> on the screen (ie. I had just <code>cat</code> an <code>.env</code> file) then it's entirely likely an LLM can see it too.</p>
<p>Anyway, I bumped from node 7 to node 22 and by some kind miracle it actually worked without any incident. It turned out that back in 2024 I had done some fairly significant work on the codebase so that I could run it on my local machine (which understandably didn't want to run node 7), so I had modernised the requisite parts.</p>
<p>Phew. At least the event loop is improved and going to be kinder on my CPU.</p>
<p>Except, it's still fucked and the adventure is far from over. Next I considered whether nginx (the proxy layer) could do with some optimisation.</p>
<h2>Fine tuning on zip-all resources</h2>
<p>Until now I've not shared what jsbin's main server runs on. It's a t2.micro AWS instance. Single CPU and 1GB of RAM. I'm often surprised it's managed so long on what I consider so little resources.</p>
<p>And yes, standing a beefier machine up (aka: throwing money at the problem) might help, but the time to reconfigure a new machine wasn't quick - I don't have an instant &quot;build a new jsbin&quot; script (remember, this server has been in maintenance for a long time and just happily running). It's also worth adding that although jsbin does has a &quot;pro&quot; offering, there's very little actual monetary resources. That's all to say: what can I do right now before creating a new machine (spoiler: I did make a new machine, and double-spoiler: it was terminated the following day as I didn't need it).</p>
<p>With the help of Chatgpt, Gemini <em>and</em> Claude (because somehow I have access to all of them, and I really don't have the full skills to know the ins and outs of nginx config) I looked at what could be tuned. I used multiple LLMs so that I could attempt to cross verify the advice (though I wasn't as diligent as I'd have liked).</p>
<p>The adjustments fell into these categories:</p>
<ul>
<li>workers spawned</li>
<li>proxy timeout</li>
<li>increase file descriptors</li>
<li>keep alive timings</li>
<li>remove http2 - to help with memory</li>
</ul>
<p>Here's some of the actual config:</p>
<pre><code class="language-yaml">worker_connections 1024;
worker_processes auto;

keepalive_timeout 10;
keepalive_requests 100;
</code></pre>
<p>With the kicking I was getting from the inbound network traffic, this made little tangible difference. I'm sure it helps in the long run, but not when the machine is struggling with over 1000 requests a second and many more trying to squeeze in.</p>
<p>And then there was this:</p>
<blockquote>
<p>Remy, have you considered CloudFlare?</p>
</blockquote>
<p>I'd thought about add CloudFlare a few times in the past, but I was worried that the configuration adjustment and changes I'd need would be either complicated or would cause some other issue.</p>
<p>Except, nothing was working, so now was the time to get CloudFlare involved.</p>
<p>And so begin new problems…</p>
<h2>Adding CloudFlare</h2>
<p>Props where due, putting CloudFlare in front of JS Bin was relatively easy. CloudFlare detected <em>most</em> of the domains and where they pointed (the important ones at least) and it was a matter of swapping over the name servers from pointing to AWS's Route 53 to CloudFlare's own name servers.</p>
<p>Close to midnight on 29-Jan, I started to see <a href="http://jsbin.com">jsbin.com</a> load in the browser.</p>
<p>I thought there might be a few snags around, but quickly I got replies on github and via email that people were still (mostly) seeing errors.</p>
<p>In particular <a href="https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/error-520/">CloudFlare's 520</a> error page, which, as it turns out can be any number of issues.</p>
<h2>Requests cutting through CloudFlare</h2>
<p>Although I did have some success in loading, I could also see traffic still hammering through on the server.</p>
<p>With some help from our LLM overlords (i.e. I would have google'd it…&quot;<a href="https://kagi.com/">kagied</a> it&quot;?), I got the <a href="https://www.cloudflare.com/en-gb/ips/">list of IP ranges</a> that meant the traffic was from CloudFlare.</p>
<p>There was still a lot of traffic that didn't match those IP ranges. So the next step was to drop non-CloudFlare traffic.</p>
<p>This is where using the LLM cost me time via newly introduced problems (which wouldn't surface until the next day). I think I was too buried in the thick of things otherwise I might have caught it earlier (and not at the end of the process).</p>
<p>The first method to blocking non-CloudFlare was to use an nginx rule and variable, and to drop all traffic that wasn't tagged.</p>
<p>This would mean adding this line to each <code>server {}</code> block - this would say <em>&quot;If the request doesn't have the CloudFlare header, then don't let it through&quot;</em>:</p>
<pre><code>if ($http_cf_ray = &quot;&quot;) {
    return 444;
}
</code></pre>
<p>Importantly there was more config directions that I followed (which I've tucked away, but you can read if you want), specifically <code>set_real_ip_from 173.245.48.0/20;</code> - this would trip me up later on.</p>
<details class="aside"><summary>The http_cf_ray config</summary>
<p>Via Captain GPT:</p>
<h4>The correct nginx-level approach (safe + reversible)</h4>
<h5>1. Tell nginx which IPs belong to Cloudflare</h5>
<p>Create a file:</p>
<pre><code>/etc/nginx/cloudflare.conf
</code></pre>
<p>Put exactly this in it (current CF IPv4 ranges):</p>
<pre><code>set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

real_ip_header CF-Connecting-IP;
real_ip_recursive on;
</code></pre>
<p>Include it near the top of <code>http {}</code>:</p>
<pre><code>http {
    include /etc/nginx/cloudflare.conf;
    ...
}
</code></pre>
<p>This ensures $remote_addr becomes the real client IP, not Cloudflare.</p>
<h5>2. Block non-Cloudflare traffic (this is the key)</h5>
<p>In each public <code>server {}</code> block (ports 80 and 443), add this at the very top:</p>
<pre><code>if ($http_cf_ray = &quot;&quot;) {
    return 444;
}
</code></pre>
<p>What this does:</p>
<ul>
<li>Cloudflare always sends CF-Ray</li>
<li>Direct clients never do</li>
<li>444 silently drops the connection (no response, no load)</li>
</ul>
<p>This is extremely effective.</p>
</details>
<p>This config was all well and good, but traffic was still coming in. This change would mean that nginx would have to process the traffic than ignoring (or &quot;dropping&quot;) it entirely (<em>#foreshadowing</em>).</p>
<p>When I ran command below, it would list the connections, and I could <em>still</em> see non-CloudFlare requests being able to complete request (at this specific point in time, I was in a frantic cycle of &quot;try a change - fail - worry a bit - ask LLM - repeat&quot;):</p>
<pre><code>ss -tan state established '( sport = :443 )'
</code></pre>
<p>The next change that I put in place was to drop the request if the IP didn't match the ones in the CloudFlare IP range (whereas before the change was looking for a CloudFlare specific header). Looking back as I write this, I'm not sure why the header method didn't work, but I was sure to add more petrol onto the fire:</p>
<pre><code>geo $is_cloudflare {
    default 0;

    173.245.48.0/20    1;
    103.21.244.0/22    1;
    # etc
}
</code></pre>
<p>This flags if the remote address is a CloudFlare IP, set the <code>$is_cloudflare=1</code>. Then in my server blocks:</p>
<pre><code>server {
    listen 443 ssl http2 default_server;
    if ($is_cloudflare = 0) { return 444; }
    if ($cf_valid = 0) { return 444; }

    # rest of config unchanged
}
</code></pre>
<p>What I didn't realise at this point, and took another 24 hours to figure out, is that the first CloudFlare nginx based change would say <em>&quot;if the IP is CloudFlare, then set the $remote_addr to the original request&quot;</em>. Then this code says <em>&quot;if the IP is NOT CloudFlare don't let them through&quot;</em>.</p>
<p>It's a mess. Now I've got reports from people on github telling me they're still seeing <em>mostly</em> 520 errors, and finally I realise I can replicate by using a VPN because for some reason it was still working for me.</p>
<p>There's two problems at this point:</p>
<ol>
<li>Traffic still coming in that isn't through CloudFlare that's causing a drain on resources</li>
<li>Real users coming through CloudFlare are, mostly, not getting the site</li>
</ol>
<p>Pretty much the worst of both worlds.</p>
<p>Finally, firewall rules spring to mind. Better late than never.</p>
<h2>Dropping traffic</h2>
<p>This was done on two fronts - just for <em>belt-and-braces</em> approach:</p>
<ol>
<li><code>ufw</code> (or as I knew it &quot;iptables&quot;) rules one the server to <code>DROP</code> or <code>ALLOW</code> traffic based on IP range</li>
<li>AWS security policy to do the same.</li>
</ol>
<p>Running the rules for <code>ufw</code> was relatively simple. A matter of allowing all the known IP ranges, then denying everything else:</p>
<pre><code class="language-shell">$ ufw allow from <span class="token number">173.245</span>.48.0/20 to any port <span class="token number">443</span>
$ ufw allow from <span class="token number">103.21</span>.244.0/22 to any port <span class="token number">443</span>
$ <span class="token comment"># etc</span>
$ ufw deny <span class="token number">443</span>
</code></pre>
<p>Then repeated for port 80. I did get tripped up during testing when I wanted to allow my IP through, but that was solved with <code>ufw status numbered</code> and then <code>ufw delete N</code>.</p>
<p>Adding all the IP ranges on AWS was not so simple. The web UI just doesn't allow for large changes - it's very, very clunky.</p>
<p>Thankfully I could automate some of it from the <a href="https://aws.amazon.com/cli/">command line</a> and script the work. The annoying thing was that the AWS command line doesn't let me bulk modify (or at least I didn't find it) and each command takes a good few seconds to run <em>and</em> requires me to press enter after the response comes back saying it had worked.</p>
<p>This was the command:</p>
<pre><code class="language-sh"><span class="token keyword">for</span> <span class="token for-or-select variable">CIDR</span> <span class="token keyword">in</span> <span class="token punctuation">\</span>
   <span class="token number">103.21</span>.244.0/22 <span class="token punctuation">\</span>
   <span class="token number">103.22</span>.200.0/22 <span class="token punctuation">\</span>
   <span class="token number">103.31</span>.4.0/22 <span class="token punctuation">\</span>
   <span class="token comment"># etc</span>
<span class="token keyword">do</span>
   aws ec2 authorize-security-group-ingress --group-id <span class="token variable">$SG_ID</span> <span class="token parameter variable">--protocol</span> tcp <span class="token parameter variable">--port</span> <span class="token number">80</span>  <span class="token parameter variable">--cidr</span> <span class="token variable">$CIDR</span> <span class="token parameter variable">--region</span> us-east-1
   aws ec2 authorize-security-group-ingress --group-id <span class="token variable">$SG_ID</span> <span class="token parameter variable">--protocol</span> tcp <span class="token parameter variable">--port</span> <span class="token number">443</span> <span class="token parameter variable">--cidr</span> <span class="token variable">$CIDR</span> <span class="token parameter variable">--region</span> us-east-1
<span class="token keyword">done</span>
</code></pre>
<p>With those in place, finally the server was breathing again. However, those pesky CloudFlare 520s were still preventing people from visiting <a href="http://jsbin.com">jsbin.com</a> (except me…somehow…).</p>
<h2>CloudFlare's 520</h2>
<p>I naively thought 520 was like a 503 (the entire server is failing to respond to CloudFlare) or a 504 (gateway timeout - usually when node doesn't come back to nginx or CloudFlare), but it's not. It's more like <em>&quot;CloudFlare made a request and the response is incompatible&quot;</em>.</p>
<p>The only real clue I had was that port 80, plain http wasn't affected. A <a href="https://github.com/jsbin/jsbin/issues/3583#issuecomment-3823361117">helpful comment from @robobuljan</a> showed that it was only the https version causing issues:</p>
<pre><code class="language-sh">$ <span class="token function">curl</span> jsbin.com             <span class="token comment"># (works!)</span>
$ <span class="token function">curl</span> http://jsbin.com      <span class="token comment"># (works!)</span>
$ <span class="token function">curl</span> https://jsbin.com     <span class="token comment"># "error code: 520"</span>
</code></pre>
<p>Although this digging took most of the day, it was the part that the LLMs really couldn't help with (and I had thankfully set them aside whilst I chewed on the meat of this problem).</p>
<p>As I looked for any clues. In the CloudFlare SSL/TLS page a section called &quot;Traffic Served Over TLS&quot; showed that there was a split in the supported TLS versions (I didn't capture a screenshot, but these numbers are from their API):</p>
<ul>
<li>TLSv1: 36 requests</li>
<li>TLSv1.1: 56 requests</li>
<li>TLSv1.2: 1,922,523 requests</li>
<li><strong>TLSv1.3: 5,216,795 requests</strong></li>
</ul>
<p>That's a lot on TLSv1.3, but I wondered how old (or actually new) v1.3 was given how old my rickety machine was. So I went back to my nginx config and found this line, repeated against each server block that ran SSL:</p>
<pre><code>ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
</code></pre>
<p>A conspicuous lack of TLSv1.3, and I wondered what would happen if the user asked for 1.3, CloudFlare tottered off wanting 1.3 and then got back 1.2… or perhaps nothing at all.</p>
<p>I first tried to add <code>TLSv1.3</code> to the list only for the <code>nginx -t</code> test to fail (the module wasn't installed and I couldn't get it without doing major upgrade work). So let's see if it can be turned off in CloudFlare.</p>
<p>The short answer is yes, but I struggled to find it.</p>
<p>The first place to confirm what's being support is the section under &quot;Speed, Settings&quot;. But to completely turn it off, it's located in &quot;SSL/TLS&quot; then &quot;Edge Certificates&quot; and towards the end of the page.</p>
<p>Disabling this unlocked a lot more genuine traffic and we were cooking again. Almost, nearly. Again, for some, now the static assets weren't loading nor was the frame that actually live runs the authored code (the <code>null.jsbin.com</code> domain).</p>
<h2>JS Bin was dropping some users</h2>
<p>It took me a few more hours to solve this last step, and I'm still not entirely sure how it all hung together. But if you recall, earlier in my nginx config, I had told nginx that if the IP of the request was from CloudFlare that we should use <code>set_real_ip_from</code>.</p>
<p>This specific command was writing the original IP of the requesting user into the <code>$remote_addr</code>, which is what nginx was now using to drop requests with the <code>return 444</code>. Somehow this wasn't in the main server block (the part that returns the index page), but was somehow in the <code>static.jsbin.com</code> and <code>null.jsbin.com</code>.</p>
<p>This kind of confusion is the result of working late hours, and working in crisis mode. I should have known better and I'm great at dolling out advice but sometimes don't tend to heed it myself.</p>
<p>After I finally removed the janky checks, IP swapping and quite a bit of the cruft that I had introduced with my pal ChatGPT, this let the last of the traffic come through correctly.</p>
<p>JS Bin was back. Entirely.</p>
<h2>The aftermath</h2>
<p>Now that CloudFlare is in front of the server, life is surprisingly chill on this 1GB single CPU machine. Way more calm than a normal day of traffic. Look at that CPU usage!</p>
<figure><img src="https://remysharp.com/images/jsbin-htop-2026.avif" alt="A screenshot of the &quot;htop&quot; program showing that the CPU is around 5% and memory usage is about 30% consumed" decoding="async"><figcaption>Just chilling out at an easy 4.6% CPU usage</figcaption></figure>
<p>I suspect if I hadn't leant on the LLMs so much during the scream-face-the-server-is-down moments, I might have caught the complexity that I was adding. Though equally I should have put CloudFlare in front of JS Bin years ago - and not at a time of crisis.</p>
<p>I definitely learnt a few gotchas, the TLS and 520 status codes are that for me.</p>
<p>The traffic has eased out from the AWS CloudWatch logs and I believe CloudFlare is now bouncing a lot of that away from me - and it seems like a lot of that is coming from Hong Kong (which I've set to have a JavaScript based check to get through CloudFlare):</p>
<figure><img src="https://remysharp.com/images/jsbin-hong-kong.avif" alt="" decoding="async"><figcaption>Hong Kong is so small in this map, you can't even see the deep blue of the chart</figcaption></figure>
<p>Since I took that screenshot, Hong Kong has 10 million requests in the last 24 hours.</p>
<p>Even though I don't think I'll ever really know what caused the amount of traffic that caused everything to fall over, my gut suspects scrapers for AI and LLMs just slurping up as much of the web it can. The only evidence I have <em>against</em> that theory is that the traffic didn't come from a single IP.</p>
<p>Whereas, weirdly, I saw a <a href="https://bsky.app/profile/remysharp.com/post/3mb55uwp3vk2j">single IP bot scraping this blog</a>, accounting for over 3GB of data and over 325,000 requests in a matter of hours. Thankfully this was running on Netlify and is entirely static, and not running on node 7 🤦.</p>
<p><em>Originally published on <a href="https://remysharp.com/2026/02/02/js-bin-down-in-2026">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Bytes I can delete after all this time [blog]</title>
      <guid isPermaLink="false">bytes-i-can-delete-after-all-this-time</guid>
      <link>https://remysharp.com/2026/01/13/bytes-i-can-delete-after-all-this-time</link>
      <pubDate>Tue, 13 Jan 2026 10:00:00 +0000</pubDate>
      <description><![CDATA[For the last few years my work-work has mostly focused on back end software (particularly around APIs). This meant that any front end work I was doing was for myself.
Being an long-in-the-tooth old dog, I tend to learn and trick, and roll it out again and again typically without taking the time to find whether I still need the trick. Case and point, I learnt about the JavaScript performance trick of ~~1.4 === 1 to floor a value (and the same float | 0) but really these days it's not &quot;faster&quot; than doing it the legible way (i.e. Math.floor(1.4)).
Given I've had a bit of time away from the backend, here's an unorganised list of things I've found I can use, and thusly remove extra code that I no longer need.]]></description>
      <content:encoded><![CDATA[
<p>For the last few years my work-work has mostly focused on back end software (particularly around APIs). This meant that any front end work I was doing was for myself.</p>
<p>Being an long-in-the-tooth old dog, I tend to learn and trick, and roll it out again and again typically without taking the time to find whether I still need the trick. Case and point, I learnt about the JavaScript performance trick of <code>~~1.4 === 1</code> to floor a value (and the same <code>float | 0</code>) but really these days it's not &quot;faster&quot; than doing it the legible way (i.e. <code>Math.floor(1.4)</code>).</p>
<p>Given I've had a bit of time away from the backend, here's an unorganised list of things I've found I can use, and thusly remove extra code that I no longer need.</p>
<h2>The list</h2>
<ol>
<li>CSS: <code>text-underline-offset</code> - the distance I can set the <span style="text-decoration: underline; font-family: 'Ubuntu Mono'; text-underline-offset: 12px">text-underline-offset: 12px</span></li>
<li>CSS: <code>gap</code> - no more faffing with margins in flexbox.</li>
<li>CSS: nested media queries on selectors (and nesting general):</li>
</ol>
<pre><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
  <span class="token property">font-size</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
  <span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<ol start="4">
<li>CSS: <code>clamp(min, variable, max)</code> - I used this extensively on FFConf 2025's <a href="https://github.com/leftlogic/ffconf2025/blob/fdb687636450058afbfb67c846a32667bdbe6c8e/style.css#L62">css</a>, the process of finding the right values is very much</li>
<li>CSS: <code>content: open-quote</code> can localised quotes <em>and</em> the <code>q</code> tag does this by default, <a href="https://www.stefanjudis.com/today-i-learned/how-to-use-language-dependent-quotes-in-css/">via this neat insight from Stefan Judis</a></li>
<li>JS: <code>catch</code> without catching the variable, let's me get past the <code>'error' is defined but never used</code>:</li>
</ol>
<pre><code class="language-js"><span class="token keyword">try</span> <span class="token punctuation">{</span>
  <span class="token function">doTheDodgyStuff</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">{</span>
  <span class="token comment">// nothing, it's fine</span>
<span class="token punctuation">}</span>
</code></pre>
<ol start="7">
<li>JS: pointer events have improved (though still from experience they're not 100% perfect) but are to replace the old double click &amp; touch handlers nonsense as seen in <a href="https://www.w3.org/TR/pointerevents/#example_1">the W3C spec</a></li>
<li>AVIF images are fully supported - I benefited from a train ride with <a href="https://jakearchibald.com/">Jake Archibald</a> and in chatting I discovered AVIF are very well supported. This means <a href="https://github.com/remy/remysharp.com/commit/19a32efcb719d1b125017577e48330606ef20ef7#diff-43c10c1d3f992c362c956760385e6ad7397a2345f90c0709e2d4da765ab2d255">easily getting 50% file size savings on JPEGs</a>. I'm regularly running the <a href="https://github.com/AOMediaCodec/libavif/">avidenc</a> command in directories:</li>
</ol>
<pre><code class="language-sh"><span class="token function">ls</span> *.jpg <span class="token operator">|</span> <span class="token function">xargs</span> <span class="token parameter variable">-P</span> <span class="token number">8</span> <span class="token parameter variable">-I</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> avifenc <span class="token parameter variable">-q</span> <span class="token number">50</span> <span class="token string">"{}"</span> <span class="token string">"{}"</span>.avif
</code></pre>
<p>So that's my little list.</p>
<p>Not even 10 things. I guess I've not learnt much yet, but even though I return to the server side of APIs in 2026, I'm sure I'll be kicking the tyres in the front again soon enough and adding to my measly list.</p>
<p><em>Originally published on <a href="https://remysharp.com/2026/01/13/bytes-i-can-delete-after-all-this-time">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>A Very Merry Murder (Malvern Farm Mystery, #6) [book]</title>
      <guid isPermaLink="false">a-very-merry-murder-malvern-farm-mystery-6</guid>
      <link>https://remysharp.com/books/2026/a-very-merry-murder-malvern-farm-mystery-6</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[Fine. Easy read. Guessed the murdered though 🤷
Grabbed it for the Christmas period (slightly annoyed I finished it on Jan 2nd). It's decently written, apparently part of a series (this being book 6) but stands fine on it's own. There's a few references to something that happened before, but doesn't affect the story.
It does read like a Columbo or Murder She Wrote, where there's people killing other people like it's just another Wednesday. All ends are fully tied up without anything left to the reader by the end.
I did figure out the killer around 1/3rd of the way in - or at least I keep asking the protagonist &quot;What about X?!&quot;.
Not terribly Christmasy, but the very last chapter was very sweet.]]></description>
      <content:encoded><![CDATA[<p>Fine. Easy read. Guessed the murdered though 🤷</p>
<p>Grabbed it for the Christmas period (slightly annoyed I finished it on Jan 2nd). It's decently written, apparently part of a series (this being book 6) but stands fine on it's own. There's a few references to something that happened before, but doesn't affect the story.</p>
<p>It does read like a Columbo or Murder She Wrote, where there's people killing other people like it's just another Wednesday. All ends are fully tied up without anything left to the reader by the end.</p>
<p>I did figure out the killer around 1/3rd of the way in - or at least I keep asking the protagonist &quot;What about X?!&quot;.</p>
<p>Not terribly Christmasy, but the very last chapter was very sweet.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2026/a-very-merry-murder-malvern-farm-mystery-6">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Books I read in 2025 [blog]</title>
      <guid isPermaLink="false">books-i-read-in-2025</guid>
      <link>https://remysharp.com/2026/01/01/books-i-read-in-2025</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate>
      <description><![CDATA[This post is mostly data driven (from my own web site's data) to give me a sense of the quality of the books I've read, otherwise individual reviews are all linked in this post or available on my books page.
Longest book: Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI - 496 pages
Shortest book: The Time Machine - 107 pages
Quickest read: 3 days - The Radleys by Matt Haig (341 pages)
Longest read: 2 months, 2 days - Butter by Asako Yuzuki (464 pages)
Diversity of authors:

Women: 9
Men: 5

Rated books
5 stars

Minority Rule: Adventures in the Culture War - 319 pages

4 stars

Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI - 496 pages
In Bloom (Sweetpea, #2) - 432 pages
Sweetpea (Sweetpea, #1) - 480 pages
The Radleys - 341 pages
The Man Who Died Twice (Thursday Murder Club, #2) - 422 pages
The Echo Wife - 347 pages

Books by decade
1890

1895: The Time Machine by H.G. Wells

1960

1969: The Left Hand of Darkness by Ursula K. Le Guin

2010

2010: The Radleys by Matt Haig
2013: Spike Milligan: Man of Letters by Spike Milligan
2017: Butter by Asako Yuzuki
2017: Sweetpea (Sweetpea, #1) by C.J. Skuse
2018: In Bloom (Sweetpea, #2) by C.J. Skuse
2019: Reasons to Be Cheerful by Nina Stibbe

2020

2021: The Man Who Died Twice (Thursday Murder Club, #2) by Richard Osman
2021: The Echo Wife by Sarah Gailey
2022: The Satsuma Complex (Gary Thorn, #1) by Bob Mortimer
2025: Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI by Karen Hao
2025: Minority Rule: Adventures in the Culture War by Ash Sarkar
2025: Making a Killing (DI Fawley #7) by Cara Hunter]]></description>
      <content:encoded><![CDATA[
<p>This post is mostly data driven (from my own web site's data) to give me a sense of the quality of the books I've read, otherwise individual reviews are all linked in this post or available on my books page.</p>
<p><strong>Longest book</strong>: <a href="https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai">Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI</a> - 496 pages</p>
<p><strong>Shortest book</strong>: <a href="https://remysharp.com/books/2025/the-time-machine">The Time Machine</a> - 107 pages</p>
<p><strong>Quickest read</strong>: 3 days - <a href="https://remysharp.com/books/2025/the-radleys">The Radleys</a> <em>by Matt Haig</em> (341 pages)</p>
<p><strong>Longest read</strong>: 2 months, 2 days - <a href="https://remysharp.com/books/2025/butter">Butter</a> <em>by Asako Yuzuki</em> (464 pages)</p>
<p><strong>Diversity of authors</strong>:</p>
<ul>
<li>Women: 9</li>
<li>Men: 5</li>
</ul>
<h2>Rated books</h2>
<h3>5 stars</h3>
<ul>
<li><a href="https://remysharp.com/books/2025/minority-rule-adventures-in-the-culture-war">Minority Rule: Adventures in the Culture War</a> - 319 pages</li>
</ul>
<h3>4 stars</h3>
<ul>
<li><a href="https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai">Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI</a> - 496 pages</li>
<li><a href="https://remysharp.com/books/2025/in-bloom-sweetpea-2">In Bloom (Sweetpea, #2)</a> - 432 pages</li>
<li><a href="https://remysharp.com/books/2025/sweetpea-sweetpea-1">Sweetpea (Sweetpea, #1)</a> - 480 pages</li>
<li><a href="https://remysharp.com/books/2025/the-radleys">The Radleys</a> - 341 pages</li>
<li><a href="https://remysharp.com/books/2025/the-man-who-died-twice-thursday-murder-club-2">The Man Who Died Twice (Thursday Murder Club, #2)</a> - 422 pages</li>
<li><a href="https://remysharp.com/books/2025/the-echo-wife">The Echo Wife</a> - 347 pages</li>
</ul>
<h2>Books by decade</h2>
<p><strong>1890</strong></p>
<ul>
<li>1895: <a href="https://remysharp.com/books/2025/the-time-machine">The Time Machine</a> <em>by H.G. Wells</em></li>
</ul>
<p><strong>1960</strong></p>
<ul>
<li>1969: <a href="https://remysharp.com/books/2025/the-left-hand-of-darkness">The Left Hand of Darkness</a> <em>by Ursula K. Le Guin</em></li>
</ul>
<p><strong>2010</strong></p>
<ul>
<li>2010: <a href="https://remysharp.com/books/2025/the-radleys">The Radleys</a> <em>by Matt Haig</em></li>
<li>2013: <a href="https://remysharp.com/books/2025/spike-milligan-man-of-letters">Spike Milligan: Man of Letters</a> <em>by Spike Milligan</em></li>
<li>2017: <a href="https://remysharp.com/books/2025/butter">Butter</a> <em>by Asako Yuzuki</em></li>
<li>2017: <a href="https://remysharp.com/books/2025/sweetpea-sweetpea-1">Sweetpea (Sweetpea, #1)</a> <em>by C.J. Skuse</em></li>
<li>2018: <a href="https://remysharp.com/books/2025/in-bloom-sweetpea-2">In Bloom (Sweetpea, #2)</a> <em>by C.J. Skuse</em></li>
<li>2019: <a href="https://remysharp.com/books/2025/reasons-to-be-cheerful">Reasons to Be Cheerful</a> <em>by Nina Stibbe</em></li>
</ul>
<p><strong>2020</strong></p>
<ul>
<li>2021: <a href="https://remysharp.com/books/2025/the-man-who-died-twice-thursday-murder-club-2">The Man Who Died Twice (Thursday Murder Club, #2)</a> <em>by Richard Osman</em></li>
<li>2021: <a href="https://remysharp.com/books/2025/the-echo-wife">The Echo Wife</a> <em>by Sarah Gailey</em></li>
<li>2022: <a href="https://remysharp.com/books/2025/the-satsuma-complex-gary-thorn-1">The Satsuma Complex (Gary Thorn, #1)</a> <em>by Bob Mortimer</em></li>
<li>2025: <a href="https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai">Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI</a> <em>by Karen Hao</em></li>
<li>2025: <a href="https://remysharp.com/books/2025/minority-rule-adventures-in-the-culture-war">Minority Rule: Adventures in the Culture War</a> <em>by Ash Sarkar</em></li>
<li>2025: <a href="https://remysharp.com/books/2025/making-a-killing-di-fawley-7">Making a Killing (DI Fawley #7)</a> <em>by Cara Hunter</em></li>
</ul>
<p><em>Originally published on <a href="https://remysharp.com/2026/01/01/books-i-read-in-2025">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>My 2025 [blog]</title>
      <guid isPermaLink="false">my-2025</guid>
      <link>https://remysharp.com/2025/12/31/my-2025</link>
      <pubDate>Wed, 31 Dec 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I've been doing these posts ending my years, aiming to publish on the 31st, so I'm pleased that I've managed to get this post out the door. Mostly for my own reading, but perhaps yours too.]]></description>
      <content:encoded><![CDATA[
<p>I've been doing these posts ending <a href="https://remysharp.com/my-years">my years</a>, aiming to publish on the 31st, so I'm pleased that I've managed to get this post out the door. Mostly for my own reading, but perhaps yours too.</p>
<h2>Reflecting and comparing</h2>
<p>I've just read through my opening to the <a href="https://remysharp.com/2024/12/31/my-2024">end of 2024</a> and wow, it doesn't feel better (to me). Though I do appreciate two things that factor into this: 1. I'm a glass half empty fella, 2. the coverage (I follow) of events doesn't often highlight the positive things happening.</p>
<p>The world is on fire. Still.</p>
<p>With this particular phrase in mind, I saw The Holdovers last week for the first time, and <a href="https://www.imdb.com/title/tt14849194/quotes/?item=qt7172147">this quote</a> struck me, for a movie set in 1970:</p>
<blockquote>
<p>the world doesn't make sense anymore. I mean, it's on fire. The rich don't give a shit. Poor kids are cannon fodder. Integrity is a punch line. Trust is just a name on a bank.</p>
</blockquote>
<p>The response to which is:</p>
<blockquote>
<p>…if that's all true, then now is when they most need someone like you.</p>
</blockquote>
<p>I think that's why we need to still feel like it's burning so that we can find that fire and help as do what we/I can.</p>
<p>Two particular legal cases that I'm trying to follow (though struggling because I suspect these legal things are <em>slow</em>):</p>
<ul>
<li>Palatine Action having been <a href="https://en.wikipedia.org/wiki/Palestine_Action#Proscription">proscribed</a>, and the <a href="https://en.wikipedia.org/wiki/Palestine_Action#Legal_challenge">legal challenge</a>, at time of writing, <a href="https://gcnchambers.co.uk/final-submissions-made-before-judgment-in-high-court-challenge-to-proscription-of-palestine-action/">&quot;Judgment is awaited&quot;</a>.</li>
<li>The ECHR &quot;interim guidance&quot; (quoted due to the lack-thereof) on &quot;woman&quot; and &quot;sex&quot; refer to the biological sex - and the resulting <a href="https://goodlawproject.org/case/were-challenging-the-ehrcs-interim-guidance/">challenge</a> showing that it's transphobic and legally wrong.</li>
</ul>
<p>A few other highlights being Labour did not swoop in and do what I had hoped (i.e. do right by people) and instead has spent a lot of their time trying to out-Reform Reform. The Giant Cheese Puff across the pond is tearing apart democracy, <a href="https://youtu.be/dergOG7vpJg?si=Oq5VTYDcVAhs3laO&amp;t=105">literally and figuratively</a> and the <a href="https://www.aljazeera.com/search/genocide?sort=date">genocide continues</a> even after a &quot;ceasefire&quot; and the <a href="https://www.theguardian.com/uk-news/2025/dec/29/number-people-britons-must-be-born-in-uk-rising-study">far right continue to raise it's ugly head</a> in the UK. Oh, an AI eating the world and people <em>actually</em> <a href="https://wapo.st/49uy2q1">dying from the companies pushing engagement</a> (<abbr title="Content warning">CW</abbr>: suicide).</p>
<p>So… with that floating around in the background, I have found it hard to muster the motivation to care about the web, about bits of code and about writing on my blog.</p>
<p>I will say that I've found a little light in the political sphere. I regularly watch <a href="https://crooked.com/podcast-series/pod-save-the-uk/">Pod Save the UK</a> on YouTube, and found Zack Polanski's campaign to lead the greens - and <em>a lot</em> of what I believed. I've joined the Greens specifically to cast a vote and not only did he win, but the Greens have seen a huge surge adding over 120,000 new members (me included).</p>
<p>In a similar vein to doom scrolling, I found myself watching Polanski's videos and interviews online to drop a pipette's worth of hope back into my system. This was a <a href="https://www.youtube.com/shorts/4nNJYpHk6Co">particular highlight</a> for me earlier this year.</p>
<p>With that out of the way, what have I been up to?</p>
<h2>Work</h2>
<p>Work-work (or as most people know it: <em>paid work</em>) has really been two main items: working with <a href="https://www.thenational.academy/">Oak National</a> and working on FFConf 2025.</p>
<p>I first started working for Oak in early 2021 and have worked with them for just over 3 years in total. Ranging from rebuilding their question and answer system, to designing and building their public facing content API (where you, Jo Public, can access any part of their curriculum).</p>
<p>I really like working with the people at Oak, and it's the first place I encountered the concept of working in squads (which I see as like mini movie production crews - ask me in person what I mean!).</p>
<p>My last contract ended in mid-August this year, but I'm looking forward to returning to work with them at the start of 2026.</p>
<p>Then onto FFConf, and with my Oak contract ending, allowed me much more time to focus on the event, planning and new software.</p>
<h3>FFConf</h3>
<p>This year's lead up to FFconf was very, very different to 2024's lead up.</p>
<p>In 2024, the stress of trying to sell tickets, trying to find sponsors, and feeling like we were spinning our tyres and getting no-where was bad. Very, very bad. Like &quot;I want to throw this all in&quot;-bad.</p>
<p>When Julie and I returned to FFConf's planning for 2025 we knew this wasn't the last one, but we also knew we needed to do something different at the very least for my own mental health (and so Julie wasn't worried about me the whole year).</p>
<p>The relatively small and simple change we made was that we decided we would send a newsletter every week. The same structure, but weekly on Thursday.</p>
<p>This seemingly obvious change, completely changed the experience for me. There was none of the stress and terror that I experienced in 2024.</p>
<p>Weirdly, tickets sold <em>mostly</em> similarly to 2024, but it <em>felt</em> completely different.</p>
<p>As for the day. It was wonderful. I had a smile throughout the day, and I felt full of love from the people who attended. The talks were amazing and inspiring and fun. I <a href="https://remysharp.com/2025/11/22/ffconf-2025">wrote about the day</a> too.</p>
<h2>Projects</h2>
<p>As I mentioned earlier, I've gone through a lot of the year not entirely excited about web stuff. I want to be, but… I'm just not right now.</p>
<p>A side effect of that feeling is that I've not really put much out into the world. I know I don't have to, but I also know that it's my creative outlet, and it's a nice thing for me to do (for me).</p>
<p>AI has been a dominant feature of the tech industries in 2025 - <a href="https://bsky.app/profile/remysharp.com/post/3mavgppf5bc2k">unavoidable</a> in a lot of cases - but I have been dabbling in lots of little personal tools and toys using (effectively) vibe coding.</p>
<p>I find myself using this vibe coding method to create personal items that make something really specific to me, like: a tracker to help me and Julie to remember who chose the movie last week <em>and</em> what the hell we watched, or, a command line tool to scan my network and try to work out where my <a href="https://esphome.io/">esphome</a> devices are, or, a percentage calculate (because I <em>keep</em> forgetting how it works), or, a tool to modify subtitle files to offset the timing, or, a stupid toy that let's me upload a photo, cut out a mouth, then it uses speech API to sing whatever you type in.</p>
<p><a href="https://remysharp.com/2025/07/18/vibe-coding-and-robocop">But these are personal toys</a>. Prototypes. Things that are not meant for more than one user.</p>
<p>I'd like to list and screenshot some of these in the future to preserve them a little better.</p>
<h3>Donations</h3>
<p>Each year, since 2021, I've been trying to look at donating <em>at least</em> 10% of the business profits to charities.</p>
<p>I put a call out on socials for anyone's lived experience with any trans support charities and the replies grew and grew (as much as I wanted to give to them all, I simply couldn't).</p>
<p>When reviewing which charities to donate to, we always check their income and want to see that they're also using their funds.</p>
<ul>
<li><a href="https://www.choirwithnoname.org/">Choir with No Name</a> - homelessness and marginalised groups</li>
<li><a href="https://www.forwardfacing.co.uk/">Forward Facing</a> - support for families with critical illness or complex disabilities</li>
<li><a href="https://clareproject.org.uk/">The Clare Project</a> - support for trans, non-binary and intersex adults</li>
</ul>
<p>Separately, I also donated to these companies (which aren't charities) from my personal donations:</p>
<ul>
<li><a href="https://transkidsdeservebetter.org/">Trans Kids Deserve Better</a></li>
<li><a href="https://transactual.org.uk/">TransActual</a></li>
<li><a href="https://goodlawproject.org/">The Good Law Project</a></li>
</ul>
<p>I hope to continue this &quot;tradition&quot; while I can.</p>
<h2>Personal</h2>
<p>Having touched on it already in the opening of this post, a lot of my personal headspace is in politics right now.</p>
<p>Outside of that particular stress, I've got my <a href="https://retrobyrem.uk/">Gameboy and retro hardware that I restore</a> (though much less often these days).</p>
<p>I also regularly tinker with my Home Assistant set up (something I've been meaning to write a little more about - there's some fun and dumb automations I have set up). A few of my favourites are: when I go to the gym, the bathroom radiator starts heating up (whereby my undies start getting warm in lieu of my shower when I return), or perhaps the increasingly <a href="https://www.collinsdictionary.com/dictionary/english/aggro">aggro</a> announcement to remind me to empty the washing machine.</p>
<p>The family keep growing (physically, thankfully the numbers have levelled off now!). The pets (3 cats and 1 dog) are happy, and the kids (one teenager and one pre-teen) keep sprouting and amazing (and <em>testing</em> us) each day.</p>
<p>The best books I read this year were both non-fiction (and thusly took me ages to read), but I highly recommend both:</p>
<ol>
<li><a href="https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai">Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI</a></li>
<li><a href="https://remysharp.com/books/2025/minority-rule-adventures-in-the-culture-war">Minority Rule: Adventures in the Culture War</a></li>
</ol>
<p>I'll see if I can rustle up my reading summary in the new year - though at 14 books, it's rather brief!</p>
<h3>Next bits - the retrospective</h3>
<p><a href="https://remysharp.com/2024/12/31/my-2024">Last year</a> I said I wanted to:</p>
<ul>
<li><strong>Increase my reading</strong> ✅ - in 2024 I read 13 books, this year I've read 14 (highly unlikely I'll finish the 15th in time!)</li>
<li><strong>Find the motivation to blog some more</strong> ❌ - I didn't find the motivation, though I did actually manage to blog more than 2024 (though still not my target of twice a month, every month)</li>
<li><strong>Hopefully some journalling will help</strong> 🤔 - I started strong, then slowed down to when I needed to put thoughts somewhere, to now… I can't remember the last time I wrote (and writing by pen is a slow process ofr me, my fingers can't keep up with the thoughts) - this one is a semi-fail</li>
<li><strong>Keep an eye on my health</strong> ✅ - this is a mixed bag really. The <abbr title="Multiple Sclerosis">MS</abbr> support here in Brighton is really good, so they regularly meet me to check things are okay (<abbr title="Magnetic resonance imaging">MRI</abbr> scans, drugs, etc). I <em>have</em> managed to return to the gym on a 2/3 times a week schedule. I had some physio that actually helped with some of my finger tip nerve issues (from <abbr title="Multiple Sclerosis">MS</abbr>) and it's improved the sensitivity for the first time in probably a decade. My diet…isn't terrible, but it's a far cry from dry chicken every day - I just think age is a reality I can't just ignore. On the other hand, my <a href="https://remysharp.com/2024/07/05/say-what-on-tinnitus-and-hearing">hearing aids</a> are superb and I love them.</li>
</ul>
<p>But hell, like I read somewhere on the web in the last week: 2026 is probably going to be a shitshow too, so don't be too hard on yourself.</p>
<p>This year, I'd like it if I found some way to get myself to write some more. I do wonder what software development is going to look like for me (particularly after how little code I've written from the ground up in the last 3 months). I'd like to keep up the reading and gym'ing. I'd like to get to bed earlier, and perhaps be a bit kinder inside my own head (to myself primarily). I want to ask &quot;can I help&quot; a lot more (if only to Julie) - it seems like a good mantra. I want to have fun with some stupid home assistant things. I want to design and build more PCBs and electronics projects (and perhaps improve my understanding).</p>
<p>And I'd really like to go to the cinema more. I miss the cinema. Maybe that'll be my thing in 2026 🤔</p>
<p>If you made it this far, thanks. If you're Remy reading this from the future - I hope things are looking up.</p>
<p>I'm now looking forward to reading your year-in-review blog posts, and I'll see you in The Future / 2026 💜</p>
<figure><img src="https://remysharp.com/images/family-2025.avif" alt="The family, growing up(wards)!" decoding="async"></figure>
<p><em>Originally published on <a href="https://remysharp.com/2025/12/31/my-2025">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Satsuma Complex (Gary Thorn, #1) [book]</title>
      <guid isPermaLink="false">the-satsuma-complex-gary-thorn-1</guid>
      <link>https://remysharp.com/books/2025/the-satsuma-complex-gary-thorn-1</link>
      <pubDate>Thu, 18 Dec 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Light on the brain.
Just the kind of read I wanted after a few longer books. Not 100% even throughout the book - but I'll come to that.
The squirrel from the cover features a nice silly-ish part of the story where the main character will often run into a local squirrel and add his own voice in a back and forth between him and the squirrel. The sort of thing I'd expect from a Bob Mortimer book.
I'm not 100% sure where the book's tone is supposed to sit. Some parts are particularly dark - police corruption, police harassment and modern day slavery. Juxtaposed next to funny little things like socks, squeaky shoes, a cranky neighbour and dog who's constantly disappointed with our main character.
The pages did turn quickly though, and it was easy reading. Nothing particularly to shout about, but not bad either.]]></description>
      <content:encoded><![CDATA[<p>Light on the brain.</p>
<p>Just the kind of read I wanted after a few longer books. Not 100% even throughout the book - but I'll come to that.</p>
<p>The squirrel from the cover features a nice silly-ish part of the story where the main character will often run into a local squirrel and add his own voice in a back and forth between him and the squirrel. The sort of thing I'd expect from a Bob Mortimer book.</p>
<p>I'm not 100% sure where the book's tone is supposed to sit. Some parts are particularly dark - police corruption, police harassment and modern day slavery. Juxtaposed next to funny little things like socks, squeaky shoes, a cranky neighbour and dog who's constantly disappointed with our main character.</p>
<p>The pages did turn quickly though, and it was easy reading. Nothing particularly to shout about, but not bad either.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-satsuma-complex-gary-thorn-1">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Butter [book]</title>
      <guid isPermaLink="false">butter</guid>
      <link>https://remysharp.com/books/2025/butter</link>
      <pubDate>Sat, 06 Dec 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Made me hungry and could even smell the foods, but took me a long time to read which made it feel extremely stretched and disconnected.
I really don't know why this book took me nearly 60 days to read, but it did which makes the events at the start of the book feel so utterly far away from the end.
I'm not sure what drew me to the book but I picked it up with zero expectations.
It discusses feminism, the pressure society puts on women to behave and appear certain ways (though this is Japanese culture, I do think a lot of this applies/is experienced by western white women too).
There's a strong feature of food and butter in particular (as you'd expect from the title) - and the author (and translator) does an excellent job of writing in a way that left me reading late at night and really wanting to taste the food (and I'm not a foodie).
There's also a story of friendship and support through those connections. This part of the book was really lovely to read, but seemed to all be crammed in at the end.
The story also includes a very manipulative character, Kajii, and whilst I read I felt sorry (and frustrated) for the protagonist, Rika, that she couldn't see the obvious manipulation (but perhaps that's the point, when we're close up to manipulation, we can't see the wood for the trees).
There really is a lot (of meaty subjects) in this book, and perhaps this contributed to my slow reading but it did leave me struggling to carry all the ideas through to the end of the book.
I've written about urgency in writing, and that's what helps me turn the page. This book doesn't have that, which isn't bad, but doesn't help me.
I suspect I would have enjoyed this more had I been able to digest it a little faster than two months.]]></description>
      <content:encoded><![CDATA[<p>Made me hungry and could even smell the foods, but took me a long time to read which made it feel extremely stretched and disconnected.</p>
<p>I really don't know why this book took me nearly 60 days to read, but it did which makes the events at the start of the book feel so utterly far away from the end.</p>
<p>I'm not sure what drew me to the book but I picked it up with zero expectations.</p>
<p>It discusses feminism, the pressure society puts on women to behave and <em>appear</em> certain ways (though this is Japanese culture, I do think a lot of this applies/is experienced by western white women too).</p>
<p>There's a <strong>strong</strong> feature of food and butter in particular (as you'd expect from the title) - and the author (and translator) does an excellent job of writing in a way that left me reading late at night and <em>really</em> wanting to taste the food (and I'm not a foodie).</p>
<p>There's also a story of friendship and support through those connections. This part of the book was really lovely to read, but seemed to all be crammed in at the end.</p>
<p>The story also includes a very manipulative character, Kajii, and whilst I read I felt sorry (and frustrated) for the protagonist, Rika, that she couldn't see the obvious manipulation (but perhaps that's the point, when we're close up to manipulation, we can't see the wood for the trees).</p>
<p>There really is a lot (of meaty subjects) in this book, and perhaps this contributed to my slow reading but it did leave me struggling to carry all the ideas through to the end of the book.</p>
<p>I've written about <em>urgency</em> in writing, and that's what helps me turn the page. This book doesn't have that, which isn't bad, but doesn't help me.</p>
<p>I suspect I would have enjoyed this more had I been able to digest it a little faster than two months.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/butter">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>An opportunity to learn: Advent of Code [blog]</title>
      <guid isPermaLink="false">an-opportunity-to-learn-advent-of-code</guid>
      <link>https://remysharp.com/2025/12/03/an-opportunity-to-learn-advent-of-code</link>
      <pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I've written about Advent of Code in the past, but that was 5 years ago, so this warrants a new post, and there's an extra opportunity, I think.]]></description>
      <content:encoded><![CDATA[<p>I've written about Advent of Code in <a href="https://remysharp.com/2020/12/01/the-advent-of-code">the past</a>, but that was 5 years ago, so this warrants a new post, and there's an extra opportunity, I think.</p>
<h2>The code calendar</h2>
<p>The advent of code is a daily code challenge, comprising of two parts based around the same problem, with increasing complexity. There's a wonderful little story that's weaved through the days, usually relating to Christmas and rouge elves that you can help.</p>
<p>I use it as a yearly chance to try to bend the <a href="https://jqlang.org/">jq language</a> into shapes it definitely wasn't intended for. It's another way of clearing out some of the cobwebs that gather in my head (<a href="https://github.com/remy/advent-of-code-solved/tree/main/2025">these are my attempts so far</a>).</p>
<p>Perhaps you're new to software development or a particular language, then this is a great way of having real problems to solve where there's an absolute answer. There's also a wealth of solutions posted up on the <a href="https://www.reddit.com/r/adventofcode/">Reddit channel</a> if you (or I) get stuck and need inspiration.</p>
<h2>Changes to the programme</h2>
<p>As of this year, there's now only 12 challenge days (previously it was 24 - one for each day until Christmas eve). For me this is welcomed, partly because I'd usually break by day 16, but also, I'm not sure I want to spend hours tinkering on a hard problem on Christmas eve (having never gotten past 16, I hadn't yet!).</p>
<p>The global leaderboard has also been removed. I personally never made it anywhere near the leaderboard, but some people would obsess over it. Sadly, with the rise and ease of access of AI tools, it meant quickly the time from challenge release to posting a correct answer started turning up in seconds long. That's single digit seconds to solve the problem - which really isn't in the spirit at all.</p>
<p>AI was banned/asked to not join in with the leaderboard, but completely removing the global leaderboard completely takes the pressure off (and that idea that &quot;why can't I solve it that fast&quot;).</p>
<p>You <em>can</em> still create a leaderboard for your friends or team - which makes sense.</p>
<h2>Speaking of AI</h2>
<p>Here's where my final suggestion might be controversial: why not try to solve using AI?</p>
<p>By that, I mean, if you're in a similar camp as me and have been sceptical of AI and a little wary, this might be a good opportunity to dip your toe in. I don't mean to paste in the challenge and have AI spit out the answer - that doesn't help anyone.</p>
<p>It could be seen as a chance to practise controlling the context and the problem space with AI doing the work. Perhaps testing different models from providers, but perhaps trying out local LLM models to see if they can do the work (so perhaps we could be a little more in control of the power usage).</p>
<p>As for me, I'm having to <em>actively</em> disable copilot in VS Code when solving the advent of code, because it's so desperate to help me!</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/12/03/an-opportunity-to-learn-advent-of-code">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Handing over to the AI for a day [blog]</title>
      <guid isPermaLink="false">handing-over-to-the-ai-for-a-day</guid>
      <link>https://remysharp.com/2025/11/29/handing-over-to-the-ai-for-a-day</link>
      <pubDate>Sat, 29 Nov 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Context: back in March 2025 I decided to put aside my scepticism and try AI driven development for the day. I appreciate that in 8 months, the AI landscape, particularly around agentic software dev has moved along, and perhaps this should have been posted originally back in March. All the same, maybe this is useful to some degree, if only to capture what it was like in time.

Whilst I sit squarely in my AI-sceptic seat, I was recently prompted to try a different tact by two post I read a few weeks back.
The first was Bruce's colonoscopy post, yes, that one. It was in fact that he was using a local LLM to create a generative image to commemorate his visit. I'd been using chatgpt (for code and electronics, basically a NLP version of a search engine), and hadn't considered that perhaps I could host myself to take some responsibility for one of the two problematic aspects of LLMs today (first being power consumption, second being the global theft).
The second post was Simon Willison's post on how he created a mini tool (the post is much broader than that, a useful read). It wasn't the post so much but the tool he was using: Claude Code. So far with chatgpt I'd copy back and forth errors and tweaks when it was helping me. Although I also have copilot enabled in VS Code it really holds down to autocomplete at a pretty junior level.
I'll often have typescript errors (for work) that copilot claims it can help fix, only resulting in even more typescript errors - so as a rule, I tend to avoid generating code fixes with copilot.
But what Simon showed with a shared transcript between himself and Claude Code was the software making the changes and offering diffs.
So that was what prompted a mini journey, and here's how it went.
Offline LLMs
Previously I had installed command line tools and even Ollama on my Mac without having the faintest idea how to use them effectively - so they sat idle and unused.
I'm not sure how, but I came across Msty, a tool that purported to make using local LLMs very easy. For a change, that seems to be true.
Since I still had Ollama running (though I should have probably ejected it), Msty quickly linked up to this and discovered (though I'd forgotten) that the DeepSeek R1 model was loaded already.
I figured since I was primarily using LLMs for software development, it made sense to find a model that suited those requirements.
Not quite that simple, or certainly not if you (ie. me) doesn't have the LLM lingo and terminology down.
I did some searching online, found a few articles, which themselves read as if they had been generated by AI, offering feature reviews of LLM models, but I couldn't find anything that said &quot;model X is excellent for Y software development&quot;.
Additionally, I found some models on huggingface.io, and figured that 7B was going to be easier/more viable to run than a 70B (big numbers need big machines? I was guessing…). However putting the name of the model into Myst yielded a lot of results and I couldn't really tell you what the difference was. More importantly, I couldn't quite be sure of the provenance (and whether that even matters…I'd assume it did).
I ended up installing the following models:

llama 3.2 (2gb)
deepseek coder v2 (which blew up)
codellama (3.8gb)
codellama 13b
WizardCoder (I think the 3gb one?) (it just didn't reply on the query)
Wavecoder-ultra-6.7b (literally never worked for me)

I tried a number of technical problems without much success and did capture the output for a number of these models to try to solve a Home Assistant automation process (which you can see here).
It's entirely likely there's better models out there for what I wanted to do. I did know I didn't need a model to know everything, so some focus (and thus: reduced size) would make sense.
However, not one of these gave useful replies to my questions.
My favourite (fail) was giving code-llama a question that read: this query gets all the lessons matching these lesson slugs (I gave it the actual SQL query), how do I get a list of all the slugs that didn't match?.
The LLM happily reported that I was trying to learn Spanish:

The provided code contains a list of sentences that are written in Spanish, and the goal is to translate them into English. However, there are some errors in the provided code that need to be fixed before it can function properly. Here are some issues with the current code:
Missing curly braces around the translate function definition: The translate function should be defined as a separate named function expression, like this: { translate } = require(&quot;google-translate&quot;);. This will ensure that the function is properly closed and can be used in other parts of the code.

For some reason it was using the slugs in the query as the most important part and completely ignoring the technical query.
For the most part, the responses I got were fairly hand wavy, text heavy (which I didn't want since I was asking about code), and in most cases irrelevant to my task.
I think em0ry42 on BlueSky sums up what I was seeing:

Smaller self hosted models will always under perform the larger ones. I think your experience and those of the other commenters are consistent with the current reality of these technologies. They can't do what people are promising. Yet.

I'm sure there are people who can tune the hell out of their setup, but sadly, running any decent LLM locally as a useful code assistant, is just not here for the rest of us.
So I parked that for a while, and turned my attention to Claude Code.
Coding without touching code
I've no clue how new Claude Code was at the time, though I've gathered it's fairly new. It's a solid product from my experience (where I even managed to lose track as to which company owns which weirdly named AI thingy).
Setup and interface is entirely on the command line, so already we're speaking my language.
I'd seen demos of developers who've been able to join up their entire codebase to the LLM but each time I'd dabbling, I would quickly get lost and give up.
Claude Code does exactly this without the walls I'd experienced in the past.
I am however, acutely wary that Claude is running on a remote machine, and likely to be chonking through so much power that we're just throwing away water to keep machines from burning up. Let's stick a pin in that (and gosh, I loath myself already for that).
The very first problem I wanted it to solve was where I was trying to download 1,000s of videos and they all needed to be added to one massive tarball (context: this is for work, to allow users to bulk download our assets).
I'd hit a problem where the tar process kept throwing an unhelpful exception the evening before and no amount of documentation on the library I was using helped me.
Overnight I had a suspicion as to the cause and it gave me an idea to try - but I thought I'd let Claude try first, see what it does.
Without any specific direction (ie. my idea for the fix), and only the name of the file and the function the problem happened, Claude Code suggested the same solution I had in my head.
The UI then offered a syntax highlighted diff of the change it wanted to commit to disk. I was able to review it (very much how I'd approach a code review) and all I then needed to do was hit enter to accept.
I tested the code in a separate terminal and indeed the change worked.
Given this positive start, I then spent most of the working time split between the Claude Code UI and in the terminal to run the main program (which was sequencing a very large dataset).
The code changes for the most part were always good and code that I accepted.
The experience was…weird. I'd heard of LLMs being referred to as junior developers but when I was going back and forth between chatgpt and vscode (again, for me, copilot never really came in useful), because of the amount of interaction that was required from me, it felt even less than working with a junior.
But this was a much closer experience. I'd describe the change and logic, sometimes pointing to filenames that would offer useful context, and Claude would spend some time (and money) thinking, then it would ask me to check a diff.
Weirdly I spent more time sitting and staring out the window waiting for code to come back than I did looking at code. It was a weirdly hands off experience. I can't tell where I sit on that.
The main criticism that I had is that, because we use specific rules for typescript (no any and types are defined, which I think seems okay), Claude wouldn't really follow those strict rules, so I needed to go in at the end to clean that part up.
The secondary criticism is more a matter of taste. The code (and logging) was verbose to my taste. Additionally, being outside the code for the majority of the work period felt really strange. Sort of like a self-driving car took me the majority of my journey, deciding itself the navigation, for me only to be needed for the final arrival through some tight country lanes. Or something!
A cost
Since I was freestyling my way on Claude Code, I did manage to rattle through $5 of credit. I did think this was (somehow) linked to my Google business account, but I'm now suspecting it was free credit to introduce me to their API.
After running through this credit now twice (I switched to my personal account for a second run) I've discovered there's tools to help manage that sprawling cost (such as /compact and /clear to reduce how much context the LLM is fed before giving me a result). I'd like to play with this more to get an idea of how much I'm really prepared to pay.
Also after writing (most of) this post, I came across an interesting project that takes the Claude UI and lets you connect up your own backend. I've not tried it yet, but I'd be interested to see if I can connect to a local LLM and try out results (though going by the current experience, it's going to have a hard time competing).
Since it was conversational...
I decided to hack together a simple keyboard keycap (I had spare) with an ESP32 board to emulate a keyboard.
Then this would send a (fairly) unique keycode that then launched a python command which started a whisper based script that let me talk, then pasted the text into whatever was focused.
This meant I had: press button, say the thing, press the button, wait for it to be done.
It wasn't great because it was a little clunky, but it definitely felt futuristic!
How I felt afterwards
(I'm now writing this 8 months late, but I remember how it felt on the day).
Even though I was surprised at the progress of the work, both for how terrible the local code solving was and how impressed I was with Claude Code - it did leave me with a feeling of disconnect.
There's certainly the issue with the maintainability of pure vibe-coded software, but this was something more.
There's a creative input that I put into my coding process. A sense of purpose and achievement in solving some complicated problem, or writing a line of code that I'm particularly pleased with. There wasn't really of that feeling of connection with the output.
Having written this retrospectively I know that my perspective has changed somewhat, but I do remember have this weird dissonance between the outcome and the experience of getting there.]]></description>
      <content:encoded><![CDATA[
<p><em><strong>Context</strong>: back in March 2025 I decided to put aside my scepticism and try AI driven development for the day. I appreciate that in 8 months, the AI landscape, particularly around agentic software dev has moved along, and perhaps this should have been posted originally back in March. All the same, maybe this is useful to some degree, if only to capture what it was like in time.</em></p>
<hr>
<p>Whilst I sit squarely in my AI-sceptic seat, I was recently prompted to try a different tact by two post I read a few weeks back.</p>
<p>The first was Bruce's <a href="https://brucelawson.co.uk/2025/colonoscopy-fun/">colonoscopy post</a>, yes, that one. It was in fact that he was using a <em>local</em> LLM to create a generative image to commemorate his visit. I'd been using chatgpt (for code and electronics, basically a NLP version of a search engine), and hadn't considered that perhaps I could host myself to take some responsibility for one of the two problematic aspects of LLMs today (first being power consumption, second being the global theft).</p>
<p>The second post was Simon Willison's post on <a href="https://simonwillison.net/2025/Mar/11/using-llms-for-code/#a-detailed-example">how he created a mini tool</a> (the post is much broader than that, a useful read). It wasn't the post so much but the tool he was using: Claude Code. So far with chatgpt I'd copy back and forth errors and tweaks when it was helping me. Although I also have copilot enabled in VS Code it really holds down to autocomplete at a pretty junior level.</p>
<p>I'll often have typescript errors (for work) that copilot claims it can help fix, only resulting in even more typescript errors - so as a rule, I tend to avoid generating code fixes with copilot.</p>
<p>But what Simon showed with a shared transcript between himself and Claude Code was the software making the changes and offering diffs.</p>
<p>So that was what prompted a mini journey, and here's how it went.</p>
<h2>Offline LLMs</h2>
<p>Previously I had installed command line tools and even Ollama on my Mac without having the faintest idea how to use them effectively - so they sat idle and unused.</p>
<p>I'm not sure how, but I came across <a href="https://msty.app/">Msty</a>, a tool that purported to make using local LLMs very easy. For a change, that seems to be true.</p>
<p>Since I still had Ollama running (though I should have probably ejected it), Msty quickly linked up to this and discovered (though I'd forgotten) that the DeepSeek R1 model was loaded already.</p>
<p>I figured since I was primarily using LLMs for software development, it made sense to find a model that suited those requirements.</p>
<p>Not quite that simple, or certainly not if you (ie. me) doesn't have the LLM lingo and terminology down.</p>
<p>I did some searching online, found a few articles, which themselves read as if they had been generated by AI, offering feature reviews of LLM models, but I couldn't find anything that said &quot;model X is excellent for Y software development&quot;.</p>
<p>Additionally, I found some models on <a href="http://huggingface.io">huggingface.io</a>, and figured that 7B was going to be easier/more viable to run than a 70B (big numbers need big machines? I was guessing…). However putting the name of the model into Myst yielded <em>a lot</em> of results and I couldn't really tell you what the difference was. More importantly, I couldn't quite be sure of the provenance (and whether that even matters…I'd assume it did).</p>
<p>I ended up installing the following models:</p>
<ol>
<li>llama 3.2 (2gb)</li>
<li>deepseek coder v2 (which blew up)</li>
<li>codellama (3.8gb)</li>
<li>codellama 13b</li>
<li>WizardCoder (I think the 3gb one?) (it just didn't reply on the query)</li>
<li>Wavecoder-ultra-6.7b (literally never worked for me)</li>
</ol>
<p>I tried a number of technical problems without much success and did capture the output for a number of these models to try to solve a Home Assistant automation process (which <a href="https://gist.github.com/remy/76d5761b57a5d42f781b74c072d22139">you can see here</a>).</p>
<p>It's entirely likely there's better models out there for what I wanted to do. I did know I didn't need a model to know everything, so some focus (and thus: reduced size) would make sense.</p>
<p>However, not one of these gave useful replies to my questions.</p>
<p>My favourite (fail) was giving code-llama a question that read: this query gets all the lessons matching these lesson slugs (I gave it the actual SQL query), how do I get a list of all the slugs that <strong>didn't</strong> match?.</p>
<p>The LLM happily reported that I was trying to learn Spanish:</p>
<blockquote>
<p>The provided code contains a list of sentences that are written in Spanish, and the goal is to translate them into English. However, there are some errors in the provided code that need to be fixed before it can function properly. Here are some issues with the current code:</p>
<p>Missing curly braces around the translate function definition: The translate function should be defined as a separate named function expression, like this: { translate } = require(&quot;google-translate&quot;);. This will ensure that the function is properly closed and can be used in other parts of the code.</p>
</blockquote>
<p>For some reason it was using the slugs in the query as the most important part and completely ignoring the technical query.</p>
<p>For the most part, the responses I got were fairly hand wavy, text heavy (which I didn't want since I was asking about code), and in most cases irrelevant to my task.</p>
<p>I think <a href="https://bsky.app/profile/em0ry42.penney-family.net/post/3lkdpqaouc22h">em0ry42 on BlueSky</a> sums up what I was seeing:</p>
<blockquote>
<p>Smaller self hosted models will always under perform the larger ones. I think your experience and those of the other commenters are consistent with the current reality of these technologies. They can't do what people are promising. Yet.</p>
</blockquote>
<p>I'm sure there are people who can tune the hell out of their setup, but sadly, running any decent LLM locally as a useful code assistant, is just not here for the rest of us.</p>
<p>So I parked that for a while, and turned my attention to <a href="https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview">Claude Code</a>.</p>
<h2>Coding without touching code</h2>
<p>I've no clue how new Claude Code was at the time, though I've gathered it's <em>fairly</em> new. It's a solid product from my experience (where I even managed to lose track as to which company owns which weirdly named AI thingy).</p>
<p>Setup and interface is entirely on the command line, so already we're speaking my language.</p>
<p>I'd seen demos of developers who've been able to join up their entire codebase to the LLM but each time I'd dabbling, I would quickly get lost and give up.</p>
<p>Claude Code does exactly this without the walls I'd experienced in the past.</p>
<p>I am however, acutely wary that Claude <em>is</em> running on a remote machine, and likely to be chonking through so much power that we're just <a href="https://m.youtube.com/shorts/PSe6GSwJ0cI">throwing away water</a> to keep machines from burning up. Let's stick a pin in that (and gosh, I loath myself already for that).</p>
<p>The very first problem I wanted it to solve was where I was trying to download 1,000s of videos and they all needed to be added to one massive tarball (context: this is for work, to allow users to bulk download our assets).</p>
<p>I'd hit a problem where the tar process kept throwing an unhelpful exception the evening before and no amount of documentation on the library I was using helped me.</p>
<p>Overnight I had a suspicion as to the cause and it gave me an idea to try - but I thought I'd let Claude try first, see what it does.</p>
<p>Without any specific direction (ie. my idea for the fix), and only the name of the file and the function the problem happened, Claude Code suggested the same solution I had in my head.</p>
<p>The UI then offered a syntax highlighted diff of the change it wanted to commit to disk. I was able to review it (very much how I'd approach a code review) and all I then needed to do was hit enter to accept.</p>
<p>I tested the code in a separate terminal and indeed the change worked.</p>
<p>Given this positive start, I then spent most of the working time split between the Claude Code UI and in the terminal to run the main program (which was sequencing a very large dataset).</p>
<p>The code changes for the most part were always good and code that I accepted.</p>
<p>The experience was…weird. I'd heard of LLMs being referred to as junior developers but when I was going back and forth between chatgpt and vscode (again, for me, copilot never really came in useful), because of the amount of interaction that was required from me, it felt even less than working with a junior.</p>
<p>But this was a much closer experience. I'd describe the change and logic, sometimes pointing to filenames that would offer useful context, and Claude would spend some time (and money) thinking, then it would ask me to check a diff.</p>
<p>Weirdly I spent more time sitting and staring out the window waiting for code to come back than I did looking at code. It was a weirdly hands off experience. I can't tell where I sit on that.</p>
<p>The main criticism that I had is that, because we use specific rules for typescript (no <code>any</code> and types are defined, which I think seems okay), Claude wouldn't really follow those strict rules, so I needed to go in at the end to clean that part up.</p>
<p>The secondary criticism is more a matter of taste. The code (and logging) was verbose to my taste. Additionally, being outside the code for the majority of the work period felt really strange. Sort of like a self-driving car took me the majority of my journey, deciding itself the navigation, for me only to be needed for the final arrival through some tight country lanes. Or something!</p>
<h2>A cost</h2>
<p>Since I was freestyling my way on Claude Code, I did manage to rattle through $5 of credit. I did think this was (somehow) linked to my Google business account, but I'm now suspecting it was free credit to introduce me to their API.</p>
<p>After running through this credit now twice (I switched to my personal account for a second run) I've discovered there's tools to help manage that sprawling cost (such as <code>/compact</code> and <code>/clear</code> to reduce how much context the LLM is fed before giving me a result). I'd like to play with this more to get an idea of how much I'm <em>really</em> prepared to pay.</p>
<p>Also after writing (most of) this post, I came across an interesting project that takes the <a href="https://github.com/dnakov/anon-kode">Claude UI and lets you connect up your own backend</a>. I've not tried it yet, but I'd be interested to see if I can connect to a local LLM and try out results (though going by the current experience, it's going to have a hard time competing).</p>
<h2>Since it was conversational...</h2>
<p>I decided to hack together a simple keyboard keycap (I had spare) with an ESP32 board to emulate a keyboard.</p>
<p>Then this would send a (fairly) unique keycode that then launched a python command which started a whisper based script that let me talk, then pasted the text into whatever was focused.</p>
<p>This meant I had: press button, say the thing, press the button, wait for it to be done.</p>
<p>It wasn't great because it was a little clunky, but it definitely felt futuristic!</p>
<h2>How I felt afterwards</h2>
<p>(I'm now writing this 8 months late, but I remember how it felt on the day).</p>
<p>Even though I was surprised at the progress of the work, both for how terrible the local code solving was and how impressed I was with Claude Code - it did leave me with a feeling of disconnect.</p>
<p>There's certainly the issue with the <a href="https://remysharp.com/2025/07/18/vibe-coding-and-robocop">maintainability of pure vibe-coded software</a>, but this was something more.</p>
<p>There's a creative input that I put into my coding process. A sense of purpose and achievement in solving some complicated problem, or writing a line of code that I'm particularly pleased with. There wasn't really of that feeling of connection with the output.</p>
<p>Having written this retrospectively I know that my perspective has changed somewhat, but I do remember have this weird dissonance between the outcome and the experience of getting there.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/11/29/handing-over-to-the-ai-for-a-day">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>FFConf 2025 [blog]</title>
      <guid isPermaLink="false">ffconf-2025</guid>
      <link>https://remysharp.com/2025/11/22/ffconf-2025</link>
      <pubDate>Sat, 22 Nov 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I've been wanting to write and share my experience of this year's event but a number of things have slowed me down - not least of all that it was Julie's birthday the following Tuesday (the first year her birthday was entirely swallowed by the event).
So now as I sit writing this a full eight days later - sat on the side of the swimming pool as many kids, including my own, do their swimming lessons - I'm trying to collect my thoughts on the day.]]></description>
      <content:encoded><![CDATA[
<p>I've been wanting to write and share my experience of this year's event but a number of things have slowed me down - not least of all that it was Julie's birthday the following Tuesday (the first year her birthday was entirely swallowed by the event).</p>
<p>So now as I sit writing this a full eight days later - sat on the side of the swimming pool as many kids, including my own, do their swimming lessons - I'm trying to collect my thoughts on the day.</p>
<h2>The talks</h2>
<p>Every year it's always an amazing experience, hearing the stories of speakers from across different walks of life.</p>
<p>The videos of the talks will be released in the coming weeks and <a href="https://ffconf.org/news">our newsletter</a> will highlight each in turn.</p>
<p>I curated the talks so that up front we had the heavier subjects: diversity from <a href="https://2025.ffconf.org/hellen">Hellen</a> and data privacy from <a href="https://2025.ffconf.org/chetan">Chetan</a>.</p>
<p>From the evening before we were talking about JavaScript, React, standards and new developers coming to the industry. We were talking specifically about whether the lessons had been learnt from the past or needed to be repeated.</p>
<p>I'm of the belief (though only in the last decade) that we need to repeat the learnings over and over and over. There's no point when the teaching is done. A fun example of this is seeing the CSS creative coders having their hay-day whilst hearing from the sidelines that the Flash developers have already done a lot of this. It's not new. But if you don't continue to pass down your lessons, the next generations will make the same mistakes and have to learn from scratch.</p>
<p>That's a very long way to say that we've covered the subjects of diversity and privacy before, but new perspectives and different experiences keep the subjects fresh and not a &quot;done&quot; task in the world of the web worker.</p>
<p>The next segment talked about web components from <a href="https://2025.ffconf.org/hannah">Hannah</a> and about cults from <a href="https://2025.ffconf.org/serges">Sèrges</a>.</p>
<p>Hannah talked about her experience bringing web components to a React based team and the process integration both from the software side but also from the team adoption perspective.</p>
<p>Unsurprisingly (see previous notes on curation 😉) Sèrges brilliant (fun and funny) talk about cults dovetailed perfectly into Hannah's talk. She argued that we're all in a cult (to some degree) and touched on the gatekeeping that was also touched on in Helen's talk about team diversity.</p>
<p>Post lunch was time to discuss AI. When friends of mine who know me from outside the web, as about the conference, they understandably assume it's all about AI. It's all we hear about these days.</p>
<p>The two talks were very different perspectives on AI. <a href="https://2025.ffconf.org/asim">Asim</a> gave a talk (obscurely but made sense in the end) entitled Don't be an Idiot on how AI can, and is, being used to democratise big decisions, putting much more nuance in the decisions offered to people.</p>
<p>The following was <a href="https://2025.ffconf.org/jessica-eda">Jess and Eda</a> on AI coding tools and skills. Told from the perspective of a senior and junior and what are the lasting impacts on relying (or being pushed into) AI tools. Very much the argument of shallow learning versus deep (human) learning through experience.</p>
<p>Finally to close the event two talks from the vast spread of experience starting with <a href="https://2025.ffconf.org/surya">Surya</a> on the journey of 6 to 16 years old and coding. As our industry continues to exist, it's the young people who will breathe life into it, so I wanted to hear about Surya's experience (even if he is a bit of a black swan - making many of us old hats feel rather like underachievers!).</p>
<p>Then to close, we (FFConf) were lucky to have <a href="https://2025.ffconf.org/sacha">Sacha Judd</a> (because she resides in New Zealand and it's a long old journey) filling our hearts with the joy of what the Good Internet is. Not just a nostalgic memory but alive today and just as weird as always.</p>
<p>Here are all <a href="https://www.flickr.com/photos/remysharp/albums/72177720330509685/">the photos from FFConf 2025</a> if you'd like to see the lovely time people had.</p>
<figure><img src="https://remysharp.com/images/ffconf-2025.avif" alt="The people attending FFConf waving for the camera" decoding="async"></figure>
<h2>Looking for meaning</h2>
<p>The last few years of FFconf have been hard for me to put into context in a world that honestly frightens me. War. Genocide. Famine. Racism. Fascism. Hate. They're alive and well.</p>
<p>Sometimes I think it's ridiculous to put on a web event when things outside are so hard on people. In part it feels like a horrible privilege to be able to sink my head down and listen to these amazing speakers.</p>
<p>On reflection though, I do believe we need to recharge. That we need a little self care if we're going to continue fighting for a fair and equal world, however we achieve it. So maybe that's important for FFConf.</p>
<p>At the top of the event, I asked everyone to try to find one thing that you'll take away. There's so much in a day of talks - it's hard to remember what got you excited in the earlier talks let alone make sweeping changes.</p>
<p>So the (first) one thing I decided to do, relatively simple, was to move to more paid services (I've switched to <a href="https://kagi.com/">Kagi</a> already). I've already started paying for my news (<a href="https://www.404media.co/">404 Media</a>, <a href="https://novaramedia.com/">Novara Media</a>, Guardian and a few others) so I'm continuing to look for these. I also know that this works if you have the funds. For me, it's part of my work, so my business pays for it. I get that it's not as simple for others (that's to say: this is <em>my</em> one thing!).</p>
<p>In my back pocket I'm going to make myself some either silly or partially useful little toys. I may or may not share these; I like that these can be for me and family/friends: low pressure nonsense.</p>
<h2>Next</h2>
<p>FFConf will return next year, Friday 13 November. I've already got a couple of speakers in mind so I need to approach them with my proposals.</p>
<p>I'll be attending more events myself next year too, including <a href="https://2026.stateofthebrowser.com/">State of the Browser</a>, <a href="https://webdayout.com/">Web Day Out</a>, (the final) <a href="https://heypresents.com/conferences/2026">All Day Hey</a>, <a href="https://beyondtellerrand.com/events/dusseldorf-2026">Beyond Tellerand</a> and <a href="https://webdevconf.com/events/">WDC</a> (tickets aren't live yet). I'm on the lookout for more events and meet ups in particular (though I have a stinking record of staying at home).</p>
<p>I know this might read as a hopeful note, and I do want it to be, but I also know that I don't 100% buy my written down optimism (I should!). Still, I hope to see you out there.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/11/22/ffconf-2025">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Building an AI Sandbox with Docker [link]</title>
      <guid isPermaLink="false">2025-11-12-dac6ad4b</guid>
      <link>https://remysharp.com/links/2025-11-12-dac6ad4b</link>
      <pubDate>Wed, 12 Nov 2025 14:08:00 +0000</pubDate>
      <description><![CDATA[As I think about how command line AI tools can be used, I know it's already a bit of the wild west not disimilar to the way we install npm modules. I've seen a bit of writing about how these tools should be run in a controlled sandbox (because they can mess with the surroudning environment), and this is a nice digestable post with working examplse of Dockerfiles.
Source: thingsithinkithink.blog]]></description>
      <content:encoded><![CDATA[
<p>As I think about how command line AI tools can be used, I know it's already a bit of the wild west not disimilar to the way we install npm modules. I've seen a bit of writing about how these tools should be run in a controlled sandbox (because they can mess with the surroudning environment), and this is a nice digestable post with working examplse of Dockerfiles.</p>
<p><em>Source: <a href="https://thingsithinkithink.blog/posts/2025/11-11-building-an-ai-sandbox-with-docker/">thingsithinkithink.blog</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-11-12-dac6ad4b">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Syntax Highlighting in Web Component Templates [blog]</title>
      <guid isPermaLink="false">syntax-highlighting-in-web-component-templates</guid>
      <link>https://remysharp.com/2025/11/12/syntax-highlighting-in-web-component-templates</link>
      <pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[A simple but effective fix to working with web components and VS Code. I wanted to get syntax highlighting and prettier support (to auto fix indenting, quotes, etc) in my component's templates.
The extremely quick read is, add /* HTML */ to the front of the template. Case sensitive and space sensitive (though hopefully one day it won't be so strict). Now highlighting and prettier (with save and fix) works.
Note that you need the es6-string-html VS Code extension for this to highlight correct (something I had forgotten I had installed).]]></description>
      <content:encoded><![CDATA[
<p>A simple but effective fix to working with web components and VS Code. I wanted to get syntax highlighting <em>and</em> prettier support (to auto fix indenting, quotes, etc) in my component's templates.</p>
<p>The extremely quick read is, add <code>/* HTML */</code> to the front of the template. Case sensitive <em>and</em> space sensitive (though hopefully one day it won't be so strict). Now highlighting and prettier (with save and fix) works.</p>
<p>Note that you need the <a href="https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html">es6-string-html</a> VS Code extension for this to highlight correct (something I had forgotten I had installed).</p>
<h2>The slightly longer read</h2>
<p>What I wanted to achieve was that I could include my template in the source of the web component (or at least in the same directory - i.e. physically near to the application logic).</p>
<p>The problem is that unless I use a build function (or tools), the markup for the template is a string.</p>
<p>Here's the starting point:</p>
<pre><code class="language-js">customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span>
  <span class="token string">'hello-world'</span><span class="token punctuation">,</span>
  <span class="token keyword">class</span> <span class="token class-name">extends</span> HTMLElement <span class="token punctuation">{</span>
    <span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> n <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">'World'</span><span class="token punctuation">;</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'open'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
      &lt;style>:host{font:600 16px system-ui;color:#222}&lt;/style>
      &lt;div>Hello, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>n<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> 👋&lt;/div></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Seen here with VS Code's syntax highlighting - note that the template itself is just green, plain text:</p>
<figure><img src="https://remysharp.com/images/template-highlight-0.png.avif" alt="Although the JavaScript is highlighted, the string of markup applied to the innerHTML is all in green" decoding="async"></figure>
<p>Using a tag function solves the main issue, but requires extra code or a <em>magic</em> build process which I really don't want or need - and look at that nasty red snake telling me I need to write more code:</p>
<figure><img src="https://remysharp.com/images/template-highlight-2.png.avif" alt="The syntax is nice and tidy now using a tag function, but my linting is highlighted that the html function is missing" decoding="async"></figure>
<p>VS Code does support highlighting if you give it a hint using a comment: <code>/*html*/</code>, but prettier doesn't format it - close, but still no dice:</p>
<figure><img src="https://remysharp.com/images/template-highlight-1.png.avif" alt="The syntax is now fully colourised, but not structured in a way that's nice and easy to read" decoding="async"></figure>
<p>Finally, if you get the comment syntax <em>exactly</em> right, that's uppercase and with spaces around the text, <code>/* HTML */</code>, then you'll get both highlighting and syntax tidy support without the need for build tools:</p>
<figure><img src="https://remysharp.com/images/template-highlight-3.png.avif" alt="The syntax is now fully colourised and it's tidy" decoding="async"></figure>
<p>I do have the VS Code option <code>&quot;prettier.embeddedLanguageFormatting&quot;: &quot;auto&quot;</code> in my settings, but if I've understood correctly, this should be the default for prettier and not required.</p>
<p>A large hat tip to <a href="https://front-end.social/@zzzzBov/115535106033886819">Timothy Leverett on Mastodon</a> for helping me.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/11/12/syntax-highlighting-in-web-component-templates">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Automerge [link]</title>
      <guid isPermaLink="false">2025-11-04-aeb3d7b3</guid>
      <link>https://remysharp.com/links/2025-11-04-aeb3d7b3</link>
      <pubDate>Tue, 04 Nov 2025 12:47:22 +0000</pubDate>
      <description><![CDATA[Version control for your data: Automerge is a local-first sync engine for multiplayer apps that works offline, prevents conflicts, and runs fast. Interesting project, possibly for mini personal projects that want to share some data (possibly without a database? unsure). But also in some kind of collaborative code editor, ala codecasting from JS Bin old days. Beautifully designed landing page though.
Source: automerge.org]]></description>
      <content:encoded><![CDATA[
<p>Version control for your data: Automerge is a local-first sync engine for multiplayer apps that works offline, prevents conflicts, and runs fast. Interesting project, possibly for mini personal projects that want to share some data (possibly without a database? unsure). But also in some kind of collaborative code editor, ala codecasting from JS Bin old days. Beautifully designed landing page though.</p>
<p><em>Source: <a href="https://automerge.org/">automerge.org</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-11-04-aeb3d7b3">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>How has FFConf changed since it was known as Full Frontal? [blog]</title>
      <guid isPermaLink="false">how-has-ffconf-changed-since-it-was-known-as-full-frontal</guid>
      <link>https://remysharp.com/2025/10/27/how-has-ffconf-changed-since-it-was-known-as-full-frontal</link>
      <pubDate>Mon, 27 Oct 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Besides the name, the entire core foundation has changed.
This question came up recently by someone who had either attended back in the early days or that knew of our event, but they (understandably) saw us as it was in 2009.
I think the vibes you came away with in the 2009 and 2010 events would be recognised, but content and the core messaging has changed quite significantly.]]></description>
      <content:encoded><![CDATA[
<p>Besides the <a href="https://remysharp.com/2016/07/22/whats-in-a-name">name</a>, the entire core foundation has changed.</p>
<p>This question came up recently by someone who had either attended back in the early days or that knew of our event, but they (understandably) saw us as it was in 2009.</p>
<p>I think the vibes you came away with in the 2009 and 2010 events would be recognised, but content and the core messaging has changed quite significantly.</p>
<p>The event was founded on the premise that there weren’t any (dedicated) JavaScript conferences in the UK at the time, and I wanted <em>something</em> to fulfil my own desire to see that change. I wanted an event that I would want to attend.</p>
<p>The first line up in 2009 was mostly brought about by asking the question: who do I know or could reach that talk about JavaScript things? I can’t say for certain, but I suspect the outreach was &quot;can you speak&quot; rather than &quot;can you speak about…&quot;.</p>
<p>Immediately I knew I wanted a rule that prevented speakers from repeating (this was a preventative measure to get me to reach outside of my circles).</p>
<p>The follow 2010 event still had a JavaScript flavour but was already showing signs of morphing into something different (albeit I wouldn’t start working on the diversity until 2012).</p>
<p>I remember Dan Webb (who spoke in 2010) saying that he realised at the end of the event he was the only speaker to show code in his slides.</p>
<p>By 2018, the 10th conference, I think the entire shape had changed. Talks like <a href="https://ffconf.org/talks/mentoring/">&quot;Mentoring: Being the help you wish you'd had&quot;</a>, <a href="https://ffconf.org/talks/dear-developer/">&quot;Dear Developer, the Web Isn't About You&quot;</a> and <a href="https://ffconf.org/talks/weird-web">&quot;Weird Web &amp; Curious Creation&quot;</a> were all based in the idea of helping others and ourselves to become better Web Citizens.</p>
<p>People who had attended told me that the talks they saw at FFConf were talks they never expected to see - in a positive way. <a href="https://ffconf.org/talks/2022_heydonworks_talk/">&quot;Capitalism, The Web, And You&quot;</a> was an excellent example of that in 2019 along with <a href="https://ffconf.org/talks/2022_lil_natw_talk/">&quot;Working towards a greener world from behind the keyboard&quot;</a> or <a href="https://ffconf.org/talks/2022_lily_2point0_talk/">&quot;Programming with Yarn&quot;</a> (although I've only included 3 talks, they're all superb, or certainly to me).</p>
<p>The talks at FFConf continue along this line of thinking. How can we learn to be better versions of ourselves - and so that we can help others.</p>
<p>From the talks on the day, you won't learn how to add aria roles to a tabbing system, but you will learn how your junior developers are being affected by learning from LLMs spouting code at them. You won't learn what the latest additions to JavaScript syntax is, but you will learn how the latest technology has been used to exploit your privacy and the ethics of how developers' decisions landed them there.</p>
<p>You won't learn what a blog post, a tiktok or chatgpt can give you. You'll come away inspired and fired up and (hopefully, usually!) excited to work. It's a hard one to measure, and hard to explain to a business &quot;what's the ROI?&quot;, but if we are happy, keen or even excited to work, then the ROI is <em>always</em> a net positive.</p>
<p>So that's what FFConf is in 2025. It's an event chooses people over technology, that aims to inspire and motivate, and that champions a better web.</p>
<figure><img src="https://remysharp.com/images/combined-ffconf-group.avif" alt="The audience over the years, looking happy with their hands raised high" decoding="async"></figure>
<p><em>Originally published on <a href="https://remysharp.com/2025/10/27/how-has-ffconf-changed-since-it-was-known-as-full-frontal">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>What Happened to Apple's Legendary Attention to Detail? [link]</title>
      <guid isPermaLink="false">2025-10-23-d9bf3277</guid>
      <link>https://remysharp.com/links/2025-10-23-d9bf3277</link>
      <pubDate>Thu, 23 Oct 2025 11:33:22 +0000</pubDate>
      <description><![CDATA[I accidentally upgraded to Tahoe (I didn't know it existed and thought I was moving to Sequoia and the UI design is all over the place, and it's constantly reminding me how bad it is. This excellent article takes what was an attention to detail that we took for granted (because tech is supposed to &quot;just work&quot;), and calls out just a handful of the failings that Apple's OS now ships with (including on iOS, that I thankfully don't have to suffer). My fear, based on experience with bad Apple UI - (like the notifications that couldn't be quickly dismissed forcing us to click EXACTLY in the right place, and with the &quot;suitable&quot; amount of delay) - is that it simply won't be fixed or even improved. /via venerable Bruce Lawson
Source: blog.johnozbay.com]]></description>
      <content:encoded><![CDATA[
<p>I accidentally upgraded to Tahoe (I didn't know it existed and thought I was moving to Sequoia and the UI design is all over the place, and it's constantly reminding me how bad it is. This excellent article takes what was an attention to detail that we took for granted (because tech is supposed to &quot;just work&quot;), and calls out just a handful of the failings that Apple's OS now ships with (including on iOS, that I thankfully don't have to suffer). My fear, based on experience with bad Apple UI - (like the notifications that couldn't be quickly dismissed forcing us to click <em>EXACTLY</em> in the right place, and with the &quot;suitable&quot; amount of delay) - is that it simply won't be fixed or even improved. /via venerable <a href="https://brucelawson.co.uk/">Bruce Lawson</a></p>
<p><em>Source: <a href="https://blog.johnozbay.com/what-happened-to-apples-attention-to-detail.html">blog.johnozbay.com</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-10-23-d9bf3277">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Empire of AI: Dreams and Nightmares in Sam Altman's OpenAI [book]</title>
      <guid isPermaLink="false">empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai</guid>
      <link>https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai</link>
      <pubDate>Mon, 06 Oct 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I'd recommend anyone who wants to understand the emergence of AI and OpenAI in particular to read this book.
I do struggle reading non-fiction, and this book was no different. It took me nearly 2 months to finish reading (in my evening snippets) - but it was definitely worthwhile. It was this 90 minute interview by Novara Media with Karen Hao that made me immediately purchase the book.
I don't think I can fully review this book and do it justice, but I can share what I learnt from the book where my original assumptions were wrong about AI and AI companies.
The book is focused primarily around OpenAI and Sam Altman, and in particular how his ousting in November 2023 came to be, through hundreds of interviews and documents, and paints a very insightful picture of the messiah complex (though not in her words) that Altman has.
Here's a short summary of things I didn't know but learnt through reading this:

Altman and friends started conversations (and business, albeit initially as a non-profit, though that didn't last) in 2015 because they were going to build AGI and that it was inevitable.
The company was formed with scientific researchers originally, again as an open company with the intent of sharing, though spoiler alert: this all changed
I had always assumed it was cowboy bro developers working on the code, but it was, originally, academic based engineering
AI safety was constantly there and initially significant - but as we all know now, eventually lost a battle to make an impact, left &quot;hobbled&quot; and thrown rather to the wayside.
The training data was, after GPT-2, ingested wholesale and attempt to clean/sanitise would happen on the results coming from prompts - i.e. the inputs were not cleaned, which means applying dizzying array of filters to catch on the output and edge cases.
Common Crawl was introduced at GPT-3 - which is also where the input filtering stopped happening
AI, or Western AI companies including OpenAI (but also Google and Microsoft) put their data centres in the Global South, additionally sourcing their data annotators from the poorest countries allowing them to pay (via third parties) literal pennies per hour for the work (which could also come with terrible mental health side effects as the worker would read and view the generative content that AI could come up with based on the unfiltered dark corners of the web)
Sam Altman lies. Little lies, but from a great deal of documentation, a lot and often to tell people what they want to hear whilst (we guess?!) having some ulterior motive
The path that OpenAI decided to take to head towards what they believe will be AGI, effectively requires unlimited compute power, when in reality, there are lots of different applications of AI that don't need that level of power, Stable Diffusion being one such example trained using 256 GPUs (still not a desktop computer, but not hundreds of thousand GPUs either)
OpenAI's approach, to close off it's scientific findings, close it's source and refusing to share methods means that there's no way to verify any of their progress, but more importantly is stripping the academic scientific community of it's researchers (as someone who has visited CERN on two occassions, seeing science being shared is incredible and incredible for society)

My only complaint about the book (and it's likely to be my own fault) is I had trouble with the jumping backwards and forwards in time - I'd often be unsure where we were in the timeline.
If you work in tech, I'd absolutely recommend this book. If it's not possible, then definitely the interview I linked above.]]></description>
      <content:encoded><![CDATA[<p>I'd recommend anyone who wants to understand the emergence of AI and OpenAI in particular to read this book.</p>
<p>I do struggle reading non-fiction, and this book was no different. It took me nearly 2 months to finish reading (in my evening snippets) - but it was definitely worthwhile. It was <a href="https://www.youtube.com/watch?v=8enXR...">this 90 minute interview</a> by Novara Media with Karen Hao that made me immediately purchase the book.</p>
<p>I don't think I can fully review this book and do it justice, but I can share what I learnt from the book where my original assumptions were wrong about AI and AI companies.</p>
<p>The book is focused primarily around OpenAI and Sam Altman, and in <a href="https://www.bbc.co.uk/news/business-6...">particular how his ousting in November 2023</a> came to be, through hundreds of interviews and documents, and paints a very insightful picture of the messiah complex (though not in her words) that Altman has.</p>
<p>Here's a short summary of things I didn't know but learnt through reading this:</p>
<ol>
<li>Altman and friends started conversations (and business, albeit initially as a non-profit, though that didn't last) in 2015 because they were <em>going</em> to build AGI and that it was inevitable.</li>
<li>The company was formed with scientific researchers originally, again as an open company with the intent of sharing, though spoiler alert: this all changed</li>
<li>I had always assumed it was cowboy bro developers working on the code, but it was, originally, academic based engineering</li>
<li>AI safety was constantly there and initially significant - but as we all know now, eventually lost a battle to make an impact, left &quot;hobbled&quot; and thrown rather to the wayside.</li>
<li>The training data was, after GPT-2, ingested wholesale and attempt to clean/sanitise would happen on the results coming from prompts - i.e. the inputs were not cleaned, which means applying dizzying array of filters to catch on the output and edge cases.</li>
<li>Common Crawl was introduced at GPT-3 - which is also where the input filtering stopped happening</li>
<li>AI, or Western AI companies including OpenAI (but also Google and Microsoft) put their data centres in the Global South, additionally sourcing their data annotators from the poorest countries allowing them to pay (via third parties) literal pennies per hour for the work (which could also come with terrible mental health side effects as the worker would read and view the generative content that AI could come up with based on the unfiltered dark corners of the web)</li>
<li>Sam Altman lies. Little lies, but from a great deal of documentation, <em>a lot</em> and often to tell people what they want to hear whilst (we guess?!) having some ulterior motive</li>
<li>The path that OpenAI decided to take to head towards what they believe will be AGI, effectively requires unlimited compute power, when in reality, there are lots of different applications of AI that don't need that level of power, Stable Diffusion being one such example trained using 256 GPUs (still not a desktop computer, but not hundreds of thousand GPUs either)</li>
<li>OpenAI's approach, to close off it's scientific findings, close it's source and refusing to share methods means that there's no way to verify any of their progress, but more importantly is stripping the academic scientific community of it's researchers (as someone who has visited CERN on two occassions, seeing science being shared is incredible and incredible for society)</li>
</ol>
<p>My only complaint about the book (and it's likely to be my own fault) is I had trouble with the jumping backwards and forwards in time - I'd often be unsure where we were in the timeline.</p>
<p>If you work in tech, I'd absolutely recommend this book. If it's not possible, then definitely the interview I linked above.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/empire-of-ai-dreams-and-nightmares-in-sam-altmans-openai">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Making a low-budget conference look high-budget using an ATEM video mixer and a long USB cable [link]</title>
      <guid isPermaLink="false">2025-10-05-22673538</guid>
      <link>https://remysharp.com/links/2025-10-05-22673538</link>
      <pubDate>Sun, 05 Oct 2025 22:28:52 +0000</pubDate>
      <description><![CDATA[Niels Leenheer goes through a detailed setup on how to take a single HDMI to projection into a mini event setup with holding screens. The technical detail is superb too, specifically considering power draw you would need but also, importantly, the length of cables and types to handle data rates. Definitely one for the back pocket read when wanting to upgrade the meetup game to something that has a lot more polish.
Source: nielsleenheer.com]]></description>
      <content:encoded><![CDATA[
<p>Niels Leenheer goes through a detailed setup on how to take a single HDMI to projection into a mini event setup with holding screens. The technical detail is superb too, specifically considering power draw you would need but also, importantly, the length of cables and types to handle data rates. Definitely one for the back pocket read when wanting to upgrade the meetup game to something that has a lot more polish.</p>
<p><em>Source: <a href="https://nielsleenheer.com/articles/2025/making-a-low-budget-conference-look-high-budget-using-an-atem-video-mixer-and-a-long-usb-cable/">nielsleenheer.com</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-10-05-22673538">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>CSS HDR Gradients [link]</title>
      <guid isPermaLink="false">2025-10-03-8c6febb9</guid>
      <link>https://remysharp.com/links/2025-10-03-8c6febb9</link>
      <pubDate>Fri, 03 Oct 2025 07:36:20 +0000</pubDate>
      <description><![CDATA[Very cool CSS gradient tooling by Adam Argyle.It's so pretty all I want to do is play with UI forgetting that it'll actually give me CSS.I also like that the URL is sharable and savable (perhaps through a bookmark or in a markdown doc).His latest changes include:- better import design- more resilient parser (understands more syntax)- supports multi-layered gradient imports
Source: gradient.style]]></description>
      <content:encoded><![CDATA[
<p>Very cool CSS gradient tooling by Adam <a href="http://Argyle.It">Argyle.It</a>'s so pretty all I want to do is play with UI forgetting that it'll actually give me CSS.I also like that the URL is sharable and savable (perhaps through a bookmark or in a markdown doc).His latest changes include:- better import design- more resilient parser (understands more syntax)- supports multi-layered gradient imports</p>
<p><em>Source: <a href="https://gradient.style/">gradient.style</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-10-03-8c6febb9">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Signal Pollution [blog]</title>
      <guid isPermaLink="false">signal-pollution</guid>
      <link>https://remysharp.com/2025/09/25/signal-pollution</link>
      <pubDate>Thu, 25 Sep 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Very recently I was forced to sign up to Meta due to a product purchase (don't at-me!) and I had forgotten what it was like to be part of the algorithms. Our entire family browse the internet (the web and internet) from behind a DNS proxy that blocks a lot of social media including Facebook/Meta/Insta/whatever it's actually called.]]></description>
      <content:encoded><![CDATA[
<p>Very recently I was forced to sign up to Meta due to a product purchase (don't at-me!) and I had forgotten what it was like to be part of the algorithms. Our entire family browse the internet (the web <em>and</em> internet) from behind a DNS proxy that blocks a lot of social media including Facebook/Meta/Insta/whatever it's actually called.</p>
<p>Unblocking these different services just to get a physical product to work (naively I thought it worked mostly offline) felt like I was exposing myself to a pretty gross network.</p>
<p>Then had to make an account for my child, whom if there were over the age of 10 and under the age of 13, could be managed under my account. Once they turned 13 (in the eyes of Meta) they were completely unrestricted (which I've got A LOT to say about, but I digress).</p>
<p>This combined with having already having to lift the protection I had in place was too much. I quickly deleted both accounts and parked the problem for the day.</p>
<p>After some reflection, I decided that I would create a single account, that we'd all share that way at least I could monitor all activity <em>and</em> the data Meta collected would be (hopefully) extremely mixed due to different people using the account.</p>
<hr>
<p>Which reminded me of a story I heard about <em>years</em> ago where villagers in some areas of Africa (and likely other places in the Global South) were sharing a single device to visit Facebook and the like, which made it nigh impossible for algorithms to profile the user and made targetting and ads effectively useless.</p>
<p><strong>Why don't we do this now?</strong> Why don't we share accounts to large social media sites to pollute the signal we're putting out?</p>
<p>I understand if you want to create an account so you can build your brand or post what you're up to, this doesn't really work. But if you're visiting these sites to keep in touch with your communities and post short comments or questions - then it seems to me that this is a viable solution.</p>
<p>If I were to propose implementing this, it would be that each shared-user would have 10-20 real people using the account, I can see how it might be chaotic with more people - plus there's an aspect of trust required in your group. If someone decides to change the password you're all of a sudden blown up.</p>
<p>Funnily enough, I've written about this same thought some <a href="https://remysharp.com/2019/06/27/a-thought-privacy-pollution">6 years ago</a>, prompted by this fun <a href="https://trackthis.link/">pollution tool</a>.</p>
<p>What do you think? What downsides am I missing here?</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/09/25/signal-pollution">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Fifteen [blog]</title>
      <guid isPermaLink="false">fifteen</guid>
      <link>https://remysharp.com/2025/08/30/fifteen</link>
      <pubDate>Sat, 30 Aug 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I'd been waiting for the grief to find me. I wasn't actively looking for it, I know which memories to poke to feel real pain, but I wanted to create space for it to find me, and throughout the month of August, this year, it couldn't find me. Until today. 30th August. This day is the knife edge. The day, 15 years ago, that Tia still had a heartbeat, still kicked, was on her way. Julie was in (long) labour. On this same day, at some point, her heart gave out, she died before she could take her first breath and the midwives had to tell us that they couldn't find that heartbeat any more. Julie was in labour, so  Tia was coming. Except that her delivery at 3am on 31st August would be the other side of our lives. The side we live on today: our derailed and rebuilt lives that exist in now.]]></description>
      <content:encoded><![CDATA[<p>I'd been waiting for the grief to find me. I wasn't actively looking for it, I know which memories to poke to feel real pain, but I wanted to create space for it to find me, and throughout the month of August, this year, it couldn't find me. Until today. 30th August. This day is the knife edge. The day, 15 years ago, that <a href="https://remysharp.com/search?q=tia">Tia</a> still had a heartbeat, still kicked, was on her way. Julie was in (long) labour. On this same day, at some point, her heart gave out, she died before she could take her first breath and the midwives had to tell us that they couldn't find that heartbeat any more. Julie was in labour, so  Tia was coming. Except that her delivery at 3am on 31st August would be the other side of our lives. The side we live on today: our derailed and rebuilt lives that exist in now.</p>
<p>So today, the 30th of August, grief wakes me up in the morning. It presses hard downwards on me. And I need it, even if it's just for a day.</p>
<p>15 years didn't fix us, or heal us, <a href="https://remysharp.com/2014/08/11/time-doesnt-heal">time doesn't do that</a>, but it does allow for new memories. For more memories. For life to fill in the space. It's that time, and that life, and our amazing two children who create the life and family we needed. It keeps us busy and it keeps us alive.</p>
<p>We only got one day with Tia, to hold her. Then we had to leave her. Alone in the hospital. That's my memory that's fraught with pain and longing, and all kinds of complicated feelings. For a long time, in those early years, it was a dark memory, a memory I hated myself for, for leaving her. It took a year of therapy to put light into that room again, to not cast judgement on myself, to leave the memory as it is. Just that one day. The one day that she was real.</p>
<p>But that's why I write these post each year. It's why I write this for myself, and in part to share with you. Tia is a memory to me, but she was real. I have to write about that to bring her existence into reality, into the world I still inhabit.</p>
<hr>
<p>I was worried this year I wouldn't find the words, that the grief wouldn't find me. I know that there's an instinct in me to shove the grief away, to busy myself, but I also know that every other day of the year can do that with no effort at all. The little one starting secondary, the big one starting his options, new achievements, new problems, new arguments, new memories. So, I'll try my best to sit in the grief today. Share it with Julie, and try to be gentle on ourselves.</p>
<p>And I'll remember Tia today.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/08/30/fifteen">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>In Bloom (Sweetpea, #2) [book]</title>
      <guid isPermaLink="false">in-bloom-sweetpea-2</guid>
      <link>https://remysharp.com/books/2025/in-bloom-sweetpea-2</link>
      <pubDate>Tue, 12 Aug 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[No quite the same murderfest from Sweetpea 1, but still enjoyably angry at the world.
I think I expected the same kind of murder spree from the first book, and it's not that at all (although Rhiannon, the protagonist, desperately wants that). It's actually written on the tin, In Bloom is Rhiannon's experience of being pregnant - which, for a change, rolls up all the shitty experiences into one.
I enjoyed, and laughed at, the cutting turn of phrases - the kind of brutal honesty that you might reserve for the closest of friends knowing that it's for the &quot;inside voice&quot;.
I can see myself reading the next instalment in the book (though perhaps not so soon) and I'm not entirely sure where the character can go given the way the book ends, so that's definitely intriguing.
But just for the the record (my record!), Rhiannon is a terrible person!]]></description>
      <content:encoded><![CDATA[<p>No quite the same murderfest from Sweetpea 1, but still enjoyably angry at the world.</p>
<p>I think I expected the same kind of murder spree from the first book, and it's not that at all (although Rhiannon, the protagonist, desperately wants that). It's actually written on the tin, In Bloom is Rhiannon's experience of being pregnant - which, for a change, rolls up all the shitty experiences into one.</p>
<p>I enjoyed, and laughed at, the cutting turn of phrases - the kind of brutal honesty that you might reserve for the closest of friends knowing that it's for the &quot;inside voice&quot;.</p>
<p>I can see myself reading the next instalment in the book (though perhaps not so soon) and I'm not entirely sure where the character can go given the way the book ends, so that's definitely intriguing.</p>
<p>But just for the the record (my record!), Rhiannon is a terrible person!</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/in-bloom-sweetpea-2">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Getting my highlights &amp; notes from KOReader [blog]</title>
      <guid isPermaLink="false">getting-my-highlights-and-notes-from-koreader</guid>
      <link>https://remysharp.com/2025/07/22/getting-my-highlights-and-notes-from-koreader</link>
      <pubDate>Tue, 22 Jul 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[It's not an intuitive process and requires a few speciality commands to work, so it made sense that I write up the process so I can duck myself later on.]]></description>
      <content:encoded><![CDATA[
<p>It's not an intuitive process and requires a few speciality commands to work, so it made sense that I write up the process so I can <a href="https://duckduckgo.com/?q=site%3Aremysharp.com+kindle&amp;t=newext">duck myself</a> later on.</p>
<p>Since I'm getting my <a href="https://remysharp.com/2025/05/01/showing-book-clippings-on-my-blog">Amazon Kindle book highlights</a> already but am also actively trying to buy books from <a href="https://remysharp.com/2025/06/29/unhooking-from-amazon-ebooks">anywhere that isn't Amazon</a> it means my highlights are in KOReader.</p>
<p>There's a multi-step process that requires special command line tools <em>and</em> navigating KOReader into the &quot;right&quot; state to allow for syncing (I'm certain there's other ways to do this process, but I've not found it yet myself).</p>
<h2>1. Export the highlights</h2>
<p>Probably the simplest part, and if you're happy to connect the Kindle via a USB port, probably a lot easier to get the exported notes off (but I'm taking the wireless route, so it's trickier!).</p>
<p>Whilst the current book is open, open the &quot;tools&quot; menu (the spanner and screwdriver icon), then <em>Export highlights</em> and <em>Export all notes in current book</em>.</p>
<p>I've already selected the format as JSON and selected my export folder (arbitrarily my exports are going to <code>/mnt/us/koreader/clipboard</code>).</p>
<p>Now the file is waiting to be lifted to my computer.</p>
<h2>2. WebDAV</h2>
<p>From the machine I want to upload the JSON file to, I need to run a WebDAV server (a technology I've always known about but never really had an need or use for until now).</p>
<p>There's quite a few options for WebDAV servers, but I wanted something very lightweight and that I could run on the command line. I ended up using a <a href="https://github.com/hacdias/webdav">Go based server</a>, via <code>brew install webdav</code>.</p>
<p>Which ever server you use, you'll need to make sure it has write permissions. To run this go version in write, in the directory I want the JSON file uploaded to, I run:</p>
<pre><code class="language-sh"><span class="token assign-left variable">WD_PERMISSIONS</span><span class="token operator">=</span>CRUD webdav <span class="token parameter variable">-p</span> <span class="token number">8181</span>
</code></pre>
<p>This says:</p>
<ol>
<li>Run on port 8181</li>
<li>Run with create, read, update and delete permissions</li>
</ol>
<p>Once I'm done with the process, I then terminate the WebDAV server.</p>
<p>However, before I shut the server down, I need to upload my JSON notes from the Kindle.</p>
<h2>3. Uploading to WebDAV from KOReader</h2>
<p>This is where KOReader's UI gets clunky again. You need to use the &quot;Cloud Storage&quot; option.</p>
<p>Cloud Storage is <em>only</em> visible in the tools menu when you're in the file browser (not in the book reading mode).</p>
<p>Once the Cloud Storage is open, tap the plus icon on the top right, then fill out the details. Assuming the Kindle is on the same network as the computer (because it needs to be), the URL/host is <code>http://{IP}:8181</code> and the folder is just <code>/</code>.</p>
<p>Once this is saved, tap on the connection name. If the directory is empty, you'll see a new screen with no files. If there's any <code>.pub</code> ebooks, then those will be listed (and could be downloaded).</p>
<p>From this new screen, tap the top left plus icon to upload a file. Navigate to the file, and long press on the file to &quot;choose&quot; the file to be uploaded.</p>
<p>If all is successful, KOReader should say so. If it fails, it could be related to permissions. I had earlier luck with a <a href="https://wsgidav.readthedocs.io/en/latest/">python WebDAV server</a>.</p>
<h2>4. Transform</h2>
<p>The JSON structure is fairly simplistic, but I have this specific <a href="https://jqterm.com">jq transform</a> to get into the format I use:</p>
<pre><code class="language-jq"><span class="token keyword">def</span> <span class="token function">slug</span><span class="token punctuation">:</span>
  <span class="token keyword">ascii_downcase</span>
  <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"[^a-z0-9]+"</span><span class="token punctuation">;</span> <span class="token string">"-"</span><span class="token punctuation">)</span>
  <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"(^-|-$)"</span><span class="token punctuation">;</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token dot important">.</span> <span class="token operator">+</span>  <span class="token punctuation">{</span> <span class="token property-literal property">slug</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>title <span class="token operator pipe">|</span> slug <span class="token punctuation">}</span> <span class="token operator pipe">|</span> <span class="token punctuation">{</span>
  <span class="token property">"<span class="token interpolation"><span class="token punctuation">\(</span><span class="token content"><span class="token punctuation">.</span>slug</span><span class="token punctuation">)</span></span>"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    title<span class="token punctuation">,</span>
    author<span class="token punctuation">,</span>
    <span class="token property-literal property">highlights</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>entries <span class="token operator pipe">|</span> <span class="token keyword">map</span><span class="token punctuation">(</span><span class="token punctuation">{</span> text<span class="token punctuation">,</span> page<span class="token punctuation">,</span> note <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Alternatively, if I'm lifting the annotations (highlights) directly from Calibre, this works:</p>
<pre><code class="language-jq"><span class="token keyword">def</span> <span class="token function">clean</span><span class="token punctuation">:</span> <span class="token keyword">tostring</span> <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"\\\\\\n"</span><span class="token punctuation">;</span> <span class="token string">"\n\n"</span><span class="token punctuation">)</span> <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"[’‘]"</span><span class="token punctuation">;</span> <span class="token string">"'"</span><span class="token punctuation">;</span> <span class="token string">"g"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">def</span> <span class="token function">percent</span><span class="token punctuation">(</span><span class="token variable">$total</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token dot important">.</span> <span class="token operator">/</span> <span class="token variable">$total</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">;</span>

<span class="token keyword">def</span> <span class="token function">slug</span><span class="token punctuation">:</span>
  <span class="token keyword">ascii_downcase</span>
  <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"[^a-z0-9]+"</span><span class="token punctuation">;</span> <span class="token string">"-"</span><span class="token punctuation">)</span>
  <span class="token operator pipe">|</span> <span class="token keyword">gsub</span><span class="token punctuation">(</span><span class="token string">"(^-|-$)"</span><span class="token punctuation">;</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token dot important">.</span> <span class="token keyword">as</span> <span class="token variable">$_</span> <span class="token operator pipe">|</span> <span class="token punctuation">{</span> <span class="token property-literal property">slug</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>stats<span class="token punctuation">.</span>title <span class="token operator pipe">|</span> slug <span class="token punctuation">}</span> <span class="token operator pipe">|</span> <span class="token punctuation">{</span>
  <span class="token property">"<span class="token interpolation"><span class="token punctuation">\(</span><span class="token content"><span class="token punctuation">.</span>slug</span><span class="token punctuation">)</span></span>"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token property-literal property">title</span><span class="token punctuation">:</span> <span class="token variable">$_</span><span class="token punctuation">.</span>stats<span class="token punctuation">.</span>title<span class="token punctuation">,</span>
    <span class="token property-literal property">author</span><span class="token punctuation">:</span> <span class="token variable">$_</span><span class="token punctuation">.</span>stats<span class="token punctuation">.</span>authors<span class="token punctuation">,</span>
    <span class="token property-literal property">highlights</span><span class="token punctuation">:</span> <span class="token variable">$_</span><span class="token punctuation">.</span>annotations <span class="token operator pipe">|</span> <span class="token keyword">to_entries</span> <span class="token operator pipe">|</span> <span class="token keyword">map</span><span class="token punctuation">(</span><span class="token punctuation">.</span>value <span class="token operator pipe">|</span> <span class="token punctuation">{</span> <span class="token property-literal property">text</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>text <span class="token operator pipe">|</span> clean<span class="token punctuation">,</span> <span class="token property-literal property">page</span><span class="token punctuation">:</span> <span class="token string">"<span class="token interpolation"><span class="token punctuation">\(</span><span class="token content"><span class="token punctuation">.</span>pageno</span><span class="token punctuation">)</span></span>/<span class="token interpolation"><span class="token punctuation">\(</span><span class="token content"><span class="token variable">$_</span><span class="token punctuation">.</span>stats<span class="token punctuation">.</span>pages</span><span class="token punctuation">)</span></span>"</span><span class="token punctuation">,</span> <span class="token property-literal property">note</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">.</span>note <span class="token operator pipe">|</span> clean<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<hr>
<p>That's how I get my notes from non-Amazon bought books into my own blog.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/07/22/getting-my-highlights-and-notes-from-koreader">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Reasons to Be Cheerful [book]</title>
      <guid isPermaLink="false">reasons-to-be-cheerful</guid>
      <link>https://remysharp.com/books/2025/reasons-to-be-cheerful</link>
      <pubDate>Mon, 21 Jul 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Honestly not sure what I thought of the book…
I'm unsure how I got this on my to-read list. I think I was looking for funny books from the last few years. I didn't laugh once reading this book though. It was a sweet story, set in the 80s, but I'm not sure where the arc was.
The story is told from the perspective of Lizzie, an 18 year old young woman who moves out of home to work as an assistant to a dentist. The dentist himself is fairly sexist and xenophobic. The mother (of the protagonist) is … hard to describe. In short, she's a pain in Lizzie's arse - though she definitely loves her.
I think there's some part of flying the nest and finding your own feet, but Lizzie somehow ends up in a very similar place by the end of the book. There's times I was reading and thinking surely she's going to realise that she herself is actually a good dentist and will take that path, but nope, she ends up as a clothes store assistant, so … I don't know.
It was cute to have 80s nostalgia peppered throughout the book, the highlight being the salad spinner (which my own mother still owns and I definitely remember spinning it for fun).
I guess I was left with the sense of why did I read what I read?]]></description>
      <content:encoded><![CDATA[<p>Honestly not sure what I thought of the book…</p>
<p>I'm unsure how I got this on my to-read list. I think I was looking for funny books from the last few years. I didn't laugh once reading this book though. It was a sweet story, set in the 80s, but I'm not sure where the arc was.</p>
<p>The story is told from the perspective of Lizzie, an 18 year old young woman who moves out of home to work as an assistant to a dentist. The dentist himself is fairly sexist and xenophobic. The mother (of the protagonist) is … hard to describe. In short, she's a pain in Lizzie's arse - though she definitely loves her.</p>
<p>I think there's some part of flying the nest and finding your own feet, but Lizzie somehow ends up in a very similar place by the end of the book. There's times I was reading and thinking surely she's going to realise that she herself is actually a good dentist and will take that path, but nope, she ends up as a clothes store assistant, so … I don't know.</p>
<p>It was cute to have 80s nostalgia peppered throughout the book, the highlight being the salad spinner (which my own mother still owns and I definitely remember spinning it for fun).</p>
<p>I guess I was left with the sense of <em>why did I read what I read</em>?</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/reasons-to-be-cheerful">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Vibe coding and Robocop [blog]</title>
      <guid isPermaLink="false">vibe-coding-and-robocop</guid>
      <link>https://remysharp.com/2025/07/18/vibe-coding-and-robocop</link>
      <pubDate>Fri, 18 Jul 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[I've been immersing myself in the AI news for the last few months, trying to get understanding of the landscape, what and why there's excitement, whether the ethics (both from a copyright and climate change perspective) are being considered, and what's the impact of my own usage.
In particular lately I've been looking at the idea of vibe coding. I've been playing with Claude Code which, as an experienced developer, is hard not to like (at time of writing!).
The short version of what I want to say is: vibe coding seems to live very squarely in the land of prototypes and toys. Promoting software that's been built entirely using this method would be akin to sending a hacked weekend prototype to production and expecting it to be stable.]]></description>
      <content:encoded><![CDATA[
<p>I've been immersing myself in the AI news for the last few months, trying to get understanding of the landscape, what and why there's excitement, whether the ethics (both from a copyright and climate change perspective) are being considered, and what's the impact of my own usage.</p>
<p>In particular lately I've been looking at the idea of vibe coding. I've been playing with <a href="https://docs.anthropic.com/en/docs/claude-code/overview">Claude Code</a> which, as an experienced developer, is hard <em>not</em> to like (at time of writing!).</p>
<p>The short version of what I want to say is: vibe coding seems to live very squarely in the land of prototypes and toys. Promoting software that's been built entirely using this method would be akin to sending a hacked weekend prototype to production and expecting it to be stable.</p>
<h2>Robocop</h2>
<p>Perhaps because I watched it when I was way too young to, but the original (1987) Robocop always pops into my mind when I think about unvetted coded going into a production level environment. Specifically Ed 209 being the prototype with the odd &quot;glitch&quot; (if you want an idea, here's a <a href="https://www.youtube.com/watch?v=TYsulVXpgYg">NSFW clip, heavy on the squibs</a>).</p>
<p>Robocop however was the version of the machine that had a human at the core to consider the protocols and determine the outcomes.</p>
<figure><img src="https://remysharp.com/images/robocop.jpg" alt="Ed 209 with the words &quot;Vibe Coding&quot; above it, and Robocop with &quot;Human Experience&quot; above it" decoding="async"></figure>
<p>Without Ed 209, there would be no Robocop, and in the world of Robocop, Robocop is (apparently) a good thing for society (which, going by Robocop 3, has rather gone to shit).</p>
<p>That's to say, in an analogy that I've rather stretched: prototypes entirely belong as part of the process.</p>
<h2>Vibe coding for toys</h2>
<p>If you're following AI it's likely you're following Simon Willison's work. He talks about using <a href="https://simonwillison.net/tags/vibe-coding/">vibe coding for creating tools and toys</a> (note this was linked July 2025).</p>
<p>I've used it myself to solve really bespoke problems where the user count is one. Most recently a tool I'd written years ago to manage our tax accounts and calculated all the VAT from Stripe stopped working (because I didn't maintain it and eventually all the libraries stopped working).</p>
<p>I turned to Claude Code and was able to create an extremely simple, but effective, single page app that would group the Stripe data (as we needed) and gave us the income and VAT that needed to be paid for the quarter. Then, again using Claude, I wrapped it up in <a href="https://tauri.app/">Tauri</a> so it could like on mine and Julie's computer (and <em>not</em> on the web) and it does the work. It took around 1-2 hour of gently giving direction to Claude Code and cost me around $5 of API tokens.</p>
<p>I can also tell you that this tool that was generated doesn't have tests. It's not accessible. It's not semantic, and it's certainly not written to the standard that I set for myself. However, that wasn't the aim, the aim was to get something working.</p>
<p>Would I put this out to production: absolutely not.</p>
<p>Though, perhaps I'm just a snowflake, because VCs are happy to throw money at anything that's popular in the moment. <a href="https://www.forbes.com/sites/iainmartin/2025/07/17/ai-vibe-coder-lovable-is-swedens-latest-unicorn/">Lovable, that has a $1.8B valuation</a> does exactly this: chat prompt =&gt; product for it's users. But then, VC culture has always been a weird thing to me.</p>
<h2>Consideration</h2>
<p>The problems I still don't have answers (even just for myself) to square away the rampant theft of content and the unprecedented energy consumption (along with data centers being built that take <a href="https://www.bbc.co.uk/news/articles/cy8gy7lv448o">priority over who gets the water</a>).</p>
<p>My concerns also sit with those people starting development careers. It's partly: if vibe, or AI assisted coding is all you learn, what happens when that generation of developers mature? This <em>feels</em> like something that might resolve itself (I'd love to see some research on this though), but moreover, money is required for vibe coding.</p>
<p>The costs of this kind of development cycle isn't really visible (yet). When you consider that junior developers or students won't have a lot of floating/free cash (if any), though I appreciate it's &quot;only&quot; cost me $5 to make the Stripe VAT tool, I've also spent quite a bit more on subscriptions and API costs before I got to that point.</p>
<p>I've got more to explore here, but I needed to get the Ed 209 vs. Robocop off my chest (albeit and excuse to actually use the analogy). Now, if you've not, go watch Robocop, it's peak late-80s over the top action (and probably violence, but in a way that makes you go - ew - see the &quot;I'm <em>melting</em>&quot; scene!).</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/07/18/vibe-coding-and-robocop">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Unhooking from Amazon ebooks [blog]</title>
      <guid isPermaLink="false">unhooking-from-amazon-ebooks</guid>
      <link>https://remysharp.com/2025/06/29/unhooking-from-amazon-ebooks</link>
      <pubDate>Sun, 29 Jun 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Over the years we, as a family, have been moving our purchases away from Amazon, except in one single place: Kindle ebooks. For me it's that I'm incapable of reading physical books (but my kindle unlocked my reading), and with a Kindle, I was limited as to where I buy my books.
When I read that it was relatively easy to jailbreak all the Kindle models I used this as my opportunity to move to buying epub books and hopefully more of that money goes to the authors (in an ideal world…).
Here's how it's going so far. In short: not quite as well as I'd like.]]></description>
      <content:encoded><![CDATA[
<p>Over the years we, as a family, have been moving our purchases away from Amazon, except in one single place: Kindle ebooks. For me it's that I'm incapable of reading physical books (<a href="https://remysharp.com/2021/12/14/how-i-relearnt-to-read">but my kindle unlocked my reading</a>), and with a Kindle, I was limited as to where I buy my books.</p>
<p>When I read that it was relatively easy to <a href="https://kindlemodding.org/jailbreaking/">jailbreak all the Kindle models</a> I used this as my opportunity to move to buying epub books and hopefully more of that money goes to the authors (in an ideal world…).</p>
<p>Here's how it's going so far. In short: not <em>quite</em> as well as I'd like.</p>
<h2>Jailbreaking</h2>
<p>I won't cover the process here, but it wasn't too difficult. You have to properly identify your model (which is tricky enough), and then carefully follow the step by step process. It's lots of reading, clicking, double checking and waiting. The Kindle needs to be connected to the computer doing the work, it's tedious but gives you more features.</p>
<p>Importantly (for me) was that the full original system is intact. The jailbreak process adds a &quot;book&quot; (app) called KUAL which is the doorway to the custom e-reader - specifically I installed <a href="https://koreader.rocks/">KOReader</a> (though I've no idea if there's other readers available!).</p>
<h2>KOReader</h2>
<p>This is the app that will read the epub format (and most other e-book formats). A little frustrating that the Amazon purchased books are read using the original Kindle reader (they're the <a href="https://en.m.wikipedia.org/wiki/Kindle_File_Format">AZW3</a> ebook format - which has Amazon's DRM protection).</p>
<p>The UI is…not entirely intuitive. I spent probably the course of a week messing around with different settings, losing where the menu options were, being unsure why there's two types of config panels and completely missing the pagination on the settings. Eventually I found that there's a &quot;Menu Search&quot; (in the right hand side hamburger menu in &quot;Help&quot;).</p>
<p>There's some nice features for when the Kindle is turned off, besides having the book cover, I've got it set to show the cover, title, how long I've been reading it for and my current progress (search for &quot;wallpaper&quot;). I particularly like the &quot;book map&quot; feature (though, I can't say I visit often):</p>
<figure><img src="https://remysharp.com/images/koreader.jpg" alt="A complete map of the book, showing sections, chapters, progress and highlights I've made" decoding="async"></figure>
<h2>Getting ebooks / not giving money to Amazon</h2>
<p>This is really the draw for me. The short version is: this is not straightforward in the slightest, and Amazon have done a pretty good job of making the user experience pretty smooth (I believe, but don't have personal experience, that a Kobo reader has a similar workflow).</p>
<p>With Amazon, I can find the book I want using my phone, buy it, and sync my Kindle and it's ready to be read. I've managed to do this abroad even without wifi because I can hotspot my Kindle to my phone to pick up the new book.</p>
<p>There's two problems to overcome to get this kind of experience:</p>
<ol>
<li>Automatic syncing of newly purchased books</li>
<li>DRM stripping</li>
</ol>
<p>There's a few ways to handling syncing, none of them entirely straight forward.</p>
<p>If you're familiar with <a href="https://calibre-ebook.com/">Calibre</a>, From here there's a KOReader plugin that your Kindle can connect to and &quot;send to main&quot; will send the book to KOReader - though this is obviously not automated.</p>
<p>There is a &quot;cloud sync&quot; option in KOReader that can connect to Dropbox (and FTP if you like). This could get books across, but the biggest issue I came up against is the DRM removal.</p>
<p>DRM stripping can all be done in Calibre, but through my exploration, I couldn't find a way to automate this, even from the command line.</p>
<p>From my (little) experience, there's two parts to the DRM removal (so your bought book can be read on KOReader):</p>
<ol>
<li>The book comes in a .ascm format - a pointer file that is serviced by <a href="https://www.adobe.com/uk/solutions/ebook/digital-editions.html">Adobe Digital Editions</a> which only then gives you a DRM encoded epub (or PDF).</li>
<li>Once I've got the epub, I can use <a href="https://github.com/apprenticeharper/DeDRM_tools">Calibre's DeDRM tools</a> so that my book is finally portable.</li>
</ol>
<p>The problem with all this is that it requires that you drive the software to do the work.</p>
<p>I did find a <a href="https://github.com/Leseratte10/acsm-calibre-plugin">DeASCM plugin</a> which worked on the Adobe Digital Editions sample book, but didn't work on the <a href="https://www.goodreads.com/book/show/38739384-in-bloom">book I had bought</a> from Google's store (though it <em>had</em> successfully fulfilled the DRM fulfilment, but just couldn't get the epub out).</p>
<p>So the workflow I have is clunky, and definitely not compatible if I'm away from home reading a book (and it's usually holidays that I'll impulse buy a book).</p>
<h2>The other downsides</h2>
<p>There's a few other factors against me when trying to cut Amazon out of the chain of suppliers:</p>
<ol>
<li>Amazon have a near monopoly on ebooks. Okay, maybe not quite, but every book publisher I've visited since jailbreaking my Kindle has pointed me (often exclusively) to Amazon to buy their book.</li>
<li>Amazon have deals on books all the time - which, if I'm honest with myself, is appealing (because I save money) but almost certainly doesn't help the authors.</li>
<li>As much as <a href="https://www.goodreads.com/author/show/4048781.Remy_Sharp">Goodreads</a> sucks, I do use the &quot;mark as reading&quot; and have pulled <a href="https://remysharp.com/2025/05/01/showing-book-clippings-on-my-blog">my book highlights</a> from their service into my blog. I do have a work around for KOReader, but again it's even more manual.</li>
<li>Fiction (or the fiction I read) is almost always in this DRM-land (and I'm slow AF reading non-fiction, so I don't do it often)</li>
</ol>
<p>Hopefully I can revisit this process in the coming years and there will be some process to help me streamline the work.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/06/29/unhooking-from-amazon-ebooks">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Sweetpea (Sweetpea, #1) [book]</title>
      <guid isPermaLink="false">sweetpea-sweetpea-1</guid>
      <link>https://remysharp.com/books/2025/sweetpea-sweetpea-1</link>
      <pubDate>Sat, 28 Jun 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Funny, unwavering and dark.
Rhiannon, Sweetpea, is according to BuzzFeed, a serial killer - and probably a psychopath. For the first half of the book I just read it as she's a bit crazy and has decided to act on her impulses.
Except as I carried on reading, I realised that what she was doing was listing the things that piss her off all the time and just landed at the inevitable conclusion that the only way to get rid of these things is to get rid of these things. Not unlike a lot of us except we don't go all the the way to fully extinguishing someone.
There were some really funny turns of phrases throughout the book and I just wondered how I could possibly end but suspected it would end the way it did which isn't a bad thing. I definitely plan to read a few more of these books in a series.
Graphic, explicit, raw, quite angry (and annoyed), funny. Good stuff!]]></description>
      <content:encoded><![CDATA[<p>Funny, unwavering and dark.</p>
<p>Rhiannon, Sweetpea, is according to BuzzFeed, a serial killer - and probably a psychopath. For the first half of the book I just read it as she's a bit crazy and has decided to act on her impulses.</p>
<p>Except as I carried on reading, I realised that what she was doing was listing the things that piss her off all the time and just landed at the inevitable conclusion that the only way to get rid of these things is to get rid of these things. Not unlike a lot of us except we don't go all the the way to fully extinguishing someone.</p>
<p>There were some really funny turns of phrases throughout the book and I just wondered how I could possibly end but suspected it would end the way it did which isn't a bad thing. I definitely plan to read a few more of these books in a series.</p>
<p>Graphic, explicit, raw, quite angry (and annoyed), funny. Good stuff!</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/sweetpea-sweetpea-1">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Radleys [book]</title>
      <guid isPermaLink="false">the-radleys</guid>
      <link>https://remysharp.com/books/2025/the-radleys</link>
      <pubDate>Wed, 18 Jun 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[An easy read, good for the holiday.
I chomped this down in a matter of days (though it helped being by the pool to spend more time reading). I've read a number of Matt Haig's books and find I prefer his older material over the newer books, and this book fitted nicely into that category.
Without examining the story too carefully (because I think the balance of humour and darkness, in retrospect, is a little off balance and the wheels sort of come off!) - it was a fun read.
Definitely reading the book in 2025 rather than the years which the Twilight stories were at their height probably makes the story a little more refreshing for me (it's about a British vampire family - not a spoiler, you'll get that from the blurb).
The characters were fairly simple to follow and switch between on each (nicely short) chapter. I didn't really feel much sympathy for the parent characters (I'm not sure if I was supposed to, but like I said, it doesn't want too close a look), but there's some fun twists on the vampire genre (or perhaps I'm not that familiar with the genre!).
Some fun gory bits, a decent mini twist here and there, very easy to read - a good choice for the holiday read.]]></description>
      <content:encoded><![CDATA[<p>An easy read, good for the holiday.</p>
<p>I chomped this down in a matter of days (though it helped being by the pool to spend more time reading). I've read a number of Matt Haig's books and find I prefer his older material over the newer books, and this book fitted nicely into that category.</p>
<p>Without examining the story too carefully (because I think the balance of humour and darkness, in retrospect, is a little off balance and the wheels sort of come off!) - it was a fun read.</p>
<p>Definitely reading the book in 2025 rather than the years which the Twilight stories were at their height probably makes the story a little more refreshing for me (it's about a British vampire family - not a spoiler, you'll get that from the blurb).</p>
<p>The characters were fairly simple to follow and switch between on each (nicely short) chapter. I didn't really feel much sympathy for the parent characters (I'm not sure if I was supposed to, but like I said, it doesn't want too close a look), but there's some fun twists on the vampire genre (or perhaps I'm not that familiar with the genre!).</p>
<p>Some fun gory bits, a decent mini twist here and there, very easy to read - a good choice for the holiday read.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-radleys">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Spike Milligan: Man of Letters [book]</title>
      <guid isPermaLink="false">spike-milligan-man-of-letters</guid>
      <link>https://remysharp.com/books/2025/spike-milligan-man-of-letters</link>
      <pubDate>Mon, 16 Jun 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[A book of letters…kinda made me want to write letters…
I picked this up based on &quot;funny books&quot; though I'm really not sure how I landed on this book. I know, sort of, Spike Milligan's work, but only from TV. I didn't know he was an author, particularly from funny war stories so I missing considerable context. Arguably I'm not sure why I read this book.
The back cover quotes appeared almost immediately at the start of the book and it was pretty clear that this read like a bottom-of-the-barrel book, just printing every letter that could be found. There was the odd occasion that the correspondence was funny, but unfortunately not laugh-out-loud-funny, just a bit of a snicker.
In the last part of the book (part 4: words of support), the letters are about mental health (dating back to the late-60s), which was interesting to read as it feels like in the real world we've only started to talk openly about mental health in the last decade or so. Milligan would write letters offering support to others, and would often talk about how the NHS support was missing the love and understanding and rather delivered pills (which he says helps, but only as a band-aid).
It was these last group of letters, mostly about sending out love and positivity to the world that brought this up from 2 stars to 3. It definitely made me want to actually write words down to people rather than the usual email, and it was nice to see some silly humour throughout his decades.]]></description>
      <content:encoded><![CDATA[<p>A book of letters…kinda made me want to write letters…</p>
<p>I picked this up based on &quot;funny books&quot; though I'm really not sure how I landed on this book. I know, sort of, Spike Milligan's work, but only from TV. I didn't know he was an author, particularly from funny war stories so I missing considerable context. Arguably I'm not sure why I read this book.</p>
<p>The back cover quotes appeared almost immediately at the start of the book and it was pretty clear that this read like a bottom-of-the-barrel book, just printing every letter that could be found. There was the odd occasion that the correspondence was funny, but unfortunately not laugh-out-loud-funny, just a bit of a snicker.</p>
<p>In the last part of the book (part 4: words of support), the letters are about mental health (dating back to the late-60s), which was interesting to read as it feels like in the real world we've only started to talk openly about mental health in the last decade or so. Milligan would write letters offering support to others, and would often talk about how the NHS support was missing the love and understanding and rather delivered pills (which he says helps, but only as a band-aid).</p>
<p>It was these last group of letters, mostly about sending out love and positivity to the world that brought this up from 2 stars to 3. It definitely made me want to actually <em>write</em> words down to people rather than the usual email, and it was nice to see some silly humour throughout his decades.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/spike-milligan-man-of-letters">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Left Hand of Darkness [book]</title>
      <guid isPermaLink="false">the-left-hand-of-darkness</guid>
      <link>https://remysharp.com/books/2025/the-left-hand-of-darkness</link>
      <pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[300 pages but wow it felt 4 times longer...
I found the writings quite difficult to consume. A lot, if not all the names were sounds but without any familiar rhythm, and Le Guin was describing, with I assume great accuracy a world with locations, cities, regions and mountains - but I was struggling to map it all.
Then the story was thread through this detail, which left me struggling to read and struggling to enjoy the book.
It's only when I hit around 60% and the two characters embark on a journey across the ice did I finally manage to engage properly. I really did feel the characters relationship change through their journey and felt their struggle, which I think is what turned this book, for me, from 2 star to 3.
There's a scene towards the end when the protagonist revisits the King of {whatever the name of the city was}, and the protagonist reflects on how the time has passed since his first visit much earlier in the book. I could empathise! It felt like a lifetime!
Probably a great book. Maybe passed me by though.]]></description>
      <content:encoded><![CDATA[<p>300 pages but wow it felt 4 times longer...</p>
<p>I found the writings quite difficult to consume. A lot, if not all the names were sounds but without any familiar rhythm, and Le Guin was describing, with I assume great accuracy a world with locations, cities, regions and mountains - but I was struggling to map it all.</p>
<p>Then the story was thread through this detail, which left me struggling to read and struggling to enjoy the book.</p>
<p>It's only when I hit around 60% and the two characters embark on a journey across the ice did I finally manage to engage properly. I really did feel the characters relationship change through their journey and felt their struggle, which I think is what turned this book, for me, from 2 star to 3.</p>
<p>There's a scene towards the end when the protagonist revisits the King of {whatever the name of the city was}, and the protagonist reflects on how the time has passed since his first visit much earlier in the book. I could empathise! It felt like a lifetime!</p>
<p>Probably a great book. Maybe passed me by though.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-left-hand-of-darkness">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>AI: did you check your work? [blog]</title>
      <guid isPermaLink="false">ai-did-you-check-your-work</guid>
      <link>https://remysharp.com/2025/05/31/ai-did-you-check-your-work</link>
      <pubDate>Sat, 31 May 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[There's no denying that the web industry, as with many others, has AI and LLMs as a ubiquitous presence. There's all kinds of different uses for LLMs, and comes with all the ethical concerns - ignored or perhaps at the heart of your use.
More recently &quot;vibe coding&quot; has me…wary.]]></description>
      <content:encoded><![CDATA[
<p>There's no denying that the web industry, as with many others, has AI and LLMs as a ubiquitous presence. There's all kinds of different uses for LLMs, and comes with all the ethical concerns - ignored or perhaps at the heart of your use.</p>
<p>More recently &quot;vibe coding&quot; has me…wary.</p>
<p>Very recently I had a fairly simple, but dull task where I had to extract the number of lessons from a log (which read &quot;23 lessons&quot; etc) and add them up.</p>
<p>This on the surface would seem like a perfect match for an LLM. Give it the log, describe the problem and the bot spits out a single number.</p>
<p>I threw this into chatgpt and it confidently (as always) gave me a number.</p>
<p>A number, that just didn't feel right at all. I already had a sense of where the result should be, and chatgpt gave me a number that was way too low.</p>
<p>Intrigued, I decided to repeat the exact same request to a number of other models, including: Gemini, Claude, multiple OpenAI models, and a couple of offline models.</p>
<p>All, except one got the wrong answer, each having wildly different values. They weren't just a bit off, they were simply wrong. Then the problem with <em>one</em> being right, unless you know the answer ahead of time, how do you tell?</p>
<figure><img src="https://remysharp.com/images/ai-counting.jpg" alt="Multiple LLMs showing different answers to the same question" decoding="async"></figure>
<p>The actual answer took me a few minutes to code up and I trust my skills for a particularly simple task. But I didn't start by coding it myself because it took less than 30 seconds to describe the problem in written English, and I <em>thought</em> these are the kinds of tasks perfectly suited to LLMs.</p>
<p>Perhaps if I had asked the LLM to write me a script, it would have had a better outcome, but they would require me then saving and running the script myself, which, sort of defeated the point of throwing the problem to the LLM.</p>
<h2>Why does this concern me?</h2>
<p>The simplicity of the task is one that, ideally, wouldn't require the kind rigour you might consider if the LLM were writing code for your code base.</p>
<p>Compound this with the simple fact that both LLMs are not deterministic (ie. if I host a local LLM it's not going to give me the same answer, or rather it won't use the same pathway), and if you're using a remote/online LLM the models update under your feet.</p>
<p>I'm sure this is because I'm not &quot;controlling&quot; the LLM in the best possible way. I recently read Simon Willison explaining that with 2 years of deep experience with LLMs the power user skill is about controlling the context the LLM has to guide it to the best outcome.</p>
<p>But most users aren't power users.</p>
<p>I don't doubt that this will change both for the positive and negative over time.</p>
<p>Is this just a hallucination? Do we seriously believe that developers will continuously check for hallucinations? I don't.</p>
<p>The web development industry moved from copying snippets of code to solve problems to installing npm modules for the answer. That's to say, due diligence has reduced over time in favour of DX.</p>
<p>Are developers going to validate everything that comes out of their LLM, particularly as vibe coding (however interpreted) becomes popular?</p>
<p>I worry we won't. I worry that asking for more rigour is too tall of an ask.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/05/31/ai-did-you-check-your-work">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Showing book clippings on my blog [blog]</title>
      <guid isPermaLink="false">showing-book-clippings-on-my-blog</guid>
      <link>https://remysharp.com/2025/05/01/showing-book-clippings-on-my-blog</link>
      <pubDate>Thu, 01 May 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[After jailbreaking my Kindle and seeing how simple it was and how all existing functionality was retained, I spotted that there was a My Clippings.txt file on the Kindle when mounted (I'm sure it's always there, I just hadn't mounted before).
This prompted me to get all my clippings (or as I think of them highlights) onto my blog since I already have all my books.]]></description>
      <content:encoded><![CDATA[
<p>After <a href="https://kindlemodding.org/mesquito/">jailbreaking my Kindle</a> and seeing how simple it was and how all existing functionality was retained, I spotted that there was a <code>My Clippings.txt</code> file on the Kindle when mounted (I'm sure it's always there, I just hadn't mounted before).</p>
<p>This prompted me to get all my clippings (or as I think of them <em>highlights</em>) onto my blog since I already have all <a href="https://remysharp.com/books">my books</a>.</p>
<h2>What failed</h2>
<p>Although the <code>My Clippings.txt</code> file was over 450Kb of content, it took me a long while to realise it contained <em>everything</em> that I highlighted, including when I was adjusting the selection on the Kindle.</p>
<p>What this means is that I have these <em>individual</em> clippings:</p>
<pre><code>- Added on Saturday, 19 May 2018 23:13:50
in sleep, had stopped dead and started again after
==========
- Added on Saturday, 19 May 2018 23:14:15
in sleep, had stopped dead and started again after a blank
==========
- Added on Sunday, 20 May 2018 15:47:35
You believe
==========
- Added on Sunday, 20 May 2018 15:47:39
You believe that reality is something objective, external,
existing in its own right.
==========
- Added on Sunday, 20 May 2018 15:48:17
You believe that reality is something objective, external,
existing in its own right. You also believe that the nature
of reality is self-evident. When you delude yourself into
thinking that you see something, you assume that everyone
else sees the same thing as you.
</code></pre>
<p>You can see it's captured every single interaction attempt at highlighting. Suddenly I can see how I managed nearly half a meg of clippings.</p>
<p>The clipping file does have additional metadata, including the book title, author and location - but given the content is (probably mostly) junk, I decided I had to get the clips from elsewhere.</p>
<h2>Getting data from Goodreads and Amazon</h2>
<p>The Goodreads API is notoriously rubbish. Their RSS feed of books you've read (i.e. books <em>I've</em> read) can sometimes, often, omit data that I know Goodreads has. So I wasn't looking forward to trying to use their site for the clippings.</p>
<p>Although I tried and failed to sniff the traffic from my Kindle when I rate a book or sync (I had hoped to capture a message to Amazon or Goodreads and intercept it), I realised I don't actually know where the &quot;I'm reading this now&quot; and the syncing actually goes.</p>
<p>It's possible it goes directly to Goodreads (I thought Amazon bought them). It's possible it goes to Amazon and then <em>across</em> to Goodreads (though, I somehow doubt that).</p>
<p>Either way, long story short, I found that there's an easily scrapable (not sure that's a word) web page on Amazon: <a href="https://read.amazon.com/notebook">https://read.amazon.com/notebook</a></p>
<p>It does also look like any changes on Goodreads (like when you make your highlights public) are pushed to this Amazon page.</p>
<p>So let's scrape the crap out of that page to get our data out.</p>
<h2>Scraping clippings</h2>
<p>The page <code>https://read.amazon.com/notebook?asin=${id}</code> contains HTML fragments that holds all the book data and highlights (and notes you made on your Kindle's janky keyboard).</p>
<p>So we'll scan the DOM for all the <code>asin</code> ids and loop through each one, extracting all the content:</p>
<pre><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">parseFromPage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// via https://read.amazon.com/notebook</span>
  <span class="token keyword">const</span> ids <span class="token operator">=</span> <span class="token function">$$</span><span class="token punctuation">(</span><span class="token string">'#kp-notebook-library > div'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_</span><span class="token punctuation">)</span> <span class="token operator">=></span> _<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">credentials</span><span class="token operator">:</span> <span class="token string">'include'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'cors'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> id <span class="token keyword">of</span> ids<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// get the plain text from each fetch</span>
    <span class="token keyword">const</span> t <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>
      <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">https://read.amazon.com/notebook?asin=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;contentLimitState=&amp;</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
      options<span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_</span><span class="token punctuation">)</span> <span class="token operator">=></span> _<span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// then parse it with the extractKindleHighlights</span>
    res<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token function">extractKindleHighlights</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> res<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now we need to parse it. You could go down the regexp path, but since we're still in the browser, we can take advantage of the DOM for navigating and iterating.</p>
<p>We already have the response as text, so the process is to <code>createDocumentFragment()</code> and insert the text as HTML into the fragment (though actually I'll put a single DOM element in the root of the fragment, then append to this):</p>
<pre><code class="language-js"><span class="token keyword">const</span> frag <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createDocumentFragment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> root <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
root<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> html<span class="token punctuation">;</span>
frag<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
<p>Using a fragment keeps it out of the main document DOM (in case it causes it go awry) but still affords us some <code>querySelector</code> goodness.</p>
<pre><code class="language-js"><span class="token keyword">function</span> <span class="token function">extractKindleHighlights</span><span class="token punctuation">(</span><span class="token parameter">html</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">author</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
    <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
    <span class="token literal-property property">highlights</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> frag <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createDocumentFragment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> root <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  root<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> html<span class="token punctuation">;</span>
  frag<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">;</span>

  result<span class="token punctuation">.</span>title <span class="token operator">=</span> root<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'h3'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  result<span class="token punctuation">.</span>author <span class="token operator">=</span> root<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'h3'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>nextElementSibling<span class="token punctuation">.</span>textContent<span class="token punctuation">;</span>

  <span class="token keyword">const</span> highlightElements <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>
    root<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'#kp-notebook-annotations > .a-row.a-spacing-base'</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> highlightElement <span class="token keyword">of</span> highlightElements<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> el <span class="token operator">=</span> highlightElement<span class="token punctuation">.</span>firstChild<span class="token punctuation">;</span>
    <span class="token keyword">const</span> page <span class="token operator">=</span> el
      <span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.kp-notebook-metadata'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">':'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> text <span class="token operator">=</span> el<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.kp-notebook-highlight'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">const</span> note <span class="token operator">=</span> el
      <span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.kp-notebook-note span:last-child'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    result<span class="token punctuation">.</span>highlights<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      text<span class="token punctuation">,</span>
      <span class="token literal-property property">note</span><span class="token operator">:</span> note <span class="token operator">||</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
      <span class="token literal-property property">page</span><span class="token operator">:</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>page<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">,</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Now the final format is more like structure JSON (I do some extra work to match up the book title to my existing reviews in markdown):</p>
<pre><code class="language-json"><span class="token punctuation">{</span>
  <span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">"George Orwell and Thomas Pynchon"</span><span class="token punctuation">,</span>
  <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Nineteen Eighty-Four (Penguin Modern Classics)"</span><span class="token punctuation">,</span>
  <span class="token property">"highlights"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"The solid, contourless body, like a block of granite, and the rasping red skin, bore the same relation to the body of a girl as the rose-hip to the rose. Why should the fruit be held inferior to the flower?"</span><span class="token punctuation">,</span>
      <span class="token property">"note"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
      <span class="token property">"page"</span><span class="token operator">:</span> <span class="token number">219</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"You believe that reality is something objective, external, existing in its own right. You also believe that the nature of reality is self-evident. When you delude yourself into thinking that you see something, you assume that everyone else sees the same thing as you."</span><span class="token punctuation">,</span>
      <span class="token property">"note"</span><span class="token operator">:</span> <span class="token null keyword">null</span><span class="token punctuation">,</span>
      <span class="token property">"page"</span><span class="token operator">:</span> <span class="token number">249</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre>
<p>So now I have my clippings on <a href="https://remysharp.com/books/2018/1984">my books like 1984</a>.</p>
<hr>
<p>The one (rather important) bit that I've not quite worked out, is how to fully automate this. I suspect I'll need something either like a headless browser to do the work, or something with my Amazon credentials which isn't ideal.</p>
<p>What <em>would</em> be perfect, is it I could find a way to capture the clippings directly from the Kindle, especially since it's jailbroken now - though I suspect this is an extremely tricky problem to crack.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/05/01/showing-book-clippings-on-my-blog">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Minority Rule: Adventures in the Culture War [book]</title>
      <guid isPermaLink="false">minority-rule-adventures-in-the-culture-war</guid>
      <link>https://remysharp.com/books/2025/minority-rule-adventures-in-the-culture-war</link>
      <pubDate>Sat, 26 Apr 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Maybe you've read this book, and you feel a sense of despair.

I think before read the book I had a sense of despair. This book did an excellent job of articulating my despair. Articulating ideas that I couldn't put words to on my own.
I bought this book on pre-order after seeing Ash Sarkar on Pod Save the UK. An articulate, intelligent individual with strong opinions, and though I always struggle to read non-fiction, I didn't find this book hard in the way I had expected (sure, still took me a month, but that's pretty good for me).
It's hard to write a review without wanting to just repeat the entire book back to you. If you consider yourself a progressive, left or left leaning, I'd recommend this book. It's definitely not cherry, but it's not angry either. It does the work of laying out what we sort of already know. And importantly it's not anecdotes, around 20%+ of the book are external references that are peppered throughout the main text.
I've highlighted a lot of the content and hope that the messages will stay with me as I feel like her book is a useful tool for arming myself for the political battle we face in the age of 2025.]]></description>
      <content:encoded><![CDATA[<blockquote>
<p>Maybe you've read this book, and you feel a sense of despair.</p>
</blockquote>
<p>I think <em>before</em> read the book I had a sense of despair. This book did an excellent job of articulating my despair. Articulating ideas that I couldn't put words to on my own.</p>
<p>I bought this book on pre-order after seeing Ash Sarkar on Pod Save the UK. An articulate, intelligent individual with strong opinions, and though I always struggle to read non-fiction, I didn't find this book hard in the way I had expected (sure, still took me a month, but that's pretty good for me).</p>
<p>It's hard to write a review without wanting to just repeat the entire book back to you. If you consider yourself a progressive, left or left leaning, I'd recommend this book. It's definitely not cherry, but it's not angry either. It does the work of laying out what we sort of already know. And importantly it's not anecdotes, around 20%+ of the book are external references that are peppered throughout the main text.</p>
<p>I've highlighted a lot of the content and hope that the messages will stay with me as I feel like her book is a useful tool for arming myself for the political battle we face in the age of 2025.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/minority-rule-adventures-in-the-culture-war">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>How I made an LED driver smart… [blog]</title>
      <guid isPermaLink="false">how-i-made-an-led-driver-smart</guid>
      <link>https://remysharp.com/2025/04/18/how-i-made-an-led-driver-smart</link>
      <pubDate>Fri, 18 Apr 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[…by just a little.
Our family bathroom has a cabinet with an IR sensor that turns on LEDs in the side of the cabinet. The IR sensor is &quot;wave your hand under the cabinet&quot; and the lights go on or off. The littlest uses this light (instead of the overhead which is connected to the extractor fan - i.e. loud) as a night light.
Since it's usually left on at night, I wanted to give the LEDs some smarts so when I go to bed, it automatically turns off with my blanket &quot;turn all the lights out&quot; home assistant command.]]></description>
      <content:encoded><![CDATA[
<p>…by just a little.</p>
<p>Our family bathroom has a cabinet with an IR sensor that turns on LEDs in the side of the cabinet. The IR sensor is &quot;wave your hand under the cabinet&quot; and the lights go on or off. The littlest uses this light (instead of the overhead which is connected to the extractor fan - i.e. loud) as a night light.</p>
<p>Since it's usually left on at night, I wanted to give the LEDs some smarts so when I go to bed, it automatically turns off with my blanket &quot;turn all the lights out&quot; home assistant command.</p>
<h2>How the cabinet worked, emphasis on past tense</h2>
<p>After I removed the doors and the enclosure housing all the electrics, I found the following inside:</p>
<ol>
<li>Mains supply coming in</li>
<li>Goes into a mains power transformer to reduce the current</li>
<li>Supply is then split between a &quot;shaver socket&quot; (which charges our toothbrushes) and a control box that takes input from the IR sensor and outputs to an LED driver</li>
<li>The control box is responsible for converting the signal from the IR sensor to a relay, if the relay is closed it then powers the LED driver</li>
<li>The LED driver then lights up the cabinet</li>
</ol>
<figure><img src="https://remysharp.com/images/cabinet_before.jpg" alt="The wiring before I got my hands on it" decoding="async"></figure>
<p>I'd found that the voltage going to the IR sensor was 5V which was perfect for an ESP32 and I had planned to incept the IR sensor and then send signals to the control box to &quot;pretend&quot; hand movement was detected, and thus turn the lights off.</p>
<p>That completely failed, and in the end I would connect to the output of the LED driver (which has stepped down to 12V) and add my own relay.</p>
<h2>First though, here's the mistakes I made</h2>
<p>There were a few (though hopefully you guessed the biggest one already - I'll save that for the end).</p>
<h3>1. Current</h3>
<p>The 5V that powered the IR sensor didn't provide enough current to boot up the ESP32. I'm not sure <em>how</em> much current it was providing, but when I connected the ESP to the control box it just wouldn't complete the boot process, which is (usually) indicative of not having enough current.</p>
<p>I think this makes sense, as the IR sensor wouldn't need a lot of current to detect a change.</p>
<h3>2. Shorting</h3>
<p>I decided perhaps I could pick up a decent supply elsewhere on the control box. I went probing around and noticed my multimeter wasn't really giving me the values I expected. Until, rather quickly, a loud BANG and flash made me jump and I realised I had blown the circuit.</p>
<p>The mistake here was that at some point, I had put my multimeter in current mode (which requires removing one of the probes and plugging it into a different socket). What that actually means is that when I probed the control box for voltage, instead of finding it, I created a short across the mains live and neutral. Hence the bang.</p>
<p>Thankfully the fuse box had just flipped the breaker.</p>
<p>After removing the control box and inspecting, I found that the trace I had been probing had been blown right off the PCB and eventually I spotted that my multimeter probe tips had melted!</p>
<h3>3. Assuming it would be okay after blowing</h3>
<p>After cleaning up the control box I decided that the only damage was my multimeter and the trace (which I had repaired with copious solder). I reinserted the control box, flipped the breaker only for it to immediately flip back down along with a bang (again) from the bathroom.</p>
<p>The control box had obviously been damaged (and frankly I should have known better), but now it was <em>really</em> dead. Lots of soot like black around components that I don't particularly recognise.</p>
<p>So now the cabinet lights are dead (well, the trigger mechanism) and it's coming to night time (since the electrics would be off for me to work on it, I'd be in the dark) - so the kids' night light was hosed for the next few nights.</p>
<figure><img src="https://remysharp.com/images/control_blown.jpg" alt="The control box completely blown" decoding="async"></figure>
<h3>4. Did I really make a better wheel?</h3>
<p>Now this is the big mistake: I'm fairly sure I made this smart for the sake of making something smart, <em>not</em> because it would enhance things.</p>
<p>I was stuck in this mess because I wanted to automate turning the light off at bedtime, but each night the process has been incredibly simple: right before I go to bed (and the kids are finally asleep), I lean into the bathroom, wave my hand, then carry on to bed.</p>
<p>I've written over 1,100 words, and spent a good few hours dismantling, fixing, breaking, testing, fixing and finally reassembling all to save myself from waving my hand in the evening. Even as I write, although the lights work again, the IR sensor doesn't - I <em>still</em> need to restore that functionality.</p>
<p>Lesson: weigh the pros and cons first, rather than diving straight in 🤦</p>
<h2>The smart LED driver</h2>
<p>Since I had blown the original control box beyond useful, I had to set my work on the output side of the LED driver.</p>
<p>I reused the relay from the control box and use a transistor to turn the relay on (because the ESP32-C3 GPIO isn't 5V safe, and the relay needed 5V).</p>
<figure><img src="https://remysharp.com/images/cab_esp_wiring.jpg" alt="The ESP32 wiring" decoding="async"></figure>
<p>This is what the schematic looks like (chatgpt suggested I include a flyback diode, which worked, but I really need to read up as to how and why - it's related to voltage spikes IIRC):</p>
<figure><img src="https://remysharp.com/images/smart-led-driver.png" alt="Smart LED driver schematic" class="bwimg" decoding="async"></figure>
<p>I also have a heat sink on the back of the 12V regulator. Finally, when this booted up, it was able to control the lights - or rather specifically: I was able to turn the lights <em>on</em> again.</p>
<figure><img src="https://remysharp.com/images/cabinet_lights_up.jpg" alt="The lights are finally on" decoding="async"></figure>
<p>The <a href="https://esphome.io">esphome</a> config revolves around this small snippet:</p>
<pre><code class="language-yaml"><span class="token key atrule">light</span><span class="token punctuation">:</span>
  <span class="token punctuation">-</span> <span class="token key atrule">platform</span><span class="token punctuation">:</span> binary
    <span class="token key atrule">name</span><span class="token punctuation">:</span> <span class="token string">'Cabinet Lights'</span>
    <span class="token key atrule">output</span><span class="token punctuation">:</span> cabinet_lights_output

<span class="token key atrule">output</span><span class="token punctuation">:</span>
  <span class="token punctuation">-</span> <span class="token key atrule">platform</span><span class="token punctuation">:</span> gpio
    <span class="token key atrule">pin</span><span class="token punctuation">:</span> GPIO1
    <span class="token key atrule">id</span><span class="token punctuation">:</span> cabinet_lights_output
</code></pre>
<p>It did then appear on my home assistant panel, and although the IR sensor is still disconnected, at least I can turn on the light (plus the littlest can use their Google Home voice thingy to turn the cabinet lights on too).</p>
<figure><img src="https://remysharp.com/images/ha-cabinet.png" alt="Home assistant showing me control over the cabinet lights" decoding="async"></figure>
<p>What a palava.</p>
<h2>What remains</h2>
<p>I do need to restore the IR sensor because that's the primary way of interacting with the light.</p>
<p>I would have liked to include a fuse where the 12V comes in, but I forgot to originally so to put it in requires disassembling the whole thing (which I will probably do).</p>
<p>I <em>could</em> put in a MM wave sensor to be used to turn the lights on automatically, but referring back to mistake number 4, I think perhaps the project doesn't <em>really</em> need it…</p>
<p>An interesting learning experience, with important learning lessons for me, specifically: <strong>if it ain't broke, don't fix it</strong>.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/04/18/how-i-made-an-led-driver-smart">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Do politics belong at web events? [blog]</title>
      <guid isPermaLink="false">do-politics-belong-at-web-events</guid>
      <link>https://remysharp.com/2025/04/07/do-politics-belong-at-web-events</link>
      <pubDate>Mon, 07 Apr 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[This question has been asked before and discussed before and I've always looked on from the sidelines, even though, as a conference organiser, I do in fact have fairly strong opinions about this.]]></description>
      <content:encoded><![CDATA[
<p>This question has been asked before and discussed before and I've always looked on from the sidelines, even though, as a conference organiser, I do in fact have fairly strong opinions about this.</p>
<p>Firstly there's the simple idea that a web event would be possibly &quot;pure&quot; tech. Certainly FFConf hasn't been 100% technical for over a decade, but the person giving talks always infuses their personality into their talks. Jokes, fun, passion, excitement, confusion - all of these add to talks, or certainly the ones we host at FFConf.</p>
<p>But perhaps views on politics is somehow different?</p>
<p>The web that I like to promote and support, and the one that many of my peer events support, is one of inclusivity and welcomeness. This means creating accessible environments. This means creating teams that are open minded. This means holding the door open for those who need it.</p>
<p>Nothing is free of politics in this era as it does indeed impact nearly everything we do. Even moreso today. American politics have a direct impact on UK politics and culture - there's no ignoring that. Equally the world climate affects how we're all operating.</p>
<p>Although my own politics stem primarily from the human side of things, recent activity in US politics, has spurred on many europeans to move our technology <em>away</em> from the US.</p>
<p>The <a href="https://european-alternatives.eu/categories">European Alternatives web site</a> has been doing the rounds lately and shares many good alternatives for us to move to, disconnecting our (read: <em>my</em>) dependency on the US for technology.</p>
<p>The web, and particularly the kind of web that we advocate at our events, is from a progressive point of view (talks on accessibility is an easy example of this), and so you'll find these particular kinds of events, those put together with love and care for others, will usually have strong political points of view that support the ideal web.</p>
<p>Politics <em>is</em> relevant to working on the web. It's relevant to how we progress the way we work and the ways we want to work.</p>
<p>Everything is political and if you're siding with the racist, fascist, homophobes, transphobes and those engaged in genocide, then I'm afraid you're in for a rough ride because that creates a closed, private, elite and corroded version of the web.</p>
<p>Discourse is fine. I was asked how I'd feel if I was at an event that was web based, and the speaker was very pro Trump and Trump agendas - then the MC agreed. How would I react? I'd feel like they were wrong, and I'd likely not want to attend again. Which is fine.</p>
<p>What isn't okay, is if I then start to harass the speaker or become violent towards them. It's obvious, but the code of conduct would (and should) eject me immediately (at a bare minimum). Infusing a web talk with your political leaning and ideals does not equate hate speech.</p>
<p>In an excellent talk I saw last week, the first slide, which had nothing to do with the talk itself and not even mentioned, simply read &quot;protect trans kids&quot;. This is part of our web. The web where we hold the door open for others. Because <em>we</em> are the web.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/04/07/do-politics-belong-at-web-events">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Making a Killing (DI Fawley #7) [book]</title>
      <guid isPermaLink="false">making-a-killing-di-fawley-7</guid>
      <link>https://remysharp.com/books/2025/making-a-killing-di-fawley-7</link>
      <pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Wanted to like it more, the gimmicks frustrated me.
I've read every DI Fawley book and I've always enjoyed them. I'm starting to think of these books as a bit of guilty pleasure. However, I'm limited to reading on the Kindle which means the medium which I read is rather specific.
Hunter will (as in: has also done in the past books) sandwich in content from different sources to give the story a bit more reach out into the world. I remember the earlier books would have tweets (and I think reddit threads, maybe) which would be between chapters (chapters being dedicated to telling one of the characters story and perspective).
The problem with the inserts is that on a Kindle these are actually images that are embedded, and trying to read the text in them, because the text doesn't scale, means zooming, and attempting to drag the image around, usually to read from one side of a sentence to the other.
This time around I just skipped them entirely - some of these included newspaper articles. I've no idea if they added anything to the story, if they contained plot points, or if they were decoration (more akin to the tweets in the earlier books). It's just too hard to get the text.
And then there's the perspective change with a font change. I use the Open Dyslexia font set at a size that helps me to read. This book then employed a completely different font for when the antagonist shares their story. One that's very, very thin and I noticed I would visibly struggled to read these pages.
All these points have nothing to do with the story, but unfortunately they had an impact on me being able to enjoy the story - because it kept taking me out. I really do wish these gimmicks would be used as an exception, rather than peppered or even core to specific characters talking.
Funnily enough, the book ends (sort of abruptly) and after the closing chapter and the author thanks, there's a mini story at the end. This is told from Adam Fawley's perspective, no gimmicks and it was super - I couldn't put the book down until I had finished this last section. This just reminds me that I really do enjoy Hunter's stories, and the characters and they can definitely stand strong on their own two feet.]]></description>
      <content:encoded><![CDATA[<p>Wanted to like it more, the gimmicks frustrated me.</p>
<p>I've read every DI Fawley book and I've always enjoyed them. I'm starting to think of these books as a bit of guilty pleasure. However, I'm limited to reading on the <a href="https://remysharp.com/2018/05/18/my-e...">Kindle</a> which means the medium which I read is rather specific.</p>
<p>Hunter will (as in: has also done in the past books) sandwich in content from different sources to give the story a bit more reach out into the world. I remember the earlier books would have tweets (and I think reddit threads, maybe) which would be between chapters (chapters being dedicated to telling one of the characters story and perspective).</p>
<p>The problem with the inserts is that on a Kindle these are actually images that are embedded, and trying to read the text in them, because the text doesn't scale, means zooming, and attempting to drag the image around, usually to read from one side of a sentence to the other.</p>
<p>This time around I just skipped them entirely - some of these included newspaper articles. I've no idea if they added anything to the story, if they contained plot points, or if they were decoration (more akin to the tweets in the earlier books). It's just too hard to get the text.</p>
<p>And then there's the perspective change with a font change. I use the Open Dyslexia font set at a size that helps me to read. This book then employed a completely different font for when the antagonist shares their story. One that's very, very thin and I noticed I would visibly struggled to read these pages.</p>
<p>All these points have nothing to do with the story, but unfortunately they had an impact on me being able to <em>enjoy</em> the story - because it kept taking me out. I really do wish these gimmicks would be used as an exception, rather than peppered or even core to specific characters talking.</p>
<p>Funnily enough, the book ends (sort of abruptly) and after the closing chapter and the author thanks, there's a mini story at the end. This is told from Adam Fawley's perspective, no gimmicks and it was super - I couldn't put the book down until I had finished this last section. This just reminds me that I really do enjoy Hunter's stories, and the characters and they can definitely stand strong on their own two feet.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/making-a-killing-di-fawley-7">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The day piracy changed [blog]</title>
      <guid isPermaLink="false">the-day-piracy-changed</guid>
      <link>https://remysharp.com/2025/03/22/the-day-piracy-changed</link>
      <pubDate>Sat, 22 Mar 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[It certainly wasn't today. It was some time ago, but I wanted to mark this in my blog as a reminder that once, long ago, piracy was, well, stealing.
That's all changed now.]]></description>
      <content:encoded><![CDATA[
<p>It certainly wasn't today. It was some time ago, but I wanted to mark this in my blog as a reminder that once, long ago, piracy was, well, stealing.</p>
<p>That's all changed now.</p>
<p>We, the web people, already knew that companies had, without permission, slurped up all our content to train their LLMs.</p>
<p>To some degree it was legitimate (though I'm not sure the licence on the site would be respected, such as creative commons) - our blog posts were on the open, and public web.</p>
<p>I'm certainly not justifying the carte blanche scraping and repurposing of our blog posts to help line the already rich pockets of tech bros.</p>
<p>However, this latest realisation has completely redefined piracy. I think I could happily argue that the concept of piracy is lost and gone.</p>
<p>I recently read Stephie Stimacs' post entitled &quot;<a href="https://blog.stephaniestimac.com/posts/2025/03/ethical-ai/">In which I discover my book has been scraped by Meta for its AI&quot;</a> - which you might be able to guess the conclusion just from the title.</p>
<p>As I was reading, I was reminded of a post I'd seen on BlueSky earlier that week:</p>
<blockquote>
<p>it’s possible to not care if people (humans, readers) pirate my book—or even feel pleased they do, if anything; and, at the same time, express fury when corporations grab and grind the text up like scrap metal for their word-garbage generators</p>
</blockquote>
<ul>
<li><a href="https://bsky.app/profile/jomc.bsky.social/post/3lkt6mwcsps2h">joanne mcneil
@jomc.bsky.social</a></li>
</ul>
<p>With all this in mind, I thought I'd check books I had written and contributed to in this LibGen database, and yep, my work was in there too. I'd go so far as bet that if you've written a book, it is too.</p>
<p>So what? So, <strong>Meta knowingly took pirated content and used it to train their models.</strong></p>
<p><a href="https://www.rollingstone.com/culture/culture-news/ai-meta-pirated-library-zuckerberg-1235235394/">The complicity goes all the way to the top.</a></p>
<p>There's piracy behind closed doors and private use (bad), and there's what Meta did (and yes, likely the other companies already rolling in cash they could have actually paid for this content). They've taken pirated content and they're selling it as their own.</p>
<p>What are the ramifications?</p>
<p>Actual ROFL. Are you fucking kidding me? Those with power have proven time and time again in this last decade that they're not accountable. The answer to no one. There's no ramifications. None.</p>
<p>So. I wanted to mark on my blog, for posterity's sake, that piracy is dead. Because without an reaction there is no action, ergo: piracy doesn't really exist in today's age of AI and the rich and powerful.</p>
<p>Though, I somehow suspect the hammer is being kept on the side just in case us little people step out of line and &quot;pirate&quot; something for our own use.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/03/22/the-day-piracy-changed">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Devs: draw your line [blog]</title>
      <guid isPermaLink="false">devs-draw-your-line</guid>
      <link>https://remysharp.com/2025/03/08/devs-draw-your-line</link>
      <pubDate>Sat, 08 Mar 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[This post is for my developers out there, web and otherwise. We have super powers. We can make something functional from practically nothing. And you know what they say about great power…
So this is short and sweet: know where you draw the line and stick to your god damn guns.]]></description>
      <content:encoded><![CDATA[
<p>This post is for my developers out there, web and otherwise. We have super powers. We can make something functional from practically nothing. And you know what they say about <a href="https://duckduckgo.com/?q=with+great+power">great power</a>…</p>
<p>So this is short and sweet: know where you draw the line and stick to your god damn guns.</p>
<h2>A case study</h2>
<p>For repeat prescriptions I use a site called <a href="https://www.pharmacy2u.co.uk/">Pharmacy2U</a> (who took over for Lloyds Pharmacy for fulfilment … I guess). The terrible name should be a give away to the quality of the site, but really for repeat prescriptions the web site interaction should be limited to: email alert, click, pay, close.</p>
<p>I've recently been browsing the web using the <a href="https://noscript.net/">NoScript</a> extension and deciding manually which scripts to enable (a point to note: nearly all checkout I've encountered requires JavaScript).</p>
<p>It was because of this script that I noticed that some developer, somewhere in the stack that put together the Pharmacy2U web site decided it was okay to share data with Facebook and TikTok.</p>
<p>I can understand (nearly) wanting to find where a business' traffic is coming from. Perhaps they're writing articles about health and wanting to track metrics (or something).</p>
<p>However, these 3rd party tracking scripts were on the prescription pages. The pages that list what medication I'm on.</p>
<p>Facebook and TikTok had been given access to read what medication I'm on by the development team responsible for the Pharmacy2U web site.</p>
<p>If that's you, sorry-not-sorry, but <strong>shame on you</strong>.</p>
<h2>Stop making our failing privacy worse</h2>
<p>As developers we're the ones that control whether these tracking scripts are included. We're the ones responsible for the security of our user's content. Some content is <em>obviously</em> more sensitive than other data. This <em>should</em> be common sense.</p>
<p>Regardless of whether developers specifically inserted the tracking scripts into the medication pages, or if they developed a CMS of sorts that let's someone insert the scripts into pages - it's still software that we developers made.</p>
<p>Analytics in and of itself isn't bad. Putting scripts that gives unfettered access to medical information is wrong. I suspect this also puts some individuals at risk too.</p>
<p>Take a stand, draw a line, and stick to it. Businesses and capitalism will want to move that line, give it some <em>wiggle room</em>, but fuck that. You're not a bad person, you're just doing your job, but as a developer, like I said, you have super powers. Use them. Stop enabling this gross misuse of software.</p>
<h2>Epilogue</h2>
<p>Even though Google very recently removed the excellent <a href="https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?pli=1">uBlock Origin</a> - it's still very much alive and available to <a href="https://ublockorigin.com/">block trackers</a> where they (trackers) don't belong.</p>
<p>Equally, for me, using <a href="https://noscript.net/">NoScript</a> blocks these in my desktop browser, but I also use <a href="https://pi-hole.net/">Pi-Hole</a> in our home network, and I've been considering using <a href="https://nextdns.io/?from=4azjnggh">NextDNS</a> for my mobile outside of my home network.</p>
<p>So, thankfully, I'm protected, but that's only because I've got the super powers too. Mum and Dad aren't protected in the same way. I'm fairly sure my siblings aren't either.</p>
<p>It's a weird situation we've gotten ourselves in that we're having to actively fight to have a safe web.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/03/08/devs-draw-your-line">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Time Machine [book]</title>
      <guid isPermaLink="false">the-time-machine</guid>
      <link>https://remysharp.com/books/2025/the-time-machine</link>
      <pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Facinating for when it was written, but felt like it was sort of missing a story.
I'm definitely reading these older classics so that I acquaint myself with what I've assumed all my peers read when they were younger, so this eventually made its way into my reading list.
It's an incredible story given when it was written, late 1800s, and I can imagine it blowing minds at the time when it was read. The technicality of the book is interesting too, how there's a story inside of the story whereby the … character we're following (not narrator, but not really the protagonist) is sharing the experience with the reader. In fact, the majority of the book is a straight up monologue. Then to compound this, it's the Time Traveller (I guess the protagonist) in the present, sharing their past experience of living in the futre!
Timey wimey.
But I wasn't really expereiencing the technical feats, more following the story along, and that's where, for me, it was sort of lacking. There wasn't really a story. In the sense that there were cause effect, struggles to overcome and character arcs.
The Time Traveller meets and partners with a woman in the story, but when she's taken by the Morlocks and he just… goes okay &quot;then, I'm off.&quot;.
It's likely that I missed something in the story, but it didn't land so well for me. I think it's interesting, but I didn't come away thinking about the content of the story, the questions it could raise.
The very, very end is bold, and I definitely appreciated that, but it really didn't add anything either.]]></description>
      <content:encoded><![CDATA[<p>Facinating for when it was written, but felt like it was sort of missing a <em>story</em>.</p>
<p>I'm definitely reading these older classics so that I acquaint myself with what I've assumed all my peers read when they were younger, so this eventually made its way into my reading list.</p>
<p>It's an incredible story given when it was written, late 1800s, and I can imagine it blowing minds at the time when it was read. The technicality of the book is interesting too, how there's a story inside of the story whereby the … character we're following (not narrator, but not really the protagonist) is sharing the experience with the reader. In fact, the majority of the book is a straight up monologue. Then to compound this, it's the Time Traveller (I guess the protagonist) in the present, sharing their past experience of living in the futre!</p>
<p>Timey wimey.</p>
<p>But I wasn't really expereiencing the technical feats, more following the story along, and that's where, for me, it was sort of lacking. There wasn't <em>really</em> a story. In the sense that there were cause effect, struggles to overcome and character arcs.</p>
<p>The Time Traveller meets and partners with a woman in the story, but when she's taken by the Morlocks and he just… goes okay &quot;then, I'm off.&quot;.</p>
<p>It's likely that I missed something in the story, but it didn't land so well for me. I think it's interesting, but I didn't come away thinking about the content of the story, the questions it could raise.</p>
<p>The very, <em>very</em> end is bold, and I definitely appreciated that, but it really didn't add anything either.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-time-machine">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Upgrading USB devices to USB-C [blog]</title>
      <guid isPermaLink="false">upgrading-usb-powered-devices-to-usbc</guid>
      <link>https://remysharp.com/2025/02/24/upgrading-usb-powered-devices-to-usbc</link>
      <pubDate>Mon, 24 Feb 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[For a small home project, my son suggested adding some under lights to a plant stand we recently added to our kitchen. I figured we could do this with a cheap LED strip and it could connect to the &quot;house&quot; using Zigbee and Home Assistant.
All was well until I realised the plug socket nearest the plants already had it's USB port in use and only the USB-C port was available. No worries, I'll just snip the USB plug and swap in a USB-C plug. Or so I thought.]]></description>
      <content:encoded><![CDATA[<p>For a small home project, my son suggested adding some under lights to a plant stand we recently added to our kitchen. I figured we could do this with a cheap LED strip and it could connect to the &quot;house&quot; using Zigbee and Home Assistant.</p>
<p>All was well until I realised the plug socket nearest the plants already had it's USB port in use and only the USB-C port was available. No worries, I'll <em>just</em> snip the USB plug and swap in a USB-C plug. Or so I thought.</p>
<h2>TL;DR</h2>
<p>The USB-C breakout boards with plugs attached tend to have a ~50K resistor tied to VCC. It needs to be removed, and a 5.1K resistor tied from ground to A5.</p>
<h2>USB-C breakout boards</h2>
<p>I was using these 4 pad USB-C breakouts since they're considerably easier to work with given the large solder pads for power and ground, <em>and</em> as I didn't need to use the data lines.</p>
<figure><img src="https://remysharp.com/images/usb-c-breakout.jpg" alt="USB-C breakout" decoding="async"></figure>
<p>I originally snipped off the end of the regular USB plug on the LED strip and then soldered on the new USB-C port. I did a short test injecting power via my bench supply and it appeared to work.</p>
<p>When I did a <em>real</em> test by plugging it into my wall USB-C socket, absolutely nothing happened. I was quite confused (for a brief moment worried I had killed my socket - I hadn't).</p>
<p>I also added a USB-C voltmeter between my new plug and the wall socket and it confirmed: zero volts being delivered.</p>
<p>This is because USB-C is smart, but to <em>be</em> smart, the &quot;host&quot; (in my case, the wall socket) needs to ask a question of the peripheral to be sure what kind of voltage to deliver.</p>
<p>I had assumed if the peripheral was dumb and didn't &quot;reply&quot;, the host was send out 5v (as it does with a traditional USB host), but apparently not (or certainly not for the case of my wall socket). Actually, if I've understood USB-C correctly, the host also needs to get confirmation that a peripheral has been inserted (and <em>not</em> another host).</p>
<p>This question and answer process is done through resistors, and possibly magic unicorns. Still, the <a href="https://www.usb.org/document-library/usb-type-cr-cable-and-connector-specification-release-24">specification is here</a> if you're interested.</p>
<p>The USB-C breakout comes with a 56K resistor tied from A5 (CC) to VCC, which means it's originally suited to making a USB-C to USB 2.0 cable. Not what I need at all.</p>
<p>To remedy this, the resistor is removed, and a 5.1K resistor is added tying A5 to ground. Thankfully the breakout board does have a preconfigured pads to make this relatively straight forward (both for SMD or THT soldering).</p>
<figure><img src="https://remysharp.com/images/usb-c-changed-resistor.jpg" alt="USB-C with new resistor" decoding="async"></figure>
<p>My soldering was a bit crunchy, but the resistor is now correct and when plugged in, the device now correctly pulls 5V from the wall socket.</p>
<p>The result, after sticking down the LED strip is now we've got some semi-fancy under plant lighting (which I promise you, looks better in real-life!):</p>
<figure><img src="https://remysharp.com/images/plant-led.jpg" alt="LED plant life" decoding="async"></figure>
<p><em>Originally published on <a href="https://remysharp.com/2025/02/24/upgrading-usb-powered-devices-to-usbc">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>How to disable Google Gemini in your Google Workspace [link]</title>
      <guid isPermaLink="false">2025-02-12-9469eb6c</guid>
      <link>https://remysharp.com/links/2025-02-12-9469eb6c</link>
      <pubDate>Wed, 12 Feb 2025 11:38:37 +0000</pubDate>
      <description><![CDATA[Google's AI offering came along to paid accounts without permission or request. What made it worse is that it couldn't be turned off, which… I don't even have words. It was via Jan on Mastodon that I'd seen it wasn't possible to disable. But Jan having more persistance than me actually managed to find a (hidden) solution (short version, keep asking support). Bless his magnificence for getting this. I'm now Gemini-free, particularly in Gmail which was the place it was annoying me the most.
Source: writing.jan.io]]></description>
      <content:encoded><![CDATA[
<p>Google's AI offering came along to paid accounts without permission or request. What made it worse is that it couldn't be turned off, which… I don't even have words. It was via Jan on Mastodon that I'd seen it wasn't possible to disable. But Jan having more persistance than me actually managed to find a (hidden) solution (short version, keep asking support). Bless his magnificence for getting this. I'm now Gemini-free, particularly in Gmail which was the place it was annoying me the most.</p>
<p><em>Source: <a href="https://writing.jan.io/2025/01/17/how-to-disable-google-gemini-in-your-workspace.html">writing.jan.io</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2025-02-12-9469eb6c">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Blog Questions Challenge [blog]</title>
      <guid isPermaLink="false">blog-questions-challenge</guid>
      <link>https://remysharp.com/2025/02/06/blog-questions-challenge</link>
      <pubDate>Thu, 06 Feb 2025 10:00:00 +0000</pubDate>
      <description><![CDATA[I remember seeing something very similar doing the rounds on that site we used to use called &quot;Twitter&quot;, possibly back in 2007. Anyway, fast forward a full grown adult and it's making the rounds, except this time instead of looking dough eyed from afar, that lovely man Stuart tagged me (though arguably Jeremy also tagged everyone in his post too).
So here's my hat in the ring.]]></description>
      <content:encoded><![CDATA[
<p>I remember seeing something very similar doing the rounds on that site we used to use called &quot;Twitter&quot;, possibly back in 2007. Anyway, fast forward a full grown adult and it's making the rounds, except this time instead of looking dough eyed from afar, that lovely man <a href="https://www.kryogenix.org/days/2025/02/03/blog-questions-challenge/">Stuart</a> tagged me (though arguably Jeremy also tagged <a href="https://youtu.be/74BzSTQCl_c?si=hxm1WKV0_zIw6vT_&amp;t=9">everyone</a> in <a href="https://adactio.com/journal/21674">his post</a> too).</p>
<p>So here's my hat in the ring.</p>
<h2>Why did you start blogging in the first place?</h2>
<p>I'd had a couple of false starts with blogging. Once in 2004 during a 3 month sabbatical in Whistler, Canada to keep friends &amp; family up to date with my activities. That last 3 weeks (on Google's blogger platform - no idea if it still exists).</p>
<p>After getting back to the UK, whilst I was working full time in London (and travelling back home to Brighton), I decided that I would start my own business, and that like any good web based business, developer thingy person, I <strong>should</strong> have a blog. Literally as simple as that.</p>
<p>Since I had no one to write for, and nothing to say, I started just writing little movie reviews of films I'd seen at the cinema and little notes that I thought were interesting things happening on the web (technically this was <a href="https://remysharp.com/2006/09/01/flickr-geo-tags">my first blog post</a> on <a href="http://remysharp.com">remysharp.com</a> even though I back-posted one to before then).</p>
<p>Simultaneously at around the same time, I was blogging on <a href="http://leftlogic.com">leftlogic.com</a> which had a handful of high traffic posts (like my <a href="https://web.archive.org/web/20061018170535/http://leftlogic.com/info/articles/microformats_bookmarklet">Microformats bookmarklet</a>), that eventually I brought some of the posts across to this blog (I should really move the rest in one day).</p>
<h2>What platform are you using to manage your blog and why did you choose it? Have you blogged on other platforms before?</h2>
<p>Today my blog is a custom rolled static site generator (SSG) written in JavaScript. It's <a href="https://github.com/remy/remysharp.com">on github</a> if you want a peek. Because the site is entirely static (with some dynamic content either regenerated on build or if I run an offline command), it means I host my site on Netlify (thanks Netlify folks).</p>
<p>Before that I was running my blog on a SSG called <a href="https://github.com/sintaxi/harp">Harp</a>, and this was hosted on Heroku. It based a lot of the inspiration for my own code (along with <a href="https://www.11ty.dev/">11ty</a>, though I didn't use it for my personal blog).</p>
<p>Before that, and originally, it was WordPress. Mostly because I could get started on the blogging side rather than get too bogged down in how comments and <a href="https://indieweb.org/pingback">pingbacks</a> work. I've literally no idea where that was hosted, but I moved away from WordPress when I'd hit my limit of viagra adverts being injected into various vulnerabilities that WordPress so kindly hosted for me.</p>
<h2>How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that's part of your blog?</h2>
<p>As markdown in whatever editor I happen to be using at the time. I'm writing this in VS Code, and if I'm at my desktop machine, it's likely the &quot;editor&quot; I'll use.</p>
<p>If I'm on my phone (and I've written <em>a lot</em> of posts on my phone, on a treadmill), it'll be iA Writer for Android.</p>
<p>I don't have any fancy formatting, and I've gotten the sense that for a lot of the bloggers who've written similar posts, they're in the same boat.</p>
<h2>When do you feel most inspired to write?</h2>
<p>There's two distinct times:</p>
<p>When there's an immediate trigger in front of me. This could be a technical problem that I thought would be useful to write up (so that I can search my own blog for the solution), or if there's some opinion on the web that I strongly want to leap on and have my say in (yes, though I won't admit it, I suspect I love the sound of my own words, I mean, look how long this post is already).</p>
<p>Those posts tend to be easier to write.</p>
<p>The other time is when I'm either in bed trying to sleep, or in bed trying not to get up, or driving and my brain is on idle. I find myself wanting to write about much more nuanced subjects, like for instance: how AI might affect the coders jobs and how that might impact children today. But inevitably they're subjects that I'm not confident to articulate and I pontificate instead (&quot;articulate&quot; and &quot;pontificate&quot; in a single blog post, 13 year old Rem would be proud, and slightly grossed out).</p>
<h2>Do you publish immediately after writing, or do you let it simmer a bit as a draft?</h2>
<p>Always immediately after. Otherwise they go to die a slow death in my <a href="https://remysharp.com/drafts/">drafts</a> - which I should really just go ahead and rename to <em>purgatory</em>.</p>
<p>The problem I now face having run my blog for 19 years (so far!), is that I don't tend to fart out posts as quickly. Ironically I've written about <a href="https://remysharp.com/2021/11/21/why-i-write-and-when-i-dont#why-i-wont-write">why I don't write</a> and what blocks me in particular. The thing that slows me down (a lot) is needing to make sure I'm being as technically accurate as possible. Checking for bugs or misunderstandings, which then leads to rabbit holes, which, quite a few times, leads to purgatory (sorry, &quot;permanent draft&quot;).</p>
<h2>What's your favourite post on your blog?</h2>
<p>I'm not entirely sure I have one. I'm very proud of <a href="https://remysharp.com/2015/09/14/jsbin-toxic-part-1">my series on JS Bin</a> (originally a single blog post at over well over 5,000 words, I was advised to break it up!). My series of posts when we <a href="https://remysharp.com/2019/02/12/cern-day-1">returned to CERN to build</a> the World Wide Web browser was fun too.</p>
<p>I'm also fairly keen on my post on <a href="https://remysharp.com/2014/03/07/youre-paying-to-speak">you're paying to speak</a> - because I <em>still</em> feel strongly about it and it's still relevant (and did kick off some good discussion).</p>
<h2>Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature?</h2>
<p>I'm regularly thinking on the backend of the blog, making slight build speed improvements, or having to fix or add some little thing. I briefly thought about adding a section on the games I'm playing and completing, but it turns out I'm pretty crap at games, so the list quickly ended at three games and hit the bin.</p>
<p>In short: no.</p>
<h2>Next?</h2>
<p>Although I love you, my dear reader, very much, I'm going to tag a few people who's blogs pop into my head. The first is <a href="https://ohhelloana.blog/posts/">Ana Rodrigues</a>. Next would be the lovely <a href="https://jakearchibald.com/">Jake Archibald</a> who, if he does blog on this one will be off brand from the mega technical posts! Finally, <a href="https://awfulwoman.com/">Charlie O'Hara</a> (aka Whale Coiner and all kinds of other names) - I'm sure Charlie's had multiple blogs, but not sure enough to bet on it, so it'll be nice to (hopefully) read her history on this too.</p>
<p>There's lots more people that I'd love to read their history, but I think it would be rude to snag them all for my own. Definitely looking forward to reading more of these posts from people.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/02/06/blog-questions-challenge">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Home Assistant: how get real-time UK electricity and gas [blog]</title>
      <guid isPermaLink="false">home-assistant-how-get-realtime-electricity-and-gas.md</guid>
      <link>https://remysharp.com/2025/02/03/home-assistant-how-get-realtime-electricity-and-gas.md</link>
      <pubDate>Mon, 03 Feb 2025 10:00:00 +0000</pubDate>
      <description><![CDATA[There's a number of ways to get real-time electricity readings into Home Assistant. I'm currently using a Puck.js to read the blinking light on my meter but our gas meter is one of the new ultrasonic ones (in particular there's no readings or lights without physically interacting with it), so there was no obvious means to getting this data.
Most UK based energy companies don't have public methods to get your data out. I know Octopus does have APIs (which isn't our supplier), and there's a specific smart meter that also makes the data available on MQTT - but it's been out of stock for (at least) the last 12 months.
However, this last weekend I discovered, so long as you have a smart meter, you can actually collect this data from the DCC.]]></description>
      <content:encoded><![CDATA[
<p>There's a number of ways to get real-time electricity readings into Home Assistant. I'm currently using a Puck.js to read the <a href="https://www.espruino.com/Smart+Meter">blinking light on my meter</a> but our gas meter is one of the new ultrasonic ones (in particular there's no readings or lights without physically interacting with it), so there was no obvious means to getting this data.</p>
<p>Most UK based energy companies don't have public methods to get your data out. I know Octopus does have APIs (which isn't <em>our</em> supplier), and there's a specific smart meter that also makes the data available on MQTT - but it's been out of stock for (at least) the last 12 months.</p>
<p>However, this last weekend I discovered, so long as you have a smart meter, you can actually collect this data from the DCC.</p>
<h2>TL;DR</h2>
<ol>
<li>Register with <a href="https://bright.glowmarkt.com">Bright</a></li>
<li>Install the <a href="https://github.com/HandyHat/ha-hildebrandglow-dcc">integration</a></li>
</ol>
<p>Job done.</p>
<h2>DCC and getting your data</h2>
<p>The Data Communications Company (DCC) enables standardised data exchange across the national smart meter network, allowing energy consumption data can be accessed securely.</p>
<p>Not particularly open to Joe Public, but if you have authorised access, such as the <em>&quot;Other User&quot;</em> role, you (they, the 3rd party) can get the data for us (not all of it, as the Other User from what I've read understandably has restricted access). The technical phrasing is:</p>
<blockquote>
<p>As a DCC Other User, we are also able to support SMETS data services without hardware.</p>
</blockquote>
<p>Creators of the Glow smart meter, Hildebrand, also have a mobile app called <a href="https://bright.glowmarkt.com">Bright</a> (native <em>and</em> web) that doesn't require any special hardware, that can collect your energy readings on a 30 minute interval (okay, not &quot;real-time&quot;, but real enough!). This is because Hildebrand have the Other User access.</p>
<p>Since the Bright is on the web, and there's an API that drives it (as seen in the network requests), there's now a way to get energy data into Home Assistant.</p>
<h2>The integration</h2>
<p>From here it's a matter of wiring up the software. The integration is available via HACS with the <a href="https://github.com/HandyHat/ha-hildebrandglow-dcc">install guide on github</a>, and a few clicks later I now have the daily running cost of both electricity <em>and</em> gas and the usage, which can help me make decisions about how we use the energy at home.</p>
<figure><img src="https://remysharp.com/images/hass-energy.png" alt="Visualisation of energy flow on Home Assistant" decoding="async"></figure>
<p>Plus, it looks cool 🤓</p>
<p>I know this is data for data's sake, but it's actually helped us to make a change at home, which should have a positive financial impact (I/we hope!). I have a glass infrared radiator in the office, which although is energy efficient, actually accounts for a fairly large whack of our daily electricity usage.</p>
<p>Switching back to the gas heating (whilst <em>not</em> heating the entire floor) is what we're testing now and should make an impact on our bills.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/02/03/home-assistant-how-get-realtime-electricity-and-gas.md">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Man Who Died Twice (Thursday Murder Club, #2) [book]</title>
      <guid isPermaLink="false">the-man-who-died-twice-thursday-murder-club-2</guid>
      <link>https://remysharp.com/books/2025/the-man-who-died-twice-thursday-murder-club-2</link>
      <pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Reeks of &quot;cosy crime&quot; - and that's good.
There's a reason that Richard Osman's series has been a runaway success for many people (or at least I can believe it) - the story is well written, the characters are extremely well drawn and the pages are a pleasure to digest.
I keep thinking I'm further along in the series than I actually am because the characters have quickly become a staple of crime fiction in my head.
Although I did find I was a little suprised at the some of the more grusome passages (realising that doesn't really fit the &quot;cosy crime&quot; sub-genre). Those parts did take me out of the story a little. Again, I feel like I've had more of these characters than I actually have (this is the second Thursday book I've read), so there's no reason why a few hard edges wouldn't be part of the story.
I did, however, have one complaint. I was quickly getting annoyed with the diary that Joyce would write in. For some reason I was having a hard time believing that Joyce was writing diary entries in such a conversational style as a pen and paper job, especially when the style of her writing/thoughts read as if she would be telling her husband… who's already shuffled off. I'm not 100% sure why I found it distracting, but I suspect that's not going to reduce any amount in the next Thursday books, so I'll probably need to find a way around it.
Otherwise, a lovely read. Definitely feels as if I'm visiting these characters, and it's always a pleasure sharing a bit of time with them all.]]></description>
      <content:encoded><![CDATA[<p>Reeks of &quot;cosy crime&quot; - and that's good.</p>
<p>There's a reason that Richard Osman's series has been a runaway success for many people (or at least I can believe it) - the story is well written, the characters are extremely well drawn and the pages are a pleasure to digest.</p>
<p>I keep thinking I'm further along in the series than I actually am because the characters have quickly become a staple of crime fiction in my head.</p>
<p>Although I did find I was a little suprised at the some of the more grusome passages (realising that doesn't really fit the &quot;cosy crime&quot; sub-genre). Those parts did take me out of the story a little. Again, I feel like I've had more of these characters than I actually have (this is the second Thursday book I've read), so there's no reason why a few hard edges wouldn't be part of the story.</p>
<p>I did, however, have one complaint. I was quickly getting annoyed with the diary that Joyce would write in. For some reason I was having a hard time believing that Joyce was writing diary entries in such a conversational style as a pen and paper job, especially when the style of her writing/thoughts read as if she would be telling her husband… who's already shuffled off. I'm not 100% sure why I found it distracting, but I suspect that's not going to reduce any amount in the next Thursday books, so I'll probably need to find a way around it.</p>
<p>Otherwise, a lovely read. Definitely feels as if I'm visiting these characters, and it's always a pleasure sharing a bit of time with them all.</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-man-who-died-twice-thursday-murder-club-2">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>What is good web design, and bad web design? [blog]</title>
      <guid isPermaLink="false">what-is-good-web-design-and-bad-web-design</guid>
      <link>https://remysharp.com/2025/01/15/what-is-good-web-design-and-bad-web-design</link>
      <pubDate>Wed, 15 Jan 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[My son, aged 13, was given this question for his homework recently. As someone (me!) having worked on the web professionally since 1999, I felt a connection to this particular bit of homework. Obviously we clashed (homework sucks, both for kids, parents and teachers) - but I thought it would be a good exercise for me to try to answer.
I will add that I don't consider myself the authority on this answer, but I have been attending design, user experience and developer conferences for over 15 years - so hopefully I've learnt a thing or two.]]></description>
      <content:encoded><![CDATA[
<p>My son, aged 13, was given this question for his homework recently. As someone (me!) having worked on the web professionally since 1999, I felt a connection to this particular bit of homework. Obviously we clashed (homework sucks, both for kids, parents <em>and</em> teachers) - but I thought it would be a good exercise for me to try to answer.</p>
<p>I will add that I don't consider myself the authority on this answer, <a href="https://remysharp.com/2017/08/12/but">but</a> I have been attending design, user experience and developer conferences for over 15 years - so hopefully I've learnt a thing or two.</p>
<h2>There's no one measure</h2>
<p>When I consider what's good or bad about a web site, there's quite a few different tests I'm considering.</p>
<p>It's also worth adding that this list is non exhaustive and I think you'll agree quickly that there's <em>a lot</em> of factors that go into weighing whether a web site is good or bad in their design.</p>
<p><strong>Can I find what I'm looking to do?</strong> This means landmarks are clear, I'm not being pulled away by distracting content (like overlays) and I can find the navigation easily.</p>
<p>For example: if I'm on Brighton council refuse web pages, can I easily and quickly find when my next collection dates are? Or if I'm on a hotel web site, can I find the address (or map), contact details and booking dates?</p>
<p><strong>Do the pages work on mobile?</strong> Much to my frustration, my mobile phone is pretty much always on me, so when I go to look up some information, I'll usually use my phone first.</p>
<p>A bad design is going to have the page jumping all over the place when it's loading (i.e. I'm about to tap a link, then something loads and the link moves away from under my finger). A bad design is one that I have to scroll vertically because the page hasn't zoomed to the phone.</p>
<p>I think it's pretty straightforward to get the feel for a bad mobile design, because it's often immediately unusable, forget about the actual looks.</p>
<p><strong>Does the site load?</strong> Although whether a web site loads or not isn't immediately down to it's design, usually the cause of a blank page, or half broken page will be down to the design decisions and interactions loaded into the page.</p>
<p>For example: if I'm travelling by train, often the connection is spotty at best - i.e. loading a page with lots of assets (images and JavaScript) or loading a page with <em>large</em> assets can and <strong>will</strong> fail. If the result is a log in form that doesn't work, or an article that refers to images that haven't loaded - then that's a bad design.</p>
<p>(Yes, this is me calling out those using JavaScript to load images <em>and</em> gently suggesting that progressive enhancement is (still) the way.)</p>
<p><strong>Is the main content the <em>main</em> content item?</strong> Put another way, is the content hidden (or fighting) clutter on the page?</p>
<p>For example: a lot of indie bloggers put their blog content front and centre. Strong simple fonts, with sensible line heights leaving me with <em>very</em> legible content. On the flip side are pages that are sandwiched between navigation and other articles (that I'm likely not interested in) and the actual content is a thin column of content where the text just doesn't have room to breathe.</p>
<p>Or is the content blocked behind a wall of popups and overlays? Such as (when signed out of twitter) you visit (for example) <a href="https://twitter.com/lingscars">twitter/lingscars</a>. This is bad design (and a bad experience).</p>
<p><strong>Does this web site work for all people?</strong> Specifically I'm thinking about accessibility. I'm fortunate that I don't have any impairments that affect how I surf the web, but being the liberal equality chasing flower that I am, a &quot;good web design&quot; means it works just as well for me as it does for anyone else.</p>
<p>For example: are input fields properly labelled? Do images have appropriate alternative text? Is the focus of a screen reader considered (and not hijacked)? Is there confusing sentences (like &quot;if you don't want to receive our newsletter, then don't click on the checkbox&quot;).</p>
<p><strong>How are images used?</strong> Text for images is a super no-no. It might be something that suits the aesthetic of the web page, but not being able to select text (such as a phone number or a business name) is a huge failure on the web site's part.</p>
<p>Bad design, potentially invisible on initial viewing, would use images for text.</p>
<p><strong>Aesthetics</strong> Generally speaking, for a western user, we're going to have an idea of whether something is pleasing or … displeasing to the eye. Taste can be quite personal, and full on, excited, animated and in your face might suit one person (ala the days of My Space and can be seen on <a href="https://www.lingscars.com/">LINGsCARS</a>), and that person might not like the brutalist styles (like those of <a href="https://heydonworks.com/">Heydon Pickering</a>'s web site), or perhaps simple and straight forward (like the <a href="https://www.gov.uk/">GOV.UK sites</a>).</p>
<p>I'd argue that a good web site design puts this last in the priorities, and a bad web site design puts aesthetics above all else.</p>
<h2>Some things that I've not mentioned, but think of from a developer's perspective</h2>
<ul>
<li>I always look at a web site and catch inaccessible aspects, be it form fields, colour contrast, keyboard support. If any of these are lacking, it's a notch towards &quot;bad web site&quot;</li>
<li>Use of JavaScript. I'm often browsing the web with JavaScript either completely disabled, or with all 3rd party JavaScript disabled. In the later case, if the site doesn't function (Amazon product listings being one), it frustrates me that I would even need to load resources from many different origins. Even more so when a web page uses JavaScript to create elements such as images, or take over the scroll bar - these are failings in my opinion</li>
<li>Use of semantic mark up. I didn't mention it earlier, because every day users aren't going to think about how that impacts them. But good mark up goes towards good accessibility (such for a screen reader to navigate) or for search engines to pull out the pertinent content (or even to allow for a &quot;reader&quot; mode, something I use on Firefox both on desktop and mobile)</li>
</ul>
<p>There's more I'm sure. Maybe this is useful (or incomplete!?) to you, but I'm fairly certain it's useful for my son!</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/01/15/what-is-good-web-design-and-bad-web-design">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>The Echo Wife [book]</title>
      <guid isPermaLink="false">the-echo-wife</guid>
      <link>https://remysharp.com/books/2025/the-echo-wife</link>
      <pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[Could not put the damn book down!
Very easy book to read, and annoyingly difficult to stop reading late at night.
The story is told from Evelyn's perspective, a brilliant scientist working in the field of human cloning.
There's a lot that goes on in the book, both from the story moving forward and driven by plot twists but also from Evelyn's own inner monologue.
The clone aspect of the story presents a, I think, believable take on the sci-fi concept (specifically the purpose clones would serve) and this easily leads to the messy question of what is a person, how we feel towards others, what we're willing to accept.
The book also shows how Evelyn struggles, or rather, maintains her anger and in particular, frustration towards others she feels (quite often) are incompetent, and it's interesting seeing where this comes from for the character, and how she goes on a journey, that can often get pretty dark (I'm not explaining myself well here, but I'm trying to avoid too much of a spoiler).
A very easy read for me. The story absolutely drives forward. There are lots of (I thought) nice turns of phrases throughout the book that didn't feel too forced.
There's really only one (biggish) plot hole based around the amount of time that passes, which the story does attempt to address, but I was so caught up in the page turning that it didn't particularly impair on the enjoyment.
Fun stuff!]]></description>
      <content:encoded><![CDATA[<p>Could not put the damn book down!</p>
<p>Very easy book to read, and annoyingly difficult to stop reading late at night.</p>
<p>The story is told from Evelyn's perspective, a brilliant scientist working in the field of human cloning.</p>
<p>There's a lot that goes on in the book, both from the story moving forward and driven by plot twists but also from Evelyn's own inner monologue.</p>
<p>The clone aspect of the story presents a, I think, believable take on the sci-fi concept (specifically the purpose clones would serve) and this easily leads to the messy question of what is a person, how we feel towards others, what we're willing to accept.</p>
<p>The book also shows how Evelyn struggles, or rather, maintains her anger and in particular, frustration towards others she feels (quite often) are incompetent, and it's interesting seeing where this comes from for the character, and how she goes on a journey, that can often get pretty dark (I'm not explaining myself well here, but I'm trying to avoid too much of a spoiler).</p>
<p>A very easy read for me. The story absolutely drives forward. There are lots of (I thought) nice turns of phrases throughout the book that didn't feel too forced.</p>
<p>There's really only one (biggish) plot hole based around the amount of time that passes, which the story does attempt to address, but I was so caught up in the page turning that it didn't particularly impair on the enjoyment.</p>
<p>Fun stuff!</p>
<p><em>Originally published on <a href="https://remysharp.com/books/2025/the-echo-wife">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Server isn't sending content length [blog]</title>
      <guid isPermaLink="false">server-isnt-sending-content-length</guid>
      <link>https://remysharp.com/2025/01/03/server-isnt-sending-content-length</link>
      <pubDate>Fri, 03 Jan 2025 00:00:00 +0000</pubDate>
      <description><![CDATA[As part of the ZX Spectrum community I wrote a tool that can download content over HTTP from a ZX Spectrum Next, but one prerequisite is that the content-length header is present (so it operate properly).
A friend from the community runs a server and built a (really nice) GUI tool on top the .http command where users can find and download software for their Spectrum.
However, for reasons unknown, it wasn't working and content-length just seemed to be missing.
This fix isn't Speccy specific, it will fix missing content length for Apache 2 servers.]]></description>
      <content:encoded><![CDATA[
<p>As part of the ZX Spectrum community I wrote <a href="https://github.com/remy/next-http">a tool that can download</a> content over HTTP from a ZX Spectrum Next, but one prerequisite is that the <code>content-length</code> header is present (so it operate properly).</p>
<p>A friend from the community runs a server and built a (really nice) GUI tool on top the <code>.http</code> command where users can find and download software for their Spectrum.</p>
<p>However, for reasons unknown, it wasn't working and <code>content-length</code> just seemed to be missing.</p>
<p><em>This fix isn't Speccy specific, it will fix missing content length for Apache 2 servers.</em></p>
<h2>Testing</h2>
<p>Even though the <code>content-length</code> was being set in the PHP files, by default, Apache was configured to chunk encoding (which is generally prefer, by the server, when sending dynamic based content, i.e. content generated by a PHP file).</p>
<p>So somewhere the <code>content-length</code> was being stripped.</p>
<p>This is all tested using a <code>curl</code> command with the <code>-i</code> flag to include headers:</p>
<pre><code>$ curl -i example.com
</code></pre>
<p>Though this does include the full body, so it's easier to debug using <code>-I</code> (which requests the headers via a <code>HEAD</code> request) but the <code>HEAD</code> request is not equal to <code>GET</code> so we'll explicitly call a <code>GET</code> using <code>-X</code>:</p>
<pre><code>$ curl -I -X GET example.com
</code></pre>
<p>Now we just have the headers and we can check if anything is missing.</p>
<hr>
<div class="update">
<strong>Note that the information about browsers is completely wrong. I <em>thought</em> the header was being synthesised by the browser, but if you look carefully the header is in the raw response. I'm sure I saw it was being added, but I now suspect I was just confused whilst debugging. I've left this here in case it's at all useful, but keep in mind it's wrong!</strong></div>
<p><strong>&lt;wrong-content&gt;</strong></p>
<p>A note about debugging with browser tools: browsers are going to augment the &quot;raw&quot; request. Firstly you have caching in play (so fire up the privacy window and disable caching from the network panel), but also they show you how <em>they</em> interpreted the headers.</p>
<p>This is Firefox for instance on <code>https://www.google.com</code>:</p>
<figure><img src="https://remysharp.com/images/firefox-headers.png" alt="Firefox GET request headers panel" decoding="async"></figure>
<p>Whereas if we tick that &quot;raw&quot; checkbox, we can see the <code>content-length</code> was never part of the headers:</p>
<figure><img src="https://remysharp.com/images/firefox-headers-raw.png" alt="Firefox raw headers" decoding="async"></figure>
<p>Chrome will also add the header (with no option for the original response) but show it as a content length of zero.</p>
<p><strong>&lt;/wrong-content&gt;</strong></p>
<hr>
<p>So, make sure you're testing as close to the metal as possible.</p>
<h2>Fixing</h2>
<p>At some point (in Apache's history), a change was made to prevent scripts from setting certain headers (in particular <code>content-length</code>) to mitigate potential <a href="https://httpd.apache.org/security/vulnerabilities_24.html">vulnerabilities</a>.</p>
<p>So to trust your scripts, an apache rule is required to apply this. The follow example solved our particular problem, including the change in an <code>.htaccess</code> file in the root of the directory being served:</p>
<pre><code>&lt;Files *.php&gt;
  SetEnv ap_trust_cgilike_cl 1
&lt;/Files&gt;
</code></pre>
<p>Now Apache will trust all the headers, <code>content-length</code> specifically, from the PHP files.</p>
<p><em>Originally published on <a href="https://remysharp.com/2025/01/03/server-isnt-sending-content-length">Remy Sharp's b:log</a></em></p>]]></content:encoded>
    </item>
  </channel>
</rss>