Copy the prompt and send it to an AI to generate the release downloads code

Release downloads

Distribute the Release assets of a GitHub repository (including private repositories) through a fixed CZL Connect address. The repository does not need to be public, and you do not need to deploy a separate download backend for each app. CZL Connect exchanges each download request for a short-lived signed link and responds with a 302 redirect. The file is served through an accelerated domain that is reachable from mainland China, and does not pass through CZL Connect servers.

Use cases

  • The client software's source lives in a private repository, but the build artifacts must be publicly downloadable
  • Your website's "Download latest" button needs an address that does not change with each version
  • A client's auto-update needs the latest version number, release notes, and the installer address for each platform

Availability

Release downloads are available according to the app owner's plan. The number of release sources is counted across all apps in the account:

PlanRelease sources
LiteNot supported
Plus3
Pro20
EnterpriseUnlimited

If a plan expires and downgrades to Lite, configured download addresses return 403 source_disabled. The configuration is kept and resumes automatically after renewal. Downloads are also unavailable while the app is disabled.

Configure a release source

Open the app detail page, go to "Release downloads", and click "Add release source":

FieldDescription
NameShown in the console only, for example "Desktop client"
GitHub repositoryowner/name; you can also paste the repository URL directly
Download slugGlobally unique, 3-48 lowercase letters, digits, or hyphens, forming the download address /dl/{slug}/…; changing it invalidates the old address immediately
Access tokenRequired for private repositories; may be left empty for public repositories
AliasesRequired, at least one; they decide which files are public, see below

On save, CZL Connect uses the token to actually access the repository once, and the source cannot be saved if access fails. Creating, editing, and deleting all require passkey confirmation and are recorded in the audit log.

Access token

We recommend a GitHub Fine-grained personal access token:

  1. GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token
  2. Under Repository access, choose Only select repositories and select only the repositories you want to distribute
  3. Under Permissions → Repository permissions, set Contents to Read-only and leave everything else at No access
  4. Set a suitable expiration; before it expires, enter the new token on the release source edit page. Leaving the field empty keeps the existing token

The token is stored encrypted and never shown again after saving; the list shows only its last 4 characters. If the token expires or loses access to the repository, the release source card shows an error, download addresses return 503 source_unavailable, and they recover after you update the token.

For a public repository, leave the token empty to use anonymous access. The quota for anonymous version lookups is shared by all anonymous release sources and is very low, so version information is cached longer (the latest version becomes visible within about 5 minutes). If you expect heavy download traffic, we still recommend providing a token.

Aliases

Only files that an alias points to are public. Download addresses, the update manifest, and the download page all recognize aliases only. Any build artifact without an alias (debug builds, internal tools, update metadata, and so on) cannot be downloaded and never appears in any public endpoint.

An alias has one of two sources:

SourceUse forHow it is returned
Release assetBuild artifacts such as installers and archives302 to a short-lived signed link; the file does not pass through CZL Connect
Repository fileSmall files in the repository, such as a signed update manifest or checksum fileCZL Connect reads it and returns it as is, up to 1 MB

Release assets

Asset names in each version usually include the version number, for example myapp-1.2.3-windows-amd64.zip. After you configure an alias for an asset, its public address stays fixed:

AliasMatch pattern
windows-x64*-windows-amd64.zip
macos-arm64*-darwin-arm64.dmg
linux-x64*-linux-amd64.tar.gz

Patterns support * (any characters) and ? (a single character). The first matching asset in asset order wins, so write patterns as precisely as possible to avoid matching several files. The "Preview" button reads the latest version, lists all repository assets, and marks which ones will be public (visible only to you), so you can check your patterns.

Repository files

Some projects commit the update manifest as an ordinary file on a repository branch (rather than as a Release asset); for example, CI may commit a signed latest.json after every release. Configure a repository file alias for it:

