Payload 1.0.0
dotnet add package Payload --version 1.0.0
NuGet\Install-Package Payload -Version 1.0.0
<PackageReference Include="Payload" Version="1.0.0" />
<PackageVersion Include="Payload" Version="1.0.0" />
<PackageReference Include="Payload" />
paket add Payload --version 1.0.0
#r "nuget: Payload, 1.0.0"
#:package Payload@1.0.0
#addin nuget:?package=Payload&version=1.0.0
#tool nuget:?package=Payload&version=1.0.0
Payload
Payload is a build-time NuGet helper for packages that need to place bundled files into a consumer repository during build.
Use it when a package should bring along content such as:
.agents/skills/...- repository templates
- starter configuration files
- docs, examples, or small asset folders
Payload is intentionally narrow. It lets a parent package declare bundled content, ship that content inside the .nupkg, and copy it into the consuming repo with simple consumer-side control by PackageId + Tag. It does not try to be a deployment engine, template renderer, or config merger.
Features
- Parent packages declare bundled content with
PayloadContent - Parent packages can declare explicit file removals with
PayloadRemove - Consumers control copy behavior per
PackageId + TagwithPayloadPolicy - Content can be a single file or a whole directory
- Directories are copied recursively while preserving relative structure
- Copy decisions use file size plus SHA-256, not timestamps
- Parent-package
.targetsare generated automatically during pack - Repository root detection is built in, with explicit override via
PayloadRootDirectory - Consumer policies can override the destination base path through
OverridePath
Installation
Package authors reference Payload from the package that will ship content:
dotnet add package Payload
The authoring package then declares PayloadContent items in its project. During pack, Payload generates the package's build and buildTransitive assets and includes the authored content under payload/ inside the .nupkg.
Authoring in a Parent Package
Declare one or more PayloadContent items:
<ItemGroup>
<PayloadContent Include="content/skills/example-skill">
<Tag>ExampleSkill</Tag>
<TargetPath>.agents/skills/example-skill</TargetPath>
</PayloadContent>
</ItemGroup>
Meaning:
IncludeThe local file or directory to packageTagThe logical group name consumers can targetTargetPathThe destination path inside the consuming repository
If the source is a directory, Payload copies all files beneath it and preserves their relative layout under TargetPath.
Parent packages may also declare CopyOnBuild on PayloadContent.
- omitted means the parent default is
true CopyOnBuild="false"marks the payload as optional by default- a consumer may override that default with
PayloadPolicy
Parent packages may also declare PayloadRemove for explicit cleanup of files that should be removed from the consumer:
<ItemGroup>
<PayloadRemove Include=".agents/skills/example-skill/obsolete.md">
<Tag>ExampleSkill</Tag>
<CopyOnBuild>false</CopyOnBuild>
</PayloadRemove>
</ItemGroup>
Meaning:
IncludeThe relative destination file path to removeTagThe same logical group used byPayloadContentandPayloadPolicyCopyOnBuildOptional parent default for cleanup-only tags, resolved with the same precedence rules asPayloadContent
PayloadRemove is file-only. If it resolves to a directory, Payload warns and skips so it does not recursively delete consumer content.
Parent-side CopyOnBuild should be consistent within a tag. If PayloadContent and PayloadRemove items under the same PackageId + Tag disagree, Payload warns and falls back to true.
Consumer Control with PayloadPolicy
Consumers can opt out of specific tags:
<ItemGroup>
<PayloadPolicy Include="ParentPackage"
Tag="ExampleSkill"
CopyOnBuild="false" />
</ItemGroup>
Meaning:
IncludeThe package idTagThe tag declared by the parent packageCopyOnBuild="false"Stops future synchronization for that tag
This is intentionally conservative:
PayloadRemoveentries for that tag do not run- existing copied files are not deleted
- missing files are not restored
- future overwrites stop
CopyOnBuild resolution works like this:
- consumer
PayloadPolicyvalue, if specified - otherwise parent
PayloadContentorPayloadRemovevalue, if specified - otherwise
true
That means a parent package can ship optional payloads by declaring:
<PayloadContent Include="content/skills/example-skill">
<Tag>ExampleSkill</Tag>
<TargetPath>.agents/skills/example-skill</TargetPath>
<CopyOnBuild>false</CopyOnBuild>
</PayloadContent>
and a consumer can opt in explicitly:
<PayloadPolicy Include="ParentPackage"
Tag="ExampleSkill"
CopyOnBuild="true" />
File-Based Apps
Payload also works with file-based apps that use #:package.
For example:
#!/usr/bin/dotnet run
#:sdk Microsoft.NET.Sdk
#:package ParentPackage@0.1.0-alpha
Console.WriteLine("Hello");
The parent package's buildTransitive targets still flow in, so bundled PayloadContent can be copied during build.
There are two practical constraints:
- Relative
TargetPathvalues still need a detectable repo root such as.git,.hg,.svn,.vs,.idea,*.sln, or*.slnx - if that is not available, set
PayloadRootDirectoryexplicitly with a file-based app property directive
Example:
#:property PayloadRootDirectory=.
Consumer-side PayloadPolicy is different. A file-based app does not have an inline ItemGroup surface, so PayloadPolicy should be declared from a sidecar Directory.Build.targets file placed next to the .cs file or in a parent directory:
<Project>
<ItemGroup>
<PayloadPolicy Include="ParentPackage"
Tag="ExampleSkill"
CopyOnBuild="false" />
</ItemGroup>
</Project>
Destination Resolution
By default, destination paths are treated as relative to the detected repository root.
If a consumer wants a specific tag to use a different destination base path, they can set OverridePath on PayloadPolicy:
<ItemGroup>
<PayloadPolicy Include="ParentPackage"
Tag="ExampleSkill"
OverridePath="/Users/david/custom-root" />
</ItemGroup>
Rules:
- parent-authored
TargetPathis always relative - absolute
TargetPathvalues are rejected with a warning - if
OverridePathis omitted, Payload uses the detected repository root orPayloadRootDirectory - if
OverridePathis present and rooted, it becomes the destination base path - if
OverridePathis present and relative, it is resolved relative to the consuming project directory
Example:
- parent
TargetPath:.agents/skills/example-skill - consumer
OverridePath:/Users/david/custom-root - final destination:
/Users/david/custom-root/.agents/skills/example-skill
How Repository Root Detection Works
Relative destinations are resolved from a detected repository root.
Payload walks upward from the consuming project directory and looks for practical repository markers such as:
.git.hg.svn.vs.idea*.sln*.slnx
Consumers can bypass detection completely:
<PropertyGroup>
<PayloadRootDirectory>/path/to/repo/root</PayloadRootDirectory>
</PropertyGroup>
If root detection fails for a relative payload, Payload warns and skips the copy rather than guessing.
Copy Behavior
When copying is enabled:
- file sources copy as files
- directory sources copy recursively
- explicit
PayloadRemoveentries delete matching files - if a
PayloadRemovepath resolves to a directory, Payload warns and skips it - local modifications are not treated as a supported customization model
- package-provided content may overwrite local files while synchronization remains enabled
Copy decisions follow this order:
- destination missing → copy
- file size differs → copy
- file size equal → compare SHA-256
- hash differs → copy
- hash equal → skip
This avoids relying on modified timestamps and works for text files, binaries, docs, and assets.
When copying is disabled with CopyOnBuild="false", Payload remains conservative:
PayloadRemoveentries for that tag are skipped- existing copied files are left in place
- missing files are not restored
- no explicit removals are executed
Generated Package Layout
When a parent package references Payload and packs successfully, Payload generates:
build/<PackageId>.targetsbuildTransitive/<PackageId>.targets- packaged authored content under
payload/...
That means the authoring package does not need to hand-maintain its own .targets file just to expose the bundled content.
Example
This repository includes an end-to-end test fixture flow:
- src/Payload The build package itself
- tests/ParentPackage A parent package fixture that ships a skill folder
- tests/ConsumerApp
A consumer fixture that references the parent package and can opt out via
PayloadPolicy
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Payload:
| Package | Downloads |
|---|---|
|
PrettyConsole
High performance, ultra-low-latency, allocation-free, feature rich and easy to use wrap over System.Console |
GitHub repositories
This package is not used by any popular GitHub repositories.