Public NuGet v3 feed for Sylinko-maintained fork packages and public internal packages.
dotnet nuget add source https://nuget.sylinko.com/v3/index.json --name SylinkoPackage repositories build the binaries. This repository reviews their release manifests and generates the feed. Cloudflare serves the generated metadata; GitHub Releases stores the package files.
| Part | Authoritative location | Purpose |
|---|---|---|
| Source and packaging workflow | Producer repository | Build, smoke-test, and pack a release |
.nupkg and optional symbol packages |
Producer's public GitHub Release | Distribute the exact package bytes |
| Package registration and version manifests | This repository's bucket/ |
Record approved IDs, provenance, artifact URLs, and SHA256 hashes |
| NuGet indexes, registrations, nuspec files, and download routes | Generated from manifests and verified packages | Rebuildable output deployed as Cloudflare Static Assets |
| HTTP routing | worker/src/index.ts |
Serve assets and redirect package downloads |
Neither package binaries nor generated metadata are committed to this repository. The runtime does not fetch metadata from GitHub raw. Each publication deploys the Worker together with the newly generated Static Assets.
flowchart TD
A[Producer workflow: build, smoke-test, pack] --> B[Shared release action]
B -->|Producer GITHUB_TOKEN| C[Create GitHub Release and upload packages]
C --> D[Read local nuspec identities and compute SHA256]
D -->|SYLINKO_NUGET_FEED_TOKEN| E[Push version manifests and open one feed PR]
E --> F[Feed PR CI: validate, download, hash, generate]
F --> G[Human reviews and merges PR into main]
G --> H[Feed deployment CI: check latest main and regenerate]
H -->|Cloudflare credentials| I[Deploy Worker and Static Assets]
I --> J[Versions become available through the feed]
A successful producer release is only the first stage. A version becomes available through this feed after its feed PR is merged and deployment succeeds.
Before the producer's first publication, merge a bucket/<lowercase-package-id>/package.yml into this repository's main. Register every package in the batch, including companion packages such as annotations or analyzers.
For example:
id: Microsoft.ML.Tokenizers
lowerId: microsoft.ml.tokenizers
source:
repository: Sylinko/Microsoft.ML
policy:
requireSylinkoPrefix: false
allowOriginalPackageId: truePreserve original package IDs where dependency compatibility requires them. The release action checks the package identity and that source.repository matches the calling repository. The policy fields document registration intent; they should not be mistaken for a complete authorization mechanism.
Registration alone publishes no version. The action creates versions/<lowercase-version>.yml after packages have been built.
| Credential | Where the workflow reads it | Purpose |
|---|---|---|
Automatic github.token / GITHUB_TOKEN |
Producer workflow run | Create the producer's GitHub Release and upload its assets; the publishing job needs contents: write |
SYLINKO_NUGET_FEED_TOKEN |
Producer repository Actions secret, or an organization Actions secret shared with that repository | Push a branch and create a PR in Sylinko/nuget-feed |
CLOUDFLARE_ACCOUNT_ID |
Feed repository Actions secrets | Select the deployment account |
CLOUDFLARE_API_TOKEN |
Feed repository Actions secrets | Deploy the Worker and Static Assets |
SYLINKO_NUGET_FEED_TOKEN is a secret name chosen by our workflows, not a NuGet API key or a Cloudflare credential. Its authority comes from the GitHub identity and permissions of the stored token. A fine-grained PAT for this purpose should target Sylinko/nuget-feed with Contents and Pull requests write access, subject to organization approval rules. Producers do not need Cloudflare credentials.
The source code cannot establish who issued the currently configured token, its expiration date, or whether repository or organization secrets supply it. Inspect those settings in GitHub. Reusing a secret name does not prove that repositories store the same token.
-
Open your GitHub account's Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token. Give it a recognizable name and expiration date.
-
Set Resource owner to the owner of the feed repository (
Sylinko). The token's account must already have the required access. If organization approval is required, wait until approval is complete. -
Under Repository access, choose Only select repositories. A combined publishing credential can select
Sylinko/nuget-feedand the current producer repository, for exampleSylinko/Microsoft.MLorSylinko/Porta.Pty. Add other producer repositories explicitly if the same credential is intentionally shared. -
Under Repository permissions, configure:
Permission Access Used for Metadata Read-only (automatically required) Read repository metadata Contents Read and write Check out repositories and push the feed release branch; also release uploads if a workflow explicitly uses this PAT for them Pull requests Read and write Read/create the batch publication PR in the feed repository Write access includes read access. No Packages permission is required: binaries live in GitHub Releases, and this feed does not publish to GitHub Packages. The shared action does not edit workflow files, so it does not require Workflows write access.
-
Generate the token and copy its value into the producer repository's Settings → Secrets and variables → Actions → New repository secret, named exactly
SYLINKO_NUGET_FEED_TOKEN. Alternatively, create an organization Actions secret and grant access to only the intended producer repositories. Configure the Actions secret on the producer, where${{ secrets.SYLINKO_NUGET_FEED_TOKEN }}is evaluated; adding it only to the feed repository does not make it available to producers. -
Pass that secret to the action's
tokeninput. Separately, give the producer publishing job's automaticGITHUB_TOKENcontents: writepermission.
Current action's minimum scope: it uses the supplied PAT only for Sylinko/nuget-feed; it uses the automatic GITHUB_TOKEN for the producer's release. Therefore selecting both repositories is supported but is not required by this action. If you want the smallest PAT scope, select only the feed. A fine-grained PAT has one resource owner, so it cannot cover a producer owned by another account/organization in the same token; the current two-token arrangement still works in that case.
The PAT's selected repositories control where it can act. The repository/organization Actions secret configuration controls which workflows can receive it. Both must be configured. Branch protection and organization policies still apply; token permissions do not bypass them.
On expiration or rotation, replace the Actions secret in each producer or update the shared organization secret. GitHub cannot show the existing secret value, and this repository cannot determine its issuer or expiration from the secret name. See GitHub's PAT setup instructions and Actions secrets.
The producer owns build, smoke tests, and dotnet pack. Pass a directory containing only the intended release batch to the action. Normal package assets should be named <PackageId>.<Version>.nupkg; optional symbols use matching .snupkg or .symbols.nupkg names.
Inside the producer's publishing job, after packing:
# The publishing job also needs: permissions: { contents: write }
- uses: Sylinko/nuget-feed@v1
with:
token: ${{ secrets.SYLINKO_NUGET_FEED_TOKEN }}
release-tag: nuget/MyPackage/${{ steps.version.outputs.version }}
artifacts-directory: artifactssteps.version.outputs.version represents a producer-defined step; the shared action does not calculate versions. Use the maintained v1 action reference, or pin a reviewed commit SHA. Changes to main do not automatically update consumers pinned to v1 or a SHA.
Our date-based producer convention is 999.YYYYMMDD.<CI-run-number>. It is a producer policy, not a requirement to rename older feed versions. Existing versions such as 6.0.0 remain valid. A new publication needs a new version; keep release assets for published versions unchanged.
New publications must use three-part SemVer versions. NuGet has additional version forms that this feed intentionally does not accept, including two- and four-component versions. Version identities ignore build metadata and prerelease casing, so 1.0.0+one and 1.0.0+two cannot be published as separate packages. Dependency ranges remain NuGet strings and are never evaluated with npm range rules.
The action installs its locked production dependencies in its own directory, then performs these operations in order:
- Create or locate the producer's GitHub Release and upload package assets using its automatic GitHub token.
- Check out this feed repository using the supplied cross-repository token.
- Scan normal packages, read their nuspec IDs and versions, match optional symbols, check registrations, reject duplicate versions, and compute hashes.
- Write version manifests containing source repository, commit, release tag, workflow run, artifact URLs, and SHA256 hashes.
- Push a
release/batch/...branch and open one PR for the batch.
Current retry limitation: upload happens before manifest validation, and the shared action currently uses gh release upload --clobber. It is not generally safe or idempotent to rerun publication against an existing release. Producer-side guards help, but do not replace a shared-action guarantee. If a run fails after uploading, inspect the release and any existing feed branch/PR before retrying. Never replace bytes already referenced by a published manifest.
PR CI validates manifests, downloads referenced release assets, verifies their hashes and nuspec identities, runs type checks and release tests, generates metadata, and validates the output. This is feed validation; producer smoke tests establish that the packaged library actually works.
Review the intended IDs and versions, source repository/commit/tag/workflow links, and the artifact changes. Hash validation establishes consistency with the manifest, not the trustworthiness of arbitrary source code. Merge the approved PR into main.
The deployment workflow reacts to a PR merged into main, or a manual run selected on main. It checks out the triggering revision, verifies that it still matches the latest origin/main, repeats validation and generation, and runs the locked Wrangler version from worker/. Production deployment jobs are serialized; a running deployment is allowed to finish before the next one begins. It checks main again immediately before deployment. Older runs skip deployment.
The domain must already be configured for this Worker, and the feed repository must have the Cloudflare credentials above. Confirm that Deploy NuGet Feed succeeds before updating a consumer's exact package version.
A direct push to main does not trigger deployment. To redeploy after such a push, open Actions → Deploy NuGet Feed → Run workflow and select main. A manual run on another branch is skipped.
sequenceDiagram
participant N as NuGet client / Rider
participant F as Cloudflare feed
participant A as Deployed Static Assets
participant G as GitHub Release
N->>F: GET /v3/index.json
F->>A: Read service index
A-->>F: Resource URLs
F-->>N: Service index
N->>F: Search or autocomplete
F->>A: Read discovery.json
A-->>F: Package metadata and ordered versions
F-->>N: Filtered query results
N->>F: Request versions, registration, or nuspec
F->>A: Read generated metadata
A-->>F: JSON or nuspec
F-->>N: Metadata response
N->>F: GET package.nupkg
F->>A: Look up routes.json
F-->>N: 307 redirect to release asset
N->>G: Follow redirect
G-->>N: Package bytes
The Worker currently redirects binary downloads; it does not proxy the package bytes through Cloudflare. Clients therefore need access to GitHub Release downloads as well as the feed domain.
The service index advertises package content, RegistrationsBaseUrl/3.6.0, search, and autocomplete. Registration responses include dependencies and available nuspec metadata, use gzip encoding, and link to generated catalog documents. Declared package icons and license files are served as assets; readmes have an escaped HTML text view. Markdown styling is not rendered. Optional symbol assets are recorded and verified, but this is not a symbol server.
Clients discover the endpoints through the service index. Examples after deploying this revision:
https://nuget.sylinko.com/v3/query?q=MessagePack&semVerLevel=2.0.0
https://nuget.sylinko.com/v3/query?skip=0&take=20&semVerLevel=2.0.0
https://nuget.sylinko.com/v3/autocomplete?q=Porta&semVerLevel=2.0.0
https://nuget.sylinko.com/v3/autocomplete?id=MessagePack&prerelease=true&semVerLevel=2.0.0
Search uses case-insensitive matching against package ID, title, description, summary, and tags. Whitespace-separated terms must all match. An empty query browses all eligible packages. Results are ordered by package ID and show metadata from the newest eligible version. The default page size is 20; take must be positive and is capped at 1000. Autocomplete version lookup returns every eligible version, without pagination.
Prerelease versions are excluded unless prerelease=true. SemVer2-specific packages are excluded unless the client opts in with semVerLevel=2.0.0 or higher. Classification considers the package's version and SemVer2 traits in dependency bounds. Both search and autocomplete support case-insensitive packageType filtering. Download counts are not measured; required per-version downloads fields use zero as a placeholder.
Version manifests may set listed: false to remove a version from discovery while retaining its registration and exact-version download. Existing manifests default to listed. A package registration with no approved versions does not appear in search.
Search, autocomplete, and richer registration metadata can be added in place. Existing package IDs, versions, release files, hashes, and the feed URL can remain unchanged. Regenerate metadata from the original verified packages, add resource entries, and deploy the updated Worker with its assets.
See the implementation plan and results for the compatibility baseline, library choices, and acceptance checks. The implementation is present in this checkout; production receives it after the reviewed deployment succeeds. Existing clients do not need a new feed URL.
bucket/ Human-reviewed package and version manifests
src/ Node.js 24 validation and generation scripts
worker/ Cloudflare Worker and Static Assets configuration
action.yml Shared release action entrypoint
.github/actions/release/ Shared release action scripts
.github/workflows/ PR validation and deployment
docs/ Implementation plan
generated/ Generated output, ignored by Git
.tmp/packages/ Download cache, ignored by Git
With Node.js 24 and pnpm 10.33.0 available:
pnpm install --frozen-lockfile
pnpm validate
pnpm typecheck
pnpm test:release
pnpm test:feed
pnpm generate
pnpm validateValidation and generation may download packages into .tmp/packages/; cached files are hashed again before use. pnpm generate rebuilds generated/. These commands do not publish packages or deploy Cloudflare.
pnpm typecheck also generates Worker binding/runtime types from the Wrangler configuration. Those types are ignored by Git. YAML, ZIP, XML, and version handling use maintained libraries; application-specific provenance checks and query matching remain in this repository.
For a local preview, generate metadata with localhost resource URLs, then start the local Worker:
$env:FEED_BASE_URL = "http://127.0.0.1:8787"
pnpm generate
pnpm --dir worker exec wrangler dev --local --ip 127.0.0.1 --port 8787After stopping the preview, remove the override and regenerate before a production deployment:
Remove-Item Env:FEED_BASE_URL
pnpm generateThe default base URL is always https://nuget.sylinko.com. The override accepts an HTTP(S) origin only and is intended for previews. Production CI regenerates output using the default.
This project's original code, workflows, documentation, and configuration are
licensed under the MIT License, copyright 2026 Sylinko and contributors.
This includes the original package registrations and version manifests in
bucket/. The MIT grant covers only rights held by this project's authors and
contributors; it does not relicense third-party material.
| Content | Applicable license |
|---|---|
Feed implementation, documentation, and original bucket/ manifests |
This repository's MIT License |
| NuGet packages and symbols referenced by manifests or served by the feed | Each package's own applicable licenses and notices |
| Copied package descriptions, nuspec files, icons, readmes, license files, and other upstream material, including copies in generated output | Their original applicable licenses and rights |
| Third-party implementation dependencies | Each dependency's own license |
Generating registration/search JSON or wrapping upstream content in HTML does not change the licensing of the underlying material. Consult each package's license metadata, included license files, and upstream notices for its terms; the feed's MIT License is not a substitute. Package IDs, author names, and links identify upstream projects and do not grant trademark rights or imply their endorsement.
You may reuse the feed implementation and original manifests under MIT, including for a feed with different packages. A fork operates with its own repository, credentials, domain, and deployment configuration; this license does not grant access to Sylinko's accounts or infrastructure.