AliasFile pathBranch
update-manifestlatest.jsonEmpty (default branch)
  • /dl/{slug}/latest/update-manifest reads the latest content on the configured branch (the repository's default branch if empty), and new commits show up within about 1 minute; /dl/{slug}/{tag}/update-manifest reads the file at that tag
  • The content is returned byte for byte as is, without any rewriting, so clients can still verify the signature with their own public key
  • .json is returned as application/json, text types such as .txt / .sig / .sha256 are returned as plain text, and all other types are returned as attachment downloads
  • Repository files do not count toward download totals (clients poll the manifest often) and do not appear in the update manifest or on the download page
  • Private repositories are read with the access token; public repositories are read anonymously, and the content is cached for about 5 minutes

Signed manifests and download acceleration: the manifest is signed, so CZL Connect cannot and should not modify the download addresses in it. If you want clients to download installers through CZL Connect, write the download address into the manifest before CI signs it, for example by putting https://connect.czl.net/dl/{slug}/{tag}/{installer alias} first in downloadUrls and keeping the direct GitHub link as a fallback.

Download addresses

Using the download slug myapp as an example:

AddressDescription
GET /dl/myapp/latest/windows-x64The asset matched by alias windows-x64 in the latest stable release
GET /dl/myapp/v1.2.3/windows-x64A fixed version v1.2.3 (the Release's tag name)
GET /dl/myapp/latest/update-manifestA repository file alias; returns the file content
GET /dl/myapp/latest.jsonThe update manifest of the latest stable release
  • "Latest stable release" follows GitHub's latest semantics: it excludes draft and prerelease versions
  • After you publish a new version, latest points to it within about 1 minute
  • Release asset aliases return 302, and the target is a signed link valid for about 5 minutes. Do not cache or store the redirected address; request /dl/... for every download
  • Public endpoints require no sign-in and are rate limited by IP; requests that are too frequent return 429

Public download page

Each release source comes with a shareable download page, https://connect.czl.net/download/{slug}, which suits apps without a standalone product page:

  • It shows the app name, icon, description, and website link, plus the latest stable release's version, publish time, and release notes
  • The installer's SHA-256 appears below the main button, and clicking it copies the value so users can verify it themselves
  • It detects the visitor's operating system and architecture automatically (including Apple Silicon and Windows ARM); the main button downloads the matching installer, and download links for other systems are listed below
  • It shows only assets matched by an alias, in alias order. For assets used only by clients, such as update packages and signature files, click the eye icon next to the alias to hide them from the download page; their download addresses and the update manifest are unaffected
  • Platform detection relies on keywords in the alias and asset name: windows / win / .exe / .msi, mac / macos / darwin / .dmg, linux / .AppImage / .deb, android / .apk, and so on; architecture detection recognizes x64 / amd64, arm64 / aarch64, universal, and so on. Name your assets with these keywords and they are classified correctly

You can copy the download page address from the release source card under app detail → "Release downloads".

Update manifest

GET /dl/{slug}/latest.json returns information about the latest stable release and suits client auto-update checks:

{
  "tag": "v1.2.3",
  "name": "v1.2.3",
  "notes": "## What's new\n- Fixed several issues",
  "prerelease": false,
  "publishedAt": "2026-10-30T08:00:00Z",
  "aliases": {
    "windows-x64": {
      "name": "myapp-1.2.3-windows-amd64.zip",
      "size": 18350080,
      "contentType": "application/zip",
      "sha256": "9f2c1a… (64 hex characters)",
      "url": "https://connect.czl.net/dl/myapp/v1.2.3/windows-x64"
    }
  }
}

Download addresses in the manifest always point to a fixed version, which prevents a version mismatch if a new version is published between the update check and the actual download. notes is the Release body (Markdown). aliases contains only the aliases that matched in this version, and the manifest never lists any asset outside the aliases.

sha256 is the SHA-256 digest (lowercase hex) that GitHub computed for the asset, used to verify file integrity after download. Older assets for which GitHub provides no digest omit this field, and clients should treat a missing field differently from a failed verification.

  1. On startup or on a schedule, request latest.json and compare tag with the local version using semantic versioning
  2. When a new version exists, show notes; after the user confirms, download aliases[current platform].url (following the 302 redirect) to a temporary file
  3. Verify the file size (size) and SHA-256 (sha256). If either does not match, delete the temporary file and prompt the user to retry. Never install a file that fails verification
  4. Replace the app or launch the installer only after verification passes

Hash verification detects corruption and tampering caused by interrupted transfers, caches, or network paths. It comes from the same origin as the download address, so it cannot replace code signing (Windows Authenticode, macOS Developer ID signing and notarization) as proof of the publisher's identity.

Error codes

Error responses have the body {"error": "<error code>"}:

HTTP statusError codeMeaning
404source_not_foundThe download slug does not exist or was deleted
404release_not_foundThe repository has no stable release, or the specified version does not exist
404asset_not_foundThe alias does not exist, or no asset in that version matches the alias
403source_disabledThe release source is disabled, the app is disabled, or the plan does not include this feature
503source_unavailableThe repository is temporarily unreadable (for example an expired token); includes a Retry-After header
429-Too many requests