Repository navigation
Releases and signing
For maintainers and for anyone who builds Mistgate with a key of their own; the release key, reproducible builds, signing and publishing a release, a bundle of your own, and rotating the key.
Mistgate trusts one Ed25519 release key: whoever holds its private half decides what node agents and panels may install. The official binaries carry the project's key, and the maintainers sign every GitHub release with it. If you install the official release, you need nothing on this page; Updates covers running the fleet. This page is for the maintainers, and for anyone who builds the binaries with a key of their own.
- The private half stays offline. The public half is stamped into both binaries at build time:
RELEASE_KEYformake build,--keyformistgate release build, and the repository variableMISTGATE_RELEASE_PUBLIC_KEYin the release workflow. - The panel saves its key in
<data-dir>/release.pubon its first start. Arelease.pubthat differs from the compiled-in key is never accepted silently, in either direction: the panel trusts no bundle untilmistgate release trust-key(see "Rotate the release key" below). - A build without a key is unsigned: its node agents never update themselves, and the panel cannot install panel releases or offer a trusted agent to the SSH installation.
mistgate release keygen --out ~/mistgate-release.keyIt writes the private key to the file (mode 0600; an existing file is never overwritten) and prints the public key and its fingerprint (the first 16 hex characters of its SHA-256).
Warning
Keep the key file offline and back it up. Without it no node can be updated from the panel: you would make a new key, build new binaries with it, and update every node and the panel by hand once.
git checkout v0.1.4
RELEASE_KEY=<public key> VERSION=v0.1.4 make buildThis builds bin/mistgate-linux-{amd64,arm64} and bin/mistgate-node-linux-{amd64,arm64} with mistgate release build. The panel and agents receive the same version, build time (the commit time of the checkout) and release public key; mistgate version and mistgate-node version print the key's fingerprint.
For every stable tag vMAJOR.MINOR.PATCH, .github/workflows/release.yml runs four jobs. node builds both Linux node agents with Go only (no Node.js, no npm packages); panel builds the admin SPA with pnpm and then the two panel binaries; edge builds the Cloudflare edition (mistgate-edge.tar.gz, see "The edge bundle" below) and loads it in workerd. All three have read-only access to the repository. publish, the only job that may write and the only one that runs no build code, creates a draft release with the four binaries, mistgate-edge.tar.gz, BUILDINFO and SHA256SUMS. Every action is pinned to a commit SHA. A draft is not visible to panels as the latest release. The workflow requires the repository variable MISTGATE_RELEASE_PUBLIC_KEY and stamps it into all four binaries; if it is missing or invalid, the workflow fails instead of publishing a release that cannot be signed.
The private key never goes to Actions, and you do not have to trust the CI's binaries: on a machine that holds the key, you build the release from the tag yourself, and mistgate release sign signs only binaries that your build reproduces byte for byte. Then upload the seven manifest and signature files, including panel-manifest.sshsig and the two edge-manifest files, and publish the draft. Within 10 minutes panels download and verify the node bundle (nodes stay on their builds until the owner updates or schedules them), and Check GitHub offers the panel release.
VERSION=v0.1.6
git clone https://github.com/Mistgate/mistgate.git && cd mistgate # or git fetch --tags in your clone
git checkout "$VERSION"
(cd web && pnpm install --frozen-lockfile && pnpm build) # the panel binary and the edge bundle embed the SPA
(cd edge/worker && pnpm install --frozen-lockfile) # the esbuild that bundles the edge Worker scripts
go build -o ../mistgate-signer ./cmd/mistgate # the signer; any version works
gh release download "$VERSION" --repo Mistgate/mistgate \
--pattern 'mistgate-linux-*' --pattern 'mistgate-node-linux-*' --pattern 'mistgate-edge.tar.gz' --dir ../downloaded
../mistgate-signer release sign --key ~/mistgate-release.key --version "$VERSION" --expires 90d \
../downloaded/mistgate-node-linux-amd64 ../downloaded/mistgate-node-linux-arm64 \
../downloaded/mistgate-linux-amd64 ../downloaded/mistgate-linux-arm64 ../downloaded/mistgate-edge.tar.gz --out ../signed
gh release upload "$VERSION" ../signed/manifest.json ../signed/manifest.sig \
../signed/panel-manifest.json ../signed/panel-manifest.sig ../signed/panel-manifest.sshsig \
../signed/edge-manifest.json ../signed/edge-manifest.sig \
--repo Mistgate/mistgate --clobber
gh release edit "$VERSION" --repo Mistgate/mistgate --draft=falserelease sign refuses unless the checkout is exactly the tag without local changes, then rebuilds every binary with mistgate release build and the public half of your key and compares it with the downloaded one. The last command publishes the draft: do not publish it before all seven files have uploaded.
If signing refuses because a binary does not match its rebuild, do not sign: find out why first. The usual causes are a different key in MISTGATE_RELEASE_PUBLIC_KEY, a tag moved after the build, or a toolchain difference (below); an unexplained difference may mean a compromised runner.
mistgate release build (used by the workflow, by make build and by the check in release sign) gives byte-identical binaries for the same tag and key; a Linux build and a Windows cross-build of one commit were checked to match. It holds when:
- the Go toolchain is the one in the
toolchainline ofgo.mod.release buildasks for exactly that version, and a different local Go downloads it once from the Go module proxy; - the source is a clean checkout of the tag. Untracked files (the built SPA,
bin/) are not stamped into the binary (-buildvcs=false); keep the repository's.gitattributes, which checks every file out with LF line endings on every system; - the flags are the fixed ones
release buildsets:CGO_ENABLED=0,-trimpath,GOAMD64=v1,GOARM64=v8.0, and-ldflags "-s -w"with onlyVersion(the tag),Built(the commit time of the tag) andReleaseKey; - for the panel binaries, the SPA is built with
pnpm install --frozen-lockfile && pnpm build(Node.js 22, the pnpm version pinned inweb/package.json).
The check proves that the CI built what the tag says. It does not vet the tag's code or its locked dependencies: a malicious package in go.sum or pnpm-lock.yaml builds the same on your machine.
mistgate-edge.tar.gz is the Cloudflare edition: six Worker scripts and the admin SPA. Five scripts are roles of the panel, each compiled for js/wasm as its own program (link, provision, public, admin and jobs: <role>.js and <role>.wasm, with the version, build time and release public key stamped in like the other binaries); the sixth, front.js, is the router that holds the hostname and has no wasm. The scripts are bundled by the esbuild that edge/worker/package.json pins. The tarball also holds the SPA (assets/, uploaded to public and admin) and edge.json (schema 2), which lists the scripts in upload order with their bindings, Durable Object migrations, secret names, the cron of jobs and the route of front, for mistgate edge deploy. mistgate release build --version V --key PUBLIC mistgate-edge.tar.gz --out DIR builds it, after the SPA is built and pnpm install --frozen-lockfile has run in edge/worker; release sign rebuilds it the same way, refuses a tarball that differs and writes edge-manifest.json (version, build time, expiry, size and SHA-256 of the tarball) and edge-manifest.sig. The edge manifest is signed in its own Ed25519 context with the same release key, so neither it nor a panel or node signature verifies as another. panel-manifest.json is not touched.
The tarball is deterministic (entries sorted, fixed modes, owner and times, a gzip header without a name or time), and holds when the Go toolchain is the one go.mod names, web/dist and edge/worker/node_modules come from the frozen lockfiles, and the signer's Go is the same version as the one that built the tarball (the compression of the gzip layer belongs to the signer's Go; a different version that compresses differently makes release sign refuse, it never signs something else).
--expires (default 30d) is how long both manifests can be installed; a captured old manifest stops working when it runs out. Use a short lifetime such as 90d and renew it before it ends: run the same release sign command for the same tag with a new --expires and upload the seven files again with --clobber. Panels refresh the node bundle of the same build without updating nodes again, and they read the panel manifest anew at every check. A panel that finds an expired panel manifest says so and does not install the release; an expired node bundle can no longer be rolled out or used by the SSH installation.
A panel built with your own key ignores the official GitHub releases: their signatures do not verify with its key. It gets node updates from the bundle you sign and copy into its data directory, and you replace the panel binary by hand (see "Early builds and manual updates" in Updates).
mistgate release sign --key ~/mistgate-release.key --version v0.1.4 --expires 90d \
bin/mistgate-node-linux-amd64 bin/mistgate-node-linux-arm64 --out dist/- Run it in the checkout of the tag (or name it with
--source): it refuses a checkout that is not exactly the tag or has local changes, rebuilds the binaries and refuses one that differs. - The binaries must be named
<name>-<os>-<arch>, asmake buildnames them.mistgate-linux-*binaries given too go intopanel-manifest.json. - The build time comes from the tag's commit; a
--builtthat differs is refused. A new build whose own build time differs from the manifest rolls itself back after the update (built_mismatch). -
--expiresis a number of days (90d) or a Go duration (2160h); the default is30d. Keep it short and renew it (see above). - The command writes
dist/manifest.jsonanddist/manifest.sig, copies the binaries next to them, reads the result back and verifies it, then prints the version, the expiry, every file with its size and the key fingerprint.
scp dist/* panel.example.com:/var/lib/mistgate/dist/The panel reads <data-dir>/dist and notices a change within a minute; Read the folder again on the Updates page reads it at once. Only regular files count (a symbolic link is not followed). Replace the whole bundle at once; changing it while a rollout runs pauses the rollout. The Release bundle line of the Updates page shows whether the signature verified: see Updates.
- Make a new key with
mistgate release keygenand setMISTGATE_RELEASE_PUBLIC_KEY(or your ownRELEASE_KEY) to its public half. - Every installed agent trusts only the old key: update each node by hand once with an agent built with the new key, as for an old agent (see "Old agents: update by hand once" in Updates).
- A panel installs panel releases only under its compiled-in key, which is still the old one: replace the panel binary by hand with one built with the new key.
- The new panel logs
release key mismatchand trusts no bundle. On the panel server runmistgate release trust-key(as root or the panel's service user, with--data-dirif yours is not/var/lib/mistgate); it writes the new key torelease.puband prints the old and new fingerprints. Restart the panel.
A key swapped into release.pub by someone else never makes the panel trust another signer: only the binary's compiled-in key can become the installation's key, and only through trust-key.
Generated from docs/ in Mistgate/mistgate — edit the docs, not the wiki.
Сгенерировано из docs/ в Mistgate/mistgate — правьте документацию, а не вики.
由 Mistgate/mistgate 中的 docs/ 生成,请编辑文档,不要直接编辑 wiki。
English
Getting started
- Overview
- Requirements
- Install the panel
- Install the Cloudflare edition
- Add a node
- Install a node over SSH
- AI agent guide
- First users
Guide
- Nodes
- Profiles
- Hysteria2
- VLESS REALITY
- VLESS and Hysteria2 on one node
- AmneziaWG
- WARP
- Users and groups
- Subscriptions
- Client apps
- User page
- DNS
- Torrent protection
- Your own decoy site
Operations
- Health
- Reachability
- Telegram alerts
- Panel watcher
- Updates
- Releases and signing
- Encrypted panel backups
- Security
- Troubleshooting
Roadmap
Reference
Русский
Начало работы
- Обзор
- Требования
- Установка панели
- Установка Cloudflare-версии
- Добавление ноды
- Установка ноды по SSH
- Инструкция для AI-агента
- Первые пользователи
Руководство
- Ноды
- Профили
- Hysteria2
- VLESS REALITY
- VLESS и Hysteria2 на одной ноде
- AmneziaWG
- WARP
- Пользователи и группы
- Подписки
- Приложения-клиенты
- Страница пользователя
- DNS
- Защита от торрентов
- Свой сайт-ширма
Эксплуатация
- Здоровье
- Доступность
- Алерты в Telegram
- Наблюдатель панели
- Обновления
- Релизы и подпись
- Зашифрованные резервные копии панели
- Безопасность
- Решение проблем
План развития
Справочник