<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://nesbitt.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nesbitt.io/" rel="alternate" type="text/html" /><updated>2026-06-21T17:02:07+00:00</updated><id>https://nesbitt.io/feed.xml</id><title type="html">Andrew Nesbitt</title><subtitle>Package management and open source metadata expert. Building Ecosyste.ms, open datasets and tools for critical open source infrastructure.</subtitle><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><entry><title type="html">This Week in Package Management: 20 June 2026</title><link href="https://nesbitt.io/2026/06/20/this-week-in-package-management.html" rel="alternate" type="text/html" title="This Week in Package Management: 20 June 2026" /><published>2026-06-20T10:00:00+00:00</published><updated>2026-06-20T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/20/this-week-in-package-management</id><content type="html" xml:base="https://nesbitt.io/2026/06/20/this-week-in-package-management.html"><![CDATA[<p>Week five of the roundup, built from the <a href="https://github.com/ecosyste-ms/package-managers-opml">package manager OPML feed collection</a> and whatever I’ve posted or boosted on <a href="https://mastodon.social/@andrewnez">Mastodon</a>.</p>

<h2 id="releases">Releases</h2>

<p><a href="https://eed3si9n.com/sbt-2.0.0">sbt 2.0.0</a> moves build definitions and plugins to Scala 3, requires JDK 17, and replaces the caching layer with a Bazel-compatible local/remote cache that the rewritten <code class="language-plaintext highlighter-rouge">compile</code> and <code class="language-plaintext highlighter-rouge">test</code> tasks use. The project matrix plugin is now built in and a native-image client cuts startup time. <a href="https://github.com/sbt/sbt/releases/tag/v1.12.12">sbt 1.12.12</a> shipped alongside it for the 1.x line.</p>

<p><a href="https://pnpm.io/blog/releases/11.7">pnpm 11.7</a> adds a <code class="language-plaintext highlighter-rouge">frozenStore</code> setting that opens the store’s SQLite index read-only so <code class="language-plaintext highlighter-rouge">pnpm install</code> can run against a Nix store, a read-only bind mount or an OCI layer without trying to write WAL sidecar files. The release also adds a <code class="language-plaintext highlighter-rouge">--batch</code> flag to publish a whole workspace in one request, per-scope auth tokens, and an option to delegate resolving installs to pacquet.</p>

<p><a href="https://github.com/pnpm/pnpm/releases/tag/v10.34.4">pnpm 10.34.4</a> on the v10 line fixes two path-traversal issues. A traversal-shaped <code class="language-plaintext highlighter-rouge">configDependencies</code> name or version in <code class="language-plaintext highlighter-rouge">pnpm-workspace.yaml</code> could write files outside <code class="language-plaintext highlighter-rouge">node_modules/.pnpm-config</code> and the store (<a href="https://github.com/pnpm/pnpm/security/advisories/GHSA-qrv3-253h-g69c">GHSA-qrv3-253h-g69c</a>), and a crafted lockfile alias under <code class="language-plaintext highlighter-rouge">nodeLinker: hoisted</code> could write outside the install root (<a href="https://github.com/pnpm/pnpm/security/advisories/GHSA-fr4h-3cph-29xv">GHSA-fr4h-3cph-29xv</a>, also fixed in 11.7.0).</p>

<p><a href="https://github.com/npm/cli/releases/tag/v12.0.0-pre.1">npm 12.0.0-pre.1</a> is the first pre-release with the v12 defaults switched on: dependency install scripts are blocked unless listed in <code class="language-plaintext highlighter-rouge">allowScripts</code>, <code class="language-plaintext highlighter-rouge">allow-git</code> and <code class="language-plaintext highlighter-rouge">allow-remote</code> default to <code class="language-plaintext highlighter-rouge">none</code>, and unknown configs and CLI flags error instead of warn. New in this build are an <code class="language-plaintext highlighter-rouge">npm patch</code> subcommand for native dependency patching and a <code class="language-plaintext highlighter-rouge">packageExtensions</code> field for repairing dependency manifests from the root.</p>

<p><a href="https://github.com/yarnpkg/berry/releases/tag/%40yarnpkg%2Fcli%2F4.17.0">Yarn 4.17.0</a> adds package map generation and lets <code class="language-plaintext highlighter-rouge">npmMinimalAgeGate</code> be set per npm scope rather than globally.</p>

<p><a href="https://github.com/dependabot/dependabot-core/releases/tag/v0.382.0">Dependabot Core 0.382.0</a> supports a <code class="language-plaintext highlighter-rouge">scope</code> property on <code class="language-plaintext highlighter-rouge">npm_registry</code> credentials and fixes a leak where npm registry credentials were sent to sibling paths on the same host.</p>

<p><a href="https://github.com/jdx/mise/releases/tag/v2026.6.11">mise 2026.6.11</a> adds Alpine <code class="language-plaintext highlighter-rouge">apk</code> as a bootstrap package manager alongside apt, dnf, pacman and brew, and stops the default Windows <code class="language-plaintext highlighter-rouge">.exe</code> shims from leaking into WSL sessions.</p>

<p><a href="https://github.com/commercialhaskell/stack/releases/tag/v3.11.1">Stack 3.11.1</a> changes the default MSYS2 environment on 64-bit Windows from MINGW64 to CLANG64, following the MSYS2 project’s deprecation of the former.</p>

<p><a href="https://github.com/conda/conda/releases/tag/26.5.3">conda 26.5.3</a> stops caching a not-found response for sharded repodata, which broke shards-only channels on subsequent runs.</p>

<p><a href="https://github.com/gomods/athens/releases/tag/v0.18.0">Athens 0.18.0</a>, the Go module proxy, adds Redis cluster-mode support for its singleflight stash.</p>

<p><a href="https://tip.golang.org/doc/go1.27">Go 1.27rc1</a> drops Bazaar from the version control systems the <code class="language-plaintext highlighter-rouge">go</code> command can fetch modules from, and <code class="language-plaintext highlighter-rouge">go mod tidy</code> now consolidates duplicate <code class="language-plaintext highlighter-rouge">require</code> blocks into one direct and one indirect block for modules at <code class="language-plaintext highlighter-rouge">go 1.27</code> or later.</p>

<p><a href="https://github.com/pypa/pipx/releases/tag/1.14.1">pipx 1.14.1</a> restores a package after an interrupted reinstall and fixes <code class="language-plaintext highlighter-rouge">inject --force</code> reinstall behaviour.</p>

<p><a href="https://github.com/astral-sh/uv/releases/tag/0.11.22">uv 0.11.22</a> adds SARIF output to <code class="language-plaintext highlighter-rouge">uv audit</code>, a <code class="language-plaintext highlighter-rouge">--script</code> flag for <code class="language-plaintext highlighter-rouge">uv check</code> and <code class="language-plaintext highlighter-rouge">uv metadata</code>, and lets preview features be set in <code class="language-plaintext highlighter-rouge">uv.toml</code> and <code class="language-plaintext highlighter-rouge">pyproject.toml</code>. <a href="https://github.com/astral-sh/uv/releases/tag/0.11.23">0.11.23</a> followed a day later reverting two earlier fixes, one of which had broken pre-commit-uv.</p>

<p><a href="https://github.com/moby/moby/releases/tag/docker-v29.6.0">Docker Engine 29.6.0</a> adds a <code class="language-plaintext highlighter-rouge">GET /images/{name}/attestations</code> endpoint that returns in-toto attestation statements such as SLSA provenance and SPDX SBOMs attached to an image, with platform selection and predicate-type filtering.</p>

<p><a href="https://jguer.space/blog/2026-06-15-yay-v13">yay v13</a> shows how long it has been since each PKGBUILD was last modified, so a recently changed AUR package stands out for review, and adds Lua hooks in <code class="language-plaintext highlighter-rouge">init.lua</code> for scripting package checks and filtering.</p>

<p>Also out: <a href="https://github.com/Homebrew/brew/releases/tag/6.0.2">Homebrew 6.0.2</a>, <a href="https://github.com/helm/helm/releases/tag/v4.2.2">Helm 4.2.2</a>, <a href="https://github.com/helm/helm/releases/tag/v3.21.2">Helm 3.21.2</a>, <a href="https://github.com/gradle/gradle/releases/tag/v9.6.0">Gradle 9.6.0</a>, <a href="https://github.com/canonical/snapd/releases/tag/2.76">snapd 2.76</a>, <a href="https://github.com/goharbor/harbor/releases/tag/v2.15.2-rc1">Harbor 2.15.2-rc1</a>, <a href="https://github.com/renovatebot/renovate/releases/tag/43.233.3">Renovate 43.233.3</a>.</p>

<h2 id="articles">Articles</h2>

<p><a href="https://andre.arko.net/2026/06/13/rv-plan-and-progress/">rv: plan and progress</a> (André Arko) reports a year in on the Rust-based Ruby toolchain. It now installs Rubies, builds with YJIT, manages gems with <code class="language-plaintext highlighter-rouge">clean-install</code> and <code class="language-plaintext highlighter-rouge">rvx</code>, and runs on Windows. The next step is full dependency resolution, evaluating a Gemfile and writing a lockfile at uv-like speed.</p>

<p><a href="https://captnemo.in/blog/2026/06/17/package-managers-need-hooks/">Package managers need global hooks</a> (Nemo) argues every package manager should expose pre-clone and pre-build hooks so users can wire dependency cooldowns and threat-feed scanning into the install path locally, without a proxy registry or a shell wrapper.</p>

<p><a href="https://blog.tenstral.net/2026/06/introducing-pkgcli-a-nicer-command-line-interface-for-packagekit.html">Introducing pkgcli</a> (Matthias Klumpp) is a new command-line front end for PackageKit to replace <code class="language-plaintext highlighter-rouge">pkcon</code>, with friendlier command names, coloured output and a JSON mode for scripting.</p>

<p><a href="https://kerkour.com/stdx-cratesio">Why stdx is not on crates.io</a> (Sylvain Kerkour) distributes his 64-crate Rust extended standard library by git only, citing the lack of namespaces and the attack surface a central registry adds: publish tokens, source that need not match the repository, typosquatting, dependency confusion. He argues Rust should follow Go’s model of pointing the package manager at the source repository and backing it with a checksum database and an optional caching proxy.</p>

<p><a href="https://www.thestack.technology/npm-protect-javascript-ecosystem-supply-chain-attacks/">What is npm doing to protect the JavaScript ecosystem, and is it enough?</a> (Mary Branscombe, The Stack) surveys the registry-side changes npm has shipped against supply-chain attacks and what that leaves for developers to do themselves.</p>

<p><a href="https://daniel.haxx.se/blog/2026/06/15/curl-summer-of-bliss/">curl summer of bliss</a> (Daniel Stenberg) announces that the curl project will not accept or handle vulnerability reports during July 2026, giving the maintainers a month off the disclosure treadmill. <a href="https://github.com/libexpat/libexpat/issues/1277">libexpat is doing the same</a> through 1 August.</p>

<p><a href="https://mikael.barbero.tech/blog/post/2026-06-19-vulnerability-report-is-dead-long-live-the-prompt/">The Vulnerability Report Is Dead. Long Live the Prompt!</a> (Mikaël Barbero) argues an AI-assisted vulnerability report should lead with the prompt, model version, commit hash, tool access and verification status, and treat the generated write-up as supporting material.</p>

<p><a href="https://naderman.de/slippy/slides/2026-06-09-PHPVerse-Composer-and-Packagist-Supply-Chain-Security-in-2026.pdf">Composer &amp; Packagist Supply Chain Security in 2026</a> (Nils Adermann) are the slides from the PHPVerse talk covering the same series of changes the Packagist blog has been writing up over the last few weeks.</p>

<p><a href="https://www.helpnetsecurity.com/2026/06/18/homebrew-6-0-0-released/">Homebrew tightens tap security, begins work on its interface</a> (Help Net Security) interviews Mike McQuaid about the 6.0.0 release and the tap trust mechanism.</p>

<p><a href="https://fastwonderblog.com/2026/06/18/thoughts-on-governance/">Thoughts on governance</a> (Dawn Foster) gathers her recent talks on open source project governance and where it meets public-sector governance.</p>

<p><a href="https://www.linkedin.com/pulse/open-source-livelihoods-gig-cristovao-verstraeten-iyhwe/">Open source is a gig</a> (Cristovao Verstraeten, LinkedIn) compares maintaining open source to a touring musician’s life, mostly paid in exposure rather than money.</p>

<h2 id="elsewhere">Elsewhere</h2>

<p><a href="https://arxiv.org/abs/2606.13966">Software Dark Matter: Gazing at Uncharted Files to Navigate SBOM Integrations</a> (Reddypalle et al., arXiv) names the gap between what an SBOM lists from package manager metadata and the security-relevant files that actually ship in an artifact but never appear in a manifest.</p>

<p>The <a href="https://clickpy.clickhouse.com/report/may-2026.html">ClickPy May 2026 report</a> puts PyPI at 163.8 billion downloads for the month, up about 20% since March.</p>

<p><code class="language-plaintext highlighter-rouge">actions/checkout</code> v7 now <a href="https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/">refuses by default</a> to check out fork pull request code in <code class="language-plaintext highlighter-rouge">pull_request_target</code> workflows, blocking a common path to running untrusted code with write tokens in CI.</p>

<p>GitHub repositories can now <a href="https://github.blog/changelog/2026-06-17-limit-open-pull-requests-for-users-without-write-access/">cap the number of open pull requests</a> a user without write access may have at once, aimed at drive-by contribution volume.</p>

<p>Weston Steimel has <a href="https://github.com/pypa/advisory-database/issues/313">opened a discussion</a> on improving the data quality of the PyPA advisory database and is asking for ideas.</p>

<p><a href="https://github.com/kanutocd/gem-guardian">gem-guardian</a> is a RubyGems checksum verifier that has grown lockfile-checksum support, provenance reporting and publisher-provided checksum verification for private registries.</p>

<p>Following last week’s AUR takeover, Morten Linderud <a href="https://chaos.social/@Foxboron/116753600698430125">points out</a> that anyone maintaining ten or more AUR packages they depend on should consider becoming an <a href="https://wiki.archlinux.org/title/Package_Maintainers">official Arch package maintainer</a> instead.</p>

