PolyhydraGames.CDN
2.0.0.16
dotnet add package PolyhydraGames.CDN --version 2.0.0.16
NuGet\Install-Package PolyhydraGames.CDN -Version 2.0.0.16
<PackageReference Include="PolyhydraGames.CDN" Version="2.0.0.16" />
<PackageVersion Include="PolyhydraGames.CDN" Version="2.0.0.16" />
<PackageReference Include="PolyhydraGames.CDN" />
paket add PolyhydraGames.CDN --version 2.0.0.16
#r "nuget: PolyhydraGames.CDN, 2.0.0.16"
#:package PolyhydraGames.CDN@2.0.0.16
#addin nuget:?package=PolyhydraGames.CDN&version=2.0.0.16
#tool nuget:?package=PolyhydraGames.CDN&version=2.0.0.16
Polyhydra CDN & Retro Media Engine
The primary backend service for the Polyhydra CDN ecosystem, consolidating media management, automated indexing, and retro-gaming metadata enrichment.
Tags
- api
- api-cdn
- dotnet
- cdn
- docs
- testing
Related Repos
retro-library— trusted DAT/hash ROM and BIOS identity, durable inventory, recovery review, curation, and frontend export manifests. Media projection coordination is tracked byretro-library#6andApi.CDN#67.Media-MetadataApi.VideoGamesApi.RetroAchievementsApi.Streamingcore.ffmpeg
Project Structure
- WebAPI (
src/WebAPI): Consolidated service for media clips, CDN indexing, and the RetroAchievements proxy. - RetroMedia Library (
src/Api.RetroMedia): Core business logic, parsers, and platform rules. - UI & Tools: WPF and Core conversion utilities.
Api.RetroMedia interprets EmulationStation gamelist.xml and scraper media
layouts. Api.CDN indexes, stores, and publishes CDN assets. Neither component
replaces retro-library as the authority for DAT/hash ROM and BIOS identity.
Gitops image digest bump
After publish-image pushes an image on main, its bump-gitops job opens a
PR against lancer1977/homelab-gitops (base dev, branch
bot/cdn-digest-<short-digest>) that pins the api-cdn image in
systems/r620/stacks/cdn/compose.yml to the new sha256 digest (never a
mutable tag). Re-runs are idempotent: an existing open PR or an already-pinned
digest is a no-op. A human merges the PR and deploy.yml applies it.
The bump-gitops job calls a reusable workflow,
lancer1977/homelab-gitops/.github/workflows/bump-image-digest.yml@main (tracked
in lancer1977/homelab-gitops#1517). It authenticates as a GitHub App, not a
personal access token. It needs two repository Actions values on lancer1977/Api.CDN:
the variable GITOPS_BUMP_APP_ID and the secret GITOPS_BUMP_APP_PRIVATE_KEY.
Without the private key the job logs a warning and skips; image publishing is
unaffected.
GITOPS_BUMP_TOKEN is no longer used. Remove it from lancer1977/Api.CDN once
the App path is verified.
Setting up the GitHub App for the gitops bump
The App's private key is a credential. Paste it only into the GitHub UI, never into a file, issue, PR, chat, or command line.
- Create a GitHub App named
polyhydra-gitops-bumpat GitHub Settings > Developer settings > GitHub Apps. - Under Repository permissions, set Contents to Read and write, Pull requests to Read and write, and Metadata to Read. Grant no other permissions.
- Leave the webhook disabled.
- Install the App only on
lancer1977/homelab-gitops. - Generate a private key from the App's settings page and keep the downloaded file out of any repository.
- In
lancer1977/Api.CDN, open Settings > Secrets and variables > Actions. Add the variableGITOPS_BUMP_APP_IDwith the App ID, and add the secretGITOPS_BUMP_APP_PRIVATE_KEYwith the full contents of the private key file. - In
lancer1977/homelab-gitops, open Settings > Actions > General > Access, and select "Accessible from repositories owned by the user lancer1977". This lets the reusable workflow be called from this repo. - Verify by re-running the Publish image workflow on
main. Thebump-gitopsjob should open a PR againsthomelab-gitopsdevand should not print the skip warning about the missing app key. - Rotate by generating a new private key for the App, replacing the
GITOPS_BUMP_APP_PRIVATE_KEYsecret, and then deleting the old key on the App's settings page.
Documentation
Comprehensive feature and infrastructure documentation can be found in the docs/features directory:
- Docs README
- System Overview
- Folder Structure & Mapping
- WebAPI Features
- RetroMedia Library
- RetroAchievements Integration
- Infrastructure & Deployment
- Roadmap
Getting Started
Local Development
The project uses the modern .slnx solution format.
# Open the solution in VS 2022 or Rider
PolyhydraGames.Api.CDN.slnx
Running the API
cd src/WebAPI
dotnet run
Swagger UI will be available at: http://localhost:5000/swagger
Testing
The project uses NUnit for unit testing. Some tests require a mock CDN directory structure.
# Run the CI-aligned local validation path
scripts/validate.sh
# Run fixture-backed CDN media analysis and artifact checks
scripts/validate-retro-media.sh
# Build a review-only manifest for a local CDN tree
python3 scripts/build-cdn-manifest.py --root /path/to/cdn --output artifacts/cdn/cdn-manifest.json
# Run the WebAPI locally and probe /api/v1/health
scripts/smoke-webapi.sh
# Build a source-SHA-labelled local container and write inspection evidence
SOURCE_SHA="$(git rev-parse HEAD)" scripts/build-local-image.sh
Local CDN fallback for development and validation
When the expected CDN mount paths (for example /mnt/syn2/cdn/roms) are not available on a local workstation, use deterministic fixture or temporary-root validation instead of guessing at live CDN state. Local validation should point the Cdn configuration at a known fixture tree or a disposable temp directory that contains the required gamelist.xml and media samples for the test being exercised.
Required Cdn values for local runs are:
RootPath: root of the local or fixture CDN tree.RetroPath: retro-media subpath when the code path expects one; keep it aligned with the fixture layout.ArchiveRoot: writable archive/temp location for archive-related tests or local runs.BaseUrl: base URL used when generated links are asserted or smoke-tested.
RetroAchievements (RaUser/RaKey) credentials are only required for live RA/online smoke paths. Do not add or invent secrets for deterministic local tests; remote CDN and RA checks should remain smoke-only and should be documented as such in the validation handoff.
The GitHub contents-client release coverage uses a local response fixture. A live GitHub contents probe is intentionally explicit and excluded from the release gate. To run it against a known public repository, supply all three values and target the integration fixture:
GITHUB_CDN_PROBE_OWNER=<owner> \
GITHUB_CDN_PROBE_REPOSITORY=<repository> \
GITHUB_CDN_PROBE_FOLDER=<folder> \
dotnet test src/Test/PolyhydraGames.Api.CDN.Test.csproj --configuration Release \
--filter FullyQualifiedName~GithubCdnIntegrationTests
If the intended mount path, runtime host, or credential boundary differs from the existing docs, block and add an explicit note rather than guessing. The setup/readiness rule is: deterministic fixture/temp-root tests prove local behavior; live CDN/RA checks only prove connectivity and current external availability.
Documentation
XML documentation is enabled for the core library. Build the project to generate the .xml documentation file.
For public CDN inventory, classification, and controlled cleanup, see Public CDN hygiene and catalog.
Configuration
Configure the Cdn section in appsettings.json or via environment variables. Environment variables use double underscores, for example Cdn__RootPath.
"Cdn": {
"RootPath": "/path/to/cdn",
"SourcePath": "/path/to/roms",
"RetroPath": "images/retro_v2",
"ArchiveRoot": "/var/lib/polyhydra/cdn-archive",
"ScanIntervalMinutes": 60,
"BaseUrl": "https://your-cdn-url.com"
}
appsettings.json is a checked-in, non-secret baseline. Supply live RetroAchievements and Seq credentials only at runtime; do not add RaKey, RaUser, or SEQ_API_KEY to a tracked JSON file.
Common environment variables for local runs:
Cdn__RootPathCdn__SourcePathCdn__RetroPathCdn__ArchiveRootCdn__ScanIntervalMinutesCdn__BaseUrlCdn__RaUserCdn__RaKeySEQ_API_KEYwhen Seq ingestion is enabledDataProtection__KeyPathpointing at a persistent, externally managed directoryDOTNET_NUGET_SIGNATURE_VERIFICATION=falsewhen restoring on Linux hosts that hit the current ReactiveUI/Splat signature-revocation issueUPLOAD_STORAGE_PROVIDER=local-filesystemto enable verified local/NFS-backed upload finalization; it requiresCdn__RootPathand is disabled when unset.Upload__MaxBytessets the maximum declared upload size (environment form ofUpload:MaxBytes; default 268435456 bytes). Upload draft creation returns400for non-positive or over-limit sizes and for an invalid suppliedTopicId(must match[A-Za-z0-9_-]{1,64}).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- PolyhydraGames.Core.Interfaces (>= 3.0.0.14)
- PolyhydraGames.Core.Interfaces.Gaming (>= 3.0.0.14)
- PolyhydraGames.Core.Models (>= 3.0.0.8)
- PolyhydraGames.Extensions (>= 2.1.2.4)
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- PolyhydraGames.Core.Interfaces (>= 3.0.0.14)
- PolyhydraGames.Core.Interfaces.Gaming (>= 3.0.0.14)
- PolyhydraGames.Core.Models (>= 3.0.0.8)
- PolyhydraGames.Extensions (>= 2.1.2.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0.16 | 78 | 10/27/2026 |
| 2.0.0.5 | 7,205 | 10/16/2026 |
| 1.0.3.372 | 91 | 10/15/2026 |
| 1.0.3.370 | 6,753 | 10/3/2026 |
| 1.0.3.369 | 107 | 10/3/2026 |
| 1.0.3.366 | 1,735 | 10/2/2026 |
| 1.0.3.365 | 95 | 10/2/2026 |
| 1.0.3.362 | 500 | 10/2/2026 |
| 1.0.3.361 | 93 | 10/2/2026 |
| 1.0.3.358 | 102 | 10/1/2026 |
| 1.0.3.354 | 98 | 9/29/2026 |
| 1.0.3.353 | 102 | 9/24/2026 |
| 1.0.3.350 | 103 | 9/17/2026 |
| 1.0.3.348 | 97 | 9/17/2026 |
| 1.0.3.347 | 95 | 9/17/2026 |
| 1.0.3.343 | 106 | 9/14/2026 |
| 1.0.3.335 | 97 | 9/14/2026 |
| 1.0.3.333 | 96 | 9/14/2026 |
| 1.0.3.322 | 103 | 9/14/2026 |
| 1.0.3.314 | 118 | 8/23/2026 |