Release packages
Every release contains three programs, each with its own portable archives and installers:gproxy-*: the CLI, with the web console built in.gproxy-headless-*: the same CLI without the console, for CI and small servers. See Headless CLI.gproxy-tauri-*: the desktop and mobile application, which also embeds the console.
The release workflow also publishes the serverless executable
(
gproxy-serverless-linux-<arch>-musl for x86_64 and aarch64), the Cloudflare
Workers bundle (gproxy-edge-cloudflare.zip), and GNU and musl container
images. Application packages receive build provenance attestations hosted on
GitHub. Public release attachments are limited to the packages,
manifest.json and SHA256SUMS; individual checksum files and build
provenance remain internal CI inputs.
Notes per platform:
- Linux: the CLI DEB installs
gproxyunder/usr/bin. GNU Linux applications require the distribution’s GTK 3 and WebKitGTK 4.1 packages. musl builds target Alpine and ship as APKs, not DEBs. - Android CLI: the Termux DEB installs under
/data/data/com.termux/files/usr. Android CLI builds use the source recipe indistribution/termux/and a pinned official Termux builder. Both the DEB and the ZIP use Termux’slibc++, OpenSSL and CA certificates; the ZIP contains the launcher, the executable andTERMUX.txtwith installation and update instructions. Self-update is disabled in these builds: install a newer DEB with APT, or usepkg upgrade gproxyonce an enabled repository provides it. - Android application: the APK is signed and verified with the
ANDROID_SIGNING_*secrets. Each app’s<target-triple>-tauri-apkentry is part of the Ed25519-signed update manifest, separately from the CLI ZIP updates. The legacy server-wrapper APK is no longer built. - Windows: the CLI MSIX has its own Partner Center identity,
LeenHawk.GPROXYCLI, and a console execution alias. The application requires the system WebView2 Runtime. - macOS: CLI DMGs contain the executable and instructions for installing it
from Terminal. Application ZIPs keep the desktop resources and, on macOS, the
complete
.app. Builds are ad-hoc signed; Developer ID signing and notarization are not configured. - OpenHarmony: builds use a cached toolchain image and a pinned experimental Tauri branch; other platforms use stable Tauri. The HAP must be signed before it can be installed. It has been verified on an emulator. Continuous background execution is implemented, but whether it works depends on the device, the OS version and the granted permissions; the proxy stops when the application process exits.
Build environment
Install stable Rust (edition 2024), Go, Clang, Node.js 22.12+ (24 LTS recommended), and pnpm. Linux desktop builds also need thewebkit2gtk-4.1, gtk+-3.0 and libsoup-3.0 development packages. The
release workflow sets up the toolchains for the other platforms.
CLI and console
From the repository root:pnpm --dir console build copies the built console into the embed directories
of the HTTP host and the Tauri host, and the following Rust build includes it.
The order matters: a fresh checkout built with Rust alone has no console
assets, and /console returns 404. To serve a separately built console
instead, pass --console-path console/dist.
Cargo features
The default feature set of thegproxy crate is
channels,memory,fs,bundled-vocabulary,embedded-console,postgres,mysql.
A smaller build with four channels and no external database drivers:
gproxy-serverless binary for the serverless platforms;
it requires the postgres feature.
Headless CLI
The headless CLI is thegproxy binary built without embedded-console. It
keeps the proxy routes, the management API, all channels, SQLite, the
PostgreSQL and MySQL drivers, the memory cache, local file storage and the
bundled vocabulary. You configure it through the CLI or the management API.
Building it needs no Node.js, pnpm or desktop libraries:
embedded-console keeps the frontend assets out of the binary even
when the checkout already contains a built console. --console=false also
stops the server from serving an external console directory. To build a
custom feature set that does include the console, add embedded-console.
The release workflow builds the headless packages independently of the
console build:
Download them from Releases:
nightly for the dev channel, staging for beta, or a stable version.
Self-update on a headless install picks the matching headless package.
On Android, inside Termux, install the package and start the server:
gproxy package, because both provide
the gproxy command. To use the ZIP instead, install the runtime libraries,
extract the ZIP into a directory under Termux’s home directory (Android’s
shared storage cannot execute files), and run it from there:
gproxy.exe in place of gproxy. The Windows MSIX has the
identity LeenHawk.GPROXYHeadless and the alias gproxy-headless.exe, so it
does not replace the CLI’s gproxy.exe. GPROXY Headless is also on the
Microsoft Store (9ND7FQQMV566).
Desktop and mobile application
After building the console:--workspace includes it.
To produce release packages locally, build the console and run the packaging
script. pnpm --dir console build already runs sync-to-embed.mjs; running
it again is harmless.
dist/release/.
Cloudflare Workers
From the repository root:worker-build compiles and optimizes the WebAssembly module. check runs a
Wrangler deployment dry run without publishing anything. See
Edge deployment for configuration and runtime limits, and
Adding a channel for
building a Worker that carries a channel of your own.
Checks CI runs
Run these before you open a pull request. CI runs them on every change, split into backend, desktop and WebAssembly jobs. Backend (the workspace’s default members):net feature, and mio does not build for this target.
pnpm --dir console lint includes the translation parity check
(pnpm i18n:check). After changing Rust DTOs, run pnpm --dir console types
to regenerate the TypeScript types.
Publishing
Branches and update channels
The Release workflow (.github/workflows/release.yml) publishes on push:
devdevelops new features. Its pushes publish only tonightly, the dev update channel.mainmaintains released functionality and bug fixes. Its pushes publish only tostaging, the beta update channel.- Stable version tags are created from
mainand publish to both the release and the beta channel. They never overwrite the dev channel.
dev, run bash scripts/push-dev.sh. It fetches the latest
origin/main, rebases dev onto it, and pushes with an explicit
--force-with-lease against the previously published dev. Enable the local
push guard with git config core.hooksPath .githooks.
Releasing a version
Runbash scripts/release.sh on top of the latest main, with only
chore(release): commits added. The script refuses to continue unless:
- the tag matches the workspace version, and
release-notes/v<version>.mdexists; - the tracked worktree is clean;
HEADcontains the latestorigin/main, and every commit after it is achore(release):commit.
pnpm update --latest, upgrades the Rust
registry requirements with cargo upgrade (including major and pinned
versions), and refreshes Cargo.lock with cargo update. The bridges for
RSA/getrandom, JNI/Tauri, Codex reqwest and the WebSocket configuration keep
their upstream-required version lines, but cargo update still refreshes their
compatible versions. Install the helper first with
cargo install cargo-edit --locked. If the refresh changes tracked files, the
script stops before tagging: resolve the compatibility changes, run the checks
above, commit and push to main, then run the script again. Prerelease tags
skip the dependency refresh.
The script then creates the tag and pushes main and the tag in one atomic
push. The tag’s workflow run publishes the release and beta channels, and the
run for main at the tagged commit publishes nothing. Afterwards, push dev
with scripts/push-dev.sh.
The workflow builds the packages, creates the signed update manifest and
uploads the assets. If a build or a signing step fails, the release is not
completely published.
Version tags also run scripts/publish-crates.sh, which publishes the
MIT-licensed library crates to crates.io. The other crates are used as git or path
dependencies; see Embedding the core.
Signing and store submission
Missing signing keys or Store identity variables make packaging fail.- Update manifest: signed with Ed25519. Self-update verifies it before installing.
- Android: APKs are signed with the
ANDROID_SIGNING_*secrets. - macOS: ad-hoc signing only.
- Windows: the packages use the Microsoft Store package identity. Configure
four variables in the
releaseenvironment:MS_STORE_IDENTITY_NAME,MS_STORE_DISPLAY_NAME,MS_STORE_IDENTITY_PUBLISHERandMS_STORE_PUBLISHER_DISPLAY_NAME. The CLI uses theMS_STORE_CLI_*counterparts. Headless has a fixed identity and display name, so its Store submission only addsMS_STORE_HEADLESS_PRODUCT_IDto the shared publisher variables. Stable releases keep the packages for both architectures for Store submission, and withMS_STORE_PUBLISH_ENABLEDset, a successful GitHub publication triggers the Store submission workflow. Store submission packages are unsigned; the Store signs them for distribution. - Windows on GitHub: release packages are not code-signed. The ZIP and MSIX
files attached to a release are unsigned, so Windows may warn before
running them, and an MSIX has to be installed with sideloading enabled.
Signed copies come from the Microsoft Store, also through
winget install --source msstore. The WinGet community source only takes signed MSIX, so it receives no new versions while releases are unsigned.