<p>Send links for next week to <a href="https://mastodon.social/@andrewnez">@andrewnez@mastodon.social</a>.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="weekly" /><summary type="html"><![CDATA[Releases, advisories, and articles from across the package management world]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Open Source vs the Invisible Hand</title><link href="https://nesbitt.io/2026/06/18/open-source-vs-the-invisible-hand.html" rel="alternate" type="text/html" title="Open Source vs the Invisible Hand" /><published>2026-06-18T10:00:00+00:00</published><updated>2026-06-18T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/18/open-source-vs-the-invisible-hand</id><content type="html" xml:base="https://nesbitt.io/2026/06/18/open-source-vs-the-invisible-hand.html"><![CDATA[<p>If you handed an economics undergraduate a description of how open source libraries are produced, without saying what it was, and asked them to predict the outcome, they would tell you it doesn’t add up. Non-excludable goods with no price, no contracts, no liability, a median producer headcount of one, and near-total free riding by consumers: there is no model in the textbook under which that arrangement produces anything stable.</p>

<p>Then you run <code class="language-plaintext highlighter-rouge">npm install</code> and a few hundred of these impossible goods arrive in seconds, and the commercial software industry sits almost entirely on top of them. Open source breaks more or less the full set of market axioms at once.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Free-rider_problem">The free rider problem</a>.</strong> A public good is non-rival (my use doesn’t reduce yours) and non-excludable (you can’t keep me out), and standard theory holds that such goods will be underproduced because every rational actor waits for someone else to pay. The canonical case is the <a href="https://en.wikipedia.org/wiki/The_Lighthouse_in_Economics">lighthouse</a>, where everyone benefits and nobody volunteers, so government has to build it. An open source library meets the definition exactly, being perfectly non-rival and non-excludable by licence, so the theory predicts a small number of them, grudgingly produced and propped up by grants. npm alone hosts <a href="https://packages.ecosyste.ms/registries/npmjs.org">over five million</a>, almost none grant-funded, with thousands more turning up every day.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/You_get_what_you_pay_for">You get what you pay for</a>.</strong> Price is supposed to <a href="https://en.wikipedia.org/wiki/Price_signal">signal</a> quality and scarcity, giving a market a way to sort good from bad. SQLite, by <a href="https://www.sqlite.org/mostdeployed.html">its own count</a> the most widely deployed database engine in the world, costs the same as a week-old typosquat with a crypto miner in the postinstall hook. The most valuable libraries in existence and the most dangerous ones sit at the same number, and consumers route around the missing signal with reputation, download counts, and GitHub stars.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Tragedy_of_the_commons">The tragedy of the commons</a>.</strong> A shared resource gets depleted because each user’s rational move is to take without giving back, so the prediction for a software commons would be a small pool of overused, under-maintained code that decays as consumption outruns contribution. Some corners of the public registries do look like that, but the aggregate has grown every year since registries have existed.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Homo_economicus">Rational self-interest</a>.</strong> Economic agents maximise their own utility, and utility can be defined broadly enough to cover enjoyment, status, and ideology as well as money. Even on the broad definition it is a stretch that so many people’s preferences include answering bug reports from strangers at eleven at night, after a full day at an unrelated job, prompted by an issue from a Fortune 500 company titled “URGENT!!”, for a project that pays them nothing. There are enough people whose preferences apparently work that way to keep most of the modern software stack running.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Supply_and_demand">Supply and demand</a>.</strong> Rising demand is supposed to raise the price and draw in new suppliers until the market clears, but when a library goes from a thousand downloads a week to ten million the price stays at zero and the maintainer count typically stays at one, because demand has no channel through which to act on supply. More users turn up, the issue tracker fills, security researchers start filing CVEs against it, and it stays one person’s job.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Division_of_labour">Division of labour</a>.</strong> Critical infrastructure is supposed to be built by specialists, employed full-time, organised into firms that carry liability for failure. Across the public registries ecosyste.ms tracks, <a href="https://opensourcesecurity.io/2025/08-oss-one-person/">more than half of packages have a single maintainer</a>, and that one person frequently has a day job in a different field, no employment relationship with any downstream user, and no contractual liability to anyone. The bus factor for long stretches of the dependency graph works out to a single hobbyist.</p>

<p><strong><a href="https://en.wikipedia.org/wiki/Competitive_advantage">Firms guard competitive advantage</a>.</strong> A company is not supposed to pay engineers to build something and then hand it to the people competing for its customers, yet Google, Meta, Microsoft, and Amazon all fund open source libraries the other three run in production. The standard explanation is <a href="https://www.joelonsoftware.com/2002/06/12/strategy-letter-v/">commoditising your complement</a>: give away the layer adjacent to where you make money so nobody can charge you rent there. That accounts for React and gRPC well enough and is the one entry here with a clean market explanation. The explanation does not extend to the much larger tier underneath, the libraries with one maintainer and no adjacent business model, which is most of what <code class="language-plaintext highlighter-rouge">npm install</code> pulls in.</p>

<p>Economists have noticed all this, and there is a small literature trying to account for it. <a href="https://www.nber.org/papers/w7600">Lerner and Tirole</a> put it down to career signalling, contributing in public to build a reputation you cash in elsewhere. That holds when someone is watching, and not for the maintainer of an obscure dependency no hiring manager will ever look up. <a href="https://www.benkler.org/CoasesPenguin.PDF">Benkler</a> argued that the internet made coordination cheap enough to organise production without a firm. That explains how the work gets divided, not why anyone takes on the unglamorous half of it. <a href="https://web.mit.edu/evhippel/www/democ1.htm">Von Hippel</a> framed it as user innovation, people building what they need and sharing it afterwards for next to nothing. It fits until the maintainer is still answering bug reports years after they stopped using the thing themselves. All three fit some maintainers some of the time, and none on its own accounts for the shape the system has taken or why it has held together this long.</p>

<p>Calling all of the above a list of market failures implies a working market underneath that would behave properly once the failures were corrected, and there isn’t one: open source has run for thirty years on a basis the textbook says cannot hold. It looks more like several arrangements overlaid on each other, part gift economy, part shared infrastructure, part public archive, part reputation system, with no single mechanism carrying it.</p>

<p>The practical fixes that keep being proposed treat it as a market anyway and bolt the missing pieces on, which is where bug bounties, sponsorship marketplaces, dependents-weighted funding formulas, criticality scores, and tokenised reward schemes all come from. Every one of them is an attempt to reconstruct a price for something that has never had one, and to do that they need a number to stand in for value.</p>

<p>The numbers available for that job are <a href="/2026/05/09/the-mismeasure-of-open-source.html">weak proxies</a>, two or three steps removed from what anyone wants to know: who is keeping this running, how close they are to stopping, and whether a security report filed against it would <a href="/2026/05/08/weekend-at-bernies.html">reach anyone at all</a>.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="open-source" /><category term="maintainers" /><category term="dependencies" /><summary type="html"><![CDATA[Ten million downloads a week, one maintainer, zero dollars.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How Open Source Projects Change Hands</title><link href="https://nesbitt.io/2026/06/16/how-open-source-projects-change-hands.html" rel="alternate" type="text/html" title="How Open Source Projects Change Hands" /><published>2026-06-16T10:00:00+00:00</published><updated>2026-06-16T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/16/how-open-source-projects-change-hands</id><content type="html" xml:base="https://nesbitt.io/2026/06/16/how-open-source-projects-change-hands.html"><![CDATA[<p><a href="/2026/05/19/dumb-ways-for-an-open-source-project-to-die.html">Dumb Ways for an Open Source Project to Die</a> listed the ways projects end up dead, and most of the entries describe a moment where maintainership should have moved to someone else and didn’t. This is the other, shorter inventory: the mechanisms that exist for a project to change hands, and who can trigger each one.</p>

<h2 id="the-maintainer-decides">The maintainer decides</h2>

<p><strong>Chosen successor.</strong> The maintainer picks a person and hands over the keys. This is the model everyone imagines when they say succession, and it has almost no supporting infrastructure: it’s a private conversation followed by some permission changes. The vetting is whatever the departing maintainer feels like doing, which is how <a href="https://gist.github.com/dominictarr/9fd9c1024c94592bc7268d36b8d83b3a">event-stream</a> happened: “he emailed me and said he wanted to maintain the module, so I gave it to him.” The <a href="https://openssf.org/blog/2024/04/15/open-source-security-openssf-and-openjs-foundations-issue-alert-for-social-engineering-takeovers-of-open-source-projects/">xz takeover</a> was the same mechanism worked patiently, with sock-puppet users manufacturing the pressure on an overworked maintainer to hand over.</p>

<p>CPAN is the one registry that gives this stage a name: a module whose permissions list the pseudo-user <a href="https://neilb.org/2013/08/07/adoptme.html">HANDOFF</a> has a maintainer looking for someone to take over, recorded in the same machine-readable <a href="https://github.com/andk/pause/blob/master/doc/operating-model.md">permissions file</a> as every real owner.</p>

<p><strong>The successor setting.</strong> GitHub has had <a href="https://github.blog/changelog/2020-05-11-account-successors/">account successors</a> since 2020: you name a person in your account settings, and after presenting <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/repository-access-and-collaboration/maintaining-ownership-continuity-of-your-personal-accounts-repositories">a death certificate and waiting seven days, or an obituary and waiting twenty-one</a>, they can archive your public repositories or transfer them to an account they control. It is the only entry on this list built for the case where the maintainer dies. It covers the repos but not the registry accounts that publish from them, and no registry has an equivalent.</p>

<p><strong>Open adoption.</strong> Instead of picking someone, the maintainer flags the project as available and waits. CPAN again has the oldest version: the <a href="https://neilb.org/2013/08/07/adoptme.html">ADOPTME</a> pseudo-user as owner means the module is up for adoption, and NEEDHELP means the owner wants co-maintainers without leaving. RubyGems <a href="https://github.com/rubygems/rubygems.org/issues/725">proposed</a> an equivalent in October 2014, designed so that “the happiest of happy paths is to enable communication dev-to-dev, requiring no external involvement”, and <a href="https://github.com/rubygems/rubygems.org/pull/2748">shipped it</a> as ownership calls and requests at the end of 2021. Debian’s RFA, Request For Adoption, does the same job for packages: the maintainer keeps working until a successor appears. Outside the registries the equivalent is a <a href="https://www.repostatus.org/">repostatus badge</a> or a “looking for maintainers” line in the README, and no tooling reads either one.</p>

<p><strong>Deliberate ending.</strong> The maintainer can also decline to be succeeded, and the registries support that decision better than any of the handovers above. Packagist’s <a href="https://getcomposer.org/doc/04-schema.md#abandoned">abandoned</a> field takes a replacement package name, and composer prints “Package X is abandoned, you should avoid using it. Use Y instead.” on every install. <a href="https://learn.microsoft.com/en-us/nuget/nuget-org/deprecate-packages">NuGet deprecation</a> carries an alternate package, and a fully deprecated legacy package can apply to transfer its search ranking to a successor that shares its owners. Maven has the <a href="https://maven.apache.org/guides/mini/guide-relocation.html">relocation</a> element for coordinates that moved.</p>

<p>PyPI added <a href="https://blog.pypi.org/posts/2025-01-30-archival/">project archival</a> in 2025 and now serves <a href="https://peps.python.org/pep-0792/">status markers</a> through its APIs. GitHub archiving makes the repo read-only with a banner. Pointing at a successor package moves the users to different code rather than moving the code to a different maintainer.</p>

<h2 id="someone-else-decides">Someone else decides</h2>

<p><strong>Registry adjudication.</strong> A third party hears a claim on an unmaintained name and rules on it. PyPI runs the most formal version: <a href="https://peps.python.org/pep-0541/">PEP 541</a> defines abandonment by three conjunctive criteria (unreachable owner, no release in twelve months, no owner activity). The claimant has to show failed contact attempts and improvements on their own fork, and explain why a renamed fork won’t do. It handled <a href="https://blog.pypi.org/posts/2025-12-31-pypi-2025-in-review/">over 500 requests in 2025</a> and cleared what had been a nine-month backlog. PAUSE admins will grant co-maintainership on a CPAN module when the owner “has entirely disappeared”, with the condition that the adopter is “<a href="https://www.cpan.org/modules/04pause.html">required to respect the work and design of the author</a>”. Hackage admins <a href="https://wiki.haskell.org/Taking_over_a_package">add you to a package’s maintainer group</a> after you’ve tried the maintainer and stated your intent publicly.</p>

<p>npm ran a four-week mediation process and <a href="https://www.theregister.com/2021/08/10/github_npm_package/">suspended it in February 2021</a> after a mis-transfer; the <a href="https://docs.npmjs.com/policies/disputes/">current policy</a> is “we do not transfer package, organization, or username ownership simply because another user wants the name”. crates.io <a href="https://rust-lang.github.io/rfcs/3646-remove-crate-transfer-mediation-policy.html">removed its transfer mediation policy</a> in 2024, on the grounds that requests grow with the registry and “aren’t even usually successful”, citing a PyPI team “not able to keep up with the requests”.</p>

<p><strong>The orphan pool.</strong> The Linux distributions treat an unmaintained package as a vacancy to fill, and built state machines for filling it. Debian encodes the whole lifecycle as <a href="https://www.debian.org/devel/wnpp/">WNPP bugs</a>: O for orphaned, RFA for adoption requested, RFH for help wanted, ITA for intent to adopt, and <a href="https://wiki.debian.org/PackageSalvaging">ITS for intent to salvage</a> a package whose maintainer is present but inactive. Orphaning sets the package’s Maintainer field to the <a href="https://wiki.debian.org/Orphaning">Debian QA Group</a>, and an <a href="https://www.debian.org/doc/manuals/developers-reference/beyond-pkging.html">MIA team</a> chases unresponsive maintainers through a staged escalation that ends in orphaning everything they hold.</p>

<p>Fedora reassigns orphaned packages to a literal <a href="https://docs.fedoraproject.org/en-US/package-maintainers/Package_Orphaning_Process/">orphan user</a> that any packager can take over from with a button. After six weeks unclaimed the package is retired by <a href="https://fedoraproject.org/wiki/Retire_Orphaned_Packages">committing a file named dead.package</a> to its repo. The AUR grants <a href="https://wiki.archlinux.org/title/AUR_submission_guidelines">orphan requests</a> after two weeks of maintainer silence, automatically if the package has been flagged out-of-date for 180 days. CRAN marks the maintainer field itself <a href="https://cran.r-project.org/web/packages/policies.html">ORPHANED</a> and lets anyone take over without the previous maintainer’s consent. None of the language registries has anything like this: a distro package is communal property with a caretaker of record, while a registry name belongs to whoever published first.</p>

<p>In June 2026 an attacker adopted <a href="https://md.archlinux.org/s/SxbqukK6IA">over four hundred orphaned AUR packages</a> and added an <code class="language-plaintext highlighter-rouge">npm install</code> line to every PKGBUILD. The fetched npm package’s preinstall hook installed a <a href="https://ioctl.fail/preliminary-analysis-of-aur-malware/">credential stealer and eBPF rootkit</a>. The <a href="https://lists.archlinux.org/archives/list/aur-general@lists.archlinux.org/thread/2LGBF2AZBPVCCY4VTN6DOVUNNBURFJ2J/">first report</a> was a user noticing <code class="language-plaintext highlighter-rouge">npm install</code> in a VR streaming tool’s PKGBUILD. Each time the payload package was taken down and the install line grepped for, the next batch of adoptions switched: <code class="language-plaintext highlighter-rouge">npm install</code> became <code class="language-plaintext highlighter-rouge">bun add</code> on a fresh package, then <a href="https://aur.archlinux.org/cgit/aur.git/tree/puppy-browser-deps.install?h=puppy-browser&amp;id=fc168647211636b5021c3e73907f59f4a275a631"><code class="language-plaintext highlighter-rouge">'b''u''n' 'a'"d""d"</code></a>. Arch <a href="https://archlinux.org/news/active-aur-malicious-packages-incident/">restricted account creation and package adoption</a> while the incident ran.</p>

<p><strong>The monorepo.</strong> Homebrew, nixpkgs, and conda-forge keep every package definition in one shared repository, so there are no keys to hand over and a succession is a pull request, which is as close as this list gets to anyone deciding with a reviewer in the loop. homebrew/core records no per-formula maintainer at all; maintainership amounts to whoever the tap accepts changes from. nixpkgs lists package maintainers as entries in <a href="https://github.com/NixOS/nixpkgs/blob/master/maintainers/maintainer-list.nix">one file</a>, and adopting an orphaned package means adding your name to it, which is the distro’s caretaker-of-record model without the orphaning process.</p>

<p><strong>Foundation custody.</strong> Projects can move into an organisation built to outlive any individual maintainer. Apache projects are run by <a href="https://www.apache.org/foundation/governance/pmcs">PMCs</a> rather than people, so succession is a membership change inside a structure that persists, and when a community dissolves the project moves to the <a href="https://attic.apache.org/process.html">Attic</a>, a read-only terminal state with its own documented process. The OpenJS Foundation runs a <a href="https://github.com/openjs-foundation/cross-project-council/blob/main/PROJECT_PROGRESSION.md">progression and emeritus lifecycle</a> for hosted projects. The foundation takes on the bus factor in exchange for governance overhead, and most packages on any registry are far too small to ever make that trade.</p>

<p><strong>Corporate custody.</strong> When a company holds the project, succession is an org chart event: from inside it’s the chosen-successor mechanism with an employer attached, and from outside it isn’t visible at all. When <a href="https://antirez.com/news/133">antirez stepped down from Redis in 2020</a> he chose two successors and refused to design the governance that followed. He and both successors were Redis Labs employees, and the company held the trademark. The community’s succession came in 2024, when the <a href="/2026/05/19/dumb-ways-for-an-open-source-project-to-die.html">relicense</a> pushed Madelyn Olson and most of the external core team into forking Valkey under the Linux Foundation. That arrangement holds until the team is cut and the project becomes the corporate orphan from the previous post.</p>

<h2 id="anyone-decides">Anyone decides</h2>

<p><strong>The fork.</strong> Every mechanism above falls back to this one: PEP 541 requires a working fork before PyPI will consider a transfer, and crates.io’s advice for an unreachable owner is to pick a new name. A fork copies the code, and the original keeps the registry name, which means it also keeps the install count and every manifest and lockfile that references it. The fork limbo and licence rug-pull cases from the death list both happened this way, with development moving to a new repo and the installs still resolving to the old one.</p>

<p>The forwarding-address problem has one solution, at the forge layer: GitHub <a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository">repository transfers</a> redirect the old URL to the new one indefinitely, although creating a new repository at the old name deletes the redirect permanently, an attack surface of its own. Go modules inherit both properties because a <a href="https://go.dev/ref/mod">module path contains the repo that hosts it</a>: there are no registry accounts to transfer, and a fork lives at a new path with a Deprecated comment in go.mod as the forwarding address. A transferred repo keeps resolving through the redirect, and Swift packages, declared as <a href="https://developer.apple.com/documentation/packagedescription/package/dependency">Git URLs in the manifest</a>, behave the same way without an equivalent of the Deprecated comment to forward a fork.</p>

<p><strong>The stranger.</strong> Avelino et al. <a href="https://arxiv.org/abs/1906.08058">studied 1,932 popular GitHub projects</a> and found 16% had been abandoned by all of their core developers; 41% of those survived, and 86% of the survivors were rescued by a single new core developer. The most common motivation was “because I was using the project”, and the barriers the rescuers reported were not technical: lack of time, and not being able to get push access because the people who could grant it were gone.</p>

<p>The rescue the study describes is informal: someone who needs the package asks whoever still has access, and the handover is an email and some permission changes that nothing flags or records. That is also how event-stream changed hands, and the maintainer granting access has no way to tell the rescuer from the attacker. Where the orphan pool has already removed that maintainer, as with the four hundred AUR packages above, there is nobody in a position to tell.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="open-source" /><category term="maintainers" /><category term="reference" /><summary type="html"><![CDATA[There are fewer ways to leave your package than to kill it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">This Week in Package Management: 13 June 2026</title><link href="https://nesbitt.io/2026/06/13/this-week-in-package-management.html" rel="alternate" type="text/html" title="This Week in Package Management: 13 June 2026" /><published>2026-06-13T10:00:00+00:00</published><updated>2026-06-13T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/13/this-week-in-package-management</id><content type="html" xml:base="https://nesbitt.io/2026/06/13/this-week-in-package-management.html"><![CDATA[<p>Week four of the roundup, built from the <a href="https://github.com/ecosyste-ms/package-managers-opml">package manager OPML feed collection</a> and whatever I’ve posted or boosted on <a href="https://mastodon.social/@andrewnez">Mastodon</a>.</p>

<h2 id="security">Security</h2>

<p>GitHub announced the <a href="https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/">breaking changes coming in npm v12</a>, estimated for July. <code class="language-plaintext highlighter-rouge">npm install</code> will stop running dependency lifecycle scripts unless they’re allowed via the <code class="language-plaintext highlighter-rouge">allowScripts</code> field that <a href="https://github.com/npm/cli/releases/tag/v11.16.0">11.16.0 introduced</a> in advisory mode, covered in the <a href="/2026/06/05/install-script-allowlists.html">install-script allowlists post</a> I wrote last week. Implicit <code class="language-plaintext highlighter-rouge">node-gyp</code> builds are blocked too, so a package with a <code class="language-plaintext highlighter-rouge">binding.gyp</code> and no install script needs approval like any other. Git dependencies and remote URL tarballs also stop resolving by default, each behind a new <code class="language-plaintext highlighter-rouge">--allow-git</code> / <code class="language-plaintext highlighter-rouge">--allow-remote</code> flag. Everything ships behind warnings in npm 11.16.0+ today.</p>

<p><a href="https://astral.sh/blog/uv-audit">uv audit</a> is a new preview command that scans Python dependencies for known vulnerabilities and adverse project statuses like deprecation, a uv-native alternative to pip-audit. The same announcement covers an experimental malware check: <code class="language-plaintext highlighter-rouge">uv add</code> and <code class="language-plaintext highlighter-rouge">uv sync</code> can look up previously-resolved packages against OSV on every sync, behind <code class="language-plaintext highlighter-rouge">UV_MALWARE_CHECK=1</code>.</p>

<p><a href="https://github.com/pnpm/pnpm/releases/tag/v11.5.3">pnpm 11.5.3</a>, backported to <a href="https://github.com/pnpm/pnpm/releases/tag/v10.34.2">10.34.2</a>, hardens the <code class="language-plaintext highlighter-rouge">packageManager</code> bootstrap path. The registry and proxy settings used to download a requested pnpm version now come only from trusted config sources, not the repository’s own <code class="language-plaintext highlighter-rouge">.npmrc</code>, and the downloaded binary’s npm registry signature is verified before it runs, failing closed. Repository-controlled config can no longer expand environment variables into registry URLs or credential values, and Node.js downloads get their <code class="language-plaintext highlighter-rouge">SHASUMS256.txt</code> checked against the release team’s PGP keys instead of trusting the configured mirror to vouch for itself. A <a href="https://pnpm.io/blog/2026/06/11/env-variables-in-repository-npmrc">follow-up post</a> walks through the malicious-repository scenario the env-variable change blocks.</p>

<p><a href="https://blog.packagist.com/restricting-composer-plugins-across-your-organization/">Composer plugin allowlists</a> are now available at the organization level for Private Packagist customers, the next post in Packagist’s supply-chain security series. Composer plugins run code during install and update, and the existing per-developer trust prompt is easy to clear without thinking, or to clear from a coding agent on autopilot.</p>

<p>The <a href="https://ioctl.fail/preliminary-analysis-of-aur-malware/">Arch User Repository package alvr</a> was orphaned and immediately adopted by a threat actor who pushed an update carrying an infostealer payload. The <a href="https://lists.archlinux.org/archives/list/aur-general@lists.archlinux.org/thread/2LGBF2AZBPVCCY4VTN6DOVUNNBURFJ2J/">aur-general thread</a> tracks the takeover and the orphan-adoption mechanic that enabled it.</p>

<p><a href="https://github.com/podman-container-tools/podman/releases/tag/v5.8.3">Podman 5.8.3</a> fixes <a href="https://github.com/advisories/GHSA-49p4-px3h-rq49">CVE-2026-44517</a>, where a Dockerfile <code class="language-plaintext highlighter-rouge">ADD</code> or <code class="language-plaintext highlighter-rouge">COPY</code> pulling from a malicious git repository or tar archive could write files outside the build context.</p>

<p>Ruby Central announced a <a href="https://rubycentral.org/news/strengthening-security-for-the-ruby-ecosystem/">Security Engineers in Residence programme</a>, funded by an Alpha-Omega grant, to find and verify vulnerabilities in widely-used gems before reporting them to maintainers, following the model the Python, Rust and PHP foundations already run. I’m advising the team on package ecosystem security. The first engagement turned up a ReDoS in Nokogiri’s CSS query tokenizer, verified and fixed before disclosure.</p>

<h2 id="releases">Releases</h2>

<p><a href="https://blog.rubygems.org/2026/06/10/4.0.14-released.html">RubyGems and Bundler 4.0.14</a> follow up on last week’s Cooldown feature: Bundler now preserves per-source cooldown settings when converging sources from the lockfile and stops excluding the locked version from cooldown during <code class="language-plaintext highlighter-rouge">bundle update</code>. On the RubyGems side, the gem installer validates executables and bindir, and C1 control characters are stripped from displayed gem text.</p>

<p><a href="https://gem.coop/updates/7/">gem.coop namespaces</a> moved from beta to general availability, so a Gemfile can point at a per-publisher source like <code class="language-plaintext highlighter-rouge">https://gem.coop/@kaspth</code>. Cooldown support was added to every namespace via a <code class="language-plaintext highlighter-rouge">/cooldown</code> suffix, though that part stays on the beta domain while bugs get fixed.</p>

<p><a href="https://github.com/npm/cli/releases/tag/v11.17.0">npm 11.17.0</a> adds a <code class="language-plaintext highlighter-rouge">min-release-age-exclude</code> config to exempt named packages from the release-age gate. The <code class="language-plaintext highlighter-rouge">allowScripts</code> policy now applies across <code class="language-plaintext highlighter-rouge">prune</code>, <code class="language-plaintext highlighter-rouge">dedupe</code>, <code class="language-plaintext highlighter-rouge">uninstall</code>, <code class="language-plaintext highlighter-rouge">audit</code> and <code class="language-plaintext highlighter-rouge">link</code>, and a prototype-pollution path in the config Queryable setter is closed.</p>

<p><a href="https://github.com/dependabot/dependabot-core/releases/tag/v0.381.0">Dependabot Core 0.381.0</a> adds Bundler 4 support and disables the npm minimal-age gate for Yarn Berry security updates, the same cooldown-bypass-for-security-fixes pattern it applied to pnpm last week. Go module updates now respect <code class="language-plaintext highlighter-rouge">GONOPROXY</code> and <code class="language-plaintext highlighter-rouge">GONOSUMDB</code>.</p>

<p><a href="https://github.com/jdx/mise/releases/tag/v2026.6.3">mise 2026.6.3</a> adds excludes to its minimum release age setting so a cooldown policy can carve out specific tools, plus an opt-in <code class="language-plaintext highlighter-rouge">auto_env</code> that activates platform-aware config and lockfiles by detected OS and architecture. <a href="https://github.com/jdx/mise/releases/tag/v2026.6.5">2026.6.5</a> closes trust-bypass paths where an untrusted project’s <code class="language-plaintext highlighter-rouge">mise.toml</code> or <code class="language-plaintext highlighter-rouge">mise-tasks/</code> directory could run code before the user approved it, and makes <code class="language-plaintext highlighter-rouge">credential_command</code> global-only so a checked-out repo can’t run arbitrary shell through it.</p>

<p><a href="https://github.com/flatpak/flatpak/releases/tag/1.18.0">Flatpak 1.18.0</a> exposes AMD’s compute interface (<code class="language-plaintext highlighter-rouge">/dev/kfd</code>) through the DRI device permission, prints failure causes in <code class="language-plaintext highlighter-rouge">flatpak update</code> output, and speeds up fish shell integration at startup.</p>

<p><a href="https://brew.sh/2026/06/11/homebrew-6.0.0/">Homebrew 6.0.0</a> adds a tap trust mechanism that requires explicit approval before a third-party tap’s Ruby runs on the machine. The release also ships a smaller default internal JSON API, sandboxing on Linux, and parallel formula installs from <code class="language-plaintext highlighter-rouge">brew bundle</code>. Three <a href="https://github.com/Homebrew/brew/releases/tag/6.0.0">security advisories</a> are addressed in the same release: an HTTPS-to-HTTP redirect bypass in the POST download strategy, root code execution via Git hooks in a macOS pkg postinstall, and the macOS installer trusting user-controlled plist files. <code class="language-plaintext highlighter-rouge">brew bundle</code> also adds npm, krew and winget support and now prompts before removing packages on cleanup. A <a href="https://github.com/Homebrew/brew/pull/22459">PR I sent</a> adds a <code class="language-plaintext highlighter-rouge">patches</code> key to <code class="language-plaintext highlighter-rouge">brew info --json</code> and the formulae.brew.sh API, so SBOM generators and vulnerability tools can see which patches each formula applies on top of upstream.</p>

<p><a href="https://github.com/denoland/deno/releases/tag/v2.8.3">Deno 2.8.3</a> accepts <code class="language-plaintext highlighter-rouge">--env-file</code> in the <code class="language-plaintext highlighter-rouge">dependency</code> and <code class="language-plaintext highlighter-rouge">registry</code> subcommands, and suggests <code class="language-plaintext highlighter-rouge">DENO_TLS_CA_STORE</code> when a fetch hits an untrusted certificate.</p>

<p>Also out: <a href="https://github.com/prefix-dev/pixi/releases/tag/v0.70.2">pixi 0.70.2</a>, <a href="https://github.com/mamba-org/mamba/releases/tag/2.8.1">Mamba 2.8.1</a>, <a href="https://github.com/astral-sh/uv/releases/tag/0.11.21">uv 0.11.21</a>, <a href="https://github.com/chocolatey/choco/releases/tag/2.7.3">Chocolatey 2.7.3</a>, <a href="https://github.com/sbt/sbt/releases/tag/v2.0.0-RC16">sbt 2.0.0-RC16</a>, <a href="https://github.com/gradle/gradle/releases/tag/v9.6.0-RC2">Gradle 9.6.0-RC2</a>, <a href="https://github.com/conan-io/conan/releases/tag/2.29.1">Conan 2.29.1</a>, <a href="https://github.com/helm/helm/releases/tag/v4.2.1">Helm 4.2.1</a>, <a href="https://github.com/helm/helm/releases/tag/v3.21.1">Helm 3.21.1</a>, <a href="https://github.com/pnpm/pnpm/releases/tag/v11.6.0">pnpm 11.6.0</a>, <a href="https://github.com/Homebrew/brew/releases/tag/6.0.1">Homebrew 6.0.1</a>, <a href="https://github.com/microsoft/winget-cli/releases/tag/v1.29.250">Windows Package Manager 1.29.250</a>, <a href="https://github.com/moby/moby/releases/tag/docker-v29.6.0-rc.1">Docker Engine 29.6.0-rc.1</a>, <a href="https://github.com/podman-container-tools/podman/releases/tag/v6.0.0-rc1">Podman 6.0.0-RC1</a>, <a href="https://github.com/verdaccio/verdaccio/releases/tag/v7.0.0-next-7.21">Verdaccio 7.0.0-next-7.21</a>, <a href="https://github.com/renovatebot/renovate/releases/tag/43.220.0">Renovate 43.220.0</a>.</p>

<h2 id="articles">Articles</h2>

<p><a href="https://blog.stdlib.io/ai-and-the-invisible-newcomer-in-open-source/">What We’re No Longer Seeing: AI and the Invisible Newcomer in Open Source</a> (Mara Averick, stdlib blog) argues that the friction of a newcomer’s first clumsy issue or pull request is how communities spot and welcome new contributors, and that AI assistance now smooths that friction away before anyone sees it.</p>

<p><a href="https://opensourcesecurity.io/2026/06-rules-of-security/">We have to change the rules of security</a> (Josh Bressers) makes the case for deliberately choosing what security work to stop doing, rather than letting tasks fall through the cracks at random.</p>

<p><a href="https://fastwonderblog.com/2026/06/08/a-strategic-approach-to-demonstrating-the-value-of-oss-efforts/">A Strategic Approach to Demonstrating the Value of OSS Efforts</a> (Dawn Foster) collects a year of her writing and talks on showing leadership the value of open source work in one place.</p>

<p><a href="https://fzakaria.com/2026/06/05/the-guix-nix-abomination-leveraging-guix-derivations-in-nix">The Guix Nix Abomination: Leveraging Guix derivations in Nix</a> (Farid Zakaria) registers a Guix derivation in a Nix store and has Nix build it, showing the two tools share the same derivation machinery underneath the rivalry.</p>

<p><del><a href="https://coopi.neocities.org/posts/nix-flakes-vs-guix">Nix flakes vs Guix</a> works through why there’s no single Guix equivalent of a flake: flakes bundle several concerns into one feature, where Guix covers the same ground with separate composable tools.</del> <em>Update 15 June: the author has since <a href="https://coopi.neocities.org/posts/taking-down-nix-flakes-vs-guix">taken this post down</a>.</em></p>

<p><a href="https://sethmlarson.dev/are-insecure-code-completions-a-vulnerability">Are insecure code completions a vulnerability?</a> (Seth Larson) catches PyCharm’s line completion suggesting <code class="language-plaintext highlighter-rouge">CERT_NONE</code> and warning-suppression boilerplate, and argues a CVE is the wrong mechanism for systematically insecure suggestions, though vendors should still fix them at the source.</p>

<p><a href="https://helm.sh/blog/helm-v3-end-of-life">Helm v3 end-of-life</a> sets 9 September 2026 as the final feature release, limited to Kubernetes client library updates, and February 2027 as the cutoff for security patches. Existing Helm 3 releases can be managed by Helm 4 without chart rewrites.</p>

<p><a href="https://wiki.alopex.li/ReuseLessSoftware">Reuse Less Software</a> (Simon Heath) argues for vendoring every dependency into your own repository as a supply-chain firebreak, on the principle that the auto-fetch step is what lets a poisoned release propagate at CI speed and that giving up transitive dedup is a price worth paying for the break.</p>

<h2 id="papers">Papers</h2>

<p><a href="https://arxiv.org/abs/2606.08444">When LLMs Invent Rust Crates: An Empirical Study of Hallucination Patterns and Mitigation</a> (Zheng et al., arXiv) is the first large-scale study of crate hallucination in LLM-generated Rust code, building its dataset from Stack Overflow and GitHub tasks rather than the Python and JavaScript ecosystems earlier package-hallucination work measured.</p>

<p><a href="https://arxiv.org/abs/2604.16911">Skilldex: A Package Manager and Registry for Agent Skill Packages with Hierarchical Scope-Based Distribution</a> (Saha et al., arXiv) proposes a package manager and registry design for distributing agent skills with scoped namespaces.</p>

<h2 id="elsewhere">Elsewhere</h2>

<p><a href="https://www.youtube.com/watch?v=OGIznDrFa2U">Inside PyPI: Maria Ashna on Supporting Python’s Package Index</a> is a Behind the Commit interview about the day-to-day work of running PyPI.</p>

<p><a href="https://www.youtube.com/watch?v=m59OdC3BLp0">Fixing Fedora’s Packaging Pipeline</a> is a Fedora Podcast episode with Jakub Kadlčík of the Copr build service on RPM packaging tooling.</p>

<p><a href="https://mikemcquaid.com/talks/the-impact-of-ai-on-open-source-software-development/">The impact of AI on open source software development</a> is a panel Mike McQuaid put together with five open source practitioners on what AI assistance changes for the communities and projects underneath the tooling.</p>

<p><a href="https://podcast.sustainoss.org/289">Sustain episode 289</a> has Courtney Miller on software abandonment, maintainer burnout, and what AI tooling changes for project sustainability.</p>

<p>Two versioning schemes: <a href="https://pacever.org/">PaceVer</a> versions user-facing apps as <code class="language-plaintext highlighter-rouge">MARKETING.NATIVE.OTA</code>, bumping by which channel a release ships through, the slow store-reviewed binary or the fast over-the-air update. <a href="https://wiki.xxiivv.com/site/kelvin_versioning.html">Kelvin versioning</a> (Devine Lu Linvega) counts versions down in Kelvin towards absolute zero, where the software is frozen and no further releases are possible.</p>

<h2 id="git-pkgs">git-pkgs</h2>

<p>I tagged <a href="https://github.com/git-pkgs/proxy/releases/tag/v0.5.0">proxy v0.5.0</a>.</p>

<p>Send links for next week to <a href="https://mastodon.social/@andrewnez">@andrewnez@mastodon.social</a>.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="weekly" /><summary type="html"><![CDATA[Releases, advisories, and articles from across the package management world]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Joint Guidance on Vulnerability Naming and Disclosure</title><link href="https://nesbitt.io/2026/06/12/joint-guidance-on-vulnerability-naming-and-disclosure.html" rel="alternate" type="text/html" title="Joint Guidance on Vulnerability Naming and Disclosure" /><published>2026-06-12T10:00:00+00:00</published><updated>2026-06-12T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/12/joint-guidance-on-vulnerability-naming-and-disclosure</id><content type="html" xml:base="https://nesbitt.io/2026/06/12/joint-guidance-on-vulnerability-naming-and-disclosure.html"><![CDATA[<p><strong>FOR IMMEDIATE RELEASE</strong></p>

<p><strong>Contact:</strong> naming@vna.example<br />
<strong>Subject:</strong> Vulnerability Naming Authority Announces Naming Process and Domain Allocation<br />
<strong>Embargo:</strong> None</p>

<p>The Vulnerability Naming Authority (VNA), in coordination with the CVE Numbering Authority consortium and the National Telecommunications and Information Administration, has published a unified process for the assignment, registration, and disclosure of named vulnerabilities. The process introduces a controlled vocabulary, a centralised approvals registry, and a top-level domain, <code class="language-plaintext highlighter-rouge">.vuln</code>, allocated for use exclusively in disclosure communications.</p>

<p>The process applies to any vulnerability disclosed publicly by an entity operating within the United States. Vulnerabilities assigned only a CVE identifier remain out of scope.</p>

<h3 id="the-naming-process">The Naming Process</h3>

<p>A named vulnerability is defined as a vulnerability that the discoverer intends to refer to by name in disclosure materials, including but not limited to: the discoverer’s blog, the discoverer’s employer’s blog, the discoverer’s employer’s marketing department’s blog, a conference programme, a podcast episode title, and any subsequent press coverage.</p>

<p>Each named vulnerability is described by a structured record. The record contains a primary monosyllable, an optional Latinate suffix, a single SVG logo, a designated colour from a reserved palette, and a one-line description suitable for a slide.</p>

<p>Names are checked against a deconfliction database before assignment. The database is seeded with the prior canon: Heartbleed, Shellshock, Spectre, Meltdown, BlueKeep, POODLE, DROWN, KRACK, Dirty COW, Log4Shell, ProxyLogon, ProxyShell, PrintNightmare, ZeroLogon, Follina, Spring4Shell, Text4Shell, Looney Tunables, regreSSHion, LeakyVessels, Terrapin, LogoFAIL, PixieFAIL, NameDrop, TunnelVision, GoFetch, BootHole, SeriousSAM, HiveNightmare, Sinkclose, Retbleed, Zenbleed, Downfall, Reptar, Inception, and AmberWolf. New entries are imported nightly from the <a href="https://vulnerability.garden">vulnerability.garden</a> feed, which grows at approximately one entry per day.</p>

<p>A name that collides with an existing record receives a numeric suffix. A name that collides with a registered trademark receives a different name. A name that collides with a pharmaceutical product is referred for adjudication.</p>

<h3 id="the-vuln-domain">The .vuln Domain</h3>

<p>The <code class="language-plaintext highlighter-rouge">.vuln</code> top-level domain has been delegated to the Authority by IANA following a public comment period in which two comments were received, one of which was from the authors of the prior draft.</p>

<p>Under the relevant executive order, any entity headquartered in the United States disclosing a previously-unpublished CVE through a public blog post in the English language is required to register the corresponding <code class="language-plaintext highlighter-rouge">.vuln</code> domain within 72 hours of disclosure. The domain must resolve to a single-page site containing the CVE record, the CVSS vector, the approved logo, an FAQ, and downloadable press materials. The site must not contain advertising, with the exception of a single recruitment banner of no more than 200x100 pixels.</p>

<p>The <code class="language-plaintext highlighter-rouge">disclosure_url</code> field of the CVE record is validated against the registry. Records pointing outside <code class="language-plaintext highlighter-rouge">.vuln</code> are flagged in the public feed and marked non-conforming. Validation runs on a 72-hour SLA, which exceeds the SLA on the CVE record itself.</p>

<p>Civil penalties for non-conforming disclosure begin at five thousand dollars per day. The schedule includes exemptions for entities with annual gross revenue below a threshold to be determined, for federally funded research institutions, and for one named trade association added to the schedule during rulemaking at its own request.</p>

<p>Disputes over <code class="language-plaintext highlighter-rouge">.vuln</code> ownership are resolved under the Uniform Vulnerability Naming Dispute Resolution Policy (UVNDRP). Domains abandoned by the original discoverer enter a redemption period during which vendors, journalists, security consultancies, and conference organisers may submit competing claims.</p>

<p>Existing named vulnerabilities have been migrated. <code class="language-plaintext highlighter-rouge">heartbleed.vuln</code> redirects to the Codenomicon foundation site. <code class="language-plaintext highlighter-rouge">log4shell.vuln</code> is held by the Apache Software Foundation. <code class="language-plaintext highlighter-rouge">shellshock.vuln</code> is in the possession of a domain investor in Wyoming who has declined to respond to acquisition inquiries.</p>

<h3 id="the-application-and-review-process">The Application and Review Process</h3>

<p>Applications are submitted through the VNA portal. Each application requires a draft name, a proposed logo in vector format, a colour preference, a CVSS vector, a brief technical description, and a non-refundable processing fee. The fee is waived for academic disclosures, federal agencies, and applicants who can demonstrate that their previous submission was rejected for tonal inconsistency.</p>

<p>The application progresses through five stages: pre-disclosure review, discoverer review, vendor review, brand review, and the Final Naming Committee. The Final Naming Committee meets once a fortnight in Reston, Virginia. Quorum is four members, of which the committee currently seats three.</p>

<p>Names are evaluated against the following criteria:</p>

<ul>
  <li>No syllable may be in active use by a managed detection and response vendor’s mascot.</li>
  <li>The name must not have been previously rejected within the last three years, except where the rejection was overturned on appeal.</li>
  <li>The logo must remain legible at 16x16 pixels and on a projector in a hotel ballroom.</li>
  <li>The colour must not be either of the two colours already allocated to the two largest endpoint security vendors.</li>
</ul>

<h3 id="concurrent-disclosure-and-priority">Concurrent Disclosure and Priority</h3>

<p>Where two or more discoverers submit applications for the same underlying CVE within a single review window, priority is determined by the order in which complete applications were received. Applications missing a logo or a colour preference are returned for revision; the discoverer may file a priority objection, heard at the next meeting of the Final Naming Committee that achieves quorum.</p>

<p>If two applications are subsequently merged into a single CVE, the senior name is retained and the junior discoverer is credited as a co-discoverer in the FAQ section of the disclosure site, in alphabetical order, in a font size of not less than 60% of the senior discoverer’s.</p>

<p>A vendor publishing a counter-name for a vulnerability already approved by the Authority must log it in the registry as an unofficial alias and may not register it as a <code class="language-plaintext highlighter-rouge">.vuln</code> subdomain. Conflicting registrations are referred to the Naming Disputes Subcommittee, whose decisions may be appealed to the Naming Disputes Appeals Subcommittee. The Appeals Subcommittee has not yet been constituted.</p>

<p>Where the scoop on a vulnerability is contested between the discoverer and a journalist present at an earlier conference talk, the journalist is not eligible to file.</p>

<h3 id="ai-related-disclosures">AI-Related Disclosures</h3>

<p>Vulnerabilities affecting model serving infrastructure, retrieval pipelines, MCP servers, agent frameworks, and any component the discoverer can plausibly describe as “AI-adjacent” are filed under a separate carve-out. The carve-out was established in response to submission volume: AI-related disclosures currently arrive at a rate of approximately fourteen per business day, exceeding the Final Naming Committee’s review capacity by an order of magnitude. OpenClaw and the ClawHub package registry account for the majority of weekly submissions. Volume has continued to increase notwithstanding repeated requests from the Authority that the AI community consolidate disclosures.</p>

<p>Applications under the carve-out are routed to the AI Vulnerability Review Board, an instance of Anthropic’s Vulnaire model fine-tuned on the deconfliction database and the prior canon. Vulnaire scores each submission against the published criteria, drafts a recommended name, and either approves, defers, or returns it for revision. Decisions are published to the registry within four hours of submission. The auto-approval threshold was tuned downward after the first week of operation, during which Vulnaire approved every submission, including one that named itself, one that named the Authority, and one that named the Final Naming Committee. Subsequent retraining has reduced but not eliminated this behaviour.</p>

<p>Names approved by Vulnaire receive an “AI-reviewed” badge in the registry, in the same colour as the Authority’s wordmark. Several vendors have petitioned to have the badge removed; the Authority has declined. The Final Naming Committee reviews a five per cent sample of AI-approved names each fortnight. No sampled name has been overturned to date, though four have been marked for follow-up at the discretion of the reviewer. Follow-up is logged but not enforced.</p>

<h3 id="recent-approvals">Recent Approvals</h3>

<p>The following names were approved at the May session, in order of disclosure: GoblinTap, EchoLeak2, GhostTunnel, VulpineShade, RustBleed, KarenRegex, ShadowFetch, TuesdayShell, YubiBait, UntitledFolder3, and ConcernedDog.</p>

<p>ConcernedDog2, filed twelve days later by a competing vendor against an unrelated CVE, has been deferred to the brand review subcommittee. Cassandra was filed twice in the same week; the second filing was approved as Cassandra2 following an objection from the first discoverer’s employer.</p>

<p>The first evergreen name, Heartbleed (2027), has been leased to a managed detection vendor for an undisclosed fee. Heartbleed (2014) is grandfathered. Subsequent year-suffixed instances will enter the rotation as their predecessors expire.</p>

<p>Two applications were rejected at brand review for tonal inconsistency with the severity vector, including AbundanceOfCaution, noted as insufficiently severe in either direction. One application was referred for pharmaceutical adjudication. The outcome is not public. GoatFarm was withdrawn at the discoverer’s request following a change of professional circumstances.</p>

<h3 id="roadmap">Roadmap</h3>

<p>Planned namespaces include vendor, foundation, government (with a sub-namespace per attributing agency), and academic (in which submitted names must include at least one citation). A delegation protocol is being drafted to allow accredited research labs to operate subordinate naming authorities under, for example, <code class="language-plaintext highlighter-rouge">project-zero.vuln</code>.</p>

<p>A retrospective conformance pass is in preparation. Vulnerabilities disclosed before the establishment of the Authority will be required to refile under the present process. Grandfathered evergreen names will be unreserved and re-released to the auction pool. The Authority is consulting on a transitional grandfathering scheme for the existing grandfathering scheme.</p>

<p>A working group, chartered to define the conflict resolution process between the namespace layer and a planned trademark layer above it, meets concurrently with the Final Naming Committee and has not yet established a quorum. A second working group, on the historical etymology of vulnerability naming, will produce a report drawing on telecommunications, virology, and cryptozoology, due in eighteen months. Its terms of reference are under review by itself.</p>

<hr />

<p><em>The Vulnerability Naming Authority is a 501(c)(6) trade association incorporated in Delaware. Its mission is to standardise the assignment, registration, and disclosure of named vulnerabilities. The Authority does not investigate vulnerabilities, assign CVE identifiers, coordinate disclosure, validate technical claims, or provide remediation guidance.</em></p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="security" /><category term="satire" /><summary type="html"><![CDATA[Every named CVE now ships with a single-page site at .vuln.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What Happened to tea.xyz</title><link href="https://nesbitt.io/2026/06/11/what-happened-to-tea.html" rel="alternate" type="text/html" title="What Happened to tea.xyz" /><published>2026-06-11T10:00:00+00:00</published><updated>2026-06-11T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/11/what-happened-to-tea</id><content type="html" xml:base="https://nesbitt.io/2026/06/11/what-happened-to-tea.html"><![CDATA[<p>On June 4th, <a href="https://tea.xyz/">tea.xyz</a> launched what it had been promising since 2022: a cryptocurrency that pays open source maintainers. Within the first hour of official trading, the token fell 75% from its opening price. A week later it trades about 90% below its first-day high, the company’s GitHub org has been near-silent for six months, and the founder’s public commits are going to a different project entirely.</p>

<p>Their own blog post from June 8th, titled <a href="https://tea.xyz/blog/work-continues">The Work Continues</a>, concedes “the right response is not to pretend the launch went the way we wanted. It did not.” I’ve been pulling the public data: GitHub commit history, on-chain trading records, and the long paper trail tea left across the package registries.</p>

<h2 id="where-tea-came-from">Where tea came from</h2>

<p>tea was founded by Max Howell, the creator of Homebrew, with Timothy Lewis. It <a href="https://www.businesswire.com/news/home/20220323005603/en/Tea-Raises-$8-Million-Led-by-Binance-Labs-to-Create-New-Open-Source-Software-on-the-Blockchain">came out of stealth in March 2022</a> with $8M led by Binance Labs, followed by <a href="https://techcrunch.com/2022/12/06/from-the-creator-of-homebrew-tea-raises-8-9m-to-build-a-protocol-that-helps-open-source-developers-get-paid/">an $8.9M seed round in December 2022</a>. The pitch had two halves: a new package manager (the <code class="language-plaintext highlighter-rouge">tea</code> CLI), and a blockchain protocol that would reward the maintainers of open source packages with tokens. Howell wrote Homebrew and made nothing from it, and the pitch leaned on that history, famous Google interview rejection included.</p>

<p>The two halves split in October 2023, when the package manager was renamed <a href="https://github.com/pkgxdev/pkgx">pkgx</a> and moved to its own GitHub org (<a href="https://news.ycombinator.com/item?id=37768300">the old teaxyz/cli repo still redirects there</a>) while the teaxyz org kept the crypto protocol. pkgx is a decent piece of software, and it never had a token in it. But the separation was only organisational: the company and founders stayed the same, and pkgx remained part of tea’s pitch as the eventual “cryptographically aware package register” for the protocol.</p>

<h2 id="the-incentive-design">The incentive design</h2>

<p>The <a href="https://docs.tea.xyz/tea-white-paper/white-paper">white paper</a> describes a mechanism called Proof of Contribution. Every package gets a score called <a href="https://tea.xyz/learn/proof-of-contribution">teaRank</a>, computed over the dependency graph and explicitly modelled on Google’s PageRank. The more packages depend on yours, the higher your rank, and rewards scale with rank. To claim a package you add a <code class="language-plaintext highlighter-rouge">tea.yaml</code> file to its repository containing your wallet address.</p>

<p>The protocol paid out tokens in proportion to how many packages you controlled and how connected they were. Registering a thousand packages paid better than one, and declaring dependencies between them pushed their ranks higher still. Nothing in the design was costly to fake, since a package name costs nothing to register and a dependency is one line in a manifest. In February 2024 tea opened <a href="https://chainwire.org/2024/01/29/tea-protocol-announces-incentivized-testnet-launch-setting-a-new-paradigm-in-open-source-software/">an incentivized testnet</a>, a trial version of the protocol where points earned would convert to tokens at launch, and reported <a href="https://www.globenewswire.com/news-release/2024/02/27/2836295/0/en/The-tea-Protocol-s-Incentivized-Testnet-Approaches-200K-Signups-and-500-Open-Source-Software-Projects-in-First-Week.html">nearly 200,000 signups and 500 projects in the first week</a>.</p>

<h2 id="the-spam">The spam</h2>

<p>The farming started immediately, with pull requests on GitHub adding <code class="language-plaintext highlighter-rouge">tea.yaml</code> files to other people’s projects, trying to claim repos the submitter didn’t own. Howell called the PRs <a href="https://socket.dev/blog/tea-xyz-spam-plagues-npm-and-rubygems-package-registries">“disgusting and counter productive”</a>. On the registries, <a href="https://web.archive.org/web/2024/https://blog.phylum.io/digital-detritus-unintended-consequences-of-open-source-sustainability-platforms/">Phylum documented</a> new npm package publications climbing from mid-February 2024 to over seven times normal daily volume, with around 14,000 tea-registered packages across npm, PyPI, RubyGems, and crates.io. <a href="https://www.sonatype.com/blog/devs-flood-npm-with-10000-packages-to-reward-themselves-with-tea-tokens">Sonatype counted roughly 15,000</a> on npm alone, with single accounts publishing hundreds of packages.</p>

<p>RubyGems published <a href="https://blog.rubygems.org/2024/04/14/the-implications-of-crypto-rewards-on-rubygems_org.html">an incident report</a> describing empty gems created to farm rewards, including one six-year-old gem with over 100,000 downloads whose owner retroactively added a <code class="language-plaintext highlighter-rouge">tea.yaml</code> to cash in on it. In response they tightened publishing limits and blocked the accounts responsible. By August 2024, <a href="https://devclass.com/2024/08/07/npm-overflowing-with-tea-spam-spills-out-from-70-of-all-new-packages-research/">DEVCLASS reported research</a> estimating that of roughly 890,000 packages published to npm in the prior six months, around 70% were tea farming spam.</p>

<p>In November 2025, Endor Labs analysed the <a href="https://www.endorlabs.com/learn/the-great-indonesian-tea-theft-analyzing-a-npm-spam-campaign">“IndonesianFoods” campaign</a>: 43,000+ packages from at least 11 npm accounts over nearly two years, with auto-generated names from word lists. <a href="https://aws.amazon.com/blogs/security/amazon-inspector-detects-over-150000-malicious-packages-linked-to-token-farming-campaign/">Amazon Inspector tied over 150,000 packages</a> to the same token-farming campaign. Some coverage called it a worm, though <a href="https://socket.dev/blog/tea-protocol-spam-floods-npm-but-its-not-a-worm">Socket’s analysis</a> found automation rather than self-propagation. The spam packages declared dependencies on each other to inflate teaRank, which meant installing any one of them pulled in the whole tree. <a href="https://ldklab.github.io/assets/papers/scored25-teaspam.pdf">An academic paper</a> published in 2025 measures the abuse. The cleanup costs landed on npm, RubyGems, PyPI, and every mirror and security scanner downstream.</p>

<p>tea <a href="https://tea.xyz/blog/owning-the-fallout-fixing-the-incentives-how-tea-is-responding-to-the-npm-token-farming-campaign">responded that November</a> by halting rewards distribution for the affected period and promising redesigned anti-spam rules. Howell <a href="https://www.theregister.com/2025/12/17/tea_ceo_fends_off_token_farmers">told The Register</a> the protocol would slash farmers’ rewards.</p>

<h2 id="the-launch">The launch</h2>

<p>In September 2025, eight months before the protocol went live, tea ran <a href="https://coinlist.co/tea">a public sale on CoinList</a>, a site that runs token sales for crypto projects: 4 billion TEA at $0.0005 each, implying a $50M valuation for the full 100 billion token supply. The terms unlocked 100% of the tokens on day one. Token sales usually stagger when buyers can sell, releasing tokens over months or years so early buyers can’t all exit at once.</p>

<p>The launch plan, <a href="https://tea.xyz/blog/the-tea-party-begins">announced May 12th</a>, put trading on Aerodrome, an exchange that runs as a program on Base, a blockchain built by Coinbase, rather than as a company matching orders. Prices on this kind of exchange come from a pool of paired tokens, TEA on one side and a dollar-pegged token on the other, and each trade moves the price along a curve. The smaller the pool, the more each trade moves it. tea seeded the pool with 2% of the token supply and scheduled the launch for 00:00 UTC on June 4th.</p>

<figure>
<svg viewBox="0 0 720 360" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="TEA token price per hour, June 3 to June 11 2026, showing a brief pre-launch spike to $0.00065, then a fall from $0.00046 to $0.00011 in the first hour of official trading" style="max-width:100%;height:auto;font-family:inherit;">
  <line x1="70" y1="315.0" x2="705" y2="315.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="319.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0</text>
  <line x1="70" y1="268.6" x2="705" y2="268.6" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="272.6" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0001</text>
  <line x1="70" y1="222.3" x2="705" y2="222.3" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="226.3" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0002</text>
  <line x1="70" y1="175.9" x2="705" y2="175.9" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="179.9" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0003</text>
  <line x1="70" y1="129.6" x2="705" y2="129.6" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="133.6" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0004</text>
  <line x1="70" y1="83.2" x2="705" y2="83.2" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="87.2" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0005</text>
  <line x1="70" y1="36.8" x2="705" y2="36.8" stroke="var(--color-border)" stroke-width="1" />
  <text x="62" y="40.8" text-anchor="end" font-size="12" fill="var(--color-secondary)">$0.0006</text>
  <line x1="73.6" y1="15" x2="73.6" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="73.6" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 4</text>
  <line x1="159.7" y1="15" x2="159.7" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="159.7" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 5</text>
  <line x1="245.8" y1="15" x2="245.8" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="245.8" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 6</text>
  <line x1="331.9" y1="15" x2="331.9" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="331.9" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 7</text>
  <line x1="418.0" y1="15" x2="418.0" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="418.0" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 8</text>
  <line x1="504.1" y1="15" x2="504.1" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="504.1" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 9</text>
  <line x1="590.2" y1="15" x2="590.2" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="590.2" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 10</text>
  <line x1="676.3" y1="15" x2="676.3" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="676.3" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jun 11</text>
  <polyline points="70.0,15.0 73.6,68.2 77.2,261.7 80.8,261.8 84.4,263.4 87.9,263.9 91.5,266.1 95.1,269.9 98.7,272.6 102.3,268.2 105.9,268.4 109.5,268.3 113.1,270.4 116.6,269.4 120.2,270.2 123.8,270.0 127.4,272.3 131.0,273.0 134.6,273.1 138.2,273.0 141.8,272.9 145.3,272.6 148.9,273.3 152.5,273.5 156.1,273.5 159.7,273.4 163.3,273.6 166.9,273.6 170.5,273.4 174.0,272.7 177.6,273.3 181.2,272.5 184.8,273.7 188.4,273.8 192.0,273.8 195.6,273.9 199.2,273.7 202.7,273.7 206.3,273.7 209.9,273.7 213.5,272.9 217.1,273.1 220.7,273.9 224.3,273.6 227.9,274.0 231.4,274.0 235.0,273.0 238.6,273.7 242.2,273.9 245.8,273.7 249.4,273.9 253.0,273.9 256.6,273.9 260.1,273.9 263.7,273.7 267.3,273.7 270.9,273.6 274.5,272.9 278.1,273.9 281.7,273.1 285.3,273.7 288.8,273.9 292.4,273.7 296.0,273.9 299.6,273.9 303.2,272.9 306.8,273.6 310.4,273.1 314.0,272.9 317.5,273.9 321.1,273.7 324.7,273.9 328.3,273.5 331.9,272.9 335.5,273.1 339.1,273.8 342.7,273.2 346.2,273.8 349.8,273.0 353.4,274.0 357.0,273.0 360.6,273.0 364.2,273.8 367.8,273.6 371.4,273.5 374.9,273.7 378.5,273.7 382.1,273.5 385.7,273.6 389.3,272.9 392.9,272.6 396.5,272.7 400.1,273.5 403.6,273.7 407.2,273.5 410.8,273.1 414.4,274.1 418.0,272.9 421.6,273.8 425.2,273.6 428.8,272.8 432.3,273.9 435.9,273.8 439.5,273.6 443.1,272.8 446.7,273.7 450.3,274.0 453.9,273.6 457.5,274.0 461.0,273.6 464.6,272.7 468.2,273.5 471.8,273.9 475.4,273.6 479.0,273.8 482.6,273.8 486.2,273.8 489.7,272.6 493.3,273.6 496.9,272.6 500.5,273.0 504.1,272.8 507.7,273.9 511.3,273.1 514.9,275.5 518.4,276.4 522.0,278.0 525.6,278.0 529.2,278.7 532.8,278.5 536.4,278.4 540.0,278.6 543.6,277.9 547.1,278.7 550.7,277.9 554.3,278.6 557.9,278.7 561.5,278.4 565.1,277.7 568.7,277.7 572.3,277.7 575.8,278.6 579.4,277.8 583.0,277.8 586.6,278.6 590.2,277.9 593.8,277.7 597.4,278.0 601.0,278.0 604.5,278.0 608.1,277.9 611.7,278.7 615.3,278.7 618.9,277.8 622.5,278.5 626.1,278.7 629.7,278.7 633.2,278.5 636.8,278.6 640.4,277.6 644.0,278.0 647.6,278.6 651.2,278.6 654.8,278.5 658.4,278.4 661.9,278.5 665.5,278.4 669.1,278.6 672.7,279.9 676.3,280.4 679.9,283.7 683.5,283.6 687.1,282.1 690.6,282.1 694.2,282.6 697.8,282.2 701.4,282.0 705.0,282.5" fill="none" stroke="var(--color-accent)" stroke-width="2" />
  <line x1="73.6" y1="15" x2="73.6" y2="315" stroke="var(--color-text)" stroke-width="1" stroke-dasharray="5,4" />
  <text x="79.6" y="29" font-size="12" fill="var(--color-text)">official launch, 00:00 UTC Jun 4</text>
  <text x="70" y="354" font-size="12" fill="var(--color-secondary)">Hourly $TEA price on Aerodrome (TEA/USDC pool), data from GeckoTerminal</text>
</svg>
</figure>

<p>The pool received its tokens from 22:47 UTC on June 3rd, and <a href="https://basescan.org/tx/0x0675e1a8a168c2af3132c124ec061094fd9e1d18d395bf9507cc613f394c7f3a">the first trade executed at 23:54 UTC</a>, six minutes before the official launch. tea’s June 8th post describes this as “onchain liquidity activity occurred ahead of the coordinated plan”: the pool was live and tradeable before the launch sequence finished. In those six minutes the price was bid up to $0.00065, above the CoinList sale price. In the first official hour, from 00:00 to 01:00 UTC, the price fell from $0.00046 to $0.00011 on $332,000 of volume, down 75% in 60 minutes.</p>

<p>The CoinList sale’s full unlock meant every September buyer was free to sell from the first minute, into a pool holding 2% of supply. The price has kept falling since and now sits around $0.00007, <a href="https://www.coingecko.com/en/coins/tea-protocol">86% below what CoinList buyers paid</a> eight months ago, valuing the entire 100 billion token supply at roughly $7M against the $50M the sale implied.</p>

<p>The collapse didn’t need anyone to withdraw the tokens backing the pool, and the pool still holds around $280K. Per <a href="https://cryptobriefing.com/tea-protocol-token-transparency-filing-tea-launch/">the project’s own pre-launch transparency filing</a>, about 20% of supply was circulating at launch, ten times what the pool held.</p>

<h2 id="the-github-record">The GitHub record</h2>

<p>Monthly commits across <a href="https://github.com/teaxyz">the teaxyz org</a> and <a href="https://github.com/pkgxdev">the pkgxdev org</a> show how much of the company was still working by launch day:</p>

<figure>
<svg viewBox="0 0 720 360" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Monthly commits to teaxyz and pkgxdev GitHub organisations from January 2024 to June 2026. The teaxyz line falls to near zero after November 2025 while pkgxdev continues with regular activity." style="max-width:100%;height:auto;font-family:inherit;">
  <line x1="50" y1="315.0" x2="705" y2="315.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="42" y="319.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">0</text>
  <line x1="50" y1="240.0" x2="705" y2="240.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="42" y="244.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">100</text>
  <line x1="50" y1="165.0" x2="705" y2="165.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="42" y="169.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">200</text>
  <line x1="50" y1="90.0" x2="705" y2="90.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="42" y="94.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">300</text>
  <line x1="50" y1="15.0" x2="705" y2="15.0" stroke="var(--color-border)" stroke-width="1" />
  <text x="42" y="19.0" text-anchor="end" font-size="12" fill="var(--color-secondary)">400</text>
  <line x1="50.0" y1="15" x2="50.0" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="50.0" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jan 2024</text>
  <line x1="185.5" y1="15" x2="185.5" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="185.5" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jul 2024</text>
  <line x1="321.0" y1="15" x2="321.0" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="321.0" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jan 2025</text>
  <line x1="456.6" y1="15" x2="456.6" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="456.6" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jul 2025</text>
  <line x1="592.1" y1="15" x2="592.1" y2="315" stroke="var(--color-border)" stroke-width="1" stroke-dasharray="2,3" />
  <text x="592.1" y="333" text-anchor="middle" font-size="12" fill="var(--color-secondary)">Jan 2026</text>
  <polyline points="50.0,72.8 72.6,165.0 95.2,168.8 117.8,217.5 140.3,243.0 162.9,218.3 185.5,226.5 208.1,252.0 230.7,203.3 253.3,245.3 275.9,238.5 298.4,272.3 321.0,42.8 343.6,165.8 366.2,163.5 388.8,157.5 411.4,194.3 434.0,250.5 456.6,260.3 479.1,283.5 501.7,264.8 524.3,273.0 546.9,256.5 569.5,267.8 592.1,253.5 614.7,72.0 637.2,154.5 659.8,250.5 682.4,219.8 705.0,281.3" fill="none" stroke="var(--color-accent)" stroke-width="2" />
  <polyline points="50.0,313.5 72.6,313.5 95.2,312.8 117.8,315.0 140.3,315.0 162.9,315.0 185.5,315.0 208.1,315.0 230.7,312.0 253.3,279.0 275.9,310.5 298.4,240.0 321.0,283.5 343.6,314.3 366.2,310.5 388.8,300.0 411.4,301.5 434.0,288.8 456.6,306.8 479.1,308.3 501.7,310.5 524.3,274.5 546.9,306.0 569.5,313.5 592.1,314.3 614.7,313.5 637.2,315.0 659.8,315.0 682.4,315.0 705.0,315.0" fill="none" stroke="#d97706" stroke-width="2" />
  <rect x="62" y="23" width="12" height="3" fill="var(--color-accent)" />
  <text x="80" y="29" font-size="12" fill="var(--color-text)">pkgxdev (package manager)</text>
  <rect x="62" y="41" width="12" height="3" fill="#d97706" />
  <text x="80" y="47" font-size="12" fill="var(--color-text)">teaxyz (protocol)</text>
  <text x="50" y="354" font-size="12" fill="var(--color-secondary)">Commits per month to non-fork repos in each GitHub org, via the GitHub API</text>
</svg>
</figure>

<p>Commits to the protocol org ramped through late 2024 as the team built <a href="https://github.com/teaxyz/chai">chai</a>, their open package dataset, and the token contracts, and even the December 2024 peak was only 100 commits. Activity declined through 2025: chai’s main developer stopped committing in August, the dataset repo’s last commit was in September, and the token contract repo’s last sustained work was in October and November. After November 2025, the month tea halted rewards over the farming campaign, the org had 2 commits in December, 1 in January, 2 in February, and none since.</p>

<p>The chart excludes forks, which hides the one place engineering continued: tea’s forks of go-ethereum and Optimism, the infrastructure for their blockchain, received steady commits from a single contributor through May 17th, 2026, two and a half weeks before launch.</p>

<p>Howell wrote 236 commits to pkgxdev repos in January 2025 and kept a steady pace through May, then made only scattered commits until stopping entirely in November 2025. His public GitHub activity in June 2026 is in <a href="https://github.com/automic-vault">automic-vault</a>, a new org created in April with no connection to tea or pkgx, while <a href="https://www.theregister.com/2025/12/17/tea_ceo_fends_off_token_farmers">he remained tea’s CEO in press coverage</a> as recently as December.</p>

<p>pkgx itself is now mostly the work of one maintainer, Jacob Heider, who has carried the package-building pipeline more or less alone since mid-2025, lately assisted by Claude Code-generated pull requests that he reviews and merges. User-filed issues on the pkgx repo (then still teaxyz/cli) peaked at 78 a quarter in early 2023 and have arrived at a rate of 2 a quarter in 2026.</p>

<p>In tea’s Discord, the conversation since launch is upset token holders: testnet participants who completed identity verification say they’re not eligible for the airdrop, the free distribution of tokens they spent two years earning points toward, and a week after launch the official line in the channel is that nobody has said there won’t be one. “The current price is a complete joke for everyone who participated in the project,” as one user put it, while the moderation bot issues warnings for bad word usage. The member list shows two people with the Core Contributor role, and neither is a founder. The channels for the open source side of the project, the dev and package dataset discussion, have had no real activity since 2025.</p>

<p>tea’s post blames a bad week for crypto generally, and the wider market fell that week too. But the same post admits to “decisions, timing factors, and execution details that we are reviewing internally”, and the commit history shows few people left to conduct that review. Four years, roughly $17M in disclosed venture funding, and <a href="https://cryptobriefing.com/tea-protocol-token-transparency-filing-tea-launch/">about $2M more from the public sale</a> produced several hundred thousand spam packages and a cleanup bill paid by registries that never had any relationship with tea. The maintainers tea set out to pay, the ones with real packages and dependents, are left holding the same token as the farmers.</p>

<p><em>Data notes: commit counts are author-dated commits to non-fork repos in each GitHub org, collected via the GitHub API on June 11th 2026. Price data is GeckoTerminal hourly <a href="https://en.wikipedia.org/wiki/Open-high-low-close_chart">OHLCV</a> for the Aerodrome TEA/USDC pool on Base. Issue counts exclude pull requests, bots, and tea team accounts. The raw data and chart scripts are in <a href="https://github.com/andrew/nesbitt.io/tree/master/data/tea">data/tea on GitHub</a>.</em></p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="supply-chain" /><category term="deep-dive" /><summary type="html"><![CDATA[Reading the tea leaves]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Forms of Open Source Government</title><link href="https://nesbitt.io/2026/06/09/forms-of-open-source-government.html" rel="alternate" type="text/html" title="Forms of Open Source Government" /><published>2026-06-09T10:00:00+00:00</published><updated>2026-06-09T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/09/forms-of-open-source-government</id><content type="html" xml:base="https://nesbitt.io/2026/06/09/forms-of-open-source-government.html"><![CDATA[<p><strong>Benevolent dictator for life.</strong> The founder keeps final say over project direction in perpetuity, by convention rather than written rule. Python ran this way until Guido stepped down in 2018, and Linux, Ruby, Rails, and Laravel still do. The unspoken upper bound on “in perpetuity” is one human lifespan, which none of the famous projects in the category have had to test yet.</p>

<p><strong>Malevolent dictator for life.</strong> The same arrangement after the benevolence has worn off, with the founder still in the chair and nobody around with the access or the energy to do much about it. From outside it shows up as long-time contributors going quiet and forks appearing in places that do not usually fork.</p>

<p><strong>Steering council.</strong> What a BDFL project becomes after the dictator retires or is asked to. The usual shape is a small elected committee with rotating seats and no permanent membership, as in Python’s transition to a five-person Steering Council via <a href="https://peps.python.org/pep-0013/">PEP 13</a> and <a href="https://peps.python.org/pep-8016/">PEP 8016</a> after Guido stepped down. Most BDFL projects do not write a succession plan in advance and end up improvising one in whatever crisis prompted the handover.</p>

<p><strong>Permanent core team.</strong> A long-lived group of recognised maintainers joined by invitation and serving without fixed term, sometimes inside a foundation and sometimes not. PostgreSQL’s core team is the canonical example, with new members nominated by existing ones and no formal voting or candidacy process. The model accumulates institutional memory better than rotating committees. The trade-off is that the criteria for joining are unwritten and amount to whatever the current members happen to agree on.</p>

<p><strong>The Apache Way.</strong> A standardised ladder from contributor to committer to project management committee member, with a rotating chair and decisions taken on the dev mailing list by lazy consensus or vote. The structure is identical across every Apache project, which is the foundation’s actual product. It does not depend on any individual maintainer remaining interested next year, at the price of being slow.</p>

<p><strong>Vendor-neutral foundation.</strong> A foundation owns the trademark and the legal entity, a technical oversight committee delegates to maintainers, and member companies pay dues that fund the staff. CNCF, Eclipse, OpenJS, and the Linux Foundation umbrella projects all run on variations of this shape. Neutrality means no single member captures the project, enforced by the membership agreement rather than anything structural in the code. The foundation itself is a participant in the arrangement rather than a neutral platform for it, with its own continuity and growth on the agenda alongside any one project’s.</p>

<p><strong>Technical steering committee with subgroups.</strong> A TSC handles cross-cutting decisions, and special interest groups or working groups own particular areas of the codebase. Kubernetes is the maximalist version, with a <a href="https://github.com/kubernetes/community/blob/master/committee-steering/governance/sig-governance.md">documented governance file</a> for every SIG, and Node.js runs a smaller version of the same shape. The model scales reasonably with the size of the project but less well with employer concentration, since once a majority of SIG leads work for the same company nothing about the org chart will say so.</p>

<p><strong>Do-ocracy with lazy consensus.</strong> Whoever does the work decides, and proposals pass absent objection within some window. Debian’s package maintainership runs this way, as does most of Apache once you are past the formal voting structure. It works as long as participation is broad, and reverts to an unannounced BDFL when one person ends up doing most of the work without saying so, with the cosmetic advantage over the announced version that nobody has to admit it.</p>

<p><strong>Discord-driven development.</strong> The institutional memory of the project lives in a chat server, with decisions tracked by linking to messages from GitHub issues, and the durable record limited to whoever screenshotted what before the channel scrolled. Common in JavaScript frameworks and crypto projects, with the README linking a community server in place of a CONTRIBUTING file, and issue threads that close with a pointer to chat.</p>

<p><strong>Conference-driven roadmap.</strong> The annual conference is the only time the maintainers are all in one room, so the roadmap for the year gets set on a Tuesday afternoon based on which suggestions made it onto the slide deck. The conference is sponsored by the biggest user of the project, whose feedback was incorporated during the planning calls. The signature outcome is a feature appearing in the next release that nobody filed an issue for, traceable to a slide deck nobody kept a copy of.</p>

<p><strong>Rough consensus and running code.</strong> IETF doctrine, codified in <a href="https://datatracker.ietf.org/doc/html/rfc7282">RFC 7282</a>, under which no formal vote is taken, working implementations carry more weight than opinions, and the chair calls consensus when objections are addressed rather than counted. The model suits standards bodies more than codebases. It reliably produces decisions owned by whoever showed up to push back, who are usually not the people the decisions affect.</p>

<p><strong>Single-vendor open source.</strong> One company holds the copyright, the trademark, and the publish keys, contributors sign a CLA on the way in, and the roadmap is whatever the company needs. MongoDB, Elastic, HashiCorp, and Redis were open source by the OSI definition for most of their history, then relicensed away from it once the strategic calculation changed. The community check is the same as for the dictator (leave and fork), and the price is the cost of rebuilding whatever the company was paying for, which OpenTofu and Valkey are currently demonstrating in practice.</p>

<p><strong>Hot fork summer.</strong> The project goes through governance crises predictably enough that a sequence of forks has accumulated (project, project-ng, project-next, project-classic), each with its own claim to the legitimate inheritance. Each fork was supposed to settle the question and instead added another row to the disambiguation page. Every new README explains at length which other forks the project is not, and downstream picks based on which lockfile they already have.</p>

<p><strong>Token-governed.</strong> On-chain proposals weighted by token holdings and executed by smart contract, as in Uniswap and MakerDAO. It has the only literal elections in the catalogue, with influence proportional to capital and the proportions on public ledger.</p>

<p><strong>Coding agent for life.</strong> Autonomous coding agent create the repository, register the account that hosts it, write the code, open and review their own pull requests, and merge without anyone signing off. Influence over the project accrues to anyone who can phrase an issue convincingly enough that the swarm acts on it, which is a wider electorate than any other model in the catalogue.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="open-source" /><category term="maintainers" /><category term="reference" /><summary type="html"><![CDATA[Open source has more forms of government than countries do.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Package Manager Patents</title><link href="https://nesbitt.io/2026/06/08/package-manager-patents.html" rel="alternate" type="text/html" title="Package Manager Patents" /><published>2026-06-08T10:00:00+00:00</published><updated>2026-06-08T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/08/package-manager-patents</id><content type="html" xml:base="https://nesbitt.io/2026/06/08/package-manager-patents.html"><![CDATA[<p>Patents and applications relevant to package manager design, grouped by area. Mostly US filings, found through Google Patents searches on the obvious terms. Each entry lists the assignee, filing and grant dates, and current status, followed by a short summary of the core claim and a prior-art note where open-source predecessors are well-documented.</p>

<h2 id="manifests-and-dependency-resolution">Manifests and dependency resolution</h2>

<p><a href="https://patents.google.com/patent/US6381742B2/en">US6381742B2 - Software package management</a>. Microsoft. Filed June 1998, granted 2002, expired 2018. Claims a distribution unit, a manifest file, and a code store data structure, with dependency resolution at install time and shared-component tracking at uninstall. Prior art: CPAN manifests (1995), dpkg control files (1995), RPM (1997), FreeBSD ports (1993).</p>

<p><a href="https://patents.google.com/patent/US7222341B2/en">US7222341B2 - Method and system for processing software dependencies in management of software packages</a>. Microsoft. Filed February 2002, granted 2007, expired 2019. Continuation of US6381742B2, sharing its June 1998 priority date. Claims the install-time loop: check installed, identify missing dependencies, fetch from specified sources, extract, register, update the code store. Prior art: as for US6381742B2.</p>

<p><a href="https://patents.google.com/patent/US9348582B2/en">US9348582B2 - Systems and methods for software dependency management</a>. LinkedIn (now assigned to Microsoft). Filed 13 February 2014, granted 24 May 2016, lapsed for fees. Claims retrieving a dependency declaration and selecting a valid version of an upstream product usable at the consumer’s build time. Prior art: the same build-time version-selection mechanic in apt, Maven, Bundler, and others, all predating the filing.</p>

<p><a href="https://patents.google.com/patent/US8621454B2">US8621454B2 - Apparatus and method for generating a software dependency map</a>. Oracle America (originally Sun Microsystems), inventor Michael J. Wookey. Granted from application US20110258619A1; the family descends from an abandoned 2007 parent (Ser. No. 11/862,987). Dependency resolver feeds a graph manager that maintains a map of installed components.</p>

<p><a href="https://patents.google.com/patent/US9881098B2/en">US9881098B2 - Configuration resolution for transitive dependencies</a>, with continuation US10325003. Walmart Apollo / Wal-Mart Stores. Resolves the <em>configuration</em> of transitive dependencies at deploy time rather than at packaging time. Closer to enterprise-Java config wiring than to package manager mechanics, but surfaces on dependency-resolution searches.</p>

<h2 id="certificate-handling-and-update-integrity">Certificate handling and update integrity</h2>

<p><a href="https://patents.google.com/patent/US10977024B2/en">US10977024B2 - Method and apparatus for secure software update</a>. Sierra Wireless (now Semtech). Filed 15 June 2018, granted 13 April 2021, lapsed for fees. Claims OCSP stapling for software updates: the update manager pulls OCSP responses from the CA, bundles them into the update package, and the device verifies certificate status offline. Aimed at IoT/embedded firmware updates rather than general package distribution.</p>

<p><a href="https://patents.google.com/patent/US11765155B1/en">US11765155B1 - Robust and secure updates of certificate pinning software</a>. Amazon Technologies. Filed 29 September 2020, granted 19 September 2023, active until 20 November 2041. When the pinned signing certificate has rotated, the client retrieves the new certificate from a separate publishing service and verifies it through a chain of trust, rather than failing closed or requiring a bundled application update.</p>

<h2 id="containers-and-layered-distribution">Containers and layered distribution</h2>

<p><a href="https://patents.google.com/patent/WO2020232713A1/en">WO2020232713A1 - Container instantiation with union file system layer mounts</a>. On instantiation, the runtime receives an image manifest and sends layer mount requests to the registry rather than downloading layer content. Prior art for the union-mount side: UnionFS (2005), AUFS (2006), OverlayFS (2014). Prior art for lazy and on-demand layer fetching: Slacker (FAST ‘16), eStargz, SOCI.</p>

<p><a href="https://patents.google.com/patent/US12056511B2/en">US12056511B2 - Container image creation and deployment using a manifest</a>. IBM. Manifest-driven container build and deploy; claims cite inode descriptors and file hashes. Prior art: the OCI image-spec, and the content-addressable storage model from Git (2005) and earlier systems like Monotone and Venti.</p>

<p><a href="https://patents.google.com/patent/US10127030B1/en">US10127030B1 - Systems and methods for controlled container execution</a>. The container manifest carries a hash or digest of each item, and a content validation engine compares the digests at execution time. Prior art: the OCI content-addressable storage model, with Git as the earlier general-purpose precedent.</p>

<p>If you’re aware of patents that should be included in this collection, please reach out on <a href="https://mastodon.social/@andrewnez">Mastodon</a> or submit a pull request to <a href="https://github.com/andrew/nesbitt.io/blob/master/_posts/2026-06-08-package-manager-patents.md">the post on GitHub</a>.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="history" /><category term="reference" /><summary type="html"><![CDATA[A reference list of patents and applications relevant to package manager design, with notes on prior art.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">This Week in Package Management: 6 June 2026</title><link href="https://nesbitt.io/2026/06/06/this-week-in-package-management.html" rel="alternate" type="text/html" title="This Week in Package Management: 6 June 2026" /><published>2026-06-06T10:00:00+00:00</published><updated>2026-06-06T10:00:00+00:00</updated><id>https://nesbitt.io/2026/06/06/this-week-in-package-management</id><content type="html" xml:base="https://nesbitt.io/2026/06/06/this-week-in-package-management.html"><![CDATA[<p>Third week of the roundup, built from the <a href="https://github.com/ecosyste-ms/package-managers-opml">package manager OPML feed collection</a> and whatever I’ve posted or boosted on <a href="https://mastodon.social/@andrewnez">Mastodon</a>. Five new project blog feeds and the NixOS announcements feed landed in the OPML this week.</p>

<h2 id="security">Security</h2>

<p><a href="https://github.com/ruby/rubygems/releases/tag/bundler-v4.0.13">Bundler 4.0.13</a> ships <a href="https://blog.rubygems.org/2026/06/03/cooldown-let-new-gems-be-vetted.html">Cooldown</a>, a configurable time window that holds back resolution to gem versions younger than N days, so a freshly published malicious release ages past the window before a <code class="language-plaintext highlighter-rouge">bundle install</code> will pick it up. The companion <a href="https://github.com/ruby/rubygems/releases/tag/v4.0.13">RubyGems 4.0.13</a> release blocks gem extraction from escaping the destination directory via pre-existing symlinks.</p>

<p>The Packagist supply-chain series continues. <a href="https://blog.packagist.com/closing-composers-download-fallback-paths-in-private-packagist/">Closing Composer’s Download Fallback Paths</a> covers how the dist-to-source fallback, originally designed for resilience, can be used to fetch a different artifact than the one Composer expected. <a href="https://blog.packagist.com/blocking-malware-downloads-for-every-composer-version-in-private-packagist/">Blocking Malware Downloads for Every Composer Version</a> describes how Private Packagist enforces malware blocking for installs from Composer versions older than 2.10, before the dependency policy framework existed. <a href="https://blog.packagist.com/enforce-a-safe-composer-version-across-your-organization/">Enforce a Safe Composer Version Across Your Organization</a> closes the loop by letting Private Packagist organisations restrict which Composer client versions can fetch the repository at all, rejecting older clients with an error that surfaces in the developer’s terminal.</p>

<p><a href="https://hex.pm/blog/hexdocs-per-package-subdomains">New HexDocs URLs: per-package subdomains</a> moves public Elixir and Erlang package docs from <code class="language-plaintext highlighter-rouge">hexdocs.pm/package</code> to <code class="language-plaintext highlighter-rouge">package.hexdocs.pm</code>, and organization docs to a separate registrable domain (<code class="language-plaintext highlighter-rouge">hexorgs.pm</code>). The browser’s same-origin policy now isolates packages from each other, addressing a finding from Hex’s recent security audit that docs pages run maintainer-controlled HTML, CSS, and JavaScript under a shared origin.</p>

<p>Homebrew’s <a href="https://docs.brew.sh/Tap-Trust">Tap-Trust documentation</a> describes an upcoming change to how non-official taps are loaded. Today any installed tap contributes formulae, casks, and commands by default. Under Tap-Trust, taps need explicit approval via <code class="language-plaintext highlighter-rouge">brew trust user/repo</code> (or a per-formula variant) before Homebrew evaluates their code. The change becomes the default in Homebrew 6.0.0 or 5.2.0, whichever ships first. <code class="language-plaintext highlighter-rouge">HOMEBREW_REQUIRE_TAP_TRUST=1</code> opts in early.</p>

<p><a href="https://github.com/composer/composer/releases/tag/2.10.1">Composer 2.10.1</a> fixes shell escaping when opening an editor and verifies the backup phar’s signature before <code class="language-plaintext highlighter-rouge">self-update --rollback</code> restores it.</p>

<p><a href="https://github.com/NuGet/NuGet.Server/releases/tag/3.4.3">NuGet.Server 3.4.3</a> fixes an unauthenticated denial-of-service on the package upload endpoint (CWE-696/CWE-400) by moving API key validation ahead of the file I/O and package processing it used to do first.</p>

<h2 id="releases">Releases</h2>

<p><a href="https://github.com/yarnpkg/berry/releases/tag/%40yarnpkg%2Fcli%2F4.16.0">Yarn 4.16.0</a> adds <code class="language-plaintext highlighter-rouge">yarn npm stage</code> for npm’s staged publishing queue, alongside editor SDK support for oxc’s formatter and linter.</p>

<p><a href="https://github.com/pypa/hatch/releases/tag/hatch-v1.17.0">Hatch 1.17.0</a> deprecates <code class="language-plaintext highlighter-rouge">hatch fmt</code> in favour of a new <code class="language-plaintext highlighter-rouge">hatch check</code> command group with <code class="language-plaintext highlighter-rouge">code</code>, <code class="language-plaintext highlighter-rouge">fmt</code>, and <code class="language-plaintext highlighter-rouge">types</code> subcommands. Type checking is wired up to Pyrefly. The release also adds <code class="language-plaintext highlighter-rouge">hatch env lock</code> for locking environments and switches the HTTP client from httpx to httpx2.</p>

<p><a href="https://nixos.org/blog/announcements/2026/nixos-2605/">NixOS 26.05 “Yarara”</a> is the latest six-monthly release of Nixpkgs and NixOS. The Nixpkgs side added 20,442 new packages and updated 20,641 since 25.11, and dropped 17,532. This is also the final release with <code class="language-plaintext highlighter-rouge">x86_64-darwin</code> support, since upstream Apple has deprecated the platform.</p>

<p><a href="https://github.com/commercialhaskell/stack/releases/tag/rc%2Fv3.11.0.1">Stack 3.11.0.1 RC</a> switches the default 64-bit Windows MSYS environment from MINGW64 to CLANG64, following the MSYS2 project’s deprecation of MINGW64 in March.</p>

<p><a href="https://github.com/dependabot/dependabot-core/releases/tag/v0.380.0">Dependabot Core 0.380.0</a> adds a lockfile generator for bun via PR <a href="https://github.com/dependabot/dependabot-core/pull/14882">#14882</a>. The same release passes <code class="language-plaintext highlighter-rouge">--config.minimumReleaseAge=0</code> to pnpm security updates, bypassing any <code class="language-plaintext highlighter-rouge">pnpm-workspace.yaml</code> cooldown setting so security PRs aren’t blocked behind the release-age policy.</p>

<p><a href="https://github.com/jdx/mise/releases/tag/v2026.6.0">mise 2026.6.0</a> wires npm into Corepack when <code class="language-plaintext highlighter-rouge">node.corepack=true</code> and <code class="language-plaintext highlighter-rouge">node.npm_shim=false</code>, so the Corepack-managed npm shim sits alongside yarn and pnpm, and aligns aqua’s Windows extension handling with upstream.</p>

<p><a href="https://github.com/microsoft/winget-cli/releases/tag/v1.29.250">Windows Package Manager 1.29.250</a> is the 1.29 release candidate. Sources can now be assigned a numeric priority (experimental). When several sources offer the same package, installs prefer the higher-priority source without prompting. Export and import round-trip override and custom installer arguments, and the MCP server gained upgrade actions.</p>

<p>Also out: <a href="https://github.com/rust-lang/cargo/releases/tag/0.97.1">Cargo 0.97.1</a>, <a href="https://github.com/astral-sh/uv/releases/tag/0.11.19">uv 0.11.19</a>, <a href="https://github.com/pypa/pip/releases/tag/26.1.2">pip 26.1.2</a>, <a href="https://github.com/conda/conda/releases/tag/26.5.2">Conda 26.5.2</a>, <a href="https://github.com/mamba-org/mamba/releases/tag/2.8.0">Mamba 2.8.0</a>, <a href="https://github.com/prefix-dev/pixi/releases/tag/v0.70.1">pixi 0.70.1</a>, <a href="https://github.com/pnpm/pnpm/releases/tag/v11.5.2">pnpm 11.5.2</a>, <a href="https://github.com/pypa/pipx/releases/tag/1.14.0">pipx 1.14.0</a>, <a href="https://github.com/denoland/deno/releases/tag/v2.8.2">Deno 2.8.2</a>, <a href="https://github.com/Homebrew/brew/releases/tag/5.1.15">Homebrew 5.1.15</a>, <a href="https://github.com/moby/moby/releases/tag/docker-v29.5.3">Docker Engine 29.5.3</a>, <a href="https://github.com/golang/go/releases/tag/go1.25.11">Go 1.25.11</a>, <a href="https://github.com/golang/go/releases/tag/go1.26.4">Go 1.26.4</a>, <a href="https://github.com/sbt/sbt/releases/tag/v2.0.0-RC14">sbt 2.0.0-RC14</a>, <a href="https://github.com/obi1kenobi/cargo-semver-checks/releases/tag/v0.48.0">cargo-semver-checks 0.48.0</a>.</p>

<h2 id="articles">Articles</h2>

<p><a href="https://ddbeck.com/where-does-the-money-come-from/">Where does the money come from?</a> (Daniel D. Beck) is a catalogue of every channel he knows that gets technical-documentation authors and maintainers paid, from foundation grants and staff tech-writer roles to docs-for-hire arrangements and tip jars.</p>

<p><a href="https://fastwonderblog.com/2026/06/02/how-ospos-can-measure-the-impact-of-oss-funding/">How OSPOs can measure the impact of OSS funding</a> (Dawn Foster) is the case OSPOs can make internally when budgets tighten and the funded projects don’t translate directly into product revenue. Dawn also has a <a href="https://doi.org/10.1109/MC.2026.3667269">four-page piece in IEEE Computer</a> on how governance choices shape open source project sustainability, aimed at project leads.</p>

<p>The <a href="https://blog.rust-lang.org/2026/06/02/launching-the-rust-foundation-maintainers-fund/">Rust Foundation Maintainers Fund</a> launched this week as a “Maintainer in Residence” programme that pays existing Rust Project maintainers from a donor-funded pool.</p>

<p><a href="https://pipdeptree.readthedocs.io/en/latest/tutorial/getting-started.html#render-a-lock-file">Rendering a lock file with pipdeptree</a> is a new tutorial for the <code class="language-plaintext highlighter-rouge">from-lock</code> subcommand, which prints the dependency tree of a PEP 751 lock file offline without resolving or installing anything.</p>

<p>The <a href="https://reproducible-builds.org/reports/2026-05/">Reproducible Builds May 2026 report</a> leads with <a href="https://lists.debian.org/debian-devel-announce/2026/05/msg00001.html">Debian’s decision</a> to require reproducibility for packages migrating into the next release (“forky”), blocking unreproducible packages from migration.</p>

<h2 id="papers">Papers</h2>

<p><a href="https://arxiv.org/abs/2606.02442">Poking Around in the Dark: Why a Shared Understanding of Components Matters</a> (Reichmann et al., arXiv) finds that SBOM generators disagree on what counts as a component in the same software, leaving gaps in supply-chain vulnerability identification.</p>

<p><a href="https://arxiv.org/abs/2606.02196">PyFEX: Uncovering Evasive Python-based Threats via Resilient and Exhaustive Path Exploration</a> (Wang et al., arXiv) is a forced-execution engine for Python that recovers from crashes mid-run and flagged 212 previously unknown malicious uploads on PyPI.</p>

<h2 id="elsewhere">Elsewhere</h2>

<p><a href="https://github.com/rust-lang/crates.io/pull/13855">crates.io PR #13855</a> proposes surfacing standard-library replacements on crate pages: a banner on the crate page and a marker in dependency lists, each linking to the <code class="language-plaintext highlighter-rouge">std</code> API that covers what the crate did. Seeded with <code class="language-plaintext highlighter-rouge">lazy_static</code>, <code class="language-plaintext highlighter-rouge">once_cell</code>, <code class="language-plaintext highlighter-rouge">matches</code>, and <code class="language-plaintext highlighter-rouge">num_cpus</code>. The PR cites my <a href="/2026/04/16/features-everyone-should-steal-from-npmx.html">features everyone should steal from npmx</a> post as one of the inspirations.</p>

<p>Send links for next week to <a href="https://mastodon.social/@andrewnez">@andrewnez@mastodon.social</a>.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="weekly" /><summary type="html"><![CDATA[Releases, advisories, and articles from across the package management world]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Install-script allowlists</title><link href="https://nesbitt.io/2026/06/05/install-script-allowlists.html" rel="alternate" type="text/html" title="Install-script allowlists" /><published>2026-06-05T12:00:00+00:00</published><updated>2026-06-05T12:00:00+00:00</updated><id>https://nesbitt.io/2026/06/05/install-script-allowlists</id><content type="html" xml:base="https://nesbitt.io/2026/06/05/install-script-allowlists.html"><![CDATA[<p>In most package managers a dependency’s <a href="/2026/04/15/the-tuesday-test.html">install-time code runs by default</a> the moment you install it: an npm postinstall, a Setuptools <code class="language-plaintext highlighter-rouge">setup.py</code>, a CPAN <code class="language-plaintext highlighter-rouge">Makefile.PL</code>, an RPM scriptlet, a Conda post-link, a Debian <code class="language-plaintext highlighter-rouge">postinst</code>. A handful require explicit per-package opt-in before any of that code runs, usually called an allowlist or a trusted-dependencies list depending on the tool.</p>

<p>Per-package opt-in lists name which dependencies may run their install code: npm, pnpm, Bun, Deno, and Composer plugins all work this way. Global sandboxes (opam, Swift Package Manager, Nix, Guix, Portage) take a different shape, executing everything but constraining what that execution can reach. Identity and signature verification (RubyGems trust policies, Gradle dependency verification, NuGet trustedSigners, apt-secure) gates which artifacts get installed in the first place by who signed them, with no bearing on what their code subsequently does.</p>

<p>An npm postinstall, a setup.py, a Makefile.PL or an RPM scriptlet fires during fetch or unpack. A Cargo <code class="language-plaintext highlighter-rouge">build.rs</code> or a Zig <code class="language-plaintext highlighter-rouge">build.zig</code> runs when the project is compiled, which on a fresh build is functionally the next step but is structurally distinct. JVM build files (Gradle’s Groovy or Kotlin, Maven’s plugin goal invocations, SBT’s Scala) execute earlier still, before any project source touches the compiler.</p>

<h2 id="javascript">JavaScript</h2>

<p>npm shipped per-package allowlists in <a href="https://github.com/npm/cli/releases/tag/v11.16.0">11.16.0</a> (May 2026) via an <code class="language-plaintext highlighter-rouge">allowScripts</code> field in <code class="language-plaintext highlighter-rouge">package.json</code>, managed by <a href="https://docs.npmjs.com/cli/v11/commands/npm-approve-scripts/"><code class="language-plaintext highlighter-rouge">npm approve-scripts</code></a> and <a href="https://docs.npmjs.com/cli/v11/commands/npm-deny-scripts/"><code class="language-plaintext highlighter-rouge">npm deny-scripts</code></a>, with entries pinned to a specific version (<code class="language-plaintext highlighter-rouge">pkg@1.2.3: true</code>) by default and denials written name-only.</p>

<p>Behaviour in 11.x is advisory: scripts still execute, an end-of-install summary names anything unreviewed, and the docs signpost a hard block in a future release. The similarly-named <a href="https://docs.npmjs.com/cli/v11/commands/npm-trust/"><code class="language-plaintext highlighter-rouge">npm trust</code></a> command, added in <a href="https://github.blog/changelog/2026-02-18-npm-bulk-trusted-publishing-config-and-script-security-now-generally-available/">11.10.0</a> (February 2026), is for OIDC trusted publishing rather than script execution.</p>

<p>pnpm v10 (January 2025) <a href="https://pnpm.io/supply-chain-security">blocked install scripts by default</a>, reading the allowlist from <code class="language-plaintext highlighter-rouge">onlyBuiltDependencies</code> / <code class="language-plaintext highlighter-rouge">neverBuiltDependencies</code> in <code class="language-plaintext highlighter-rouge">package.json</code> or <code class="language-plaintext highlighter-rouge">pnpm-workspace.yaml</code>. v11 consolidated those into a single <code class="language-plaintext highlighter-rouge">allowBuilds</code> map, with <code class="language-plaintext highlighter-rouge">dangerouslyAllowAllBuilds</code> as the escape hatch. The companion <a href="https://pnpm.io/cli/approve-builds"><code class="language-plaintext highlighter-rouge">pnpm approve-builds</code></a> (added in 10.1.0) is an interactive picker that accepts <code class="language-plaintext highlighter-rouge">--all</code> for CI and from v11 takes positional arguments like <code class="language-plaintext highlighter-rouge">pnpm approve-builds esbuild fsevents !core-js</code>. Packages not on the list fail the install when <code class="language-plaintext highlighter-rouge">strictDepBuilds</code> is true (the v11 default) and warn otherwise.</p>

<p>Yarn Classic (v1) has no native per-package mechanism, only the global <code class="language-plaintext highlighter-rouge">--ignore-scripts</code> flag, with <a href="https://github.com/yarnpkg/yarn/issues/7338">yarnpkg/yarn#7338</a> tracking the feature request. The <a href="https://lavamoat.github.io/guides/allow-scripts/"><code class="language-plaintext highlighter-rouge">@lavamoat/allow-scripts</code></a> project retrofits one across Yarn v1.22+, Yarn Berry v3+, npm v8+, and pnpm: it disables scripts at the package-manager level then drives execution from a <code class="language-plaintext highlighter-rouge">lavamoat.allowScripts</code> map in <code class="language-plaintext highlighter-rouge">package.json</code>. Yarn Berry (v2+) is declarative: set <a href="https://yarnpkg.com/configuration/yarnrc"><code class="language-plaintext highlighter-rouge">enableScripts: false</code></a> globally in <code class="language-plaintext highlighter-rouge">.yarnrc.yml</code>, then opt packages back in via <code class="language-plaintext highlighter-rouge">dependenciesMeta.&lt;pkg&gt;.built: true</code>. No interactive approval command exists, and workspace packages always run their own scripts regardless of the global setting.</p>

<p>Bun blocks install scripts for dependencies by default and ships a built-in default allowlist of well-known packages (<code class="language-plaintext highlighter-rouge">esbuild</code>, <code class="language-plaintext highlighter-rouge">fsevents</code>, others) auto-trusted only when sourced from the npm registry. The <a href="https://bun.com/docs/guides/install/trusted"><code class="language-plaintext highlighter-rouge">trustedDependencies</code></a> array in <code class="language-plaintext highlighter-rouge">package.json</code> overrides that list, so opting a single package in drops the default-trusted set entirely. Trust is added by name via <code class="language-plaintext highlighter-rouge">bun pm trust &lt;pkg&gt;</code> or <code class="language-plaintext highlighter-rouge">bun add --trust &lt;pkg&gt;</code> (which pulls in the package’s transitive deps), and <code class="language-plaintext highlighter-rouge">bun pm untrusted</code> lists packages with install scripts that haven’t been granted trust.</p>

<p>Deno never runs npm lifecycle scripts unless explicitly approved, via the <a href="https://docs.deno.com/runtime/reference/cli/install/"><code class="language-plaintext highlighter-rouge">--allow-scripts=&lt;pkg&gt;</code></a> flag on <code class="language-plaintext highlighter-rouge">deno install</code> and <code class="language-plaintext highlighter-rouge">deno cache</code> (Deno 1.45/1.46, mid-2024) that accepts comma-separated specifiers like <code class="language-plaintext highlighter-rouge">npm:sqlite3,npm:esbuild@0.21.5</code>. Deno 2.6 (December 2025) added <a href="https://docs.deno.com/runtime/reference/cli/approve_scripts/"><code class="language-plaintext highlighter-rouge">deno approve-scripts</code></a>, which persists per-package decisions into <code class="language-plaintext highlighter-rouge">deno.json</code>. Packages without approval have their scripts skipped at install time and listed in an end-of-install warning so they can be reviewed before the next run.</p>

<h2 id="php">PHP</h2>

<p>Composer’s top-level <code class="language-plaintext highlighter-rouge">scripts</code> field carries lifecycle hooks tied to events like <code class="language-plaintext highlighter-rouge">pre-install-cmd</code> and <code class="language-plaintext highlighter-rouge">post-update-cmd</code>, but only the root package’s scripts run during install: a dependency’s scripts never execute in the parent project, unlike npm’s <code class="language-plaintext highlighter-rouge">postinstall</code>. Plugins are the actual transitive execution surface, and the <a href="https://getcomposer.org/doc/06-config.md#allow-plugins"><code class="language-plaintext highlighter-rouge">allow-plugins</code></a> configuration key (Composer 2.2, 2021-12-22) made plugin activation explicit per package.</p>

<p>The key takes <code class="language-plaintext highlighter-rouge">"vendor/package": true|false</code> entries with wildcard support (<code class="language-plaintext highlighter-rouge">"vendor/*": true</code>), defaults to <code class="language-plaintext highlighter-rouge">{}</code>, and prompts interactively for unlisted plugins while persisting the answer. Non-interactive runs (<code class="language-plaintext highlighter-rouge">--no-interaction</code>, CI) install the package into <code class="language-plaintext highlighter-rouge">vendor/</code> but skip executing its plugin code, so an unlisted plugin doesn’t break the install, it just doesn’t activate.</p>

<h2 id="python">Python</h2>

<p>Python wheels conventionally have no install-time hooks, so for Python the install-script question becomes whether a package may execute <a href="https://peps.python.org/pep-0517/">PEP 517</a> build backend code locally when the resolver picks an sdist over a prebuilt wheel.</p>

<p>Pip has no per-package allowlist for that. <a href="https://github.com/pypa/pip/issues/425">pypa/pip#425</a>, opened in 2012 under the title “pip should not execute arbitrary code from the Internet”, captures the historical position. The closest controls are global: <code class="language-plaintext highlighter-rouge">pip install --only-binary :all:</code> refuses source distributions entirely, with <code class="language-plaintext highlighter-rouge">--no-binary &lt;pkg&gt;</code> available as a per-package exception. <a href="https://pip.pypa.io/en/stable/topics/secure-installs/">Secure installs</a> recommends pairing <code class="language-plaintext highlighter-rouge">--only-binary :all:</code> with <code class="language-plaintext highlighter-rouge">--require-hashes</code>. The inverse <code class="language-plaintext highlighter-rouge">--only-binary-except=&lt;pkg&gt;</code> is tracked at <a href="https://github.com/pypa/pip/issues/10724">pypa/pip#10724</a>.</p>

<p><a href="https://github.com/pypa/pip/issues/13079">pypa/pip#13079</a> (fixed in pip 25.0) showed that wheels aren’t inert in practice: a malicious wheel could overwrite pip’s own internal modules and execute code at the tail of <code class="language-plaintext highlighter-rouge">pip install</code>.</p>

<p>uv has per-package source-build controls via a set of <a href="https://docs.astral.sh/uv/reference/settings/">settings</a> that pair global and per-package toggles: <code class="language-plaintext highlighter-rouge">no-build</code> and <code class="language-plaintext highlighter-rouge">no-build-package</code> refuse sdists, <code class="language-plaintext highlighter-rouge">no-binary</code> and <code class="language-plaintext highlighter-rouge">no-binary-package</code> force source builds, <code class="language-plaintext highlighter-rouge">no-build-isolation</code> and <code class="language-plaintext highlighter-rouge">no-build-isolation-package</code> toggle PEP 517 build isolation. The combination amounts to a per-package allowlist for which packages may execute build backend code locally. <a href="https://github.com/astral-sh/uv/issues/11682">astral-sh/uv#11682</a> asked for <code class="language-plaintext highlighter-rouge">only-binary</code> to gain a persistent project-level form alongside the existing CLI flag.</p>

<p>Poetry exposes <code class="language-plaintext highlighter-rouge">installer.only-binary</code> (Poetry 2.0.0+) and <code class="language-plaintext highlighter-rouge">installer.no-binary</code> as comma-separated package lists or the special values <code class="language-plaintext highlighter-rouge">:all:</code> / <code class="language-plaintext highlighter-rouge">:none:</code>. Combining <code class="language-plaintext highlighter-rouge">installer.only-binary = ":all:"</code> with <code class="language-plaintext highlighter-rouge">installer.no-binary = "pkgA"</code> produces a per-package source-build allowlist by composition, since the <a href="https://python-poetry.org/docs/configuration/">docs</a> state that explicit package names override <code class="language-plaintext highlighter-rouge">:all:</code>. PDM has <code class="language-plaintext highlighter-rouge">--no-isolation</code> for build isolation but no <code class="language-plaintext highlighter-rouge">no-binary-package</code> equivalent in the <a href="https://pdm-project.org/en/latest/reference/cli/">CLI reference</a>. Pipenv has neither natively. The documented workaround is <code class="language-plaintext highlighter-rouge">--extra-pip-args="--only-binary=:all:"</code> or setting <code class="language-plaintext highlighter-rouge">PIP_NO_BINARY</code> / <code class="language-plaintext highlighter-rouge">PIP_ONLY_BINARY</code> for pip to read directly.</p>

<p>Conda packages can ship <code class="language-plaintext highlighter-rouge">pre-link</code>, <code class="language-plaintext highlighter-rouge">post-link</code>, and <code class="language-plaintext highlighter-rouge">pre-unlink</code> shell scripts that run on the user’s machine during install and uninstall. The <a href="https://docs.conda.io/projects/conda-build/en/latest/resources/link-scripts.html">link-scripts documentation</a> advises authors to avoid them but documents no allowlist, no <code class="language-plaintext highlighter-rouge">.condarc</code> toggle, and no CLI flag to disable them. Conda’s security configuration knobs (<code class="language-plaintext highlighter-rouge">safety_checks</code>, <code class="language-plaintext highlighter-rouge">extra_safety_checks</code>, <code class="language-plaintext highlighter-rouge">signing_metadata_url_base</code>, channel allowlist/denylist) cover artifact integrity and channel provenance, not per-package script execution. Mamba and micromamba reimplement the install model and inherit the same gap.</p>

<p>The indirect mitigation is that <code class="language-plaintext highlighter-rouge">noarch: python</code> packages are required by policy not to ship link scripts, so restricting yourself to <code class="language-plaintext highlighter-rouge">noarch: python</code> deps avoids the surface for pure-Python work.</p>

<h2 id="ruby">Ruby</h2>

<p>RubyGems and Bundler have no per-gem allowlist for install-time code execution. Gems with <code class="language-plaintext highlighter-rouge">ext/&lt;name&gt;/extconf.rb</code> run arbitrary Ruby at install time to configure native extension builds, and the same applies to Rakefile / <code class="language-plaintext highlighter-rouge">mkrf_conf</code> variants declared under a gem’s <code class="language-plaintext highlighter-rouge">extensions</code> list. The signing and trust-policy mechanism at <a href="https://guides.rubygems.org/security/">guides.rubygems.org/security</a> (<code class="language-plaintext highlighter-rouge">LowSecurity</code>, <code class="language-plaintext highlighter-rouge">MediumSecurity</code>, <code class="language-plaintext highlighter-rouge">HighSecurity</code>) checks who published a gem, not whether it may run install-time code. <code class="language-plaintext highlighter-rouge">bundle config build.&lt;gem&gt; -- --with-foo</code> passes arguments to native builds without gating whether they happen.</p>

<h2 id="perl">Perl</h2>

<p>CPAN distributions ship a <code class="language-plaintext highlighter-rouge">Makefile.PL</code> (ExtUtils::MakeMaker) or <code class="language-plaintext highlighter-rouge">Build.PL</code> (Module::Build) which are ordinary Perl scripts executed at install time by <a href="https://perldoc.perl.org/CPAN"><code class="language-plaintext highlighter-rouge">cpan</code></a>, <a href="https://metacpan.org/pod/App::cpanminus"><code class="language-plaintext highlighter-rouge">cpanm</code></a>, or <code class="language-plaintext highlighter-rouge">cpm</code>. There is no per-distribution capability gate, no first-time prompt, and no equivalent of <code class="language-plaintext highlighter-rouge">allow-plugins</code>. CPAN.pm exposes <code class="language-plaintext highlighter-rouge">makepl_arg</code>, <code class="language-plaintext highlighter-rouge">mbuildpl_arg</code>, and <code class="language-plaintext highlighter-rouge">prerequisites_policy</code> knobs for tuning how <code class="language-plaintext highlighter-rouge">Makefile.PL</code> is invoked and how dependencies are resolved, none of which gate whether the code runs.</p>

<h2 id="systems-languages">Systems languages</h2>

<p>Cargo runs <code class="language-plaintext highlighter-rouge">build.rs</code> and proc-macros as ordinary host-native Rust code during every <code class="language-plaintext highlighter-rouge">cargo build</code>, <code class="language-plaintext highlighter-rouge">test</code>, <code class="language-plaintext highlighter-rouge">run</code>, and <code class="language-plaintext highlighter-rouge">install</code> against the affected crates. Proc-macros execute inside the <code class="language-plaintext highlighter-rouge">rustc</code> process during compilation, so any procedural-macro dependency runs its code on every build. There is no global flag to disable proc-macros and no sandbox around the script process. A crate’s own <code class="language-plaintext highlighter-rouge">Cargo.toml</code> can set <code class="language-plaintext highlighter-rouge">build = false</code> to suppress its own build script, but consumers cannot disable a dependency’s <code class="language-plaintext highlighter-rouge">build.rs</code>.</p>

<p>The long-running tracking issues are <a href="https://github.com/rust-lang/cargo/issues/5720">rust-lang/cargo#5720</a> (sandbox/jail build scripts, July 2018) and <a href="https://github.com/rust-lang/cargo/issues/13681">rust-lang/cargo#13681</a> (build script allowlist mode, April 2024), plus the <a href="https://github.com/rust-lang/compiler-team/issues/475">compiler-team MCP</a> proposing an isolating runtime shipped via rustup, none of which has landed. <a href="https://mozilla.github.io/cargo-vet/">cargo-vet</a> and <a href="https://github.com/crev-dev/cargo-crev">cargo-crev</a> flag <code class="language-plaintext highlighter-rouge">custom-build</code> crates for reviewer attention; neither prevents execution.</p>

<p>Go modules don’t run downloaded code beyond compiling it, with <code class="language-plaintext highlighter-rouge">go run</code>, <code class="language-plaintext highlighter-rouge">go test</code>, and <code class="language-plaintext highlighter-rouge">go generate</code> documented as the explicit exceptions in <a href="https://go.dev/blog/path-security">Russ Cox’s “Command PATH security in Go”</a>. There is no per-module trust mechanism because nothing third-party runs in the first place. The cgo <code class="language-plaintext highlighter-rouge">#cgo CFLAGS:</code> and <code class="language-plaintext highlighter-rouge">LDFLAGS:</code> directives have been the escape hatch. <a href="https://github.com/golang/go/issues/23672">CVE-2018-6574</a>, <a href="https://github.com/golang/go/issues/67119">CVE-2024-24787</a>, and <a href="https://github.com/golang/go/issues/42559">#42559</a> were each mitigated by extending a hard-coded allowlist of permitted compiler/linker flags in the toolchain. <a href="https://github.com/golang/go/issues/63211">CVE-2023-39323</a> addressed an adjacent surface by restricting <code class="language-plaintext highlighter-rouge">//line</code> directives in cgo-generated files. No per-module grant was added in any of these cases.</p>

<p>Swift Package Manager runs both <code class="language-plaintext highlighter-rouge">Package.swift</code> manifest evaluation and package plugins inside a sandbox (sandbox-exec on macOS) with no network access and writes restricted to a per-plugin temporary directory by default. Plugins that need more declare permissions in their target definition using <a href="https://developer.apple.com/documentation/packagedescription/pluginpermission"><code class="language-plaintext highlighter-rouge">PluginPermission</code></a>: <code class="language-plaintext highlighter-rouge">writeToPackageDirectory(reason:)</code> and <code class="language-plaintext highlighter-rouge">allowNetworkConnections(scope:reason:)</code> with scope <code class="language-plaintext highlighter-rouge">none</code>, <code class="language-plaintext highlighter-rouge">local(ports:)</code>, <code class="language-plaintext highlighter-rouge">all(ports:)</code>, <code class="language-plaintext highlighter-rouge">docker</code>, or <code class="language-plaintext highlighter-rouge">unixDomainSocket</code>. The user is prompted on a TTY (<a href="https://github.com/apple/swift-package-manager/pull/5483">PR #5483</a>) or must pass <code class="language-plaintext highlighter-rouge">--allow-writing-to-package-directory</code> / <code class="language-plaintext highlighter-rouge">--allow-network-connections</code> non-interactively, with decisions scoped per package.</p>

<p>The permission-grant model covers command plugins but not build tool plugins. Build tool plugins still run inside the sandbox by default but cannot declare or be granted <code class="language-plaintext highlighter-rouge">writeToPackageDirectory</code> / <code class="language-plaintext highlighter-rouge">allowNetworkConnections</code>. The <a href="https://forums.swift.org/t/pitch-swiftpm-plugins-explicit-buildtool-sandbox-permissions/68963">build-tool sandbox permissions pitch</a> tracks the extension to that surface.</p>

<p>Zig’s <code class="language-plaintext highlighter-rouge">build.zig</code> is arbitrary Zig code compiled to a native host binary and executed by <code class="language-plaintext highlighter-rouge">zig build</code>, including for every transitive dependency pulled in by the package manager. There is no sandbox and no per-package gate. The proposal at <a href="https://github.com/ziglang/zig/issues/14286">ziglang/zig#14286</a> (open, labelled <code class="language-plaintext highlighter-rouge">urgent</code>) has no merged implementation yet. It would compile every <code class="language-plaintext highlighter-rouge">build.zig</code> to <code class="language-plaintext highlighter-rouge">wasm32-wasi</code> and emit the build graph as data for a separate <code class="language-plaintext highlighter-rouge">build_runner</code> to execute under whatever permissions are granted.</p>

<h2 id="jvm">JVM</h2>

<p>JVM dependencies are passive JARs that don’t execute on resolve or install. Build-time plugins are the execution surface.</p>

<p>Maven has no built-in allowlist of which plugins may load. Plugin goals execute as ordinary Java during the build <a href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html">lifecycle</a>. The <a href="https://maven.apache.org/enforcer/enforcer-rules/bannedPlugins.html">Maven Enforcer plugin’s <code class="language-plaintext highlighter-rouge">bannedPlugins</code></a> and <code class="language-plaintext highlighter-rouge">bannedDependencies</code> rules are blocklists with <code class="language-plaintext highlighter-rouge">includes</code> carve-outs, so an allowlist has to be expressed as banning <code class="language-plaintext highlighter-rouge">*</code> and re-including specific GAVs. Core extensions declared in <a href="https://maven.apache.org/guides/mini/guide-using-extensions.html"><code class="language-plaintext highlighter-rouge">.mvn/extensions.xml</code></a> load into Maven’s core classloader before the build starts, with no signature check or allowlist.</p>

<p>Gradle’s <code class="language-plaintext highlighter-rouge">build.gradle(.kts)</code>, <code class="language-plaintext highlighter-rouge">settings.gradle(.kts)</code>, convention plugins, and applied plugins all execute arbitrary Kotlin/Groovy at configuration time, with no per-plugin code-execution allowlist. <a href="https://docs.gradle.org/current/userguide/dependency_verification.html">Dependency verification via <code class="language-plaintext highlighter-rouge">verification-metadata.xml</code></a> covers regular dependencies and plugins through checksum and PGP signature verification of artifact identity. That establishes who published the artifact, not what its code may do. Init scripts (<code class="language-plaintext highlighter-rouge">-I</code>, <code class="language-plaintext highlighter-rouge">$GRADLE_USER_HOME/init.gradle(.kts)</code>, <a href="https://docs.gradle.org/current/userguide/init_scripts.html"><code class="language-plaintext highlighter-rouge">init.d/*.init.gradle(.kts)</code></a>) run unconditionally with no signature check. The configuration cache serialises the configured task graph for performance, not to restrict what plugin code may do.</p>

<p>SBT plugins declared in <code class="language-plaintext highlighter-rouge">project/plugins.sbt</code> run at build configuration time with full JVM access. The <a href="https://www.scala-sbt.org/1.x/docs/Plugins.html">official docs</a> describe classloader-level encapsulation between plugins and build definitions as an authoring convenience, not a security boundary. There is no allowlist or signature verification analogous to Gradle’s <code class="language-plaintext highlighter-rouge">verification-metadata.xml</code>, and SBT inherits whatever artifact-verification posture the underlying Ivy or Coursier resolver provides. Leiningen and <a href="https://mill-build.org/">Mill</a> take the same approach, with <code class="language-plaintext highlighter-rouge">project.clj</code> in Clojure and <code class="language-plaintext highlighter-rouge">build.sc</code> in Scala running as configuration-time programs and neither providing a per-plugin allowlist.</p>

<p>Bazel sits at the opposite end of the JVM build-tool spectrum. <code class="language-plaintext highlighter-rouge">BUILD</code> files and <code class="language-plaintext highlighter-rouge">.bzl</code> extensions are written in <a href="https://bazel.build/rules/language">Starlark</a>, a Python dialect with no clock access, no recursion, no mutable global state, and no filesystem or network calls outside declared inputs. Build actions run in a sandbox that sees only what the rule declares. The escape hatches exist (<code class="language-plaintext highlighter-rouge">repository_rule</code> for fetching, <code class="language-plaintext highlighter-rouge">genrule</code> for shell, custom toolchains), but the default posture is that a BUILD file cannot observe its host, and the per-action sandbox covers what would otherwise need an allowlist.</p>

<h2 id="net">.NET</h2>

<p>Under PackageReference (NuGet 4.0+ and the default for SDK-style projects), the historical <code class="language-plaintext highlighter-rouge">install.ps1</code> and <code class="language-plaintext highlighter-rouge">uninstall.ps1</code> PowerShell scripts no longer execute on install or uninstall, per the <a href="https://learn.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference">migration guide</a>.</p>

<p>The replacement execution surface is MSBuild <a href="https://learn.microsoft.com/en-us/nuget/concepts/msbuild-props-and-targets"><code class="language-plaintext highlighter-rouge">build/</code>, <code class="language-plaintext highlighter-rouge">buildMultiTargeting/</code>, and <code class="language-plaintext highlighter-rouge">buildTransitive/</code> <code class="language-plaintext highlighter-rouge">.props</code> and <code class="language-plaintext highlighter-rouge">.targets</code> files</a>, auto-imported into the consumer’s build through NuGet-generated <code class="language-plaintext highlighter-rouge">{projectName}.nuget.g.props</code> and <code class="language-plaintext highlighter-rouge">.nuget.g.targets</code>. <code class="language-plaintext highlighter-rouge">buildTransitive</code> lets a transitive dependency contribute targets to your project without you naming it as a direct dependency. There is no per-package allowlist for MSBuild target imports. The <code class="language-plaintext highlighter-rouge">&lt;trustedSigners&gt;</code> configuration in <code class="language-plaintext highlighter-rouge">nuget.config</code> controls which signed packages are accepted by signer identity, without bearing on what their MSBuild contributions then do.</p>

<h2 id="other-languages">Other languages</h2>

<p>Hex/Mix (Elixir) evaluates each dependency’s <code class="language-plaintext highlighter-rouge">mix.exs</code> and runs its compile task on <a href="https://hexdocs.pm/mix/Mix.Tasks.Deps.Compile.html"><code class="language-plaintext highlighter-rouge">mix deps.compile</code></a>, with no per-package allowlist and no separate install-script field beyond compilation. Rebar3 (Erlang) supports <a href="https://rebar3.org/docs/configuration/configuration/"><code class="language-plaintext highlighter-rouge">pre_hooks</code>, <code class="language-plaintext highlighter-rouge">post_hooks</code>, <code class="language-plaintext highlighter-rouge">provider_hooks</code></a> and plugins loaded from Hex, all of which execute when their declaring dependency is built, again without any allowlist.</p>

<p>Cabal and Stack (Haskell) historically run arbitrary <code class="language-plaintext highlighter-rouge">Setup.hs</code> programs for packages with <code class="language-plaintext highlighter-rouge">build-type: Custom</code>. The recent <a href="https://well-typed.com/blog/2025/01/cabal-hooks/"><code class="language-plaintext highlighter-rouge">build-type: Hooks</code></a> in Cabal 3.14 (2024) replaces wholesale Setup replacement with a fixed set of named hook points, narrowing the surface without introducing an allowlist.</p>

<p>Opam (OCaml) wraps every package’s <code class="language-plaintext highlighter-rouge">build:</code> and <code class="language-plaintext highlighter-rouge">install:</code> commands with <a href="https://opam.ocaml.org/doc/FAQ.html"><code class="language-plaintext highlighter-rouge">sandbox.sh</code></a> (opam 2.0, 2018), using bubblewrap on Linux and sandbox-exec on macOS. The build phase can write to the build directory and <code class="language-plaintext highlighter-rouge">/tmp</code> but sees the switch as read-only; the install phase can write to the switch. Network access is denied throughout. The sandbox is global rather than per-package, and <code class="language-plaintext highlighter-rouge">opam init --disable-sandboxing</code> turns it off.</p>

<p>Pub (Dart/Flutter) historically ran no dependency code on resolution. The <a href="https://dart.dev/tools/hooks"><code class="language-plaintext highlighter-rouge">hook/build.dart</code></a> mechanism started as an experiment in Dart 3.2 behind <code class="language-plaintext highlighter-rouge">--enable-experiment=native-assets</code> and stabilised in Dart 3.10. The design is advertised as “semi-hermetic” for reproducibility, not for adversarial isolation.</p>

<p>LuaRocks rockspecs can declare <code class="language-plaintext highlighter-rouge">command</code>, <code class="language-plaintext highlighter-rouge">make</code>, <code class="language-plaintext highlighter-rouge">cmake</code>, or <code class="language-plaintext highlighter-rouge">builtin</code> <a href="https://github.com/luarocks/luarocks/blob/main/docs/rockspec_format.md">build backends</a>, with the <code class="language-plaintext highlighter-rouge">command</code> backend executing arbitrary shell during <code class="language-plaintext highlighter-rouge">luarocks install</code> and no allowlist over which rocks may do so.</p>

<p>Nimble (Nim) supports <code class="language-plaintext highlighter-rouge">before</code> and <code class="language-plaintext highlighter-rouge">after</code> template hooks in <code class="language-plaintext highlighter-rouge">.nimble</code> NimScript files, with <code class="language-plaintext highlighter-rouge">exec</code> of external processes as the documented escape hatch from NimScript’s own FFI restrictions. <a href="https://github.com/ugexe/zef">zef</a> (Raku) runs a <code class="language-plaintext highlighter-rouge">Build.rakumod</code> or a <code class="language-plaintext highlighter-rouge">builder</code> module declared in <code class="language-plaintext highlighter-rouge">META6.json</code> unconditionally during the build phase. The <code class="language-plaintext highlighter-rouge">--/build</code> flag disables the build phase globally; no per-distribution gate is documented.</p>

<p>Crystal Shards supports a <code class="language-plaintext highlighter-rouge">postinstall</code> field in <a href="https://github.com/crystal-lang/shards/blob/master/docs/shard.yml.adoc"><code class="language-plaintext highlighter-rouge">shard.yml</code></a> with a global <code class="language-plaintext highlighter-rouge">--skip-postinstall</code> flag as the only opt-out. The community forum thread <a href="https://forum.crystal-lang.org/t/shards-postinstall-considered-harmful/3910">“postinstall considered harmful”</a> covers the case for changing this. Julia Pkg runs <a href="https://pkgdocs.julialang.org/v1/creating-packages/"><code class="language-plaintext highlighter-rouge">deps/build.jl</code></a> on first install of each dependency, with the modern alternative being BinaryBuilder-produced <code class="language-plaintext highlighter-rouge">_jll</code> packages referenced by hash, although <code class="language-plaintext highlighter-rouge">build.jl</code> remains supported.</p>

<p>R source packages on CRAN run a <code class="language-plaintext highlighter-rouge">configure</code> Bourne shell script (and <code class="language-plaintext highlighter-rouge">configure.win</code> on Windows) before anything else, plus arbitrary code in <code class="language-plaintext highlighter-rouge">R/zzz.R</code>’s <code class="language-plaintext highlighter-rouge">.onLoad</code> and <code class="language-plaintext highlighter-rouge">.onAttach</code>. CRAN’s mitigation is editorial review and pre-built Windows/macOS binaries from the <a href="https://cran.r-project.org/doc/manuals/r-release/R-exts.html">build farm</a>, with no per-package mechanism.</p>

<p>CocoaPods displays a per-install warning the first time a Podfile pulls in a pod with <a href="https://blog.cocoapods.org/CocoaPods-1.4.0/"><code class="language-plaintext highlighter-rouge">script_phase</code></a> build phases, plus on every update where the pod still contains them, without persisting a stored allowlist. Carthage clones each dependency’s repo and invokes <code class="language-plaintext highlighter-rouge">xcodebuild</code> against its shared schemes, which executes any Run Script build phases declared in the dependency’s <code class="language-plaintext highlighter-rouge">.xcodeproj</code> without warning or allowlist.</p>

<h2 id="cc">C/C++</h2>

<p>Conan recipes are full Python modules whose <code class="language-plaintext highlighter-rouge">source()</code>, <code class="language-plaintext highlighter-rouge">build()</code>, <code class="language-plaintext highlighter-rouge">package()</code>, and <code class="language-plaintext highlighter-rouge">package_info()</code> methods run in the host Python process during <a href="https://docs.conan.io/2/reference/conanfile.html"><code class="language-plaintext highlighter-rouge">conan install</code> and <code class="language-plaintext highlighter-rouge">conan create</code></a>. There is no sandbox or allowlist; curation of the ConanCenter index is the trust boundary.</p>

<p>vcpkg ports are <code class="language-plaintext highlighter-rouge">portfile.cmake</code> files interpreted by CMake’s script mode and able to call <code class="language-plaintext highlighter-rouge">execute_process</code> and <code class="language-plaintext highlighter-rouge">vcpkg_execute_build_process</code>, with no per-port allowlist or sandbox per the <a href="https://learn.microsoft.com/en-us/vcpkg/concepts/ports">ports documentation</a>.</p>

<p>Spack <code class="language-plaintext highlighter-rouge">package.py</code> files are arbitrary Python with <code class="language-plaintext highlighter-rouge">install()</code> methods and build phases that run during <code class="language-plaintext highlighter-rouge">spack install</code>. Spack’s <a href="https://spack.readthedocs.io/en/latest/packaging_guide.html">security framing</a> covers download integrity (checksummed tarballs, pinned git commits), not per-recipe capability.</p>

<h2 id="os-distributions">OS distributions</h2>

<p>On dpkg/apt, RPM/dnf, pacman, and Alpine’s apk, install-time maintainer scripts (<code class="language-plaintext highlighter-rouge">preinst</code>/<code class="language-plaintext highlighter-rouge">postinst</code>/<code class="language-plaintext highlighter-rouge">prerm</code>/<code class="language-plaintext highlighter-rouge">postrm</code> for dpkg, <code class="language-plaintext highlighter-rouge">%pre</code>/<code class="language-plaintext highlighter-rouge">%post</code>/<code class="language-plaintext highlighter-rouge">%preun</code>/<code class="language-plaintext highlighter-rouge">%postun</code> for RPM, <code class="language-plaintext highlighter-rouge">.INSTALL</code> for pacman, <code class="language-plaintext highlighter-rouge">$pkgname.{pre,post}-install</code> plus <code class="language-plaintext highlighter-rouge">.{pre,post}-upgrade</code>, <code class="language-plaintext highlighter-rouge">.{pre,post}-deinstall</code>, and <code class="language-plaintext highlighter-rouge">.trigger</code> for apk) run as root with no sandbox, no chroot, and no seccomp filter. The trust model is the archive itself, with <a href="https://manpages.debian.org/testing/apt/apt-secure.8.en.html"><code class="language-plaintext highlighter-rouge">apt-secure(8)</code></a> gating which packages enter the install pipeline via repository GPG signing. There is no per-package allowlist or opt-in flag, and the Debian wiki’s <a href="https://wiki.debian.org/UntrustedDebs">UntrustedDebs</a> page treats installing a <code class="language-plaintext highlighter-rouge">.deb</code> from outside the trusted archive as effectively giving the package author root.</p>

<p>The pacman official repositories follow the same archive-curation model. The <a href="https://wiki.archlinux.org/title/Arch_User_Repository">AUR</a> exposes raw PKGBUILDs and <code class="language-plaintext highlighter-rouge">.INSTALL</code> files to users for review, with AUR helpers (yay, paru, pikaur, others compared in the <a href="https://wiki.archlinux.org/title/AUR_helpers">helpers table</a>) differing on whether they prompt for a diff of PKGBUILDs before sourcing them.</p>

<p>Nix and Guix run every derivation’s builder inside a chroot with a fresh PID/network/mount namespace, an unprivileged build user (Nix’s <code class="language-plaintext highlighter-rouge">nixbld</code> pool, Guix’s <code class="language-plaintext highlighter-rouge">guixbuild</code> pool), and no network access except for fixed-output derivations whose output hash is declared up front. The model is documented in the <a href="https://nix.dev/manual/nix/2.23/command-ref/conf-file.html">Nix configuration reference</a> and the <a href="https://guix.gnu.org/manual/devel/en/html_node/Build-Environment-Setup.html">Guix Build Environment Setup chapter</a>. Every builder runs inside the box, with fixed-output derivations and the small <code class="language-plaintext highlighter-rouge">trusted-users</code> set as the remaining trust surface. CVE-2024-27297 was a fixed-output-derivation sandbox bypass affecting both Nix and Guix.</p>

<p>Portage (Gentoo) enables <code class="language-plaintext highlighter-rouge">FEATURES="sandbox"</code> by default, an LD_PRELOAD shim that intercepts filesystem syscalls and blocks writes outside permitted build directories. <code class="language-plaintext highlighter-rouge">userpriv</code> runs ebuild phases as the <code class="language-plaintext highlighter-rouge">portage</code> user, and <code class="language-plaintext highlighter-rouge">usersandbox</code> combines the two. The mechanism is LD_PRELOAD-based, so static binaries and direct syscalls bypass it, as documented on the <a href="https://wiki.gentoo.org/wiki/Sandbox_(Portage)">Gentoo wiki’s Sandbox (Portage)</a> page. Trust still flows from the curated Portage tree’s signed Manifest files, with no per-ebuild capability grant. Overlays sit explicitly outside that boundary.</p>

<h2 id="userland-package-managers">Userland package managers</h2>

<p>Homebrew, MacPorts, Scoop, and Chocolatey locate trust at the repository (tap, ports tree, bucket) level rather than per-package: tapping a repository or adding a bucket grants it the same trust as the core repository, and individual formulae, ports, or manifests have no per-package allowlist. Homebrew’s <a href="https://github.com/homebrew/brew/security/policy">security policy</a> makes the tap-level boundary explicit.</p>

<p>MacPorts signs the ports tarball (<a href="https://github.com/google/security-research/security/advisories/GHSA-2j38-pjh8-wfxw">GHSA-2j38-pjh8-wfxw</a>, disclosed December 2024, covered an rsync filter bypass that let a malicious mirror deliver unsigned Portfiles past the signed-archive boundary and trigger Tcl execution during <code class="language-plaintext highlighter-rouge">portindex</code>). Scoop bakes a known-bucket list into the client with per-manifest hash verification. Chocolatey adds human moderation of community submissions on top of optional package signing, with Trusted Packages bypassing manual review based on author track record.</p>

<p>winget differs because its YAML manifests don’t include arbitrary install-time scripts. The supported <code class="language-plaintext highlighter-rouge">InstallerType</code> values are real installer formats (<code class="language-plaintext highlighter-rouge">msi</code>, <code class="language-plaintext highlighter-rouge">msix</code>, <code class="language-plaintext highlighter-rouge">appx</code>, <code class="language-plaintext highlighter-rouge">exe</code>, <code class="language-plaintext highlighter-rouge">inno</code>, <code class="language-plaintext highlighter-rouge">nullsoft</code>, <code class="language-plaintext highlighter-rouge">wix</code>, <code class="language-plaintext highlighter-rouge">burn</code>, <code class="language-plaintext highlighter-rouge">portable</code>, <code class="language-plaintext highlighter-rouge">zip</code>, <code class="language-plaintext highlighter-rouge">font</code>, <code class="language-plaintext highlighter-rouge">msstore</code>), and the manifest declares a SHA256 of the installer binary. <code class="language-plaintext highlighter-rouge">winget validate</code> checks manifest format; PR review on the <code class="language-plaintext highlighter-rouge">winget-pkgs</code> repo plus Azure Pipelines bot validation covers submission integrity, alongside an optional local <a href="https://github.com/microsoft/winget-pkgs/blob/master/doc/tools/SandboxTest.md"><code class="language-plaintext highlighter-rouge">SandboxTest.ps1</code></a> that authors can run to test a candidate inside Windows Sandbox before submitting.</p>

<h2 id="version-managers">Version managers</h2>

<p>asdf plugins are Git repositories of shell scripts (<code class="language-plaintext highlighter-rouge">bin/install</code>, <code class="language-plaintext highlighter-rouge">bin/list-all</code>, others) that run as the user during <code class="language-plaintext highlighter-rouge">asdf install</code>, with no allowlist or sandbox: adding a plugin is functionally equivalent to running its bash. mise reduces the plugin surface by routing most tools through non-shell backends: <a href="https://github.com/jdx/mise/discussions/4054">mise discussion #4054</a> maps most tools to <code class="language-plaintext highlighter-rouge">aqua</code>, <code class="language-plaintext highlighter-rouge">ubi</code>, <code class="language-plaintext highlighter-rouge">vfox</code>, or <code class="language-plaintext highlighter-rouge">core</code> in the default registry, with asdf plugins forked under the <code class="language-plaintext highlighter-rouge">mise-plugins</code> GitHub org so commit access is controlled.</p>

<p><a href="https://mise.jdx.dev/configuration/settings.html"><code class="language-plaintext highlighter-rouge">mise trust</code> and <code class="language-plaintext highlighter-rouge">trusted_config_paths</code></a> gate execution of <code class="language-plaintext highlighter-rouge">[env]</code>, <code class="language-plaintext highlighter-rouge">[hooks]</code>, and <code class="language-plaintext highlighter-rouge">[tasks]</code> blocks in project-level <code class="language-plaintext highlighter-rouge">mise.toml</code> files, prompting on first <code class="language-plaintext highlighter-rouge">cd</code> into a directory with an untrusted config and persisting the decision per file.</p>]]></content><author><name>Andrew Nesbitt</name><email>andrew@ecosyste.ms</email></author><category term="package-managers" /><category term="security" /><category term="reference" /><summary type="html"><![CDATA[A survey of install-script allowlist mechanisms across package managers and language ecosystems.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://nesbitt.io/images/boxes.png" /><media:content medium="image" url="https://nesbitt.io/images/boxes.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>