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:
pcpm installreadsDirectory.Packages.propsand the discovered.csprojfiles, and resolves the full transitive graph using the sameNuGet.Versioninglibrarydotnet restoreitself uses — so range parsing, pre-release handling, and floating semantics stay consistent.- Each resolved
.nupkgis downloaded once, hashed, and stored under its content hash in the global store. - The package is hardlinked (NTFS / APFS / ext4) from the store
into the standard
~/.nuget/packages/<id>/<version>/layout. dotnet restorereads the standard layout; nothing about the consumer side is custom.pcpm.lockrecords the resolved version and the sha256 of the.nupkgfor 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
- Installation — get
pcpmon yourPATH. - Quick start — the fastest path to a working setup.
- Migrating from CPM — convert an existing solution.
- Content-addressable store — the part of PCPM that does the heavy lifting on disk.
- CI integration —
pcpm cifor reproducible builds.