Securing Open-Source Nostr Libraries with Claude Code
Just over a year ago, I was obsessed with identity management. I was trying to build an app and kept getting tripped up on the idea of hosting my own authentication service. Then I discovered Nostr and realized I didn't have to. In that process, I ended up rolling up my sleeves and building.
I was amazed that I had done the work — the rush of `npm publish`, thinking... wow this is in the world. That's what happened with the Nostr libraries; Seven npm packages, built across 2024 and early 2025. And at the time, they were covering everything from cryptographic primitives to WebSocket relay management to Node-RED integration. They worked. And I hadn't touched them in months.
The Ecosystem Nobody Asked For
When I first got into Nostr development, I had basic questions. How do I generate a keypair? How do I sign an event? How do I connect to a relay and filter messages? And there wasn't a lot of documentation. The Nostr community vibe was more "read the NIPs and figure it out" than "here's a library that handles it."
Now, with me actively Agentic Coding everyday, I sat down with Claude Code and we did something I couldn't have done alone, we cleaned up ALL the outstanding issues I didn't have the brain-space to complete in any reasonable amount of time. Not because I lack the skill — but because the sheer surface area of maintaining seven interconnected packages across security vulnerabilities, linting standards, documentation, and module compatibility is the kind of work that eats weeks of solo developer time.
PLOT SPOILER !!! ... We did it in a day.
This is the story of that day. But really, it's a story about what happens when open-source maintenance meets AI collaboration — and why I think the result has genuine civic value.
The Vulnerability Problem
Here's something they don't tell you when you first `npm init`: your dependencies have dependencies, and their dependencies have dependencies, and somewhere four levels deep there's a package with a HIGH severity CVE that `npm audit` will scream about every time you install anything. I had written the libraries myself and I had no idea how to navigate the errors.
- nostr-crypto-utils became the foundation — key generation, event signing, signature verification, NIP implementations. Everything else builds on it.
- nostr-websocket-utils handles the WebSocket lifecycle — connecting to relays, reconnecting when they drop, queueing messages, heartbeat monitoring.
- nostr-nsec-seedphrase gives you BIP39 mnemonic support for Nostr keys.
- nostr-auth-middleware** & nostr-biometric-auth-utils bring Nostr identity into Express.js applications.
- nostr-dm-magiclink-utils enables magic link authentication through encrypted DMs.
- node-red-contrib-nostr brings the whole thing into Node-RED for visual flow programming.
Seven packages. All TypeScript. All published to npm under my GitHub for Humanjava Enterprises. All... sadly accumulating dust and security warnings.
When I came back to these repos after months away, the audit reports were ugly. The headline: **elliptic** — a HIGH severity vulnerability in a cryptographic library — was sitting in my production dependency chain. In a *crypto* library. The irony was not lost on me.
The path was:
`nostr-crypto-utils` → `crypto-browserify` → `elliptic` → GHSA-vjh7-7g9h-fjfh.
A vulnerability in a package I'd included as a WebCrypto polyfill fallback. The thing is, I'd set `engines.node >= 18` from the start. Node 18 ships with WebCrypto built in. Every modern browser has `globalThis.crypto`. The fallback was dead code — but it was dead code with a live vulnerability, and it was the only production-facing HIGH across all seven repos.
Beyond that, `rimraf` was pulling in vulnerable versions of `minimatch` through `glob` in three repos. `serve` was doing the same in one more. All dev dependencies, but still — noise that obscures signal.
If you've ever stared at an `npm audit` report and felt that particular mix of guilt and paralysis — yeah. That's where I was.
The Day We Fixed Everything
To start, I have been working on all sorts of "Default world" stuff. Client work, passion projects and everything. I'd taken a break form the Nostr community but a post reminded me of why I have to refocus my time.
Nostr user "Oren ☂️ #BIP-128" reached out and asked:
> *"Does node-red-contrib-nostr work on latest Node RED? I tried to install it from the User Settings palette, and it says Cannot find module '/data/node_modules/nostr-websocket-utils/...'"*
The bug was an issue that I'd sort of known about, but had no way to trace it... and by the time I had built up to the "destination", I'd spent all my time debugging it and fixing; and the whole ship-it mentality was in full effect. Turns out, the root cause was subtle and genuinely interesting and hard to figure out with some AI assistance.
It was that push to lean in. I'd been working with Claude Code on my other project and decided to spend a few hours on an ESLint 8-to-10 migration across all seven repos and we had momentum. We co-wrote a remediation plan... Not vague intentions!!! A specific, file-by-file plan covering every edit across every repo, with verification steps. Then I typed: terminal prompt:> implement it.
The Cascade
Here's where maintaining an ecosystem gets interesting. Removing `crypto-browserify` from `nostr-crypto-utils` means publishing a new version. Publishing a new version means updating all six downstream repos. Updating them means verifying their builds still work, their tests still pass, their audits come back clean. Then publishing *those*. Then verifying again.
We did all of it. `nostr-crypto-utils@0.4.17` went up first. Then cascading updates across all downstream consumers. Every repo: lint clean, tests passing, `npm audit --omit=dev` showing zero vulnerabilities.
Zero. Across all seven. Never in my wildest dreams could I have done that on my own. I will FOREVER be a fan of Agentic Coding as a result.
The fix is a well-known pattern: drop a `package.json` containing just `{"type": "commonjs"}` into the `dist/cjs/` directory. This overrides the parent package's module type for everything in that folder.
One file. Fourteen characters of JSON. Problem solved.
We (more like Claude Code) added it to the build script so it regenerates automatically, verified it fixed the require chain, published `nostr-websocket-utils@0.3.16`, updated `node-red-contrib-nostr@0.1.3`, and told the guy on Nostr to try again.
That entire debugging and fix cycle — from "someone can't install my package" to "here's the published fix" — happened in real time, in one session.
The Remaining Noise
Transparency matters. After all this work, `npm audit` still shows vulnerabilities in every repo. Here's what they are and why they don't matter:
Every remaining finding is a dev dependency only; never shipped to production, never runs in end users' environments.
They're all in the most mainstream, well-maintained tools in the JavaScript ecosystem: ESLint, TypeScript-ESLint, Vitest, Typedoc.
The specific CVEs are ReDoS vulnerabilities in `minimatch` and `ajv` — meaning an attacker would need to craft a malicious glob pattern and feed it to your *linter*. Not a realistic threat model.
There is literally nothing better to switch to. These are the gold-standard tools. Upstream maintainers are aware. Fixes will come in their own time. `npm audit` doesn't distinguish between "minimatch processing untrusted user input in production" and "minimatch helping ESLint find your TypeScript files." The severity is inherited from the CVE regardless of context.
A year ago, I didn't understand the nuance of this and chased every bug. Now I have a different mindset. Document it and move on ... Every README now documents this clearly so anyone evaluating these packages knows exactly where things stand.
What This Actually Means
When I first got into all this civic minded software development / before Claude, before any AI tools — I would hit an `npm audit` warning and spend *days* forking repos. Trying to understand dependency trees four levels deep. Breaking things. Reverting. Breaking them again. Often ending up right back where I started, having learned a lot but fixed nothing.
The thing that's different now isn't just speed, though the speed is remarkable. It's the ability to hold the entire ecosystem in working memory.
Seven repos. Dozens of interconnected dependencies. Source code, build configs, webpack setups, package.json files, published npm versions, downstream consumers. Claude Code could track all of that simultaneously while I focused on decisions: what's the right approach? Should we publish now or wait? Is this fix correct?
That's collaboration. Not "AI wrote my code." Not "I prompted a chatbot." Two perspectives working together on a problem that was too wide for one human to efficiently tackle alone.
The Civic Angle
Nostr is infrastructure. Not the fun kind — not the "check out my new app" kind. The plumbing kind. Decentralized identity. Censorship-resistant communication. Cryptographic verification of authorship. These are the primitives that a free internet needs, and the developer experience around them is still rough.
Every library that makes it easier for the next developer to connect to a relay, sign an event, or verify a key — that's a small contribution to something larger. And every vulnerability we remove, every ESM/CJS compatibility issue we fix, every README we update — that's reducing friction for people we'll never meet.
Someone in a Node-RED Docker container somewhere tried to install `node-red-contrib-nostr` and hit a wall. They reached out. We fixed it. That's the open-source loop working the way it's supposed to.
These seven packages aren't going to change the world. But they might save some developer a few days of frustration. They might be the thing that lets someone prototype a Nostr integration without having to understand the entire protocol stack first. And they're secure, documented, and maintained — which is more than I could say a week ago.
---
## The Packages
All available on npm under the [HumanjavaEnterprises](https://github.com/HumanjavaEnterprises) GitHub org:
- nostr-crypto-utils — Core cryptographic primitives for Nostr
- nostr-websocket-utils — WebSocket client/server with reconnection and message queueing
- nostr-nsec-seedphrase — BIP39 mnemonic support for Nostr keys
- nostr-auth-middleware — Express.js authentication middleware using Nostr identity
- nostr-biometric-auth-utils — WebAuthn biometric auth for Nostr
- nostr-dm-magiclink-utils — Magic link authentication via Nostr encrypted DMs
- node-red-contrib-nostr — Node-RED nodes for Nostr protocol integration
Zero production vulnerabilities. All seven. And if you read all the way to here ... Go build something and ping me on Nostr or X and make the world 1 project more interesting that it already is. Be curious and follow that novelty ... that's been my North Star since the lockdowns.
*This article was written in collaboration with Claude Code (Claude Opus). Vergel provided the direction, decisions, and lived experience. Claude provided the technical execution. Together they co-authored the prose with final edits and reads of course 100% human. The vulnerability remediation work described here was performed collaboratively in real time.*