<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Open Source on PHP Boy Scout</title><link>https://phpboyscout.uk/tags/open-source/</link><description>Recent content in Open Source on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Tue, 26 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://phpboyscout.uk/tags/open-source/index.xml" rel="self" type="application/rss+xml"/><item><title>AI didn't kill curl's bug bounty. The bounty did.</title><link>https://phpboyscout.uk/ai-didnt-kill-curls-bug-bounty/</link><pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/ai-didnt-kill-curls-bug-bounty/</guid><description>&lt;img src="https://phpboyscout.uk/ai-didnt-kill-curls-bug-bounty/cover-ai-didnt-kill-curls-bug-bounty.png" alt="Featured image of post AI didn't kill curl's bug bounty. The bounty did." /&gt;&lt;p&gt;In January, Daniel Stenberg shut down curl&amp;rsquo;s bug bounty. The headlines wrote
themselves, and they all said the same thing: AI killed it. A flood of
machine-generated slop drowned the maintainers, so they pulled the plug.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s true, as far as it goes. It&amp;rsquo;s also the wrong lesson, and the right one
is sitting in plain sight in the same project, in the same few months.&lt;/p&gt;
&lt;h2 id="volume-without-validation-is-the-attack"&gt;Volume without validation is the attack
&lt;/h2&gt;&lt;p&gt;curl had run its bounty since April 2019. Over its life it paid out
&lt;a class="link" href="https://daniel.haxx.se/blog/2026/01/26/the-end-of-the-curl-bug-bounty/" target="_blank" rel="noopener"
 &gt;more than $100,000 for 87 genuine vulnerabilities&lt;/a&gt;,
a thoroughly good return for one of the most depended-on pieces of software on
the planet. Then the reports stopped being reports. The confirmation rate, the
share of submissions that turned out to be a real bug, had historically sat
north of 15%. By 2025 it was below 5%. Fewer than one in twenty submissions
were worth anything, and the rest still had to be read.&lt;/p&gt;
&lt;p&gt;That last part is the whole problem. A bogus report doesn&amp;rsquo;t announce itself.
Someone has to open it, take it seriously, try to reproduce it, and work out
that it&amp;rsquo;s nonsense, and that someone is a human being with a finite number of
hours and a project to run. Stenberg put it plainly: the slop &amp;ldquo;take[s] a
serious mental toll to manage and sometimes also a long time to debunk.&amp;rdquo; The
submitter spends seconds. The maintainer spends an afternoon. Do that at volume
and it stops being noise and becomes an attack, a denial-of-service aimed not
at curl&amp;rsquo;s servers but at its maintainers&amp;rsquo; attention. No exploit required. Just
plausibility, in bulk.&lt;/p&gt;
&lt;h2 id="the-bounty-was-the-accelerant-not-the-ai"&gt;The bounty was the accelerant, not the AI
&lt;/h2&gt;&lt;p&gt;So far this is the story everyone tells. Here&amp;rsquo;s where I get off the bus.&lt;/p&gt;
&lt;p&gt;The instinct is to blame the AI for the slop. But look at what a bounty actually
is. It&amp;rsquo;s a cash prize, and curl&amp;rsquo;s was priced for the thing it wanted: the hours
and the judgement a skilled human pours into finding a real flaw. That pricing
made complete sense right up until the cost of producing something that &lt;em&gt;looked
like&lt;/em&gt; a finding collapsed to nearly nothing.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what AI changed. Not the supply of bugs. The supply of plausible-looking
bug reports. Put a cash prize on &amp;ldquo;looks like a finding&amp;rdquo;, then make &amp;ldquo;looks like a
finding&amp;rdquo; free to generate, and you haven&amp;rsquo;t got a bug bounty any more. You&amp;rsquo;ve got
a slot machine. Stenberg said he&amp;rsquo;d started to sense &amp;ldquo;a bad faith attitude&amp;rdquo; in
the reports, and of course he had. The incentive was openly inviting it.&lt;/p&gt;
&lt;p&gt;So the death spiral was structural, not bad luck. The moment generating
plausible reports went free, any cash bounty became a magnet for spray-and-pray,
and the only open questions were how fast it would rot and whether you&amp;rsquo;d close
the programme or just let the rewards quietly wither. The AI was the match. The
bounty was the petrol. We have been pointing at the wrong one.&lt;/p&gt;
&lt;h2 id="the-proof-curl-turned-around-and-hired-the-ai"&gt;The proof: curl turned around and hired the AI
&lt;/h2&gt;&lt;p&gt;If AI were really the villain here, you&amp;rsquo;d expect curl to have slammed the door
on it. It did the opposite.&lt;/p&gt;
&lt;p&gt;In the same stretch, &lt;a class="link" href="https://aisle.com/blog/curl-adopts-aisle-after-its-ai-agents-discovered-5-cves" target="_blank" rel="noopener"
 &gt;by AISLE&amp;rsquo;s own account&lt;/a&gt;,
