# cnpmcore: a self-hosted npm registry written in TypeScript

> cnpmcore is the TypeScript reimplementation behind the npmmirror.com registry, a private npm registry for enterprises that keeps its own package metadata, supports Elasticsearch for search and MySQL or PostgreSQL for storage, and it is the software behind the China npm mirror npmmirror.com.

**cnpm/cnpmcore** — Private NPM Registry for self-host. Example, npm registry Mirror on China https://registry.npmmirror.com

- Repository: https://github.com/cnpm/cnpmcore
- Website: https://npmmirror.com
- Stars: 736 · Forks: 113
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/cnpm-cnpmcore

## The TypeScript rewrite of cnpmjs.org that runs npmmirror

cnpmcore is a private npm registry for enterprise use, described as a reimplementation of cnpmjs.org in TypeScript. The primary language of the repository is TypeScript, and the application is published to npm under the name cnpmcore.

The proof that it works at scale is the deployment it powers. npmmirror.com is the China npm mirror hosted by cnpmcore, with infrastructure sponsored by Alibaba Cloud. That is the same registry the project description points at as its example, which tells you the self-hosted path and the public mirror path are the same codebase.

The architecture separates the npm-facing protocol from the operational surface. There is a registry HTTP API documented in docs/registry-api.md, which is what npm itself talks to. There is a separate internal API documented in docs/internal-api.md for direct HTTP requests covering package synchronization, administration and other advanced operations. Keeping those apart is the design decision that matters most in practice: the npm client protocol stays narrow, while the things an operator needs, syncing packages upstream and managing the registry, live behind their own endpoints.

Documentation is split by audience. DEVELOPER.md covers contribution, INTEGRATE.md covers integrating the registry into a build pipeline, and docs/deploy-in-docker.md covers configuration, startup and health checks for running it yourself.

## Two database engines and three compose files

Storage is configurable between MySQL and PostgreSQL, and the choice is visible in both the configuration and the development tooling.

The environment example ships both sets of credentials, one commented block per engine. The MySQL block sets CNPMCORE_DATABASE_TYPE=MySQL with the user, password and name, and the PostgreSQL block sets CNPMCORE_DATABASE_TYPE=PostgreSQL with its own user, password and name:

```bash
# CNPMCORE_DATABASE_TYPE=MySQL
# CNPMCORE_DATABASE_USER=root
# CNPMCORE_DATABASE_PASSWORD=
# CNPMCORE_DATABASE_NAME=cnpmcore

# CNPMCORE_DATABASE_TYPE=PostgreSQL
# CNPMCORE_DATABASE_USER=postgres
# CNPMCORE_DATABASE_PASSWORD=postgres
# CNPMCORE_DATABASE_NAME=cnpmcore
```

The development stack is expressed as compose files, and the repository ships three of them: docker-compose.yml for MySQL, docker-compose-postgres.yml for PostgreSQL and docker-compose-es.yml adding Elasticsearch. The MySQL file defines three services, a Redis service on redis:6-alpine with the port published, a MySQL service on the mysql:9 image started with the server character set set to utf8mb4 and the collation set to utf8mb4_unicode_ci, and a phpMyAdmin service on port 8080 for browsing the database.

That collation choice is the kind of detail worth noticing rather than skipping. A registry storing package metadata and tarball paths with mixed scripts and user text will hit encoding problems on a default collation, so utf8mb4 with unicode collation is a deliberate choice.

The scripts directory at the root holds prepare-database-mysql.sh and prepare-database-postgresql.sh, which bootstrap a fresh database in each engine, and there is a scripts/wait-for-ready.mjs file shipped with the package itself, which suggests the container startup waits for dependencies to answer before serving.

## Elasticsearch and Redis are separate concerns with their own settings

Beyond the primary database, two more services can be configured, and the environment example keeps them clearly separated.

