Publish .NET packages
kanject packages is the release control plane for NuGet and SDK repositories. It turns the evaluated MSBuild graph into a reviewed public/private policy, proves versions are immutable, stages candidates to a real feed, builds clean consumers, and journals the approved publication.
- Choose a split or all-private repository policy and classify every packable project
- Gate closure, package metadata, version floors, and already-claimed identities before release
- Stage unique
-rc.Ncandidates and build consumers from the actual candidate feed - Promote only after approval, using a trusted publisher and a resumable operation journal
Use the right shipping family
Use kanject packages when the repository's product is one or more NuGet packages. Use kanject aws distribute when the product is a downloadable application routed by edition and runtime identifier. A legacy kind: "library" distribution cell still works for a deliberately product-coupled artifact, but it is not the normal authority for an SDK surface.
Start with policy, not a push command
init --split scaffolds a public/private policy and human-review list; --all-private creates the BaaS-style shape with no public destination. The policy names feed environment variables rather than embedding credentials. plan --write maintains the reviewable public list; verify is the blocking local and CI gate.
# Pin the CLI in the repositorydotnet new tool-manifestdotnet tool install Kanject.Cli# Pick the repository shapedotnet kanject packages init --split# or: dotnet kanject packages init --all-private# Make the intended package surface reviewabledotnet kanject packages plan --writedotnet kanject packages verify --advisories - Closure: a public package cannot depend on a private first-party package.
- Classification: every packable project resolves to one deliberate tier.
- Contract: effective dependency floors cannot exceed the sibling versions being built.
- Content: configured public metadata, license, symbol, and SourceLink rules are enforced.
- Fail loud: an unreadable project graph or unknown policy member is a failure, never a skipped check.
Audit versions before anyone waits for approval
A package identity is permanently claimed by its version and contract. versions --packed-dir compares the packed payload plus normalized dependency contract with the destination feed. If the bytes or dependency contract moved, the project needs a new VersionPrefix; the CLI names it but never guesses whether the bump is patch, minor, or major.
# Pack the exact release inputsdotnet pack Acme.Sdk.slnx -c Release -o packed# Refuse versions already claimed by different package contractsdotnet kanject packages versions --packed-dir packed --strict# Preserve the evaluated dependency graph as release evidencedotnet kanject packages graph --out package-graph.json Evidence first, candidate second
The normal pipeline compiles once and packs twice: stable identities travel in the protected artifact, while the same assemblies are packed with a feed-derived -rc.N suffix for testing. The hostile build role has no release secret and can write only to the candidate repository.
# Build-stage evidence — no package is pusheddotnet kanject packages publish --profile release-evidence --packed-dir packed# Candidate stage — writes only to the configured rc feeddotnet kanject packages publish --profile candidate --packed-dir packed-rc --ci# Prove clean consumers restore and build from the real candidate feeddotnet kanject packages smoke feed --plan release-plan-rc.json --build smoke feed --build installs each planned candidate into a clean consumer and builds it with analyzer failures elevated. Packages that need a runtime companion or literal source activation keep their isolated smoke and add a composite fixture; packages smoke fixture init scaffolds that proof fail-loud rather than inventing a green no-op fixture.
Approve the bytes that were tested
After a person validates the RC, the publish stage recomputes routing from deployment-owned inputs, byte-verifies any existing identity, pushes dependency-first, and records every operation. It installs the infrastructure-pinned released CLI—not the repository's candidate tool—and its buildspec is controlled by the release stack.
# Trusted publish stage, after manual approvalPUBLISH_PUBLIC=true dotnet kanject packages publish --profile approved-release --packed-dir packed --ci# A partial publication resumes from its durable journalPUBLISH_PUBLIC=true dotnet kanject packages publish --profile approved-release --packed-dir packed --resume release-journal.json --ci The three receipts
packages init → plan → verifyturns the real project graph into a deliberate, reviewable package surface.versions --packed-dir --strictcatches claimed identities before candidate staging.- Candidates live in a separate rc feed and must restore/build from that feed before approval.
- The trusted publisher ships the approved stable packages dependency-first and persists plan, preflight, and journal evidence.