I maintain a lot of open source projects, and one key question when you're shipping updates is where that publish occurs. Most projects use a CI environment to publish build artifacts to npm. It’s great, after all, to have a controlled "clean room"-like environment to produce your build and publish from there, especially when working with a large team.

I literally said to my co-worker sitting next to me, “This feels way less secure”.

Others, however, prefer to publish directly from their own local workspace. I was in this camp for a long time, and believe it or not, there are reasons to do this although local publishing has significant drawbacks—not least of which is the ability to accidentally publish things that should be private. This, btw, is probably how the Claude Code source map leaked (btw, easy to work around this by explicitly listing the files you want to publish in the files property of package.json). But publishing from local has some benefits too.

Sometimes builds fail in really weird ways, where they don't actually "fail" (exit code 0), but in practice they fail because the output is incorrect. I felt much more secure getting to manually review the build artifacts, with my own eyeballs, before sending them out to thousands of people. Another common issue is npm outages. Sometimes you’re publishing a monorepo and half the packages publish, but npm randomly stops accepting new publishes halfway through (or some other weird edge case). Having your local state persist in that moment is really helpful for getting things back on track.

But the biggest reason I didn't want to move my publish workflows to CI was security. My projects are almost all open source. The repos are not private, the builds are not private, the tests are not private...nothing is private. You know what is private? My personal machine. So moving my private publish workflow into a CI that I don’t know the security of wasn't appealing. Especially not GitHub Actions. Can it be locked-down and secured, of course, is that what I want to spend my time doing? No.

So for a long time I didn’t.

Coveted trusted publisher provenance checkmark

Trusted Publishers” forced me to change. It effectively required maintainers to use either GitHub Actions, GitLab CI, or CircleCI to publish their packages in order for them to be considered “trusted.” To publish to npm the old-school way, you normally need to authenticate the npm CLI instance with an account that has write access to the project you are publishing. Typically this involved a 2FA login flow, but Trusted Publishing changed that. Instead, you tell npm which workflow file in which repository on GitHub publishes to each package on npm, and then GitHub uses its sanctioned OIDC to generate tokens that allow it to publish. No user keys required.

Setting up trusted publisher connection for dmux.ai publishing

When set up correctly, your packages on npm get a nice little green checkmark next to their version, telling you this was published from the approved workflow. However, all this does is move the burden of security away from managing npm tokens, something we all know to do, over to GitHub Actions—not exactly a place known for quality engineering. I didn’t like this. While going through the arduous task of setting up dozens of “Trusted Publisher” connections, I literally said to my co-worker sitting next to me, “This feels way less secure. Now all someone has to do is get some code to run in a workflow.” Sure enough, it was GitHub Actions that was exploited to allow the Tanstack/"Mini" Shai-Hulud infection to make it to npm. What’s the solution? Frankly, I don’t know. As much as I hate the idea, perhaps some kind of 2FA—but even then, infections injected into build artifacts would be effectively invisible to maintainers. One thing is for sure, this was not @tannerlinsley or any other maintainer's fault.

Globally, we’re in a tough place from a security perspective, and I don’t think it will get better anytime soon. But at least let's all agree that perhaps GitHub shouldn’t be holding the keys to all the kingdoms.