an AI security platform contributed 24 pull requests to curl, five of which
earned CVEs, and the project now runs it internally for continuous review. The
same tooling reportedly found &lt;a class="link" href="https://www.lesswrong.com/posts/7aJwgbMEiKq5egQbd/" target="_blank" rel="noopener"
 &gt;all twelve zero-days&lt;/a&gt;
in an OpenSSL release in late January. (Both of those are the tool-makers&amp;rsquo; and a
third party&amp;rsquo;s numbers rather than curl&amp;rsquo;s audited figures, so weigh them as such.
But curl adopting the thing isn&amp;rsquo;t a claim. It&amp;rsquo;s a decision.)&lt;/p&gt;
&lt;p&gt;Sit with the shape of that. curl shut down strangers being paid for AI-shaped
noise, and in the same breath put AI to work as a tool its own maintainers
drive. The two moves look contradictory only if you think &amp;ldquo;AI&amp;rdquo; is a single thing
with a single verdict attached. It isn&amp;rsquo;t. Pointed at the problem by people
accountable for the result, with no prize to farm, it found real bugs. Dangled
in front of anonymous strangers chasing a payout, it produced sand.&lt;/p&gt;
&lt;h2 id="the-tell-is-which-ai-curl-kept-and-which-it-mocked"&gt;The tell is which AI curl kept, and which it mocked
&lt;/h2&gt;&lt;p&gt;Stenberg drew that line about as sharply as a person can. When Anthropic put its
security model, Mythos, in front of curl this spring, it
&lt;a class="link" href="https://daniel.haxx.se/blog/2026/05/11/mythos-finds-a-curl-vulnerability/" target="_blank" rel="noopener"
 &gt;scanned 176,000 lines of C and surfaced a single flaw&lt;/a&gt;,
and Stenberg called the surrounding fanfare
&lt;a class="link" href="https://www.theregister.com/security/2026/05/11/anthropics-bug-hunting-mythos-was-greatest-marketing-stunt-ever-says-curl-creator/5238111" target="_blank" rel="noopener"
 &gt;the greatest marketing stunt he&amp;rsquo;d seen&lt;/a&gt;.
