Shellsharks Blogroll - BlogFlock2025-12-02T12:04:09.236ZBlogFlockWerd I/O, cool-as-heck, destructured, Evan Boehs, <span>Songs</span> on the Security of Networks, Adepts of 0xCC, Aaron Parecki, cmdr-nova@internet:~$, Sophie Koonin, Westenberg, fLaMEd, Hey, it's Jason!, gynvael.coldwind//vx.log (pl), Johnny.Decimal, James' Coffee Blog, Terence Eden’s Blog, Molly White, Robb Knight, joelchrono, Trail of Bits Blog, Posts feed, Kev QuirkIntroducing Roundabout - Werd I/O692e3cb4b96d5b0001117aa52025-12-02T01:11:16.000Z<p>[<a href="https://newpublic.substack.com/p/introducing-roundabout-built-for?ref=werd.io">Hays Witt and Josh Kramer at New_ Public</a>]</p><p>Really interesting to see <a href="https://newpublic.org/?ref=werd.io">New_ Public</a> announce its first community product from its <a href="https://newpublic.org/local?ref=werd.io">Local Lab</a>:</p><blockquote>“The main thing to know, maybe the most important thing, is that this is not just another social media app. Roundabout is a community space, built from the ground up with community leaders and neighbors.<br><br>[…] As a project incubated within New_ Public, a nonprofit, Roundabout will grow incrementally, sustained by a diverse and balanced set of revenue sources. With business incentives aligned towards utility and everyday value, instead of engagement and relentless scale, we’re designing Roundabout to be shielded from the cycle of enshittification. The ultimate goal is to build for social trust — every decision, every design, optimized to build bonds and increase belonging.”</blockquote><p>There’s a lot to comment on here.</p><p>It’s <em>amazing</em> to see a social product co-designed with communities. For the safety and equity of all involved, this is how it should be done. I really hope New_ Public shows off more of its methodology in the future. I’d love to dive into the meta-conversation about what they’ve learned about this kind of co-design. The descriptions of participating communities — in Burlington, NC; Richmond, VA; Lincoln County, WI; North Chattanooga, TN; and Lancaster, PA — are already really promising.</p><p>The technical lead is <a href="https://bsky.app/profile/blaine.bsky.social?ref=werd.io">Blaine Cook</a>, who you might remember as Twitter’s first employee and first CTO. Since then he’s been a strong, sharp advocate for decentralized social.</p><p>On Mastodon, New_ Public <a href="https://mastodon.social/@wearenew_public/115645643962877064?ref=werd.io">mentioned that it’s building the platform in a way that’s compatible with AT Protocol</a>, although it’s not the main focus for now.</p><p>Over on Bluesky, <a href="https://bsky.app/profile/blaine.bsky.social/post/3m6vbrdgvn22a?ref=werd.io">Blaine said they’re</a> “building on atproto primitives but off-network because it's currently not possible to push private/scoped data around the wider atproto network.” He also made the important point that it’s not worth building for interop until you know what the user behaviors are actually going to be — so it’s too early to focus on decentralization.</p><p>That community co-design is key, and it makes sense that this is the first step. Communities are human; they can’t be defined by protocols. The protocols should describe real human behavior, not the other way around.</p><p>I’m excited to see how the platform develops, and how New_ Public seeds the ecosystem conversations around it. And: this is only one of its community initiatives. There’s more to come.</p><p>[<a href="https://newpublic.substack.com/p/introducing-roundabout-built-for?ref=werd.io">Link</a>]</p>What Happens After the Hype? Lessons from Mobile Internet’s Long Road to Success - Werd I/O692da766b96d5b0001117a922025-12-01T14:34:14.000Z<p>[<a href="https://shomila.medium.com/what-happens-after-the-hype-lessons-from-mobile-internets-long-road-to-success-22d0b15e0625?ref=werd.io">Shomila Malik</a>]</p><p>We’re beginning to see the end of the AI hype cycle, and thank goodness. That doesn’t mean that there aren’t uses for aspects of the technology, but it does mean that some of the hyperbole will diminish as investors and speculators move on to the next thing.</p><p>As <a href="https://ecruecard.com/?ref=werd.io">Ecrue</a> founder Shomila Malik points out here:</p><blockquote>“The question isn’t whether the current AI investment cycle will face a reckoning. It’s what form that reckoning takes — and what comes after.”</blockquote><p>The lessons she draws from the mobile industry’s hype and decline also parallel what happened during the dotcom crash, when a lot of companies went away but a lot of underlying useful infrastructure was left for the next generation of innovations. But a facet of how those two events were different is exactly <em>how</em> they imploded:</p><blockquote>“The difference between a pop and a deflation often comes down to how adaptable the infrastructure is. 3G networks built for one vision of mobile internet ended up powering something completely different — but they still got used. The investment wasn’t wasted, just redirected. Time will tell if AI will be a deflation like mobile internet or a ear deafening explosion like the dot com crash.”</blockquote><p>Either way, investment is way ahead of proven capabilities or even business models. Companies like OpenAI are losing money hand over fist. At some point, these endeavors have to touch oxygen, and either they’ll find their way to stunning profitability, or they’ll fizzle into acquisitions at best and leave some interesting ideas behind.</p><p>My bet? Ten years from now we’ll be looking at a series of smaller, more focused models that perform well-scoped tasks really well, and we’ll look back at the hype around generalized megamodels — and particularly AGI — with rolled eyes and a slight shudder when we remember the environmental and human impacts.</p><p>[<a href="https://shomila.medium.com/what-happens-after-the-hype-lessons-from-mobile-internets-long-road-to-success-22d0b15e0625?ref=werd.io">Link</a>]</p>Year 3 at the Smallholding - Kev Quirkhttps://kevquirk.com/blog/year-3-at-the-smallholding/2025-12-01T12:00:00.000Z
<p style="font-size: 1.2em;">We're been living on our smallholding in Wales for 3 years now. Here's how things have been going this year.</p>
<p>I’m a little late with my update this year because I’ve had a few things going on at home. This has accidentally become an annual tradition at this point, so I’m gonna roll with it.</p>
<p>I re-read <a href="https://kevquirk.com/blog/year-2-at-the-smallholding/">last year’s update</a> to see what I did and, more importantly, what I’d listed in the closing <em>what’s next?</em> section. It included:</p>
<ul>
<li>Renovating the brick shed on the drive into an annex for guests.</li>
<li>Continuing to work on the garden.</li>
<li>Insulating the roof in the conservatory.</li>
<li>Two more bathrooms, a downstairs loo, and the kitchen.</li>
</ul>
<p>Well, dear reader, most of that plan went to utter rat shit this year. Aside from continuing to work on the garden, we got absolutely nothing on the list done. That’s mainly because we ended up having to replace the entire roof on the house, which is still ongoing as I write this.</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-12-01-year-3-at-the-smallholding/roof.webp" alt="Our roof, half stripped" /></p>
<p>Worse still, because the new roof is heading into winter, we’ve had lots of rain. That in turn means leaks in the house, the worst of which has been in my oldest son’s bedroom. If you read last year’s post, you’ll recall we only renovated that room last year. 😡</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-12-01-year-3-at-the-smallholding/leak.webp" alt="Bedroom leak" /></p>
<p>If you’ve never had to put a new roof on your house, firstly you’re very lucky. But secondly, they’re <em>really</em> expensive. So that has meant we haven’t had any disposable income for other projects.</p>
<h2 id="making-progress-in-the-garden">Making progress in the garden</h2>
<p>We managed to plant some wildflowers in the far field, which one of our neighbours who keeps bees is very appreciative of.</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-12-01-year-3-at-the-smallholding/wild-flowers-in-field.webp" alt="Wild flowers in field" />
<em>Wild flowers starting to come through in field</em></p>
<p>We also had a load of groundwork done at the back to flatten some of it off. They ended up moving 50 tonnes of soil from the area to make it level. Our hope is that this summer we’ll be able to enjoy drinks and barbecues on the new flat piece of land.</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-12-01-year-3-at-the-smallholding/groundworks.webp" alt="Groundworks" /></p>
<p>We’re also continuing to improve the chicken coop. We now have a fairly large enclosed outdoor space for them, mainly because of bird flu in the and the council applying restrictions on where they can roam. We’ve also grown the flock to 17 hens and a rooster.</p>
<h2 id="looking-to-year-4">Looking to year 4</h2>
<p>The list for this year is going to be similar to last year’s. I just hope there will be fewer expensive surprises.</p>
<p>We’re hoping to add a polytunnel so we can grow more of our own vegetables. I’d also like to insulate the roof in the conservatory as it’ll effectively give us another room we can use all year round.</p>
<p>The bathrooms, toilet and kitchen will have to wait. We’ll just have to see how the cashflow looks. If next year I can get the polytunnel and conservatory done, I’ll class that as a win. I’d love to get a quad bike for towing <a href="https://kevquirk.com/blog/flailing-about-with-my-mower/">the flail</a> too, but again…money.</p>
<p>All in all it’s been a difficult year at the smallholding, but we have a new roof, so that’s good. I suppose…</p>
<div class="email-hidden">
<hr>
<p>Thanks for reading this post via RSS. RSS is great, and you're great for using it. ❤️</p>
<p>
<a href="mailto:72ja@qrk.one?subject=Year 3 at the Smallholding">Reply to this post by email</a>
</p>
</div>
Knowing when to leave - Werd I/O684a32f170b0d5000143b9362025-11-30T17:12:19.000Z<p><em>I wrote this in my blog drafts well over a decade ago (exact date unclear). I thought I'd publish it as-is as an aside. I'm sure it was going to be an excellent full post, but clearly this is just the intro. I still agree though: knowing when to go is important!</em></p><p>Some years ago, someone I worked with closely on a project that was failing had a mini-meltdown. He unfriended the rest of the team across social media, including the official accounts, which he took over unilaterally; he refused to even sit at the same table as the rest of the team on company trips, and at events.</p><p>To this day, I still don't exactly know what triggered the episode. But it was a moment that made me realize the project we were working on together could never succeed. Four months later, I had extracted myself.</p><p>Years later, I believe this was the wrong decision.</p><p>Knowing when to leave is an important skill. You've got to balance your loyalty, and your long-term career, against the objective facts in any situation. Leaving a project early is not a good idea: it sends a message that you're ready to flake out when times get tough. (In a startup situation, times <em>will</em> get tough.) However, if a project:</p><ul><li>genuinely has no long-term prospects, either for you personally or as a whole</li><li>and there's absolutely nothing you can do about it</li><li>and the situation is not directly of your own making</li></ul><p>You need to think about going. Anything else is running on a treadmill for no reason.</p>imperfect notes & my second subconscious - Werd I/O692c5320b96d5b0001117a5a2025-11-30T14:22:24.000Z<p>[<a href="https://winnielim.org/journal/imperfect-notes-my-second-subconscious/?ref=werd.io">Winnie Lim</a>]</p><p>I’ve never been a successful notetaker. <a href="https://winnielim.org/?ref=werd.io">Winnie Lim</a> enumerates the many reasons why not, which seem to be very close to her thinking too:</p><blockquote>“Because of my personality I tend to solve for the whole before wanting to do something. For years I wanted to figure out how I could retrieve the notes in a meaningful manner before I committed to making them. If I cannot remember I had made the note, did the note really exist?”</blockquote><p>The problem is that you end up trying to come up with a smart taxonomy of notes ahead of time — and that’s always bound to fail, at least for me. I’ve lost count of the number of times I’ve wiped my Obsidian vault clean because I didn’t like the structure or the maintenance of it all threatened to overtake any utility. Instead, as Winnie points out, the best thing to do is just <em>write the note</em>. It’s a bit like throwing the text into a big bucket, and that’s okay.</p><p>I hate to say it, but this might be a decent use case for some kind of personal LLM (ideally on-device so I’m not sharing my private notes with a third party I don’t trust). If you’re constantly just making notes without structure, being able to ask something about their content feels like it would have a lot of utility — again, at least for me. I’d love to be able to have my notes about a certain topic summarized when I need them. Or even have the summary proactively come up for me depending on my context.</p><p>Then again, maybe that doesn’t matter at all:</p><blockquote>“My brain is constantly holding scattered bits of information so it is just better to offload them somewhere in one place. I think the main difference is I don’t see obsidian as my second brain, I see it as my second subconscious.”</blockquote><p>I like that. Blogging is a little bit that for me, but blogging has an audience. There’s something useful in being the Harriet the Spy of your own life and putting words to things that otherwise might go unsaid. There’s poetry in it, too, which is very obvious from Winnie’s post.</p><p>I’ll give notetaking another try.</p><p>[<a href="https://winnielim.org/journal/imperfect-notes-my-second-subconscious/?ref=werd.io">Link</a>]</p>A big list of things I disable in WordPress - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=633442025-11-30T12:34:23.000Z<p>There are many things I like about the WordPress blogging software, and many things I find irritating. The most annoying aspect is that WordPress insists that its way is the best and there shall be no deviance. That means a <em>lot</em> of forced cruft being injected into my site. Headers that bloat my page size, Gutenberg stuff I've no use for, and <a href="https://developer.wordpress.org/reference/functions/capital_p_dangit/">ridiculous editorial decisions</a>.</p>
<p>To double-down on the annoyance, there's no simple way to turn them off. In part, that is due to the "<a href="https://wordpress.org/about/philosophy/">WordPress Philosophy</a>":</p>
<blockquote><p><strong>Decisions, not options</strong></p>
<p>[…] Every time you give a user an option, you are asking them to make a decision. When a user doesn’t care or understand the option this ultimately leads to frustration.</p></blockquote>
<p>I broadly agree with that. Having hundreds of options is a burden for users and a nightmare for maintainers. Do please read this <a href="https://tommcfarlin.com/wordpress-philosophy-decisions-not-options/">excellent discussion from Tom McFarlin for a more detailed analysis</a>.</p>
<p>But I <em>want</em> to turn things off. Luckily, there is a way. If you're a developer, you can remove a fair number of these "enforced" decisions. Add the following to your theme's <code>functions.php</code> file and watch the mandatory WordPress bloat whither away. I've commented each removal and, where possible, given a source for more information. Feel free to leave a comment suggesting how this script can be improved and simplified.</p>
<pre><code class="language-php">// Remove mandatory classic theme.
function disable_classic_theme_styles() {
wp_deregister_style( "classic-theme-styles" );
wp_dequeue_style( "classic-theme-styles" );
}
add_action( "wp_enqueue_scripts", "disable_classic_theme_styles" );
// Remove WP Emoji.
// http://www.denisbouquet.com/remove-wordpress-emoji-code/
remove_action( "wp_head", "print_emoji_detection_script", 7 );
remove_action( "wp_print_styles", "print_emoji_styles" );
remove_action( "admin_print_scripts", "print_emoji_detection_script" );
remove_action( "admin_print_styles", "print_emoji_styles" );
// https://wordpress.org/support/topic/remove-the-new-dns-prefetch-code/
add_filter( "emoji_svg_url", "__return_false" );
// Stop emoji replacement with images in RSS / Atom Feeds
// https://danq.me/2023/09/04/wordpress-stop-emoji-images/
remove_filter( "the_content_feed", "wp_staticize_emoji" );
remove_filter( "comment_text_rss", "wp_staticize_emoji" );
// Remove automatic formatting.
// https://css-tricks.com/snippets/wordpress/disable-automatic-formatting/
remove_filter( "the_content", "wptexturize" );
remove_filter( "the_excerpt", "wptexturize" );
remove_filter( "comment_text", "wptexturize" );
remove_filter( "the_title", "wptexturize" );
// More formatting crap.
add_action("init", function() {
remove_filter( "the_content", "convert_smilies", 20 );
foreach ( array( "the_content", "the_title", "wp_title", "document_title" ) as $filter ) {
remove_filter( $filter, "capital_P_dangit", 11 );
}
remove_filter( "comment_text", "capital_P_dangit", 31 ); // No idea why this is separate
remove_filter( "the_content", "do_blocks", 9 );
}, 11);
// Remove Gutenberg Styles.
// https://wordpress.org/support/topic/how-to-disable-inline-styling-style-idglobal-styles-inline-css/
remove_action( "wp_enqueue_scripts", "wp_enqueue_global_styles" );
// Remove Gutenberg editing widgets.
// From https://wordpress.org/plugins/classic-widgets/
// Disables the block editor from managing widgets in the Gutenberg plugin.
add_filter( "gutenberg_use_widgets_block_editor", "__return_false" );
// Disables the block editor from managing widgets.
add_filter( "use_widgets_block_editor", "__return_false" );
// Remove Gutenberg Block Library CSS from loading on the frontend.
// https://smartwp.com/remove-gutenberg-css/
function remove_wp_block_library_css() {
wp_dequeue_style( "wp-block-library" );
wp_dequeue_style( "wp-block-library-theme" );
wp_dequeue_style( "wp-components" );
}
add_action( "wp_enqueue_scripts", "remove_wp_block_library_css", 100 );
// Remove hovercards on comment links in admin area.
// https://wordpress.org/support/topic/how-to-disable-mshots-service/#post-12946617
add_filter( "akismet_enable_mshots", "__return_false" );
// Remove Unused Plugin code.
function remove_plugin_css_js() {
wp_dequeue_style( "image-sizes" );
}
add_action( "wp_enqueue_scripts", "remove_plugin_css_js", 100 );
// Remove WordPress forced image size
// https://core.trac.wordpress.org/ticket/62413#comment:40
add_filter( "wp_img_tag_add_auto_sizes", "__return_false" );
// Remove <img> enhancements
// https://developer.wordpress.org/reference/functions/wp_filter_content_tags/
remove_filter( "the_content", "wp_filter_content_tags", 12 );
// Stop rewriting http:// URls for the main domain.
// https://developer.wordpress.org/reference/hooks/wp_should_replace_insecure_home_url/
remove_filter( "the_content", "wp_replace_insecure_home_url", 10 );
// Remove the attachment stuff
// https://developer.wordpress.org/news/2024/01/building-dynamic-block-based-attachment-templates-in-themes/
remove_filter( "the_content", "prepend_attachment" );
// Remove the block filter
remove_filter( "the_content", "apply_block_hooks_to_content_from_post_object", 8 );
// Remove browser check from Admin dashboard.
// https://core.trac.wordpress.org/attachment/ticket/27626/disable-wp-check-browser-version.0.2.php
if ( !empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
add_filter( "pre_site_transient_browser_" . md5( $_SERVER["HTTP_USER_AGENT"] ), "__return_null" );
}
// Remove shortlink.
// https://stackoverflow.com/questions/42444063/disable-wordpress-short-links
remove_action( "wp_head", "wp_shortlink_wp_head" );
// Remove RSD.
// https://wpengineer.com/1438/wordpress-header/
remove_action( "wp_head", "rsd_link" );
// Remove extra feed links.
// https://developer.wordpress.org/reference/functions/feed_links/
add_filter( "feed_links_show_comments_feed", "__return_false" );
add_filter( "feed_links_show_posts_feed", "__return_false" );
// Remove api.w.org link.
// https://wordpress.stackexchange.com/questions/211467/remove-json-api-links-in-header-html
remove_action( "wp_head", "rest_output_link_wp_head" );
// https://wordpress.stackexchange.com/questions/211817/how-to-remove-rest-api-link-in-http-headers
// https://developer.wordpress.org/reference/functions/rest_output_link_header/
remove_action( "template_redirect", "rest_output_link_header", 11, 0 );
</code></pre>
<p>You can find the latest version of <a href="https://gitlab.com/edent/blog-theme/-/blob/master/includes/remove.php">my debloat script</a> in my theme's repo.</p>
<p>If there are other things you find helpful to remove, or a better way to organise this file, please drop a comment in the box.</p>
"Disagree and Let’s See" - Werd I/O692b9c6eb96d5b0001117a482025-11-30T01:22:54.000Z<p>[<a href="https://mollyg.substack.com/p/disagree-and-lets-see?ref=werd.io">Molly Graham</a>]</p><p>This feels emotionally honest and an idea I can get behind, as an alternative to the popular “disagree and commit”:</p><blockquote>““Disagree and let’s see” allows you to stay aligned with the team without forcing you to pretend you had conviction you didn’t have. It lets you walk into a room with your team and be honest:<br><br>“Here’s the path that was chosen. It wasn’t my first pick, but here’s the experiment we’re running, and here’s what we’re trying to learn.””</blockquote><p>Committing to something you disagree with is an emotional contortion that is hard to do in practice. But the work of every team is a series of experiments at its heart, and by changing the onus from “let’s commit to this thing we don’t all agree with” to “let’s try it and see what happens”, we move from steamrollering dissent to mutually agreeing on an experimental hypothesis and testing it. You’re learning based on agreed criteria.</p><p>That’s much harder to argue with — and at the end, there’s no “I told you so” or winners and losers. There’s just a “here’s what we learned” and an implied set of next steps. Bliss.</p><p>[<a href="https://mollyg.substack.com/p/disagree-and-lets-see?ref=werd.io">Link</a>]</p>The Optical Illusion of Prosperity - Werd I/O692b0ab0b96d5b0001117a052025-11-29T15:01:04.000Z<p>[<a href="https://www.yesigiveafig.com/p/part-1-my-life-is-a-lie?open=false&ref=werd.io#%C2%A7how-a-broken-benchmark-quietly-broke-america">Michael W. Green</a>]</p><p>Simplify Asset Management’s Chief Strategist and Portfolio Manager <a href="https://www.yesigiveafig.com/?ref=werd.io">Michael W Green</a> examined how the US poverty line is calculated, and discovered that it has been wildly miscalculated for years. In fact:</p><blockquote>“[…] if you measured income inadequacy today the way Orshansky measured it in 1963, the threshold for a family of four wouldn’t be $31,200.<br><br>It would be somewhere between $130,000 and $150,000.”</blockquote><p>The big differentiator is childcare, which in the US averages out at $32,773 a year, but it’s not the <em>only</em> differentiator. Our costs are enormous, and a poverty line at $31,200 only really helps legislators avoid having to provide (and therefore pay for) real support.</p><p>A family of four that genuinely earns $32,773 will receive all kinds of state help. A family that earns $80,000 does not. As Green points out, the difference largely comes from costs that went away during the pandemic:</p><blockquote>Childcare ($32k): Suspended. Kids were home.<br><br>Commuting ($15k): Suspended.<br><br>Work Lunches/Clothes ($5k): Suspended.</blockquote><p>Of course, many incomes <em>also</em> went away, depending on the jobs that were keeping these families afloat. Knowledge workers were relatively sitting pretty, while people who worked in retail, etc, were in trouble. There’s a lot more help that we can provide <em>everyone</em>. But this is one reason why I cannot stand return to office mandates, particularly when peoples’ salaries are under the $140,000 threshold that Green identifies. (Hint: outside high earning categories like big tech, it’s almost all of them, and inside those categories there are plenty of people who are earning lower.)</p><p>It sounds like Green is moving to show that 401(k)s and similar instruments are also a scam for most ordinary earners — something I tend to agree with. If society is an operating system that allows people to live well, start businesses, be healthy, etc, it’s failing us on every level. I say it’s time for an upgrade.</p><p>[<a href="https://www.yesigiveafig.com/p/part-1-my-life-is-a-lie?open=false&ref=werd.io#%C2%A7how-a-broken-benchmark-quietly-broke-america">Link</a>]</p>Saturday - James' Coffee Bloghttps://jamesg.blog/2025/11/29/saturday/2025-11-29T14:49:04.000Z
<p>Last week I ran a <a href="https://events.indieweb.org/2025/11/homebrew-website-club-writing-edition-zGoNxYZCrJhC" rel="noreferrer">meetup for writers on the web</a>. During the session, I brought up the topic of distance in writing. [1] We explored the topic in various ways – the distance between writing and publishing, the distance between reader and writer, how distance varies. As I write, I am thinking about the distance of poetic writing.</p><p>When I write poetically, I can use the ambiguity of poetic language to create a space for the reader to interpret something in whatever ways come to mind. I find it easier to write about the child-like joy of snow than to say that I had been missing the feeling of play I felt on that day for a while. But it also means I might not say something directly. That is not to say writing a personal blog post could substitute this writing. Poetic writing helps me say what I want to say.</p><p>What does this mean? That I want to write more personal stories? Right now, it is a thought, one that, previously only in my head, now exists in words. Through words I get closer to figuring out how I feel. It’s one of the reasons I love writing. It is also one of the reasons that writing can be so difficult. If I am not sure where to begin, the blank page feels particularly difficult. I might wish to have a formula for writing to come easier, but would the exercise be the same?</p><p>I intended for this blog post to be a bit of a narrative of my day. Sometimes I go to write one thing and end up in another direction. Thus is one of the joys of making things! With that said, I do still want to share the narrative of my day – this winter Saturday.</p><p>I had intended on going to a museum today and to pick up a library card but I had a bit of a headache in the morning. I might be getting a cold; I have what I would call the sniffles, but not to a great extent. Looking back I realise I sneezed a few times yesterday in such a way that makes me think I might be getting the cold. Nevertheless, I thought I’d stay inside and stay warm (although if you have ever spent time in a Scottish house in winter, you might know that it can still be a bit chilly indoors without the heating on!).</p><p>I spent the morning finishing Robin Sloan’s <em>Sourdough</em> book, which has been a delightful read. I enjoyed Sloan’s <em>Mr. Penumbra’s 24-Hour Bookshop</em>. Both titles were set in San Francisco, the former about the curious characteristics of a sourdough starter and the latter about a bookshop with more stories to tell than met the eye. I then continued reading another book, had some lunch, and <a href="https://www.youtube.com/watch?v=yyahes3tSFk">watched a vlog of someone going around Japan</a>. The vlogger mentioned a band called Yoasobi whose music I have been listening to since – Yoasobi’s music is great!</p><p>I plan to spend the rest of the day listening to music, reading, and maybe catching up on a few blogs!</p><p><em>[1]: V.H. Belvadi did a terrific </em><a href="https://vhbelvadi.com/indieweb-writing-edition" rel="noreferrer"><em>write-up on the writing event too</em></a><em>. </em></p>
Little moments of joy - James' Coffee Bloghttps://jamesg.blog/2025/11/29/little-moments-of-joy/2025-11-29T13:21:12.000Z
<p>I was thinking about a design for a web page yesterday that I wanted to build with a few illustrations. I remembered that there was a site with old-timey illustrations that were available in the public domain. I thought <em>I have no idea how to find this website!</em></p><p>Today,, I was both surprised and delighted that the search query “old timey illustrations” allowed me to find the site, which I now know is called <a href="https://www.oldbookillustrations.com/">Old Book Illustrations</a>. It is amazing that I can type in a query like “old timey illustrations” into a web search engine and find the page for which I am looking. The speed with which I found the website for which I was looking – and how a search engine found the page with my description "old timey" – brought a smile to my face.</p><p><em>Addendum: It looks like not all illustrations are public domain according to the </em><a href="https://www.oldbookillustrations.com/terms-of-use/" rel="noreferrer"><em>Terms of Use</em></a><em>, but this doesn't detract from the story :)</em></p>
Experimenting with web design - James' Coffee Bloghttps://jamesg.blog/2025/11/29/experimenting-with-web-design/2025-11-29T11:25:27.000Z
<p>Every so often I open developer tools in the web browser and start tinkering with my website. I love experimenting with different design directions, not necessarily in pursuit of a complete design so much as fuelled by a sense of curiosity. <em>What would it look like if I made my web pages wider? What would I do with the extra room? Would this help me create grids of images that would be easier to peruse? What would it look like if my accent colour was on the left side of a page?</em></p><p>I was recently playing around with using more of the space available on desktops to create wider content columns. I experimented with this both in the context of blog posts and then, later, an overall layout that I could use as a template for other pages on my website.</p><p>While the final results are not anywhere on the web, I was proud of what I made so I decided to take the styles from my browser and put them into a local stylesheet. I started developing a layout. I then took a few screenshots of the results as documentation for the project, which I have listed below. Perhaps you will find them interesting!</p><h2 id="a-blog-post-layout-with-an-image">A blog post layout with an image</h2><p>In this concept, I simplified my navigation bar, moved it to the top of the page, and created a new card component that appears on the far left with metadata about a blog post. I also moved my accent stripe from the top of the web page to the left side.</p><img alt="" class="kg-image" loading="lazy" sizes="(min-width: 720px) 720px" src="https://editor.jamesg.blog/content/images/2025/11/content.png" srcset="https://editor.jamesg.blog/content/images/size/w600/2025/11/content.png 600w, https://editor.jamesg.blog/content/images/size/w1000/2025/11/content.png 1000w, https://editor.jamesg.blog/content/images/size/w1600/2025/11/content.png 1600w, https://editor.jamesg.blog/content/images/size/w2400/2025/11/content.png 2400w"/><h2 id="a-blog-post-with-a-wider-image">A blog post with a wider image</h2><p>I then replaced the heading with a serif font which I thought would fit the page better. I also made the image on the page bleed out both the left and right sides of the main content container, and made the text a bit wider, too.</p><img alt="" class="kg-image" loading="lazy" sizes="(min-width: 720px) 720px" src="https://editor.jamesg.blog/content/images/2025/11/Screenshot-2025-11-17-at-20.54.49.png" srcset="https://editor.jamesg.blog/content/images/size/w600/2025/11/Screenshot-2025-11-17-at-20.54.49.png 600w, https://editor.jamesg.blog/content/images/size/w1000/2025/11/Screenshot-2025-11-17-at-20.54.49.png 1000w, https://editor.jamesg.blog/content/images/size/w1600/2025/11/Screenshot-2025-11-17-at-20.54.49.png 1600w, https://editor.jamesg.blog/content/images/size/w2400/2025/11/Screenshot-2025-11-17-at-20.54.49.png 2400w"/><h2 id="a-page-for-hats">A page for hats</h2><p>I was enjoying the direction I was exploring, so I decided to see what the design would look like on a page that was not for blog posts. I experimented with my <a href="https://editor.jamesg.blog/hats" rel="noreferrer">hats page</a>, creating a four-column grid that escaped the main content container in a similar fashion to the full-bleed image I tried in the blog post.</p><img alt="" class="kg-image" loading="lazy" sizes="(min-width: 720px) 720px" src="https://editor.jamesg.blog/content/images/2025/11/hats.png" srcset="https://editor.jamesg.blog/content/images/size/w600/2025/11/hats.png 600w, https://editor.jamesg.blog/content/images/size/w1000/2025/11/hats.png 1000w, https://editor.jamesg.blog/content/images/size/w1600/2025/11/hats.png 1600w, https://editor.jamesg.blog/content/images/2025/11/hats.png 2186w"/><h2 id="an-about-page">An about page</h2><p>I liked the single-column content layout and also the floating container I had for tags on my blog post layout. I decided to experiment with a layout that had a single column for content and a column on the right side of the page for images. I experimented with this on an about page.</p><img alt="" class="kg-image" loading="lazy" sizes="(min-width: 720px) 720px" src="https://editor.jamesg.blog/content/images/2025/11/about.png" srcset="https://editor.jamesg.blog/content/images/size/w600/2025/11/about.png 600w, https://editor.jamesg.blog/content/images/size/w1000/2025/11/about.png 1000w, https://editor.jamesg.blog/content/images/size/w1600/2025/11/about.png 1600w, https://editor.jamesg.blog/content/images/2025/11/about.png 2198w"/><h2 id="playing">Playing</h2><p>I was going to start this paragraph with "I spent many more hours tinkering" but, in truth, I'm not sure how long I spent playing around. Indeed, I wasn't keeping track. Whether an hour or several passed, it didn't matter for I was having so much fun with the new direction.</p><p>I thought about turning my template into a new layout and design for my website, but I then realised that it would be non-trivial to transfer my new design concepts to my existing website. I had made changes to the HTML, and wanted to change some of my pages to have new elements like the image grid on the about page. It would have been a lot of work to make everything work. And I wanted to play!</p><p>With that said, I did copy what I wrote for the about page I designed onto this website. So now <a href="https://jamesg.blog/about" rel="noreferrer">I have an About page</a>!</p>
Announcing Validate Everything - James' Coffee Bloghttps://jamesg.blog/2025/11/28/validate-everything/2025-11-28T21:09:32.000Z
<p>Today the <a href="https://events.indieweb.org/2025/11/indieweb-black-friday-create-day-build-don-t-buy-C044CcYllKyt" rel="noreferrer">IndieWeb community hosted a Create Day even</a>t in which participants were invited to make something on the web. The theme of the event was “Build Don't Buy”, an invitation to make something this Black Friday.</p><p>While I wasn’t sure what I wanted to make, I knew I wanted to make <em>something</em>. I consulted my list of ideas and one stood out: a website that lets you type in a URL and creates links to various helpful validators. This project was inspired by Adactio’s “<a href="https://adactio.com/journal/20965">Bookmarklets for testing your website</a>” project.</p><p>During the event, I made a new page on my website called “<a href="https://jamesg.blog/validate-everything">Validate Everything</a>”. The web page lets you paste in a URL and automatically generates links to validators like the WAVE accessibility validator, the Google Rich Results Test tool, microformats testers, and more. I added tools I have used in the past to validate or understand the markup behind web pages.</p><p>I use a several validators but I typically end up typing the name of the validator into a search engine and then reading through the results to find the one I want. With this project, I wanted to have a single page with many common validators.</p><p>You can <a href="https://jamesg.blog/validate-everything" rel="noreferrer">try out the page today</a>, and <a href="https://github.com/capjamesg/validate-everything">see the source code on GitHub</a>. Contributions are most welcome!</p><h2 id="how-the-page-works"><strong>How the page works</strong></h2><p>As part of the project, I wanted to experiment with CSS. I asked myself: could I show/hide the validator links depending on whether there was a valid URL in the form field on the web page? After some thought, reading, experimentation, and discussion with fellow participants in the Create Day event, I learned that you can select a form field if it its contents are valid or invalid using the <code>:valid</code> and <code>:invalid</code> selectors. I could then combine these with the <code>:has</code> and <code>:not</code> selectors to manipulate content on the page depending on whether the user has typed in a valid URL in the form field on the page.</p><p>I came up with the following selector to show/hide the validator links not he page depending on if there is a valid URL in the page form field:</p><div class="highlight"><pre><span></span><span class="nt">body</span><span class="p">:</span><span class="nd">not</span><span class="o">(</span><span class="p">:</span><span class="nd">has</span><span class="o">(</span><span class="p">#</span><span class="nn">url</span><span class="p">:</span><span class="nd">valid</span><span class="o">))</span><span class="w"> </span><span class="p">#</span><span class="nn">items</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">none</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>This selector will select the <code>#items</code> element if the page <code>body</code> does not have an element with the ID <code>#url</code> that is valid. If the condition is met, the <code>#items</code> element is hidden. The <code>#items</code> element contains the list of validators.</p><p>For <code>:valid</code> to work, I needed to specify a few validation rules in my HTML input. I wrote the following input tag:</p><div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"url"</span> <span class="na">id</span><span class="o">=</span><span class="s">"url"</span> <span class="na">name</span><span class="o">=</span><span class="s">"url"</span> <span class="na">placeholder</span><span class="o">=</span><span class="s">"https://example.com"</span> <span class="na">pattern</span><span class="o">=</span><span class="s">"https://.*\.[a-zA-Z]{3,}.*"</span> <span class="na">required</span> <span class="na">autocomplete</span><span class="o">=</span><span class="s">"off"</span> <span class="p">/></span>
</pre></div>
<p>This input says that the URL field must have a URL that:</p><ol><li>Starts with <code>https://</code>, and;</li><li>Is followed by any string of characters, which is then;</li><li>Followed by a <code>.</code> (dot), which is then;</li><li>Followed by two or more letters.</li></ol><p>For example, <code>https://jamesg.blog</code> would be valid, and as a result the validator URL list would appear. But <code>https://jamesg.b</code> would not be valid (only one character follows the <code>.</code>), so the validator list would not appear. An input is also required by specifying the HTML <code>required</code> boolean. This means that the validator URLs will not appear when the field is blank.</p><p><em>NB: This regex is not testing for whether a user’s TLD is valid. This could be done with a more complex regex, but this is something I can always add later. What I have right now is good enough!</em></p><p>When a URL is typed in, JavaScript makes two changes to the page:</p><ol><li>A <code>?url=…</code> parameter is added so that the user has a link they can bookmark/share, and;</li><li>All the validator links are updated to add the text the user has typed in.</li></ol><p>This means that when the user has finished typing – or has pasted in – their URL, all the links on the page will directly link to different validator websites.</p><h2 id="building-things-is-fun"><strong>Building things is fun</strong></h2><p>I had a lot of fun making this web page! I learned about the <code>:valid</code> CSS rule. I got to play around with the <code>:has</code> selector more. I experimented with page spacing and typography. And I also learned that you can’t click on a <code>view-source:</code> link directly on a page in Firefox for security reasons. Indeed, with everything I build, I always learn something new.</p><p>I have been in a bit of a creative rut lately in terms of making new things. I am learning about so many new things, but I’m not ready to translate much of what I am learning into a project. I am eager to make things that push me outside my comfort zone and let me use my new skills, but I haven’t been quite sure where to begin. I read somewhere that sometimes the hardest step is getting started. Today, I was able to get started and create a page that I think is fun and interesting, useful, and based on an idea I had several months ago. I’m proud of the page I made today. I love making things.</p><h2 id="addendum:-how-it-started">Addendum: How it started</h2><p>I like taking screenshots of my projects as I build them, especially if I'm experimenting with different designs.</p><p>I had a tab open from my first version of this project I made early in the meetup. I took the following screenshot:</p><img alt="" class="kg-image" loading="lazy" src="https://editor.jamesg.blog/content/images/2025/11/list.png"/><p>When <a href="https://jamesg.blog/validate-everything" rel="noreferrer">compared with the live page</a>, you can see the above list has the essence of what I wanted: a URL bar, and a list of links. I then added subheadings to separate the validators into categories, realising there were many I wanted to add. I added some styles (and dark mode!) and worked on the JavaScript to make the page work.</p><p>Which is to say: all projects start somewhere, and are built one step at a time!</p>
Deploy on push with Forgejo and Coolify - Posts feedhttps://www.coryd.dev/posts/2025/deploy-on-push-with-forgejo-and-coolify2025-11-28T19:48:00.000Z<p>All of my projects are now stored on my <a href="https://forgejo.org">Forgejo</a> instance rather than GitHub as the latter continues to speed run the enshittification curve. I've implemented a manual deploy button in my site's admin but for other, lighter-weight projects, I prefer to deploy changes whenever I push them up.</p>
<p><a href="https://coolify.io">Coolify</a> has a native integration with GitHub, but not with other git hosts. For deploys, I've worked around this by cloning my projects using an API key within the Dockerfile for the project as a sort of lightweight continuous integration.</p>
<p>Within each <a href="https://coolify.io">Coolify</a> resource you'll find a webhooks section. There's a unique deploy webhook for each resource that accepts <code>POST</code> requests to trigger a new deployment. Rather than use a <a href="https://forgejo.org">Forgejo</a> action, I'm having the repositories that I want to deploy on push send a webhook event to the appropriate <a href="https://coolify.io">Coolify</a> deploy URL. You can do this in the <a href="https://forgejo.org">Forgejo</a> repository's webhooks settings by adding a webhook, selecting a target (I simply selected <a href="https://forgejo.org">Forgejo</a>, the first option in the list), adding the <a href="https://coolify.io">Coolify</a> URL, changing the <code>force</code> parameter value at the end of the URL from <code>false</code> to <code>true</code> to ensure the build is performed without using the cache and add a <code>Bearer <COOLIFY-DEPLOY-TOKEN></code>. The Coolify API token only needs <code>deploy</code> permissions.</p>
<p>Now, when I push to Forgejo, it sends a <code>POST</code> request to the Coolify webhook and deploys a fresh build of the project.</p>
<img src="https://stats.coryd.dev/count?p=/posts/2025/deploy-on-push-with-forgejo-and-coolify&t=Deploy+on+push+with+Forgejo+and+Coolify&r=rss" style="position:absolute;left:-9999px;">My Wikipedia account is now old enough to vote - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=590512025-11-28T12:34:17.000Z<p>I have no idea what I was doing on the 28th of November 2007 but, apparently, <a href="https://en.wikipedia.org/w/index.php?title=Special:Log&logid=12237532">that's when I first logged in to Wikipedia</a>. Which means, as of right now, my Wikipedia account is 18 years old!</p>
<p>I didn't make <a href="https://en.wikipedia.org/w/index.php?title=Ada_Lovelace&diff=prev&oldid=281477201">my first edit</a> until April 2009. That was for the nascent <a href="https://shkspr.mobi/blog/2009/03/ada-lovelace-day/">Ada Lovelace Day</a>.</p>
<p>Since then, I've racked up a bit <a href="https://xtools.wmcloud.org/globalcontribs/Edent">over 600 edits</a> which simultaneously feels like a lot and barely anything.</p>
<p>Every edit gives you a crude representation of how many characters you've deleted or added. If I've done my sums right, I've added about 86k letters to Wikipedia and deleted about 25k. So a net addition of 61K characters.</p>
<p>That feels like a worthwhile contribution to the commons.</p>
How I Replaced My Son’s PC With an £88 iMac - Kev Quirkhttps://kevquirk.com/blog/how-i-replaced-my-son-s-pc-with-an-88-imac/2025-11-28T07:51:00.000Z
<p style="font-size: 1.2em;">I recently replaced my son's broken PC with a 2015 iMac from eBay. Here's how it went...</p>
<p>A year or so ago, my wife and I gave our oldest son a spare computer we had lying around. This was mainly for homework, but also for some light gaming, like Minecraft and Super Tux Cart. The machine was actually <a href="https://kevquirk.com/blog/my-home-server-2-months-on/">the little home server I built</a> a few years ago.</p>
<p>At 6 years old the motherboard decided to give up the ghost and blew. After talking to him about it, we decided to look for something a bit smaller and cool looking. Originally I was going to go with one of those <a href="https://thepihut.com/products/raspberry-pi-500-plus-desktop-kit">Raspberry Pi keyboards</a>, but they’re £200 and would likely struggle playing Minecraft.</p>
<p>Then I read about someone loading Linux onto an old Mac with great success (I can’t remember who it was, so can’t provide a link I’m afraid). Anyway, I’ve always <em>loved</em> the look of the old iMacs, so decided to have a peek on eBay for one.</p>
<p>I ended up finding a late-2015 21.5” iMac with 8GB RAM, a 1TB HDD, and a 4th Gen Core i5 processor <strong>for £88, delivered!</strong></p>
<p>For that price I wasn’t expecting much. But it looked clean in the pictures, so I decided to take a punt.</p>
<h2 id="getting-the-imac">Getting the iMac</h2>
<p>While waiting for it to arrive, I ordered CPU paste, adhesive strips for resealing the screen, and a RAM upgrade before realising the RAM is soldered on these models, so that was a bust. I also replace the tired old HDD with the 512GB SSD from my son’s previous machine.</p>
<p>I opened it up, replaced the paste, installed the SSD, and sealed it back up. The SSD already had Ubuntu Mate 22.04 on it with all his files and Minecraft maps, so I booted it and let the drivers sort themselves out.</p>
<p>A quick <code class="language-plaintext highlighter-rouge">sudo apt update && apt upgrade -y</code> later and everything worked perfectly. I also bumped it to 24.04 while I was there.</p>
<p>Here’s how the new machine looks on his little desk in his bedroom:</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-11-28-how-i-replaced-my-sons-pc-with-an-88-imac/kids-imac.webp" alt="Kid's iMac" /></p>
<p>Pretty cool, I think you’ll agree.</p>
<h2 id="performance">Performance</h2>
<p>Oh lordy, this thing is <em>quick</em>! It boots up in a few seconds, every single app opens pretty much instantly, and he gets a stable 60+ FPS on Minecraft. He’s <em>thrilled</em> with it, and so are we. So much so that we will probably do the same thing again next year when we look to get a machine for our youngest.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>If you’re on the hunt for a new device, I’d seriously consider looking at an Intel Mac with Linux. They’re blazing fast, they look great, and are <em>amazing</em> value.</p>
<p>The only downside is that this window is closing. Apple’s move to the M-series chips means Linux support will be tougher over time. Projects like <a href="https://asahilinux.org/">Asahi Linux</a> exist, but support varies and the long-term outlook is unclear.</p>
<p>For now though, our oldest son has a solid machine that should last him for a few years. If it dies again, I’ll probably look at a <a href="https://frame.work/gb/en/laptop12">Framework 12</a>, but hopefully it won’t come to that.</p>
<p>What do you think about running Linux on older hardware? Would you try something like this? Leave a comment or drop me an email and let me know.</p>
<div class="email-hidden">
<hr>
<p>Thanks for reading this post via RSS. RSS is great, and you're great for using it. ❤️</p>
<p>
<a href="mailto:72ja@qrk.one?subject=How I Replaced My Son’s PC With an £88 iMac">Reply to this post by email</a>
</p>
</div>
The Idiot Sandwich - On Embedding Alt Text - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=637902025-11-27T12:34:27.000Z<p>Alt text is <em>great</em>. It allows people who can't see an image to understand what that image represents.</p>
<p>For example, the code might say: <code><img src="whatever.gif" alt="Two cute kittens are playing on a blanket"></code></p>
<p>If you are blind, you get an idea of what's being conveyed by that image. If you're on a train and the WiFi craps out just before the image loads, you'll <em>also</em> benefit! If the image is of text in a language you don't read, your device can translate it for you.</p>
<p>The alt text can be as long or as short as is necessary. It might just be "kid giving a thumbs up" or it could be incredibly detailed. Here's how the BBC's Newsbeat typically adds alt text for younger viewers:</p>
<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/10/bbc-alt-lossy.webp" alt="Screenshot showing broken images. The alt text on them reads "October 02, 2023, Kolkata City, India,: An Indian hairdresser finishes the haircut showing a Cricket World Cup design make at a hair salon near Kolkata on 2 October 2023 in Kolkata". Another says "Doja Cat attends the 2023 Video Music Awards. The singer has short bleached blonde hair and dark brown eyes. Her makeup includes thinly drawn on eye brows, purple eyeshadow, false spidery lashes and gems dotted around her eyes. She wears a spider shaped ear cuff and long dangly silver earrings." A third says "Olivia Rodrigo in the Live Lounge. Olivia is a 20-year-old woman with long brown hair worn loose over her shoulders. She wears a white silk slip-style dress with a lace trim and has red lipstick on. She holds a microphone stand with both hands and closes her eyes as she sings." " width="540" height="551" class="aligncenter size-full wp-image-63798"/>
<p>Is that too much? Maybe. It depends on your audience. For partially sighted kids who crave the same pop information as their sighted peers, I think it is great.</p>
<p>So alt text is a good thing. But people are lazy and don't always write it. Perhaps the answer is to <a href="https://shkspr.mobi/blog/2023/07/should-you-embed-alt-text-inside-image-metadata/">embed alt text inside image metadata</a>?</p>
<p>It's a lovely idea - and technically feasible - but it fails to account for user needs.</p>
<p>And that brings me to the point of this post. Who is your alt text for? What information are you trying to share?</p>
<p>Here's a good example. I looked at a bunch of popular memes which had alt-text pre-populated in them. Here's what they said:</p>
<div class="activitypub-embed u-in-reply-to h-cite"> <div class="activitypub-embed-header p-author h-card"> <img class="u-photo" src="https://files.mastodon.social/accounts/avatars/000/007/112/original/388649acb2026701.webp" alt=""/> <div class="activitypub-embed-header-text"> <h2 class="p-name" id="terence-eden"><a href="https://shkspr.mobi/blog/2025/11/the-idiot-sandwich-on-embedding-alt-text/#terence-eden">Terence Eden</a></h2> <a href="https://mastodon.social/users/Edent" class="ap-account u-url">@Edent@mastodon.social</a> </div> </div> <div class="activitypub-embed-content"> <div class="ap-subtitle p-summary e-content"><p>Whenever people talk about embedding alt text into images, I remember that lots of gif search services already try to do that.</p><p>Here's BlueSky's gif service. I searched for some popular memes. Each had alt-text baked in.</p><p>Take a look and tell me if you think that the embedded text conveys the sentiment of the image? If you couldn't see the animation, would you understand what was going on from that alt?</p></div> <div class="ap-preview layout-4"> <img class="u-photo u-featured" src="https://files.mastodon.social/media_attachments/files/115/165/042/975/730/482/original/1e7cc65db6887d11.png" alt="The idiot sandwich meme. The default alt text is "a man is holding a piece of bread over a woman 's face and asking what are you ?""/> <img class="u-photo u-featured" src="https://files.mastodon.social/media_attachments/files/115/165/042/976/199/735/original/dd22dab9aa5a0fb1.png" alt="Clip from The Hobbit with the subtitle "What about second breakfast?". The default alt text is "two men are standing next to each other talking about second breakfast"."/> <img class="u-photo u-featured" src="https://files.mastodon.social/media_attachments/files/115/165/042/985/649/025/original/91e0e747e8e4da5a.png" alt="The meme of Homer Simpson walking backwards into a hedge. The default alt text is "A cartoon of homer simpson standing in a grassy area.""/> <img class="u-photo u-featured" src="https://files.mastodon.social/media_attachments/files/115/165/042/993/873/973/original/f97c27accad5c0f9.png" alt="The Chuckle Brothers looking at each other. The default alt text is "a man in a striped shirt is kissing another man in a white suit"."/> </div> </div> <div class="activitypub-embed-meta"> <a href="https://mastodon.social/users/Edent/statuses/115165068315048568" class="ap-stat ap-date dt-published u-in-reply-to">2025-09-07, 21:11</a> <span class="ap-stat"> <strong>12</strong> boosts </span> <span class="ap-stat"> <strong>22</strong> favorites </span> </div> </div>
<style>/** * ActivityPub embed styles. */ .activitypub-embed { background: #fff; border: 1px solid #e6e6e6; border-radius: 12px; padding: 0; max-width: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } .activitypub-reply-block .activitypub-embed { margin: 1em 0; } .activitypub-embed-header { padding: 15px; display: flex; align-items: center; gap: 10px; } .activitypub-embed-header img { width: 48px; height: 48px; border-radius: 50%; } .activitypub-embed-header-text { flex-grow: 1; } .activitypub-embed-header-text h2 { color: #000; font-size: 15px; font-weight: 600; margin: 0; padding: 0; } .activitypub-embed-header-text .ap-account { color: #687684; font-size: 14px; text-decoration: none; } .activitypub-embed-content { padding: 0 15px 15px; } .activitypub-embed-content .ap-title { font-size: 23px; font-weight: 600; margin: 0 0 10px; padding: 0; color: #000; } .activitypub-embed-content .ap-subtitle { font-size: 15px; color: #000; margin: 0 0 15px; } .activitypub-embed-content .ap-preview { border: 1px solid #e6e6e6; border-radius: 8px; overflow: hidden; } .activitypub-embed-content .ap-preview img { width: 100%; height: auto; display: block; } .activitypub-embed-content .ap-preview { border-radius: 8px; box-sizing: border-box; display: grid; gap: 2px; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; margin: 1em 0 0; min-height: 64px; overflow: hidden; position: relative; width: 100%; } .activitypub-embed-content .ap-preview.layout-1 { grid-template-columns: 1fr; grid-template-rows: 1fr; } .activitypub-embed-content .ap-preview.layout-2 { aspect-ratio: auto; grid-template-rows: 1fr; height: auto; } .activitypub-embed-content .ap-preview.layout-3 > img:first-child { grid-row: span 2; } .activitypub-embed-content .ap-preview img { border: 0; box-sizing: border-box; display: inline-block; height: 100%; object-fit: cover; overflow: hidden; position: relative; width: 100%; } .activitypub-embed-content .ap-preview video, .activitypub-embed-content .ap-preview audio { max-width: 100%; display: block; grid-column: 1 / span 2; } .activitypub-embed-content .ap-preview audio { width: 100%; } .activitypub-embed-content .ap-preview-text { padding: 15px; } .activitypub-embed-meta { padding: 15px; border-top: 1px solid #e6e6e6; color: #687684; font-size: 13px; display: flex; gap: 15px; } .activitypub-embed-meta .ap-stat { display: flex; align-items: center; gap: 5px; } @media only screen and (max-width: 399px) { .activitypub-embed-meta span.ap-stat { display: none !important; } } .activitypub-embed-meta a.ap-stat { color: inherit; text-decoration: none; } .activitypub-embed-meta strong { font-weight: 600; color: #000; } .activitypub-embed-meta .ap-stat-label { color: #687684; } </style>
<p>OK, so sometimes the captioner makes a mistake and thinks <a href="https://tenor.com/en-GB/view/chuckle-vision-chuckle-brothers-paul-chuckle-paul-elliot-barry-elliot-gif-16410194">the Chuckle Brothers are kissing</a> (WTF?!) perhaps we can excuse that as being an obscure image. But the "<a href="https://tenor.com/en-GB/view/gordon-ramsay-idiot-sandwich-angry-mad-what-are-you-gif-4169547">idiot sandwich</a>" one is inexcusable. It's a popular meme with a specific meaning.</p>
<p>Which leaves me with a few questions for you:</p>
<ul>
<li>If you saw that the image you were sharing had crap alt text - would you bother editing it?</li>
<li>Is bad alt text worse than no alt text?</li>
<li>Can the same image have multiple meanings?</li>
<li>Have you spent any time browsing the web with images turned off? Did you enjoy it?</li>
</ul>
<p><a href="https://www.rnib.org.uk/living-with-sight-loss/assistive-aids-and-technology/tv-audio-and-gaming/guide-to-accessible-social-media/">You can find out more about Alt Text on the RNIB site</a>.</p>
Local vs Cloud - Kev Quirkhttps://kevquirk.com/blog/local-vs-cloud/2025-11-27T11:33:00.000Z
<p style="font-size: 1.2em;">I was listening to the Waveform podcast on my way to work this morning and they were talking about cloud vs local computing, and I have thoughts...</p>
<p>I was listening to the <a href="https://podcasts.voxmedia.com/show/waveform-the-mkbhd-podcast">Waveform podcast</a> on my commute this morning when they started talking about cloud vs local computing. The discussion quickly drifted into hypotheticals about unlimited storage and choosing one world or the other.</p>
<p>But the whole debate felt off to me, because it rests on a bad assumption: that “cloud” and “local” are two totally separate things.</p>
<h2 id="defining-the-cloud">Defining “the cloud”</h2>
<p>Before we can argue about cloud vs local, we need to be clear about what we’re comparing.</p>
<p>People talk about the cloud like it’s some mystical ether, but as the saying goes, it’s really just someone else’s computer. If it’s a machine you don’t own, sitting in a datacentre somewhere, it’s cloud.</p>
<p>By contrast, local doesn’t just mean “<em>the laptop you’re holding</em>”. It includes anything you own and control: your PC, a server in a cupboard, or a NAS on your home network.</p>
<p>Once you see it this way, the Waveform question becomes more interesting. Because I think local can include your own private cloud.</p>
<h2 id="my-approach">My approach</h2>
<p>At home I use a <a href="https://kevquirk.com/blog/synology-vs-nextcloud-which-is-better-for-a-home-server/">Synology NAS</a> as the centre of my own little ecosystem. It runs all the services I rely on daily, but with the convenience you’d usually expect from big cloud providers. A few examples:</p>
<ul>
<li>Plex for media</li>
<li>Synology Photos for backing up images from my phone</li>
<li>Calendar and Contacts, all synced via DAV</li>
<li>Synology Drive for documents</li>
<li><a href="https://kevquirk.com/blog/more-journal-app-updates/">My journal app</a></li>
<li>A notes app I use for Fediverse posts, so I have local copies of everything I post</li>
</ul>
<p>Everything lives on hardware I control, but it’s still available wherever I am.</p>
<p>Backups are handled locally (to a USB drive connected to the Synology) and off-site (to Backblaze B2, encrypted before upload). The result is a system that behaves like a cloud service, but where I hold the keys.</p>
<p>Here’s my <em>extremely</em> high-quality architectural diagram:</p>
<p><img src="https://kevquirk.com/assets/images/blog/2025-11-27-local-vs-cloud/lan.webp" alt="My LAN diagram" />
<em>No, I never studied art.</em></p>
<h2 id="what-about-security">What about security?</h2>
<p>I’m not going to get into specifics for obvious reasons, but the short version is that my Synology isn’t exposed to the Internet at all. My router only accepts traffic from specific networks, so I connect over VPN. It’s always-on for me and my wife, so the experience is completely transparent.</p>
<p class="warning">If you’re thinking of building something like this, I’d <em>strongly</em> recommend not exposing any part of your home network directly to the Internet.</p>
<h2 id="so-cloud-or-local">So… cloud or local?</h2>
<p>Back to the original question. The unlimited storage bit doesn’t matter; you only need enough storage, not infinite.</p>
<p>Given the choice between 100% cloud or 100% local, I’d choose local every time. Not because I want to avoid cloud-like features, but because local gives me the same benefits without giving away control. My photos sync automatically, I can share links to files, edit documents anywhere, and my data is <a href="https://kevquirk.com/blog/how-to-backup-a-synology-to-backblaze-b2/">backed up properly</a>.</p>
<p>The truth is that the whole premise of cloud vs local is a false choice. You don’t have to pick one at the expense of the other.</p>
<p>You can have the convenience of the cloud running entirely on hardware you own. The real choice isn’t cloud or local, it’s whose cloud you want to rely on.</p>
<p>What do you think? Do you lean toward cloud, local, or something in between? Feel free to leave a comment or drop me an email, I’d love to hear how you approach it.</p>
<div class="email-hidden">
<hr>
<p>Thanks for reading this post via RSS. RSS is great, and you're great for using it. ❤️</p>
<p>
<a href="mailto:72ja@qrk.one?subject=Local vs Cloud">Reply to this post by email</a>
</p>
</div>
22.00.0170 Decimal Diary: TGIF and vintage annual reports - Johnny.Decimalhttps://johnnydecimal.com/22.00.0170/2025-11-26T09:20:33.000Z<h1 id="decimal-diary-tgif-and-vintage-annual-reports">Decimal Diary: TGIF and vintage annual reports</h1>
<p>Dear Decimal Diary,</p>
<p>Last week I did a little TGIF – <a href="https://jdcm.al/22.00.0097">Thank God I Filed it</a> – time. And when I was nosing around I found a fun folder I'd forgotten about in <code>43 Images</code> in our <a href="https://jdcm.al/15.04">Small Business System</a>.</p>
<p>TGIF is Johnny's idea about spending some time on Friday afternoons filing things properly and neatening your system. When your brain is too tired for other stuff.<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup> It's the kind of thing I put off, and then when I start I can't stop. Now I know about how files <a href="https://johnnydecimal.com/10-19-concepts/11-core/11.06-saving-files/#sorting-by-year-month">sort beautifully using the date</a>, I get a small thrill from tidying up messy filenames.<sup><a href="#user-content-fn-2" id="user-content-fnref-2" data-footnote-ref="" aria-describedby="footnote-label">2</a></sup></p>
<p>The ID I neatened last week was <code>43.21 Vintage annual reports</code>. It has some PDFs I'd downloaded for inspiration at some point. The filenames were haphazard and as-downloaded. And since they're annual reports, putting the year out the front makes sense to me. Now they're all lined up and tidy. It's a small win, but satisfying. And I'll remember I have these in the chamber.</p>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="A nicely sorted ID" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.43.21-vintageannualreports--cx-844x859.png" width="844" height="859"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170A. A nicely sorted ID.</figcaption> </picture>
<h3 id="some-reports">Some reports</h3>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Bureau of Economic Geology" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-bureauofeconomicgeology--cx-632x821.png" width="632" height="821"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170B. The Bureau of Economic Geology.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Ford Foundation" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-fordfoundation--cx-738x953.png" width="738" height="953"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170C. The Ford Foundation.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Deep East Texas MHMR" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-deepeasttexasmhmr-cx-635x811.png" width="635" height="811"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170D. The Deep East Texas MHMR.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="A page with a financial report" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-deepeasttexasmhmr-finances--cx-635x811.png" width="635" height="811"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170E. A page with a financial report.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Philippine Wood Products Association" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-philippinewoodproductsassociation--cx-635x821.png" width="635" height="821"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170F. The Philippine Wood Products Association.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Jet Propulsion Laboratory" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-jetpropulsionlaboratory-cover--cx-619x806.png" width="619" height="806"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170G. The Jet Propulsion Laboratory.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="A page with a photo by the Voyager probe" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1980-jetpropulsionlaboratory--cx-628x825.png" width="628" height="825"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170H. A page with a photo by the Voyager probe.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Electricity Supply Board" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1981-electricitysupplyboard--737x1067.jpeg" width="737" height="1067"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170I. The Electricity Supply Board.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Jet Propulsion Laboratory" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1981-jetpropulsionlaboratory--cx-617x805.png" width="617" height="805"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170J. The Jet Propulsion Laboratory.</figcaption> </picture>
<picture class="JDImage6 astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (min-width: 600px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: light) and (max-width: 599px)" class="astro-3zw7efbj"> <source media="(prefers-color-scheme: dark) and (max-width: 599px)" class="astro-3zw7efbj"> <img alt="The Jet Propulsion Laboratory" class=" astro-3zw7efbj" loading="lazy" src="https://johnnydecimal.com/img/v6/22.00.0170.1985 - Jet Propulsion Laboratory--cx-1136x1169.png" width="1136" height="1169"> <figcaption class="astro-3zw7efbj">Figure 22.00.0170K. The Jet Propulsion Laboratory.</figcaption> </picture>
<p>If you have any leanings this way, the vintage annual report world can be a dangerous rabbit hole.<sup><a href="#user-content-fn-3" id="user-content-fnref-3" data-footnote-ref="" aria-describedby="footnote-label">3</a></sup> And so can filing using the date to sort.</p>
<p>From Lucy</p>
<hr>
<p><em>100% human. 0% AI. Always.</em></p>
<section data-footnotes="" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Our Friday afternoon soundtrack is often the <a href="https://en.wikipedia.org/wiki/Platinum_Collection_(Genesis_album)">Platinum Collection by Genesis</a>, but anything similar will improve this type of admin. <a href="#user-content-fnref-1" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Remember, you can set up a <a href="https://jdcm.al/22.00.0094">shortcut to add dates automatically</a>. <a href="#user-content-fnref-2" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Especially the space ones. <a href="#user-content-fnref-3" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>Client Registration and Enterprise Management in the November 2025 MCP Authorization Spec - Articles by Aaron Pareckihttps://aaronparecki.com/2025/11/25/1/mcp-authorization-spec-update2025-11-25T21:25:00.000Z<p>The New MCP Authorization Spec is Here! Today marks the one-year anniversary of the Model Context Protocol, and with it, the launch of the new <b><a href="https://modelcontextprotocol.io/specification/2025-11-25">2025-11-25 specification</a></b>.</p>
<p>I’ve been helping out with the authorization part of the spec for the last several months, working to make sure we aren't just shipping something that works for hobbyists, but something that even scales to the enterprise. If you’ve been following my posts like <a href="https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp">Enterprise-Ready MCP</a> or <a href="https://aaronparecki.com/2025/04/03/15/oauth-for-model-context-protocol">Let's Fix OAuth in MCP</a>, you know this has been a bit of a journey over the past year.</p>
<p>The new spec just dropped, and while there are a ton of great updates across the board, far more than I can get in to in this blog post, there are two changes in the authorization layer that I am most excited about. They fundamentally change how clients identify themselves and how enterprises manage access to AI-enabled apps.</p>
<h2>Client ID Metadata Documents (CIMD)</h2>
<p>If you’ve ever tried to work with an open ecosystem of OAuth clients and servers, you know the "Client Registration" problem. In traditional OAuth, you go to a developer portal, register your app, and get a <code>client_id</code> and <code>client_secret</code>. That works great when there is one central server (like Google or GitHub) and many clients that want to use that server.</p>
<p>It breaks down completely in an open ecosystem like MCP, where we have many clients talking to many servers. You can't expect a developer of a new AI Agent to manually register with every single one of the 2,000 MCP servers in the <a href="https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/">MCP server registry</a>. Plus, when a new MCP server launches, that server wouldn't be able to ask every client developer to register either.</p>
<p>Until now, the answer for MCP was Dynamic Client Registration (DCR). But as implementation experiences has shown us over the last several months, DCR introduces a massive amount of complexity and risk for both sides.</p>
<p>For <b>Authorization Servers</b>, DCR endpoints are a headache. They require public-facing APIs that need strict rate limiting to prevent abuse, and they lead to unbounded database growth as thousands of random clients register themselves. The number of client registrations will only ever increase, so the authorization server is likely to implement some sort of "cleanup" mechanism to delete old client registrations. The problem is there is no clear definition of what an "old" client is. And if a dynamically registered client is deleted, the client doesn't know about it, and the user is often stuck with no way to recover. Because of the security implications of an endpoint like this, DCR has also been a massive barrier to enterprise adoption of MCP.</p>
<p>For <b>Clients</b>, it’s just as bad. They have to manage the lifecycle of their client credentials on top of the actual access tokens, and there is no standardized way to check if the client registration is still valid. This frequently leads to sloppy implementations where clients simply register a brand new <code>client_id</code> every single time a user logs in, further increasing the number of client registrations at the authorization server. This isn't a theoretical problem, this is also how Mastodon has worked for the last several years, and has some <a href="https://github.com/mastodon/mastodon/issues/27740">GitHub issue threads</a> describing the challenges it creates.</p>
<p>The new MCP spec solves this by adopting <b>Client ID Metadata Documents</b>.</p>
<p>The OAuth Working Group <a href="https://aaronparecki.com/2025/10/08/4/cimd">adopted the Client ID Metadata Document spec in October</a> after about a year of discussion, so it's still relatively new. But seeing it land as the default mechanism in MCP is huge. Instead of the client registering with each authorization server, the client establishes its own identity with a URL it controls and uses the URL to identify itself during an OAuth flow.</p>
<p>When the client starts an OAuth request to the MCP authorization server, it says, "Hi, I'm <code>https://example-app.com/client.json</code>." The server fetches the JSON document at that URL and finds the client's metadata (logo, name, redirect URIs) and proceeds on as usual.</p>
<p>This creates a decentralized trust model based on DNS. If you trust <code>example.com</code>, you trust the client. It removes the registration friction entirely while keeping the security guarantees we need. It’s the same pattern we’ve used in <a href="https://indieauth.net/">IndieAuth</a> for over a decade, and it fits MCP perfectly.</p>
<p>There are definitely some new considerations and risks this brings, so it's worth diving into the details about Client ID Metadata Documents in the <a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-id-metadata-documents">MCP spec</a> as well as the <a href="https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-6">IETF spec</a>. For example, if you're building an MCP client that is running on a web server, you can actually manage private keys and publish the public keys in your metadata document, enabling strong client authentication. And like Dynamic Client Registration, there are still limitations for how desktop clients can leverage this, which can hopefully be solved by a future extension. I talked more about this during a hugely popular session at the Internet Identity Workshop in October, you can find the slides <a href="https://speakerdeck.com/aaronpk/oauth-for-mcp-internet-identity-workshop-october-2025">here</a>.</p>
<p>You can try out this new flow today in VSCode, the first MCP client to ship support for CIMD even before it was officially in the spec. You can also learn more and test it out at the excellent website the folks at Stytch created: <a href="https://client.dev/">client.dev</a>.</p>
<h2>Enterprise-Managed Authorization (Cross App Access)</h2>
<p>This is the big one for anyone asking, "Is MCP safe to use in the enterprise?"</p>
<p>Until now, when an AI agent connected to an MCP server, the connection was established directly between the MCP client and server. For example if you are using ChatGPT to connect to the <a href="https://developers.asana.com/docs/using-asanas-mcp-server">Asana MCP server</a>, ChatGPT would start an OAuth flow to Asana. But if your Asana account is actually connected to an enterprise IdP like Okta, Okta would only see that you're logging in to Asana, and wouldn't be aware of the connection established between ChatGPT and Asana. This means today there are a huge number of what are effectively unmanaged connections between MCP clients and servers in the enterprise. Enterprise IT admins hate this because it creates "Shadow IT" connections that bypass enterprise policy.</p>
<p>The new MCP spec incorporates <b>Cross App Access (XAA) </b>as the authorization extension "<a href="https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/enterprise-managed-authorization.mdx">Enterprise-Managed Authorization</a>".</p>
<p>This builds on the work I discussed in <a href="https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp">Enterprise-Ready MCP</a> leveraging the <b>Identity Assertion Authorization Grant</b>. The flow puts the enterprise Identity Provider (IdP) back in the driver's seat.</p>
<p>Here is how it works:</p>
<p>
<ol start="1">
<li>
<p><b>Single Sign-On</b>: First you log into an MCP Client (like Claude or an IDE) using your corporate SSO, the client gets an ID token.</p>
</li>
<li>
<p><b>Token Exchange</b>: Instead of the client starting an OAuth flow to ask the user to manually approve access to a downstream tool (like an Asana MCP server), the client takes that ID token back to the Enterprise IdP to ask for access.</p>
</li>
<li>
<p><b>Policy Check</b>: The IdP checks corporate policy. "Is <code>Engineering</code> allowed to use <code>Claude</code> to access <code>Asana</code>?" If the policy passes, the IdP issues a temporary token (ID-JAG) that the client can take to the MCP authorization server.</p>
</li>
<li>
<p><b>Access Token Request</b>: The MCP client takes the ID-JAG to the MCP authorization server saying "hey this IdP says you can issue me an access token for this user". The authorization server validates the ID-JAG the same way it would have validated an ID Token (remember this app is also set up for SSO to the same corporate IdP), and issues an access token.</p>
</li>
</ol>
</p>
<p>This happens entirely behind the scenes without user interaction. The user doesn't get bombarded with consent screens, and the enterprise admin gets full visibility and revocability. If you want to shut down AI access to a specific internal tool, you do it in one place: your IdP.</p>
<h2>Further Reading</h2>
<p>There is a lot more in the full spec update, but these two pieces—<b>CIMD</b> for scalable client identity and <b>Cross App Access</b> for enterprise security—are the two I am most excited about. They take MCP to the next level by solving the biggest challenges that were preventing scalable adoption of MCP in the enterprise.</p>
<p>You can read more about the MCP authorization spec update in <a href="https://den.dev/blog/mcp-november-authorization-spec/">Den's excellent post</a>, and more about all the updates to the MCP spec in the <a href="https://blog.modelcontextprotocol.io/posts/2025-11-25-first-mcp-anniversary/">official announcement post</a>.</p>
<p>Links to docs and specs about everything mentioned in this post are below.</p>
<ul>
<li><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization">MCP Authorization Spec 2025-11-25</a></li>
<li><a href="https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/">Client ID Metadata Document</a> (ietf.org)</li>
<li><a href="https://datatracker.ietf.org/doc/draft-parecki-oauth-identity-assertion-authz-grant/">Identity Assertion Authorization Grant</a> (ietf.org)</li><a href="https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp">Enterprise-Ready MCP</a></li>
<li><a href="https://blog.modelcontextprotocol.io/posts/client_registration/">Evolving Client Registration</a> (blog.modelcontextprotocol.io)</li>
<li><a href="https://oauth.net/cross-app-access/">Cross App Access</a> (oauth.net)</li>
</ul>My default apps, 2025 edition - Posts feedhttps://www.coryd.dev/posts/2025/my-default-apps-2025-edition2025-11-25T19:47:00.000Z<p>An update on <a href="https://www.coryd.dev/posts/2024/my-default-apps-2024-edition">my 2024 post</a>. Some fairly major changes this time around. I've been making a concerted effort to move to more self-hosted applications and, failing that, hosted applications built with privacy in mind. This has also been done with an eye towards relying on fewer Apple services.</p>
<ul>
<li>📮 Mail service: <a href="https://pr.tn/ref/X775YX40Z50G">Proton</a></li>
<li>📨 Mail client: <a href="https://pr.tn/ref/X775YX40Z50G">Proton</a>'s macOS and iOS clients.</li>
<li>📝 Notes: <a href="https://obsidian.md">Obsidian</a></li>
<li>✅ Tasks: <a href="https://obsidian.md">Obsidian</a> with the <a href="https://github.com/obsidian-tasks-group/obsidian-tasks">obsidian-tasks</a> plugin.</li>
<li>📖 RSS service/client/bookmarking: <a href="https://freshrss.org/">FreshRSS</a> + <a href="https://netnewswire.com/">NetNewsWire</a> + articles sent to <a href="https://linkding.link">linkding</a></li>
<li>🔎 Launcher: Spotlight</li>
<li>📇 Contacts: <a href="https://sabre.io/baikal/">Baïkal</a></li>
<li>😶🌫️ Cloud storage: <a href="https://pr.tn/ref/X775YX40Z50G">Proton</a> Drive + iCloud</li>
<li>🎞️ Photo library: <a href="https://ente.io">Ente</a></li>
<li>🌐 Web browser: <a href="https://kagi.com/orion">Orion</a></li>
<li>💬 Chat: iMessage + <a href="https://signal.org">Signal</a></li>
<li>🗓️ Calendar: <a href="https://pr.tn/ref/X775YX40Z50G">Proton</a> calendar</li>
<li>🌤️ Weather: <a href="https://mercuryweather.app">Mercury Weather</a></li>
<li>📚 Books: an ePub PWA I wrote that leverages <a href="https://www.coryd.dev/audiobookshelf.org">Audiobookshelf</a>'s API + <a href="https://www.coryd.dev/audiobookshelf.org">Audiobookshelf</a> using <a href="https://github.com/rasmuslos/ShelfPlayer">ShelfPlayer</a></li>
<li>🎙️ Podcasts: <a href="https://www.coryd.dev/audiobookshelf.org">Audiobookshelf</a><sup id="fnref:1"><span>1</span></sup> using <a href="https://github.com/rasmuslos/ShelfPlayer">ShelfPlayer</a></li>
<li>🎧 Music: <a href="https://www.coryd.dev/posts/2025/i-made-a-music-app">Cadence</a> + <a href="https://www.navidrome.org">Navidrome</a> + <a href="https://www.coryd.dev/posts/2025/evolving-my-personal-music-scrobbler">a custom scrobbler</a></li>
<li>🔐 Passwords: <a href="https://pr.tn/ref/X775YX40Z50G">Proton</a> Pass</li>
<li>💸 Budgeting: A spreadsheet in <a href="https://www.libreoffice.org">LibreOffice</a></li>
<li>🐘 Mastodon: <a href="https://getmona.app">Mona</a></li>
<li>📺 TV/Movie tracking: <a href="https://coryd.dev/watching">Custom/self-hosted implementation.</a></li>
<li>👨🏼💻 Code: <a href="https://cursor.com">Cursor</a> + <a href="https://iterm2.com/">iTerm</a></li>
<li>🕵🏻 Search: <a href="https://kagi.com">Kagi</a></li>
<li>📦 Package tracking: <a href="https://parcelapp.net">Parcel</a></li>
</ul>
<div class="footnotes" role="doc-endnotes"><hr><ol><li class="footnote" id="fn:1" role="doc-endnote"><p>I only listen to <a href="https://www.404media.co">404 Media</a>'s and saved items from <a href="https://linkding.link">linkding</a> I convert to audio. <span>↩</span></p></li></ol></div>
<img src="https://stats.coryd.dev/count?p=/posts/2025/my-default-apps-2025-edition&t=My+default+apps%2C+2025+edition&r=rss" style="position:absolute;left:-9999px;">