diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 4892bec..2baf3ca 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -26,6 +26,10 @@ code. - **yay** — GPL-3.0-or-later; the official ARM64 release and its versioned license are pinned in `guest/spec.json` and packaged into the guest's local repository. +- **Voxtype** — MIT; the signed v1.0.1 ARM64 CPU, ONNX, and OSD release assets, + release key, source archive, and checksums are pinned in `guest/spec.json`. + They are packaged in the guest's local repository but remain uninstalled + until the user invokes Omarchy's optional dictation installer. - **1Password** — proprietary software not redistributed by Try Omarchy. When a user explicitly invokes its optional ARM64 installer, the guest resolves the current vendor release and AUR CLI recipe after the factory build. These diff --git a/guest/build.sh b/guest/build.sh index 34fc7a0..aa5dc79 100755 --- a/guest/build.sh +++ b/guest/build.sh @@ -253,6 +253,11 @@ python3 "$guest_dir/scripts/apply-omarchy-backports.py" --root "$root" --spec "$ --work "$work" \ --spec "$spec" \ --pacman-config "$pacman_config" +"$guest_dir/scripts/register-pinned-voxtype.sh" \ + --root "$root" \ + --work "$work" \ + --spec "$spec" \ + --pacman-config "$pacman_config" "$guest_dir/scripts/register-local-repository.sh" --root "$root" --spec "$spec" arch-chroot "$root" /usr/local/lib/try-omarchy/finalize-rootfs arch-chroot "$root" pacman -Q | LC_ALL=C sort >"$root/usr/share/try-omarchy/packages.lock.txt" diff --git a/guest/keys/voxtype-release.asc b/guest/keys/voxtype-release.asc new file mode 100644 index 0000000..74a070a --- /dev/null +++ b/guest/keys/voxtype-release.asc @@ -0,0 +1,12 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: 9CCF 7915 B750 CAE8 B095 ED1A A3FC 9F33 FD20 9279 +Comment: Voxtype Release Signing &2 exit 1 } +expected_voxtype="$(read_spec '["supplyChain"]["voxtype"]["version"]')-$(read_spec '["supplyChain"]["voxtype"]["pkgrel"]')" +[[ ! $(pacman -Qq voxtype-bin 2>/dev/null || true) ]] || { + echo "Opt-in Voxtype must not be installed in the factory image" >&2 + exit 1 +} +voxtype_resolution=$(pacman -Sp --print-format '%n %v %a' voxtype-bin) +grep -Fxq "voxtype-bin $expected_voxtype aarch64" <<<"$voxtype_resolution" || { + echo "Pinned ARM64 Voxtype package does not resolve: $voxtype_resolution" >&2 + exit 1 +} +for dependency in gtk4-layer-shell which; do + grep -Eq "^${dependency} [^ ]+ aarch64$" <<<"$voxtype_resolution" || { + echo "Voxtype runtime dependency does not resolve for ARM64: $dependency" >&2 + exit 1 + } +done [[ $(pacman -Qoq /usr/local/bin/omarchy-native-cursor-restore) == try-omarchy-runtime ]] || { echo "Screensaver cursor helper is not owned by the Omarchy runtime package" >&2 exit 1 diff --git a/guest/scripts/register-local-repository.sh b/guest/scripts/register-local-repository.sh index 706f64d..c2ae641 100755 --- a/guest/scripts/register-local-repository.sh +++ b/guest/scripts/register-local-repository.sh @@ -61,16 +61,21 @@ print(spec["image"]["sourceDateEpoch"]) print(spec.get("guest", {}).get("profile")) hyprland = spec["supplyChain"]["hyprland"] print(f'{hyprland["version"]}-{hyprland["pkgrel"]}') +voxtype = spec["supplyChain"]["voxtype"] +print(f'{voxtype["version"]}-{voxtype["pkgrel"]}') PY ) -(( ${#metadata[@]} == 3 )) || fail "could not read local repository contract" +(( ${#metadata[@]} == 4 )) || fail "could not read local repository contract" source_date_epoch=${metadata[0]} profile=${metadata[1]} expected_hyprland_version=${metadata[2]} +expected_voxtype_version=${metadata[3]} [[ $source_date_epoch =~ ^[0-9]+$ ]] || fail "invalid source date epoch" [[ $profile == factory ]] || fail "native guest profile must be factory" [[ $expected_hyprland_version =~ ^[0-9]+\.[0-9]+\.[0-9]+-[0-9.]+$ ]] || fail "invalid patched Hyprland package version" +[[ $expected_voxtype_version =~ ^[0-9]+\.[0-9]+\.[0-9]+-[1-9][0-9]*$ ]] || + fail "invalid Voxtype package version" repo_name=try-omarchy repo_dir="$root/usr/share/try-omarchy/repo" @@ -78,7 +83,7 @@ repo_dir="$root/usr/share/try-omarchy/repo" shopt -s nullglob archives=("$repo_dir"/*.pkg.tar.zst) shopt -u nullglob -expected_archive_count=5 +expected_archive_count=6 (( ${#archives[@]} == expected_archive_count )) || fail "local repository expected $expected_archive_count package archive(s), found ${#archives[@]}" [[ ${archives[*]} == *'/try-omarchy-runtime-'* ]] || fail "local repository is missing the Omarchy runtime" @@ -87,6 +92,8 @@ expected_archive_count=5 [[ ${archives[*]} == *'/try-omarchy-yay-'* ]] || fail "factory repository is missing pinned yay" [[ ${archives[*]} == *"/hyprland-$expected_hyprland_version-aarch64.pkg.tar.zst"* ]] || fail "factory repository is missing patched Hyprland" +[[ ${archives[*]} == *"/voxtype-bin-$expected_voxtype_version-aarch64.pkg.tar.zst"* ]] || + fail "factory repository is missing pinned Voxtype" temporary=$(mktemp -d "$root/usr/share/try-omarchy/.repo-db.XXXXXX") cleanup() { @@ -143,8 +150,8 @@ if text.count(marker) != 1 or "\n[try-omarchy]\n" in text: raise SystemExit(1) block = """ # Immutable packages assembled from the checksummed Try Omarchy build spec. -# Keep this before remote repositories so Omarchy's explicit package reinstall -# resolves the patched Hyprland package locally. +# Keep this before remote repositories so Omarchy's explicit package installs +# resolve patched and ARM64-only packages locally. [try-omarchy] SigLevel = Optional TrustAll Server = file:///usr/share/try-omarchy/repo diff --git a/guest/scripts/register-pinned-voxtype.sh b/guest/scripts/register-pinned-voxtype.sh new file mode 100755 index 0000000..e4fbbb8 --- /dev/null +++ b/guest/scripts/register-pinned-voxtype.sh @@ -0,0 +1,395 @@ +#!/bin/bash + +set -euo pipefail + +usage() { + cat <<'USAGE' +Usage: register-pinned-voxtype.sh --root ROOT --work WORK --spec SPEC --pacman-config CONFIG + +Downloads the signed Voxtype ARM64 release pinned by the guest spec, packages +the CPU, ONNX, and OSD binaries, and stages the opt-in voxtype-bin package in +the guest's immutable local repository without installing it in the factory. +USAGE +} + +fail() { + echo "register-pinned-voxtype: $*" >&2 + exit 1 +} + +root="" +work="" +spec="" +pacman_config="" + +while (($#)); do + case "$1" in + --root) + root=${2:-} + shift 2 + ;; + --work) + work=${2:-} + shift 2 + ;; + --spec) + spec=${2:-} + shift 2 + ;; + --pacman-config) + pacman_config=${2:-} + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + fail "unknown option: $1" + ;; + esac +done + +[[ $root == /* && -d $root ]] || fail "--root must be an absolute staged root" +case "$root" in + /|/bin|/boot|/etc|/home|/opt|/root|/usr|/var) + fail "refusing unsafe root: $root" + ;; +esac +[[ $work == /* && -d $work ]] || fail "--work must be an absolute directory" +[[ -f $spec ]] || fail "spec not found: $spec" +[[ -f $pacman_config ]] || fail "pacman config not found: $pacman_config" +for command in arch-chroot curl gpg install pacman python3 sha256sum tar zstd; do + command -v "$command" >/dev/null || fail "$command is required" +done + +mapfile -t metadata < <(python3 - "$spec" <<'PY' +import json +import pathlib +import sys + +spec = json.loads(pathlib.Path(sys.argv[1]).read_text()) +component = spec.get("supplyChain", {}).get("voxtype") +if component is None: + print("disabled") + raise SystemExit +print("enabled") +for key in ( + "version", + "pkgrel", + "repository", + "sourceUrl", + "sourceSha256", + "sourceSignatureUrl", + "sourceSignatureSha256", + "signingKey", + "signingKeySha256", + "signingFingerprint", + "reportedVersion", + "license", +): + print(component[key]) +print(spec["image"]["architecture"]) +print(spec["image"]["sourceDateEpoch"]) +for name, asset in sorted(component["assets"].items()): + print("|".join((name, asset["url"], asset["sha256"], asset["signatureUrl"], asset["signatureSha256"]))) +PY +) || fail "could not read pinned Voxtype metadata" +[[ ${metadata[0]:-} == disabled ]] && exit 0 +[[ ${metadata[0]:-} == enabled ]] || fail "invalid pinned Voxtype state" +(( ${#metadata[@]} == 21 )) || fail "pinned Voxtype metadata is incomplete" +version=${metadata[1]} +pkgrel=${metadata[2]} +repository=${metadata[3]} +source_url=${metadata[4]} +source_sha256=${metadata[5]} +source_signature_url=${metadata[6]} +source_signature_sha256=${metadata[7]} +signing_key_relative=${metadata[8]} +signing_key_sha256=${metadata[9]} +signing_fingerprint=${metadata[10]} +reported_version=${metadata[11]} +license=${metadata[12]} +architecture=${metadata[13]} +source_date_epoch=${metadata[14]} +asset_records=("${metadata[@]:15}") + +[[ $architecture == aarch64 ]] || fail "pinned Voxtype package supports only aarch64" +[[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "invalid Voxtype version: $version" +[[ $pkgrel =~ ^[1-9][0-9]*$ ]] || fail "invalid Voxtype pkgrel: $pkgrel" +[[ $repository == https://github.com/peteonrails/voxtype ]] || fail "unexpected Voxtype repository" +[[ $source_url == "https://github.com/peteonrails/voxtype/archive/refs/tags/v$version.tar.gz" ]] || + fail "Voxtype source URL does not match the pinned tag" +[[ $source_signature_url == "https://github.com/peteonrails/voxtype/releases/download/v$version/voxtype-$version.tar.gz.asc" ]] || + fail "Voxtype source signature URL does not match the pinned release" +[[ $source_sha256 =~ ^[0-9a-f]{64}$ ]] || fail "invalid Voxtype source digest" +[[ $source_signature_sha256 =~ ^[0-9a-f]{64}$ ]] || fail "invalid Voxtype source signature digest" +[[ $signing_key_relative == keys/voxtype-release.asc ]] || fail "unexpected Voxtype signing key path" +[[ $signing_key_sha256 =~ ^[0-9a-f]{64}$ ]] || fail "invalid Voxtype signing key digest" +[[ $signing_fingerprint =~ ^[0-9A-F]{40}$ ]] || fail "invalid Voxtype signing fingerprint" +[[ $reported_version == "voxtype $version" ]] || fail "invalid Voxtype reported version" +[[ $license == MIT ]] || fail "unexpected Voxtype license: $license" +[[ $source_date_epoch =~ ^[0-9]+$ ]] || fail "invalid source date epoch" + +spec_dir=$(cd "$(dirname "$spec")" && pwd -P) +signing_key="$spec_dir/$signing_key_relative" +[[ -f $signing_key && ! -L $signing_key ]] || fail "Voxtype signing key is missing or symlinked" + +expected_asset_names=(audioBridge cpu onnx osd osdGtk4 osdQuickshell) +(( ${#asset_records[@]} == ${#expected_asset_names[@]} )) || fail "unexpected Voxtype asset count" +for index in "${!asset_records[@]}"; do + IFS='|' read -r name url digest signature_url signature_digest <<<"${asset_records[$index]}" + [[ $name == "${expected_asset_names[$index]}" ]] || fail "unexpected Voxtype asset order: $name" + [[ $digest =~ ^[0-9a-f]{64}$ ]] || fail "invalid Voxtype asset digest: $name" + [[ $signature_digest =~ ^[0-9a-f]{64}$ ]] || fail "invalid Voxtype signature digest: $name" + case "$name" in + audioBridge) suffix=audio-bridge ;; + cpu) suffix=cpu ;; + onnx) suffix=onnx ;; + osd) suffix=osd ;; + osdGtk4) suffix=osd-gtk4 ;; + osdQuickshell) suffix=osd-quickshell ;; + *) fail "unsupported Voxtype asset: $name" ;; + esac + expected_url="https://github.com/peteonrails/voxtype/releases/download/v$version/voxtype-$version-linux-aarch64-$suffix" + [[ $url == "$expected_url" ]] || fail "Voxtype asset URL does not match the pinned release: $name" + [[ $signature_url == "$expected_url.asc" ]] || fail "Voxtype signature URL does not match the pinned release: $name" +done + +verify_file() { + local expected=$1 + local path=$2 + printf '%s %s\n' "$expected" "$path" | sha256sum -c - >/dev/null +} + +verify_file "$signing_key_sha256" "$signing_key" || fail "Voxtype signing key digest mismatch" + +cache_dir="$work/download-cache" +install -d -m 0755 "$cache_dir" + +download_verified() { + local source_url=$1 + local expected=$2 + local destination=$3 + local temporary="" + + [[ ! -L $destination ]] || fail "refusing symlinked download cache entry: $destination" + if [[ -f $destination ]] && verify_file "$expected" "$destination"; then + return 0 + fi + rm -f "$destination" + temporary=$(mktemp "$cache_dir/.download.XXXXXX") + if ! curl --fail --location --proto '=https' --tlsv1.2 --silent --show-error \ + "$source_url" --output "$temporary"; then + rm -f "$temporary" + fail "download failed: $source_url" + fi + if ! verify_file "$expected" "$temporary"; then + rm -f "$temporary" + fail "download digest mismatch: $source_url" + fi + chmod 0644 "$temporary" + mv "$temporary" "$destination" +} + +source_cache="$cache_dir/voxtype-$version.tar.gz" +source_signature_cache="$source_cache.asc" +download_verified "$source_url" "$source_sha256" "$source_cache" +download_verified "$source_signature_url" "$source_signature_sha256" "$source_signature_cache" + +declare -A asset_paths=() +declare -A asset_digests=() +declare -A signature_paths=() +for record in "${asset_records[@]}"; do + IFS='|' read -r name url digest signature_url signature_digest <<<"$record" + asset_paths[$name]="$cache_dir/voxtype-$version-$name" + signature_paths[$name]="${asset_paths[$name]}.asc" + asset_digests[$name]=$digest + download_verified "$url" "$digest" "${asset_paths[$name]}" + download_verified "$signature_url" "$signature_digest" "${signature_paths[$name]}" +done + +stage=$(mktemp -d "$work/voxtype-package.XXXXXX") +cleanup() { + rm -rf "$stage" +} +trap cleanup EXIT + +gpg_home="$stage/gpg" +install -d -m 0700 "$gpg_home" +gpg --batch --homedir "$gpg_home" --import "$signing_key" >/dev/null 2>&1 || + fail "could not import the pinned Voxtype signing key" +mapfile -t imported_fingerprints < <( + gpg --batch --homedir "$gpg_home" --with-colons --fingerprint "$signing_fingerprint" | + awk -F: '$1 == "fpr" { print $10 }' +) +[[ ${imported_fingerprints[*]} == "$signing_fingerprint" ]] || fail "Voxtype signing key fingerprint mismatch" + +verify_signature() { + local signature=$1 + local payload=$2 + local status="" + if ! status=$(gpg --batch --homedir "$gpg_home" --status-fd 1 --verify "$signature" "$payload" 2>/dev/null); then + fail "Voxtype release signature verification failed: $(basename "$payload")" + fi + grep -q "^\[GNUPG:\] VALIDSIG $signing_fingerprint " <<<"$status" || + fail "Voxtype release signature used an unexpected key: $(basename "$payload")" +} + +verify_signature "$source_signature_cache" "$source_cache" +for name in "${expected_asset_names[@]}"; do + verify_signature "${signature_paths[$name]}" "${asset_paths[$name]}" +done + +python3 - "$source_cache" "$version" <<'PY' || fail "Voxtype source archive has an unsafe member set" +import pathlib +import posixpath +import sys +import tarfile + +archive = pathlib.Path(sys.argv[1]) +prefix = f"voxtype-{sys.argv[2]}" +required = { + f"{prefix}/LICENSE", + f"{prefix}/README.md", + f"{prefix}/config/default.toml", + f"{prefix}/packaging/completions/voxtype.bash", + f"{prefix}/packaging/completions/voxtype.fish", + f"{prefix}/packaging/completions/voxtype.zsh", + f"{prefix}/packaging/scripts/voxtype-configure-launcher", + f"{prefix}/packaging/systemd/voxtype.service", + f"{prefix}/packaging/voxtype-configure.desktop", + f"{prefix}/quickshell/shell.qml", + f"{prefix}/quickshell/voxtype-shared/qmldir", +} +seen = set() +with tarfile.open(archive, "r:gz") as source: + for member in source.getmembers(): + path = pathlib.PurePosixPath(member.name) + if path.is_absolute() or ".." in path.parts or not path.parts or path.parts[0] != prefix: + raise SystemExit(1) + if not (member.isfile() or member.isdir() or member.issym()): + raise SystemExit(1) + if member.issym(): + target = member.linkname + if target.startswith("/"): + raise SystemExit(1) + resolved = posixpath.normpath(posixpath.join(posixpath.dirname(member.name), target)) + if not (resolved == prefix or resolved.startswith(prefix + "/")): + raise SystemExit(1) + seen.add(member.name) +if not required <= seen: + raise SystemExit(1) +PY + +install -d -m 0755 "$stage/source" +tar -xzf "$source_cache" --no-same-owner --no-same-permissions -C "$stage/source" +source_root="$stage/source/voxtype-$version" + +for name in "${expected_asset_names[@]}"; do + python3 - "${asset_paths[$name]}" <<'PY' || fail "Voxtype $name asset is not an ARM64 ELF binary" +import pathlib +import struct +import sys + +data = pathlib.Path(sys.argv[1]).read_bytes()[:20] +if len(data) != 20 or data[:6] != b"\x7fELF\x02\x01" or struct.unpack_from(""$stage/.PKGINFO" < None: == vivaldi["signingKeySha256"], "Vivaldi package key digest matches the build spec", ) + voxtype = spec.get("supplyChain", {}).get("voxtype", {}) + voxtype_version = voxtype.get("version", "") + voxtype_release = f"https://github.com/peteonrails/voxtype/releases/download/v{voxtype_version}" + voxtype_assets = voxtype.get("assets", {}) + expected_voxtype_suffixes = { + "audioBridge": "audio-bridge", + "cpu": "cpu", + "onnx": "onnx", + "osd": "osd", + "osdGtk4": "osd-gtk4", + "osdQuickshell": "osd-quickshell", + } + check( + set(voxtype) + == { + "assets", + "license", + "pkgrel", + "reportedVersion", + "repository", + "signingFingerprint", + "signingKey", + "signingKeySha256", + "sourceSha256", + "sourceSignatureSha256", + "sourceSignatureUrl", + "sourceUrl", + "version", + } + and re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", voxtype_version) is not None + and voxtype.get("pkgrel") == 1 + and voxtype.get("repository") == "https://github.com/peteonrails/voxtype" + and voxtype.get("sourceUrl") + == f"https://github.com/peteonrails/voxtype/archive/refs/tags/v{voxtype_version}.tar.gz" + and voxtype.get("sourceSignatureUrl") + == f"{voxtype_release}/voxtype-{voxtype_version}.tar.gz.asc" + and voxtype.get("signingFingerprint") == "9CCF7915B750CAE8B095ED1AA3FC9F33FD209279" + and voxtype.get("signingKey") == "keys/voxtype-release.asc" + and voxtype.get("reportedVersion") == f"voxtype {voxtype_version}" + and voxtype.get("license") == "MIT" + and all( + re.fullmatch(r"[0-9a-f]{64}", voxtype.get(key, "")) is not None + for key in ( + "sourceSha256", + "sourceSignatureSha256", + "signingKeySha256", + ) + ) + and set(voxtype_assets) == set(expected_voxtype_suffixes) + and all( + asset.get("url") + == f"{voxtype_release}/voxtype-{voxtype_version}-linux-aarch64-{expected_voxtype_suffixes[name]}" + and asset.get("signatureUrl") == f'{asset.get("url")}.asc' + and re.fullmatch(r"[0-9a-f]{64}", asset.get("sha256", "")) is not None + and re.fullmatch(r"[0-9a-f]{64}", asset.get("signatureSha256", "")) is not None + for name, asset in voxtype_assets.items() + ), + "signed official Voxtype ARM64 release is fully pinned", + ) + voxtype_key = GUEST / voxtype["signingKey"] + check( + voxtype_key.is_file() + and hashlib.sha256(voxtype_key.read_bytes()).hexdigest() == voxtype["signingKeySha256"], + "Voxtype release key digest matches the build spec", + ) ttfx = spec.get("supplyChain", {}).get("ttfx", {}) check( ttfx @@ -424,6 +489,21 @@ def main() -> None: and vivaldi["signingFingerprint"] in launcher, "native launcher accepts only the reviewed signed Vivaldi ARM64 release", ) + voxtype_identity = hashlib.sha256( + json.dumps( + voxtype, + ensure_ascii=True, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + ).hexdigest() + check( + 'supply_chain.get("voxtype")' in launcher + and '"build spec voxtype component"' in launcher + and '"build spec voxtype assets"' in launcher + and voxtype_identity in launcher, + "native launcher accepts and pins the signed Voxtype ARM64 component", + ) hyprland_patch_text = read(hyprland_patch) check( "roundingWithBorderCoverage" in hyprland_patch_text @@ -567,14 +647,15 @@ def main() -> None: "pacman recovery files snapshot the final local-repository configuration", ) check( - "expected_archive_count=5" in local_repository + "expected_archive_count=6" in local_repository and "factory repository is missing pinned ttfx" in local_repository and "factory repository is missing pinned yay" in local_repository and "factory repository is missing patched Hyprland" in local_repository + and "factory repository is missing pinned Voxtype" in local_repository and "immutable local repository does not have priority" in local_repository - and "resolves the patched Hyprland package locally" in local_repository + and "resolve patched and ARM64-only packages locally" in local_repository and "refusing canonical unsafe root" in local_repository, - "immutable local repository requires and prioritizes the patched Hyprland", + "immutable local repository requires and prioritizes patched and ARM64-only packages", ) shared_folder = spec["runtime"]["sharedFolder"] check( @@ -755,11 +836,43 @@ def main() -> None: and "refusing canonical unsafe root" in register_hyprland, "guest builds and packages the verified Hyprland rounded-border backport", ) + register_voxtype = read(GUEST / "scripts/register-pinned-voxtype.sh") + finalizer = read(GUEST / "scripts/finalize-rootfs.sh") + check( + "register-pinned-voxtype.sh" in build + and build.index("register-pinned-voxtype.sh") + < build.index("register-local-repository.sh") + and "Voxtype signing key digest mismatch" in register_voxtype + and "Voxtype release signature used an unexpected key" in register_voxtype + and "Voxtype source archive has an unsafe member set" in register_voxtype + and "is not an ARM64 ELF binary" in register_voxtype + and "pkgname = $package_name" in register_voxtype + and "package_name=voxtype-bin" in register_voxtype + and "provides = voxtype=$version" in register_voxtype + and "conflict = voxtype" in register_voxtype + and "depend = gtk4-layer-shell" in register_voxtype + and "depend = which" in register_voxtype + and "Voxtype package is missing required runtime dependency" in register_voxtype + and "optdepend = gtk4-layer-shell" not in register_voxtype + and 'ln -s /usr/lib/voxtype/voxtype-native "$stage/usr/bin/voxtype"' + in register_voxtype + and "Registered opt-in $query" in register_voxtype + and "pacman -Sp --print-format '%n %v %a' voxtype-bin" in finalizer + and "Voxtype runtime dependency does not resolve for ARM64" in finalizer + and "Opt-in Voxtype must not be installed in the factory image" in finalizer, + "guest packages signed ARM64 Voxtype as an opt-in upstream-compatible target", + ) third_party_notices = read(REPO / "THIRD_PARTY_NOTICES.md") check( "**yay**" in third_party_notices and "GPL-3.0-or-later" in third_party_notices, "third-party notices cover the pinned yay redistribution", ) + check( + "**Voxtype**" in third_party_notices + and "MIT" in third_party_notices + and "remain uninstalled" in third_party_notices, + "third-party notices cover the opt-in Voxtype redistribution", + ) check( "**ttfx**" in third_party_notices and "TerminalTextEffects" in third_party_notices diff --git a/macos/run-qemu-gpu.sh b/macos/run-qemu-gpu.sh index 2337892..994e030 100755 --- a/macos/run-qemu-gpu.sh +++ b/macos/run-qemu-gpu.sh @@ -513,6 +513,7 @@ supply_chain_keys = { "omarchyPackagesRepository", "ttfx", "vivaldi", + "voxtype", "yay", } supply_chain = exact_keys(spec.get("supplyChain"), supply_chain_keys, "build spec supply chain") @@ -687,6 +688,41 @@ if vivaldi != { "license": "Multiple, see https://www.vivaldi.com/", }: fail("Vivaldi installer is not pinned to the reviewed signed ARM64 release") +voxtype = exact_keys( + supply_chain.get("voxtype"), + { + "assets", + "license", + "pkgrel", + "reportedVersion", + "repository", + "signingFingerprint", + "signingKey", + "signingKeySha256", + "sourceSha256", + "sourceSignatureSha256", + "sourceSignatureUrl", + "sourceUrl", + "version", + }, + "build spec voxtype component", +) +voxtype_assets = exact_keys( + voxtype.get("assets"), + {"audioBridge", "cpu", "onnx", "osd", "osdGtk4", "osdQuickshell"}, + "build spec voxtype assets", +) +for name, asset in voxtype_assets.items(): + exact_keys( + asset, + {"sha256", "signatureSha256", "signatureUrl", "url"}, + f"build spec voxtype asset {name}", + ) +voxtype_identity = hashlib.sha256( + json.dumps(voxtype, ensure_ascii=True, sort_keys=True, separators=(",", ":")).encode("utf-8") +).hexdigest() +if voxtype_identity != "906951dd6a221d39a63116af86dddf77c202bf8dfca59cccc73536c44cd22669": + fail("factory Voxtype component is not the reviewed signed ARM64 release") command_line = runtime.get("kernelCommandLine") if not isinstance(command_line, str) or not command_line or any(character in command_line for character in "\x00\r\n\t"):