Same maintainer. Adopts one AI, rubbishes another.&lt;/p&gt;
&lt;p&gt;The deciding factor was never whether the thing was AI. Both were. It was
whether the output survived a human checking it, and whether you could check it
at all. AISLE handed over pull requests and CVEs you could read and merge.
Mythos arrived as a closed model and a press release, which is to say a claim
the community has no way to independently test.&lt;/p&gt;
&lt;p&gt;My bias, up front, because it runs the opposite way to what you&amp;rsquo;d expect from
someone writing this: I&amp;rsquo;m a paying Claude subscriber and I lean on Anthropic&amp;rsquo;s
models every working day, the one behind the spadework for this post included.
I&amp;rsquo;m an advocate, not a sceptic, and AI genuinely has its place. That is
&lt;em&gt;exactly&lt;/em&gt; why the Mythos fanfare grates. Overselling a closed model to get out
ahead of the competition, when the one test the public got to see turned up a
single bug, is the sort of thing that chips away at trust in all of it. A result
you can&amp;rsquo;t verify is marketing until proven otherwise, whoever&amp;rsquo;s logo is on the
slide, and I&amp;rsquo;d rather the tools I depend on didn&amp;rsquo;t stoop to it.&lt;/p&gt;
&lt;h2 id="the-cheap-half-and-the-expensive-half"&gt;The cheap half and the expensive half
&lt;/h2&gt;&lt;p&gt;Pull back from curl for a moment, because the lesson isn&amp;rsquo;t really about bounties
at all. Anyone who works with these tools every day knows the same thing: when
they go wrong, it&amp;rsquo;s rarely the model running off on its own. It&amp;rsquo;s the context it
wasn&amp;rsquo;t given, the rope it was handed, the output nobody checked closely enough.
The failure sits on the human side of the keyboard, at the one step that&amp;rsquo;s
easiest to skip, which is verification.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the pattern curl hit at the scale of an ecosystem. AI made one thing
nearly free: producing work that looks right. It did not make the other thing a
penny cheaper: confirming the work &lt;em&gt;is&lt;/em&gt; right. That cost still falls, in full,
on a person. (A scanner, &lt;a class="link" href="https://phpboyscout.uk/the-security-finding-you-must-not-fix/" &gt;I&amp;rsquo;ve argued before&lt;/a&gt;,
is an argument, not an order; the same goes double for a model.) The bounty&amp;rsquo;s
fatal mistake was paying for the cheap half and quietly assuming it had bought
the expensive one. The same trap waits in code review, in hiring, in CVs read by
machines, but that&amp;rsquo;s a bigger argument for another post.&lt;/p&gt;
&lt;h2 id="pouring-sand-into-the-machine"&gt;Pouring sand into the machine
&lt;/h2&gt;&lt;p&gt;curl didn&amp;rsquo;t capitulate to AI, whatever the headlines decided. It stopped paying
for the worthless half and started using the valuable half, and it had the
discernment to tell a useful tool from a press release while it did so.&lt;/p&gt;
&lt;p&gt;The bounty wasn&amp;rsquo;t a casualty of artificial intelligence. It was a structure
that, the instant plausible output became free, could only fill with sand.
Stenberg said he hopes closing it stops &amp;ldquo;more people pouring sand into the
machine.&amp;rdquo; Reading the last year of his inbox, I think he&amp;rsquo;ll get his wish. The
sand was only ever there because somebody left a bucket of money beside the
funnel.&lt;/p&gt;</description></item><item><title>go-tool-base: I got tired of reinventing the wheel</title><link>https://phpboyscout.uk/introducing-go-tool-base/</link><pubDate>Wed, 18 Mar 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/introducing-go-tool-base/</guid><description>&lt;img src="https://phpboyscout.uk/introducing-go-tool-base/cover-introducing-go-tool-base.png" alt="Featured image of post go-tool-base: I got tired of reinventing the wheel" /&gt;&lt;p&gt;If you&amp;rsquo;ve written more than two or three command-line tools in Go, you&amp;rsquo;ll recognise the shape of the first afternoon. I certainly do! You reach for &lt;a class="link" href="https://github.com/spf13/cobra" target="_blank" rel="noopener"
 &gt;Cobra&lt;/a&gt; for the command tree, &lt;a class="link" href="https://github.com/spf13/viper" target="_blank" rel="noopener"
 &gt;Viper&lt;/a&gt; for config, and then you start the part nobody ever puts in the README&amp;hellip; the plumbing.&lt;/p&gt;
