feed.json

{
  "product": "MyApp", "channel": "stable",
  "latest": {
    "version": "1.2.3", "minSupportedVersion": "1.1.0",
    "full":  { "url": "1.2.3/Setup-MyApp-1.2.3.exe", "sha256": "…" },
    "delta": { "manifestUrl": "1.2.3/_payload-manifest.json", "blobBaseUrl": "1.2.3/_blobs/" }
  },
  "modules": [
    { "id": "TheTechIdea.Beep.PostgresDriver", "version": "2.4.1", "sha256": "…", "required": false }
  ]
}

Every artifact carries a SHA-256 verified before use. minSupportedVersion forces a full install when the installed version is too old to delta from. Feed integrity (D11) for v1 is TLS + per-artifact SHA-256; signing the feed itself is deferred until a public-internet fleet exists.

Publish with /PUBLISHFEED

The FeedPublisher builds then stages into a static feed: <feedDir>/<version>/ holds the full Setup.exe (+ its hash) and, when the payload is solid, the loose content-addressed store (_blobs/ + _payload-manifest.json) a delta updater consumes. feed.json is written atomically.

Beep.Installer.exe /BUILD=MyApp.bsetup /PUBLISHFEED=\\host\feed \
    /FEEDURL=https://updates.example.com [/CHANNEL=stable] [/MINVERSION=1.1.0] [/REPUBLISH]

Immutability is a hard rule: re-publishing an existing version fails without /REPUBLISH. New content ⇒ new version, always — the lesson of the stale-package cache incident.

Hosting (D10)

The client only ever sees URLs + hashes, so any static host works: GitHub Releases, S3/Azure Blob, an IIS/nginx static site, or a LAN folder share (the client's transport falls back to the filesystem). Moving folder → CDN → API later is a hosting change, not a client change. Add the optional update server only when you need staged rollout, gated downloads, telemetry, or an admin dashboard.

Provisioning

The build stamps update-settings.json beside the app (from AppUpdatesURL/AppUpdateMode), which AddBeepAppUpdates() reads by default. For a side-by-side install, the install step also patches it with the runtime InstallRoot.