Redis is present in the development compose file with a volume for its data and a restart policy of always. The commented command line in that file shows the shape of a production-ish configuration, starting the server with appendonly enabled and requiring the cnpm password, which suggests authentication is expected in a real deployment even though it is off in the local compose file.

Elasticsearch is optional and is controlled by its own group of variables. CNPMCORE_CONFIG_ENABLE_ES turns it on, CNPMCORE_CONFIG_ES_CLIENT_NODE points at the HTTP endpoint, and CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME and CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD hold the credentials. The example values point at http://localhost:9200 with the elastic user.

The Elasticsearch stack in the example environment is a full one rather than a single container. It sets the version of the Elastic products with STACK_VERSION at 8.7.1, with an arm64 variant commented out for platforms that need it, names the cluster with CLUSTER_NAME, and sets the licence mode to basic rather than a trial. Ports are exposed for Elasticsearch on 9200 and for Kibana on 5601, and memory limits for the Elasticsearch, Kibana and Logstash processes are all set to 1073741824 bytes.

One configuration line deserves attention in a different way. The example carries a sample encryption key, labelled as predefined and only to be used in proof of concept environments. Any real deployment needs its own value there, and the example says so in the comment directly above it.

The .devcontainer directory and the GitHub Codespaces badge in the readme mean you can get a working environment with MySQL, Redis and a preconfigured dev database without assembling that stack by hand.

## The application is an egg-bin project with strict script discipline

The package layout shows a Node application with a clear division between layers, and the exports map makes that explicit.

The package is an ES module whose entry point is./dist/app.js, with typings at./dist/app. The exports map exposes five subpaths, common, core, infra, port and repository, each with its own package.json and a mapping to the built JavaScript under./dist/app. That split is the usual layered shape for a server-side application, with port holding the interfaces, repository the storage implementation, infra the wiring, core the domain and common the shared utilities.

The scripts are where the developer workflow is enforced:

```bash
    "dev": "egg-bin dev",
    "dev:postgresql": "CNPMCORE_DATABASE_TYPE=PostgreSQL egg-bin dev",
    "typecheck": "tsc --noEmit",
    "fmt": "vp fmt",
    "fmtcheck": "vp fmt --check",
    "api-docs:lint": "redocly lint docs/openapi.yaml",
```

The dev script runs egg-bin, and there is a separate PostgreSQL variant that sets the database type through the environment before starting. The type check is a plain tsc --noEmit with no output emitted, and formatting and linting go through the vp tool, with a separate check variant for CI.

The API documentation is treated as a build artifact rather than a hand-written file. redocly lints docs/openapi.yaml, and a bundle step exists alongside it, which means the OpenAPI document is validated in the same pipeline as the code. A redocly.yaml config file at the root governs that.

The rest of the layout is conventional: benchmark/ for performance work, test/ for the test suite, config/ for configuration, sql/ for SQL and a sql-review-override.yml for database change review, app.ts as the application entry, and CLAUDE.md, SECURITY.md and a renovate.json for dependency automation.

## A public mirror with a stated usage policy

npmmirror.com is a public service, and the readme publishes the rules for using it, in both English and Chinese, side by side.

The stated purpose of the policy is a more stable and reliable service. The rules are that the registry should not be abused with excessive crawling or scraping, that automated access generating unusually high traffic may be rate limited without notice, and that anyone with large scale usage needs, or who is affected by rate limiting, should open a GitHub issue to make contact.

That third point is the one that matters for a private mirror. Self-hosting cnpmcore is not the same as pointing at npmmirror.com, and the readme keeps those two things distinct by giving the policy its own section under the npmmirror.com heading rather than under the general project documentation.

The operational side of the public service has its own documentation too. Clearing the CDN cache is a documented request procedure with a dedicated file, docs/cdn-cache-clear.md, and there is a separate configuration folder for the OpenAPI specification used by the redocly tooling.

