release: restore ZUPT and harden source-only 5.2.2

This commit is contained in:
Cristian Cezar Moisés 2026-08-31 14:14:36 -03:00
commit ff99770bd0
205 changed files with 19627 additions and 13215 deletions

View file

@ -1,258 +1,290 @@
# Distributing VaptVupt
# Distributing ZUPT 5.2.2
This document describes the upstream packaging recipes shipped under `packaging/` and the path from a local source tree to an installable package.
This document describes the packaging material maintained in the ZUPT
source repository. A recipe in `packaging/` is not evidence that a package has
been accepted by a distribution or that every target platform has been tested.
Record each build and test result separately; an unexecuted target is a skip.
Real submission to AUR / Debian / Fedora / Homebrew / NixOS / openSUSE is operational work outside this repository.
The canonical repository is:
## Producing a reproducible source tarball
Every packaging recipe expects an upstream tarball `vaptvupt-VERSION.tar.gz` produced by the project's `make dist` target. The tarball is byte-reproducible:
```sh
make dist
# → /tmp/vaptvupt-5.0.0.tar.gz
```text
https://github.com/cristiancmoises/zupt
```
Re-running `make dist` on the same source tree produces an identical sha256 (verified by `tests/test_dist_reproducible.sh`, wired into `make test`). This lets distros pin a stable hash in their recipes.
GitHub is the canonical source and release host. Packaging must never fetch
`zupt-web` or substitute an asset from another project.
The reproducibility properties:
## Source-only boundary
- Files sorted by name (deterministic order across filesystems)
- mtime fixed to `SOURCE_DATE_EPOCH` (default `1747699200`; override via env)
- uid/gid pinned to root (0/0) via `--owner=0 --group=0 --numeric-owner`
- gzip wrapped with `-9n` (no embedded timestamp or filename)
- Source-only — no `.o`, no built binaries, no `.git/` tree
Git, `git archive`, and the upstream source tarball contain source code,
textual assembly, documentation, packaging metadata, and necessary data files.
They do not contain compiled objects or executables, shared or static libraries,
or DEB/RPM/AppImage packages.
The tree is source-only. The default `make` build needs only a C compiler, make, libm, and pthread — no external crypto library, and it installs no `.so`. The optional SDK-backed modes (`--pq-sdk`, `--pq-box`) and the Argon2id KDF are built only with `make WITH_SDK=1` against the separately distributed `libzuptsdk` / `libpqvaptvupt` libraries.
To force a specific epoch (for distro release-day pinning):
The default build is deliberately independent of the optional SDK and PQBOX
libraries:
```sh
SOURCE_DATE_EPOCH=1727740800 make dist # 2024-10-01 UTC
make clean
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
WITH_SDK=0 WITH_PQBOX=0
make WITH_SDK=0 WITH_PQBOX=0 check
```
## Recipes shipped
`WITH_SDK=1` and `WITH_PQBOX=1` use separately installed system development
libraries. They never load a library committed under `vendor/`, never download
a dependency during build or test, and fail explicitly when their development
metadata is unavailable. Distribution builds should keep both options at `0`
unless the corresponding source-built system packages are declared as build
requirements.
| Distro / Platform | Path | Format |
|-------------------|---------------------------------|----------------|
| Arch Linux | `packaging/aur/PKGBUILD` | AUR PKGBUILD |
| Debian / Ubuntu | `packaging/debian/` | Source package (`3.0 (quilt)`) |
| Fedora / RHEL | `packaging/rpm/vaptvupt.spec` | RPM .spec |
| openSUSE | `packaging/opensuse/` | RPM .spec (OBS) |
| macOS | `packaging/homebrew/vaptvupt.rb`| Homebrew formula |
| NixOS / Nix flake | `packaging/nix/flake.nix` | Nix flake |
All recipes:
- Install the binary to `$PREFIX/bin/vaptvupt` (default `/usr/bin/vaptvupt`)
- Install manpage to `$PREFIX/share/man/man1/vaptvupt.1.gz`
- Install docs (README, SECURITY, CHANGELOG) to `$PREFIX/share/doc/vaptvupt/`
- Run the full upstream regression suite (`make test`) during build when the distro's package guidelines allow check-phase execution
## Arch Linux (AUR)
Maintainer flow:
Audit the current tree or a generated archive with:
```sh
# 1. Produce the upstream tarball
make dist
# → /tmp/vaptvupt-5.0.0.tar.gz
# 2. Upload to a stable URL (e.g. git.securityops.co releases)
# 3. Update packaging/aur/PKGBUILD:
# - Set pkgver=5.0.0
# - Set sha256sums=("$(sha256sum /tmp/vaptvupt-5.0.0.tar.gz | awk '{print $1}')")
# 4. Generate .SRCINFO
cd packaging/aur && makepkg --printsrcinfo > .SRCINFO
# 5. Test locally
makepkg -s
# 6. Push to AUR
git clone ssh://aur@aur.archlinux.org/vaptvupt.git aur-vaptvupt
cp packaging/aur/PKGBUILD packaging/aur/.SRCINFO aur-vaptvupt/
cd aur-vaptvupt && git add -A && git commit -m "v5.0.0" && git push
scripts/check-source-only.sh
scripts/check-source-only.sh --archive /path/to/zupt-5.2.2.tar.gz
```
User install:
The scanner reports paths, not file contents, and exits nonzero on a violation.
## Reproducible source archive
`make dist` verifies committed `HEAD` and exports its tree object, normalizes
member order, timestamps, owner/group metadata, and gzip metadata, and audits
the result before moving it to its destination. Exporting the tree rather than
the commit omits Git's commit-ID PAX header:
```sh
yay -S vaptvupt # or paru, pikaur, etc.
SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)" \
make DIST_TARBALL=/tmp/zupt-5.2.2.tar.gz dist
sha256sum /tmp/zupt-5.2.2.tar.gz
```
## Shell completions
With identical committed input and `SOURCE_DATE_EPOCH`, repeated exports must
have the same SHA-256 digest. Do not generate a release tarball from uncommitted
working-tree files.
`make install` automatically installs Bash, zsh, and fish completion files alongside the binary and manpage:
The AUR, Homebrew, and Guix recipes pin the checksum of this tarball. They are
marked `export-ignore` in `.gitattributes` so their own checksum fields do not
make the archive self-referential. A commit changing only those ignored recipes
therefore leaves the fixed-epoch archive byte-identical. The recipes remain
versioned in Git and must be updated after the final source archive checksum is
known.
| Shell | Path |
|---|---|
| Bash | `$PREFIX/share/bash-completion/completions/vaptvupt` |
| zsh | `$PREFIX/share/zsh/site-functions/_vaptvupt` |
| fish | `$PREFIX/share/fish/vendor_completions.d/vaptvupt.fish` |
Do not commit the generated tarball or checksum file. Host them as immutable
release assets after the release tag is published.
The source files live under `completions/` in the project tree. Distros that prefer a different install location should override the relevant paths in their `make install` invocation.
## Staged installation
For per-user installation without root:
Packagers should preserve distribution flags and install into a package root:
```sh
# Bash
cp completions/vaptvupt.bash ~/.local/share/bash-completion/completions/vaptvupt
# zsh (somewhere in $fpath; add the directory to ~/.zshrc if needed)
cp completions/_vaptvupt ~/.zsh/completion/_vaptvupt
# fish
cp completions/vaptvupt.fish ~/.config/fish/completions/vaptvupt.fish
make -j"${JOBS:-1}" WITH_SDK=0 WITH_PQBOX=0 \
CPPFLAGS="$CPPFLAGS" CFLAGS="$CFLAGS" \
LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS"
make WITH_SDK=0 WITH_PQBOX=0 check
make DESTDIR="$pkgroot" PREFIX=/usr \
WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
```
Completions cover every CLI flag the binary actually parses (`--kdf`, `--comment`, `--comment-file`, `--pq`, `--dedup`, etc.) and are validated on every CI run via `tests/test_completions_manpage.sh`.
`INSTALL_LEGACY_ALIAS=0` installs only `zupt`. The `vaptvupt`
command can be requested explicitly with `INSTALL_LEGACY_ALIAS=1`, but it is
not installed by default and is not part of the openSUSE main package. This
keeps the canonical package surface limited to ZUPT and `zupt`.
## Debian / Ubuntu
The Makefile accepts the usual `BINDIR`, `LIBDIR`, `INCLUDEDIR`, `MANDIR`, and
completion-directory overrides. It does not strip package builds or add a
private-library RPATH.
The `packaging/debian/` tree is a Debian source-package layout. Maintainer flow:
## Packaging material
| Target | Maintained path | Intended output |
|---|---|---|
| openSUSE / OBS | `packaging/opensuse/` | source and binary RPM through OBS |
| Debian / Ubuntu | `packaging/debian/`, `packaging/build-deb.sh` | Debian metadata and binary DEB after the target gate |
| RPM release artifact | `packaging/opensuse/zupt.spec`, `packaging/build-rpm.sh` | source and binary RPM after the target gate |
| GUI DEB | `packaging/build-gui-deb.sh` | `zupt-gui_5.2.2_all.deb` after payload/dependency and installed integration gates |
| GUI RPM | `packaging/build-gui-rpm.sh` | `zupt-gui-5.2.2-1.noarch.rpm` and matching `.src.rpm` after package and installed integration gates |
| Linux CLI archive | `.github/workflows/ci.yml` | `zupt-5.2.2-linux-x86_64.tar.xz` with notices after dependency, member, and extracted functional gates |
| Portable GUI source | `packaging/portable/`, `.github/workflows/ci.yml` | `zupt-gui-5.2.2-portable.zip` after source scan, member allowlist, and extracted off-screen integration gate |
| Fedora / RPM-based systems | `packaging/rpm/zupt.spec` | downstream RPM starting point |
| AppImage helper | `packaging/build-appimage.sh` | downstream-only helper; no 5.2.2 AppImage is promoted |
| Windows | `.github/workflows/cross-platform.yml` | native ZIP (executable plus notices) after the required native gate |
| macOS | `packaging/build-dmg.sh` | native-architecture DMG after the native gate |
| Arch Linux | `packaging/aur/PKGBUILD` | AUR package recipe |
| Homebrew | `packaging/homebrew/zupt.rb` | formula-built package |
| Guix | `packaging/guix/zupt.scm` | Guix package definition |
| Nix | `packaging/nix/flake.nix` | flake-built package |
These files are upstream starting points. Use each distribution's isolated
builder and current policy checks; do not claim support based only on parsing a
recipe.
### openSUSE / OBS
The authoritative instructions, tested matrix, and outstanding gates are in
`packaging/opensuse/README.md`. The normal local flow is:
```sh
# 1. Produce the upstream tarball with the standard Debian
# orig.tar.gz naming convention:
make dist
cp /tmp/vaptvupt-5.0.0.tar.gz /tmp/vaptvupt_5.0.0.orig.tar.gz
# 2. Unpack and overlay the debian/ tree:
cd /tmp && tar xzf vaptvupt_5.0.0.orig.tar.gz && cd vaptvupt-5.0.0
cp -a /path/to/vaptvupt/packaging/debian ./debian
# 3. Build the source package:
dpkg-buildpackage -S -us -uc # source-only
dpkg-buildpackage -b -us -uc # binary
# 4. Lint:
lintian vaptvupt_5.0.0-1_*.deb
# 5. Submit via the standard Debian mentors process:
# https://mentors.debian.net/intro-maintainers/
cd packaging/opensuse
xmllint --noout _service
osc service manualrun
rpmspec -P zupt.spec >/dev/null
osc build openSUSE_Tumbleweed x86_64 zupt.spec
```
User install (after the package lands in Debian unstable / Ubuntu):
Run `rpmlint` on all produced RPMs and install the binary RPM in a disposable
environment for `--version`, `--help`, and archive round-trip tests. Presence of
the OBS files upstream does not mean the package has been submitted or accepted
by openSUSE Factory.
### Debian and RPM release artifacts
The release helper scripts build from this source tree, stage into temporary
directories, run their format and installed-binary checks, and place only their
final outputs in an explicitly selected directory. Run them from an exact
checkout of the immutable tag inside a clean target container, chroot, or VM:
```sh
sudo apt install vaptvupt
release_dir=$(mktemp -d)
# Native Debian/Ubuntu binary package
DIST_DIR="$release_dir" RUN_CHECKS=1 packaging/build-deb.sh
# Source and binary RPM using the openSUSE spec
DIST_DIR="$release_dir" packaging/build-rpm.sh
# Architecture-independent GUI DEB and noarch/source GUI RPM
DIST_DIR="$release_dir" packaging/build-gui-deb.sh
DIST_DIR="$release_dir" packaging/build-gui-rpm.sh
```
## Fedora / RHEL / CentOS
`packaging/build-deb.sh` creates a native binary DEB; it does not claim to
create a Debian source package. The files in `packaging/debian/` are Debian
source-package metadata and must be staged as the source package's top-level
`debian/` directory before using `dpkg-buildpackage`. Running
`dpkg-buildpackage` directly at the ZUPT repository root is not the
documented release-artifact path.
`packaging/build-rpm.sh` creates its audited Source0 archive, builds both the
binary RPM and source RPM, inspects the installed payload, and copies both
outputs to `DIST_DIR`. The separate `packaging/rpm/zupt.spec` is a
Fedora-family downstream starting point; build and lint it only after staging
Source0 in a normal RPM build tree.
Run the target's metadata and lint tools in addition to the script gates. A
package built for one distribution release or architecture is not evidence for
another.
The GUI helpers package Python/Qt source rather than compiled application code.
They validate exact version, payload, dependency, ownership and legacy-alias
expectations, then test the installed launcher off-screen against the matching
`zupt` CLI. A successful GUI DEB gate does not imply an RPM gate, or vice versa.
### Portable and native release artifacts
The Linux x86_64 gate packages the tested `zupt` executable as
`zupt-5.2.2-linux-x86_64.tar.xz` beside README, changelog, security guidance,
and every applicable public license and notice. Its dynamic-library allowlist,
archive member allowlist, and extracted CLI functional suite must pass.
The `zupt-gui-5.2.2-portable.zip` artifact is source-only: it contains the GUI
Python source, shell/macOS/Windows launchers, icons, provenance, changelog, and
licenses, but no Python, Qt, CLI, or compiled runtime. The gate scans both the
assembled and extracted trees, verifies an exact safe member allowlist, and
runs the extracted launcher off-screen against the tested CLI.
AppImage creation is deliberately offline and is not a 5.2.2 release gate.
Supply a locally verified `appimagetool`, type-2 runtime, and the complete
license/source-relink compliance notice for those exact runtime bytes; the
helper never downloads any input:
```sh
# 1. Produce the tarball
make dist
cp /tmp/vaptvupt-5.0.0.tar.gz ~/rpmbuild/SOURCES/
# 2. Drop the .spec into the SPECS directory:
cp packaging/rpm/vaptvupt.spec ~/rpmbuild/SPECS/
# 3. Build source + binary RPMs:
cd ~/rpmbuild && rpmbuild -ba SPECS/vaptvupt.spec
# 4. Lint:
rpmlint RPMS/x86_64/vaptvupt-5.0.0-1.fc*.rpm
# 5. Submit via the Fedora new-package review process:
# https://docs.fedoraproject.org/en-US/package-maintainers/Package_Review_Process/
# EPEL automatically inherits Fedora packages.
DIST_DIR="$release_dir" RUN_CHECKS=1 \
APPIMAGETOOL=/verified/path/appimagetool \
APPIMAGE_RUNTIME_FILE=/verified/path/runtime-x86_64 \
APPIMAGE_RUNTIME_COMPLIANCE_FILE=/verified/path/runtime-compliance.txt \
packaging/build-appimage.sh
```
User install (after the package lands in Fedora / EPEL):
The runtime inspected while preparing 5.2.2 omitted a linked component from
its notice and did not provide the complete LGPL source/relink handoff required
by this release policy. No AppImage produced by this helper is promoted by the
upstream 5.2.2 workflow. AppDir and Flatpak bundles and GUI platform installers
are also excluded. Bare Linux and Windows executables are not promoted; their
CLI programs appear only inside notice-bearing archives. The Windows ZIP and
macOS DMG remain CLI-only.
Run `packaging/build-dmg.sh` only on a native macOS host. It records the host
architecture in the filename and tests the binary before and after packaging:
```sh
sudo dnf install vaptvupt # Fedora
sudo dnf install epel-release vaptvupt # RHEL/CentOS via EPEL
DIST_DIR="$release_dir" RUN_CHECKS=1 packaging/build-dmg.sh
```
## openSUSE
The Windows ZIP (including its executable and notices) must be built and tested
by the Windows job in `.github/workflows/cross-platform.yml`; it is not a
cross-compiled release claim from a Linux build. No Wine result is retained as
5.2.2 release evidence. Extended-length/device namespace paths, raw UNC output
roots, and mapped/network-drive output are not supported in 5.2.2. Publish the
exact architecture recorded by the native job.
These helpers create binary distribution artifacts for the release page, not
content to be committed to Git or included in the source archive.
The `packaging/opensuse/` tree carries an RPM `.spec` suited to the Open Build Service (OBS).
### AUR, Homebrew, Guix, and Nix
```sh
# 1. Produce the tarball
make dist
After calculating the final reproducible source archive, but before creating or
publishing the immutable tag, update each recipe to version 5.2.2 and to the
exact digest or content hash expected by its package manager. These recipe
directories are excluded from the source archive, so this does not create a
checksum cycle. Commit the pinned recipes in the tagged tree, then build and
test with each package manager before publishing its recipe. Keep build inputs
offline-capable: the check phase must not fetch source or dependencies
dynamically.
# 2. In an OBS package checkout (osc), stage the sources and spec:
cp /tmp/vaptvupt-5.0.0.tar.gz .
cp /path/to/vaptvupt/packaging/opensuse/vaptvupt.spec .
## Release-page artifacts
# 3. Build locally against a target repository:
osc build openSUSE_Tumbleweed x86_64
The source-only policy applies to Git and upstream source archives. A release
page may also carry CLI/GUI DEB and RPM artifacts, the notice-bearing Linux CLI
tar.xz, source-only portable GUI ZIP, CLI Windows ZIP, or CLI macOS DMG when
each is built from the tagged source in its target environment and passes its
format-specific tests. These are separate outputs, never inputs to a source
build.
# 4. Commit to OBS once the build and check phase pass:
osc addremove && osc commit
```
For every published artifact:
User install (after the package lands in a distribution or OBS repository):
1. start from the immutable `v5.2.2` tag;
2. keep `WITH_SDK=0 WITH_PQBOX=0` unless system dependencies are declared;
3. record the exact OS, distribution release, architecture, and toolchain;
4. run format validation plus installed `--version`, `--help`, and archive
round-trip tests;
5. publish a SHA-256 checksum;
6. scan the source inputs and ensure no credential or build path is embedded;
7. label an unbuilt or untested target `SKIP`, never `PASS`.
```sh
sudo zypper install vaptvupt
```
Do not infer multi-architecture compatibility from portable source. Do not add
precompiled optional libraries to make a package build.
## macOS (Homebrew)
Publish release assets at the canonical GitHub release. If an expected asset is
absent or has a different checksum, report that target as unpublished rather
than redirecting consumers to an unverified file.
```sh
# 1. Produce the tarball and upload to a stable release URL.
## Downstream checklist
# 2. Update packaging/homebrew/vaptvupt.rb:
# - Set url to the release URL
# - Set sha256 to the upstream tarball sha256
# 3. Test locally:
brew install --build-from-source ./packaging/homebrew/vaptvupt.rb
brew test vaptvupt
brew audit --strict --online vaptvupt
# 4. Submit to homebrew-core (preferred, requires popularity threshold):
# https://docs.brew.sh/Adding-Software-to-Homebrew
#
# OR host in your own tap:
# https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap
```
User install (after submission lands):
```sh
brew install vaptvupt
# OR from a custom tap:
brew install cristiancmoises/tap/vaptvupt
```
## NixOS / Nix flake
```sh
# 1. Build directly from the flake (no central submission needed):
nix build github:cristiancmoises/vaptvupt#vaptvupt
nix run github:cristiancmoises/vaptvupt#vaptvupt -- version
# 2. To consume from another flake:
# inputs.vaptvupt.url = "github:cristiancmoises/vaptvupt?ref=v5.0.0";
# packages.x86_64-linux.default = inputs.vaptvupt.packages.x86_64-linux.vaptvupt;
# 3. To submit to nixpkgs (https://github.com/NixOS/nixpkgs):
# - Adapt packaging/nix/flake.nix's `vaptvupt` derivation into a
# pkgs/by-name/va/vaptvupt/package.nix using fetchurl and a hash.
# - Follow the nixpkgs contribution guide:
# https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md
```
## Submitting upstream — checklist
Before pushing any recipe to a distro repository:
- [ ] `make dist` produces a reproducible tarball (verified by `tests/test_dist_reproducible.sh` on every `make test`)
- [ ] The tarball is uploaded to a stable, immutable URL
- [ ] The recipe's checksum field is updated to match `sha256sum /tmp/vaptvupt-VERSION.tar.gz`
- [ ] The recipe builds and tests pass in a clean chroot/container
- [ ] The CHANGELOG mentions distro-relevant changes since the last release
- [ ] The license metadata is correct (AGPL-3.0-or-later for VaptVupt core; GPL-3.0-or-later for the vendored VaptVupt codec)
## Security posture for downstream
Every packaging recipe runs `make test` during build (`check()` for AUR, `override_dh_auto_test` for Debian, `%check` for RPM and openSUSE, `checkPhase` for Nix, `test` block for Homebrew). The test suite runs in each recipe's check phase, including the tamper/integrity regressions and the `make dist` byte-identical reproducibility check.
A build that doesn't pass `make test` will fail at distro check time — the recipes don't paper over regressions.
- [ ] The source URL resolves to the immutable `v5.2.2` tag.
- [ ] The source archive passes `scripts/check-source-only.sh --archive`.
- [ ] The recipe checksum matches the downloaded source exactly.
- [ ] `WITH_SDK=0 WITH_PQBOX=0` is explicit, or system dependencies are complete.
- [ ] Distribution compiler and linker flags are preserved.
- [ ] The real upstream `check` target runs without network access.
- [ ] Installation uses `DESTDIR` and does not write under `/usr/local`.
- [ ] The main package installs `zupt`; any `vaptvupt` alias is explicitly documented as compatibility-only.
- [ ] Licenses include AGPL-3.0-or-later for the application,
GPL-3.0-or-later for the bundled source codec, and BSD-2-Clause for the
xxHash-derived XXH64 routines, plus CC0-1.0 for the
pq-crystals/kyber-derived ML-KEM portions and BSD-3-Clause for the
curve25519-donna-derived X25519 portions.
- [ ] Package contents, dependencies, hardening, RPATH/RUNPATH, and debug info
have been inspected with target-native tools.
- [ ] Installed-package smoke and round-trip tests pass.
- [ ] Only tested target artifacts are attached to the release.