This documentation guides you through integrating CZL Connect into your app.
Have a question? Leave a message on the forumRelease 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:
| Plan | Release sources |
|---|---|
| Lite | Not supported |
| Plus | 3 |
| Pro | 20 |
| Enterprise | Unlimited |
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":
| Field | Description |
|---|---|
| Name | Shown in the console only, for example "Desktop client" |
| GitHub repository | owner/name; you can also paste the repository URL directly |
| Download slug | Globally unique, 3-48 lowercase letters, digits, or hyphens, forming the download address /dl/{slug}/…; changing it invalidates the old address immediately |
| Access token | Required for private repositories; may be left empty for public repositories |
| Aliases | Required, 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:
- GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token
- Under Repository access, choose Only select repositories and select only the repositories you want to distribute
- Under Permissions → Repository permissions, set Contents to Read-only and leave everything else at No access
- 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:
| Source | Use for | How it is returned |
|---|---|---|
| Release asset | Build artifacts such as installers and archives | 302 to a short-lived signed link; the file does not pass through CZL Connect |
| Repository file | Small files in the repository, such as a signed update manifest or checksum file | CZL 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:
| Alias | Match 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:
| Alias | File path | Branch |
|---|---|---|
update-manifest | latest.json | Empty (default branch) |
/dl/{slug}/latest/update-manifestreads 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-manifestreads 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
.jsonis returned asapplication/json, text types such as.txt/.sig/.sha256are 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:
| Address | Description |
|---|---|
GET /dl/myapp/latest/windows-x64 | The asset matched by alias windows-x64 in the latest stable release |
GET /dl/myapp/v1.2.3/windows-x64 | A fixed version v1.2.3 (the Release's tag name) |
GET /dl/myapp/latest/update-manifest | A repository file alias; returns the file content |
GET /dl/myapp/latest.json | The 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,
latestpoints 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 recognizesx64/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.
Recommended auto-update flow
- On startup or on a schedule, request
latest.jsonand comparetagwith the local version using semantic versioning - When a new version exists, show
notes; after the user confirms, downloadaliases[current platform].url(following the 302 redirect) to a temporary file - 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 - 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 status | Error code | Meaning |
|---|---|---|
| 404 | source_not_found | The download slug does not exist or was deleted |
| 404 | release_not_found | The repository has no stable release, or the specified version does not exist |
| 404 | asset_not_found | The alias does not exist, or no asset in that version matches the alias |
| 403 | source_disabled | The release source is disabled, the app is disabled, or the plan does not include this feature |
| 503 | source_unavailable | The repository is temporarily unreadable (for example an expired token); includes a Retry-After header |
| 429 | - | Too many requests |