&lt;p&gt;Where does config live? A file, an env var, an embedded default? In what order do they override each other? How does the tool tell the user there&amp;rsquo;s a newer version, and how does it actually update itself? What does logging look like, and is it the same logging the next tool will use? And how do you wire all of that into each command without every command reaching into a pile of globals?&lt;/p&gt;
&lt;p&gt;None of it is hard. That&amp;rsquo;s the problem! It&amp;rsquo;s not hard, it&amp;rsquo;s just &lt;em&gt;there&lt;/em&gt;, every single time, and every single time I&amp;rsquo;d find myself reinventing it slightly differently to the last time. Different override precedence here. A subtly different update flow there. Logging that didn&amp;rsquo;t quite match the tool I&amp;rsquo;d written three months earlier. Each new tool was a fresh re-litigation of decisions I&amp;rsquo;d already made and then promptly forgotten.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;ve banged on about the Boy Scout rule for years (leave the codebase better than you found it), but it has an uncomfortable corollary. If you keep turning up to the same campsite and finding it in the same mess, at some point the honest thing to do is to stop tidying it and go and build a better campsite.&lt;/p&gt;
&lt;h2 id="first-just-packages"&gt;First, just packages
&lt;/h2&gt;&lt;p&gt;So I started pulling the recurring pieces out into their own packages. Nothing grand. A config package that did the hierarchical merge the way I always ended up doing it anyway. A version package that knew how to compare semver and spot a development build. A setup package that handled first-run bootstrap and self-updating from a release. They lived as separate repos, and if you go digging through my GitHub history you can still find the scruffy ancestors of them scattered about.&lt;/p&gt;
&lt;p&gt;Separate packages was the right &lt;em&gt;first&lt;/em&gt; move. It forced each piece to stand on its own and earn its keep on a real project before I trusted it on the next one. A package that&amp;rsquo;s only ever been used in the repo it was born in hasn&amp;rsquo;t really been tested&amp;hellip; it&amp;rsquo;s just been agreed with.&lt;/p&gt;
&lt;p&gt;But separate packages come with a tax. Each one has its own release cadence, its own changelog, its own CI. Worse, they have to agree with each other at the seams, and when they&amp;rsquo;re versioned independently those seams drift. I&amp;rsquo;d bump the config package, and the setup package that depended on it would quietly need a matching bump, and the tool that used both would need telling about both. I&amp;rsquo;d traded &amp;ldquo;reinvent the wheel&amp;rdquo; for &amp;ldquo;keep a dozen wheels in sync&amp;rdquo;, and I&amp;rsquo;m really not convinced that&amp;rsquo;s a better deal.&lt;/p&gt;
&lt;h2 id="then-one-library"&gt;Then, one library
&lt;/h2&gt;&lt;p&gt;Once the packages had been used enough (used in anger, on real tools, by people who weren&amp;rsquo;t me) the shape of them stopped moving. The interfaces settled. The arguments about precedence and defaults were over, because the answers had survived contact with reality.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the point where separate packages stop being a virtue and start being friction. So I forged them into one and called it &lt;strong&gt;go-tool-base&lt;/strong&gt;. One module, one version number, one changelog, and one set of seams that are now internal and can&amp;rsquo;t drift, because they ship together.&lt;/p&gt;
&lt;p&gt;The heart of it is a dependency-injection container, a &lt;a class="link" href="https://gitlab.com/phpboyscout/go-tool-base/-/blob/5c78fc9/pkg/props/props.go#L15" target="_blank" rel="noopener"
 &gt;&lt;code&gt;Props&lt;/code&gt; struct&lt;/a&gt;, that holds the things every command needs: the logger, the config, the embedded assets, the filesystem handle, the error handler, the tool&amp;rsquo;s own metadata. Commands are handed &lt;code&gt;Props&lt;/code&gt; explicitly rather than reaching for globals, which means a command is just a function of its inputs and is therefore trivially testable. That one decision has quietly paid for itself on every tool I&amp;rsquo;ve built since.&lt;/p&gt;