For a self-hosted instance, the equivalent operational question is how you clear your own cache, and the answer for the public instance is a documented request rather than a self-service command. That is worth noting if you are planning to run cnpmcore behind your own CDN.

## Actively released, with a parallel 3.x line still shipping

The maintenance picture here is unusually healthy, and it is visible in the release list rather than in a status badge.

Three releases appear at the top. v4.37.4 was published on 2026-10-18, v4.37.3 on 2026-10-15, and v3.84.3 on 2026-10-15, the same day as the 4.37.3 tag. The repository is not archived, the default branch is master, and the last push was on 2026-10-19.

The presence of v3.84.3 alongside v4.37.x matters. There are two maintained major lines, and a patch release landed on the older one on the same day as a patch on the newer one. For an organisation running cnpmcore as infrastructure, that is a meaningful commitment: it means there is likely still a path for deployments that have not migrated to the 4.x line.

The licence is MIT, with the LICENSE file at the root and the same identifier declared in the package metadata. The package is published to npm as cnpmcore, so it can be installed rather than only built from source, and the published file list is narrow, covering the dist directory plus scripts/wait-for-ready.mjs.

The project runs on Node with a .node-version file pinning the version, a CI workflow for Node with a badge on the readme, codecov coverage reporting, a FOSSA licence and security scan, and Renovate for dependency updates. The most recent CHANGELOG.md at the root is where version level changes are recorded.

## Conclusion

cnpmcore suits an organisation that wants to run its own npm registry, mirror packages for internal use or cut dependence on a public registry, and that has the capacity to run MySQL or PostgreSQL, Redis and optionally Elasticsearch. It does not suit a team that wants a one-command turnkey install or a project that expects a small surface, because the application itself is a multi-service system with an Elasticsearch option and its own administration API. Before deploying, read docs/deploy-in-docker.md for configuration, startup and health checks, copy .env.example to .env and set the database credentials and a fresh ENCRYPTION_KEY, and confirm your plan against the npmmirror.com usage policy if you intend to point automated traffic at a public instance.

## FAQ

### What is cnpmcore and what is it used for?

cnpmcore is a private npm registry for enterprise use, a TypeScript reimplementation of cnpmjs.org. It is the software behind npmmirror.com, the China npm mirror hosted by cnpmcore with infrastructure sponsored by Alibaba Cloud.

### Which databases can cnpmcore use?

MySQL and PostgreSQL. The choice is made with the CNPMCORE_DATABASE_TYPE setting, and the repository ships docker-compose.yml for MySQL and docker-compose-postgres.yml for PostgreSQL, plus prepare-database-mysql.sh and prepare-database-postgresql.sh scripts to bootstrap each.

### How do I deploy cnpmcore with Docker?

The documentation for configuration, startup and health checks is in docs/deploy-in-docker.md. The repository ships a.env.example to copy from, docker-compose files for the MySQL, PostgreSQL and Elasticsearch setups.

### What is the difference between the registry API and the internal API in cnpmcore?

The registry HTTP API in docs/registry-api.md is what npm itself talks to. The internal API in docs/internal-api.md covers direct HTTP requests for package synchronization, administration and other advanced operations, keeping operational endpoints out of the client-facing protocol.

### Can I run cnpmcore locally with GitHub Codespaces?

Yes. The readme points at the GitHub Codespaces template as the quickest way to get a ready-to-code environment with MySQL, Redis and a preconfigured dev database. The Dev Containers extension works locally too, with details in .devcontainer/README.md.

## Sources

- [cnpm/cnpmcore on GitHub](https://github.com/cnpm/cnpmcore)
- [License: MIT](https://github.com/cnpm/cnpmcore/blob/master/LICENSE)
- [Project website](https://npmmirror.com)
- [README](https://github.com/cnpm/cnpmcore/blob/master/README.md)
- [Releases](https://github.com/cnpm/cnpmcore/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cnpm-cnpmcore
