Files
voculinux/packaging/void
xavierk 34d33d73bd fix(void): package the fork's sources and runtime helpers, not upstream's
The app template fetched upstream's 0.18.5 archive, carried upstream's
homepage and maintainer, and renamed the launchers and desktop entry in
post_install. The fork's sdist already names them voculinux, so the
template now sources the fork's release asset and installs the names as
they are. The checksum is an explicit pending marker until the fork's
v0.18.5 release exists, which fails closed in xbps-src and never falls
back to the upstream digest.

depends now pulls in pipewire (capture), wtype (direct typing on niri),
wl-clipboard (clipboard), xdotool (X11 and XWayland) and pulseaudio-utils
(pactl), and drops ibus, which cannot inject on niri. Every name is
checked against the Void index.

INSTALL.msg states the niri first run plainly (no permission prompt;
typing goes direct), never points at the portal on niri, and drops the
IBus claim. The container smoke queries the fork's distribution name.

Tests assert the fork sources, the pending-or-pinned checksum, the
runtime helpers, the absence of ibus, and the niri wording.
2026-11-06 18:25:22 +05:30
..

Void packaging

Void Linux (glibc, x86_64) templates in void-packages layout, built with the standard Void source tooling. Submission to upstream void-packages is out of scope for spec #16; these templates serve the signed personal xbps repository first.

Layout

packaging/void/
├── README.md                  this file
├── build.sh                   podman harness: lint + xbps-src build + import smoke
├── smoke.sh                   ticket #28 gate: every template, one command
├── repo.sh                    ticket #29 pipeline: build all, index, sign
├── container-repo.sh          ticket #29 container half (index + sign + verify)
├── tool_checksums.txt         pinned base image and void-packages commit
└── srcpkgs/
    ├── python3-pywhispercpp/  ticket #26: whisper.cpp Python binding
    ├── ...                    ticket #25: small Python dependencies
    └── voculinux              ticket #27: the application

srcpkgs/ mirrors void-packages so the tree overlays cleanly: cp -r packaging/void/srcpkgs/. /path/to/void-packages/srcpkgs/ (build.sh does this inside the container.)

Templates

Template Ticket Notes
python3-pywhispercpp #26 Vulkan backend on by default; see below
voculinux #27 The app, from the canonical release sdist; see below

python3-pywhispercpp

