en

Introduction

What PCPM does for you, the problems it solves, and how it relates to pnpm, CPM, and the NuGet ecosystem.

PCPM is a content-addressable package manager for .NET, built on top of Central Package Management. It brings the parts of pnpm that actually move the needle — a global store, hardlinks, a content-hashed lockfile, and strict resolution — to the NuGet ecosystem.

PCPM does not invent a new manifest format. It writes the same Directory.Packages.props and <PackageReference /> you already use. dotnet restore still does the restore; MSBuild still does the build. PCPM just changes where the bytes come from, which version is pinned, and what runs in CI.

What it does for you, concretely

These are the things people switch to PCPM for. The rest of the docs walk through how each one works.

70–90% less disk on the NuGet cache

Without PCPM, every distinct (id, version) is unpacked once per project in ~/.nuget/packages. On a 30-project monorepo with 200 unique packages, that’s roughly 8 GB of duplicated bytes even though the contents are byte-identical.

With PCPM, every distinct .nupkg lives once in a content-addressable store, hardlinked into ~/.nuget/packages. The store does not grow with the number of projects.

Reproducible builds, enforced

pcpm.lock records the resolved version and the sha256 of the .nupkg for every entry. pcpm ci is the build-server command: it refuses to proceed if the lockfile is stale, missing, or has hashes that don’t match the store. A clean CI run that suddenly resolves a new transitive becomes a build failure, not a silent regression.

Faster clean restores on CI

The first install downloads the world. Every subsequent install — including on a clean CI runner with a warmed cache — only fetches the deltas. The hardlink step is metadata-only.

Tells you who pulled in what

pcpm why <package> walks the lockfile and shows the full chain of dependents that brought <package> into your build. When a transitive bump surprises you, you know exactly which direct dependency to negotiate with.

Diagnoses your CPM setup before CI does

pcpm doctor runs a battery of checks against the current workspace and exits non-zero on real problems: missing ManagePackageVersionsCentrally, floating versions, hardcoded Version= in <PackageReference />, orphaned entries, missing entries, lockfile drift, and known CVEs from the NuGet feed. Wire it into your build gate.

Audits security and licensing, and ships an SBOM

pcpm audit scans the resolved graph against vulnerability advisories and license metadata. --output <dir> writes a CycloneDX SBOM plus a license report. JSON output is available for CI piping.

Migrates existing solutions in one command

pcpm convert walks a solution (with or without CPM) and rewrites it in place: hoists every per-project Version= into a <PackageVersion />, adopts an existing Directory.Packages.props if present, and writes pcpm.json + pcpm.lock. --dry-run previews the diff. --revert undoes a previous convert.

How it works under the hood

PCPM is a thin layer on top of the .NET SDK. The flow is:

  1. pcpm install reads Directory.Packages.props and the discovered .csproj files, and resolves the full transitive graph using the same NuGet.Versioning library dotnet restore itself uses — so range parsing, pre-release handling, and floating semantics stay consistent.
  2. Each resolved .nupkg is downloaded once, hashed, and stored under its content hash in the global store.
  3. The package is hardlinked (NTFS / APFS / ext4) from the store into the standard ~/.nuget/packages/<id>/<version>/ layout.
  4. dotnet restore reads the standard layout; nothing about the consumer side is custom.
  5. pcpm.lock records the resolved version and the sha256 of the .nupkg for every entry.

The hardlinks are free — zero extra disk, instant materialisation. dotnet restore sees a perfectly normal NuGet layout, so all the MSBuild magic keeps working.

A 30-second tour

# In a fresh checkout of your .NET project:
pcpm convert                     # migrate an existing solution, or…
pcpm init                        # …start fresh in a new workspace

pcpm add Serilog                 # latest stable → CPM + .csproj
pcpm install                     # hydrate the store, hardlinks, dotnet restore

pcpm why Serilog                 # who pulled it in
pcpm doctor                      # CPM + lockfile + CVE sanity check
pcpm audit                       # vulnerabilities + license report + SBOM
pcpm outdated                    # newer versions on the feed
pcpm ci                          # strict install for CI

No daemon, no background service, no manifest migration.

Next steps