When to run it

You don't need it to ship updates — a static host is enough. Run Beep.Installer.UpdateServer only for what a static host can't do:

CapabilityNeeds a server?
Ship updates, deltas, module updates, Required updatesNo — static host
Staged / percentage rollout (stable per-client cohorts)Yes
Telemetry & active-version statsYes
Token-gated (license) downloadsYes
Publish API + admin dashboardYes

Endpoints

Method / RouteAuthPurpose
GET /{channel}/feed.json?cid=…publicThe rollout-tailored feed for a client (cohort from cid).
GET /artifacts/{channel}/{version}/{**path}public / ?token=Setup.exe, _blobs/*, manifest — token-gated when the channel requires it.
POST /api/telemetrypublicClient reports an update event.
POST /api/publishX-Api-KeyPublish a version (multipart: setup + optional payload + fields) — runs FeedPublisher.
POST /api/rolloutX-Api-Key{channel,version,percent?,enabled?}.
POST /api/gate · /api/tokenX-Api-KeyToggle token-gated downloads; issue a download token.
GET /api/channels · /api/statsX-Api-KeyAdmin reads (dashboard).

Staged rollout uses a deterministic per-(client, version) cohort bucket, so a client consistently is (or isn't) in a rollout and the cohort reshuffles per release. Active-version stats count only completed applies, not checks.

Run & deploy

dotnet publish Beep.Installer.UpdateServer -c Release -o /opt/beep-updates
export UpdateServer__ApiKey="$(openssl rand -hex 24)"
export UpdateServer__TokenSecret="$(openssl rand -hex 32)"
export UpdateServer__StorageRoot="/var/lib/beep-updates"
export ASPNETCORE_URLS="http://127.0.0.1:5080"
/opt/beep-updates/Beep.Installer.UpdateServer
  • Terminate HTTPS at nginx/Caddy/IIS and proxy to Kestrel; raise the proxy body limit for large Setup.exe uploads.
  • The admin dashboard is at / — and admin is locked until you change the default API key (the server rejects change-me).
  • File-backed, no database: channel.json per channel + telemetry.jsonl. Back up StorageRoot — it's the whole server.
  • Front /artifacts/* with a CDN for large fleets (immutable, content-addressed).

Publish from CI

curl -X POST https://updates.example.com/api/publish \
  -H "X-Api-Key: $KEY" \
  -F channel=stable -F product=MyApp -F version=1.2.3 -F rolloutPercent=10 \
  -F setup=@out/Setup-MyApp-1.2.3.exe \
  -F payload=@out/payload.zip           # optional; enables deltas

The server runs the same FeedPublisher the installer uses (immutable versions, solid→loose blob expansion), then stages the version at 10% rollout. Bump it in the dashboard when confident.