Version 1.5.0, pinned to the same version install.sh (PYWHISPERCPP_VERSION) and uv.lock pin — never newest upstream. The sdist checksum in the template must equal the sha256 uv.lock records for the sdist; tests/test_void_pywhispercpp_template.py fails otherwise.

  • Vulkan is a build option, on by default. Rebuild CPU-only with build.sh python3-pywhispercpp -- -o ~vulkan pkg. There is no CUDA build: Void ships no CUDA toolkit package. Rebuilding locally with a hand-installed toolkit (GGML_CUDA=1) is a documented, unsupported escape hatch.
  • Portable CPU flags: every ggml SIMD option is pinned off (-DGGML_NATIVE=OFF -DGGML_SSE42=OFF ...), the plain x86-64 baseline already used by the Flatpak (#938, #957). The offline test asserts each pin in the template (the build configuration), and the harness scans the shipped libggml-cpu (.GCC.command.line, .comment) for recorded -march=native/-mcpu=native/-mtune=native flags and fails on a match. The recorded-flags scan is a tripwire, not a proof: the proof is the pinned configuration plus setup.py's documented CMAKE_ARGS passthrough.
  • Shared libraries: BUILD_SHARED_LIBS=ON with an $ORIGIN rpath (as install.sh does), so the extension resolves the whisper.cpp libraries installed beside it with no LD_LIBRARY_PATH.
  • Offline build: the sdist vendors whisper.cpp, pybind11, and the ggml sources; the build fetches nothing beyond the declared distfiles. PYWHISPERCPP_VERSION is injected because the sdist carries no version.txt (its version otherwise comes from git metadata).
  • Runtime deps (python3-numpy, python3-requests, python3-tqdm, python3-platformdirs) are the sdist's own install_requires and were verified to exist in current void-packages; the container build re-verifies via dependency resolution.

voculinux

Version 0.18.5, matching src/vocalinux/version.py — never newest upstream. The template builds the canonical release source distribution (the releases/download sdist, the same bytes published to PyPI) with a pinned checksum; tests/test_void_app_template.py fails otherwise. The 0.18.5 asset still carries the pre-rename archive root; xbps-src moves the single top-level directory to pkgname-version itself, so no wrksrc override is needed (wrksrc is deprecated and xlint rejects it). The first release cut after the rename points distfiles at the renamed asset.

  • Runtime deps are Void's own interpreter, GTK and desktop bindings (python3, python3-gobject, python3-cairo, gtk+3, libayatana-appindicator, ibus, gobject-introspection), the packaged Python libraries, and the three templates from tickets #25 and #26 (python3-pynput, python3-PyAudio, python3-pywhispercpp). Every name was verified to exist in current void-packages; the container build re-verifies via dependency resolution.
  • No payload beyond the app: no speech models (they download on first run), no vendored wheels, no pip at build or install time. The container smoke fails the build if the payload carries model weights or wheels.
  • Renamed paths: the voculinux.desktop entry, the hicolor icons, and a usr/share/voculinux/packaged marker the app reads to present package-manager updates. The 0.18.5 sdist predates the rename, so post_install promotes the old file names at install time; the payload smoke rejects any pre-rename leftovers in the OS-visible paths.
  • INSTALL.msg covers what xbps cannot express: the input group for the evdev hotkey backend, first-run model downloads, optional engines as manual additions (vosk, OpenAI whisper, Parakeet, faster-whisper, onnxruntime), the absent ydotool rung, and updating through xbps.
  • Update checker: packaged installs keep background checks as a signal, but every update affordance (tray entry, notification, About page) presents the package-manager path (src/vocalinux/utils/packaging.py).

Building

packaging/void/build.sh python3-pywhispercpp
packaging/void/build.sh python3-pywhispercpp -- -o ~vulkan pkg
packaging/void/build.sh voculinux

Podman only, never Docker. The base image digest and the void-packages commit come from tool_checksums.txt; bump them there, never in the script.

Smoke gate

One command runs the whole gate from a clean checkout — no host Void install — linting, building, installing and smoke-testing every template under srcpkgs in dependency order (the app last):

packaging/void/smoke.sh
# or: just void-smoke

Each package delegates to build.sh, so the pinned container and the per-package stages (xlint, xbps-src, install, payload/version/import smoke) stay implemented once. The gate fails fast: a broken package prints FAIL: <pkgname> naming which package broke, above it the harness echoes which stage broke. tests/test_void_smoke_gate.py asserts offline that the gate stays wired to every srcpkgs template, so a renamed or added template cannot silently drop out.

Signed repository

packaging/void/repo.sh builds every template in smoke-gate order, indexes the full repository (x86_64-repodata), and signs every binary package (*.sig2) plus the repository metadata with the RSA4096 repository key — one command, same pinned container via podman, no host Void install:

# Run the smoke gate first: the repo assembles tested packages.
packaging/void/smoke.sh
# or: just void-smoke

printf '%s' "$XBPS_SIGNING_KEY" > /tmp/xbps-sign-key.pem
XBPS_SIGNING_KEY_FILE=/tmp/xbps-sign-key.pem packaging/void/repo.sh
# or: XBPS_SIGNING_KEY_FILE=/tmp/xbps-sign-key.pem just void-repo

The pipeline stages publish/xbps/repo/ (packages, signatures, repodata) plus publish/xbps/keys/ (the client .plist named by key fingerprint, and a FINGERPRINT file carrying the signer identity and fingerprint for release notes). publish/ is local build output and stays uncommitted.

Fail-closed: a missing, unreadable, or non-RSA4096 key aborts before any container runs, so an unsigned repository can never publish. CI holds the PEM-encoded key in the XBPS_SIGNING_KEY secret and writes it to a file first (a file keeps newlines intact and the key out of process listings).

Maintainer key setup (one time):

openssl genrsa -out xbps-sign-key.pem 4096
# back the key up somewhere safe, then store it as XBPS_SIGNING_KEY

Client setup:

# as root on the Void system (the release notes carry the repo URL):
cp <fingerprint>.plist /var/db/xbps/keys/
TAG=v0.18.5  # match the release you install from
echo "repository=https://github.com/VocaHQ/vocalinux/releases/download/${TAG}/" > /etc/xbps.d/voculinux.conf
xbps-install -Sy voculinux

Answer Y when xbps shows the fingerprint prompt on first sync, and check it matches the published FINGERPRINT file. tests/test_void_signed_repo.py asserts offline that the pipeline stays wired to every srcpkgs template, stays podman-only on the pinned inputs, and keeps the fail-closed key contract.