&lt;p&gt;Around that container sits all the stuff I was so tired of rewriting: hierarchical config, structured logging, version checking, self-update from GitHub or GitLab releases, an interactive TUI documentation browser, AI integration, service lifecycle management. A new tool inherits the lot and gets to spend its first afternoon on the thing that&amp;rsquo;s actually novel&amp;hellip; its own logic.&lt;/p&gt;
&lt;h2 id="finally-a-generator"&gt;Finally, a generator
&lt;/h2&gt;&lt;p&gt;A library still leaves you staring at a blank &lt;code&gt;main.go&lt;/code&gt;. You still have to know the conventions, wire the container, lay out the directories, register the commands. All knowable, but all boilerplate. And boilerplate is exactly the enemy I set out to kill in the first place.&lt;/p&gt;
&lt;p&gt;So go-tool-base ships a generator. &lt;code&gt;gtb generate project&lt;/code&gt; scaffolds a complete, working, idiomatic project: directory layout, the wired &lt;code&gt;Props&lt;/code&gt; container, the command tree, CI, the whole lot. &lt;code&gt;gtb generate command&lt;/code&gt; adds a new command and registers it for you. The generator also handles upkeep: when the framework&amp;rsquo;s conventions move, it can regenerate the scaffolding of an existing project without trampling all over the code you&amp;rsquo;ve written on top. (That last bit turned out to be a properly interesting problem in its own right, and a future post.)&lt;/p&gt;
&lt;p&gt;The goal is blunt. Creating a CLI tool should be about the tool, not the scaffolding. The first afternoon should be spent on the part that&amp;rsquo;s actually worth writing.&lt;/p&gt;
&lt;h2 id="one-thing-i-was-careful-about"&gt;One thing I was careful about
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s a nasty failure mode with &amp;ldquo;batteries-included&amp;rdquo; frameworks: the day you outgrow them, they hold you hostage. You either stay inside the framework&amp;rsquo;s worldview forever, or you face a rewrite. I&amp;rsquo;ve been burned by that before and I had no intention of inflicting it on anyone else.&lt;/p&gt;
&lt;p&gt;So go-tool-base generates idiomatic, standard-library-compliant Go. There&amp;rsquo;s no magic runtime you can&amp;rsquo;t see, no clever code you couldn&amp;rsquo;t have written by hand. If you ever outgrow the framework the generated code stands on its own and you walk away with a perfectly normal Go project. A framework should be a starting point you&amp;rsquo;re glad you took, not a room you can&amp;rsquo;t get out of.&lt;/p&gt;
&lt;h2 id="where-this-leaves-me"&gt;Where this leaves me
&lt;/h2&gt;&lt;p&gt;go-tool-base exists because I was spending the first afternoon of every Go CLI tool rebuilding the same plumbing, and rebuilding it slightly wrong relative to last time. It started life as separate packages so each piece could earn its place on real projects; once they&amp;rsquo;d stopped moving I forged them into a single library so the seams couldn&amp;rsquo;t drift; and then I wrapped a generator around it so a new tool starts as a working project rather than a blank file.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a framework for the boring 80% (config, versioning, updates, logging, lifecycle) so you can spend your time on the 20% that&amp;rsquo;s actually yours.&lt;/p&gt;
&lt;p&gt;Over the coming posts I&amp;rsquo;ll dig into the individual pieces&amp;hellip; the generator that won&amp;rsquo;t clobber your edits, the credential handling, the self-update integrity checks, and a few Go techniques I&amp;rsquo;m rather pleased with along the way. Stay tuned!&lt;/p&gt;</description></item><item><title>Free Open Source Website for Scouts</title><link>https://phpboyscout.uk/free-open-source-website-scouts/</link><pubDate>Mon, 12 Jan 2015 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/free-open-source-website-scouts/</guid><description>&lt;img src="https://phpboyscout.uk/free-open-source-website-scouts/scouts-snapshot.png" alt="Featured image of post Free Open Source Website for Scouts" /&gt;&lt;p&gt;I&amp;rsquo;ve been a Scout Leader for a few years now and the District I work within have very little by way of internet presence. As a bit of a pet project I started building a simple Scout based website for them to use.&lt;/p&gt;
&lt;p&gt;Its nothing too fancy, I created a simple module and theme for the Silverstripe CMS and have now put it into a GitHub Repository to share with the wider scouting community.&lt;/p&gt;
&lt;p&gt;I chose Silverstripe because of the speed with which I could develop something usable as well as providing a super simple management interface that can be handled by users of all skill levels.&lt;/p&gt;
&lt;p&gt;The module itself extends some very common extensions available for the CMS and makes them scout focused. Features include.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customisable theme&lt;/li&gt;
&lt;li&gt;Multi tiered Event Calendars&lt;/li&gt;
&lt;li&gt;Customisable Group/Section Pages&lt;/li&gt;
&lt;li&gt;Dynamic Forms&lt;/li&gt;
&lt;li&gt;A reliable News/Blog system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are just a few of the most obvious features and hopefully I will continue to add more.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also offering to help any Scout Groups/Districts/Counties if they are wanting to use these modules and get their sites built and up and running for them free of charge.&lt;/p&gt;
&lt;p&gt;If you want to take a look at the code and have a play yourselves you can find it online at&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/phpboyscout/silverstripe-scouts" target="_blank" rel="noopener"
 &gt;https://github.com/phpboyscout/silverstripe-scouts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/phpboyscout/silverstripe-scouts-theme" target="_blank" rel="noopener"
 &gt;https://github.com/phpboyscout/silverstripe-scouts-theme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to get in touch or would like more information about having a website built for you please fill in the form below.&lt;/p&gt;
&lt;p&gt;[contact-form &lt;a class="link" href="mailto:to=%27matt@phpboyscout.uk" &gt;to='matt@phpboyscout.uk&lt;/a&gt;&amp;rsquo; subject=&amp;lsquo;Request for Scouts Website&amp;rsquo;][contact-field label=&amp;lsquo;Name&amp;rsquo; type=&amp;lsquo;name&amp;rsquo; required=&amp;lsquo;1&amp;rsquo;/][contact-field label=&amp;lsquo;Email&amp;rsquo; type=&amp;lsquo;email&amp;rsquo; required=&amp;lsquo;1&amp;rsquo;/][contact-field label=&amp;lsquo;Scout Group/District/County&amp;rsquo; type=&amp;lsquo;url&amp;rsquo; required=&amp;lsquo;1&amp;rsquo;/][/contact-form]&lt;/p&gt;</description></item></channel></rss>