← cd ~/lab

./run npmshield-impero-demolito-da-un-agente

NPMShield: I designed a 7-database empire, an agent razed it to the ground

How I sketched out an anti-malware platform for npm in Claude Code — sandbox, AI, 7 databases — and how an adversarial review cut it down to a CLI and…

status
poc
project
npm-shield
updated
2026-07-08
tags
#npm#supply-chain#malware#static-analysis#sandbox#claude-code
Diagram of a two-tier npm package analysis pipeline: fast static verdict and asynchronous dynamic verdict in a sandbox

The first file I wrote for NPMShield wasn’t code. It was a BOOTSTRAP.md that told Claude Code, in sequence: build the core engine, then the intelligence layer, then the CLI, then the API, then the dashboard, then the Kubernetes deploy. Six phases, an empire. The second file I wrote, three weeks later, was a one-word verdict: PIVOT. This diary is about what happened in between — and why the most useful moment of the project was when an agent demolished my plan.

The idea (the good one, the one that stuck)

Malware in the npm supply chain is no longer a theoretical problem. Shai-Hulud 2.0, in November 2025, compromised 796 packages accounting for 20 million weekly downloads, propagating across more than 25,000 GitHub repositories. CanisterWorm — which surfaced recently — uses the ICP blockchain as a command-and-control channel: no domain to seize, no takedown possible. The common pattern is malicious code inside legitimate packages, in postinstall hooks that ship process.env off to an external server. CVE scanners don’t see it: there’s no known vulnerability, there’s a behavior.

The idea is an antivirus for the supply-chain era: intercept that behavior before install, with static analysis, a dynamic sandbox, and threat intelligence assisted by Claude.

The bootstrap: sketch everything before writing anything

The approach I used — and would use again — is to have Claude Code draw the entire system before writing a single line of real code. Each module starts life as a BOOTSTRAP.md with an objective, a stack, and files to create, followed by an invocation:

cd core
claude code "Costruisci il core analyzer. Parti dallo StaticAnalyzer:
parsing di package.json, rilevamento di postinstall sospetti,
download esterni, modifiche di sistema."

The technical core rests on three pieces in a row:

  1. StaticAnalyzer — AST with tree-sitter (via py-tree-sitter) plus Semgrep rules and custom .scm queries. The patterns that matter are few and concrete: eval(), Function(), child_process.exec, fs.writeFileSync targeting .bashrc, dynamic require() of base64-decoded strings, exfiltration of process.env. GuardDog as a baseline gets 93.32% F1; combining techniques gets you toward 96%.
  2. DynamicSandbox — running the install in isolation, observing syscalls and network traffic.
  3. ThreatIntel — Claude synthesizing the final package-level verdict, where reasoning quality genuinely matters.

So far, sensible. The problem was everything else I’d bolted on around it.

The hard call: when the plan collapsed

Before writing code I ran a research and validation phase — four dossiers (market, domain, technical feasibility, adversarial review) plus a synthesis, all produced by agents inside Claude Code. The adversarial review was brutal, and it was right.

My original plan was: 14 features, 7 databases, multi-language, AI-everywhere, solo founder, 8 weeks, against three funded incumbents (one that had raised $65M, another $70M, a third valued at $1B). Verdict: a combination with a historical success rate close to zero. Not “Kill,” not “Ship.” Pivot: keep the problem, gut the solution.

Out of that cut came the most valuable part: the wedge, the crack no competitor is guarding. It’s called cross-package publish fan-out velocity correlation. When a single npm publisher pushes patches across N packages within minutes, that’s the signature of a worm. No single-package scanner sees it — Socket, Snyk, GuardDog, Phylum analyze one package at a time. By sitting on the consumer side (the machine doing the install) and aggregating publish telemetry across customers, that correlator would have caught Shai-Hulud 2.0 hours before the registry moderated it.

Everything else ended up on the “cut from MVP-0” list: web dashboard, FalkorDB, pgvector, eBPF/Tetragon, Firecracker, federated learning, self-hosted enterprise, Kubernetes. MVP-0 is now a Go CLI and a FastAPI service on a single VM with Docker Compose. And there’s an honest kill switch written in black and white: if within 12 weeks of launch I don’t reach 100 weekly active developers, the project shuts down.

The number that was a physical lie

The original spec promised <200ms full scan. The feasibility phase took it apart with physics: a Firecracker microVM with snapshot restores in ~28ms, but a real scan includes fetching the tarball (50–500ms of network), extraction, parsing, and for the dynamic case an npm install with syscall capture. It doesn’t fit in 200ms. Full stop.

The fix was to split into two tiers and rewrite the public SLO:

verdetto statico:  < 200ms p95   (sincrono, cache-hit: AST + euristiche)
verdetto dinamico: < 30s p95      (asincrono, via WebSocket, sandbox)

Corollary: Claude can’t live in the hot 200ms path. Haiku has a TTFT of ~300ms; a 500-token verdict is ~3s. Claude lives in the dynamic tier, not the synchronous one.

Honest gotchas

  • README and reality diverge, and that’s fine. The IDEA.md is an enthusiastic pitch with a business plan, exit strategy, and funding roadmap. The later CLAUDE.md explicitly marks it as “historical context, non-authoritative.” I left both: the story of the pivot is more credible than the cleaned-up plan.
  • Anthropic doesn’t have an embeddings API. I’d taken it for granted in three places in the plan. It doesn’t exist: the official cookbook points to Voyage AI. I use voyage-code-3 at 512 dimensions, int8-quantized — at $0.18 per million tokens, backfilling the entire npm registry costs about $27. Practically free.
  • The license that poisons SaaS. FalkorDB is SSPLv1: in a SaaS it forces you to open the entire stack. Decision: FalkorDB only in the self-hosted edition (where the customer is the operator), Postgres + Apache AGE in the SaaS. And for MVP-0/1, no graph DB at all: a recursive CTE in Postgres holds up under a million nodes.
  • The irony of postinstall. An anti-supply-chain tool distributed via npm with a postinstall can become the compromise vector. Iron rule: every release signed with Sigstore, the postinstall surface reduced to downloading a binary with a verified checksum. And a serious evaluation of not distributing via npm at all.
  • The cost of AI running away. Sonnet 4.6 on one package costs ~$0.020 with prompt caching. The strategy is Haiku-first, escalating to Sonnet only below 0.85 confidence: a blended cost of ~$8 per 1000 scans, with a circuit breaker to a local model when the daily budget is exceeded.

How it’s going

Diary-level honesty: there’s no code yet. NPMShield is in the planning/POC phase. What exists is the synthesized research, decisions locked into a CLAUDE.md, and a BMAD roadmap that goes from product brief to PRD to architecture to implementation. The next concrete action isn’t “write the StaticAnalyzer” — it’s formalizing the MVP-0 brief and signing three design partners before touching the keyboard. My primary test artifact, when the code arrives, will be a comparison harness that runs nightly against Socket, Snyk, and npm audit on public malware corpora.

What I learned

That the value of a bootstrap in Claude Code isn’t how fast it produces code — it’s how fast it produces a plan detailed enough to be demolished. Sketching the 7-database empire was worth it: without that complete plan, the adversarial review would have had nothing specific to attack. The pivot didn’t come from scaling back the ambitions, but from having made the ambitions concrete enough to be falsified.

And the lesson happiest to be unpopular: the most useful thing I wrote for an ambitious project was the condition under which it gets shut down. A kill switch at 100 users in 12 weeks is worth more than ten pages of go-to-market. If you’re alone against people who’ve raised eighty million, the discipline of what not to build is the only competitive advantage you can afford.