Shellsharks Blogroll - BlogFlock https://blogflock.com/list/xJ8yq 2026-10-14T17:06:31.000Z BlogFlock shellsharks Proposed Events for Meetable - Articles by Aaron Parecki https://aaronparecki.com/2026/10/14/6/meetable-proposed-events 2026-10-14T17:06:31.000Z Aaron Parecki <p>Meetable can now replace your Doodle polls! Meetable, my open source event listing website, now lets you propose an event with several possible dates/times. People vote on which one works for them, and the organizer can choose the best date from the votes later.</p> <p><img src="https://aaronparecki.com/2026/10/14/6/image-1.png" alt=""></p> <p>Up until now, every event in Meetable needed a date before it could be created. That works fine when the date is already known, but a lot of events start out as "we should do this, when can everyone make it?" That part of the planning had to happen somewhere else, in a poll or a chat thread, and then someone would create the event on the calendar once the date was decided. In the meantime, nobody browsing the calendar would know the event was being planned at all.</p> <p>I was a little unsure of how best to add this since almost everything in Meetable is built around the event date. The home page sorts by it, the ics feeds need it, and even the permalink of the event is made from it. I also didn't want the calendar to end up full of events that are "maybe" happening, mixed in with events that are actually scheduled. So the goal was to support this without the proposals getting in the way of the regular calendar.</p> <p>Here's how it works:</p> <ul> <li>Proposed events are off by default, and can be turned on in the site settings.</li> <li>Anyone who can create events can also propose one. There is a new "Propose an Event" option in the "Add Event" menu.</li> <li>It's the same form as creating a regular event, except instead of a date, you enter at least two possible dates. Times are optional, and you can give a date an end date to propose a multi-day event.</li> <li>Proposed events don't appear in the main event list, ics feeds, Discord notifications or search. Instead they're listed on a new "Proposed Events" page in the navigation, and there is a banner on the home page when any events are looking for a date.</li> <li>Anyone who is logged in can vote "Yes", "If need be" or "No" on each date. Votes are saved as soon as you click, and the profile photos of everyone who voted are shown with each answer so you can see who can make which date.</li> <li>The date with the most "yes" votes is marked as "Leading". (Ties are broken by the most "if need be" votes, then the fewest "no" votes.)</li> <li>When it's time to decide, anyone who can edit the event clicks "Schedule this date" next to one of the options. The event gets that date, shows up on the calendar at its normal permalink, and voting closes.</li> <li>The old proposed event URL redirects to the new permalink so that any links shared during the vote keep working.</li> <li>The poll is kept on the event page after the date is chosen, collapsed under a note like "The date was chosen from 4 proposed dates", so you can see how the decision was made.</li> <li>If you ask for a Zoom meeting when proposing the event, the meeting is scheduled once the date is chosen, since there is no time to schedule it for until then.</li> <li>If you edit the proposed dates later, votes are kept for the dates that didn't change. Removing or changing a date removes its votes, since those votes were for a different date.</li> </ul> <p>There is currently no deadline for voting and no reminder sent to the organizer, so a proposed event will sit there until someone chooses a date or deletes it. I'd like to see how this gets used before adding anything more to it, so if a voting deadline or something like that would be useful for your community, let me know.</p> <p>Hopefully this makes it easier to get events on the calendar earlier in the planning process! You can install your own copy of Meetable from the source code on GitHub.</p> Argentina's top news app was made by two 23 year olds using AI - Werd I/O 6acfb65ec70972000161762d 2026-10-14T17:05:34.000Z Werd I/O <p>Link: <a href="https://www.niemanlab.org/2026/10/two-23-year-olds-used-ai-to-create-a-daily-audio-briefing-its-now-the-top-news-app-in-argentina/"><em>Two 23-year-olds used AI to create a daily audio briefing. It’s now the top news app in Argentina., by Hanaa' Tameez in Nieman Lab</em></a></p><p>I think there are several notable things about this story.</p><p>The first, most obvious one is that two 23 year olds were able to create a news app that rose to become the top-ranked news app in their country, using AI. There is no way they would have been able to build this with the time, team, and resources at their disposal otherwise. From an engagement perspective, this is a bona-fide AI-in-journalism-distribution success story.</p><p>The second is that it’s not a success story for incumbent newsrooms. Clearly, a short, personalized news briefing is something people wanted; every existing newsroom could have built it, at least using their own content, but they didn’t. The gap they left meant that two newcomers could come in with very few resources and gain an audience off the back of existing journalists’ work. The app credits stories in audio and leaves citations in the chat, but my guess is that most people aren’t looking at their screens as they listen to their audio briefing. Very few people will click through; I think these listens won’t translate to pageviews or subscriptions.</p><p>The third is that outsiders are clearly much more free to build experiments than existing newsrooms. The latter have the weight of their brands and existing revenue operations to consider. Most outsiders have none of that: nobody expects anything from them, so they’re free to experiment. Most experiments will fail, but some may succeed. Left alone, this represents intermediation that could undermine the connection between newsrooms and their communities. But there’s an opportunity to partner more deeply and represent newsroom needs in the app. And then consider how they can foster an ecosystem with more experimentation, not less.</p> Why is Pure Blog so ugly? - Kev Quirk https://kevquirk.com/why-is-pure-blog-so-ugly 2026-10-14T17:03:00.000Z Kev Quirk <p>Someone emailed recently to tell me that they like the idea of Pure Blog, but they thought the admin interface was <em>"really ugly"</em> so they can't use it. Fair enough - beauty is in the eye of the beholder and all that, but I don't think that was a fair evaluation of Pure Blog's admin UI.</p> <p>When I built Pure Blog, I wanted it to be different from the rest of the crowd. The whole point of it was to have just enough features, and no fluff. So to me a more brutalist/utilitarian UI made sense.</p> <p>The likes of Grav, WordPress, Ghost, and Bludit all have similar interfaces. White and grey with blues, purples, and greens. Rounded corners etc. All very iOS-ish. All very...safe. I wanted Pure Blog to stand out from the crowd and have its own identity, and honestly, I'm thrilled with the results.</p> <p class="notice tip">Pssst. If you haven't tried Pure Blog yet, check out the <a href="https://demo.pureblog.org" rel="noopener noreferrer">demo site</a>.</p> <p>But what I like may not be what you like, which is why I built Pure Blog to be <em>very</em> customisable from the start. You can change the fonts from mono, to sans, or serif with the click of a button, or you can change the look of the entire UI if you know some CSS.</p> <p>And this is what I said to the person who emailed me saying it was ugly. But they came back with...</p> <blockquote> <p>Great. Do you have any examples?</p> </blockquote> <p>I didn't! So I decided to create some examples and show them here so you can see just how customisable Pure Blog really is. I know other CMSs can customise their UI too, but unlike them, Pure Blog needs no plugins, hacks, or bullshit. This can all be done with standard Pure Blog right out of the box.</p> <p>So here are some examples of the Pure Blog admin UI where all I've added is custom CSS within settings.</p> <h2>Cupertino</h2> <p>How about bringing a macOS / iOS vibe to your Pure Blog site?</p> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-dashboard-light.webp" alt="cupertino-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-dashboard-dark.webp" alt="cupertino-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-content-light.webp" alt="cupertino-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-content-dark.webp" alt="cupertino-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-editor-light.webp" alt="cupertino-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="cupertino-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/cupertino-editor-dark.webp" alt="cupertino-editor-dark" loading="lazy"> </a> </figure> </div> <h2>Purely Ghost</h2> <p>Or maybe you like the super clean lines of the Ghost UI? Well, you can have something similar to that too if you like:</p> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-dashboard-light.webp" alt="ghost-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-dashboard-dark.webp" alt="ghost-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-content-light.webp" alt="ghost-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-content-dark.webp" alt="ghost-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-editor-light.webp" alt="ghost-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="ghost-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/ghost-editor-dark.webp" alt="ghost-editor-dark" loading="lazy"> </a> </figure> </div> <h2>Pure Press</h2> <p>Are you a WordPress purist who really wants to be as faithful as possible to their design? Well guess what - you can have that too!</p> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="wp-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-dashboard-light.webp" alt="wp-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="wp-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-dashboard-dark.webp" alt="wp-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="wp-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-content-light.webp" alt="wp-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="wp-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-content-dark.webp" alt="wp-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="wp-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-editor-light.webp" alt="wp-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="wp-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/wp-editor-dark.webp" alt="wp-editor-dark" loading="lazy"> </a> </figure> </div> <h2>Windows 4 lyfe</h2> <p>Maybe you're a lover of anything Bill Gates touches. If so, a Windows theme might be more your thing.</p> <h3>How about Windows 11?</h3> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="win11-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-dashboard-light.webp" alt="win11-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win11-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-dashboard-dark.webp" alt="win11-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="win11-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-content-light.webp" alt="win11-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win11-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-content-dark.webp" alt="win11-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="win11-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-editor-light.webp" alt="win11-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win11-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win11-editor-dark.webp" alt="win11-editor-dark" loading="lazy"> </a> </figure> </div> <h3>Or Windows XP?</h3> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-dashboard-light.webp" alt="winxp-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-dashboard-dark.webp" alt="winxp-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-content-light.webp" alt="winxp-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-content-dark.webp" alt="winxp-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-editor-light.webp" alt="winxp-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="winxp-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/winxp-editor-dark.webp" alt="winxp-editor-dark" loading="lazy"> </a> </figure> </div> <h3>Or if you're <em>really</em> old, Windows 95</h3> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-dashboard-light.webp" class="gallery-link" data-pb-lightbox data-caption="win95-dashboard-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-dashboard-light.webp" alt="win95-dashboard-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-dashboard-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win95-dashboard-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-dashboard-dark.webp" alt="win95-dashboard-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-content-light.webp" class="gallery-link" data-pb-lightbox data-caption="win95-content-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-content-light.webp" alt="win95-content-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-content-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win95-content-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-content-dark.webp" alt="win95-content-dark" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-editor-light.webp" class="gallery-link" data-pb-lightbox data-caption="win95-editor-light"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-editor-light.webp" alt="win95-editor-light" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-editor-dark.webp" class="gallery-link" data-pb-lightbox data-caption="win95-editor-dark"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/win95-editor-dark.webp" alt="win95-editor-dark" loading="lazy"> </a> </figure> </div> <h2>Terminal FTW</h2> <p>Maybe all this GUI stuff isn't your jam? Do you yearn for the days of yore when the interface to a computer was terminal-only with a monochrome CRT monitor?</p> <p>Well, Pure Blog can do that too:</p> <p>Green – the connoisseur's terminal.</p> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-dashboard.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-green-dashboard"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-dashboard.webp" alt="terminal-green-dashboard" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-content.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-green-content"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-content.webp" alt="terminal-green-content" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-editor.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-green-editor"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-green-editor.webp" alt="terminal-green-editor" loading="lazy"> </a> </figure> </div> <p>Or orange, the truly old-school terminal.</p> <div class="gallery"> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-dashboard.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-dashboard"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-dashboard.webp" alt="terminal-dashboard" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-content.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-content"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-content.webp" alt="terminal-content" loading="lazy"> </a> </figure> <figure class="gallery-item"> <a href="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-editor.webp" class="gallery-link" data-pb-lightbox data-caption="terminal-editor"> <img src="https://kevquirk.com/content/images/why-is-pure-blog-so-ugly/terminal-editor.webp" alt="terminal-editor" loading="lazy"> </a> </figure> </div> <h2>Your imagination is the limit</h2> <p>I think these screenshots show what Pure Blog is capable of. Your imagination really is the limit. So just because you think the <em>default</em> look of Pure Blog is ugly, don't fret - you can easily change it to be whatever you want it to be.</p> <p>If you're interested in grabbing any of these themes for your Pure Blog installation, all the CSS is available over <a href="https://docs.pureblog.org/customising-your-pure-blog/" rel="noopener noreferrer">on the docs site</a>.</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=Why%20is%20Pure%20Blog%20so%20ugly%3F">reply to this post by email</a>, or <a href="https://kevquirk.com/why-is-pure-blog-so-ugly#comments">leave a comment</a>.</p> </div> Now (October 2026) - Robb Knight • Posts • Atom Feed https://rknight.me/blog/now-october-2026/ 2026-10-14T11:36:06.000Z Robb Knight <h3>Doing...</h3> <p>I hand-delivered the final drawing for St Jude this morning and we're almost halfway through <a href="/blog/tags/inktober2026">Inktober</a> — I am furious to confirm that practicing actually works and does indeed improve your skills. Some of the art I've done in the past month I'm properly proud of. Jim loved his so much, <a href="https://jimbo.lol/posts/weekly-recap-63">he framed it</a>.</p> <figure><img src="https://cdn.rknight.me/site/2026/st-jude-jimothy-framed.jpg" alt="A photo of some collectables and a framed drawing of Jimothy the raccoon." /></figure> <p>The Knightlings have been ill on and off for the past month while the weather can't make it's mind up. We've had a couple of hospital visits as well — one for scary breathing-related things and one because Baby Knight the first is clumsy af and had to have her head glued back together.</p> <p>A few weeks later, the same nurse who did the aforementioned gluing also fixed my finger when I sliced it open sideways with a Stanley knife<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>. Can't imagine why my children are so clumsy, could come from anywhere.</p> <p>Eagle-eyed people might have noticed I added a single illustration to the home page of some hats. I have some more I'm going to add in the next few weeks.</p> <h3>Making...</h3> <p><a href="https://shotbox.rknight.me">Shotbox</a> is the third utility along with <a href="https://doodlescan.rknight.me">Doodle Scan</a> and <a href="https://favicons.rknight.me">Favicons To Go</a> in my little webapp trilogy.</p> <p>I've not made much progress with the self-hosted version of EchoFeed but I do have a solid plan so that should be happening soon. I want to make it as easy as possible to self-host it.</p> <p>I've made a minimal (~10 lines of CSS, !10 lines of JS) image gallery using the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog"><code>dialog</code> element</a> to showcase the St Jude drawings once they arrive with everyone who donated.</p> <h3>Listening...</h3> <ul> <li>Still going through <a href="https://www.last.fm/music/Connor+Price">Connor Price's</a> back catalog including the tracks he's done with <a href="https://www.last.fm/music/Nic+D">Nic D</a></li> <li><a href="https://rknight.me/almanac/music/2026-08-31-czarface-meets-frankie-pulitzer/">Czarface Meets Frankie Pulitzer</a>. Frankie Pulitzer is Tom Hardy, which was news to me and he goes <em>hard</em>.</li> <li><a href="https://rknight.me/almanac/music/2026-09-25-unshatter-film-soundtrack-live-in-so-paulo/">Unshatter by Linkin Park</a>. Emily Armstrong was such a great choice.</li> <li><a href="https://www.last.fm/music/Ekoh,+L%C3%B8+Spirit+&+Ninth+Key">Ekoh, Lø Spirit & Ninth Key</a> are dropping banger after banger in their 2000s-era Linkin Park style. I love it.</li> </ul> <h3>Watching...</h3> <p>We've been rewatching <a href="https://en.wikipedia.org/wiki/My_Name_Is_Earl">My Name is Earl</a> and it's so wholesome. I wasn't sure if it would hold up given when it was made but it really does. It's a damn shame it never got it's final season to wrap everything up.</p> <p>I'm two episodes into the new <a href="https://en.wikipedia.org/wiki/Reacher_(TV_series)">Reacher</a> and I'm into it.</p> <h3>Writing with...</h3> <ul> <li><a href="https://cultpens.com/collections/pilot-prera">Pilot Prera pink</a>, F nib with Pilot black ink. I really love writing with this pen.</li> <li><a href="https://www.amazon.co.uk/dp/B06XWXC935">Neon Pink Pentel P205</a></li> </ul> <hr class="footnotes-sep" /> <section class="footnotes"> <ol class="footnotes-list"> <li id="fn1" class="footnote-item"><p>Americans: I have been informed you might know this as an exacto knife rather than the British generic trademark usage of "Stanley" <a href="#fnref1" class="footnote-backref">⤾</a></p> </li> </ol> </section> The Internet of Very Cheap Things - Terence Eden’s Blog https://shkspr.mobi/blog/?p=75776 2026-10-14T11:34:08.000Z Terence Eden’s Blog <p>Twelve years ago, I was <a href="https://shkspr.mobi/blog/2014/09/the-internet-of-expensive-things/">complaining that IoT devices were too expensive</a>. Last week I bought a colour changing Zigbee lighbulb for £4. Not a typo - less than a fiver.</p> <p>While the aipocalypse is devouring RAM with its insatiable appetite, Moore's Law appears to be keeping up its end of the bargain when it comes to domestic gadgetry.</p> <p>This blog posts compares today's prices with those from a little over a decade ago. Some of the changes are startling, others less so. I'm basing the prices on what a customer can easily get today from Amazon. Yes, AliExpress is cheaper - but delivery takes and age and returns are a faff. Also, I get delicious affiliate revenue when you click the links which allows me to buy more tech crap to review. Everyone's a winner!</p> <!-- 👋👋👋👋👋👋👋👋👋 Welcome to Comment Club! This content is only available to cool people who read HTML comments. Part of this post is me justifying to myself that ****maybe**** it is OK to spend some money on some fancy new gadgets. I'm lucky that companies occasionally send me stuff to review - but there are only so many thermal cameras a boy can eat, you know? Running this blog is lots of fun - but the steady drip of affiliate revenue is a great motivator. It isn't life-changing sums, but it helps pay for hosting etc - so I don't feel /too/ guilty for shoving those Amazon links in everywhere. While you're here, 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 future blog posts 😃 TTFN. --> <h2 id="door-window-sensors"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#door-window-sensors">Door / Window Sensors</a></h2> <p>I don't know how many doors and windows you have in your property - we have about fifteen, not including internal doors and cupboards. Back then a single sensor cost around £30. Today you can get <a href="https://link.amazon/B02mpa9ff">Zigbee door sensors</a> for between £15 - £30 <em>for a pack of four</em>!</p> <p>Prices are at least quartered. Do you <em>need</em> a sensor on every cupboard door or drawer? It is useful to be able to check your phone to see if the toilet seat is up or down? These are reaching the price where you might as well hook them up to anything.</p> <h2 id="lightbulbs"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#lightbulbs">Lightbulbs</a></h2> <p>Hue's WiFi lightbulbs were about fifty-quid each. Again, count the number of lights you need in your home and run to your bank-manager for an overdraft!</p> <p>Today, <a href="https://link.amazon/B09UCBQh1">Hue Bulbs are about £16 each</a>. That's a pretty steep drop. If you want generic <a href="https://link.amazon/B042fOMp3">Zigbee Bulbs</a> you can expect to pay a tenner each. That's down to a fifth of the price from 2014. If you're happy buying from AliExpress, they're usually under a fiver.</p> <h2 id="smart-electrical-sockets"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#smart-electrical-sockets">Smart Electrical Sockets</a></h2> <p>It's useful to be able to remotely control a power socket. It used to be the case they'd cost £30 per plug. Nowadays <a href="https://link.amazon/B06WYAKty">smart plugs are about £7.50</a>. These ones come with energy monitoring and will easily integrate with HomeAssistant and other smarthome controllers.</p> <p>There's still a limit to the things which can be directly controlled from a socket. That said, I'm love my <a href="https://shkspr.mobi/blog/2018/11/building-an-alexa-powered-electric-blanket/">IoT Electric Blanket</a>.</p> <h2 id="bluetooth-finders"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#bluetooth-finders">Bluetooth Finders</a></h2> <p>The dream is to have a magic tag stuck on everything you might lose. Back in 2014, we thought iBeacons / Eddystones would win out. Sadly that dream died and instead we're stuck with proprietary "Find My" networks from Google and Apple.</p> <p>The iBeacons were about a tenner each back then. <a href="https://link.amazon/B0ehVOGpF">Today they're £5 - £10 each</a> depending on the features you want. That's still not cheap enough to attach to every sock - but there's no excuse for losing your keys, wallet, bag, or spouse again.</p> <p>Annoyingly they don't use an open standard, so you'll need a Google or Apple account to use them.</p> <h2 id="nfc-tags"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#nfc-tags">NFC Tags</a></h2> <p>I still prefer QR codes (free!) to NFC, but I concede they do have their uses. Their capacity and features are much the same as they were a decade ago, but prices have dropped from about 75p per tag <a href="https://link.amazon/B09WNLkdw">down to about 10p-20p per tag</a>.</p> <p>At that price, it's just about cheap enough to stick them in every book you own or sew them into all your underpants. Quite <em>why</em> you'd want to do that is left as an exercise for the reader.</p> <h2 id="internet-fridge"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#internet-fridge">Internet Fridge</a></h2> <p>You know what, I didn't bother looking at the actual cost of an Internet Fridge all those years ago. But earlier this year I got a WiFi enabled tumble dryer. It was as cheap as all the other models and means I can shout "ALEXA! MAKE DRY!" then receive a notification from HomeAssistant when the dry is fully maked.</p> <p>I hope my next fridge comes with a sensor that alerts me if the door is left open or the freezer is getting too warm. That would be handy.</p> <p>The <a href="https://link.amazon/B0eRlU9jW">cost of an ESP32</a> is basically a rounding error for most large appliances, especially when bought in bulk. If it uses electricity, it is probably trivial to add WiFi and Bluetooth.</p> <h2 id="whats-next"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#whats-next">What's Next?</a></h2> <p>There's probably a realistic limit to how low prices can go while the products still remain safe and durable. For smaller items that appears to be between a fiver in bulk direct from China to about a tenner sourced from a mainstream UK supplier.</p> <p>For domestic appliances, there doesn't seem to be any significant extra cost. Take a look at these <a href="https://markselectrical.co.uk/laundry/tumble-dryers/heat-pump?sort=price">heat pump tumble dryers</a> - even the cheapest of them comes with an app.</p> <p>There are a few bits of my home which haven't yet been given their own IP address - although they're mostly towards the mechanical side of things.</p> <ul> <li>Locks <ul> <li>There are some <a href="https://link.amazon/B0gkcYQHT">smart-locks for doors</a>. I've not seen any for windows. I'm not sure how well I'd trust their security.</li> </ul></li> <li>Opening / Closing Mechanisms <ul> <li>It's a cold day, how do I get my windows to automatically close? Would involved either running wiring everywhere or constantly recharging batteries.</li> </ul></li> <li>Drawing Curtains <ul> <li><a href="https://link.amazon/B01XlBC1d">These do exist</a>! But are mainly for blinds. Again, power requirements make this a bit more difficult.</li> </ul></li> <li>Cooking <ul> <li>I have a <a href="https://shkspr.mobi/blog/2024/03/receive-push-notifications-from-your-rice-cooker/">smart rice cooker</a>. For everything else, I have to rely on an Alexa beep-detector to know when my kitchen needs attention.</li> </ul></li> <li>Water Pipes <ul> <li>I already have a basic leak detector and a smart meter for gas / electric. How about <a href="https://watergate.ai/">a smart water meter</a>? Not sure I want to start sawing into my plumbing though!</li> </ul></li> </ul> <h2 id="but-i-dont-want-to-live-in-a-smarthome-%f0%9f%98%a1"><a href="https://shkspr.mobi/blog/2026/10/the-internet-of-very-cheap-things/#but-i-dont-want-to-live-in-a-smarthome-%f0%9f%98%a1">But I Don't Want To Live In A Smarthome 😡</a></h2> <p>Literally no one is forcing you. If you don't want your TV to spy on you, it'll work just fine if you don't connect it to your WiFi.</p> <p>If you like getting out of bed to make sure you switched off the downstairs lights, that option is still available to you.</p> <p>There's no law which says you have to connect your solar panels to the web - but I like seeing exciting graphs each day.</p> <p>I cannot pre-heat my <a href="https://shkspr.mobi/blog/2024/04/toilet-review-better-bathrooms-_27403/">smart toilet seat</a>, nor can I change the bidet's water pressure from my phone. That's <em>probably</em> a good thing?</p> <p>But I <em>like</em> gadgets! Sure, some of them are crappy - but I find it fun to get them working with HomeAssistant. Might they get hacked and send all my data to Arstotzka? Meh, they're all on a separate LAN and are connected to things which are unlikely to burn down my house. Might I wake up one day and find that the machine has stopped and my ability to draw my own curtains has atrophied? That's a chance I'm willing to take.</p> <p>The practical advantage I get from being about to check my security cameras on my phone from the other side of the planet is worth the theoretical risks.</p> <p>I wonder what other domestic gadgetry will fall prey to the irresistible lure of cheap connectivity?</p> <img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=75776&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager"> Package managers have an integrity problem - Trail of Bits Blog https://blog.trailofbits.com/2026/10/14/package-managers-have-an-integrity-problem/ 2026-10-14T11:00:00.000Z Trail of Bits Blog <p>I tested 25 of the most popular package managers to check whether they detect a compromised package repository injecting malicious code into legitimate packages. This code integrity problem could be solved using cryptographic hashes or signatures, but many package managers are still vulnerable to this threat. Of the 25 package managers reviewed<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, fewer than half used lockfiles that checked for tampering by default, and only two verified the integrity of every package they downloaded.</p> <p>Supply-chain attacks caused by developer compromise are a common story, but imagine what happens if ongoing mitigation attempts, including work by Trail of Bits<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, are successful. If developers of popular packages are no longer the easiest targets, attackers will shift their focus to the next weakest link in the system: the package repositories that serve the code. The cryptographic answers to this problem are more subtle than they appear, and many implementations fall short due to deliberate design choices, resource prioritization, or the complexity of developer-friendly solutions.</p> <p>One natural answer is to include a cryptographic hash of a package’s contents in a lockfile and compare hashes after downloading the package. This automatic verification was the default behavior in about half of the package managers I tested, but others required extra configuration to include or verify these hashes, used hashes in a cryptographically insecure way, or lacked hashes entirely. This is summarized in the following table.</p> <table> <thead> <tr> <th style="text-align: left"></th> <th style="text-align: left"></th> </tr> </thead> <tbody> <tr> <td style="text-align: left"><strong>Verifies integrity</strong></td> <td style="text-align: left">Go, vcpkg</td> </tr> <tr> <td style="text-align: left"><strong>Verifies if lockfile is present</strong></td> <td style="text-align: left">Bun, Bundler, Cargo, npm, pnpm, Poetry, SwiftPM, uv, Yarn</td> </tr> <tr> <td style="text-align: left"><strong>Verifies with lockfile + extra steps</strong></td> <td style="text-align: left">Conda, Gradle, Pip, NuGet, Pub</td> </tr> <tr> <td style="text-align: left"><strong>Does not verify with lockfile or N/A</strong></td> <td style="text-align: left">CocoaPods, Composer, Conan, cpanminus, Gem, Maven, PowerShellGet, PSResourceGet, R</td> </tr> </tbody> </table> <p><em>Table 1: Lockfile-based security of a package manager fetching dependencies from a malicious repository, categorized by how much work it takes a developer to achieve integrity. Experimentally verified for a particular configuration. Caveats detailed below.</em></p> <p>Another natural approach is to use signatures, avoiding the need to securely distribute a hash for every package. However, in my tests, only Go verified signatures by default; others required extra configuration, lacked verification code, or did not support signatures.</p> <table> <thead> <tr> <th style="text-align: left"></th> <th style="text-align: left"></th> </tr> </thead> <tbody> <tr> <td style="text-align: left"><strong>Verifies signatures</strong></td> <td style="text-align: left">Go</td> </tr> <tr> <td style="text-align: left"><strong>Verifies with configuration</strong></td> <td style="text-align: left">Bundler, Gem, Gradle, Maven, npm, NuGet</td> </tr> <tr> <td style="text-align: left"><strong>Missing or incomplete verification</strong></td> <td style="text-align: left">Bun, cpanminus, Pip, pnpm, Poetry, PowerShellGet, PSResourceGet, uv, Yarn</td> </tr> <tr> <td style="text-align: left"><strong>No signatures available for verification</strong></td> <td style="text-align: left">Cargo, CocoaPods, Composer, Conan, Conda, Pub, R, SwiftPM, vcpkg</td> </tr> </tbody> </table> <p><em>Table 2: Signature-based security of various package managers. Also experimentally verified and subject to similar caveats.</em></p> <p>As both tables show, I found a surprising variation in the cryptographic designs and the level of protection they provide. Despite the challenges, I also found several communities on the frontier of this integrity problem that are bringing improved designs to reality. This post explores those variations, showing where certain designs fall short, why code integrity is harder to solve than it appears, and what cryptographic designs might be adopted in the future.</p> <h2 id="simulating-malicious-package-repositories">Simulating malicious package repositories</h2> <p>Studying the cryptographic behavior of 25 package managers is quite the challenge. I wanted to minimize manual analysis, but also be 100% confident when I claim an attack from a malicious package repository is possible. To solve this problem, I built a testing harness based on Docker containers and <a href="https://www.mitmproxy.org/">mitmproxy</a> that dynamically evaluates the security of package managers against a malicious server.</p> <p> <figure> <img src="https://blog.trailofbits.com/2026/10/14/package-managers-have-an-integrity-problem/package-manager-integrity-banner-figure-1_hu_c095267cc912b4da.webp" alt="“Figure showing experimental design of the test harness used to simulate malicious package repositories”" width="764" height="543" loading="lazy" decoding="async" /> <figcaption>Figure 1: Experimental design of the test harness used to simulate malicious package repositories</figcaption> </figure> </p> <p>The Ubuntu-based Docker images include a package manager, sample code, and optionally a lockfile. There are <a href="https://arxiv.org/abs/2505.04834v3">conflicting opinions</a> about whether to include lockfiles in source code repositories, so I tested both configurations. The harness instructs the package manager to fetch dependencies from the repository, and the resulting network request <a href="https://docs.mitmproxy.org/stable/concepts/modes/#wireguard">routes</a> through a WireGuard network to a custom mitmproxy addon.</p> <p>Because the mitmproxy TLS CA is installed in the Docker image, the addon can fully manipulate the plaintext from the package repository, simulating a compromised server. I assumed any web-facing server could be compromised (e.g., Composer fetches metadata from Packagist and packages from GitHub), but backend resources, like any private signing keys, remain secure. The simulated repository embeds malicious code into the downloaded packages, and if the sample code executes the malware, the attack is successful.</p> <p>I tried to keep the experimental setup reflective of real-world usage, but there are limitations. I installed the latest package manager versions, followed quickstart guides for setting up sample projects inside the container, and picked popular packages from public repositories to infect. I ran package manager commands with default flags to see if extra steps, like modifying the project manifest or running extra commands, were needed to enforce cryptographic integrity. However, I am limited to testing a single package with a single package manager version on a single platform, and the test conditions (e.g., PowerShell and CocoaPods on Ubuntu) may not represent the most common usage patterns. The setup isn’t meant to show that every developer is vulnerable to a malicious package repository, just that a developer following reasonable conventions is.</p> <p>With the package managers installed and network intercept functional, one (mostly) promising observation is clear.</p> <h3 id="https-is-almost-everywhere">HTTPS is (almost) everywhere</h3> <p>Our harness architecture captures all network traffic, so it’s easy to test that package managers are using TLS and validating the certificates. In almost every package manager, they are.</p> <p>The exception is cpanminus, the <a href="https://www.cpan.org/modules/INSTALL.html">frequently</a> <a href="https://learn.perl.org/installing/unix_linux.html">recommended</a> package manager for Perl. cpanminus uses unencrypted HTTP by default to connect to the Perl package repository, allowing any active network attacker to inject malicious code into downloaded packages. This <a href="https://github.com/advisories/GHSA-9mmm-86g7-vp9g">vulnerability</a> has been known for years, but the cpanminus developer has not switched to HTTPS, since doing so would be a <a href="https://github.com/miyagawa/cpanminus/issues/611#issuecomment-648642186">breaking change</a> to default behavior.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p> <h2 id="hash-functions-dont-always-provide-integrity">Hash functions don’t always provide integrity</h2> <p>By using the mitmproxy addon to modify the package repository responses, I could investigate how different package managers use cryptographic hashes. Hashes are a natural way to ensure that a downloaded file has not been tampered with, but the main question is how the package manager learns the correct hash in the first place.</p> <p>Microsoft’s <a href="https://vcpkg.io/en/">vcpkg</a> package manager for C and C++ libraries offers an interesting example. The package manager source code includes content <a href="https://github.com/microsoft/vcpkg/blob/9e593bb18ea69cc5095e012465dcd675a822ed0d/ports/zlib/portfile.cmake#L6">hashes</a> for <a href="https://github.com/microsoft/vcpkg/tree/master/ports">thousands</a> of dependencies. When a developer updates their library and wants to register the new content in vcpkg, they <a href="https://learn.microsoft.com/en-us/vcpkg/get_started/get-started-packaging">recompute</a> the hash of their package and open a <a href="https://learn.microsoft.com/en-us/vcpkg/get_started/get-started-adding-to-registry">pull request</a> against the vcpkg source code with the new hash and link to the package. When a package is downloaded by vcpkg, the hash is checked. So as long as the package manager is trusted, this approach is fully secure against a malicious package repository.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p> <h3 id="hashes-in-lockfiles">Hashes in lockfiles</h3> <p>It is significantly more common that the package manager learns the correct hash via a lockfile. A lockfile is frequently created by a package manager, resides alongside a project’s source code, and references specific versions of dependencies needed by the project. If a developer checks a lockfile with cryptographic hashes into the source code repository, and the user’s package manager validates downloaded packages against the hashes, then the user’s dependencies perfectly match the developer’s.</p> <p>This is how it works for the Bun package manager for JavaScript projects. Installing a package with <code>bun add</code> <a href="https://bun.com/docs/pm/cli/add">updates</a> the <code>bun.lock</code> lockfile; committing the lockfile to <a href="https://bun.com/docs/pm/lockfile#should-it-be-committed-to-git">Git</a> and calling <code>bun install</code> on another copy of the repo <a href="https://github.com/oven-sh/bun/blob/34cbb9a40b4bd1bd767d134a7065e66c2432a676/src/install/extract_tarball.rs#L47">verifies</a> the hash in the lockfile against the new copy of the dependency. Similar behavior is found in npm, pnpm, and Yarn (also JavaScript package managers); uv and Poetry (Python package managers); Bundler (for Ruby); Cargo (for Rust); and SwiftPM (for Swift).</p> <p>Other package managers support lockfiles, but with more steps than the standard add/restore process. Python’s Pip has <a href="https://pip.pypa.io/en/stable/cli/pip_lock/">experimental</a> support for the <code>pylock.toml</code> format, and Conda has the optional library <a href="https://conda.github.io/conda-lock/">Conda lock</a>. NuGet, the .NET package manager, generates a lockfile after <a href="https://devblogs.microsoft.com/dotnet/enable-repeatable-package-restores-using-a-lock-file/">adding</a> the <code>RestorePackagesWithLockFile</code> property to the project.</p> <p>Many package managers (Maven, R, PowerShellGet, PSResourceGet, and cpanminus) do not have dedicated lockfiles, and some have lockfiles (Gradle’s <code>gradle.lockfile</code><sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> and Gem’s <code>Gemfile.lock</code>) without hashes in them.</p> <h3 id="hashes-present-but-not-for-integrity">Hashes present, but not for integrity</h3> <p>Surprisingly, I found a few examples where the lockfiles had a hash, but could still be compromised by a malicious server. This is not necessarily reflective of a vulnerability, since the underlying behavior is clearly documented, but it shows how there are multiple valid opinions about how lockfiles should be structured and consumed. Nevertheless, a user who is accustomed to the behavior of one package manager may be surprised by that of another.</p> <p>The first example is the Pub package manager for Dart and Flutter. The following excerpt of a <code>pubspec.lock</code> file includes a SHA-256 hash for the <code>uuid</code> package.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">uuid</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">dependency</span><span class="p">:</span><span class="w"> </span><span class="s2">"direct main"</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">uuid</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">sha256</span><span class="p">:</span><span class="w"> </span><span class="s2">"9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd"</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://pub.dev"</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="l">hosted</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s2">"4.6.0"</span></span></span></code></pre> </figure> <p>If a user calls <code>flutter pub get</code> to fetch dependencies, Pub downloads the package from the <code>pub.dev</code> server and computes the hash of the returned binary. If there is a mismatch, Pub detects the error, but installs the package anyway.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">The existing content-hash from pubspec.lock doesn't match contents for: </span></span><span class="line"><span class="cl"> * uuid-4.6.0 from "https://pub.dev" </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">This indicates one of: </span></span><span class="line"><span class="cl"> * The content has changed on the server since you created the pubspec.lock. </span></span><span class="line"><span class="cl"> * The pubspec.lock has been corrupted. </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl">The content-hashes in pubspec.lock has been updated.</span></span></code></pre> </figure> <p>This is <a href="https://dart.dev/resources/glossary#pub-content-hash">documented behavior</a>, and requires the non-default <code>--enforce-lockfile</code> option to block installation<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.</p> <p>Overwriting the lockfile after a hash mismatch was also the default behavior in pnpm until version <a href="https://pnpm.io/blog/releases/11.4">11.4</a> from May 2026, when it was changed to close “supply-chain holes.” Like Pub, pnpm also supported a non-default <code>--frozen-lockfile</code> flag, indicating that the previous behavior which updated the lockfile was expected.</p> <p>Next, here is an excerpt of a <code>conan.lock</code> file, used by the Conan package manager for C and C++.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">"requires"</span><span class="err">:</span> <span class="p">[</span> </span></span><span class="line"><span class="cl"> <span class="s2">"zlib/1.3.1#cac0f6daea041b0ccf42934163defb20%1765284699.337"</span> </span></span><span class="line"><span class="cl"><span class="p">]</span><span class="err">,</span></span></span></code></pre> </figure> <p>This includes the package name (<code>zlib</code>), the version number (<code>1.3.1</code>), a recipe revision <code>cac0f6daea041b0ccf42934163defb20</code>, and a timestamp. The 128-bit revision is an MD5 <a href="https://docs.conan.io/2/tutorial/versioning/revisions.html">hash</a> that includes source code. However, this hash is <a href="https://github.com/conan-io/conan/issues/7800#issuecomment-702870494">not intended</a> to provide cryptographic security, but rather serves as a unique identifier. When Conan installs dependencies using this lockfile, it uses the recipe revision hash to download a particular file from the ConanCenter repository, but it does not recompute the recipe hash. Even if it did, there’s a separate package revision hash for platform-specific binaries that is not in the lockfile, and MD5 collisions could allow an attacker to create two distinct recipes with different behavior but the same hash.</p> <p>The CocoaPods dependency manager for Swift and Objective-C has a similar pattern. Here is part of a <code>Podfile.lock</code> file for a project that uses the popular <code>Files</code> dependency.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">SPEC CHECKSUMS</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">Files</span><span class="p">:</span><span class="w"> </span><span class="l">d88e31b84d65e754935053b1c584ea8e2320698a</span></span></span></code></pre> </figure> <p>This 160-bit checksum is the SHA-1 hash of the Files <a href="https://cdn.jsdelivr.net/cocoa/Specs/9/1/f/Files/4.0.2/Files.podspec.json">Podspec</a>, which contains the following link to the source code. CocoaPods uses this link to fetch the Git repository.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">"source"</span><span class="err">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">"git"</span><span class="p">:</span> <span class="s2">"https://github.com/johnsundell/files.git"</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">"tag"</span><span class="p">:</span> <span class="s2">"4.0.2"</span> </span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre> </figure> <p>So even though the lockfile can prevent tampering with the GitHub URL in the Podspec (setting aside expensive SHA-1 collision attacks), a compromised GitHub server could just serve a malicious version of the repository with the same tag.</p> <p>Finally, the Composer dependency manager for PHP includes commit hashes in the <code>composer.lock</code> file, as in the following excerpt.</p> <figure class="highlight"> <pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">"dist"</span><span class="err">:</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nt">"type"</span><span class="p">:</span> <span class="s2">"zip"</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">"url"</span><span class="p">:</span> <span class="s2">"https://api.github.com/repos/guzzle/psr7/zipball/b094ded77ee97a6027ad6cf0e8c7b9f88381814c"</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">"reference"</span><span class="p">:</span> <span class="s2">"b094ded77ee97a6027ad6cf0e8c7b9f88381814c"</span><span class="p">,</span> </span></span><span class="line"><span class="cl"> <span class="nt">"shasum"</span><span class="p">:</span> <span class="s2">""</span> </span></span><span class="line"><span class="cl"><span class="p">}</span><span class="err">,</span></span></span></code></pre> </figure> <p>However, even though the <code>b094ded...</code> commit hash uniquely identifies the code, the ZIP archive in the URL does not include the Git tree as a way to verify the hash. Once again, GitHub could serve a malicious ZIP archive without detection by Composer.</p> <h3 id="lockfiles-have-many-roles-besides-supply-chain-integrity">Lockfiles have many roles besides supply-chain integrity</h3> <p>The previous experimental results show that lockfile hashes often <em>can be</em> used to enforce package integrity, but this is not their sole or even original purpose. Alternatively, they can lock dependencies to a particular functionality, enable an easy way for application developers to share a consistent environment, or facilitate reproducible builds. Analyzing package manager lockfiles through the lens of supply-chain security is a recent interpretation that can be at odds with their original design. The recent change to pnpm’s <code>--frozen-lockfile</code> is one example.</p> <p>Another example of this tension is the question of whether the lockfile should be committed to version control. Most modern guidance recommends committing the lockfile for applications, since the final build environment is fully controlled. The question is more complicated for libraries, since package managers often ignore libraries’ lockfiles to avoid locking to conflicting transitive dependencies. Committing the lockfile makes it easier to develop the library by sharing a consistent build environment with other developers, but excluding the lockfile makes it easier to catch conflicts with different dependency versions before they are found by users. Many package managers recommend committing the lockfile for libraries (e.g., <a href="https://docs.npmjs.com/cli/v12/configuring-npm/package-lock-json">npm</a>, <a href="https://pnpm.io/git">pnpm</a>, <a href="https://python-poetry.org/docs/basic-usage/">Poetry</a>, <a href="https://docs.astral.sh/uv/guides/projects/#uvlock">uv</a>, and <a href="https://classic.yarnpkg.com/blog/2016/11/24/lockfiles-for-all/">Yarn</a>), some recommend excluding it (e.g., <a href="https://guides.rubygems.org/faqs/#using-gemfiles-inside-gems">Bundler</a>), and some leave it up to the developer (e.g., <a href="https://blog.rust-lang.org/2023/08/29/committing-lockfiles/">Cargo</a>).</p> <p>Many of these questions are explored in a recent <a href="https://arxiv.org/abs/2505.04834v3">study</a> by Gamage et al. The authors explore lockfile design decisions for seven package managers, analyze published projects, and perform semi-structured interviews with 15 developers. They find that the practice of committing a lockfile varies by which package manager is used, and categorize five benefits to using lockfiles, as perceived by the interview subjects: build determinism, integrity, transparency into indirect dependencies, debugging, and security scanning. Some of the developers also preferred to not use lockfiles, citing a preference for end users to resolve to the latest version of a dependency, which may have more bug fixes and vulnerability patches.</p> <p>The diversity of package manager designs and views surrounding the purpose of lockfiles makes it difficult to prescribe a single recommendation. With regard to supply-chain integrity, it is common, but not universal, to view lockfiles as a source of integrity that should be committed with the source code. For package managers with lockfiles that do not provide cryptographic integrity, it is worth considering whether their users share that expectation. Even when lockfile hashes can provide cryptographic integrity, locking to specific package hashes means that users do not immediately receive subsequent updates or security fixes, since the newer package has a different hash. This leads to frequent lockfile bumping to distribute the latest hashes. Wanting immediate updates is a separate concern from wanting package integrity, but the current, common approach of using hashes in lockfiles is at odds with that goal. In conclusion, lockfile hashes are often, but not always, effective at providing cryptographic integrity, but do not work in every situation.</p> <h2 id="signatures-are-common-verification-is-not">Signatures are common; verification is not</h2> <p>Signatures are a natural way to address these shortcomings, but implementations frequently fall short. In theory, developers could sign every release with their own private key, and the package manager could check the signature of the package it downloads from the package repository. In fact, signatures are relatively common in different package ecosystems, but proper signature validation is surprisingly lacking. The reality of using signatures to provide code integrity is more nuanced than it seems, and package managers have different approaches to the problem.</p> <p>Many package managers have no signatures available. Arguably, vcpkg does not need them, because all package hashes are distributed with the manager, but this leaves Cargo, CocoaPods, Composer, Conan, Conda, Pub, R, and SwiftPM<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>. The Conan developers are aware and previously decided not to fix it, <a href="https://github.com/conan-io/conan/issues/1959">citing</a> several implementation challenges and suggesting organizations where integrity is critical could run their own repository server. Cargo developers are actively <a href="https://rustfoundation.org/media/improving-supply-chain-security-for-rust-through-artifact-signing/">considering</a> different signing designs, but this effort is still in progress. Composer developers have been <a href="https://blog.packagist.com/an-update-on-composer-packagist-supply-chain-security/">implementing</a> many new protections against supply-chain attacks, with future plans for artifact integrity.</p> <h3 id="repository-signing">Repository signing</h3> <p>One straightforward approach, known as repository signing, is when the package repository signs every package once it’s uploaded, and the repository’s public key is distributed with the package manager. This doesn’t solve the problem of a totally compromised package repository, since a malicious package repository could legitimately sign a malicious package, but it would protect against untrusted mirrors or a partial compromise of a package repository’s serving infrastructure.</p> <p>Repository signatures are used by the <a href="https://docs.npmjs.com/about-registry-signatures">npm Registry</a>, the <a href="https://learn.microsoft.com/en-us/nuget/reference/signed-packages-reference">NuGet Registry</a>, and the PAUSE server for Perl module distribution. In the npm Registry, signatures are distributed within package <a href="https://registry.npmjs.org/debug">metadata</a> with repository-specific <a href="https://registry.npmjs.org/-/npm/v1/keys">public keys</a>. NuGet Registry repository signatures are found in <code>.signature.p7s</code> <a href="https://devblogs.microsoft.com/dotnet/introducing-repository-signatures/">files</a> within the distributed ZIP package, again with repository-specific <a href="https://api.nuget.org/v3-index/repository-signatures/5.0.0/index.json">public keys</a>. The PAUSE server takes a different approach, using its <a href="https://pause.perl.org/pause/query?ACTION=pause_04about#pubkeybat">GPG key</a> to batch sign the <a href="https://cpan.metacpan.org/authors/id/S/SR/SRI/CHECKSUMS">cryptographic hashes</a> of several packages at a time.</p> <h3 id="developer-signing">Developer signing</h3> <p>Developer signing involves the developer maintaining the private key and signing the package before uploading it to the repository for distribution. This prevents the package repository from tampering with the contents, but it also provides a way for the package repository to authenticate the uploader.</p> <p>There are two common approaches for a developer asserting their public key. In the first approach, the public key is distributed out-of-band. The Maven Central repository (used by Maven and Gradle) and the RubyGems repository (used by the <code>gem</code> and Bundler package managers) <a href="https://docs.ruby-lang.org/en/master/Gem/Security.html">both</a> <a href="https://central.sonatype.org/publish/requirements/#sign-files-with-gpgpgp">support</a> this type of developer signing. Maven Central uses PGP signatures, and developer keys are distributed by <a href="https://central.sonatype.org/publish/requirements/gpg/#signing-a-file">public keyservers</a>. RubyGems uses self-signed certificates, and their distribution is left to the developers (e.g., <a href="https://github.com/rspec/rspec/blob/main/rspec/certs/rspec.pem">rspec</a>). The major difference is that developer signing is mandatory for Maven Central, and optional for RubyGems. Developer signing is rare for RubyGems, <a href="https://guides.rubygems.org/security/">due to</a> the lack of an established chain of trust, and a previous attempt to introduce a certificate authority <a href="https://www.grant-olson.net/news/2013/09/29/nobody-cares-about-signed-gems.html">failed</a>.</p> <p>The second approach involves a chain of trust. The NuGet Registry and PowerShell Gallery both support developer signatures. Neither makes signatures mandatory, but the PowerShell Gallery <a href="https://learn.microsoft.com/en-us/powershell/gallery/concepts/publishing-guidelines?view=powershellget-3.x#sign-your-code">recommends</a> it and the NuGet Registry has strict <a href="https://learn.microsoft.com/en-us/nuget/reference/signed-packages-reference#signature-requirements-on-nugetorg">requirements</a> if developer signing is used. In both, the developer’s key must chain to a <a href="https://learn.microsoft.com/en-us/nuget/create-packages/sign-a-package#get-a-code-signing-certificate">trusted CA</a>, and acquiring a valid code-signing certificate can <a href="https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/code-signing-options">cost</a> hundreds of dollars per year. The benefit of this approach is that trusted root certificates can be preinstalled on the platform, allowing package managers to verify public keys.</p> <p>One downside of developer signing is that developers are responsible for key management, and they can lose the ability to update their code if they lose access to their key.</p> <h3 id="identity-based-signing">Identity-based signing</h3> <p>An increasingly common middle ground is “identity-based” or “keyless” signing from <a href="https://docs.sigstore.dev/cosign/signing/overview/">Sigstore</a>. Rather than require developers to manage long-term private keys, a developer can use Sigstore’s Fulcio CA to issue a fresh signing certificate on demand, for free. To prevent impersonation, the developer proves their identity with an OIDC token: a cryptographically signed statement from a third party like Google, Microsoft, or GitHub that attests to the email address, username, or potentially other claims about the developer’s identity. The verifier can check that the username is correct and the certificate chain links back to Sigstore Fulcio’s root certificate, proving that someone with access to that account created the signature.</p> <p>Keyless signing is particularly useful because it can be integrated into a developer’s existing workflow-based deployment pipeline. This is frequently called <a href="https://blog.trailofbits.com/2024/11/14/attestations-a-new-generation-of-signatures-on-pypi/">provenance</a> attestation or trusted publishing<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>. GitHub and GitLab provide OIDC tokens to Actions and CI/CD workflows that attest to the build workflow, the repository, and the commit hash. A workflow can use this token to obtain a signing certificate from Sigstore, sign the built package, and upload the signed package to the package repository. Since the signing certificate includes the commit hash and workflow details, this cryptographically links the built binary artifact to its source code.</p> <p>Provenance attestation via Sigstore is supported by Python’s <a href="https://blog.trailofbits.com/2024/11/14/attestations-a-new-generation-of-signatures-on-pypi/">PyPI</a>, the <a href="https://docs.npmjs.com/generating-provenance-statements">npm Registry</a>, <a href="https://blog.rubygems.org/2023/12/14/trusted-publishing.html">RubyGems repository</a>, and <a href="https://central.sonatype.org/news/20250128_sigstore_signature_validation_via_portal/">Maven Central</a>. While Sigstore attestations are not mandatory, there is already <a href="https://trailofbits.github.io/are-we-pep740-yet/">better</a> <a href="https://segiddins.github.io/are-we-attested-yet/">adoption</a> than RubyGems’ optional support for developer-managed signing keys.</p> <h3 id="verification-code-is-skipped-unconfigured-or-nonexistent">Verification code is skipped, unconfigured, or nonexistent</h3> <p>Unfortunately, just because these signatures exist does not mean they are being verified by package managers.</p> <p>In the npm package manager, the repository signatures are not checked when running <code>npm install</code>. Checking the signatures requires running <code>npm audit signatures</code>, which uses Sigstore to validate the <a href="https://github.com/sigstore/root-signing/blob/main/targets/registry.npmjs.org/keys.json">registry public keys</a> and then validates the signatures of the installed packages. pnpm recently added the <code>pnpm audit signatures</code> <a href="https://pnpm.io/cli/audit#signatures">command</a>, but that implementation fetches the registry public keys from the potentially malicious registry rather than verifying with Sigstore, so an adversary could simply add their own key and signature. Bun and <a href="https://github.com/yarnpkg/berry/issues/6487">Yarn</a> currently lack an equivalent <code>audit signatures</code> command.</p> <p>In the NuGet package manager, the <a href="https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file">default</a> <code>signatureValidationMode</code> is <code>accept</code>, which installs invalidly signed packages. The user must set this to <code>require</code> and configure the list of trusted signers, such as with <code>dotnet nuget trust source</code>. These configurations (which include the repository public keys) can be securely distributed within a <code>nuget.config</code> configuration file, functioning as a lockfile that locks the trusted signers.</p> <p>Ruby’s Gem and Bundler, and Java’s Maven and Gradle, which all use repositories with developer signatures, do not verify signatures by default. They require the user to manually add developer public keys (<code>gem cert --add</code>) or list the allowed public keys that should be trusted. In Maven, one can use the third-party <code>pgpverify-maven-plugin</code> with <code>pgp-keys-map.list</code> to specify the trusted PGP fingerprints. Gradle offers <a href="https://docs.gradle.org/current/userguide/dependency_verification.html">dependency verification</a>, which works similarly.</p> <p>Finally, several package managers seemingly lack any sort of verification code at all. <a href="https://github.com/pypa/pip/issues/12766">Pip</a>, <a href="https://github.com/astral-sh/uv/issues/9122">uv</a>, and Poetry do not currently have a way to verify identity-based attestations described in PEP740. cpanminus <a href="https://metacpan.org/dist/App-cpanminus/view/bin/cpanm">does not</a> have a way to verify the integrity of the repository-signed CHECKSUMS file. It once did, but in response to a verification bypass <a href="https://blog.hackeriet.no/cpan-signature-verification-vulnerabilities/">vulnerability</a>, this verification code was <a href="https://github.com/miyagawa/cpanminus/pull/638">removed</a>. Even though PowerShell is <a href="https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell?view=powershell-7.5">supported</a> on non-Windows platforms, Authenticode signature verification <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-authenticodesignature?view=powershell-7.5#notes">is</a> <a href="https://github.com/PowerShell/PSResourceGet/pull/1891">not</a><sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>. At one point, the PowerShell repository noted that this <a href="https://github.com/PowerShell/PowerShell/blob/b25b8df7cae323c376fe4770882ebbfd805fdbf4/docs/KNOWNISSUES.md#command-availability">issue</a> “should be fixed in a future release.”</p> <h2 id="go-package-management-and-transparency-logs">Go package management and transparency logs</h2> <p>Go’s built-in package manager is the only one I tested that verifies signatures by default, and it also introduces one final concept for cryptographic package integrity. When <code>go get</code> or <code>go mod download</code> fetches a package<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>, it hashes the package contents and compares the hash, package name, and version against the <a href="https://go.dev/blog/module-mirror-launch">checksum database</a>. The contents of the database are registry-signed (analogous to PGP signatures in Perl’s PAUSE server), and the default checksum database public key is <a href="https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/key.go">hardcoded</a> in Go’s source code. As long as the checksum database’s private key is used honestly, this design ensures that the package name and version are enough to uniquely identify the binary contents of a package. As a result, the <code>go.mod</code> lockfile achieves package integrity without needing to store cryptographic hashes.<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup></p> <p>Go’s checksum database is an example of a transparency log, or an immutable, global, tamper-resistant log of metadata. The same capability is provided by Sigstore’s <a href="https://docs.sigstore.dev/logging/overview/">Rekor</a> and certificate transparency logs. In package managers, it cryptographically ensures that the package manager only ever installs the same binaries recorded by the transparency log. If everyone agrees on the transparency log, then everyone agrees on the package contents. This makes it more difficult for an adversary to selectively and undetectably target a victim with a malicious package. Transparency logs can still fail if there are <a href="https://www.cve.org/CVERecord?id=CVE-2026-56865">other</a> <a href="https://www.cve.org/CVERecord?id=CVE-2026-56864">vulnerabilities</a>, the log’s private key is compromised, or no one checks the transparency log for malicious behavior.</p> <p>Of all the package managers I tested, Go performed the best at maintaining cryptographic integrity in the face of a compromised package repository or proxy. However, the registry-signature-like design means that much of the system’s trust is concentrated in a single private key.</p> <h2 id="a-proposed-cryptographic-design-for-package-managers">A proposed cryptographic design for package managers</h2> <p>One potential approach that combines the ideas in this post is to include signature information in the lockfile. Rather than commit to the hash of a particular package, the lockfile could commit to a developer’s identity or public key. This reuses much of the infrastructure in place for lockfile hashes, but it separates the question of immediate updates from the question of package integrity.</p> <p>With this design, the lockfile could specify a single dependency version (for build reproducibility) or a lower bound (for immediate updates), and the included identity ensures that a malicious repository or proxy cannot easily tamper with whatever package version the manager resolves to. In addition, a transparency log can protect against selective targeting, building on Sigstore’s identity-based signing and transparency log service. Sigstore avoids long-lived keys by default, but it also <a href="https://docs.sigstore.dev/cosign/key_management/overview/">supports</a> self-managed keys, KMS providers, and hardware tokens. This gives an easily adopted and secure default while still giving flexibility to developers who desire more control over their keys.</p> <p>This idea is not new and not my own. Attestation identities are part of the <code>pylock.toml</code> Python lockfile <a href="https://packaging.python.org/en/latest/specifications/pylock-toml/#packages-attestation-identities">specification</a>, the <code>trusted-keys</code> in Gradle’s <a href="https://docs.gradle.org/current/userguide/dependency_verification.html">verification metadata</a>, and the <code>pgp-keys-map.list</code> file in the <code>pgpverify-maven-plugin</code> for Maven. This is the direction that many package managers are already moving towards, as evidenced by the increasing adoption of Sigstore into publishing workflows, attention on supply-chain security, and bugfixes that specifically cite potential tampering by a malicious adversary. There is increasing recognition that this is a problem, and increasing appetite to fix it.</p> <p>It won’t be easy. There are many subtle nuances that have to be considered and addressed, signing verification code needs to be developed and audited, and developers need to adopt the resulting design. In many of the package managers I reviewed as part of this project, I found <a href="https://openssf.org/">communities</a> of <a href="https://rust-lang.zulipchat.com/#narrow/stream/417663-tbd-signing">people</a> who have been thinking about and pushing for cryptographic integrity and package signing for years. They know best what those package managers need, and they deserve the resources to make this a reality.</p> <p>One of the greatest cryptographic success stories in the past decade has been the <a href="https://transparencyreport.google.com/https/overview?hl=en">nearly universal</a> adoption of HTTPS, driven in part by industry and community recognition of the security risks of unencrypted traffic, and the nonprofit <a href="https://letsencrypt.org/">Let’s Encrypt</a> providing free and easy-to-use TLS certificates as a public good. Package signing is in a similar state now to where HTTPS was a decade ago: persistent adversaries are a huge concern, and cryptographic security is nearly nonexistent. Universal package signing is not here yet, but it is on the horizon. With the right attention and investment, we can make it a reality.</p> <h2 id="acknowledgments">Acknowledgments</h2> <p>Thank you to William Woodruff for valuable discussions about lockfiles, identity signing, and the challenges of package manager security.</p> <div class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1"> <p>This post focuses on dependency managers for source code rather than system package managers like APT, Chocolatey, or Homebrew. The line between the two is blurred, but concentrating on one helps highlight particular usage and deployment patterns in that group. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:2"> <p>Trail of Bits has <a href="https://blog.trailofbits.com/2026/01/29/building-cryptographic-agility-into-sigstore/">several</a> <a href="https://pytransparency.dev/">ongoing</a> and <a href="https://blog.trailofbits.com/2023/05/23/trusted-publishing-a-new-benchmark-for-packaging-security/">completed</a> <a href="https://blog.trailofbits.com/2024/11/14/attestations-a-new-generation-of-signatures-on-pypi/">projects</a> to <a href="https://blog.trailofbits.com/2025/01/30/pypi-now-supports-archiving-projects/">improve</a> <a href="https://blog.trailofbits.com/2025/09/24/supply-chain-attacks-are-exploiting-our-assumptions/">supply-chain</a> <a href="https://blog.trailofbits.com/2025/12/12/catching-malicious-package-releases-using-a-transparency-log/">security</a> in some of these package ecosystems, including helping address both account takeover threats and the cryptographic integrity problem. <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:3"> <p>cpanminus is included in the default Docker container for <a href="https://hub.docker.com/_/perl/">Perl</a>, but the container build scripts <a href="https://github.com/Perl/docker-perl/blob/8045b8fcfaa30683f233e2cdfa52c98095a85425/5.042.000-main-trixie/Dockerfile#L23">patch</a> cpanminus to use HTTPS by default. <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:4"> <p>A subtle distinction here is that vcpkg must be updated using <code>git pull</code> in order to fetch the latest package hashes, shifting the question from securely installing packages to securely updating the package manager. For consistency with other package managers, we do not consider this question, and we note this as a limitation of our threat model. <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:5"> <p>Gradle <a href="https://docs.gradle.org/current/userguide/dependency_verification.html">can have</a> a <code>verification-metadata.xml</code> file with content hashes, even though <code>gradle.lockfile</code> does not. <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:6"> <p>Note that some <code>flutter</code> subcommands, including <code>flutter run</code> and <code>flutter test</code>, call <code>flutter pub get</code> by default, so these commands can also overwrite the <code>pubspec.lock</code> file unless called with <code>--no-pub</code>. <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:7"> <p>The SwiftPM client has two main ways to download packages: via Git and via the custom Swift Package Registry Service Specification. The latter <a href="https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#443-signature-validation">supports</a> signatures, and the server protocol is implemented by private services such as <a href="https://jfrog.com/blog/swiftpm-cocoapods-enterprise-development-apple-platforms/">JFrog Artifactory</a> and <a href="https://aws.amazon.com/about-aws/whats-new/2023/09/swift-package-manager-aws-code-artifact/">AWS CodeArtifact</a>. As far as I can find, there is no repository for the general public that implements this protocol, so I tested the signatureless Git format. <a href="#fnref:7" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:8"> <p>Trusted publishing <a href="https://blog.trailofbits.com/2023/05/23/trusted-publishing-a-new-benchmark-for-packaging-security/">can also</a> refer to using OIDC tokens to authenticate to a package repository, and it does not necessarily involve identity-based signing. <a href="#fnref:8" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:9"> <p>Although I did not test PowerShell on Windows, the <a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7.5">documented</a> default <code>ExecutionPolicy</code> behavior is to block scripts on Windows client machines, and to require a valid signature for Windows servers. This achieves package integrity. <a href="#fnref:9" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:10"> <p>Go calls these downloadable artifacts “modules,” which consist of “packages.” For the sake of consistent terminology with the rest of the post, I use “package” in the general sense, not the Go-specific one. <a href="#fnref:10" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:11"> <p>The <code>go.sum</code> file stores hashes, but it is <a href="https://words.filippo.io/gosum/">not a lockfile</a>. Instead, it is a local cache for entries from the checksum database. <a href="#fnref:11" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> 2026-10-14 11:53: Out for coffee this morning with my wife, as is our Wednesday morning routine, and... - Kev Quirk https://kevquirk.com/2026-10-14-1153 2026-10-14T10:53:00.000Z Kev Quirk <p>Out for coffee this morning with my wife, as is our Wednesday morning routine, and this little guy came to sit with us.</p> <p><img loading="lazy" src="https://kevquirk.com/content/images/2026-10-14-1153/1000012447.webp" alt="1000012447" /></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-14%2011%3A53">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-10-14-1153#comments">leave a comment</a>.</p> </div> Meaning - James' Coffee Blog https://jamesg.blog/2026/10/14/meaning 2026-10-14T00:00:00.000Z James' Coffee Blog <p><em>What does the concrete path mean to me?</em></p> <p>In the mirrored ground I see many meanings. Here is a place I have stopped to think. Here is a place over which I have idly walked on my way to somewhere familiar or somewhere new. Here is a vein of the city, made by all those who walk through. Here is where I feel an invigorating air where I feel at home even among the rain – a place where, no matter how many times I have been here, I see something new.</p> <p style="text-align: center;">⁂ ⁂ ⁂</p> <p>If, in my mind, I go back to where I stood still, I see both mirrored puddles and the past and the future. I carry with me the hope that has accompanied many of my walks; the kind of hope that borders on romanticism, a kind of romanticism I like because there is energy to be had in knowing that there is a mere possibility of something new existing in places we know well. I walk on old paths while I find new ones; while I become more comfortable with the world I know. With time, I find the confidence to step somewhere new, revealing new worlds – new thoughts, new questions. <em>I have never seen the city like this before. I can’t remember the last time I saw a tree this way. I didn’t know trees could be this colour. Or, I did, but I had forgotten.</em></p> <p>Perhaps the reason I write so much about Nature is that to better understand Nature I need to exist with its presence over time. New shades of light and seasons and people and days reveal attributes that I had previously not noticed, like the progression of green to burgundy to orange trees on a familiar path (how amazing it is that a tree can have a colour as intense as a deep red wine) that entertained me on my last walk. The path was the same, but the surroundings had changed. I was changing, with the walk, with the seasons, with the environment whose air was chilling and in which I need to rely more on myself to make warmth.</p> <p style="text-align: center;">⁂ ⁂ ⁂</p> <p>I walk along treaded paths because they help me get to where I want to go; the path on which I stood frozen one day is one that has taken me to worlds of art and Nature, rooms of stories, and plazas where my mind feels clearer. I love places where I can see far, where I stand in front of an incomprehensible amount of detail and let myself live in the moment, seeing what stands out. I love looking out to hills I know well and thinking about them. Yet it was only recently that I started to ask the question “what do these hills mean to me?” I started with what I know, and I keep looking, waiting to see what I might notice.</p> <p>In writing, I feel the intersection of time and knowledge. These words flow into existence as I formulate them into thoughts; as I process what I have seen and felt and think about their significance. I feel the weight of questions I cannot answer fully. <em>What does the concrete path mean to me?</em> I feel the weight of questions that have been with me for a long time. <em>What’s next?</em> If nothing else, each word gives a contour to a thought – a potential foundation for something new. In writing I feel the chance to sit with a question.</p> <p>Paintings and art in galleries has a similar effect on me. In looking at a painting I know well or have never seen before, questions always come to mind, questions I may not yet be able to answer, but that I can stow away. I can imagine what a world might have been like. I can think about what the world is like as I see it. I can be with time. I can feel my limits, as well as the energy that drives me forward, fuelled by the part of me that sees something and wants to think about it more, like the concrete path with the mirrored puddles on which I walked months ago and in which I still see <em>something</em>.</p> <p style="text-align: center;">⁂ ⁂ ⁂</p> <blockquote> <p><em>What does Nature mean to me?</em> This is one of those questions I’ll need to hold in my mind and my heart for a while. <em>-</em> <a href="https://jamesg.blog/2026/10/10/walking"><em>Me, last week</em></a></p> </blockquote> <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:'a4a685a67b4ee080',t:'MTc5MTk4MDI1OA=='};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://jamesg.blog/2026/10/10/walking">Me, last week</a> Emio - The Smiling Man - Joel's Log Files https://joelchrono.xyz/blog/emio 2026-10-13T19:00:00.000Z joelchrono <p>After the masterpiece that is <em>Outer Wilds</em>, I wanted to chill for a moment, and what a better way to do so than by going through a detective story!</p> <p>Famicom Detective Club is a pretty niche series by Nintendo, with only a couple games that were only on the NES—though recently remade for Nintendo Switch.</p> <p><em>Emio The Smiling Man</em> came out in 2024, and it brings the formula to current times!</p> <p>I first heard of this game a couple of years ago in an episode of <a href="https://intothecast.transistor.fm/episodes/whats-up-tommy-more-feat-metaphor-refantazio-slay-the-princess-and-emio-the-smiling-man">Into The Aether</a>, brought up by Brendon Bigley, and literally this <a href="https://intothecast.transistor.fm/episodes/the-impromptu-goty-feat-dicevaders-castlevania-belmonts-curse-emio-the-smiling-man-and-more">past week</a>, now that Stephen is finally playing it as well.</p> <p>I decided to give it a go during my Summer Game Challenge, and it’s finally time to write a review for it here.</p> <figure> <img src="/assets/img/blogs/2026-10-13-cover.webp" /> <figcaption>Artwork of the game</figcaption> </figure> <h2 id="story">Story</h2> <p>The game happens in Japan, and it seems to be dated somewhere in the 80s or 90s. The story begins when a high school student is found dead, wearing a paper bag with a creepy smiley face drawn on it.</p> <p>You take control of a junior detective—which you get to name! Jules Poirot in my case—working for the Utsugi Detective Agency and the police to uncover the truth of this murder.</p> <p>The case is linked with the urband legend of Emio, the Smiling Man—it’s the name of the videogame, wooooow!—which mentiones a creepy man wearing a paper bag who approaches young girls crying and—unless they smile when seeing him—murders them, covering their face with a paperbag with a smiley face drawn. Matching almost exactly with the event that triggered the game and investigation.</p> <p>At the same time, this murder is linked with a different case that went cold eighteen years ago, involving the serial murders of girls who died by strangulation too, although there are differences that need to be accounted for. You’ll have to research from multiple perspectives to find out the truth.</p> <p>Besides your character, you’ll work with Ayumi Tachibana, another junior in the agency that will go through her own investgation with a different group of characters to question. Both of you work for Shunsuke Utsugi, the private detective that runs the agency and mentors you both. He follows yet another lead in the case and seems to have a history working on the previous murders as well…</p> <p>The game will switch perspectives between both junior detectives as the plot unfolds, and feature separate cutscenes and sequences that will keep you on the edge of your seat until the end!</p> <figure> <img src="/assets/img/blogs/2026-10-13-1.webp" /> <figcaption>These characters are very suprised by some newfound information!</figcaption> </figure> <h2 id="gameplay">Gameplay</h2> <p>It’s basically a visual novel. You can Ask/Listen, Look/Examine, Think, Call with your cellphone, check your notebook and such.</p> <p>During any talk with a character, the conversation won’t simply be fed into you, you’ll have to react to things in a way that sort of makes sense, and it’s not just about mindlessly pressing talk over and over. The game provides hints of what could be done next as well.</p> <p>Sometimes you’ll have to <em>think</em> before talking again, sometimes a character will make a nervous face and you’ll have to look at them before continuing a conversation. There are a couple simple puzzles where you’ll look at the environment to figure out what to say next, and the like.</p> <p>At the end of every day, there’s a review time, where you go through all you’ve learned, fill up some blanks and check notes of your notebook to link clues together.</p> <p>Although the order in which you find out clues can change slightly, the game itself is rather linear, the reviews are a nice challenge for yourself, but getting something wrong doesn’t punish you much, it only leads to a correction later on to put you in the right track.</p> <p>The overall gameplay is really simplistic, but it can get confusing sometimes, when a chain of dialogue cuts off suddenly and you have to figure out what to do next. Thinking often helps a lot if you need a hint, but there are definitely situations where I was like a whole minute stuck and making no progress.</p> <p>You also have the Travel option, that shows up at the end of some dialogues, or while exploring the city. Most of these actions are pretty self-explanatory and will make sense as the game goes on.</p> <p>Admittedly, tha game it’s rather linear, don’t expect multiple endings and such. It’s a mystery slowly revealing itself to you and it’s very much worth it!</p> <figure> <img src="/assets/img/blogs/2026-10-13-2.webp" /> <figcaption>Characters in the game are quite relatable</figcaption> </figure> <h2 id="art">Art</h2> <p>This game is absolutely beautiful, as long as you are okay with the anime artstyle. It is easy on the eyes, minimal in interface and features plenty of high quality animation as well, such as mouth movements, expressions, hand gestures and the like. This is not your regular visual novel or JRPG with drawings being swapped around. Blinking, changes in posture, there are details everywhere. It kinda feels like they are rigged characters, like some VTuber streamers and YouTube channels. Looks fantastic.</p> <p>The backgrounds are also very well crafted, with a great amount of detail and many descriptions for you to find using the Look action. There are city streets, forests, the police station, and plenty of other environments, some of them featuring multiple NPCs in the background that have some small animations as well.</p> <p>This is the kind of stuff that a big company like Nintendo can do within a genre often lead by indie developers and small budgets. The budget doesn’t reach the heights of a Mario or a Zelda at all, but it’s clearly superior to what most visual novels would get, and it really shows in the presentation here.</p> <figure> <img src="/assets/img/blogs/2026-10-13-3.webp" /> <figcaption>Is this an accurate description, teachers who read this?</figcaption> </figure> <h2 id="music--sound">Music & Sound</h2> <p>The soundtrack of this game is fitting of the occassion, it has a nostalgic 80s vibe that makes me think of the origins of this series back in the Famicom, exclusively for Japan. The main theme already sets the mood of the whole thing, but it’s not all there is to it!</p> <p>It will get tense and emotional, as well as happy, carefree and even comical! The game is not afraid to jump between high stakes moments and comedic relief, and this is perfectly showcased on its soundtrack!</p> <p><em>Emio</em> also features fully voiced lines in Japanese. This is absolutely wonderful stuff. Even though I don’t understand the language itself, the emotional expressions and tone is easy to pick up from the excellent performances, and they really enhance every scene during revelations and the like.</p> <figure> <img src="/assets/img/blogs/2026-10-13-5.webp" /> <figcaption>If I could speak Japanese this investigation would be even easier!</figcaption> </figure> <h2 id="overall">Overall</h2> <p>Something I really appreciate about this game is how well it manages the tone of its story. There are really gruesome implications and dark moments here, some stuff that I would have never thought Nintendo would even publish, but this game does handle things well.</p> <p>There are also quite a few fun moments, the game is not afraid to play with your assumptions and use them for comedic relief. There are also a couple genuinely charming moments between the characters. The dialogue is quite fun and easy to go through (as long as you don’t get stuck in a dialogue trying to figure out what’s next!) and the writing of the whole thing is really marvelous, drip-feeding you information in the best of ways.</p> <p>The final chapters and the build up of the big reveal is genuinely fantastic, and I really think it makes every little nitpick worth it! However, it did feel a bit rushed, and some things are not explained after the credits rolled, so make sure you stick until the end to see that side of things. Definitely worth it in my opinion.</p> <p>At the end of the day, this is still a visual novel. If you don’t want to basically read and read and click on things, this game will not be for you.</p> <p>If you want multiple pathways and different endings, this is also not the game you’re looking for.</p> <p>However, if you are up for an excellent murder mystery story to read through and find out clues before the game gives them to you, this is genuinely fantastic stuff.</p> <figure> <img src="/assets/img/blogs/2026-10-13-6.webp" /> <figcaption>The readers who reached the end of this review be like:</figcaption> </figure> <p>This is day 50 of <a href="https://100daystooffload.com">#100DaysToOffload</a></p> <p> <a href="mailto:me@joelchrono.xyz?subject=Emio - The Smiling Man">Reply to this post via email</a> | <a href="https://fosstodon.org/@joel/117435179461225182">Reply on Fediverse</a> </p> Young people trust AI more than the people in their lives. - Werd I/O 6ace7861347e5700016794e4 2026-10-13T18:28:49.000Z Werd I/O <p>Link: <a href="https://knightfoundation.org/articles/research/young-people-and-the-future-of-free-expression/"><em>Young People and the Future of Free Expression, by the Knight Foundation</em></a></p><p>Worth paying attention to:</p><blockquote>“New Knight Foundation-Ipsos research among young adults and high school students points to a generation that strongly values free speech as a democratic principle, but is less confident about exercising it without consequences.”</blockquote><p>Workplaces and classrooms feel the least free, but it’s good to hear that home is generally a space where they feel <em>more</em> free.</p><p>What’s surprising, and a bit worrying, is that AI chatbots are also one of the spaces where they feel free — and, in fact, 48% of high school aged respondents that used AI chatbots said they felt more free to speak to them than the people in their lives. Those conversations might feel like intimate spaces, private to them; in reality, they may be available to their vendors for analysis and even training. People from low income backgrounds felt this more than most. Young people worry about conflict: they hold back because of peer censorship and the fear of being judged. The perception is that the AI won’t judge them. In a world where chatbots <a href="https://en.wikipedia.org/wiki/Deaths_linked_to_chatbots">have been linked to self-harm and suicide in young people</a>, that’s quite dangerous.</p><p>The other findings here dovetail together to paint a pretty bleak picture. White and wealthier people feel more able to exercise their freedom of speech without consequence, and that number plummets to just 28% of Black respondents. Only around a half of college aged respondents felt like other democratic rights like voting were secure.</p><p>So we find ourselves in a world where people don’t feel free to express themselves — except to AI chatbots that happen to be produced by some of the tech companies who have directly contributed to this underlying authoritarianism. It’s obvious how those conversations could be compromised, and there are certainly betrayals to come — but more than that, it’s just sad that young people don’t feel they can speak and share with each other.</p> I'm thinking about making some changes - Cool As Heck https://cool-as-heck.blog/im-thinking-about-making-some-changes 2026-10-13T12:55:34.000Z cool-as-heck <div>In recent months, the cost of running DMV.community has increased. At the same time, donations have been decreasing.</div> <div><br></div> <div>To be fully transparent, there have been times where I've considered shutting it down. I've been experimenting with running a GoToSocial instance from my home server, just in case. I've been running more things from my home server in general anyway. I recently started up a BBS/forum at <a href="https://bbs.snakepit.us">https://bbs.snakepit.us</a>, if anyone is interested in joining.</div> <div><br></div> <div>I'm not sure what I'm going to do yet, just wanted to put it out there in the interest of honesty and transparency.</div> Book Review: The Thousand Deaths of Romeo and Juliet - Caroline Finch ★★★★★ - Terence Eden’s Blog https://shkspr.mobi/blog/?p=76683 2026-10-13T11:34:37.000Z Terence Eden’s Blog <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/10/randj.webp" alt="Book cover." width="200" class="alignleft"> <p>The thing about Romeo and Juliet is that it is a true story. It isn't literally true, but it <em>is</em> literary true. We see the same motif repeated throughout all cultures and all times. Caroline Lea (writing as Caroline Finch) asks an important question - what if every pair of star-cross'd lovers were reincarnations of the original Verona duo?</p> <p>The essence of tragedy is that the end is unavoidable. You've probably watched Romeo sup poison a hundred times. It is <em>inevitable</em> that Juliet will take her own life in response. Horrific! Yet, not only do you refuse to look away, you gleefully seek it out for another viewing. There's a reason why Tony and Maria cannot escape their destinies in West Side Story - something more than fate is pushing them onto a path that will accept no deviation. The only way through is, of course, death.</p> <p>The Thousand Deaths starts slowly - the first "act" felt like an excuse to retell the classic tale in a new setting. Nothing wrong with that, of course, and it is suffused with lovely writing - but it felt a bit "so what?"</p> <p>And then they die.</p> <p>As the acts progress and they get closer to the their thousandth death the pace picks up. The writing is absolutely gorgeous, packed with allusions and references for the careful reader (more on that later). The story is heartbreaking - as well you know - and the author has the perfect knack of keeping her characters on just the right side of histrionics.</p> <p>I'd sort of classify this as <em>almost</em> romantasy. There's an undercurrent of sci-fi and fantasy which can't be ignored - but you can also hear the stirring of the violins in the background. Our hero and heroine come together and are violently broken apart for our insatiable entertainment. Do we care about whether that's good for them? What does it feel like to be a Mercutio forever used as a pawn-sacrifice? Does Tybalt resent his forcible reincarnation into a perpetual baddie? Are we culpable for insisting that they strut upon an endless stage just so we can wipe tears from our eyes?</p> <p>The book builds to a fine crescendo. It is heartfelt and <em>utterly</em> tragic. The ending will, I suspect, divide people. I found it mostly satisfactory and completely in keeping with the rest of the story.</p> <p>One final thing - the end notes are <em>superb</em>. They go through all the references you might have missed, explain where certain phrases came from, and point the curious reader to the books which were an inspiration. Too many authors treat their literary allusions as a precious puzzle and jealously guard them. I'm delighted that Caroline Finch is generous enough to share her secrets with us.</p> <p>Many thanks to NetGalley for the review copy. The book will be released in 2027 - I strongly recommend putting it on your pre-order list.</p> <img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76683&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager"> Wonders of Web Weaving, Episode 23 - James' Coffee Blog https://jamesg.blog/2026/10/13/www-23 2026-10-13T00:00:00.000Z James' Coffee Blog <p><a href="https://web-weaving.jamesg.blog/23">The twenty-third episode of Wonders of Web Weaving is out</a>:</p> <blockquote> <p>In Episode 23, I chat with <a href="https://werd.io">Ben</a>, the author of <a href="https://werd.io">werd.io</a> about, among other things, connection on the indie web, community building, growing the indie web, 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:'a49c789edc1ad2ec',t:'MTc5MTg3NDg2Nw=='};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://web-weaving.jamesg.blog/23">The twenty-third 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> <a class="tag" href="https://werd.io">Ben</a> <a class="tag" href="https://werd.io">werd.io</a> Cycling, Jigsaws and Ice - W41 - Joel's Log Files https://joelchrono.xyz/blog/w41 2026-10-12T23:00:00.000Z joelchrono <p>Another week of October is gone, just like that. The coldness of the air is finally felt enough for me to wear a long-sleeved shirt every once in a while, I am yet to actually keep a jacket on for a whole day honestly!</p> <ul> <li> <p>🗓️ <strong>I made a plan!</strong> Detailing the videogames and books I want to complete by the end of October (plus one week of November). So far very good, a book finished, a game started.</p> </li> <li> <p>📚 <strong>Updated the firmware of my XTEINK X4</strong> to the latest version of CrossPoint 1.6.5. There have been plenty of changes that I am yet to actually explore! In any case, it’s pretty cool to see the support for even more devices since then.</p> </li> <li> <p>🕹️ Decided to buy <em>Astral Ascent</em> for Nintendo Switch, I’ve wanted to have <strong>a fast-paced 2-player roguelike</strong> for those times I want something challenging for a friend and I. We didn’t get around to playing it this time though.</p> </li> <li> <p>🎮 Realized that <strong>Steam finally let’s me gift games outside my region!</strong> I’ve been meaning to gift my favorite games to my online friends sometimes, and it wouldn’t work. Now they let you pay the international price (just like GOG has done for ages). I already made use of this feature for a few of my buddies based on their wishlists. Are you one of the lucky ones?</p> </li> <li> <p>🛍️ Had a fun time going out for <strong>lunch with my parents</strong>. This was a more normal visit to the mall. We also went to the library there but nothing caught my eye this time around. I also got a <em>Rice Krispies Treat</em> bar which I hadn’t eaten in ages. So good.</p> </li> <li> <p>🚲 Finally <strong>went out on my bicycle</strong> after a bunch of lazyness, the day was cloudy and the air was fresh, I still only wore shorts and a t-shirt though. I went downtown and enjoyed the scenery quite a bit!</p> </li> <li> <p>📖 I actually stayed in the plaza downtown and <strong>read a book sitting on a bench under a tree</strong>right in front of the city’s cathedral. The weather got very cold while I read some of the latter chapters of <em>The Left Hand of Darkness</em>, an atmosphere that fit the narrative perfectly.</p> </li> <li> <details><summary>🙏 This note deals with loss of life</summary>My church visited the hospital to give food, coffee or water to people there. During this, a family outside got news of a patient, a loved one, which passed away. It was a sad and difficult moment, my church gathered around them, we prayed, and cried with them. Hope and solidarity among the suffering.</details> </li> </ul> <figure> <img src="/assets/img/blogs/2026-10-12-week.webp" /> <figcaption>A bar of Rice Krispies Treats, a panel from Alita Battle Angel, my Kobo and my bicycle in the background, some coffee and sweet bread, art from Echoes of the Eye, a puzzle!</figcaption> </figure> <h2 id="reading">Reading</h2> <p>A magazine, two books and a manga this time around. Some great speculative fiction, cyberpunk, horror and the like.</p> <h3 id="started">Started</h3> <p><strong>Clarkesworld Magazine #212</strong> - I decided to return to my XTEINK X4 and start this at last! I managed to complete the first short story: <em>Fishy</em> by Alice Towey. I absolutely fell in love with that short story, it was very charming and it brought me joy.</p> <h3 id="completed">Completed</h3> <p><strong>The Left Hand of Darkness</strong> - Chapters 8-20. In just about a week I managed to bring this book to completion! It only got better as the chapters went on. The world grew on me, the characters, the dire situation, a sense of understanding that deepened over time, similarly to the plot of the book and the development of the characters. This was a very interesting read, and I’m glad I gave it a go at last!</p> <h3 id="ongoing">Ongoing</h3> <p><strong>The House on the Borderland</strong> - Chapters 13-14. I only did a few pages of this book, but they were extremely powerful stuff. It was one of those moments in the story that barely contain any detail, as the understanding of what’s in front of you is null. Pages and incomplete fragments, frightening stuff. Looking forward to what’s next.</p> <p><strong>Alita Battle Angel</strong> - Chapters 45-48. The final volume of the manga has begun, and some more battles are still on the horizon. I have to say I am quite hyped to bring an end to this. A lof of the cyberpunk aesthethic can be seen in this work, although the story was a little mixed, it reached great highs nonetheless. We’ll see if it sticks the landing.</p> <h2 id="watching">Watching</h2> <p>A movie and a TV show, both of pretty entertaining!</p> <p><strong>Bumblebee</strong> - I thought I had seen this movie before but it’s not on my Letterboxd… In any case, I really enjoyed this Transformers film, the original designs of these characters are just so awesome to see! This kinda feels like a strong edition of E.T./The Iron Giant and I love it.</p> <p><strong>Lanterns</strong> - Watched chapter 5 of this one which seems to have ended an “arc” or big bad guy and I’m curious to see how the show progresses after the very interesting events that happened at the end of this one.</p> <h2 id="gaming">Gaming</h2> <p>A puzzle game completed, a new mystery begins, and some classic multiplayer fun.</p> <h3 id="started-1">Started</h3> <p><strong>Outer Wilds: Echoes of the Eye</strong> - The first game on my October plan. My breath was taken away the moment I ventured into this adventure within the main game… I have to say I am extremely surprised by where this DLC is going. I will not make any more spoilers. <a href="/blog/outer-wilds/">Just play Outer Wilds</a></p> <h3 id="completed-1">Completed</h3> <p><strong>Strange Jigsaws</strong> - I got this on a whim a while back and decided I’d finally give it a shot. It absolutely hooked me and I got it beat in a single day. This is a mind-wrecking title and you really owe it to yourself to give it a go. I definitely had to search a couple hints on the website though, I admit. Superb concepts.</p> <h3 id="ongoing-1">Ongoing</h3> <p><strong>Nintendo Switch Sports</strong> - I continue to get decent at Golf but I also played with friends locally this time! Fun times.</p> <p><strong>Ultimate Chicken Horse</strong> - Returned to this epic game and it continues to surprise us, we unlocked a whole new map to play through, and the Fox character! There’s only two maps left to unlock.</p> <p><strong>Kirby and the Forgotten Land</strong> - Played this one for a little while again, I completed one level and then I was asked to put another game, some day!</p> <h2 id="around-the-web">Around the Web</h2> <ul> <li> <p><a href="https://hollie.eilloh.net/posts/im-doing-better-i-had-covid">I’m doing better! I had covid.</a> by Hollie - This was a very interesting summary of what Hollie has been going through lately. COVID is still a danger, and shouldn’t be dismissed.</p> </li> <li> <p><a href="https://brainbaking.com/post/2026/10/laptop-stickers-yay-or-nay/">Laptop Stickers: Yay Or Nay?</a> by Wouter Groeneveld - I have stickers on my laptop and I’m actually surprised I’ve not written about it.</p> </li> <li> <p><a href="https://adrianperales.com/2026/10/mas-cacharros-que-me-gustaria-probar/">Más cacharros que me gustaría probar</a> by Adrian Perales - In Spanish. A post from Adrian sharing some neat tech he doesn’t have. I want him to have some of these though, what to do?</p> </li> <li> <p><a href="https://kevquirk.com/my-hobby-is-learning">My Hobby Is Learning</a> by Kev Quirk - This is what I enjoy as well, very well put here.</p> </li> <li> <p><a href="https://brennan.day/here-is-a-person-enter-the-tildeverse/">Here Is a Person: Enter the Tildeverse</a> by Brennan Kenneth Brown - I really love Tildes, and this run down some of their history was very epic to go through.</p> </li> <li> <p><a href="https://schafe-sind-bessere-rasenmaeher.de/posts/outer-wilds-2019/">Outer Wilds (2019)</a> by Robert - I’m glad to be a part of this wonderful journey of making everyone I know play Outer Wilds.</p> </li> </ul> <p>This is day 49 of <a href="https://100daystooffload.com">#100DaysToOffload</a></p> <p> <a href="mailto:me@joelchrono.xyz?subject=Cycling, Jigsaws and Ice - W41">Reply to this post via email</a> | <a href="https://fosstodon.org/@joel/117430486147268187">Reply on Fediverse</a> </p> I paid people to try and follow my README - Terence Eden’s Blog https://shkspr.mobi/blog/?p=76333 2026-10-11T11:34:39.000Z Terence Eden’s Blog <p>Developers suck at writing documentation. We like to say RTFM but often there ain't no FM to R. At best we have a half-arsed collection of notes, some obsolete tips, and a link to a desolate forum full of people asking the same questions again and again. That's no way to treat our users.</p> <p>I naïvely believe there's a better way. As I've written before, you should sit down and <a href="https://shkspr.mobi/blog/2025/10/how-to-actually-test-your-readme/"><em>actually</em> test your readme</a>. Spin up a fresh Virtual Machine, go through what you've written, and see if it makes sense.</p> <p>But it is <em>really</em> hard to ignore your own biases. Of course you know that certain commands require <code>sudo</code> and obviously when you wrote <code>-foo</code> you meant <code>--foo</code> and everyone knows that you have to reboot afterwards.</p> <p>How do you get rid of those biases? I paid people!</p> <p>As part of my <a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/">NLnet grant application for ActivityBot</a>, I said I wanted to test the install experience with real users and I was prepared to pay them €25 for an hour of their time. I stuck out the call on Mastodon, gathered a few people, and had a video call with them.</p> <p>The format was simple. I explained that I was looking for feedback on the first-run experience. I knew that it wasn't perfect and actively wanted constructive criticism. I asked the volunteer to share their screen and, crucially, to speak aloud. Tell me what they were doing. What they didn't understand, what confused them, what delighted them, what frustrated them, etc.</p> <p>I took notes by hand (fuck feeding the machine) and spent several hours being told what incorrect assumptions I'd made.</p> <p>Amongst the highlights I discovered were:</p> <ul> <li>The link to the demo tool was wrong.</li> <li>Some people read a README in the terminal.</li> <li>What does it actually mean to rename a file?</li> <li>How do you rename a hidden file?</li> <li>Should a demo tool be available on the web or just the user's local machine?</li> <li>Why does some text explicitly need to be quoted and some not?</li> <li>My jokes aren't funny and are actively confusing.</li> <li>Some of the technical terminology needed explaining.</li> <li>The ordering of the different sections was confusing.</li> <li>I hadn't actually explained what the software would do.</li> <li>A whole section which was technically interesting to me was utterly confusing to everyone else.</li> <li>Not all web servers use permissions in the same way.</li> </ul> <p>And on it went!</p> <p>After each session I updated the README based on what people found difficult, then I re-tested it with the next person.</p> <p>In total, I paid out around €150 to have a bunch of people criticise me to my face. Hey, cheaper than therapy, right?</p> <p>I know someone is going to say "why not just ask an LLM to simulate a range of users?" The answer is very simple - I want to speak to real people. People are brilliant! They can make you laugh, you can see their cat when it wanders on to the call, they bring a unique perspective to the problem, and they're really happy when you give them a €25 voucher. Some will gladly do it for free and make <em>you</em> happy!</p> <!--👋👋👋👋👋👋👋 Welcome to Comment Club! Shhhh! Only cool people can read this bit of the post. I did have a serious think about using an LLM for this. Ethics aside (and you can never really put ethics aside) I just didn't feel like I could trust their judgement. I get the argument that it is not much different from using a spellcheck - but I fundamentally don't want to feed the beast. 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 future blog posts 😃 Be seeing you 👌👁 --> <p>When I was doing technical writing for GOV.UK, all my beautiful prose was given a second-eye by another human. They eviscerated all my flowery phrases and turned the document into something more readable. They were the ones who caught the mistakes that no spell chequer could. They were able to have a proper conversation with me. More importantly, I could <em>hear</em> the frustration in their voice - that's the thing which lets you know a mistake needs to be corrected.</p> <p>I don't claim that <a href="https://gitlab.com/edent/activity-bot/-/blob/main/README.md?ref_type=heads">ActivityBot's README</a> is now perfect - far from it - but it is now demonstrably easier to follow. Developers need to talk to <em>real</em> people. You don't have to pay them if you can't afford it - but find a few people who will talk aloud while they try to follow your instructions. I guarantee you that your README will become much better for it.</p> <img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76333&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager"> Johnny.Decimal en français - Johnny.Decimal https://johnnydecimal.com/blog/0256-jd-en-francais/ 2026-10-11T11:16:22.000Z Johnny.Decimal <div lang="fr"><p>Si vous parlez français, allez voir le site de Monique. Elle écrit sur Johnny.Decimal et d'autres techniques d'organisation.</p><p>Monique a eu la gentillesse de m'écrire pour me demander si elle pouvait republier certains de nos concepts.</p><p>Vous trouverez son site sur <a href="https://monauberson.ch/johnny-decimal/">monauberson.ch</a>.</p></div> <hr> <h2 id="johnnydecimal-in-french">Johnny.Decimal in French</h2> <p>If you speak French, check out Monique's site. She's writing about Johnny.Decimal and other organisation techniques.</p> <p>Monique was kind enough to write and ask me if it was okay to republish some of our concepts.</p> <p>You'll find her site at <a href="https://monauberson.ch/johnny-decimal/">monauberson.ch</a>.</p> <blockquote> <p>French by Claude. Sorry, Mrs. Haddad. It's been a long time.</p> </blockquote> Wonders of Web Weaving, Episode 22 - James' Coffee Blog https://jamesg.blog/2026/10/11/www-22 2026-10-11T00:00:00.000Z James' Coffee Blog <p><a href="https://web-weaving.jamesg.blog/22">The twenty-second episode of Wonders of Web Weaving is out</a>:</p> <blockquote> <p>In Episode 22, I chat with <a href="https://tracydurnell.com">Tracy</a>, the author of <a href="https://tracydurnell.com">tracydurnell.com</a> about, how websites fit into writing, writing series of posts, websites as part of conversations, 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> <p><em>The episode was released on Tuesday, October 11th. I usually publish the blog post announcement on the day, but I forgot this week.</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:'a49034be1c5f4b36',t:'MTc5MTc0NjI1Nw=='};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://tracydurnell.com">Tracy</a> <a class="tag" href="https://tracydurnell.com">tracydurnell.com</a> <a class="tag" href="https://web-weaving.jamesg.blog/22">The twenty-second 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> Some quick thoughts on Unit Testing ActivityPub - Terence Eden’s Blog https://shkspr.mobi/blog/?p=75774 2026-10-10T11:34:15.000Z Terence Eden’s Blog <p>I'll be radically honest here - I never really got the idea of "Test Driven Development". I'm much more of a "telnet into production and damn the consequences" kind of guy. Look, it's much more fun and my cardiologist recommends severe shocks every now and again.</p> <p>The essence of unit testing is pretty simple:</p> <ol> <li>Feed a function some data</li> <li>See how it responds</li> </ol> <p>Send it good data and if it returns "true" the test passes. Send it bad data and make sure it returns false. Easy peasy lemon squeezy.</p> <p>As part of my <a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/">NLnet funded work on ActivityBot</a>, I'm writing some proper tests for my bot framework. I am hopeful that some of them will be useful to other projects doing similar things.</p> <p>There are four major classes of tests - here are the problems I've found while writing them.</p> <h2 id="basic-text-validation"><a href="https://shkspr.mobi/blog/2026/10/some-thoughts-on-unit-testing-activitypub/#basic-text-validation">Basic text validation</a></h2> <p>A basic test might be how to validate that an ActivityPub username is valid. Typically it would be something like <code>@edent@mastodon.social</code> - what assumptions do we make with that? It starts with an <code>@</code> and has another <code>@</code> somewhere in it, dividing into two parts, the user and the server. The server has to be a valid domain name - but that can include non-ASCII Internationalised Domain Names and Emoji domains.</p> <p>What about the user part? Is it just A-Z and 0-9? Can it have dots? What about apostrophes like an email address?</p> <p>Basically, how easy is it find the specifications for all the various component parts of ActivityPub? Not very!</p> <h2 id="message-validation"><a href="https://shkspr.mobi/blog/2026/10/some-thoughts-on-unit-testing-activitypub/#message-validation">Message validation</a></h2> <p>What does a <a href="https://www.rfc-editor.org/info/rfc7033/">WebFinger response</a> look like? Which parts are mandatory and what values can they contain?</p> <p>With any message that your project generates, how can you be sure that other ActivityPub servers will understand it?</p> <p>Similarly, when we receive a follow message from an external server, what should it look like?</p> <p>There are some <a href="https://github.com/steve-bate/fediverse-jsonschema">JSON Schemas</a> which can be used to validate some type of messages.</p> <p>But, again, there are <em>many</em> scattered specifications.</p> <h2 id="signature-verification"><a href="https://shkspr.mobi/blog/2026/10/some-thoughts-on-unit-testing-activitypub/#signature-verification">Signature verification</a></h2> <p>As I've written about before, <a href="https://shkspr.mobi/blog/2026/09/a-reasonably-practical-guide-to-validating-rfc-9421-http-signatures-for-activitypub-in-php/">HTTP Signatures are difficult</a>. There are two main problems. The first is verifying signatures.</p> <p>In order to do that, you need to gather a bunch of signatures and then find a way to replay them into the test. So I'm in the process of saving signatures which I can then share with others.</p> <p>The second problem is <em>generating</em> signatures. Sure, you can run tests on the signatures - but there's only one <em>real</em> way to test where's they're acceptable…</p> <h2 id="communicating-with-others"><a href="https://shkspr.mobi/blog/2026/10/some-thoughts-on-unit-testing-activitypub/#communicating-with-others">Communicating with others</a></h2> <p>Passing tests means nothing if you can't communicate with others and they can't communicate with you.</p> <p>Some tests are easier than others. Sending a signed request should be easy and repeatable. But receiving messages isn't easily automated. It requires being able to control an external server and asking that to send messages.</p> <p>With the <a href="https://docs.joinmastodon.org/client/intro/">Mastodon API</a> it is possible to make requests which will interact with your server - but only if it is a <em>public</em> server.</p> <h2 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2026/10/some-thoughts-on-unit-testing-activitypub/#putting-it-all-together">Putting it all together</a></h2> <p>I'm putting <a href="https://gitlab.com/edent/activity-bot/-/tree/main/tests?ref_type=heads">all my tests online</a>. I'm also sharing a <a href="https://gitlab.com/edent/activity-bot/-/tree/main/tests/headers?ref_type=heads">collection of messages and signatures</a> which you can use in your tests.</p> <p>I'd love to know if you find them useful.</p> <img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=75774&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager"> Weeknote #2021 - Robb Knight • Posts • Atom Feed https://rknight.me/blog/weeknote-2021/ 2026-10-10T06:12:00.000Z Robb Knight <p>I'm not updating my personal Mac so this isn't relevant to me but <a href="https://github.com/omlahore/RemoveMacAI">this script</a> turns off all the Apple Intelligence shit on MacOS and gets the model space back.</p> <p>I did <a href="https://www.coryd.dev/posts/2026/become-worthless-to-tech-companies">link to this post from Cory</a> but it's worth linking again now — this is like the sabotage field manual for the modern web.</p> <p><a href="https://crawlerzoo.com">Crawler Zoo</a> is fun.</p> <p>Blake made his own <a href="https://www.watsonbrosgames.com/solitaire/">Solitaire game</a> and I got distracted for a lot longer than I should when he linked it.</p> <p>Da Vinci, the guy who totally "invented" the helicopter, drew some <a href="https://www.fastcompany.com/3035602/comical-penis-drawings-from-the-sketchbook-of-leonardo-da-vinci">legged penises in his sketchbook</a>. Inside every man there is a burning desire to draw knobs.</p> <p>I wanted some new coloured pencils and I ended up down a rabbit hole of comparisons and information about who manufactures what for who. <a href="https://sarahrenaeclark.com/ultimate-colored-pencil-comparison/">This comparison</a> was my <s>gateway drug</s> starting point. I landed on this <a href="https://www.amazon.co.uk/dp/B09Y1NKH8X">Melifluo/Brutfuner set</a>.</p> <p>This <a href="https://github.com/szabadkai/c64-keyboard-font/">recreation of the Commodore 64 keycap font</a> is perfect.</p> <p>This <a href="https://getartcraft.com/apps">AI generated copy of the Abode suite</a> is certainly something to watch if only to see how long it lasts.</p> <p>This person <a href="https://antonfrolov.substack.com/p/i-hired-an-illustrator-to-draw-my">hired an artist to draw their house and now they're using it for their home assistant dashboard</a> and it's cool as fuck.</p> Walking - James' Coffee Blog https://jamesg.blog/2026/10/10/walking 2026-10-10T00:00:00.000Z James' Coffee Blog <p><em>I stand before Nature, looking up to a castle bathed in gold light. What are perfect places? I wonder if this is the closest I’ll find: with the air of home and the light of Day, with the breeze in the trees and history to see; with life in the moment and rhythm and movement; with gentleness bathing the Earth, winds propelling leaves into the air, painting the picture of the season.</em></p> <p style="text-align: center;">⁂ ⁂ ⁂</p> <p>Going on a long walk through the city has become something of a tradition for me over the last few weeks. I like to take the long way to run errands: through the beautiful, expansive parks in which I see so many other people walking as the sun beams through the trees; through the streets with beautiful architecture, whose buildings leave me in awe; walking on the paths I love, while holding an eye toward discovering new ones.</p> <p>Every walk is different, my feet going where they would like, guided by direction and experience but also wonder and curiosity. <em>“What if I take this path?”</em> I can walk with and through the feelings in my own heart, while listening to whatever songs seem to fit the mood, the energy moving from my ears to my mind to my legs that walk forward and forward. Occasionally, a song asks me a question. <em>"What the fuck are perfect places anyway?"</em> (Lorde)</p> <p>The light from the sky was anew today; the sun lowers as autumn progresses. Paths I usually take become blindingly bright by mid-day. Whereas other places – like the aforementioned castle, or the pastry shop into which an intense glow beamed, drawing attention to the pastries in the front while I enjoyed the pleasing scent permeating through the open door – are illuminated in a way that brings me joy.</p> <p>I thought about the familiar and the new. How the places I walk were once new and, with time, and walking, became familiar – how there is a spectrum of familiarity: of the places I know well, and of the places I am yet to discover. Standing outside one of my favourite bookshops, enjoying the powerful, cooling autumn breeze that, until this week, I didn’t realise how much I missed, I thought about how life is a mix of what we know and what we experience for the first time. Every moment is new, but holds associations to what we know.</p> <p>I have visited my favourite bookshop a few times since they moved. But I have never visited it today, on this exact date – therein, the familiar and the new, together.</p> <p>By the end of my journey today I felt my legs more than any other day this week; tense from all the walking, from all the exploration. I enjoyed my favourite muffin for lunch while watching the world go by, carrying with me a book on art history that caught my eye earlier in the day. Later on, I started to read the first chapter of the book with excitement – a relaxing moment after an invigorating day.</p> <p style="text-align: center;">⁂ ⁂ ⁂</p> <p>And now I write, holding onto a question I have been thinking about ever since I started my second year at university, one provoked by our present studies of art and its environments: <em>what does Nature mean to me?</em> This is one of those questions I’ll need to hold in my mind and my heart for a while. For now, however, the question lives here, in the story of today.</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:'a48687094f176172',t:'MTc5MTY0NDc3MA=='};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>