How Claude Code's Source Code Leaked Through npm

·Operation Risk·5 min read

Translated from the original Korean post. 한국어 원문 보기 →

Nothing to patch — there was nothing to block

On March 31, the entire source code of Anthropic's Claude Code was sitting on npm, fully readable, via shipped source maps. Nobody hacked anything. No insider walked out with a drive. The published package just happened to include the debug files.

The leaked repo hit 1,100 stars and 1,900 forks almost immediately. But once I started reading it, "some code got out" wasn't really the story. The thing is built far more carefully than I expected.

Claude Code by the numbers

Start with size. 1,900 TypeScript files, over 512,000 lines of code, roughly 40 built-in tools, roughly 50 slash commands.

This is not "a CLI tool." It runs on the Bun runtime, and the terminal UI is React with Ink. The whole thing is a modular, tool-based architecture.

Writing half a million lines for a command-line tool means you're not treating it as a one-off utility. You're treating it as a platform you plan to run and extend for years. The intent stood out to me more than the line count.

What caught my eye in the architecture

The tool system

The plugin-style tool architecture was the most interesting part. File reads, bash execution, web fetch, LSP integration — each one is its own isolated tool, and each carries its own permissions. The base tool definitions alone are 29,000 lines of TypeScript.

interface Tool {
  name: string;
  permissions: PermissionGate;
  execute(context: ToolContext): Promise<ToolResult>;
}

Look where permissions live: inside the tool interface itself. Every time you add a capability, you're forced to declare what that capability is allowed to do. Anyone who has run microservices and tried to bolt authorization on after the fact will recognize immediately why this belongs at the interface level from day one.

The query engine

A 46,000-line query engine handles every LLM API call, plus streaming and caching. It's the single largest module in the codebase. The heart of this tool is the pipeline between it and the model, and that's honestly where the code piled up.

Multi-agent orchestration

There's a "swarm" system that spawns subagents for complex work. Each agent runs in its own context with its own tool permissions. In human terms: split the job, hand pieces to workers with restricted access. It looks a lot like how you'd design worker isolation in a distributed system.

Reading the technology choices

Follow the team's decisions and a few things stand out.

Bun as the runtime was a bet on fast startup and dead code elimination. A CLI boots fresh every single time, so cold start is perceived performance. Picking Bun over Node says they took startup latency seriously.

Building the terminal UI as components with Ink was a bolder call. They pulled state management in and made a terminal interface that behaves like a web app. From a maintenance angle I get it. Write terminal output logic procedurally for long enough and you end up with code nobody wants to touch.

Validation runs through Zod v4. Every tool input, every API response, every config file has schema validation on it. It's a stance of trusting neither external input nor model output, which is a reasonable line to hold when LLM responses are nondeterministic.

So how did this happen

The cause is almost anticlimactic. Source map files got bundled into the npm package.

Source maps exist for debugging. They map minified, bundled code back to the original source, which is great during development. The problem is what happens when they ride along into a production npm release: you're effectively publishing your whole codebase in readable form.

Calling it a security incident feels off. Nobody exploited a vulnerability. Files that shouldn't have shipped simply shipped. One line missing from the last step of the build pipeline. There's something a little bitter about a tool built to help you write better code tripping over its own build config.

What to take from this as a developer

A couple of things worth keeping. First, before you publish to npm, run npm pack --dry-run and actually look at the file list with your own eyes. That habit alone catches most of what happened here.

Second, treat source maps as source code. Unless it's deliberate, they don't belong in a production package. The most dangerous flags are the ones you leave on absentmindedly in the name of "easier debugging."

The architecture is worth studying on its own terms, separate from how it got out. Claude Code's tool system, permission gates, and multi-agent patterns are a solid reference for anyone building AI-powered tooling.

A leak on the surface, something else underneath

The leak itself is an unfortunate accident, no question. But the exposed code showed how high the baseline for AI coding tools has climbed. This isn't a thin API wrapper. Serious engineering went into building a production-grade developer experience.

Permission systems, multi-agent orchestration, IDE bridges, persistent memory management. Components at that level are starting to feel like table stakes in this space.

What stuck with me, though, wasn't the architecture. It was one line of build config. All that care poured into 5.12 million lines, and the tile that gave way was a trivial setting right before release. Run systems long enough and you see this scene over and over. What takes a system down is usually not the most complex part. It's the part everyone assumes is fine and nobody looks at again. This one just confirmed it one more time.

Was this post helpful?

One click helps me write the next one

#Claude Code#Source Code Leak#npm#Source Maps#Application Security