Shellsharks Blogroll - BlogFlockhttps://blogflock.com/list/xJ8yq2026-10-21T15:00:00.000ZBlogFlockshellsharksYour AI agent fixed the bug, but what else did it change? - Trail of Bits Bloghttps://blog.trailofbits.com/2026/10/21/your-ai-agent-fixed-the-bug-but-what-else-did-it-change/2026-10-21T15:00:00.000ZTrail of Bits Blog<p>Today we’re publishing <a href="https://github.com/trailofbits/skills/tree/main/plugins/patching-bugs">patching-bugs</a> and <a href="https://github.com/trailofbits/skills/tree/main/plugins/regression-hunt">regression-hunt</a>, two plugins that let engineers delegate patch development and regression checks to agents. Our patching-bugs plugin reproduces reported bugs, develops fixes, and tests them. The regression-hunt plugin checks whether any changes break previously working and supported behavior. Engineers can direct the work and review their results; maintainers can supply their own security assumptions and patching requirements, so the agents work within the project’s rules.</p>
<p>We built patching-bugs and regression-hunt through <a href="https://trailofbits.com/patch-the-planet/">Patch the Planet</a>, our joint initiative with OpenAI, where we’ve co-authored hundreds of security patches for widely used open-source projects. Motivated by our major successes on projects like <a href="https://github.com/trailofbits/publications/blob/master/presentations/Auditing%20Scapy%20With%20Agents/README.md">Scapy</a>, our plugins draw on our analysis of merged patches and thousands of our engineers’ Codex sessions.</p>
<p>The plugins provide maintainers with a structured workflow, evidence, independent checks, and best practices to improve their patching hygiene. In our preliminary evaluations, regression-hunt produces 25% fewer false-positive findings than a baseline Codex agent prompted to “review the PR for regressions.”</p>
<h2 id="standardizing-the-patching-process">Standardizing the patching process</h2>
<p>Our <a href="https://blog.trailofbits.com/2026/09/15/1passwords-ai-patching-benchmark-is-misleading/">September post</a> reported regressions introduced by some of our Patch the Planet patches. Since then, we’ve reviewed every patch that a Trail of Bits engineer authored and maintainers merged upstream. The review confirmed 21 regressions from over 130 PRs merged: 16 functional, one performance, and four build/test. None broke a security protection.</p>
<p>(“Regression” refers to a new defect introduced by a change that breaks previously correct and supported behavior along a path a real user or attacker can reach.)</p>
<p>For each patch, an agent looked for potential regressions, confirmed each one with a proof-of-concept (PoC) run before and after the change, and then passed the results to an engineer for review.</p>
<p>The review was noisy: today’s bug disclosures arrive at an unprecedented speed and scale, and for patching to keep pace, the industry needs to increase its signal-to-noise ratio. The 21 confirmed regressions were identified among 58 candidates in a single run (~36% precision). But because agents take many different approaches and reach different results, each run generates more candidates to manually sort through.</p>
<p>That led us to two conclusions:</p>
<ol>
<li>To introduce fewer regressions, we needed a standard way to write patches, with the same checks applied every time.</li>
<li>To catch the regressions we still introduce, we needed a standard way to check for them, with written rules for what counts as a regression.</li>
</ol>
<p>Standardizing both steps also makes every run repeatable and comparable. No fixed checklist will catch every regression, so we collect agent trajectories from our engineers’ patching sessions, and use the tasks that an earlier version failed to benchmark for the next one (figure 1).</p>
<p>
<figure>
<img src="https://blog.trailofbits.com/2026/10/21/your-ai-agent-fixed-the-bug-but-what-else-did-it-change/patching_figure1_hu_fd1b211e358e4802.webp"
alt="“Figure showing the feedback loop for generating and evaluating patches.”"
width="1200"
height="596"
loading="lazy"
decoding="async" />
<figcaption>Figure 1: Feedback loop for generating and evaluating patches.</figcaption>
</figure>
</p>
<h2 id="how-we-designed-the-plugins">How we designed the plugins</h2>
<p>We used two primary sources to guide the design of our plugins: a review of every patch we merged upstream and the thousands of Codex sessions our engineers used to write those patches.</p>
<p>This led to four principles that shaped our patching-bugs and regression-hunt plugins:</p>
<ol>
<li><strong>Opinionated guidance in as few words as possible.</strong> We give the agent wide latitude and record only Trail of Bits’ judgment of what constitutes a good patch and a real regression. The goal is to direct the agent towards the desired result with the least possible guidance.</li>
<li><strong>Modular steps.</strong> You don’t have to run either plugin end-to-end, as both provide multiple skills that can be used in tandem or in isolation.</li>
<li><strong>Strict input and output schemas.</strong> Every step takes and returns a defined schema that includes the claim, the code revision, the required check, the raw evidence, and open questions. Fixed schemas reduce variability across runs and allow each step to verify the previous step’s work rather than trusting its summary.</li>
<li><strong>A live proof of concept for every claim.</strong> Every conclusion depends on a PoC run against a live deployment: a built, running instance of the project, reached through the interfaces a real user would use. When reproducing a bug, proving a fix, and confirming a regression, if a step can’t produce a live PoC, its result is inconclusive.</li>
</ol>
<h2 id="regression-hunt-reduces-false-positive-rates">regression-hunt reduces false positive rates</h2>
<p>The regression-hunt plugin (Figure 2):</p>
<ol>
<li><strong>Enumerates</strong> <strong>candidate regressions</strong> by tracing the changed code (the diff) through its callers to its real interfaces.</li>
<li><strong>Reproduces</strong> each candidate regression on a <strong>live deployment of the system.</strong></li>
<li><strong>Replays</strong> the PoC on the live deployment on a <strong>fresh-context agent.</strong></li>
<li><strong>Judges</strong> the results of all the previous steps to validate that the <strong>proof meets the requirements of a regression.</strong></li>
</ol>
<p>
<figure>
<img src="https://blog.trailofbits.com/2026/10/21/your-ai-agent-fixed-the-bug-but-what-else-did-it-change/patching_figure2_hu_ba5a49e1c64e072d.webp"
alt="“Figure showing the regression-hunt workflow”"
width="1200"
height="1037"
loading="lazy"
decoding="async" />
<figcaption>Figure 2: The regression-hunt workflow.</figcaption>
</figure>
</p>
<p>In addition to the four principles above, there are two core aspects that are critical to the plugin:</p>
<ol>
<li><strong>Defining a regression:</strong> The plugin has strong opinions about what a regression is and, more importantly, what a regression is NOT. The burden of proof is placed on the agent (specifically during the “adjudication agent” step) to prove that there is sufficient evidence to show that the suspected regression is not a false positive, an incomplete fix, a latent bug, a pre-existing bug, and so on. The plugin only reports a regression if all of these gates are passed.</li>
<li><strong>Incorporating a threat model:</strong> If a threat model is available, the orchestrator will intentionally pass this document as part of the global context for each individual agentic step. This is an attempt to require each step/agent to refer back to the threat model before reaching its conclusion.</li>
</ol>
<p>Our review of merged PRs yielded a set of 21 confirmed regressions that we could use to evaluate regression-hunt. We used the whole set to compare the plugin with a baseline Codex agent prompted to “review the PR for regressions.”</p>
<p>Across those runs, regression-hunt produced 25% fewer false-positive findings than the baseline, with a 95% confidence interval of 14% to 38%. Its strict evidence requirements also left some known regressions classified as borderline: the plugin identified a possible regression but did not gather enough evidence to confirm it. Fewer false positives therefore came with a tradeoff in how many known regressions it could confidently report.</p>
<p>Next, we’ll investigate why these cases remained borderline and improve the plugin’s strategies for gathering evidence. Our goal is to confirm more real regressions while giving maintainers clearer information when the evidence remains inconclusive.</p>
<h2 id="patching-bugs-streamlines-best-practices-from-thousands-of-agent-sessions">patching-bugs streamlines best practices from thousands of agent sessions</h2>
<p>The patching-bugs plugin is a distillation of all the best practices we’ve identified by analyzing thousands of Codex sessions by Trail of Bits engineers (Figure 3). In addition to the more traditional capabilities (such as reproduction on live deployments and independent adversarial review), the plugin emphasizes:</p>
<ol>
<li><strong>The three-arm proof</strong>: The proof runs the same reproduction against three specifications: the original code must fail, the patched code must pass, and a complete revert must fail again. Bringing the bug back by removing the patch provides stronger evidence that the patch—rather than an unrelated environment or test change—actually fixed the bug.</li>
<li><strong>Invariant-guided repair:</strong> The patch attempts to fix the underlying, violated security invariant rather than fix the specific bug. This helps generalize the patch for possible variants of the same bug class.</li>
<li><strong>Incorporates regression analysis:</strong> The plugin mandates that regression-hunt is run to ensure that any patch goes through the same standard pipeline to check for mistakes.</li>
</ol>
<p>
<figure>
<img src="https://blog.trailofbits.com/2026/10/21/your-ai-agent-fixed-the-bug-but-what-else-did-it-change/patching_figure3_hu_7c360ee8fc4c02a3.webp"
alt="“Figure showing the patching-bugs workflow”"
width="1200"
height="933"
loading="lazy"
decoding="async" />
<figcaption>Figure 3: The patching-bugs workflow.</figcaption>
</figure>
</p>
<h2 id="whats-next-for-the-plugins">What’s next for the plugins</h2>
<p>Both patching-bugs and regression-hunt will continue to evolve. Our engineers are using them in their Patch the Planet work now, and we’re collecting data on how they’re being used, what works, and what doesn’t. That data, along with better evaluations, will shape the next version’s capabilities:</p>
<ul>
<li><strong>Evaluate patching-bugs.</strong> We’re building an evaluation that measures whether the plugin’s patches fix the reported bug and its variants without introducing regressions.</li>
<li><strong>Learn from agent traces.</strong> We’re analyzing the first batch of trace-level data from our engineers’ sessions. It will show whether Codex follows the rules and definitions required at each step, which steps it skips, and where its evidence is weakest.</li>
<li><strong>Make the plugins more configurable.</strong> patching-bugs already reads a project’s own patching guidelines (more on that in a bit). Next, we want maintainers to be able to choose whether to minimize patch sets—or, when broader coverage is worth more false positives, to loosen regression-hunt’s rules to surface more candidates.</li>
</ul>
<p>These plugins encode best practices we’ve found for an agent to patch a bug properly, and they improve when you tell them how your project works.</p>
<p>The plugins work best when you input two kinds of project knowledge:</p>
<ol>
<li>A <strong><code>THREAT_MODEL.md</code></strong>, which tells agents what counts as a vulnerability or a regression. The regression-hunt plugin passes it to every step, so each agent has to check its conclusions against the file. It should define the following: assets whose compromise matters, trust boundaries, access controls, supported configurations, specifications on what is or isn’t a vulnerability, and evidence required for a valid bug report (<a href="https://github.com/gpac/gpac/pull/3953">example</a>).</li>
<li>A <strong><code>PATCHING_GUIDELINES.md</code> (or <code>AGENTS.md</code>)</strong>, which tells agents and patching-bugs how your project wants fixes written. It should outline how to build and test the project, how big a fix should be, where a fix should go, how to present proposed changes, and what a regression test must show (<a href="https://github.com/gpac/gpac/pull/3953">example</a>).</li>
</ol>
<p>For our Patch the Planet work with GPAC, our engineers wrote a <a href="https://github.com/gpac/gpac/pull/3953">threat model and a project-specific patching skill</a> that captures how GPAC’s maintainers want bugs reproduced, what evidence a fix needs, and what a patch must preserve. <a href="https://github.com/open-quantum-safe/liboqs/pull/2477">Liboqs</a> and <a href="https://github.com/validatorjs/validator.js/">validator.js</a> have also published threat models in accordance with our guidance.</p>
<p>The more comprehensive and specific these files are to your project, code conventions, and operating style, the better the output you’ll receive.</p>
<h2 id="the-industry-needs-to-focus-on-patching">The industry needs to focus on patching</h2>
<p>As AI accelerates bug discovery, we need to turn those findings into reliable fixes just as quickly. Solutions need to balance speed and quality: an agent needs to understand a report, decide what needs to change, and check that a patch fixes the issue without introducing regressions. Our patching-bugs and regression-hunt plugins bring what we’ve learned from Patch the Planet into that workflow, helping maintainers move from a bug report to a patch they can confidently review and ship.</p>
<p>Install <a href="https://github.com/trailofbits/skills/tree/main/plugins/patching-bugs">patching-bugs</a> and <a href="https://github.com/trailofbits/skills/tree/main/plugins/regression-hunt">regression-hunt</a>, add a <code>THREAT_MODEL.md</code> and a <code>PATCHING_GUIDELINES.md</code> to your repository, and put them to work on your next bug report. Give the tools your project’s context so they can help you focus on the right fix and validate it faster. Then share your <a href="https://github.com/trailofbits/skills/issues">feedback</a>, including comments about where our defaults don’t fit your project. Those real-world cases will help us build the next version.</p>Get 18% more keys for free with this one crazy trick - Johnny.Decimalhttps://johnnydecimal.com/blog/0258-18pc-more-keys-for-free/2026-10-21T03:43:24.000ZJohnny.Decimal<p>You probably only have 78 keys on your keyboard.<sup><a href="#user-content-fn-mac-us" id="user-content-fnref-mac-us" data-footnote-ref="true" aria-describedby="footnote-label" class="footnote">1</a></sup> So why do you never use 12 of them?</p>
<p>These keys, of course, are the F-keys <kbd>F1</kbd> through <kbd>F12</kbd>. By default, macOS uses them for the glyphs printed thereon. But tell me the last time you pressed any of these:</p>
<ul>
<li><kbd>F3</kbd> for Mission Control.</li>
<li><kbd>F4</kbd> for Spotlight.</li>
<li><kbd>F5</kbd> for whatever that microphone does.</li>
<li><kbd>F6</kbd> for Sleep.<sup><a href="#user-content-fn-sleep" id="user-content-fnref-sleep" data-footnote-ref="true" aria-describedby="footnote-label" class="footnote">2</a></sup></li>
<li><kbd>F7</kbd>–<kbd>F9</kbd> for Music control.</li>
</ul>
<p>I'll grant that you might use <kbd>F1</kbd>/<kbd>F2</kbd> for brightness and <kbd>F10</kbd>–<kbd>F12</kbd> for volume control. But still, how often?</p>
<h2 id="let-your-apps-use-them">Let your apps use them</h2>
<p>I'm writing an app. Apps work really well with shortcut keys. We all have 12 perfectly great keys right there, begging to be used – and we never do.</p>
<p><em>'Of course'</em>, you'll say. <em>'The user can press the <kbd>Fn</kbd> key to access the F-keys'</em>. And, right, they <em>can</em> do that. But a fraction of people know that this is possible. So they never, ever will.</p>
<p>I implore you, then, to change this setting. Go to <strong>Settings</strong> > <strong>Keyboard</strong> > <strong>Keyboard Shortcuts…</strong> > <strong>Function Keys</strong>. Turn on <strong>Use F1, F2, etc. keys as standard function keys</strong>.</p>
<p>And there you go. Previously you had 66 usable keys. Now you have 78.</p>
<div data-footnotes="true" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-mac-us">
<p>US keyboard on a modern Mac laptop. <a href="#user-content-fnref-mac-us" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
<li id="user-content-fn-sleep">
<p>TIL that there's a built-in Sleep shortcut! I've created my own via a Raycast shortcut for <em>years</em> now.<sup><a href="#user-content-fn-my-shortcut" id="user-content-fnref-my-shortcut" data-footnote-ref="true" aria-describedby="footnote-label" class="footnote">3</a></sup> 😭 <a href="#user-content-fnref-sleep" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
<li id="user-content-fn-my-shortcut">
<p>For 'sleep' I use <kbd><kbd>Control</kbd>-<kbd>Option</kbd>-<kbd>L</kbd></kbd>. I use <kbd><kbd>Control</kbd>-<kbd>Option</kbd></kbd> for my own global shortcuts because not much else does. <kbd>L</kbd> because the shortcut, which triggers Raycast's system action <strong>Sleep Displays</strong>, has the effect of <strong>L</strong>ocking my laptop. I may have inherited this from Windows, where <kbd><kbd>Win</kbd>-<kbd>L</kbd></kbd> does the same. <a href="#user-content-fnref-my-shortcut" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
</ol>
</div>Brainstorming multi-lingual use of Artemis - James' Coffee Bloghttps://jamesg.blog/2026/10/21/brainstorming-multi-lingual-artemis2026-10-21T00:00:00.000ZJames' Coffee Blog
<p>I now follow three websites or feeds whose authors write in French; in at least two cases, the authors write in both French and English. When I see posts in different languages, I use Firefox’s Translate feature to read them. Firefox Translate is not perfect, but as long as I can get the gist of something I am happy (although suggestions for better translation practices, ideally locally, and without complex, internet-connected, or paid technology, would be sincerely appreciated!).</p>
<p>Now that I am seeing more French in my <a href="https://artemis.jamesg.blog">Artemis</a> feed, I have been thinking about what features may be useful to help me make the most of what I see. Here is a snippet of some posts in my web reader right now:</p>
<p><figure><picture><img alt="Five posts in Artemis by four authors. The first three posts are in French. The last two are in English." loading="lazy" src="https://jamesg.blog/assets/artemis-multi-lingual.png" style=" max-width: 130%;"/></picture><div class="alt"><label><input aria-label="Toggle image alt text on screen" type="checkbox"/>ALT</label><div class="content">Five posts in Artemis by four authors. The first three posts are in French. The last two are in English.</div></div></figure></p>
<p>One of the features I am experimenting <sup class="footnote-reference" id="f-1"><a href="https://jamesg.blog/longform-feed#1">1</a></sup> with (seen in the screenshot above) is adding the language code associated with an article before the title of the article, like so:</p>
<blockquote>
<p>[FR] [post title]</p>
</blockquote>
<p>I only add this if the title isn't in a language I know (English).</p>
<p>To determine the language that corresponds with a post, I am using the “<a href="https://pypi.org/project/langdetect/">langdetect</a>” Python package <sup class="footnote-reference" id="f-2"><a href="https://jamesg.blog/longform-feed#2">2</a></sup>. There are likely ways to infer language deterministically (ActivityStreams has a “contentMap” value that contains as keys language codes that map to post content, e.g. “fr” would map to a post written in French).</p>
<p>This feature is most useful when a title is also written in a language I don’t know. The tag gives me a clear visual indication that a post is in a different language before I start reading the title. This helps me while scanning my feed.</p>
<p>One edge case where this tag adds confusion, however, is when all the words in the title happen to be in English. This was the case with one title in my feed, “<a href="https://alexsirac.com/writober-2026-lounge/">Writober 2026 – Lounge </a>”, whose contents are in French but the title is readable in English. In this case, an “[FR]” preface to the title is technically correct, but is jarring given the title co-incidentally is in words that I can read as English.</p>
<p style="text-align: center;">⁂ ⁂ ⁂</p>
<p>Earlier today at HWC Asia/Pacific, I raised the idea of multi-lingual feeds and we had a good discussion. Part of me wants to offer a translated version of a post title, but I am not sure whether I want to be running a full translation model on the server. <a href="https://github.com/argosopentech/argos-translate/">Argos Translate</a> looks like a promising model should I want to go down this path, though I would sincerely appreciate recommendations on this front. The red line is that I want all translation to either happen within Artemis or, if absolutely necessary, the user’s browser. I don’t want to use an external API.</p>
<p>There is an experimental <a href="https://developer.mozilla.org/en-US/docs/Web/API/Translator/translate">Translator API</a> in the browser which could, in theory, allow the use of JavaScript to translate specific parts of the page on load. With that said, this API does not have Firefox or Safari support, and it would mean titles would be translated on every page load.</p>
<p>I am also unsure whether using a translation will deprive me of some part of the experience of seeing multilingual text. I read one Mastodon post in Artemis today that was written in French. I was able to translate the post myself approximately enough to get the joke, even though I know <em>very</em> few words of French. I felt really good after this!</p>
<p style="text-align: center;">⁂ ⁂ ⁂</p>
<p>Reflecting on what multi-lingual features a readers should have has taken me down a long rabbit hole of considerations. If an article has a translated version, could a web reader show an icon to indicate that there is a translated version? Could said reader automatically retrieve the translated version? What could/should Artemis do when the titles of feeds are in a different language? Yesterday I was subscribing to a French blog that uses WordPress. The titles of the feeds were in French and at first I accidentally selected the comments feed as the one to which I wanted to subscribe.</p>
<p>Language use on the web is complicated, and I am only scratching the surface. Stephanie Booth’s talk “<a href="https://www.youtube.com/watch?v=wdAT1P4fKXc">While We Wait for BabelFish: Languages on theInternet</a> [sic]”, although given in 2007, has given me a lot to think about. As I only speak one language (although I do understand the specific Scots dialect/words from where I grew up), I have a limited view of what is best to do. Someone who only speaks one language may prefer a different UI to someone who speaks multiple. That is to say: there is a lot to explore, and I would love help! If you have thoughts, <a href="https://jamesg.blog/email">I would be delighted to hear from you</a>.</p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label" id="f-3">1</sup>
<p>This experiment is currently enabled only on my account. I do this sometimes so I can get a feel for a feature before releasing it either in the Artemis Labs section of the software as an experiment or as a fully-fledged feature.</p>
<a href="https://jamesg.blog/longform-feed#f-1">[↩]</a></div>
<div class="footnote-definition" id="2"><sup class="footnote-definition-label" id="f-4">2</sup>
<p>Technical note: I check if the post content is more than two words to prevent false positives, something I ran into with short Mastodon posts. This logic will likely need to be made more robust, though.</p>
<a href="https://jamesg.blog/longform-feed#f-2">[↩]</a></div>
<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4e090377e166586',t:'MTc5MjU4ODg2NQ=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
<a class="tag" href="https://alexsirac.com/writober-2026-lounge/">Writober 2026 – Lounge</a>
<a class="tag" href="https://developer.mozilla.org/en-US/docs/Web/API/Translator/translate">Translator API</a>
<a class="tag" href="https://github.com/argosopentech/argos-translate/">Argos Translate</a>
<a class="tag" href="https://jamesg.blog/email">I would be delighted to hear from you</a>
<a class="tag" href="https://jamesg.blog/longform-feed#1">1</a>
<a class="tag" href="https://jamesg.blog/longform-feed#2">2</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-1">[↩]</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-2">[↩]</a>
<a class="tag" href="https://pypi.org/project/langdetect/">langdetect</a>
<a class="tag" href="https://www.youtube.com/watch?v=wdAT1P4fKXc">While We Wait for BabelFish: Languages on theInternet</a>
Hilary Duff at the Spark Arena - The Weblog of fLaMEdhttps://flamedfury.com/posts/hilary-duff-at-the-spark-arena/2026-10-20T22:47:52.000ZfLaMEd<p>What’s going on, Internet? Just got back from seeing Hilary Duff at the Spark Arena (on a Tuesday night, whooo).</p>
<p>Great show. I wish I knew her music more as I would have loved to be belting out the lyrics with the rest of the crowd.</p>
<p>Definite millenial energy in the crowd. Loved it.</p>
<p>Hey, thanks for reading this post in your feed reader! Want to chat? <a href="mailto:hello@flamedfury.com?subject=RE: Hilary Duff at the Spark Arena">Reply by email</a> or add me on <a href="xmpp:flamed@omg.lol">XMPP</a>, or send a <a href="https://flamedfury.com/posts/hilary-duff-at-the-spark-arena/#webmention">webmention</a>. Check out the <a href="https://flamedfury.com/posts/">posts archive</a> on the website.</p>
Read "The Internet goes on high alert after discovery of 8 compromised TLS certs" - Molly White's activity feed6ad7ec7a914413745263cc0d2026-10-20T22:34:34.000ZMolly White<article class="entry h-entry hentry"><header><div class="description">Read: </div></header><div class="content e-content"><div class="article h-cite hcite"><div class="title"><a class="u-url u-repost-of" href="https://arstechnica.com/security/2026/10/the-internet-goes-on-high-alert-after-discovery-of-8-compromised-tls-certs/?utm_source=bsky&utm_medium=social" rel="bookmark">“<span class="p-name">The Internet goes on high alert after discovery of 8 compromised TLS certs</span>”</a>. </div><div class="byline"><span class="p-author h-card">Dan Goodin</span> in <i class="p-publication">Ars Technica</i>. <span class="read-date"> Published <time class="dt-published published" datetime="2026-10-20">October 20, 2026</time>.</span></div><blockquote class="summary p-summary entry-summary">“That is extremely in the ‘this should not be possible’ category,” Valdorda said. “Maybe some horribly dangerous key generation interface has a ‘size’ parameter and they set it to 1 by mistake???”</blockquote><img src="https://www.mollywhite.net/assets/images/placeholder_social.png" alt="Illustration of Molly White sitting and typing on a laptop, on a purple background with 'Molly White' in white serif." style="display: none;"/></div><img src="https://www.mollywhite.net/assets/images/placeholder_social.png" alt="Illustration of Molly White sitting and typing on a laptop, on a purple background with 'Molly White' in white serif." style="display: none;"/></div><footer class="footer"><div class="flex-row post-meta"><div class="timestamp">Posted: <time class="dt-published" datetime="2026-10-20T22:34:34+00:00" title="October 20, 2026 at 10:34 PM UTC">October 20, 2026 at 10:34 PM UTC</time>. </div></div></footer></article>AI can weaken relationships between people - and teams suffer as a result. - Werd I/O6ad7a9bf7e1d7000013c22652026-10-20T17:49:51.000ZWerd I/O<p>Link: <a href="https://hbr.org/2026/10/team-dynamics-weaken-when-employees-turn-to-ai-instead-of-colleagues"><em>Team Dynamics Weaken When Employees Turn to AI Instead of Colleagues, by Kate Niederhoffer, Kristina Rapuano, Alex Liebscher, Alexi Robichaux and Jeffrey T. Hancock in Harvard Business Review</em></a></p><p>Relationships take time, effort, and practice to keep alive. So in a sense, it shouldn’t be a surprise to see this finding from a survey of more than a thousand desk workers across 20 industries:</p><blockquote>“The more they turned to AI instead of a teammate, the more their trust in each other and their ability to coordinate seemed to erode.”</blockquote><p>It turns out that if you start asking an AI system instead of your colleague, coordination and trust drop by at least 20%. These technologies are still relatively new; I imagine that over time this will continue to drop. People who feel isolated in their work today will feel more so as this trend solidifies. Similarly, people who feel untrusted today will only feel more so as their teams continue to ask AI systems instead of them.</p><p>That doesn’t mean AI is always inherently bad. Like all work processes, it demands that we think hard about culture: what kinds of behaviors and feelings do we want to promote? What’s important to the organization? And do we care more about an individual’s productivity than creating a high-performing team? (We shouldn’t.)</p><p>This also rings true:</p><blockquote>“People consistently underestimate how enjoyable a human conversation turns out to be. Whether the motivation is fear of judgment or a desire not to impose, avoiding a colleague is hardly a new workplace behavior. AI now provides an alluring alternative that forgoes the social and emotional costs of getting support.”</blockquote><p>One way we can counteract this is by making asking for help (and giving it) an explicit norm in our team cultures, and leading by example by doing it ourselves. In remote or hybrid teams that rely on tools like Slack, that also means doing it in a non-private channel, so that other team members can see it and it can become normalized.</p><p>Although a bit of a cliché and genuinely heinous if they’re done poorly, I think standups can also be really helpful here, if you can do them live. Here, everyone goes round a circle and talks briefly about what they’re working on, with explicit needs for help made as part of the update. Implicit requests for help can be answered once everyone has gone around the circle.</p><p>Can AI help? Totally. As the authors discuss in this piece, you can use technology to help conversations to happen asynchronously, particularly on teams that spread across multiple timezones, and it can actively facilitate connections between people.</p><p>Let’s do that, instead of displacing our people and our relationships. AI can be a facilitator between people, but it should never be an intermediator: our relationships, our communities, and our companies will suffer.</p>Foursquare died at the hands of private equity. What happens next? - Werd I/O6ad79ac77e1d7000013c22432026-10-20T16:45:59.000ZWerd I/O<p>Link: <a href="https://teendrama.substack.com/p/some-final-words-on-foursquare?utm_id=97760_v0_s00_e0_tv4&just_subscribed=true"><em>Some final words on Foursquare, by Dennis Crowley</em></a></p><p>I freaking loved Foursquare. It was a wonderful way to explore cities with the help of my friends: in the same way that Upcoming was my best way of figuring out which events I should be going to, Foursquare let me know which restaurants, bars, parks, etc, were worth checking out. If Chris ranked a cocktail bar, you know it was amazing. Aaron always found the best coffee spots. And so on: my friends were better recommenders than any other source I had.</p><p>My own map lists every state and country I’ve been to over the last few decades. If I’m back in Amsterdam, I can figure out which Indonesian food place I really loved, because there it is on my map. (It’s <a href="https://paonbali.nl/">Paon Bali</a>, by the way. You’re welcome.)</p><p>So this was sad to see as an outcome. I think there are a bunch of important lessons here – not least the dangers of raising too much too soon without tethering the business to revenue. But at the same time, Foursquare launched during a startup explosion, and this kind of fundraising was the norm; I don’t fault Dennis for his decisions. The bigger problem seems to have been the private equity leadership more recently. I can’t think of a time when private equity <em>hasn’t</em> led to toxic leadership and a strip-mined company with an inequitable deal for employees.</p><p>Which leads me to an obvious problem. That check-in data is intimate stuff. It probably shouldn’t be something private equity can just inherit and do <em>whatever</em> with. Journalists and activists should hopefully not have been using Foursquare to check in; if they did, that data can almost certainly have been bought for analysis. And then a future-facing question: how might we have these kinds of social effects <em>without</em> sacrificing privacy?</p><p>Regardless, I’m pouring one out for Foursquare. I loved it. Thanks to everyone involved for building something that I found genuinely meaningful.</p>2026-10-20 16:31: Hooman, we are ready to receive your apple core. 🍎 - Kev Quirkhttps://kevquirk.com/2026-10-20-16312026-10-20T15:31:00.000ZKev Quirk<p>Hooman, we are ready to receive your apple core. 🍎</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-10-20-1631/1000012549.webp" alt="1000012549" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=2026-10-20%2016%3A31">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-10-20-1631#comments">leave a comment</a>.</p>
</div>Book Review: Quackery - A Brief History of the Worst Ways to Cure Everything by Lydia Kang ★★★⯪☆ - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=742962026-10-20T11:34:35.000ZTerence Eden’s Blog<img src="https://shkspr.mobi/blog/wp-content/uploads/2026/08/9781523501854.webp" alt="Book cover." width="200" class="alignleft size-full wp-image-74298">
<p>This is a charming and funny book all about the various ways humans have promoted fake medical cures in exchange for money.</p>
<p>Despite the grim (and occasionally distressing) subject matter, the tone remains light and poppy throughout. There are lots of snarky asides (especially about the fake medical practitioners) and a real appreciation for the bizarre. It's fun the whole way through, although sometimes at the expense of people who perhaps didn't know better than to trust charlatans.</p>
<p>One thing that is constantly brought back is how it can be hard to determine what <em>is</em> patent nonsense and what is a <i lang="la">bone fide</i> cure:</p>
<blockquote><p>Prevent blood clotting: Extract mucus from the intestinal membranes of slaughtered pigs or from the lungs of slaughtered cows. Inject. (heparin)</p></blockquote>
<p>Is that any more believable than injecting goat testicles to cure impotence?</p>
<p>It also brings to light how squeamish we can be, especially when faced with unusual medical procedures:</p>
<blockquote><p>We borrow other people’s wombs for surrogacy. And yet, plenty of people squirm at the idea of breastmilk banks. We are a society of contradictions.</p></blockquote>
<p>The eBook is <em>beautifully</em> formatted. For once, images are in-line with the text! Usually in books of this sort, publishers treat the eBook like a mass-market paperback and stick the illustrations at the end.</p>
<p>It ends rather abruptly, after taking us through light, electricity, colour therapy, animal magnetism, and a couple of dozen other "cure-alls"</p>
<p>Sadly, there are no references or footnotes. While I have no reason to doubt the veracity of the claims, for a book about scientific misinformation and malpractice, I'd've expected a way to find out more information.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=74296&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager">Wonders of Web Weaving, Episode 24 - James' Coffee Bloghttps://jamesg.blog/2026/10/20/www-242026-10-20T00:00:00.000ZJames' Coffee Blog
<p><a href="https://web-weaving.jamesg.blog/24">The twenty-fourth episode of Wonders of Web Weaving is out</a>:</p>
<blockquote>
<p>In Episode 24, I chat with <a href="https://alexsirac.com">Alex</a>, the author of <a href="https://alexsirac.com">alexsirac.com</a> about, among other things, collective intelligence in public commons, what the IndieWeb can learn from Wikipedia, community in comments, and more.</p>
</blockquote>
<p>I hope you enjoy the episode!</p>
<p><a href="https://web-weaving.jamesg.blog/subscribe/" rel="noreferrer"><em>Wonders of Web Weaving also has an RSS feed</em></a><em> you can use to follow along from wherever you get your podcasts.</em></p>
<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4d68aa16febfced',t:'MTc5MjQ4Mzc3OA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
<a class="tag" href="https://alexsirac.com">Alex</a>
<a class="tag" href="https://alexsirac.com">alexsirac.com</a>
<a class="tag" href="https://web-weaving.jamesg.blog/24">The twenty-fourth episode of Wonders of Web Weaving is out</a>
<a class="tag" href="https://web-weaving.jamesg.blog/subscribe/">Wonders of Web Weaving also has an RSS feed</a>
contours - James' Coffee Bloghttps://jamesg.blog/2026/10/19/contours2026-10-19T00:00:00.000ZJames' Coffee Blog
<p>Spectra of coherence — ground, sky; all presents. // Deconstruct.<br/>
Where do I find the word that embodies a step?<br/>
— elevation, propulsion, placing; in cycles, forward, forward.<br/>
A mirror of irreducibility.<br/>
How can a word reflect my cold nose, dripping with winter air,<br/>
Breathing in fast, deeply.<br/>
Stories, of moments — <br/>
Collapsed colour, stark senses, every expression:<br/>
Poem, no contours; possible?<br/>
(But in contours definition exists.)</p>
seen - James' Coffee Bloghttps://jamesg.blog/2026/10/19/seen2026-10-19T00:00:00.000ZJames' Coffee Blog
<p>A flat sky<br/>
Field of fog<br/>
Hidden hills<br/>
Echoes of snowfall —<br/>
Not yet.</p><script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4d03410e99dec6f',t:'MTc5MjQxNzMxOA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
A landmark study of AI in newsrooms reveals they need to be coaxed to experiment with their business models - Werd I/O6ad55be517eda60001d0d9eb2026-10-18T23:53:10.000ZWerd I/O<p>Link: <a href="https://cronkite.asu.edu/knightcenter/field-report-AI-in-news"><em>The News + AI Field Report, by Djordje Padejski for the Walter Cronkite School of Journalism and Mass Communication</em></a></p><p>The <a href="https://cronkite.asu.edu/knightcenter/field-report-AI-in-news">News + AI Field Report for 2022-2025</a>, produced by <a href="https://www.djordje.padejski.net/">Djordje Padejski</a> in conjunction with <a href="https://knightcenter.asu.edu/">ASU’s Knight Center for the Future of News</a>, is a landmark analysis of how newsrooms are experimenting with AI. The <a href="https://aimap.futureof.news/">interactive app</a> is also a really useful way to explore the 160 projects covered by this research.</p><p>I’ve been mulling over one of the findings described in the summary:</p><blockquote>“Newsrooms are most worried about the business threat from AI and have built the least for it. Interviews reveal widespread anxiety about the effects of AI on site traffic, audience behavior and news business models. Yet relatively few projects focus on revenue, subscriptions or fundraising.”</blockquote><p>20% of projects were business-related, but every fundraising project and over half of the business projects were grantees in the American Journalism Project’s AI programs. If those newsrooms are stripped out, the numbers drop significantly.</p><p>I think there’s a subtext worth talking about: I suspect the innovation projects listed here (that weren’t a part of a specific cohort) are a temperature check on how newsrooms are building technology / product experiments in general. If that’s the case, it implies that news is sticking to its comfort zone and spending much more time innovating on editorial concerns, and much less time experimenting with solutions for its existential business threats.</p><p>Reader-facing chatbots are a different angle on this: I’d call them a way for newsrooms to say they’re experimenting with AI without actually addressing a core user need. Newsroom websites are absolutely not where the chat layer should live (except, perhaps, for the use cases that provide deeper archive search for subscribers): AI users already have their own chatbots, which are the interfaces for <em>all</em> their AI-powered information. There are deeper ways AI could address user needs; the trick is that they would involve experimenting with the fundamental ways newsrooms do business. In turn, that requires both a willingness to do so, and much deeper collaboration between teams to make it happen.</p><p>So then the question becomes: how do we not just beef up journalism’s experiments in its business models, but the excitement and appetite to do so? Often the product teams that might be the owners of these kinds of experiments don’t (quite reasonably) own business models; how can those teams collaborate effectively? Funding from AJP clearly made a difference — is outside scaffolding required for this work to take place? How might product teams empower every team in the newsroom to run experiments?</p><p>These are much bigger questions than AI – and they’re questions newsrooms probably should be grappling with. In the meantime, I’m very grateful this analysis exists, and I hope it’s renewed to capture the year that follows.</p>2026-10-18 13:22: Terence Eden asking on Mastodon what people's setups look like. Here's mine - 38" ultrawide... - Kev Quirkhttps://kevquirk.com/2026-10-18-13222026-10-18T12:22:00.000ZKev Quirk<p>Terence Eden <a href="https://mastodon.social/@Edent/117461406670274597" rel="noopener noreferrer">asking on Mastodon</a> what people's setups look like. Here's mine - 38" ultrawide LG monitor that docks to my Framework 13, Fully standing desk, fish tank and motorbike helmets on the wall. I love my home office, but I do like heading into the office too.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-10-18-1322/PXL_20261018_121838452.webp" alt="PXL_20261018_121838452" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=2026-10-18%2013%3A22">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-10-18-1322#comments">leave a comment</a>.</p>
</div>Year 4 at the Smallholding - Kev Quirkhttps://kevquirk.com/year-4-at-the-smallholding2026-10-18T12:10:00.000ZKev Quirk<p>So today marks the 4th anniversary of us moving to our 2.5 acre smallholding in north Wales. I can't believe it's been 4 years already - it feels like 5 minutes ago that I wad writing my <a href="https://kevquirk.com/year-3-at-the-smallholding">3 years at the smallholding post</a>.</p>
<p>Last year's update wasn't as positive as previous years thanks to us having to get a new roof, which cost a shit tonne of money, and caused leaks all over the house. But I'm happy to say that the update for this year is mostly positive. I ended last year's post by saying:</p>
<blockquote>
<p>If next year I can get the polytunnel and conservatory done, I'll class that as a win. I'd love to get a quad bike for towing the flail too, but again...money.</p>
</blockquote>
<p>Unfortunately there's no poly tunnel, no insulation in the conservatory, and no quad bike in the barn. Let me explain...</p>
<h2>Goats</h2>
<p>In March of this year we decided it would be a good idea to get some goats. My wife had been talking to a goat farmer in mid-Wales for a few months, and we decided the time was right to get some. So we ordered 4 male pygmy goats and brought them home in the back of the car!</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/goats-home-car.webp" alt="goats-home-car" />
<em>Our first 4 goats arriving home</em></p>
<p>While we were at the farm, the farmer showed us a beautiful half pygmy, half "normal" goat and her 2 kids, along with a little pygmy goat that followed her around like a lost puppy. We immediately warmed to all of them, but we couldn't fit them in the car, so we had to pass.</p>
<p>After a week or so of thinking about them, my wife decided to head back down there and pick them up too. So we ended up with 8 goats all together. I'm <em>really</em> glad that my wife decided to get the second batch, as the mum (now called Betsy) is so friendly and one of my favourite of the group.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/betsy.webp" alt="betsy" />
<em>Betsy being lovely (as usual)</em></p>
<h3>Improving their home</h3>
<p>We're very lucky to have a pair of stables at the back of our property, one was already used by the chickens, so we setup the other for the goats. However, the stables and surrounding needed some work. We had to:</p>
<ul>
<li>Install a land drain and level the ground</li>
<li>Extend the hard standing around the stables</li>
<li>Get some fencing installed</li>
<li>Replace the doors and windows</li>
<li>Install ventilation and power</li>
<li>Create a "rock wall" for the goats to play on</li>
</ul>
<p>Unfortunately this all took up what little budget we had left following the roof, so the poly tunnel and insulation in the conservatory had to wait.</p>
<p>We completed the renovations on the stables and both the chickens and the goats now have a lovely little home that has lots of space, but (more importantly) meets their needs.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/goats-and-chickens.webp" alt="goats-and-chickens" />
<em>Goats and chickens on the rock wall</em></p>
<p>Here's a little gallery of how the work progressed on the stables:</p>
<div class="gallery">
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-00.webp" class="gallery-link" data-pb-lightbox data-caption="Land drain installed and ground leveled">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-00.webp" alt="stables-00" loading="lazy">
</a>
<figcaption class="gallery-caption">Land drain installed and ground leveled</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-01.webp" class="gallery-link" data-pb-lightbox data-caption="Extended hard standing around the stables">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-01.webp" alt="stables-01" loading="lazy">
</a>
<figcaption class="gallery-caption">Extended hard standing around the stables</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-02.webp" class="gallery-link" data-pb-lightbox data-caption="Fencing being installed">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-02.webp" alt="stables-02" loading="lazy">
</a>
<figcaption class="gallery-caption">Fencing being installed</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-03.webp" class="gallery-link" data-pb-lightbox data-caption="New windows and doors being installed">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-03.webp" alt="stables-03" loading="lazy">
</a>
<figcaption class="gallery-caption">New windows and doors being installed</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-04.webp" class="gallery-link" data-pb-lightbox data-caption="All finished...ish">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-04.webp" alt="stables-04" loading="lazy">
</a>
<figcaption class="gallery-caption">All finished...ish</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-05.webp" class="gallery-link" data-pb-lightbox data-caption="Bonus photo of a happy Lando sitting in his food bowl">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/stables-05.webp" alt="stables-05" loading="lazy">
</a>
<figcaption class="gallery-caption">Bonus photo of a happy Lando sitting in his food bowl</figcaption>
</figure>
</div>
<h2>Chickens</h2>
<p>As well as goats, we decided to add to our flock of chickens, but instead of just buying more we wanted to do it a little differently. We have a rooster now, so all our eggs are fertilised and we decided to try and hatch some eggs instead. We also wanted to get something a little different, so my wife bought some guinea fowl eggs, which we put under a broody hen to see if they would hatch. They did, and we ended up with 5 beautiful (but noisy) guinea fowl.</p>
<div class="gallery">
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-01.webp" class="gallery-link" data-pb-lightbox data-caption="Three of the fowl keets">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-01.webp" alt="fowl-01" loading="lazy">
</a>
<figcaption class="gallery-caption">Three of the fowl keets</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-02.webp" class="gallery-link" data-pb-lightbox data-caption="The other 2 with their adopted mum, Nugget">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-02.webp" alt="fowl-02" loading="lazy">
</a>
<figcaption class="gallery-caption">The other 2 with their adopted mum, Nugget</figcaption>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-03.webp" class="gallery-link" data-pb-lightbox data-caption="4 of the 5 fowl today, still with Nugget">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/fowl-03.webp" alt="fowl-03" loading="lazy">
</a>
<figcaption class="gallery-caption">4 of the 5 fowl today, still with Nugget</figcaption>
</figure>
</div>
<p>The guinea fowl were a great success - 5 of the 6 eggs hatched. So we decided to attempt the same thing with some of our chicken eggs, especially since a fox had gotten one of the hens and we wanted to hatch some of her eggs. Unfortunately we had no broody hens at the time, so we decided to buy an incubator and cheat.</p>
<div class="gallery">
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-01.webp" class="gallery-link" data-pb-lightbox data-caption="chickens-01">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-01.webp" alt="chickens-01" loading="lazy">
</a>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-02.webp" class="gallery-link" data-pb-lightbox data-caption="chickens-02">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-02.webp" alt="chickens-02" loading="lazy">
</a>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-03.webp" class="gallery-link" data-pb-lightbox data-caption="chickens-03">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/chickens-03.webp" alt="chickens-03" loading="lazy">
</a>
</figure>
</div>
<p>We attempted to hatch 12 eggs, but only 3 were successful unfortunately. We think 2 of them are roosters too, so we will see how that goes as they grow up and undoubtedly challenge their dad. We're thinking about doing it again this spring, but we're worried about having a plethora of roosters and what we will ultimately have to do to them. 💀</p>
<p>Finally I installed some new perches for the chickens to roost on in the evening. Below is a removable shelf that makes things <em>way</em> easier to clean.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/chicken-perch.webp" alt="chicken-perch" /></p>
<h2>Another blow</h2>
<p>So at this point we had used up <em>everything</em> we had saved. We had literally nothing in our savings account. Then we noticed that our older dog, Tia, who's 14, was getting very smelly breath and whenever she was chewing her favourite ball, her mouth was bleeding.</p>
<p>So we took her to the vets and they told us she had a fairly common type of benign tumour growing in her mouth. Although it was benign, it was <em>very</em> aggressive and if left untreated she would have 6-12 months if she was lucky. And it would be a very uncomfortable end for her.</p>
<p>We had to get it looked at, but this was going to cost <em>thousands</em> and in a 14 year old dog, I wasn't sure if it was worth it. The last thing we wanted was to put her through all this only to find out in a few months that she had another terminal condition that would end her life anyway.</p>
<p class="notice">I know that sounds heartless, but I think at times like this it's important to be practical and take into account how much the dog will suffer, and whether we're doing it for us, or them.</p>
<p>She needed a CT scan on her jaw to see if surgery was viable, so we decided to get a full body CT scan done instead. This would give us a good indication if anything else untoward was going on in her body. Lucky for us the CT came back all clear and the surgery on her jaw was viable, so we went ahead.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/tia-01.webp" alt="tia-01" /></p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/tia-02.webp" alt="tia-02" /></p>
<p>Three operations, 1 infection, and lots of discomfort later for poor Tia, and she's now fully recovered after having around 40% of her lower jaw removed. Unfortunately for her, this means her rather large tongue often dangles out of her mouth these days. But we think she's still gorgeous.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/year-4-at-the-smallholding/tia-03.webp" alt="tia-03" /></p>
<p>As if this wasn't punishment enough, we also got a new puppy, Nelly, at the same time that Tia was going through all this. So managing a sick dog, a new puppy, and keeping 2 kids entertained during their summer holidays really wasn't a fun time.</p>
<div class="gallery">
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/nelly-01.webp" class="gallery-link" data-pb-lightbox data-caption="nelly-01">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/nelly-01.webp" alt="nelly-01" loading="lazy">
</a>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/nelly-02.webp" class="gallery-link" data-pb-lightbox data-caption="nelly-02">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/nelly-02.webp" alt="nelly-02" loading="lazy">
</a>
</figure>
<figure class="gallery-item">
<a href="https://kevquirk.com/content/images/year-4-at-the-smallholding/all-3-dogs.webp" class="gallery-link" data-pb-lightbox data-caption="all-3-dogs">
<img src="https://kevquirk.com/content/images/year-4-at-the-smallholding/all-3-dogs.webp" alt="all-3-dogs" loading="lazy">
</a>
</figure>
</div>
<p>We had bought Nelly way before we knew Tia was sick. The kids knew she was coming, and we had all met Nell multiple times. So although we knew it would be a nightmare, we didn't want to renege on buying her. Let me tell you, dear reader, it was <em>rough</em>. Like, <em>really</em> rough, but we're glad we stuck it out because Nelly is beautiful and such a wonderful addition to our family.</p>
<h2>What's next?</h2>
<p>Tia's surgery ended up costing us around £6,000 (~$8,000). We already had no money, so it had to go on a credit card, which we will pay off over the next few months.</p>
<p>Our plans for year 5 are the same as year 4, I think - we'd love to get a poly tunnel in and the roof in the conservatory insulated so we can use it all year round. But we will need to see how the finances go - hopefully there will be no more unexpected expenses for a little while. We can only hope.</p>
<p>In the meantime we continue to enjoy our brood of various animals and the little plot we call home.</p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=Year%204%20at%20the%20Smallholding">reply to this post by email</a>, or <a href="https://kevquirk.com/year-4-at-the-smallholding#comments">leave a comment</a>.</p>
</div>Una Watch TOTP - MFA on your wrist! - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=761042026-10-18T11:34:08.000ZTerence Eden’s Blog<p>Let's get this out of the way first - this is a <em>terrible</em> idea! I want my two-factor authentication codes to be easily accessible to me, so I've built a watch app which will display them. That's really convenient for me - but makes for a terrible security posture. Anyone who steals my watch will be able to see my one-time code. If they're clever, they can decompile the app and find the secret which powers the code generation.</p>
<p>You should absolutely not do this and, if you ignore my warnings, you should only do this for codes which aren't critical to your security.</p>
<!--
👋👋👋👋👋👋👋👋👋
Welcome to Comment Club! If this is your first time - hello! This content is only available to sophisticated nerds who read my HTML comments.
You know what? I *do* use this. I have a low-ish security account which constantly needs my MFA number. Sometimes I can't be bothered unlocking my phone to get it. Of course, on my wrist I save it under a different name. Perhaps I'm an idiot for doing so, but sometimes convenience wins.
Anyway, the three rules of Comment Club are…
1. You must not tell anyone about Comment Club - let them find out about it themselves.
2. You *must* add some Comment Club Content to at least one of your blog posts. Doesn't have to be much, just a little note will do.
3. You should drop an email to anyone whose comments you've read. Start by sending me a friendly message at comment.club@shkspr.mobi
Keep your eyes peeled for more comments in my other blog posts 😃
TTFN.
-->
<p>In my defence, an attacker would still need my username and password to make any use of these codes - but please don't trust your security to my deranged witterings.</p>
<h2 id="what-it-looks-like"><a href="https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/#what-it-looks-like">What It Looks Like</a></h2>
<img src="https://shkspr.mobi/blog/wp-content/uploads/2026/09/MFA.webp" alt="My wristwatch showing "HSBC MFA Code 751646". Don't worry, this isn't my real code. OR IS IT?!?!" width="1024" height="576" class="aligncenter">
<h2 id="prior-art"><a href="https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/#prior-art">Prior Art</a></h2>
<p>I'm sure many other people have done this. Indeed, three years ago I built <a href="https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/">An eInk, Wrist-Mounted, TOTP Generator</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/07/Wrist-TOTP.jpg" alt="A chunky wristwatch showing the time and a selection of 6 digit codes and their corresponding entities." width="1024" height="1024" class="aligncenter">
<h2 id="the-service"><a href="https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/#the-service">The Service</a></h2>
<p>This is in my <code>Service.cpp</code> - as you can see, it's pretty simple. This won't let you add in hundreds of codes - you get one and that's it. There's no countdown when a code is about to expire, so I recommend waiting until a new one pops up before you start typing it in.</p>
<p>I'll be candid, I haven't used C++ properly since university - and that was in the last century. I've mostly just cut things out to see what breaks. Feel free to educate me on the correct idiomatic code.</p>
<p>You will need to set your own secret in here.</p>
<pre><code class="language-cpp">// Standard includes.
#include "Service.hpp"
#include "SDK/Messages/CommandMessages.hpp"
#include "SDK/Messages/MessageGuard.hpp"
// Needed for PureTOTP.
#include <iostream>
#include "TOTP.h"
#include <string>
#include <sstream>
// What this Glance can do.
Service::Service(SDK::Kernel &kernel)
: mKernel(kernel)
, mGlanceUI()
, mGlanceTitle()
, mGlanceValue()
{}
void Service::createGuiControls()
{
// Set the main title in the Glance.
mGlanceTitle = mGlanceUI.createText();
mGlanceTitle.pos({ kTitleX, kTitleY }, { kTitleW, kTitleH })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_20)
.color(GlanceColor_t::GLANCE_COLOR_TEAL)
.setText("HSBC MFA Code")
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);
// Set the default text for the value.
mGlanceValue = mGlanceUI.createText();
mGlanceValue.pos({ 20, kValueY }, { 200, kValueH })
.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_20)
.color(GlanceColor_t::GLANCE_COLOR_YELLOW_DARK)
.setText( "Waiting for update" )
.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);
}
void Service::glanceUpdate()
{
// Set the Base32 encoded secret (all uppercase).
std::string secret = "QWERTYUIOP";
// Generate the code based on the Unix timestamp right now.
const std::time_t t = std::time(0);
std::string code = TOTP::GenerateTOTP( 6, 30, TOTP::HMAC_SHA1, secret, t );
std::ostringstream ss;
ss << t;
// Convert the string to char* and display it as the value.
mGlanceValue.print( code.c_str() );
// Show the Unix time to check the timezone is correct.
// mGlanceTitle.print( ss.str().c_str() );
}
// No service.
Service::~Service(){}
// When this glance is opened.
void Service::run()
{
while (true) {
SDK::MessageBase *msg;
if(!mKernel.comm.getMessage(msg)) {
continue;
}
switch (msg->getType()) {
// The Glance has started.
case SDK::MessageType::EVENT_GLANCE_START:
if (configGui()) {
createGuiControls();
} else {
mKernel.comm.releaseMessage(msg);
mKernel.sys.exit(0);
}
break;
// The Glance has been stopped.
case SDK::MessageType::COMMAND_APP_STOP:
case SDK::MessageType::EVENT_GLANCE_STOP:
mKernel.comm.releaseMessage(msg);
return;
break;
// Every second, perform an action.
case SDK::MessageType::EVENT_GLANCE_TICK:
onGlanceTick();
break;
default:
break;
}
mKernel.comm.releaseMessage(msg);
}
}
void Service::onGlanceTick()
{
if (mGlanceUI.isInvalid()) {
if (auto upd = SDK::make_msg<SDK::Message::RequestGlanceUpdate>(mKernel)) {
upd->name = APP_NAME;
upd->controls = mGlanceUI.data();
upd->controlsNumber = static_cast<uint32_t>(mGlanceUI.size());
upd.send(100);
}
mGlanceUI.setValid();
}
glanceUpdate();
}
bool Service::configGui()
{
bool status = false;
if (auto gc = SDK::make_msg<SDK::Message::RequestGlanceConfig>(mKernel)) {
if (gc.send(100) && gc.ok()) {
if (gc->maxControls >= 3) {
mGlanceUI.setWidth(gc->width);
mGlanceUI.setHeight(gc->height);
status = true;
}
}
}
return status;
}
</code></pre>
<h2 id="the-library"><a href="https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/#the-library">The Library</a></h2>
<p>There are dozens of C++ libraries for RFC 6238 generation. I ended up using <a href="https://github.com/Einztein/PureTOTP">PureTOTP</a> as it didn't require any external dependencies and was only two files. As I only needed SHA1 TOTP codes, I stripped out a bunch of functionality which was unnecessary for me. Similarly, I only wanted to generate 6 digit codes with a 30 second period - so took out anything which did more than that.</p>
<p>I've tidied up the code and comments where I can. The original was MIT Licenced - so this is as well.</p>
<p>There are two parts to this. First, the header</p>
<pre><code class="language-_">// TOTP.h
//
#pragma once
#include <string>
class TOTP
{
public:
const static int HMAC_SHA1 = 20; // Length of HMAC.
static std::string GenerateTOTP(const std::string& secret);
static std::string GenerateTOTP(int digit, int refreshSeconds, int algorithm, const std::string& secret, int specTime);
TOTP(int digit, int refreshSeconds, int algorithm, const std::string& secret): digit(digit), refreshSeconds(refreshSeconds), algorithm(algorithm), secret(secret) {}
TOTP(const std::string& secret): digit(6), refreshSeconds(30), algorithm(HMAC_SHA1), secret(secret) {}
std::string currentTOTP();
private:
int digit, refreshSeconds, algorithm;
std::string secret;
};
</code></pre>
<p>Next is the standalone library which does the TOTP generation, <code>TOTP.cpp</code></p>
<pre><code class="language-cpp">// TOTP.cpp
//
#include "TOTP.h"
#include <vector>
#include <stack>
#include <queue>
#include <sstream>
#include <iomanip>
#include <cstdint>
#include <cstring>
#include <array>
#include <stdexcept>
#include <algorithm>
using namespace std;
struct JEVP_MD
{
enum Kind {
SHA1
};
Kind kind;
int md_size;
int block_size;
};
static inline uint32_t rotl32(uint32_t x, uint32_t n) { return (x << n) | (x >> (32 - n)); }
static inline uint32_t rotr32(uint32_t x, uint32_t n) { return (x >> n) | (x << (32 - n)); }
static inline uint64_t rotr64(uint64_t x, uint64_t n) { return (x >> n) | (x << (64 - n)); }
static uint32_t load_be32(const unsigned char* p)
{
return (uint32_t(p[0]) << 24) |
(uint32_t(p[1]) << 16) |
(uint32_t(p[2]) << 8) |
uint32_t(p[3]);
}
static void store_be32(uint32_t v, unsigned char* p)
{
p[0] = static_cast<unsigned char>((v >> 24) & 0xFF);
p[1] = static_cast<unsigned char>((v >> 16) & 0xFF);
p[2] = static_cast<unsigned char>((v >> 8) & 0xFF);
p[3] = static_cast<unsigned char>( v & 0xFF);
}
static void store_be64(uint64_t v, unsigned char* p)
{
p[0] = static_cast<unsigned char>((v >> 56) & 0xFF);
p[1] = static_cast<unsigned char>((v >> 48) & 0xFF);
p[2] = static_cast<unsigned char>((v >> 40) & 0xFF);
p[3] = static_cast<unsigned char>((v >> 32) & 0xFF);
p[4] = static_cast<unsigned char>((v >> 24) & 0xFF);
p[5] = static_cast<unsigned char>((v >> 16) & 0xFF);
p[6] = static_cast<unsigned char>((v >> 8) & 0xFF);
p[7] = static_cast<unsigned char>( v & 0xFF);
}
static vector<unsigned char> sha1_hash(const unsigned char* data, size_t len)
{
vector<unsigned char> msg(data, data + len);
uint64_t bit_len = static_cast<uint64_t>(len) * 8;
msg.push_back(0x80);
while ((msg.size() % 64) != 56) msg.push_back(0x00);
unsigned char lenbuf[8];
store_be64(bit_len, lenbuf);
msg.insert(msg.end(), lenbuf, lenbuf + 8);
uint32_t h0 = 0x67452301;
uint32_t h1 = 0xEFCDAB89;
uint32_t h2 = 0x98BADCFE;
uint32_t h3 = 0x10325476;
uint32_t h4 = 0xC3D2E1F0;
for (size_t chunk = 0; chunk < msg.size(); chunk += 64)
{
uint32_t w[80];
for (int i = 0; i < 16; ++i)
w[i] = load_be32(&msg[chunk + i * 4]);
for (int i = 16; i < 80; ++i)
w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;
for (int i = 0; i < 80; ++i)
{
uint32_t f, k;
if (i < 20)
{
f = (b & c) | ((~b) & d);
k = 0x5A827999;
}
else if (i < 40)
{
f = b ^ c ^ d;
k = 0x6ED9EBA1;
}
else if (i < 60)
{
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
}
else
{
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = rotl32(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = rotl32(b, 30);
b = a;
a = temp;
}
h0 += a;
h1 += b;
h2 += c;
h3 += d;
h4 += e;
}
vector<unsigned char> out(20);
store_be32(h0, &out[ 0]);
store_be32(h1, &out[ 4]);
store_be32(h2, &out[ 8]);
store_be32(h3, &out[12]);
store_be32(h4, &out[16]);
return out;
}
static const JEVP_MD g_sha1 = {JEVP_MD::SHA1, 20, 64};
const JEVP_MD* JEVP_sha1() { return &g_sha1; }
unsigned char* JHMAC(const JEVP_MD* JEVP_MD, const void* key, int key_len, const unsigned char* data, size_t data_len, unsigned char* md, unsigned int* md_len)
{
if (!JEVP_MD || !key || key_len < 0 || !data)
return nullptr;
static thread_local unsigned char static_md[64];
if (!md)
md = static_md;
vector<unsigned char> key_block(JEVP_MD->block_size, 0);
const unsigned char* key_bytes = static_cast<const unsigned char*>(key);
vector<unsigned char> real_key;
if (key_len > JEVP_MD->block_size)
{
real_key = sha1_hash(key_bytes, static_cast<size_t>(key_len));
std::copy(real_key.begin(), real_key.end(), key_block.begin());
}
else
{
std::copy(key_bytes, key_bytes + key_len, key_block.begin());
}
vector<unsigned char> ipad(JEVP_MD->block_size);
vector<unsigned char> opad(JEVP_MD->block_size);
for (int i = 0; i < JEVP_MD->block_size; ++i)
{
ipad[i] = static_cast<unsigned char>(key_block[i] ^ 0x36);
opad[i] = static_cast<unsigned char>(key_block[i] ^ 0x5C);
}
vector<unsigned char> inner;
inner.reserve(JEVP_MD->block_size + data_len);
inner.insert(inner.end(), ipad.begin(), ipad.end());
inner.insert(inner.end(), data, data + data_len);
vector<unsigned char> inner_hash = sha1_hash(inner.data(), inner.size());
vector<unsigned char> outer;
outer.reserve(JEVP_MD->block_size + inner_hash.size());
outer.insert(outer.end(), opad.begin(), opad.end());
outer.insert(outer.end(), inner_hash.begin(), inner_hash.end());
vector<unsigned char> final_hash = sha1_hash(outer.data(), outer.size());
std::memcpy(md, final_hash.data(), final_hash.size());
if (md_len) *md_len = static_cast<unsigned int>(final_hash.size());
return md;
}
// Base32 decoding table.
/* clang-format off */
const int base32_reverse_map[128] =
{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 15
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 31
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 47
// (49) 2 3 4 5 6 7 (56)
255, 255, 26, 27, 28, 29, 30, 31, 255, 255, 255, 255, 255, 255, 255, 255, // 63
// (64) A B C D E F G H I J K L M N O
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 79
// P Q R S T U V W X Y Z (91)
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // 95
// (96) A B C D E F G H I J K L M N O // lower case instead
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 111
// P Q R S T U V W X Y Z (123) // lower case instead
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255 // 127
};
/* clang-format on */
// Base32 decoding function.
vector<unsigned char> base32_decode(const string& base32)
{
queue<int> q_rev;
for (const char& c : base32)
{
if (c <= 49 || (c >= 56 && c <= 64) || (c >= 91 && c <= 96) || c >= 123)
{ // Illegal lower case will be treat as upper case thanks to base32_reverse_map.
if (c == '=') break;
}
else
{
q_rev.push(base32_reverse_map[(int)c]); // Get integers of base32.
}
}
queue<bool> q_bin;
while (!q_rev.empty()) // Transform the integers into bits and queue them up.
{
int now = q_rev.front(); // Get a integer from queue.
q_rev.pop();
stack<bool> sb;
for (int i = 0; i < 5; i++) // Push the bits of this integer into stack
{ // 5 bits into 1 group
sb.push(now & 1); // Push the last bit of this integer into stack.
now >>= 1; // Flush the last bit.
}
while (!sb.empty()) // Flush the bits of this integer into binary queue.
{
q_bin.push(sb.top()); // Pop bits from stack into binary queue.
sb.pop();
}
}
vector<unsigned char> uc;
while (!q_bin.empty()) // Transform the bits into bytes.
{
int thehex = 0;
for (int i = 0; i < 8; i++) // Read a byte from the bits queue.
{
if (q_bin.empty())
{ // If the bits queue is empty now,
thehex <<= 8 - i; // set 0 on the last bits of a byte.
break;
}
thehex <<= 1; // Make the space for the new bit.
thehex |= (int)q_bin.front(); // Push this bit into byte.
q_bin.pop();
}
uc.push_back((unsigned char)thehex);
}
if (uc.back() == 0) uc.pop_back(); // TOTP may abandon last zero.
if (uc.size() % 2 != 0) uc.pop_back(); // TOTP may abandon odd res.
return uc;
}
// Get Unix Time.
time_t getUnixTime()
{
return time(0);
}
// Calculate the TOTP time signature.
time_t getTimeSign(int refreshPeriod, time_t specUnixTime)
{
time_t outTime = specUnixTime / refreshPeriod;
return outTime;
}
// Transform time_t aka <long long> to bytes.
vector<unsigned char> llong2bytes(time_t tt)
{
uint64_t value = static_cast<uint64_t>(tt);
vector<unsigned char> bytes(8);
for (int i = 7; i >= 0; --i)
{
bytes[i] = static_cast<unsigned char>(value & 0xFF);
value >>= 8;
}
return bytes;
}
// Fast Exponentiation algorithm.
unsigned fastpow(unsigned base, unsigned index)
{
if (index == 0) return 1;
unsigned t = 1;
while (index != 0)
{
if (index & 1) t *= base;
index >>= 1;
base *= base;
}
return t;
}
// Calculate TOTP according to RFC standard.
std::string hash_to_TOTP(const unsigned char* hmac, unsigned int length, int digit)
{
int offset = hmac[length - 1] & 0x0F;
uint32_t bin_code = ((hmac[offset] & 0x7F) << 24) |
((hmac[offset + 1] & 0xFF) << 16) |
((hmac[offset + 2] & 0xFF) << 8) |
( hmac[offset + 3] & 0xFF);
uint32_t mod = 1;
for (int i = 0; i < digit; ++i) mod *= 10;
uint32_t otp = bin_code % mod;
std::ostringstream oss;
oss << std::setw(digit) << std::setfill('0') << otp;
return oss.str();
}
std::string TOTP::GenerateTOTP(const std::string& secret)
{
return GenerateTOTP(6, 30, HMAC_SHA1, secret, getUnixTime());
}
std::string TOTP::GenerateTOTP(int digit, int refreshSeconds, int algorithm, const std::string& secret, int specTime)
{
vector<unsigned char> h_msg = llong2bytes(getTimeSign(refreshSeconds, specTime)); // Get current time signature and transform time_t aka <long long> to bytes.
vector<unsigned char> h_key = base32_decode(secret); // Transform base32 encoded secret key to decoded bytes.
unsigned char* out_hmac = new unsigned char[algorithm]; // Perform openssl function to calculate HMAC-SHA1 hash.
unsigned int out_hmac_length;
const JEVP_MD* emd;
emd = JEVP_sha1();
JHMAC(emd, h_key.data(), (int)h_key.size(), h_msg.data(), (int)h_msg.size(), out_hmac, &out_hmac_length);
string fine = hash_to_TOTP(out_hmac, out_hmac_length, digit);
delete[] out_hmac;
return fine; // Calculate TOTP according to RFC standard.
}
std::string TOTP::currentTOTP()
{
return GenerateTOTP(digit, refreshSeconds, algorithm, secret, getUnixTime());
}
</code></pre>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76104&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager">Olivia Dean at Western Springs - The Weblog of fLaMEdhttps://flamedfury.com/posts/olivia-dean-at-western-springs/2026-10-18T07:07:30.000ZfLaMEd<p>What’s going on, Internet? Saturday was a pretty good day. Sun was shining, but damn it was freezing cold (literal shivering) when out of the sun. So after we had dinner down at Ralph’s on our way to Western Springs to see Olivia Dean we decided to make a quick detour home to grab our jackets.</p>
<p>Lucky we did, because damn, it was cold. Western Springs is an outdoor stadium. We were amongst 45,000 people who turned up to see Olivia Dean. She kicked off on time at 9pm, but three songs in disaster struck. Equipment failure. She was visibly fustrated, but wanted to reassure the crowd… Forty minutes later she was back in action and completed her set.</p>
<p>It was cold, it was getting late and that forty minutes wrecked me so we dipped a bit early before she wrapped up.</p>
<p>Home and in bed before 11:30. Gotta pick up some of her vinyl now.</p>
<p>Hey, thanks for reading this post in your feed reader! Want to chat? <a href="mailto:hello@flamedfury.com?subject=RE: Olivia Dean at Western Springs">Reply by email</a> or add me on <a href="xmpp:flamed@omg.lol">XMPP</a>, or send a <a href="https://flamedfury.com/posts/olivia-dean-at-western-springs/#webmention">webmention</a>. Check out the <a href="https://flamedfury.com/posts/">posts archive</a> on the website.</p>
Forum accounts are being connected to Johnny.Decimal accounts - Johnny.Decimalhttps://johnnydecimal.com/blog/0257-forum-account-connection/2026-10-18T06:21:07.000ZJohnny.Decimal<p>Until today, your forum account has been a completely separate account to your Johnny.Decimal account.</p>
<p>This was confusing. People wonder why they needed <em>another</em> account for the forum.</p>
<p>As of today, they're connected. When you log in at the forum, there's a button to do that <strong>With your Johnny.Decimal account</strong>.</p>
<p>Existing forum logins will stop working in a month.</p>
<p>There are more details in <a href="https://forum.johnnydecimal.com/t/the-forum-login-is-changing-to-your-johnny-decimal-account/2980">this pinned forum post</a>. There's a bit of a migration process if your email addresses don't match, sorry. If you have any problems, post in that thread or <a href="mailto:hello@johnnydecimal.com">email me</a>.</p>Digital recipe management for fastidious cooks - localghosthttps://localghost.dev/blog/digital-recipe-management-for-fastidious-cooks/2026-10-18T00:00:00.000ZSophie Koonin<p>Much like your favourite recipe websites, I'm not going to get straight to the point, I'm going to start with a backstory and you're not getting a skip link.</p>
<p>Having grown up with a dad who read cookbooks for leisure, and who caused me to turn vegetarian for several weeks once at the age of 13 when he presented me with a lamb heart stew for dinner, it was inevitable that I would be a foodie. Dad’s squid and red wine stew was my favourite dish as a child. He can't cook any more, but I have fond memories of those meals (even if I did eat as fast as I could so I could leave the table and go back on MSN) and I owe my inquisitive palate to his globetrotting home cooking. As middle-class Brits go, I’m as Waitrose as they come: say “Ottolenghi” three times into a Guardian tote bag and I’ll appear in a puff of sumac.</p>
<p>These days, cooking for people is how I show love, which I definitely got from Dad. While the novelty's worn off for feeding myself and my husband (sorry James), I really enjoy having people over and putting on a spread (even if the kitchen then looks like a hurricane tore through it). Even at university, we used to hold parties in our student house where I'd cook for two solid days making sliders with homemade buns, big pots of chilli, clumsily-knotted pretzels, salads and cakes. Every Christmas I spend a weekend baking biscuits and sweets to post out to friends and family.</p>
<p>I used to have a shelf full of cookbooks. Only instead of being lovingly creased and food-stained through use like my parents’ books are, mine sat on a shelf gathering dust, largely untouched, pages spotless and not at all stuck together. My problem is that I have very poor object permanence: if something’s not in immediate sight, I will forget it’s there. I couldn’t remember the recipes that I had in the books, or even that I <em>had</em> the books in the first place, so I never used them. (The same logic applies to the very old packets of various seasonings in the kitchen cupboards.)</p>
<p>I’ve found the only way I reliably use recipes is if I have them available digitally. I’ve been collecting recipes from food blogs and recipe websites for a long time now - originally storing them in Evernote, which is what we did in the 2010s.</p>
<p>And somehow, over the last decade, I’ve amassed over 5,000 recipes. Have I made all of them? Of course I haven’t. And there's definitely a few I come back to again and again. But it’s nice to know I have them in case I find myself in desperate need of a carrot cake recipe (or five).</p>
<h2 id="paprika" tabindex="-1">Paprika</h2>
<p>For the last 7 years or so I’ve been using <a href="https://www.paprikaapp.com/">Paprika 3</a> to manage my digital recipe collection. It’s a solid cross-platform app with cloud sync and a one-off cost per-platform rather than a subscription. I can clip recipes on my laptop, plan the next week's meals on my phone on the train, and I've got an old iPad sitting on the kitchen counter to view the recipe on while I'm cooking.</p>
<p>All the recipe management apps I’ve tried have largely the same features:</p>
<ul>
<li>clip recipes from the web</li>
<li>browse recipe websites from within the app, and download recipes</li>
<li>create recipes from scratch</li>
<li>scale and convert ingredient units</li>
<li>tag recipes</li>
<li>create meal plans</li>
<li>create shopping lists from recipes</li>
</ul>
<p>I've categorised my recipes into what book they came from, different types of baked goods, different situations - for example, I've got a "quick weeknight dinners" category that's great if we just need something fast for dinner midweek.</p>
<figure><picture><source type="image/webp" srcset="https://localghost.dev/img/n_sre3d4V0-280.webp 280w, https://localghost.dev/img/n_sre3d4V0-566.webp 566w" sizes="auto"><img loading="lazy" decoding="async" src="https://localghost.dev/img/n_sre3d4V0-280.jpeg" alt="A screenshot of some of the categories I've created in Paprika, laid out as a folder tree. There's Air Fryer, Baking, which has subcategories: Biscuits & cookies, Bread & rolls, Cake fillings & icings, Cakes, Crackers & savoury biscuits, Dough & pastry, Dumplings, Filled and flavoured breads, Flatbreads, Pastries & patisserie, Pizzas & tarts, Sweet breads & rolls, Sweet tarts & pies, Sweets, Traybakes, Vegan baking; Batch cook, BBQ, Books, which is cut off but you can see the first few - A Good Day To Bake, A Modern Way to Cook, Atsuko's Japanese Kitchen" class="small" width="566" height="1350" srcset="https://localghost.dev/img/n_sre3d4V0-280.jpeg 280w, https://localghost.dev/img/n_sre3d4V0-566.jpeg 566w" sizes="auto"></picture></figure>
<h2 id="goldilocks-and-the-three-recipe-managers" tabindex="-1">Goldilocks and the three recipe managers</h2>
<p>Paprika is a stalwart in the recipe app scene (and believe me, I’ve tried most of them), but it hadn’t been updated for years and I was getting a bit frustrated. It was <em>so</em> good, but lacked any kind of smart features - I was so bored of manually tagging recipes, and my extremely convoluted way of digitising recipe books and importing them into my collection was tedious as hell. My kingdom for literally any kind of OCR COME ON PEOPLE IT'S 2026. (Though while writing this I have just come across this <a href="https://github.com/IanDBallard/RecipeParser">Paprika Recipe Parser</a> tool which I’ll definitely try out.)</p>
<p>Some newer competitors came on the scene post-2020, and offered some nice features Paprika didn’t have. <a href="https://pestlechef.app">Pestle</a> is a lovely-looking app which has really leant into the capabilities of Apple Intelligence, making importing super easy, and also supports importing from Instagram and TikTok; <a href="https://mela.recipes/">Mela</a> (by the same person who built <a href="https://reeder.app/">Reeder</a>) integrates with Apple Reminders and has a built-in RSS reader, which would be perfect for the food blogs I subscribe to. But I always found myself coming back to Paprika because other apps didn’t support nested recipe folders (which sounds incredibly nitpicky, but is a non-negotiable for me!)<a href="https://localghost.dev/blog/digital-recipe-management-for-fastidious-cooks/#footnote">*</a>.</p>
<p>I mentioned I had an extremely convoluted way of importing recipes from recipe books to my Paprika collection. Well.</p>
<p>If it’s a physical cookbook, I have to take photos of the pages, use Apple’s OCR in Preview to copy the text, then manually create the recipe in Paprika, which is so boring I rarely actually get round to it. If it’s an ebook, it’s a ridiculous workflow that I think I found on Reddit:</p>
<ul>
<li>import epub into <a href="https://calibre-ebook.com">Calibre</a></li>
<li>convert the book to ZIP (an archive of HTML files)</li>
<li>extract into a folder</li>
<li>fix some of the garbled XHTML files that Calibre inevitably outputs</li>
<li>run a local HTTP server in the folder</li>
<li>open the IP address in Paprika’s in-app browser, and clip each recipe individually while half-watching ER (and I can't even use the "Download recipe" button because there are usually several recipes per outputted page)</li>
</ul>
<p>Given how boring that was to <em>read</em>, I'm sure you can guess how often I can be bothered to do that.</p>
<p><small id="footnote">* If you're wondering why I don't just build one myself that does everything I want it to, the answer is that I have enough software maintenance to do in my day job, and my free time is for petting dogs </small></p>
<h3 id="paprika-4-is-here-and-it-does-the-thing-now" tabindex="-1">Paprika 4 is here and it does the thing now</h3>
<p>Great news for me: I happened to look at Paprika’s website yesterday and saw that they’d <a href="https://www.paprikaapp.com/news/2026/09/28/paprika-4-is-now-in-open-beta/">opened up the <strong>Paprika 4 beta</strong></a>, which supports - yesssss - <strong>recipe importing from photos</strong> and social media, recipe sharing, bulk categorisation, as well as being able to guess what categories you might want to add to a recipe! I’m extremely particular about the categories I want on my recipes, but also too lazy to scroll down a list and add them to recipes one by one, so this is a total game changer. (It is LLM-powered, so isn’t perfect - it keeps trying to categorise everything as being from “Dinner” by Meera Sodha - but I expect it’ll stop doing that eventually.)</p>
<p>I tested it out by importing recipes from the <a href="https://farmerj.com">Farmer J</a> cookbook I’d picked up from a charity shop. It worked a treat! It’s basically completely shortcutted the workflow I had going before, and I don’t need to fill out any of the metadata except the category (which, as I mentioned, is super quick to do now).</p>
<figure><picture><source type="image/webp" srcset="https://localghost.dev/img/vlCwcwW2qV-280.webp 280w, https://localghost.dev/img/vlCwcwW2qV-640.webp 640w, https://localghost.dev/img/vlCwcwW2qV-960.webp 960w" sizes="auto"><img loading="lazy" decoding="async" src="https://localghost.dev/img/vlCwcwW2qV-280.jpeg" alt="A screenshot of the final recipe that Paprika scanned in, cut off so you can't see the whole method. The title is 'Farmer J Mac' and there's a very enticing looking picture of golden baked mac and cheese with broccoli. It's clipped the timings for the recipe - prep 15 mins, cook 30 mins - and the number of servings (4). The method is a bit cut off on purpose but it says '1. Preheat the oven to 180°C fan (200°C/400°F). 2. Get your veg going. Chop whatever's in season and give it a good toss with olive oil, salt and pepper. Roast until golden and soft around the edges. Set aside. 3. Cook the pasta in a large pan of salted boiling water until al dente. Drain and set aside. 4. Make the béchamel. Melt the butter in a large pan, then add the flour and whisk for 2-3 minutes until smooth. Slowly pour in the warm milk, whisking'. It's clipped all the ingredients nicely: 400-500g seasonal veg (see facing page); Olive oil, for drizzling; 450g elbow macaroni or cavatappi; 2 tbsp unsalted butter; 2 tbsp plain flour; 1 litre whole milk, warmed; 100g extra mature Cheddar, grated;50g Parmesan, finely grated; Pinch of grated nutmeg (optional); Pinch of chilli flakes (optional); 100g mozzarella; grated Sea salt and black pepper." width="960" height="438" srcset="https://localghost.dev/img/vlCwcwW2qV-280.jpeg 280w, https://localghost.dev/img/vlCwcwW2qV-640.jpeg 640w, https://localghost.dev/img/vlCwcwW2qV-960.jpeg 960w" sizes="auto"></picture></figure>
<p>Having switched to buying only epubs, I miss having a shelf full of cookbooks, so maybe I’ll start buying physical ones again if they're this simple to import now - I love it when I go to someone’s house and they have a copy of a cookbook I really like - or I can just screenshot ebook pages to import them, which is slightly less convenient but still way easier than the stupid dance I had to do before.</p>
<p>Paprika 4 will be <a href="https://paprikaapp.zendesk.com/hc/en-us/articles/41887059110167-Will-Paprika-4-have-a-recurring-subscription-fee">moving to a subscription model</a>, which is unsurprising - the new features use LLMs, which aren't free. But it looks like existing users can keep using Paprika for free without the new features if they want.</p>
<p>And if you ever need a good recipe, let me know - I've got a few.</p>Weeknotes W1 - James' Coffee Bloghttps://jamesg.blog/2026/10/18/weeknotes2026-10-18T00:00:00.000ZJames' Coffee Blog
<p>Many of my pieces of writing, especially my creative non-fiction and Nature pieces, describe a single moment or the events that happened over the course of a day. With that in mind, one of the skills I want to develop in my writing is to consider longer periods of time in my writing – to tell stories that expand over greater lengths.</p>
<p>During the <a href="https://indieweb.org/events/2026-10-14-hwc-asia-pacific">Homebrew Website Club Asia/Pacific meetup this week</a> – open to all, hosted at a time that works well in the Asia/Pacific timezone by <a href="https://burgeonlab.com">Naty</a> – the topic of weeknotes came up. This got me thinking. Could weeknotes be a way for me to think about a longer time horizon in my writing? Shortly thereafter, I decided I wanted to try weeknotes again, this time committing to writing one week note each week for four weeks, ideally on a Sunday evening.</p>
<p>I suspect my weeknotes will be oriented more around bullet points than longer prose, but, in any case, I am coming at weeknotes afresh (<a href="https://jamesg.blog/weeknotes">I have tried to author weeknotes twice</a>, but on my last try I ended up spending more time making a custom page layout than writing, which may have made the practice less sustainable). I am keen to see what I write and share!</p>
<h2 id="the-highlights">The highlights</h2>
<p><em>Herein, events of the week.</em></p>
<p>This was a busy week in terms of studying. I am continuing to work my way through the “art and the environment” block that I am studying. This week I spent a lot of time reading the textbook. I especially enjoyed learning about the Frost Fairs in London. Part of our work was focused on what we can learn about European attitudes to cold climates by analysing both art and prints from the London Frost Fairs as well as art and documents pertaining to the Arctic.</p>
<p>We did a “virtual gallery visit” where one of our lecturers presented photos of and historical background about artefacts from the Uffizi Gallery in Florence, discussing them in the context of our module. This was my second time attending such a visit, and I got a lot out of it. I didn't realise the extent of the excess wealth the Medici family had.</p>
<p>This was a busy week for events, too. I hosted a Homebrew Website Club (HWC) on Wednesday (<a href="https://indieweb.org/events/2026-10-14-hwc-europe">notes</a>), a HWC Writing discussion on Saturday (<a href="https://indieweb.org/events/2026-10-17-hwc-writing">notes</a>), and the 32-Bit Café Web Weaving Workshop today, as well as helping out with a non-technical event in a different community. Reflecting on these events in aggregate, they all have their own identity: I ask different opening questions in each, discussions flow in different ways. Indeed, when I am hosting an event I have to be “on” as much as possible; even if I take a few moments to find a link or write notes, I still need to be attentive to the wider room, thinking about how to make sure discussion continues between several participants. <em>Perhaps I should write something about hosting online events. Reader, what would you like to know?</em></p>
<p>I had one day this week where I had <em>four</em> events I wanted to go to that were scheduled for the same three hour window: <a href="https://indieweb.org/Front_End_Study_Hall">Front End Study Hall</a>, the aforementioned virtual gallery visit, another university event, and a writing club event. It was hard to choose, but I decided to attend the first 30 mins of Front End Study Hall, which started before all the other events, then the full gallery visit. This worked out well: I had a great evening, and I didn't end up pushing myself to try and be in too many places at once.</p>
<p>This afternoon I watched the last game in the NFL London series on the television. Stroud’s passes were amazing. The Jaguars had me on the edge of my seat many times due to flags (a problem in their game in London last week too). I missed the final minute, at which point the game was equal. I just read the final score and the <a href="https://www.skysports.com/nfl/news/13600610/nfl-london-games-2026-houston-texans-beat-jacksonville-jaguars-after-kaimi-fairbairns-walk-off-field-goal">Texans won 34-31</a>. I am not sure how many more games I will watch this season – the London games are on at such a convenient time – but I do want to learn more about how to “follow” a sport without watching the games. Any suggestions would be sincerely appreciated.</p>
<h2 id="music-corner">Music corner</h2>
<p><em>Herein, music I have enjoyed listening to this week.</em></p>
<ul>
<li>Courtney Barnett’s music is amazing. Thank you <a href="https://thamara.co.uk">Thamara</a> for the recommendation. I have listened to almost two whole album after learning about her music yesterday. Barnett is one of many Australian singers whose music I really appreciate, others being Hilltop Hoods, Montaigne, and Angus & Julia Stone.</li>
<li>Taylor Swift released two new versions of Patient Zero: the acoustic and piano versions. Of the two, the piano version is my favourite. I shall have to listen to it again after finishing this post.</li>
<li>I am on the lookout for hip-hop / rap bands, ideally that are more words-focused in their songs (and don’t sing too fast). If you have any recommendations, send me an email!</li>
<li>I listened to a few songs by Sigur Rós this week whose music I loved. Any more recommendations along these lines would be sincerely appreciated!</li>
</ul>
<h2 id="thoughts,-wandering">Thoughts, wandering</h2>
<p><em>Herein, wondering thoughts as-yet not explicitly included in my writing.</em></p>
<ul>
<li>How titles change a work.</li>
<li>“Reading the texts of time.”</li>
<li>I haven’t been doing many technical changes to my site lately. I haven’t had much inspiration for the coding side of my site of late. I feel both satisfied with the design of my website and also feeling like I want to make something new. Maybe a <a href="https://playground.jamesg.blog">new page in my playground</a>?</li>
<li>I am interested in why bookshops are so fascinating to me even when I know I don’t want to buy any books because I have some to read already. I’m thinking of bookshops as a place for visual inspiration.</li>
<li>I would like to make more zines, but I am also struggling to find the motivation. I saw a cool zine in a bookshop at the weekend that had a photo on the front cover and was tiny. I wonder if I could make a tiny photo zine?</li>
<li>Blogging encourages writing to become part of life.</li>
<li>I need new shoes for the winter. I always procrastinate on shoe shopping because I struggle to find shoes that are comfortable and that I like.</li>
</ul>
<h2 id="miscellanea">Miscellanea</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Santiago_Calatrava">Santiago Calatrava</a> has worked on many impressive buildings. Two that stood out that he has worked on are the <a href="https://en.wikipedia.org/wiki/Milwaukee_Art_Museum">Milwaukee Art Museum</a> and <a href="https://en.wikipedia.org/wiki/Li%C3%A8ge-Guillemins_railway_station">Liège-Guillemins railway station</a>.</li>
<li>This series of Wonders of Web Weaving is coming to a close. I am not sure if there will be another series yet, but w will see! I will think more about that in the next year.</li>
<li>I thought I had more links to share in this post, but I looked at my bookmarks and I realised I haven’t saved many. I should keep this in mind for next week!</li>
<li><a href="https://burgeonlab.com/notes/2026/1017-2202/">Naty’s recent feed changes</a> caused some people to see all of her posts again in their web readers. This is common in tweaking web feeds. I have done the same in the past. I learned this week that Artemis was able to handle the changes with grace and didn’t show all of the posts in Naty’s feed as new. I can’t exactly remember all of the technical decisions I made to make this happen – it has been a while since I touched the polling part of the project – but I am glad to see things working.</li>
<li><a href="https://www.bbc.co.uk/programmes/m0032jvl">The Book That Changed My Life</a> was a wonderful documentary. After the documentary I found myself thinking more about my favourite book, <a href="https://jamesg.blog/2026/01/15/walden">Walden</a>.</li>
<li>Seeing <a href="https://joelchrono.xyz">Joel</a> and <a href="https://burgeonlab.com">Naty</a> publish weeknotes is what inspired me to give this a go. I have had fun! This is a different kind of writing from my other posts, but it’s nice to think through the week a little bit.</li>
</ul>
<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4ca43c56f2fab71',t:'MTc5MjM1NTA0Nw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
<a class="tag" href="https://burgeonlab.com">Naty</a>
<a class="tag" href="https://burgeonlab.com/notes/2026/1017-2202/">Naty’s recent feed changes</a>
<a class="tag" href="https://en.wikipedia.org/wiki/Li%C3%A8ge-Guillemins_railway_station">Liège-Guillemins railway station</a>
<a class="tag" href="https://en.wikipedia.org/wiki/Milwaukee_Art_Museum">Milwaukee Art Museum</a>
<a class="tag" href="https://en.wikipedia.org/wiki/Santiago_Calatrava">Santiago Calatrava</a>
<a class="tag" href="https://indieweb.org/Front_End_Study_Hall">Front End Study Hall</a>
<a class="tag" href="https://indieweb.org/events/2026-10-14-hwc-asia-pacific">Homebrew Website Club Asia/Pacific meetup this week</a>
<a class="tag" href="https://indieweb.org/events/2026-10-14-hwc-europe">notes</a>
<a class="tag" href="https://indieweb.org/events/2026-10-17-hwc-writing">notes</a>
<a class="tag" href="https://jamesg.blog/2026/01/15/walden">Walden</a>
<a class="tag" href="https://jamesg.blog/weeknotes">I have tried to author weeknotes twice</a>
<a class="tag" href="https://joelchrono.xyz">Joel</a>
<a class="tag" href="https://playground.jamesg.blog">new page in my playground</a>
<a class="tag" href="https://thamara.co.uk">Thamara</a>
<a class="tag" href="https://www.bbc.co.uk/programmes/m0032jvl">The Book That Changed My Life</a>
<a class="tag" href="https://www.skysports.com/nfl/news/13600610/nfl-london-games-2026-houston-texans-beat-jacksonville-jaguars-after-kaimi-fairbairns-walk-off-field-goal">Texans won 34-31</a>