From 57ea984ed4dc15a860389d908a2a5c3493f034bc Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Tue, 15 Sep 2026 00:37:22 +0530 Subject: [PATCH 1/7] Prepare RC4 bootstrap with finalized signing trust --- build-inputs/omarchy-mac-keyring/PKGBUILD | 8 +- build-packages.sh | 47 +++++++- default/pacman/keyrings/omarchy-mac-trusted | 2 +- default/pacman/keyrings/omarchy-mac.gpg | Bin 803 -> 534 bytes docs/arm-package-sources.md | 2 +- install.sh | 2 +- install/helpers/arm-channel.sh | 37 +++--- migrations/1789316115.sh | 2 +- migrations/1789390468.sh | 14 +++ migrations/1789407944.sh | 19 +++ .../install-mac-snapper-dependency-test.sh | 2 +- .../omarchy-mac-installer-trust-test.sh | 42 +++++++ .../omarchy-mac-keyring-migration-test.sh | 2 +- ...marchy-mac-keyring-package-install-test.sh | 108 ++++++++++++++++++ .../omarchy-mac-keyring-successor-test.sh | 60 ++++++++++ .../omarchy-mac-keyring-transition-test.sh | 106 +++++++++++++++++ test/shell.d/package-build-contract-test.sh | 24 +++- test/shell.d/rc4-bootstrap-boundary-test.sh | 61 ++++++++++ 18 files changed, 510 insertions(+), 28 deletions(-) create mode 100644 migrations/1789390468.sh create mode 100644 migrations/1789407944.sh create mode 100644 test/shell.d/omarchy-mac-installer-trust-test.sh create mode 100644 test/shell.d/omarchy-mac-keyring-package-install-test.sh create mode 100644 test/shell.d/omarchy-mac-keyring-successor-test.sh create mode 100644 test/shell.d/omarchy-mac-keyring-transition-test.sh create mode 100644 test/shell.d/rc4-bootstrap-boundary-test.sh diff --git a/build-inputs/omarchy-mac-keyring/PKGBUILD b/build-inputs/omarchy-mac-keyring/PKGBUILD index 31e730e6f5c..de4de9a1f1d 100644 --- a/build-inputs/omarchy-mac-keyring/PKGBUILD +++ b/build-inputs/omarchy-mac-keyring/PKGBUILD @@ -1,16 +1,16 @@ # Maintainer: Omarchy Mac pkgname=omarchy-mac-keyring -pkgver=20260913 -pkgrel=1 +pkgver=20260914 +pkgrel=2 pkgdesc='Omarchy Mac package signing keyring' arch=(any) url='https://github.com/omarchy-mac/omarchy-mac' license=('GPL-3.0-or-later') install=$pkgname.install source=('omarchy-mac.gpg' 'omarchy-mac-trusted' 'omarchy-mac-revoked') -sha512sums=('0e411148bf58cb1cf3c1ef544ea0936d82b0d7b3996c3ef3d20ac909959129c1d7cb58dc84ac466cd3e2d75db228aff896640899b4bbbef19e7636f3033a7ea8' - '2fb4497f96fd9f446e0177f9dbf8eb34f0ad3dce5fc7fb687312c5e20a1127ceb68f0d6022424f35b37e2a6a91fb097b2212595375fa4d37d7547697b17de86e' +sha512sums=('3b09ea4dcae2e7957c8543fc926522cb35f19915d5855b998db78761f4d35b04a1409f53726e47267fcfecfd31e66b7d16a8894603dbb20886dd000e4ef984fc' + '5ff1f0c04c98963d89a941fe160bb4a3010ee541f1a013931230eae40e1c7f9ea59b867d4241b4d8c1f39387655c8e1794db3912e5c369f9e98883df074d46a7' 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e') package() { diff --git a/build-packages.sh b/build-packages.sh index a622b946a6a..016b600bd6c 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -122,14 +122,53 @@ ensure_snapper_dependency() { } ensure_omarchy_mac_keyring_dependency() { - local pkgbuild="$1" + local pkgbuild="$1" minimum="20260914-2" + local index in_depends=0 found=0 insert_index=0 line scan token suffix operator version replacement + local pattern="['\"]omarchy-mac-keyring((>=|<=|=|>|<)([^'\"]+))?['\"]" + local -a lines=() grep -qx 'depends=(' "$pkgbuild" || fail "omarchy PKGBUILD no longer has the expected depends array: $pkgbuild" - if ! sed -n '/^depends=(/,/^)/p' "$pkgbuild" | - grep -qE "^[[:space:]]*['\"]omarchy-mac-keyring([<>=][^'\"]*)?['\"]([[:space:]]|$)"; then - sed -i "/^depends=(/a\\ 'omarchy-mac-keyring'" "$pkgbuild" + mapfile -t lines <"$pkgbuild" + for index in "${!lines[@]}"; do + line=${lines[$index]} + if [[ $line == 'depends=(' ]]; then + in_depends=1 + insert_index=$index + elif [[ $line == ')' ]]; then + in_depends=0 + elif (( in_depends )); then + scan=${line%%#*} + while [[ $scan =~ $pattern ]]; do + token=${BASH_REMATCH[0]} + suffix=${BASH_REMATCH[1]} + operator=${BASH_REMATCH[2]} + version=${BASH_REMATCH[3]} + replacement="'omarchy-mac-keyring>=$minimum'" + if [[ -n $suffix ]]; then + case "$operator" in + '>='|'>'|'=') + if (( $(vercmp "$version" "$minimum") >= 0 )); then + replacement=$token + elif [[ $operator == '=' ]]; then + fail "Keyring pin predates the required trust transition: $token" + fi + ;; + *) fail "Keyring upper bound needs review for the trust transition: $token" ;; + esac + fi + # Replace only this dependency token, preserving adjacent dependencies. + line=${line//"$token"/"$replacement"} + scan=${scan#*"$token"} + found=1 + done + lines[$index]=$line + fi + done + if (( ! found )); then + lines[$insert_index]+=$'\n'" 'omarchy-mac-keyring>=$minimum'" fi + printf '%s\n' "${lines[@]}" >"$pkgbuild" } # Upstream's package() deletes /etc/mkinitcpio.conf.d wholesale on aarch64, diff --git a/default/pacman/keyrings/omarchy-mac-trusted b/default/pacman/keyrings/omarchy-mac-trusted index 557aa18eee6..56f9bf5360b 100644 --- a/default/pacman/keyrings/omarchy-mac-trusted +++ b/default/pacman/keyrings/omarchy-mac-trusted @@ -1 +1 @@ -F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7:4: +FBD6874D423C418DDB6D143EECE19CDDE306DBD2:4: diff --git a/default/pacman/keyrings/omarchy-mac.gpg b/default/pacman/keyrings/omarchy-mac.gpg index 24fbc026b911e56e8f7217a3ddc4c774672fd3fa..86b8ecb319fa5d69e5dc7af430b643a134ad773c 100644 GIT binary patch literal 534 zcmbPX%#yYIZL1ikHX9=g<1Kf7Mn-mro{Voon)lAMEGgWdGkK{V%U_A~*3|9$jGiQZ zC}b=A(6&X|KR2-`Iipg+H!)cuATc>RF+EiwI5RyjGcUbk8jG+P7lWOcBFpbSn zj=i^YMeN=@oOAav+wDuNOd$K1q#0Q`Sr^Z0VCClIU}s_z<>KVvW?~UzVrFELV`LI< zVBq2ufN61A%kaPSdf$?L*Ix8Iykn-Yi{byFLl$$}7lt)F23e^@|A{HRkn`;+GpvkTEF@DTygU@<{j94syJ!NIk(fb9P@WFsC-XjwYzrv zR}5=K3ky#}>%Omz6AL@OvPb|uqej3pOst%Y4~-ib8WD~zQ(%z<>XZfQyguII9!lOVwW@+C~9se+MVCA)C#rsMyVW;yaEmGahB2Hs{|v+QBj(}RK6 z6wgcMw=(?CS+>8nq)@VR;ioAdJrvH#FaPxZvEhG>HNmlKN2L6uk2*=02H#h>C|fyl%Ri2KjUVo14lA@}VMX5A*AHX0ypovdO+-nFhMJhpIjoI&?><0H~XR}o#l~jzjJS~a9k-60(%=v6G2n}Uz7$~xqU?vI0QQLt4 zieN4FFs=(GL^j3x{h`4oNeZY@Wq$D4mhROjnCK9yz-& zlI>A>pYh5}rW>>@L5=HM>!v-P5>3l1f7ThnUgx_*=;v26s1|uEiU;i{hKcTfvQwrX z|Av==VVf$oi&Gv>@}>Vy8|rtS&z;eFo6pZ{d(ZF(s#iSV$`T?$%QHBYs8ATvYpsT= z9e5rxWFJ~|?OE&n=`=-=(_e<`0jjUxqXrpo0RRU_AwNXL8VHMp3rNd&6c0{*V6%%# zjzU|erSVB*aS;}rH(%|3S3i~DDmaF}!~?A%^w=ZWcgyZLH%71DOiztB94Gd|zCVgh zhqiG!wk^8$B8JEU<%1qACW4#Ohq=7$`Tg=vPFgIZ_*nACjN zT43lUb+;-rcgn|Vc^DTq8%p)_LMS@10aSvtsU)E3jb4dBqxW>tpQo3uPy1N&csiE( eihw?_3@Og|s7#r>Dz8U!wv^ukC%*Nc_5T7NnffCD diff --git a/docs/arm-package-sources.md b/docs/arm-package-sources.md index 7a5fc381e7b..319769b4a99 100644 --- a/docs/arm-package-sources.md +++ b/docs/arm-package-sources.md @@ -12,7 +12,7 @@ Use `omarchy update` for system upgrades. A bare `pacman -Syu` does not update t The fork-owned repository uses distinct `stable`, `rc`, and `edge` release coordinates under `https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/`. All three lanes provide `omarchy` and `omarchy-settings`; ARM does not request the x86 `omarchy-dev` pair. Channel reporting reads the managed ARM server, so an older installation pointing at `/edge` reports edge even when its installed package names are `omarchy` and `omarchy-settings`. -Omarchy Mac has a separate package-signing primary key, `F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7`. Its exact public bytes ship in `omarchy-mac-keyring`; upstream `omarchy-keyring` remains installed for upstream packages. The 4.0.3rc4 bootstrap is the final unsigned fork transaction and retains the old repository policy only long enough to deliver and populate this trust. The following RC is signed by subkey `6C2597C6E69FC4898D3D560E331307696030285E` and changes the fork policy to `PackageRequired DatabaseRequired TrustedOnly`. Against that signed lane, a client that skipped the bootstrap cannot verify the candidate and stops before changing packages. +Omarchy Mac's new package-signing primary is `FBD6874D423C418DDB6D143EECE19CDDE306DBD2`, with signing subkey `D791ED0C72439D9F8757421258043B2770A25762`. Only the new fork primary ships in `omarchy-mac-keyring` version `20260914-2`; upstream keyrings remain installed. This local RC4 bootstrap candidate delivers and populates the keyring without changing the existing fork signature policy. New bootstrap configurations use the explicitly disclosed `Optional TrustAll` policy; stricter existing policies are preserved. After actual client delivery and upgrade validation, a separate signed RC5 may activate `PackageRequired DatabaseRequired TrustedOnly`. This candidate contains no strict-signing activation migration, and preparing it does not publish a release. An explicit channel switch goes through the normal update lock, snapshot and migration pipeline. It stages the current pacman configuration, changing only the managed ARM lane and reapplying the existing explicit upstream graphics policy. Other repository ordering, options and mirror Includes are preserved. Custom or ambiguous ARM server/Include layouts are rejected rather than guessed. ARM refresh uses this same path and no longer runs the reset-only `pre-refresh-pacman` hook, because it does not discard and recreate the user's configuration. The x86 reset path retains that hook; normal update hooks still run after successful migrations. diff --git a/install.sh b/install.sh index d8de1f8aaeb..f8463b5db1b 100755 --- a/install.sh +++ b/install.sh @@ -11,7 +11,7 @@ set -euo pipefail readonly checkout="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" readonly package_output="$checkout/build-output" readonly asahi_alarm_key="12CE6799A94A3F1B5DDFFE88F576553597FB8FEB" -readonly omarchy_mac_key="F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7" +readonly omarchy_mac_key="FBD6874D423C418DDB6D143EECE19CDDE306DBD2" source "$checkout/install/helpers/arm-package-sources.sh" source "$checkout/install/helpers/arm-channel.sh" install_channel="${OMARCHY_MIRROR:-}" diff --git a/install/helpers/arm-channel.sh b/install/helpers/arm-channel.sh index 93e48e787da..c02d6fc36e2 100644 --- a/install/helpers/arm-channel.sh +++ b/install/helpers/arm-channel.sh @@ -213,6 +213,29 @@ omarchy_arm_channel_key_fingerprints() { # Preflight has no installed-package/config/keyring side effects. The caller # retains this directory until applying or abandoning the captured transaction. +# Trust the pinned new primary only inside the private transaction keyring. +omarchy_arm_channel_trust_fork() { + local keyring="$1" fork_key import_needed=0 + local active_key="FBD6874D423C418DDB6D143EECE19CDDE306DBD2" + local fork_keyfile="${OMARCHY_SIGNING_SOURCE:-$OMARCHY_PATH}/default/pacman/keyrings/omarchy-mac.gpg" + for fork_key in "$active_key"; do + if ! sudo gpg --homedir "$keyring" --batch --list-keys "$fork_key" >/dev/null 2>&1; then + import_needed=1 + fi + done + if (( import_needed )); then + [[ -f $fork_keyfile && ! -L $fork_keyfile ]] || { + echo "Pinned Omarchy Mac signing key is missing or unsafe: $fork_keyfile" >&2 + return 1 + } + sudo pacman-key --gpgdir "$keyring" --add "$fork_keyfile" || return 1 + fi + for fork_key in "$active_key"; do + omarchy_arm_channel_key_fingerprints "$keyring" | grep -qxF "$fork_key" || return 1 + sudo pacman-key --gpgdir "$keyring" --lsign-key "$fork_key" || return 1 + done +} + omarchy_arm_channel_prepare() { local stage="$1" channel="$2" allow_new="${3:-}" local config="${OMARCHY_PACMAN_CONFIG:-/etc/pacman.conf}" @@ -250,19 +273,7 @@ omarchy_arm_channel_prepare() { if [[ $allow_new == "fresh" ]]; then sudo pacman-key --gpgdir "$stage/keyring" --lsign-key "$key" fi - # The fork key is source-pinned. Import exactly those committed bytes rather - # than consulting a keyserver, then verify the full primary fingerprint. - local fork_key="F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7" - local fork_keyfile="${OMARCHY_SIGNING_SOURCE:-$OMARCHY_PATH}/default/pacman/keyrings/omarchy-mac.gpg" - if ! sudo gpg --homedir "$stage/keyring" --batch --list-keys "$fork_key" >/dev/null 2>&1; then - [[ -f $fork_keyfile && ! -L $fork_keyfile ]] || { - echo "Pinned Omarchy Mac signing key is missing or unsafe: $fork_keyfile" >&2 - return 1 - } - sudo pacman-key --gpgdir "$stage/keyring" --add "$fork_keyfile" - omarchy_arm_channel_key_fingerprints "$stage/keyring" | grep -qxF "$fork_key" || return 1 - fi - sudo pacman-key --gpgdir "$stage/keyring" --lsign-key "$fork_key" + omarchy_arm_channel_trust_fork "$stage/keyring" || return 1 omarchy_arm_channel_key_fingerprints "$stage/keyring" >"$stage/keys-before" local -a probe=(--config "$stage/resolved.conf" --dbpath "$stage/db" --cachedir "$stage/cache" --gpgdir "$stage/keyring" --logfile "$stage/preflight.log") sudo env OMARCHY_UPDATE_PACMAN=1 pacman "${probe[@]}" -Sy --noconfirm diff --git a/migrations/1789316115.sh b/migrations/1789316115.sh index b81f5978f2f..2a3ff447171 100644 --- a/migrations/1789316115.sh +++ b/migrations/1789316115.sh @@ -1,6 +1,6 @@ echo "Trust packages signed by Omarchy Mac" -readonly omarchy_mac_signing_key='F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7' +readonly omarchy_mac_signing_key='FBD6874D423C418DDB6D143EECE19CDDE306DBD2' # The package is a dependency of omarchy, but keep this self-repairing for a # partial/manual upgrade. Do not weaken the repository policy to fetch it. diff --git a/migrations/1789390468.sh b/migrations/1789390468.sh new file mode 100644 index 00000000000..c3cb39a43b1 --- /dev/null +++ b/migrations/1789390468.sh @@ -0,0 +1,14 @@ +echo "Populate Omarchy Mac signing trust with the new key" + +# The updated keyring must arrive through an already trusted transaction before +# activating the replacement signer. This migration does not change repo policy. +if omarchy-pkg-missing omarchy-mac-keyring; then + echo "Install the reviewed Omarchy Mac keyring transition before activating the replacement signer." >&2 + return 1 +fi + +sudo pacman-key --populate omarchy-mac +for omarchy_mac_signing_key in \ + FBD6874D423C418DDB6D143EECE19CDDE306DBD2; do + sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" +done diff --git a/migrations/1789407944.sh b/migrations/1789407944.sh new file mode 100644 index 00000000000..2297a382c65 --- /dev/null +++ b/migrations/1789407944.sh @@ -0,0 +1,19 @@ +echo "Populate the finalized Omarchy Mac signing keyring" + +# A successor is required for clients that already marked earlier migrations +# complete. Never remove existing keys or change repository signature policy. +installed=$(pacman -Q omarchy-mac-keyring) || { + echo "Install the reviewed Omarchy Mac keyring package first." >&2 + exit 1 +} +version=${installed#* } +comparison=$(vercmp "$version" 20260914-2) || exit 1 +if (( comparison < 0 )); then + echo "Omarchy Mac keyring 20260914-2 or newer is required." >&2 + exit 1 +fi + +sudo pacman-key --populate omarchy-mac +omarchy_mac_signing_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 +sudo pacman-key --finger "$omarchy_mac_signing_key" | + tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" diff --git a/test/shell.d/install-mac-snapper-dependency-test.sh b/test/shell.d/install-mac-snapper-dependency-test.sh index ecd2a3ea757..f88a2d01a4f 100644 --- a/test/shell.d/install-mac-snapper-dependency-test.sh +++ b/test/shell.d/install-mac-snapper-dependency-test.sh @@ -43,7 +43,7 @@ PKGBUILD expected_dependency=$dependency [[ $dependency != "absent" ]] || expected_dependency=snapper - expected=$(printf '%s\n' gum omarchy-mac-keyring "$expected_dependency" | sort) + expected=$(printf '%s\n' gum 'omarchy-mac-keyring>=20260914-2' "$expected_dependency" | sort) actual=$(sort "$case_dir/output/omarchy.pkg.tar.zst") [[ $actual == "$expected" ]] || fail "Mac build requires Snapper exactly once and preserves other dependencies ($dependency)" "$actual" diff --git a/test/shell.d/omarchy-mac-installer-trust-test.sh b/test/shell.d/omarchy-mac-installer-trust-test.sh new file mode 100644 index 00000000000..5c23d10f705 --- /dev/null +++ b/test/shell.d/omarchy-mac-installer-trust-test.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +checkout="$ROOT" +new_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 +old_key=F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 +# Extract just the production helper and its pin; never run the installer. +eval "$(sed -n '/^readonly omarchy_mac_key=/p' "$ROOT/install.sh")" +eval "$(sed -n '/^ensure_omarchy_mac_keyring() {$/,/^}$/p' "$ROOT/install.sh")" +[[ $omarchy_mac_key == "$new_key" ]] || fail 'installer pins the wrong primary' +log() { :; } +sudo() { + printf '%s\n' "$*" >>"$test_tmp/calls" + case "$*" in + "pacman-key --list-keys $new_key") [[ $has_new == 1 ]] ;; + "pacman-key --add $checkout/default/pacman/keyrings/omarchy-mac.gpg") + [[ ${reject_import:-0} == 0 ]] || return 1 + has_new=1 ;; + "pacman-key --finger $new_key") + [[ $has_new == 1 ]] || return 1 + printf '%s\n' "${returned_key:-$new_key}" ;; + "pacman-key --lsign-key $new_key") [[ $has_new == 1 ]] ;; + *) return 99 ;; + esac +} +for state in fresh old-only new-only both; do + has_new=0 + [[ $state != new-only && $state != both ]] || has_new=1 + expected_imports=$((1-has_new)) + : >"$test_tmp/calls" + ensure_omarchy_mac_keyring + [[ $(grep -c -- '--add ' "$test_tmp/calls" || true) == "$expected_imports" ]] || fail "$state import count" + grep -qxF "pacman-key --lsign-key $new_key" "$test_tmp/calls" || fail "$state missing new trust" + if grep -qF "$old_key" "$test_tmp/calls"; then fail "$state requested old key"; fi +done +has_new=0 +if (reject_import=1; ensure_omarchy_mac_keyring) >/dev/null 2>&1; then fail 'failed import accepted'; fi +has_new=1 +if (returned_key="$old_key"; ensure_omarchy_mac_keyring) >/dev/null 2>&1; then fail 'wrong fingerprint accepted'; fi +pass 'fresh installer establishes only the new pin and rejects missing or wrong trust' diff --git a/test/shell.d/omarchy-mac-keyring-migration-test.sh b/test/shell.d/omarchy-mac-keyring-migration-test.sh index f5b50472149..a57cda5bfd8 100644 --- a/test/shell.d/omarchy-mac-keyring-migration-test.sh +++ b/test/shell.d/omarchy-mac-keyring-migration-test.sh @@ -28,7 +28,7 @@ if [[ $1 == pacman-key && $2 == --populate ]]; then exit 0 fi if [[ $1 == pacman-key && $2 == --finger ]]; then - printf 'Key fingerprint = F3C5 AE3F CFFC 738C 301E 30A8 F0C5 48C0 D272 79F7\n' + printf 'Key fingerprint = FBD6 874D 423C 418D DB6D 143E ECE1 9CDD E306 DBD2\n' exit 0 fi exec "$@" diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh new file mode 100644 index 00000000000..57e540084bd --- /dev/null +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" + +# Explicit opt-in: this builds a package and initializes disposable GPG trust. +if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then + printf 'ok - native keyring package install # SKIP set OMARCHY_RUN_NATIVE_KEYRING_TEST=1\n' + exit 0 +fi +for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done +scratch_parent=${TMPDIR:-/home/naeem/.cache/codex/tmp} +case $(findmnt -n -o FSTYPE -T "$scratch_parent") in + ''|tmpfs|ramfs) fail 'native keyring test requires disk-backed scratch' ;; +esac +work=$(mktemp -d "$scratch_parent/keyring-package-install-XXXXXX") +trap 'rm -rf -- "$work"' EXIT +export TMPDIR="$work/tmp" TMP="$work/tmp" TEMP="$work/tmp" +mkdir -p "$work"/{tmp,build,root/etc/pacman.d,root/var/lib/pacman,root/var/cache/pacman/pkg,root/usr/share/pacman/keyrings,root/home,root/dev} +cp "$ROOT/build-inputs/omarchy-mac-keyring/"* "$work/build/" +cp "$ROOT/default/pacman/keyrings/"* "$work/build/" +# Build through the real recipe, including its source checksum validation. +(cd "$work/build" && makepkg --nodeps --nosign --nocheck >"$work/build.log" 2>&1) || { + cat "$work/build.log" >&2; fail 'keyring package builds'; +} +packages=("$work/build/"*.pkg.tar.*) +[[ ${#packages[@]} == 1 ]] || fail 'exactly one keyring package was built' +cp "${packages[0]}" "$work/keyring.pkg.tar.zst" +# Freeze the reviewed -1 baseline so committing the cleanup does not change it. +baseline=61e7f31a3b47608325998cad5a1e8912f559e7cb +# Only public package sources are read; no owner key material. +mkdir "$work/build-previous" +for name in PKGBUILD omarchy-mac-keyring.install; do + git -C "$ROOT" show "$baseline:build-inputs/omarchy-mac-keyring/$name" >"$work/build-previous/$name" +done +for name in omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked; do + git -C "$ROOT" show "$baseline:default/pacman/keyrings/$name" >"$work/build-previous/$name" +done +(cd "$work/build-previous" && makepkg --nodeps --nosign --nocheck >"$work/build-previous.log" 2>&1) || { + cat "$work/build-previous.log" >&2; fail 'previous committed keyring package builds'; +} +previous_packages=("$work/build-previous/"*.pkg.tar.*) +[[ ${#previous_packages[@]} == 1 ]] || fail 'exactly one previous keyring package was built' +cp "${previous_packages[0]}" "$work/previous.pkg.tar.zst" +cat >"$work/root/etc/pacman.conf" <<'CONF' +[options] +Architecture = auto +SigLevel = Never +LocalFileSigLevel = Never +CONF +cat >"$work/check.sh" <<'INNER' +set -euo pipefail +export HOME=/home TMPDIR=/tmp TMP=/tmp TEMP=/tmp +key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 +# The hook must install payload gracefully before pacman trust is initialized. +pacman -U --noconfirm /work/keyring.pkg.tar.zst >/work/uninitialized.log 2>&1 +grep -q 'Initialize pacman-key' /work/uninitialized.log +[[ ! -e /etc/pacman.d/gnupg/trustdb.gpg ]] +pacman -R --noconfirm omarchy-mac-keyring >/work/remove-uninitialized.log 2>&1 +printf 'ok - package hook leaves uninitialized pacman trust untouched and explains recovery\n' +pacman-key --init >/work/init.log 2>&1 +mkdir -m700 /work/unrelated +# Disposable independent trust must survive installation and upgrade. +gpg --homedir /work/unrelated --batch --passphrase '' --quick-generate-key 'Independent fixture' ed25519 cert 1d >/work/unrelated.log 2>&1 +other=$(gpg --homedir /work/unrelated --with-colons --list-keys 2>/dev/null | awk -F: '$1=="fpr" {print $10; exit}') +gpg --homedir /work/unrelated --export "$other" >/work/unrelated.gpg +pacman-key --add /work/unrelated.gpg >/work/add.log 2>&1 +pacman-key --lsign-key "$other" >>/work/add.log 2>&1 +trusted() { + gpg --homedir /etc/pacman.d/gnupg --batch --with-colons --list-keys "$1" 2>/dev/null | + awk -F: '$1=="pub" && ($2=="f" || $2=="u") {ok=1} END {exit !ok}' +} +trusted "$other" +pacman -U --noconfirm /work/keyring.pkg.tar.zst >/work/install.log 2>&1 +for name in omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked; do + cmp "/usr/share/pacman/keyrings/$name" "/work/build/$name" +done +trusted "$key" +trusted "$other" +printf 'ok - actual package post_install establishes new trust and preserves unrelated trust\n' +# Exercise a real prior-version upgrade, after the fresh-install check. +pacman -R --noconfirm omarchy-mac-keyring >/work/remove-fresh.log 2>&1 +pacman -U --noconfirm /work/previous.pkg.tar.zst >/work/previous.log 2>&1 +[[ $(pacman -Q omarchy-mac-keyring) == 'omarchy-mac-keyring 20260914-1' ]] +previous_key=F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 +trusted "$previous_key" +# Remove only the disposable new key; upgrade must restore it via post_upgrade. +pacman-key --delete "$key" >/work/delete.log 2>&1 +if gpg --homedir /etc/pacman.d/gnupg --list-keys "$key" >/dev/null 2>&1; then exit 1; fi +pacman -U --noconfirm /work/keyring.pkg.tar.zst >/work/upgrade.log 2>&1 +[[ $(pacman -Q omarchy-mac-keyring) == 'omarchy-mac-keyring 20260914-2' ]] +grep -q 'upgrading omarchy-mac-keyring' /work/upgrade.log +trusted "$previous_key" +trusted "$key" +trusted "$other" +printf 'ok - actual 20260914-1 to -2 package upgrade restores new trust and preserves existing old and unrelated trust\n' +INNER +# No host home, /etc, /var, device tree, sockets or network are exposed. +# All writable paths including /tmp live under verified disk-backed scratch. +bwrap --unshare-all --die-with-parent --new-session --uid 0 --gid 0 \ + --bind "$work/root" / --ro-bind /usr/bin /usr/bin --ro-bind /usr/lib /usr/lib \ + --ro-bind /usr/share/makepkg /usr/share/makepkg \ + --symlink usr/bin /bin --symlink usr/bin /sbin --symlink usr/lib /lib \ + --dev-bind /dev/null /dev/null --dev-bind /dev/random /dev/random --dev-bind /dev/urandom /dev/urandom \ + --bind "$work" /work --bind "$work/tmp" /tmp --proc /proc \ + /bin/bash /work/check.sh || { + for log in "$work"/*.log; do printf '%s\n' "${log##*/}" >&2; tail -20 "$log" >&2; done + fail 'isolated real package install/upgrade trust validation'; + } diff --git a/test/shell.d/omarchy-mac-keyring-successor-test.sh b/test/shell.d/omarchy-mac-keyring-successor-test.sh new file mode 100644 index 00000000000..301ab3e806e --- /dev/null +++ b/test/shell.d/omarchy-mac-keyring-successor-test.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +export TEST_CALLS="$test_tmp/calls" +new_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 +export TEST_NEW_KEY="$new_key" +mkdir -p "$test_tmp/source/migrations" +for name in 1789316115 1789390468 1789407944; do + cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" +done +pacman() { + [[ $* == '-Q omarchy-mac-keyring' ]] || return 99 + [[ ${TEST_VERSION:-20260914-2} != missing ]] || return 1 + printf 'omarchy-mac-keyring %s\n' "${TEST_VERSION:-20260914-2}" +} +sudo() { + printf '%s\n' "$*" >>"$TEST_CALLS" + case "$*" in + 'pacman-key --populate omarchy-mac') return "${TEST_POPULATE_FAILURE:-0}" ;; + "pacman-key --finger $TEST_NEW_KEY") + [[ ${TEST_MISSING_KEY:-0} == 0 ]] || return 1 + printf '%s\n' "$TEST_NEW_KEY" ;; + *) return 99 ;; + esac +} +omarchy-notification-dismiss() { :; } +export -f pacman sudo omarchy-notification-dismiss + +for scenario in current newer missing older populate fingerprint; do + markers="$test_tmp/$scenario" + mkdir "$markers" + touch "$markers/1789316115.sh" "$markers/1789317000.sh" "$markers/1789390468.sh" + export TEST_VERSION=20260914-2 TEST_POPULATE_FAILURE=0 TEST_MISSING_KEY=0 + case "$scenario" in + newer) TEST_VERSION=20260915-1 ;; + missing) TEST_VERSION=missing ;; + older) TEST_VERSION=20260914-1 ;; + populate) TEST_POPULATE_FAILURE=1 ;; + fingerprint) TEST_MISSING_KEY=1 ;; + esac + : >"$TEST_CALLS" + if OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$markers" bash "$ROOT/bin/omarchy-migrate" >"$test_tmp/result" 2>&1; then + [[ $scenario == current || $scenario == newer ]] || fail "$scenario accepted" + [[ -f $markers/1789407944.sh ]] || fail 'successor marker missing' + [[ $(cat "$TEST_CALLS") == "pacman-key --populate omarchy-mac"$'\n'"pacman-key --finger $new_key" ]] || fail 'unexpected trust operations' + : >"$TEST_CALLS" + OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$markers" bash "$ROOT/bin/omarchy-migrate" >/dev/null + [[ ! -s $TEST_CALLS ]] || fail 'completed successor ran twice' + else + [[ $scenario != current && $scenario != newer ]] || fail "$scenario failed" "$(cat "$test_tmp/result")" + [[ ! -f $markers/1789407944.sh ]] || fail 'failed successor marked complete' + if [[ $scenario == older || $scenario == missing ]]; then + [[ ! -s $TEST_CALLS ]] || fail 'rejected package still touched trust' + fi + fi +done +pass 'successor repairs completed migrations, requires current package and remains pending on failure' diff --git a/test/shell.d/omarchy-mac-keyring-transition-test.sh b/test/shell.d/omarchy-mac-keyring-transition-test.sh new file mode 100644 index 00000000000..6e09e4f0964 --- /dev/null +++ b/test/shell.d/omarchy-mac-keyring-transition-test.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +export TEST_TRUST_CALLS="$test_tmp/calls" OMARCHY_PATH="$ROOT" +old_key=F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 +new_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 +export TEST_OLD_KEY="$old_key" TEST_NEW_KEY="$new_key" + +# Reject every privileged operation except the explicitly modeled trust calls. +sudo() { + printf '%s\n' "$*" >>"$TEST_TRUST_CALLS" + if [[ $* == 'pacman-key --populate omarchy-mac' ]]; then + return "${TEST_POPULATE_FAILURE:-0}" + elif [[ $1 == pacman-key && $2 == --finger && $# == 3 ]]; then + [[ $3 != "${TEST_MISSING_KEY:-}" ]] || return 1 + printf '%s\n' "$3" + else + return 99 + fi +} +omarchy-pkg-missing() { return "${TEST_PACKAGE_PRESENT:-1}"; } +omarchy-notification-dismiss() { :; } +export -f sudo omarchy-pkg-missing omarchy-notification-dismiss + +mkdir -p "$test_tmp/source/migrations" "$test_tmp/markers" +for name in 1789316115 1789390468; do + cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" +done +touch "$test_tmp/markers/1789316115.sh" "$test_tmp/markers/1789317000.sh" +export OMARCHY_MIGRATION_STATE="$test_tmp/markers" +OMARCHY_PATH="$test_tmp/source" bash "$ROOT/bin/omarchy-migrate" >/dev/null +[[ -f $OMARCHY_MIGRATION_STATE/1789390468.sh ]] || fail 'successor marker missing' +[[ $(cat "$TEST_TRUST_CALLS") == "pacman-key --populate omarchy-mac"$'\n'"pacman-key --finger $new_key" ]] || + fail 'completed old migrations must stay skipped and only the new key must be checked' +: >"$TEST_TRUST_CALLS" +OMARCHY_PATH="$test_tmp/source" bash "$ROOT/bin/omarchy-migrate" >/dev/null +[[ ! -s $TEST_TRUST_CALLS ]] || fail 'completed successor ran again' +pass 'existing completed migrations stay skipped while successor establishes new trust without requiring the old key' + +# Failure leaves the new migration pending and never changes repository policy. +for failure in new populate package; do + mkdir "$test_tmp/markers-$failure" + touch "$test_tmp/markers-$failure/1789316115.sh" "$test_tmp/markers-$failure/1789317000.sh" + export TEST_MISSING_KEY='' TEST_POPULATE_FAILURE=0 TEST_PACKAGE_PRESENT=1 + case "$failure" in + new) TEST_MISSING_KEY="$new_key" ;; + populate) TEST_POPULATE_FAILURE=1 ;; + package) TEST_PACKAGE_PRESENT=0 ;; + esac + if OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/markers-$failure" bash "$ROOT/bin/omarchy-migrate" >"$test_tmp/rejected" 2>&1; then + fail "$failure trust failure must stop migration" + fi + [[ ! -e $test_tmp/markers-$failure/1789390468.sh ]] || fail 'failed successor was marked complete' +done +unset TEST_MISSING_KEY TEST_POPULATE_FAILURE TEST_PACKAGE_PRESENT +pass 'missing package, missing new key and population failure leave successor pending' + +source "$ROOT/install/helpers/arm-channel.sh" +omarchy_arm_channel_key_fingerprints() { printf '%s\n' "${fixture_keys[@]}"; } +sudo() { + printf '%s\n' "$*" >>"$TEST_TRUST_CALLS" + if [[ $1 == gpg && $4 == --batch && $5 == --list-keys ]]; then + printf '%s\n' "${fixture_keys[@]}" | grep -qxF "$6" + elif [[ $1 == pacman-key && $4 == --add ]]; then + [[ $5 == "$ROOT/default/pacman/keyrings/omarchy-mac.gpg" ]] || return 99 + [[ ${TEST_INCOMPLETE_CERT:-0} == 1 ]] || fixture_keys+=("$new_key") + return 0 + elif [[ $1 == pacman-key && $4 == --lsign-key ]]; then + [[ $5 == "$new_key" ]] + else + return 99 + fi +} +for state in fresh old-only new-only both; do + fixture_keys=() + case "$state" in + old-only) fixture_keys=("$old_key") ;; + new-only) fixture_keys=("$new_key") ;; + both) fixture_keys=("$old_key" "$new_key") ;; + esac + : >"$TEST_TRUST_CALLS" + omarchy_arm_channel_trust_fork "$test_tmp/private-keyring" + if grep -qxF "pacman-key --gpgdir $test_tmp/private-keyring --lsign-key $old_key" "$TEST_TRUST_CALLS"; then fail "$state authorized old trust"; fi + grep -qxF "pacman-key --gpgdir $test_tmp/private-keyring --lsign-key $new_key" "$TEST_TRUST_CALLS" || fail "$state did not establish new trust" + expected=1 + [[ $state != both && $state != new-only ]] || expected=0 + [[ $(grep -c -- '--add ' "$TEST_TRUST_CALLS" || true) == "$expected" ]] || fail "$state certificate import count differs" +done +fixture_keys=() +if TEST_INCOMPLETE_CERT=1 omarchy_arm_channel_trust_fork "$test_tmp/private-keyring"; then + fail 'fresh trust accepts a certificate missing the replacement primary' +fi +pass 'fresh and channel trust stages require the new primary for fresh and old-only clients' + +[[ $(cat "$ROOT/default/pacman/keyrings/omarchy-mac-trusted") == "$new_key:4:" ]] || fail 'packaged trusted primaries differ from transition pins' +( + source "$ROOT/build-inputs/omarchy-mac-keyring/PKGBUILD" + [[ $pkgver == 20260914 && $pkgrel == 2 ]] + for index in "${!source[@]}"; do + [[ $(sha512sum "$ROOT/default/pacman/keyrings/${source[$index]}" | cut -d' ' -f1) == "${sha512sums[$index]}" ]] + done +) || fail 'versioned package recipe must bind exact public trust files' +pass 'packaged public trust and checksums match the versioned transition' diff --git a/test/shell.d/package-build-contract-test.sh b/test/shell.d/package-build-contract-test.sh index a44d72c9173..baae6f14802 100755 --- a/test/shell.d/package-build-contract-test.sh +++ b/test/shell.d/package-build-contract-test.sh @@ -30,15 +30,37 @@ pass 'source version replaces stale recipe version and preserves explicit pkgrel cat >"$work_dir/omarchy-PKGBUILD" <<'RECIPE' depends=( 'omarchy-settings' + 'omarchy-mac-keyring' ) RECIPE ensure_omarchy_mac_keyring_dependency "$work_dir/omarchy-PKGBUILD" ensure_omarchy_mac_keyring_dependency "$work_dir/omarchy-PKGBUILD" - [[ $(grep -c "^[[:space:]]*'omarchy-mac-keyring'$" "$work_dir/omarchy-PKGBUILD") == 1 ]] + [[ $(grep -c "^[[:space:]]*'omarchy-mac-keyring>=20260914-2'$" "$work_dir/omarchy-PKGBUILD") == 1 ]] [[ " ${packages[*]} " == *' omarchy-mac-keyring '* ]] + source "$work_dir/omarchy-PKGBUILD" + [[ $(vercmp 20260913-1 "${depends[1]#*>=}") == -1 ]] + [[ $(vercmp 20260914-2 "${depends[1]#*>=}") == 0 ]] ) || fail 'omarchy package depends exactly once on the fork keyring' pass 'build includes the fork keyring and makes it an Omarchy dependency' +( + source "$ROOT/build-packages.sh" + for requirement in omarchy-mac-keyring 'omarchy-mac-keyring>=20260913-1' 'omarchy-mac-keyring>=20260915-1'; do + printf "depends=(\n 'before' '%s' 'after' # preserved comment\n)\n" "$requirement" >"$work_dir/shared-line" + ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" + cp "$work_dir/shared-line" "$work_dir/once" + ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" + cmp "$work_dir/once" "$work_dir/shared-line" + source "$work_dir/shared-line" + [[ ${depends[0]} == before && ${depends[2]} == after && ${#depends[@]} == 3 ]] + expected='omarchy-mac-keyring>=20260914-2' + [[ $requirement != 'omarchy-mac-keyring>=20260915-1' ]] || expected=$requirement + [[ ${depends[1]} == "$expected" ]] + grep -qF '# preserved comment' "$work_dir/shared-line" + done +) || fail 'keyring transition must preserve adjacent dependencies and stronger existing bounds' +pass 'keyring dependency normalization preserves shared lines and stronger version requirements' + # Reject dirty/wrong default sources before touching an existing output. ( source "$ROOT/build-inputs/prepare-recipes.sh" diff --git a/test/shell.d/rc4-bootstrap-boundary-test.sh b/test/shell.d/rc4-bootstrap-boundary-test.sh new file mode 100644 index 00000000000..28a89207b1b --- /dev/null +++ b/test/shell.d/rc4-bootstrap-boundary-test.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" +source "$ROOT/install/helpers/arm-channel.sh" +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +[[ $(cat "$ROOT/version") == 4.0.3rc4 ]] || fail 'bootstrap source version is not RC4' +[[ ! -e $ROOT/migrations/1789317000.sh ]] || fail 'RC5 strict migration leaked into RC4' +for policy in 'Optional TrustAll' 'PackageRequired DatabaseRequired TrustedOnly'; do + cat >"$test_tmp/config" <>"$TEST_CALLS" + case "$*" in + 'pacman-key --populate omarchy-mac') return 0 ;; + 'pacman-key --finger FBD6874D423C418DDB6D143EECE19CDDE306DBD2') + printf '%s\n' FBD6874D423C418DDB6D143EECE19CDDE306DBD2 ;; + *) return 99 ;; + esac +} +pacman() { + [[ $* == '-Q omarchy-mac-keyring' ]] || return 99 + printf '%s\n' 'omarchy-mac-keyring 20260914-2' +} +omarchy-pkg-missing() { return 1; } +omarchy-notification-dismiss() { :; } +export -f sudo pacman omarchy-pkg-missing omarchy-notification-dismiss +mkdir -p "$test_tmp/source/migrations" "$test_tmp/markers" +for name in 1789316115 1789390468 1789407944; do + cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" +done +OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/markers" bash "$ROOT/bin/omarchy-migrate" >/dev/null +for name in 1789316115 1789390468 1789407944; do + [[ -f $test_tmp/markers/$name.sh ]] || fail "$name did not complete" +done +[[ $(grep -c '^pacman-key --populate omarchy-mac$' "$TEST_CALLS") == 3 ]] || fail 'not all trust stages populated' +[[ $(wc -l <"$TEST_CALLS") == 6 ]] || fail 'unexpected privileged operation' +: >"$TEST_CALLS" +OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/markers" bash "$ROOT/bin/omarchy-migrate" >/dev/null +[[ ! -s $TEST_CALLS ]] || fail 'completed bootstrap reran' +pass 'all pending RC4 trust migrations complete without old-key or policy operations' From 555425310072aefecdc4c42bd71ed1368c87ab9e Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Tue, 15 Sep 2026 00:49:41 +0530 Subject: [PATCH 2/7] Use direct checks for the single active signing primary --- install/helpers/arm-channel.sh | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/install/helpers/arm-channel.sh b/install/helpers/arm-channel.sh index c02d6fc36e2..897788e3a5b 100644 --- a/install/helpers/arm-channel.sh +++ b/install/helpers/arm-channel.sh @@ -215,25 +215,18 @@ omarchy_arm_channel_key_fingerprints() { # retains this directory until applying or abandoning the captured transaction. # Trust the pinned new primary only inside the private transaction keyring. omarchy_arm_channel_trust_fork() { - local keyring="$1" fork_key import_needed=0 + local keyring="$1" local active_key="FBD6874D423C418DDB6D143EECE19CDDE306DBD2" local fork_keyfile="${OMARCHY_SIGNING_SOURCE:-$OMARCHY_PATH}/default/pacman/keyrings/omarchy-mac.gpg" - for fork_key in "$active_key"; do - if ! sudo gpg --homedir "$keyring" --batch --list-keys "$fork_key" >/dev/null 2>&1; then - import_needed=1 - fi - done - if (( import_needed )); then + if ! sudo gpg --homedir "$keyring" --batch --list-keys "$active_key" >/dev/null 2>&1; then [[ -f $fork_keyfile && ! -L $fork_keyfile ]] || { echo "Pinned Omarchy Mac signing key is missing or unsafe: $fork_keyfile" >&2 return 1 } sudo pacman-key --gpgdir "$keyring" --add "$fork_keyfile" || return 1 fi - for fork_key in "$active_key"; do - omarchy_arm_channel_key_fingerprints "$keyring" | grep -qxF "$fork_key" || return 1 - sudo pacman-key --gpgdir "$keyring" --lsign-key "$fork_key" || return 1 - done + omarchy_arm_channel_key_fingerprints "$keyring" | grep -qxF "$active_key" || return 1 + sudo pacman-key --gpgdir "$keyring" --lsign-key "$active_key" || return 1 } omarchy_arm_channel_prepare() { From 07753c84583aee5f112adc3d74bacdb300a7be0d Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Tue, 15 Sep 2026 10:43:48 +0530 Subject: [PATCH 3/7] Fix RC4 migration gates and make keyring upgrade tests reproducible --- .github/workflows/install-vm.yml | 3 +- docs/arm-package-sources.md | 10 +++ install/helpers/arm-channel.sh | 4 +- migrations/1789316115.sh | 25 +++++- migrations/1789390468.sh | 14 ---- migrations/1789407944.sh | 11 ++- .../omarchy-mac-keyring-20260914-1/PKGBUILD | 19 +++++ .../omarchy-mac-keyring-20260914-1/README.md | 5 ++ .../omarchy-mac-keyring-20260914-1/SHA256SUMS | 5 ++ .../omarchy-mac-keyring.install | 11 +++ .../omarchy-mac-revoked | 0 .../omarchy-mac-trusted | 2 + .../omarchy-mac.gpg | 32 +++++++ test/shell.d/install-vm-workflow-test.sh | 6 +- .../omarchy-mac-keyring-migration-test.sh | 84 +++++++++++++++++++ ...marchy-mac-keyring-package-install-test.sh | 15 ++-- .../omarchy-mac-keyring-successor-test.sh | 12 ++- .../omarchy-mac-keyring-transition-test.sh | 50 +---------- test/shell.d/rc4-bootstrap-boundary-test.sh | 9 +- test/vm/run-selective-edge | 12 +++ 20 files changed, 241 insertions(+), 88 deletions(-) delete mode 100644 migrations/1789390468.sh create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/PKGBUILD create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/README.md create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/SHA256SUMS create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-keyring.install create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-revoked create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-trusted create mode 100644 test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac.gpg diff --git a/.github/workflows/install-vm.yml b/.github/workflows/install-vm.yml index 7f9a9378ade..a6f95d698bb 100644 --- a/.github/workflows/install-vm.yml +++ b/.github/workflows/install-vm.yml @@ -30,6 +30,7 @@ jobs: OMARCHY_INSTALL_VM_WORK: /var/tmp/omarchy-install-${{ github.run_id }}-${{ github.run_attempt }} OMARCHY_INSTALL_VM_PACKAGE_SOURCES: '1' OMARCHY_INSTALL_VM_IDEMPOTENCY: '1' + OMARCHY_INSTALL_VM_KEYRING: '1' steps: - name: Checkout uses: actions/checkout@v4 @@ -81,7 +82,7 @@ jobs: run: | mkdir -p "$OMARCHY_INSTALL_VM_WORK/logs" set +e - sudo --preserve-env=HOME,OMARCHY_PKGS_PATH,OMARCHY_INSTALL_VM_WORK,OMARCHY_INSTALL_VM_CACHE,OMARCHY_INSTALL_VM_PACKAGE_SOURCES,OMARCHY_INSTALL_VM_IDEMPOTENCY \ + sudo --preserve-env=HOME,OMARCHY_PKGS_PATH,OMARCHY_INSTALL_VM_WORK,OMARCHY_INSTALL_VM_CACHE,OMARCHY_INSTALL_VM_PACKAGE_SOURCES,OMARCHY_INSTALL_VM_IDEMPOTENCY,OMARCHY_INSTALL_VM_KEYRING \ bash ./test/vm/run-selective-edge 2>&1 | tee "$RUNNER_TEMP/omarchy-harness.log" status=${PIPESTATUS[0]} set -e diff --git a/docs/arm-package-sources.md b/docs/arm-package-sources.md index 319769b4a99..231609cf787 100644 --- a/docs/arm-package-sources.md +++ b/docs/arm-package-sources.md @@ -22,6 +22,16 @@ The persistent configuration is committed only after successful package installa This freezes one switch transaction, not future distribution upgrades. Arch Linux ARM, Asahi and the explicitly selected upstream graphics stack still resolve according to their rolling policies on the next update. Record their resolved versions when qualifying an RC; a different resolved stack needs new compatibility evidence. Captured repositories use a task directory beneath `${XDG_CACHE_HOME:-$HOME/.cache}/omarchy/channels`, require disk-backed storage with sufficient free space, and are removed after the transaction. Existing package caches are reused without deleting their archives. +## RC4 trust bootstrap qualification + +The primary changed before signing activation to finalize signing-key management; this is housekeeping, not a compromise response. The package ships only the new primary and an empty `omarchy-mac-revoked` file. Population does not remove previously installed keys: a host that already trusted the old primary retains that trust, while a fresh host receives only the new fork primary. No migration deletes or revokes old or unrelated trust. + +Publish the reviewed `omarchy-mac-keyring` package at `20260914-2` or newer with the RC4 package pair before asking clients to run this source's migrations. Both the pending bootstrap migration and its successor require that installed version. A source/manual update ahead of package publication stops with an explanatory error and remains pending; it must not skip the failed migration or weaken signature policy to continue. + +Before RC4 publication, qualify the actual artifacts on a fresh base and an existing installation. Include a real prior-keyring upgrade and verify that the new primary becomes trusted, old and unrelated installed trust survives, completed migration markers stay skipped, and pending markers appear only after successful trust population. Hosted native package tests cover disposable package hooks and trust; they do not replace physical Mac installation and upgrade qualification. + +A development host that already ran the later strict-signing migration cannot install unsigned RC4 under `PackageRequired DatabaseRequired TrustedOnly`. A migration-marker fixture does not prove that transaction works. Use a clean disposable baseline with the supported RC4 policy for the unsigned canary; do not automatically reset a host's stricter policy or delete its migration markers. Qualify strict signing separately with the signed candidate. + ## Fresh Apple Silicon installation `./install.sh --channel rc` (or `OMARCHY_MIRROR=rc ./install.sh`) installs the published lane's captured `omarchy`/`omarchy-settings` pair. It verifies availability, resolves dependencies and downloads under the configured signature policy before changing locale, packages or active repository configuration. If the base has no managed ARM section, preflight adds one only to its candidate; custom or hidden managed sections must be configured explicitly. The rc4 bootstrap retains the existing `Optional TrustAll` fork policy because its archive and database are intentionally unsigned. This is the final use of that trust model. The following signed RC requires trusted package and database signatures. Required upstream graphics signatures remain required throughout. diff --git a/install/helpers/arm-channel.sh b/install/helpers/arm-channel.sh index 897788e3a5b..c6c5c085c3d 100644 --- a/install/helpers/arm-channel.sh +++ b/install/helpers/arm-channel.sh @@ -211,8 +211,6 @@ omarchy_arm_channel_key_fingerprints() { awk -F: '$1 == "fpr" { print $10 }' | sort } -# Preflight has no installed-package/config/keyring side effects. The caller -# retains this directory until applying or abandoning the captured transaction. # Trust the pinned new primary only inside the private transaction keyring. omarchy_arm_channel_trust_fork() { local keyring="$1" @@ -229,6 +227,8 @@ omarchy_arm_channel_trust_fork() { sudo pacman-key --gpgdir "$keyring" --lsign-key "$active_key" || return 1 } +# Preflight has no installed-package/config/keyring side effects. The caller +# retains this directory until applying or abandoning the captured transaction. omarchy_arm_channel_prepare() { local stage="$1" channel="$2" allow_new="${3:-}" local config="${OMARCHY_PACMAN_CONFIG:-/etc/pacman.conf}" diff --git a/migrations/1789316115.sh b/migrations/1789316115.sh index 2a3ff447171..22a80f4b665 100644 --- a/migrations/1789316115.sh +++ b/migrations/1789316115.sh @@ -8,8 +8,29 @@ if omarchy-pkg-missing omarchy-mac-keyring; then omarchy-pkg-add omarchy-mac-keyring fi -sudo pacman-key --populate omarchy-mac -sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" +# The helper may skip an unavailable package, and an installed older package +# does not satisfy the bootstrap. Check before explicit trust population or +# completing this marker so a checkout ahead of publication fails clearly. +# If the helper installs a package, that package's own scriptlets still run. +installed_keyring=$(pacman -Q omarchy-mac-keyring) || { + echo "Omarchy Mac keyring 20260914-2 or newer is required; install the reviewed RC4 keyring package before retrying this migration." >&2 + exit 1 +} +installed_version=${installed_keyring#* } +version_comparison=$(vercmp "$installed_version" 20260914-2) || exit 1 +if (( version_comparison < 0 )); then + echo "Omarchy Mac keyring 20260914-2 or newer is required (installed: $installed_version); complete the reviewed RC4 package upgrade before retrying this migration." >&2 + exit 1 +fi + +sudo pacman-key --populate omarchy-mac || { + echo "Could not populate Omarchy Mac signing trust; this migration remains pending." >&2 + exit 1 +} +if ! sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key"; then + echo "The required Omarchy Mac signing primary $omarchy_mac_signing_key is missing after keyring population." >&2 + exit 1 +fi # Policy remains unchanged for this one disclosed bootstrap transaction. The # next, signed RC carries a successor migration that requires both package and diff --git a/migrations/1789390468.sh b/migrations/1789390468.sh deleted file mode 100644 index c3cb39a43b1..00000000000 --- a/migrations/1789390468.sh +++ /dev/null @@ -1,14 +0,0 @@ -echo "Populate Omarchy Mac signing trust with the new key" - -# The updated keyring must arrive through an already trusted transaction before -# activating the replacement signer. This migration does not change repo policy. -if omarchy-pkg-missing omarchy-mac-keyring; then - echo "Install the reviewed Omarchy Mac keyring transition before activating the replacement signer." >&2 - return 1 -fi - -sudo pacman-key --populate omarchy-mac -for omarchy_mac_signing_key in \ - FBD6874D423C418DDB6D143EECE19CDDE306DBD2; do - sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" -done diff --git a/migrations/1789407944.sh b/migrations/1789407944.sh index 2297a382c65..b1ac8801a83 100644 --- a/migrations/1789407944.sh +++ b/migrations/1789407944.sh @@ -13,7 +13,12 @@ if (( comparison < 0 )); then exit 1 fi -sudo pacman-key --populate omarchy-mac +sudo pacman-key --populate omarchy-mac || { + echo "Could not populate Omarchy Mac signing trust; this migration remains pending." >&2 + exit 1 +} omarchy_mac_signing_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 -sudo pacman-key --finger "$omarchy_mac_signing_key" | - tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" +if ! sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key"; then + echo "The required Omarchy Mac signing primary $omarchy_mac_signing_key is missing after keyring population." >&2 + exit 1 +fi diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/PKGBUILD b/test/fixtures/omarchy-mac-keyring-20260914-1/PKGBUILD new file mode 100644 index 00000000000..b6085f0a989 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/PKGBUILD @@ -0,0 +1,19 @@ +# Maintainer: Omarchy Mac + +pkgname=omarchy-mac-keyring +pkgver=20260914 +pkgrel=1 +pkgdesc='Omarchy Mac package signing keyring' +arch=(any) +url='https://github.com/omarchy-mac/omarchy-mac' +license=('GPL-3.0-or-later') +install=$pkgname.install +source=('omarchy-mac.gpg' 'omarchy-mac-trusted' 'omarchy-mac-revoked') +sha512sums=('1372202bdde5a3f8a79caceed4cff537012f7c1000a63e3cde808842fa913248b5d139641e9dcde378713b18658df8ace30b315a7193415402f614c70ad267a2' + '9414cc6dfca81c1eb098e23908c4e9bfb4bf5ab75ddb75e8b538c204e64ab2e3869f6e91f59886625d6fd0c53c1837db9184b12e4e4450c25175f221ffeca9f9' + 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e') + +package() { + install -D -m0644 -t "$pkgdir/usr/share/pacman/keyrings/" \ + omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked +} diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/README.md b/test/fixtures/omarchy-mac-keyring-20260914-1/README.md new file mode 100644 index 00000000000..7611f7ed54e --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/README.md @@ -0,0 +1,5 @@ +# Previous public keyring package fixture + +These five package sources are copied byte-for-byte from local reviewed commit `61e7f31a3b47608325998cad5a1e8912f559e7cb`: `build-inputs/omarchy-mac-keyring/{PKGBUILD,omarchy-mac-keyring.install}` and `default/pacman/keyrings/{omarchy-mac.gpg,omarchy-mac-trusted,omarchy-mac-revoked}`. The package version is `20260914-1`. This provenance identifier is informational; running the test requires no Git history or remote access. + +The fixture contains only public certificates, public trust metadata, and package source. It models the prior two-primary package for upgrade testing; it is not shipped as the current keyring, does not revoke keys, and does not assert this package was publicly released. `SHA256SUMS` binds the five original files. The native test verifies this manifest before building and checks that upgrading preserves preexisting client trust. diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/SHA256SUMS b/test/fixtures/omarchy-mac-keyring-20260914-1/SHA256SUMS new file mode 100644 index 00000000000..ee818776786 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/SHA256SUMS @@ -0,0 +1,5 @@ +edd499ac6fd3b6a0341df630e57a9529e3ce20afb7d3cebc83bd55ee00e5789b PKGBUILD +604333c2027bf219ad759d7c1e8f8238a6fc3405f8fdb7ab8757c51c27b1cf60 omarchy-mac-keyring.install +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 omarchy-mac-revoked +857ab8f968749b38b54e6e821e3c8e8de2b9ac5aae148bf7bb554f5df6664eac omarchy-mac-trusted +0ba3d0465b7583095723dd30c5933a9c6b6f61b0f0daf84ee4315e6724c3de31 omarchy-mac.gpg diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-keyring.install b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-keyring.install new file mode 100644 index 00000000000..40a474972ff --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-keyring.install @@ -0,0 +1,11 @@ +post_upgrade() { + if [ -x usr/bin/pacman-key ] && usr/bin/pacman-key -l >/dev/null 2>&1; then + usr/bin/pacman-key --populate omarchy-mac + else + echo ' >>> Initialize pacman-key, then run pacman-key --populate omarchy-mac.' + fi +} + +post_install() { + post_upgrade +} diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-revoked b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-revoked new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-trusted b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-trusted new file mode 100644 index 00000000000..87441e0dd8e --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac-trusted @@ -0,0 +1,2 @@ +F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7:4: +FBD6874D423C418DDB6D143EECE19CDDE306DBD2:4: diff --git a/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac.gpg b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac.gpg new file mode 100644 index 00000000000..68e0bb89c7d --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260914-1/omarchy-mac.gpg @@ -0,0 +1,32 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaqbKtRYJKwYBBAHaRw8BAQdAI7gUCEOd14QplbZ12KYIV7XkM+/wyPPG9QBz +mPvV5bW0G09tYXJjaHkgTWFjIFBhY2thZ2UgU2lnbmluZ4iWBBMWCgA+FiEE88Wu +P8/8c4wwHjCo8MVIwNJyefcFAmqmyrUCGwEFCQlmAYAFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQ8MVIwNJyefc4HgEArUgsUks8G6WRTBV5Ef52/djRyIGu7Uai +e5Ih8U3XB9gA/RjtlKVqC3E00ZhN5m4Oc2IbfPNZr8Oh7ZlYdvFq148BuDMEaqbK +3xYJKwYBBAHaRw8BAQdAWA4H8/FHdnZP1Z9NeFkFNmMNIJyDT66oSHcGCxBjLIOI +9AQYFgoAJhYhBPPFrj/P/HOMMB4wqPDFSMDScnn3BQJqpsrfAhsCBQkB4TOAAIAJ +EPDFSMDScnn3dSAEGRYKAB0WIQRsJZfG5p/EiY09Vg4zEwdpYDAoXgUCaqbK3wAK +CRAzEwdpYDAoXvBSAPMGvUcm9mi64yMqX3nkMsi1H7HuvdXmBaRgyllBrqYRAQDA +xFiDGYOtJqPZwpcIupeEn0Z62ScAwjBS5ONqXNfdAjvBAP41jIblDA42gFKa/G8b +xL9fy7HGsg/zpEFBnGsA/EB03wEA02k2hYO0FuV7qo3pmqVqR0MIVygZLEA2sGWB +Jyvh8wU= +=iEoD +-----END PGP PUBLIC KEY BLOCK----- +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaqfthRYJKwYBBAHaRw8BAQdAjGj2EinezISkcb9sk6UuBP0YZ4Vlt74y5GHw +cQZx8Ia0G09tYXJjaHkgTWFjIFBhY2thZ2UgU2lnbmluZ4iWBBMWCgA+FiEE+9aH +TUI8QY3bbRQ+7OGc3eMG29IFAmqn7YUCGwEFCQWjmoAFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQ7OGc3eMG29JErQD/ddeOpL7W6Izh3DYgugD/wsI4nYegFaGs +fuXloY5/kKUBAMu5AxRdzYAvhxes12PUgOp6PeJQh5osqq+zx50XN4YDuDMEaqft +hRYJKwYBBAHaRw8BAQdAIZKUzkbLfQifuQAk92YFPtbb+lwFeIQEDICFvvWBkXGI +9QQYFgoAJhYhBPvWh01CPEGN220UPuzhnN3jBtvSBQJqp+2FAhsCBQkB4TOAAIEJ +EOzhnN3jBtvSdiAEGRYKAB0WIQTXke0MckOdn4dXQhJYBDsncKJXYgUCaqfthQAK +CRBYBDsncKJXYoJ4AQCpBxN9ssf+IppBDWJ1DX15UdsBrKbheuXBUdYhzxlvhQD/ +bKa/fXRxGYmh8pTxSCDOH6fy7+Mx/yisU12tHrGAfgLxQAD+MAsOKMW/HpA2T2hu +pFNajI8hd9EtynNy82mJ4pu9uvwA/jjQ8kApBbZaV6STdS2aX3h2J04rmsh0cZKo +7AxGkigO +=hN5B +-----END PGP PUBLIC KEY BLOCK----- diff --git a/test/shell.d/install-vm-workflow-test.sh b/test/shell.d/install-vm-workflow-test.sh index e7829028d1d..22f3bf91c3c 100644 --- a/test/shell.d/install-vm-workflow-test.sh +++ b/test/shell.d/install-vm-workflow-test.sh @@ -33,11 +33,11 @@ assert not any('actions/cache@' in step.get('uses', '') for step in job['steps'] assert not (root / '.github/workflows/install-vm-selective-edge.yml').exists() install_step = next(step for step in job['steps'] if 'bash ./test/vm/run-selective-edge' in step.get('run', '')) install_env = {**job['env'], **install_step.get('env', {})} -for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY'): +for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY', 'OMARCHY_INSTALL_VM_KEYRING'): assert install_env[key] == '1', f'{key} must be enabled' for key in ('OMARCHY_INSTALL_VM_WORK', 'OMARCHY_INSTALL_VM_CACHE'): assert 'github.run_id' in install_env[key] and 'github.run_attempt' in install_env[key] -print('ok - every PR gets isolated ARM install coverage with both validation modes') +print('ok - every PR gets isolated ARM install coverage with package-source, repeat-install and native keyring validation') run = install_step['run'] preserved = re.search(r'--preserve-env=([^\s]+)', run).group(1).split(',') @@ -51,7 +51,7 @@ with tempfile.TemporaryDirectory() as temp: (cwd / 'bin/sudo').write_text('#!/bin/bash\nshift\nexec "$@"\n') (cwd / 'bin/sudo').chmod(0o755) (cwd / 'test/vm/run-selective-edge').write_text('''#!/bin/bash -[[ $OMARCHY_INSTALL_VM_PACKAGE_SOURCES == 1 && $OMARCHY_INSTALL_VM_IDEMPOTENCY == 1 ]] || exit 99 +[[ $OMARCHY_INSTALL_VM_PACKAGE_SOURCES == 1 && $OMARCHY_INSTALL_VM_IDEMPOTENCY == 1 && $OMARCHY_INSTALL_VM_KEYRING == 1 ]] || exit 99 rm -rf "$OMARCHY_INSTALL_VM_WORK/logs" mkdir -p "$OMARCHY_INSTALL_VM_WORK/logs" echo 'harness output before exit' diff --git a/test/shell.d/omarchy-mac-keyring-migration-test.sh b/test/shell.d/omarchy-mac-keyring-migration-test.sh index a57cda5bfd8..32fbf110e81 100644 --- a/test/shell.d/omarchy-mac-keyring-migration-test.sh +++ b/test/shell.d/omarchy-mac-keyring-migration-test.sh @@ -35,6 +35,10 @@ exec "$@" SH chmod +x "$test_tmp/bin/sudo" +pacman() { + [[ $* == '-Q omarchy-mac-keyring' ]] || return 99 + printf 'omarchy-mac-keyring 20260914-2\n' +} omarchy-pkg-missing() { return 1; } omarchy-pkg-add() { echo "add $*" >>"$calls"; } export TEST_CALLS="$calls" @@ -67,3 +71,83 @@ if omarchy_arm_signature_policy_apply "$test_tmp/pacman-link.conf" 'PackageRequi fail 'policy application follows a pacman.conf symlink' fi pass 'policy rendering handles formatting and missing overrides while application rejects symlinks' + +# Execute the pending queue as production does, using the real package helpers. +# A source checkout can reach this migration before the required package is +# available. Stub pacman and sudo only; no host package or trust operation runs. +unset -f omarchy-pkg-missing omarchy-pkg-add +export PATH="$ROOT/bin:$PATH" +export TEST_INSTALLED_VERSION="$test_tmp/installed-version" +pacman() { + case "$*" in + '-Q omarchy-mac-keyring') + [[ -s $TEST_INSTALLED_VERSION ]] || return 1 + printf 'omarchy-mac-keyring %s\n' "$(cat "$TEST_INSTALLED_VERSION")" ;; + '-Si omarchy-mac-keyring') [[ $TEST_AVAILABLE == 1 ]] ;; + '-S --noconfirm --needed omarchy-mac-keyring') + printf '%s\n' "$*" >>"$TEST_CALLS" + [[ $TEST_AVAILABLE == 1 ]] || return 1 + printf '%s\n' "$TEST_REPO_VERSION" >"$TEST_INSTALLED_VERSION" ;; + *) return 99 ;; + esac +} +sudo() { + case "$*" in + 'pacman -S --noconfirm --needed omarchy-mac-keyring') pacman "${@:2}" ;; + 'pacman-key --populate omarchy-mac') + printf '%s\n' "$*" >>"$TEST_CALLS" + return "$TEST_POPULATE_FAILURE" ;; + 'pacman-key --finger FBD6874D423C418DDB6D143EECE19CDDE306DBD2') + printf '%s\n' "$*" >>"$TEST_CALLS" + if [[ $TEST_MISSING_KEY == 1 ]]; then + # Even a successful command returning the wrong key must fail the gate. + printf '%s\n' F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 + else + printf '%s\n' FBD6874D423C418DDB6D143EECE19CDDE306DBD2 + fi ;; + *) return 99 ;; + esac +} +omarchy-notification-dismiss() { :; } +export -f pacman sudo omarchy-notification-dismiss +mkdir -p "$test_tmp/source/migrations" +for name in 1789316115 1789407944; do + cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" +done +for scenario in current newer old-only combined-old missing install-current install-old populate fingerprint; do + markers="$test_tmp/markers-$scenario" + mkdir "$markers" + printf '20260914-2\n' >"$TEST_INSTALLED_VERSION" + export TEST_AVAILABLE=0 TEST_REPO_VERSION=20260914-2 TEST_POPULATE_FAILURE=0 TEST_MISSING_KEY=0 + case "$scenario" in + newer) printf '20260915-1\n' >"$TEST_INSTALLED_VERSION" ;; + old-only) printf '20260913-1\n' >"$TEST_INSTALLED_VERSION" ;; + combined-old) printf '20260914-1\n' >"$TEST_INSTALLED_VERSION" ;; + missing) : >"$TEST_INSTALLED_VERSION" ;; + install-current) : >"$TEST_INSTALLED_VERSION"; TEST_AVAILABLE=1 ;; + install-old) : >"$TEST_INSTALLED_VERSION"; TEST_AVAILABLE=1; TEST_REPO_VERSION=20260913-1 ;; + populate) TEST_POPULATE_FAILURE=1 ;; + fingerprint) TEST_MISSING_KEY=1 ;; + esac + : >"$TEST_CALLS" + if OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$markers" bash "$ROOT/bin/omarchy-migrate" >"$test_tmp/queue-result" 2>&1; then + case "$scenario" in current|newer|install-current) ;; *) fail "$scenario accepted" ;; esac + [[ -f $markers/1789316115.sh && -f $markers/1789407944.sh ]] || fail 'bootstrap queue did not complete' + : >"$TEST_CALLS" + OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$markers" bash "$ROOT/bin/omarchy-migrate" >/dev/null + [[ ! -s $TEST_CALLS ]] || fail 'completed bootstrap queue reran' + else + case "$scenario" in current|newer|install-current) fail "$scenario failed" "$(cat "$test_tmp/queue-result")" ;; esac + [[ ! -e $markers/1789316115.sh && ! -e $markers/1789407944.sh ]] || fail 'failed bootstrap advanced the queue' + case "$scenario" in + populate) expected_error='Could not populate Omarchy Mac signing trust' ;; + fingerprint) expected_error='is missing after keyring population' ;; + *) + expected_error='20260914-2 or newer is required' + if grep -qF 'pacman-key' "$TEST_CALLS"; then fail 'rejected package touched trust'; fi ;; + esac + grep -qF "$expected_error" "$test_tmp/queue-result" || fail "$scenario has no useful diagnostic" + if grep -qF 'can only `return' "$test_tmp/queue-result"; then fail 'top-level return error'; fi + fi +done +pass 'pending bootstrap checks real helper results and package version before trust, with explicit errors and no later markers on failure' diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh index 57e540084bd..7489f59b24f 100644 --- a/test/shell.d/omarchy-mac-keyring-package-install-test.sh +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -8,7 +8,7 @@ if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then exit 0 fi for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done -scratch_parent=${TMPDIR:-/home/naeem/.cache/codex/tmp} +scratch_parent=${OMARCHY_TEST_TMPDIR:-${TMPDIR:-/var/tmp}} case $(findmnt -n -o FSTYPE -T "$scratch_parent") in ''|tmpfs|ramfs) fail 'native keyring test requires disk-backed scratch' ;; esac @@ -25,15 +25,12 @@ cp "$ROOT/default/pacman/keyrings/"* "$work/build/" packages=("$work/build/"*.pkg.tar.*) [[ ${#packages[@]} == 1 ]] || fail 'exactly one keyring package was built' cp "${packages[0]}" "$work/keyring.pkg.tar.zst" -# Freeze the reviewed -1 baseline so committing the cleanup does not change it. -baseline=61e7f31a3b47608325998cad5a1e8912f559e7cb -# Only public package sources are read; no owner key material. +# Committed public-only fixture works in shallow checkouts and source archives. +baseline="$ROOT/test/fixtures/omarchy-mac-keyring-20260914-1" +(cd "$baseline" && sha256sum --check SHA256SUMS) || fail 'previous public fixture checksums' mkdir "$work/build-previous" -for name in PKGBUILD omarchy-mac-keyring.install; do - git -C "$ROOT" show "$baseline:build-inputs/omarchy-mac-keyring/$name" >"$work/build-previous/$name" -done -for name in omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked; do - git -C "$ROOT" show "$baseline:default/pacman/keyrings/$name" >"$work/build-previous/$name" +for name in PKGBUILD omarchy-mac-keyring.install omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked; do + cp "$baseline/$name" "$work/build-previous/$name" done (cd "$work/build-previous" && makepkg --nodeps --nosign --nocheck >"$work/build-previous.log" 2>&1) || { cat "$work/build-previous.log" >&2; fail 'previous committed keyring package builds'; diff --git a/test/shell.d/omarchy-mac-keyring-successor-test.sh b/test/shell.d/omarchy-mac-keyring-successor-test.sh index 301ab3e806e..c5b808c4fa9 100644 --- a/test/shell.d/omarchy-mac-keyring-successor-test.sh +++ b/test/shell.d/omarchy-mac-keyring-successor-test.sh @@ -8,7 +8,7 @@ export TEST_CALLS="$test_tmp/calls" new_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 export TEST_NEW_KEY="$new_key" mkdir -p "$test_tmp/source/migrations" -for name in 1789316115 1789390468 1789407944; do +for name in 1789316115 1789407944; do cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" done pacman() { @@ -32,6 +32,8 @@ export -f pacman sudo omarchy-notification-dismiss for scenario in current newer missing older populate fingerprint; do markers="$test_tmp/$scenario" mkdir "$markers" + # Obsolete development markers must not prevent the retained successor. + # This models marker handling only, not installing unsigned RC4 under strict policy. touch "$markers/1789316115.sh" "$markers/1789317000.sh" "$markers/1789390468.sh" export TEST_VERSION=20260914-2 TEST_POPULATE_FAILURE=0 TEST_MISSING_KEY=0 case "$scenario" in @@ -52,6 +54,14 @@ for scenario in current newer missing older populate fingerprint; do else [[ $scenario != current && $scenario != newer ]] || fail "$scenario failed" "$(cat "$test_tmp/result")" [[ ! -f $markers/1789407944.sh ]] || fail 'failed successor marked complete' + case "$scenario" in + older) expected_error='20260914-2 or newer is required' ;; + missing) expected_error='Install the reviewed Omarchy Mac keyring package first' ;; + populate) expected_error='Could not populate Omarchy Mac signing trust' ;; + fingerprint) expected_error='is missing after keyring population' ;; + esac + grep -qF "$expected_error" "$test_tmp/result" || fail "$scenario has no useful diagnostic" + if grep -qF 'can only `return' "$test_tmp/result"; then fail 'top-level return error'; fi if [[ $scenario == older || $scenario == missing ]]; then [[ ! -s $TEST_CALLS ]] || fail 'rejected package still touched trust' fi diff --git a/test/shell.d/omarchy-mac-keyring-transition-test.sh b/test/shell.d/omarchy-mac-keyring-transition-test.sh index 6e09e4f0964..f73b221f115 100644 --- a/test/shell.d/omarchy-mac-keyring-transition-test.sh +++ b/test/shell.d/omarchy-mac-keyring-transition-test.sh @@ -9,55 +9,7 @@ old_key=F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 new_key=FBD6874D423C418DDB6D143EECE19CDDE306DBD2 export TEST_OLD_KEY="$old_key" TEST_NEW_KEY="$new_key" -# Reject every privileged operation except the explicitly modeled trust calls. -sudo() { - printf '%s\n' "$*" >>"$TEST_TRUST_CALLS" - if [[ $* == 'pacman-key --populate omarchy-mac' ]]; then - return "${TEST_POPULATE_FAILURE:-0}" - elif [[ $1 == pacman-key && $2 == --finger && $# == 3 ]]; then - [[ $3 != "${TEST_MISSING_KEY:-}" ]] || return 1 - printf '%s\n' "$3" - else - return 99 - fi -} -omarchy-pkg-missing() { return "${TEST_PACKAGE_PRESENT:-1}"; } -omarchy-notification-dismiss() { :; } -export -f sudo omarchy-pkg-missing omarchy-notification-dismiss - -mkdir -p "$test_tmp/source/migrations" "$test_tmp/markers" -for name in 1789316115 1789390468; do - cp "$ROOT/migrations/$name.sh" "$test_tmp/source/migrations/" -done -touch "$test_tmp/markers/1789316115.sh" "$test_tmp/markers/1789317000.sh" -export OMARCHY_MIGRATION_STATE="$test_tmp/markers" -OMARCHY_PATH="$test_tmp/source" bash "$ROOT/bin/omarchy-migrate" >/dev/null -[[ -f $OMARCHY_MIGRATION_STATE/1789390468.sh ]] || fail 'successor marker missing' -[[ $(cat "$TEST_TRUST_CALLS") == "pacman-key --populate omarchy-mac"$'\n'"pacman-key --finger $new_key" ]] || - fail 'completed old migrations must stay skipped and only the new key must be checked' -: >"$TEST_TRUST_CALLS" -OMARCHY_PATH="$test_tmp/source" bash "$ROOT/bin/omarchy-migrate" >/dev/null -[[ ! -s $TEST_TRUST_CALLS ]] || fail 'completed successor ran again' -pass 'existing completed migrations stay skipped while successor establishes new trust without requiring the old key' - -# Failure leaves the new migration pending and never changes repository policy. -for failure in new populate package; do - mkdir "$test_tmp/markers-$failure" - touch "$test_tmp/markers-$failure/1789316115.sh" "$test_tmp/markers-$failure/1789317000.sh" - export TEST_MISSING_KEY='' TEST_POPULATE_FAILURE=0 TEST_PACKAGE_PRESENT=1 - case "$failure" in - new) TEST_MISSING_KEY="$new_key" ;; - populate) TEST_POPULATE_FAILURE=1 ;; - package) TEST_PACKAGE_PRESENT=0 ;; - esac - if OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/markers-$failure" bash "$ROOT/bin/omarchy-migrate" >"$test_tmp/rejected" 2>&1; then - fail "$failure trust failure must stop migration" - fi - [[ ! -e $test_tmp/markers-$failure/1789390468.sh ]] || fail 'failed successor was marked complete' -done -unset TEST_MISSING_KEY TEST_POPULATE_FAILURE TEST_PACKAGE_PRESENT -pass 'missing package, missing new key and population failure leave successor pending' - +# Migration queue coverage lives in the migration and successor suites. source "$ROOT/install/helpers/arm-channel.sh" omarchy_arm_channel_key_fingerprints() { printf '%s\n' "${fixture_keys[@]}"; } sudo() { diff --git a/test/shell.d/rc4-bootstrap-boundary-test.sh b/test/shell.d/rc4-bootstrap-boundary-test.sh index 28a89207b1b..47eceeb5161 100644 --- a/test/shell.d/rc4-bootstrap-boundary-test.sh +++ b/test/shell.d/rc4-bootstrap-boundary-test.sh @@ -7,6 +7,7 @@ trap 'rm -rf "$test_tmp"' EXIT [[ $(cat "$ROOT/version") == 4.0.3rc4 ]] || fail 'bootstrap source version is not RC4' [[ ! -e $ROOT/migrations/1789317000.sh ]] || fail 'RC5 strict migration leaked into RC4' +[[ ! -e $ROOT/migrations/1789390468.sh ]] || fail 'redundant transition migration remains in RC4' for policy in 'Optional TrustAll' 'PackageRequired DatabaseRequired TrustedOnly'; do cat >"$test_tmp/config" </dev/null -for name in 1789316115 1789390468 1789407944; do +for name in 1789316115 1789407944; do [[ -f $test_tmp/markers/$name.sh ]] || fail "$name did not complete" done -[[ $(grep -c '^pacman-key --populate omarchy-mac$' "$TEST_CALLS") == 3 ]] || fail 'not all trust stages populated' -[[ $(wc -l <"$TEST_CALLS") == 6 ]] || fail 'unexpected privileged operation' +[[ $(grep -c '^pacman-key --populate omarchy-mac$' "$TEST_CALLS") == 2 ]] || fail 'not all trust stages populated' +[[ $(wc -l <"$TEST_CALLS") == 4 ]] || fail 'unexpected privileged operation' : >"$TEST_CALLS" OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/markers" bash "$ROOT/bin/omarchy-migrate" >/dev/null [[ ! -s $TEST_CALLS ]] || fail 'completed bootstrap reran' diff --git a/test/vm/run-selective-edge b/test/vm/run-selective-edge index 800edcdbaff..bcdf23d0c2b 100644 --- a/test/vm/run-selective-edge +++ b/test/vm/run-selective-edge @@ -18,6 +18,7 @@ GUEST_USER=ci KEEP_ROOT=${OMARCHY_INSTALL_VM_KEEP:-0} CHECK_IDEMPOTENCY=${OMARCHY_INSTALL_VM_IDEMPOTENCY:-0} CHECK_PACKAGE_SOURCES=${OMARCHY_INSTALL_VM_PACKAGE_SOURCES:-0} +CHECK_KEYRING=${OMARCHY_INSTALL_VM_KEYRING:-0} PREPARED_CACHE_VERSION=3 ROOTFS_SIGNING_FINGERPRINT=68B3537F39A313B3E574D06777193F152BDBE6A6 ROOTFS_KEYSERVER=hkps://keyserver.ubuntu.com @@ -217,6 +218,17 @@ fi stage_source +if [[ $CHECK_KEYRING == "1" ]]; then + log "validating actual keyring package install and upgrade in isolated ARM trust" + # Tool installation changes only the disposable Arch guest, including when + # its cached base predates this test. Package tests run without networking. + nspawn pacman -S --needed --noconfirm bubblewrap + nspawn --private-network --user="$GUEST_USER" --chdir=/src/omarchy \ + env OMARCHY_RUN_NATIVE_KEYRING_TEST=1 TMPDIR=/var/tmp TMP=/var/tmp TEMP=/var/tmp \ + bash test/shell.d/omarchy-mac-keyring-package-install-test.sh \ + 2>&1 | tee "$WORK/logs/keyring-package.log" +fi + if [[ $CHECK_PACKAGE_SOURCES == "1" ]]; then log "validating restricted package sources and current-stack repository remediation" nspawn /bin/bash -euo pipefail <<'PACKAGE_SOURCES' 2>&1 | tee "$WORK/logs/package-sources.log" From 745ebd536c7571843eab5941a1cd41e3f2a6187a Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Wed, 16 Sep 2026 00:00:11 +0530 Subject: [PATCH 4/7] Harden RC4 keyring validation --- test/shell.d/install-vm-workflow-test.sh | 8 +++ ...marchy-mac-keyring-package-install-test.sh | 27 +++++++- test/shell.d/package-build-contract-test.sh | 61 +++++++++++++------ test/vm/run-selective-edge | 3 +- 4 files changed, 78 insertions(+), 21 deletions(-) diff --git a/test/shell.d/install-vm-workflow-test.sh b/test/shell.d/install-vm-workflow-test.sh index 22f3bf91c3c..03b877ff50b 100644 --- a/test/shell.d/install-vm-workflow-test.sh +++ b/test/shell.d/install-vm-workflow-test.sh @@ -31,6 +31,14 @@ checkout = next(step for step in job['steps'] if step.get('uses', '').startswith assert checkout['with'] == {'persist-credentials': 'false'}, 'use the default merge ref without stored credentials' assert not any('actions/cache@' in step.get('uses', '') for step in job['steps']) assert not (root / '.github/workflows/install-vm-selective-edge.yml').exists() +harness = (root / 'test/vm/run-selective-edge').read_text() +private_keyring_call = re.search( + r'nspawn --private-network[^\n]*\\\n' + r'\s+env OMARCHY_RUN_NATIVE_KEYRING_TEST=1 OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1', + harness, +) +assert private_keyring_call, 'native keyring test may reuse only nspawn private networking' +assert harness.count('OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1') == 1 install_step = next(step for step in job['steps'] if 'bash ./test/vm/run-selective-edge' in step.get('run', '')) install_env = {**job['env'], **install_step.get('env', {})} for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY', 'OMARCHY_INSTALL_VM_KEYRING'): diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh index 7489f59b24f..3d701d83911 100644 --- a/test/shell.d/omarchy-mac-keyring-package-install-test.sh +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -8,6 +8,27 @@ if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then exit 0 fi for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done +bwrap_namespace_args=(--unshare-all) +case ${OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK:-0} in + 0) ;; + 1) + [[ -r /proc/net/dev ]] || fail 'private caller network state is unavailable' + private_loopback=0 + while IFS= read -r network_device; do + [[ $network_device == *:* ]] || continue + interface=${network_device%%:*} + interface=${interface//[[:space:]]/} + if [[ $interface == lo ]]; then + private_loopback=1 + else + fail "private caller network exposes non-loopback interface: $interface" + fi + done =20260913-1' 'omarchy-mac-keyring>=20260915-1'; do - printf "depends=(\n 'before' '%s' 'after' # preserved comment\n)\n" "$requirement" >"$work_dir/shared-line" - ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" - cp "$work_dir/shared-line" "$work_dir/once" - ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" - cmp "$work_dir/once" "$work_dir/shared-line" - source "$work_dir/shared-line" - [[ ${depends[0]} == before && ${depends[2]} == after && ${#depends[@]} == 3 ]] + source "$ROOT/build-packages.sh" || exit 1 + for requirement in \ + omarchy-mac-keyring \ + 'omarchy-mac-keyring>=20260913-1' \ + 'omarchy-mac-keyring>=20260915-1' \ + 'omarchy-mac-keyring>20260913-1' \ + 'omarchy-mac-keyring>20260915-1' \ + 'omarchy-mac-keyring=20260914-2' \ + 'omarchy-mac-keyring=20260915-1'; do + printf "depends=(\n 'before' '%s' 'after' # preserved comment\n)\n" "$requirement" >"$work_dir/shared-line" || exit 1 + ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" || exit 1 + cp "$work_dir/shared-line" "$work_dir/once" || exit 1 + ensure_omarchy_mac_keyring_dependency "$work_dir/shared-line" || exit 1 + cmp "$work_dir/once" "$work_dir/shared-line" || exit 1 + source "$work_dir/shared-line" || exit 1 + [[ ${depends[0]} == before && ${depends[2]} == after && ${#depends[@]} == 3 ]] || exit 1 expected='omarchy-mac-keyring>=20260914-2' - [[ $requirement != 'omarchy-mac-keyring>=20260915-1' ]] || expected=$requirement - [[ ${depends[1]} == "$expected" ]] - grep -qF '# preserved comment' "$work_dir/shared-line" + case $requirement in + 'omarchy-mac-keyring>=20260915-1'|'omarchy-mac-keyring>20260915-1'|'omarchy-mac-keyring=20260914-2'|'omarchy-mac-keyring=20260915-1') + expected=$requirement + ;; + esac + [[ ${depends[1]} == "$expected" ]] || exit 1 + grep -qF '# preserved comment' "$work_dir/shared-line" || exit 1 done ) || fail 'keyring transition must preserve adjacent dependencies and stronger existing bounds' pass 'keyring dependency normalization preserves shared lines and stronger version requirements' +( + source "$ROOT/build-packages.sh" + for requirement in \ + 'omarchy-mac-keyring=20260913-1' \ + 'omarchy-mac-keyring<20260914-2' \ + 'omarchy-mac-keyring<=20260914-2'; do + printf "depends=(\n '%s'\n)\n" "$requirement" >"$work_dir/rejected-keyring-bound" + if ( ensure_omarchy_mac_keyring_dependency "$work_dir/rejected-keyring-bound" 2>/dev/null ); then + exit 1 + fi + done +) || fail 'stale exact pins and upper bounds must fail closed' +pass 'keyring dependency normalization rejects stale exact pins and upper bounds' + # Reject dirty/wrong default sources before touching an existing output. ( source "$ROOT/build-inputs/prepare-recipes.sh" @@ -71,14 +96,14 @@ pass 'keyring dependency normalization preserves shared lines and stronger versi pass 'release builds reject unversioned recipe inputs before staging' ( - source "$ROOT/build-inputs/prepare-recipes.sh" + source "$ROOT/build-inputs/prepare-recipes.sh" || exit 1 OMARCHY_ALLOW_CUSTOM_RECIPES=1 prepare_omarchy_recipes \ - "$recipe_source" "$work_dir/with-keyring" >/dev/null + "$recipe_source" "$work_dir/with-keyring" >/dev/null || exit 1 keyring="$work_dir/with-keyring/pkgbuilds/omarchy-mac-keyring" - [[ -f $keyring/PKGBUILD && -f $keyring/omarchy-mac-keyring.install ]] - cmp "$ROOT/default/pacman/keyrings/omarchy-mac.gpg" "$keyring/omarchy-mac.gpg" - cmp "$ROOT/default/pacman/keyrings/omarchy-mac-trusted" "$keyring/omarchy-mac-trusted" - [[ ! -s $keyring/omarchy-mac-revoked ]] + [[ -f $keyring/PKGBUILD && -f $keyring/omarchy-mac-keyring.install ]] || exit 1 + cmp "$ROOT/default/pacman/keyrings/omarchy-mac.gpg" "$keyring/omarchy-mac.gpg" || exit 1 + cmp "$ROOT/default/pacman/keyrings/omarchy-mac-trusted" "$keyring/omarchy-mac-trusted" || exit 1 + [[ ! -s $keyring/omarchy-mac-revoked ]] || exit 1 ) || fail 'prepared recipes contain the exact pinned fork keyring payload' pass 'prepared recipes carry exact fork-owned trust bytes' diff --git a/test/vm/run-selective-edge b/test/vm/run-selective-edge index bcdf23d0c2b..6db6344651f 100644 --- a/test/vm/run-selective-edge +++ b/test/vm/run-selective-edge @@ -224,7 +224,8 @@ if [[ $CHECK_KEYRING == "1" ]]; then # its cached base predates this test. Package tests run without networking. nspawn pacman -S --needed --noconfirm bubblewrap nspawn --private-network --user="$GUEST_USER" --chdir=/src/omarchy \ - env OMARCHY_RUN_NATIVE_KEYRING_TEST=1 TMPDIR=/var/tmp TMP=/var/tmp TEMP=/var/tmp \ + env OMARCHY_RUN_NATIVE_KEYRING_TEST=1 OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1 \ + TMPDIR=/var/tmp TMP=/var/tmp TEMP=/var/tmp \ bash test/shell.d/omarchy-mac-keyring-package-install-test.sh \ 2>&1 | tee "$WORK/logs/keyring-package.log" fi From 9237ce33e396d7548c8a3a5542fbf59c1148ea00 Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Wed, 16 Sep 2026 01:48:58 +0530 Subject: [PATCH 5/7] Run keyring upgrade test in hosted ARM guest --- test/shell.d/install-vm-workflow-test.sh | 3 +++ ...marchy-mac-keyring-package-install-test.sh | 22 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/test/shell.d/install-vm-workflow-test.sh b/test/shell.d/install-vm-workflow-test.sh index 03b877ff50b..ec149f633ac 100644 --- a/test/shell.d/install-vm-workflow-test.sh +++ b/test/shell.d/install-vm-workflow-test.sh @@ -39,6 +39,9 @@ private_keyring_call = re.search( ) assert private_keyring_call, 'native keyring test may reuse only nspawn private networking' assert harness.count('OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1') == 1 +keyring_test = (root / 'test/shell.d/omarchy-mac-keyring-package-install-test.sh').read_text() +assert 'bwrap_command=(sudo -n bwrap)' in keyring_test, 'hosted nested bwrap must enter its uid namespace as guest root' +assert 'sudo -n chown -R 0:0 "$work"' in keyring_test, 'hosted bwrap bind sources must be owned by mapped guest root' install_step = next(step for step in job['steps'] if 'bash ./test/vm/run-selective-edge' in step.get('run', '')) install_env = {**job['env'], **install_step.get('env', {})} for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY', 'OMARCHY_INSTALL_VM_KEYRING'): diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh index 3d701d83911..7b6aab017c1 100644 --- a/test/shell.d/omarchy-mac-keyring-package-install-test.sh +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -9,6 +9,9 @@ if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then fi for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done bwrap_namespace_args=(--unshare-all) +bwrap_command=(bwrap) +cleanup_command=(rm -rf --) +hosted_private_boundary=0 case ${OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK:-0} in 0) ;; 1) @@ -25,7 +28,12 @@ case ${OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK:-0} in fi done Date: Wed, 16 Sep 2026 02:47:40 +0530 Subject: [PATCH 6/7] Keep native keyring test on Arch hosts --- .github/workflows/install-vm.yml | 3 +- docs/arm-package-sources.md | 2 +- test/shell.d/install-vm-workflow-test.sh | 18 +++----- ...marchy-mac-keyring-package-install-test.sh | 45 ++----------------- test/vm/run-selective-edge | 13 ------ 5 files changed, 10 insertions(+), 71 deletions(-) diff --git a/.github/workflows/install-vm.yml b/.github/workflows/install-vm.yml index a6f95d698bb..7f9a9378ade 100644 --- a/.github/workflows/install-vm.yml +++ b/.github/workflows/install-vm.yml @@ -30,7 +30,6 @@ jobs: OMARCHY_INSTALL_VM_WORK: /var/tmp/omarchy-install-${{ github.run_id }}-${{ github.run_attempt }} OMARCHY_INSTALL_VM_PACKAGE_SOURCES: '1' OMARCHY_INSTALL_VM_IDEMPOTENCY: '1' - OMARCHY_INSTALL_VM_KEYRING: '1' steps: - name: Checkout uses: actions/checkout@v4 @@ -82,7 +81,7 @@ jobs: run: | mkdir -p "$OMARCHY_INSTALL_VM_WORK/logs" set +e - sudo --preserve-env=HOME,OMARCHY_PKGS_PATH,OMARCHY_INSTALL_VM_WORK,OMARCHY_INSTALL_VM_CACHE,OMARCHY_INSTALL_VM_PACKAGE_SOURCES,OMARCHY_INSTALL_VM_IDEMPOTENCY,OMARCHY_INSTALL_VM_KEYRING \ + sudo --preserve-env=HOME,OMARCHY_PKGS_PATH,OMARCHY_INSTALL_VM_WORK,OMARCHY_INSTALL_VM_CACHE,OMARCHY_INSTALL_VM_PACKAGE_SOURCES,OMARCHY_INSTALL_VM_IDEMPOTENCY \ bash ./test/vm/run-selective-edge 2>&1 | tee "$RUNNER_TEMP/omarchy-harness.log" status=${PIPESTATUS[0]} set -e diff --git a/docs/arm-package-sources.md b/docs/arm-package-sources.md index 231609cf787..a0c9ee8b49e 100644 --- a/docs/arm-package-sources.md +++ b/docs/arm-package-sources.md @@ -28,7 +28,7 @@ The primary changed before signing activation to finalize signing-key management Publish the reviewed `omarchy-mac-keyring` package at `20260914-2` or newer with the RC4 package pair before asking clients to run this source's migrations. Both the pending bootstrap migration and its successor require that installed version. A source/manual update ahead of package publication stops with an explanatory error and remains pending; it must not skip the failed migration or weaken signature policy to continue. -Before RC4 publication, qualify the actual artifacts on a fresh base and an existing installation. Include a real prior-keyring upgrade and verify that the new primary becomes trusted, old and unrelated installed trust survives, completed migration markers stay skipped, and pending markers appear only after successful trust population. Hosted native package tests cover disposable package hooks and trust; they do not replace physical Mac installation and upgrade qualification. +Before RC4 publication, qualify the actual artifacts on a fresh base and an existing installation. Include a real prior-keyring upgrade and verify that the new primary becomes trusted, old and unrelated installed trust survives, completed migration markers stay skipped, and pending markers appear only after successful trust population. The opt-in native package test covers disposable package hooks and trust when run directly on Arch Linux ARM; it is intentionally separate from the hosted Ubuntu/nspawn install workflow and does not replace physical Mac installation and upgrade qualification. A development host that already ran the later strict-signing migration cannot install unsigned RC4 under `PackageRequired DatabaseRequired TrustedOnly`. A migration-marker fixture does not prove that transaction works. Use a clean disposable baseline with the supported RC4 policy for the unsigned canary; do not automatically reset a host's stricter policy or delete its migration markers. Qualify strict signing separately with the signed candidate. diff --git a/test/shell.d/install-vm-workflow-test.sh b/test/shell.d/install-vm-workflow-test.sh index ec149f633ac..3936a80f473 100644 --- a/test/shell.d/install-vm-workflow-test.sh +++ b/test/shell.d/install-vm-workflow-test.sh @@ -32,23 +32,15 @@ assert checkout['with'] == {'persist-credentials': 'false'}, 'use the default me assert not any('actions/cache@' in step.get('uses', '') for step in job['steps']) assert not (root / '.github/workflows/install-vm-selective-edge.yml').exists() harness = (root / 'test/vm/run-selective-edge').read_text() -private_keyring_call = re.search( - r'nspawn --private-network[^\n]*\\\n' - r'\s+env OMARCHY_RUN_NATIVE_KEYRING_TEST=1 OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1', - harness, -) -assert private_keyring_call, 'native keyring test may reuse only nspawn private networking' -assert harness.count('OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK=1') == 1 -keyring_test = (root / 'test/shell.d/omarchy-mac-keyring-package-install-test.sh').read_text() -assert 'bwrap_command=(sudo -n bwrap)' in keyring_test, 'hosted nested bwrap must enter its uid namespace as guest root' -assert 'sudo -n chown -R 0:0 "$work"' in keyring_test, 'hosted bwrap bind sources must be owned by mapped guest root' +assert 'OMARCHY_INSTALL_VM_KEYRING' not in str(workflow), 'hosted nspawn must not claim nested native keyring coverage' +assert 'omarchy-mac-keyring-package-install-test.sh' not in harness, 'hosted nspawn must not run the bwrap test' install_step = next(step for step in job['steps'] if 'bash ./test/vm/run-selective-edge' in step.get('run', '')) install_env = {**job['env'], **install_step.get('env', {})} -for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY', 'OMARCHY_INSTALL_VM_KEYRING'): +for key in ('OMARCHY_INSTALL_VM_PACKAGE_SOURCES', 'OMARCHY_INSTALL_VM_IDEMPOTENCY'): assert install_env[key] == '1', f'{key} must be enabled' for key in ('OMARCHY_INSTALL_VM_WORK', 'OMARCHY_INSTALL_VM_CACHE'): assert 'github.run_id' in install_env[key] and 'github.run_attempt' in install_env[key] -print('ok - every PR gets isolated ARM install coverage with package-source, repeat-install and native keyring validation') +print('ok - every PR gets isolated ARM install coverage with package-source and repeat-install validation') run = install_step['run'] preserved = re.search(r'--preserve-env=([^\s]+)', run).group(1).split(',') @@ -62,7 +54,7 @@ with tempfile.TemporaryDirectory() as temp: (cwd / 'bin/sudo').write_text('#!/bin/bash\nshift\nexec "$@"\n') (cwd / 'bin/sudo').chmod(0o755) (cwd / 'test/vm/run-selective-edge').write_text('''#!/bin/bash -[[ $OMARCHY_INSTALL_VM_PACKAGE_SOURCES == 1 && $OMARCHY_INSTALL_VM_IDEMPOTENCY == 1 && $OMARCHY_INSTALL_VM_KEYRING == 1 ]] || exit 99 +[[ $OMARCHY_INSTALL_VM_PACKAGE_SOURCES == 1 && $OMARCHY_INSTALL_VM_IDEMPOTENCY == 1 ]] || exit 99 rm -rf "$OMARCHY_INSTALL_VM_WORK/logs" mkdir -p "$OMARCHY_INSTALL_VM_WORK/logs" echo 'harness output before exit' diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh index 7b6aab017c1..2ece0db920f 100644 --- a/test/shell.d/omarchy-mac-keyring-package-install-test.sh +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -8,42 +8,12 @@ if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then exit 0 fi for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done -bwrap_namespace_args=(--unshare-all) -bwrap_command=(bwrap) -cleanup_command=(rm -rf --) -hosted_private_boundary=0 -case ${OMARCHY_KEYRING_TEST_NSPAWN_PRIVATE_NETWORK:-0} in - 0) ;; - 1) - [[ -r /proc/net/dev ]] || fail 'private caller network state is unavailable' - private_loopback=0 - while IFS= read -r network_device; do - [[ $network_device == *:* ]] || continue - interface=${network_device%%:*} - interface=${interface//[[:space:]]/} - if [[ $interface == lo ]]; then - private_loopback=1 - else - fail "private caller network exposes non-loopback interface: $interface" - fi - done &1 | tee "$WORK/logs/keyring-package.log" -fi - if [[ $CHECK_PACKAGE_SOURCES == "1" ]]; then log "validating restricted package sources and current-stack repository remediation" nspawn /bin/bash -euo pipefail <<'PACKAGE_SOURCES' 2>&1 | tee "$WORK/logs/package-sources.log" From a61ee8730d3dbe2a1a0ec85a047d733f7a9745cc Mon Sep 17 00:00:00 2001 From: Naeem Malik Date: Wed, 16 Sep 2026 11:52:07 +0530 Subject: [PATCH 7/7] Test shipped keyring upgrade baseline --- .../omarchy-mac-keyring-20260913-1/PKGBUILD | 19 ++++++++++++ .../omarchy-mac-keyring-20260913-1/README.md | 5 ++++ .../omarchy-mac-keyring-20260913-1/SHA256SUMS | 5 ++++ .../omarchy-mac-keyring.install | 11 +++++++ .../omarchy-mac-revoked | 0 .../omarchy-mac-trusted | 1 + .../omarchy-mac.gpg | 16 ++++++++++ ...marchy-mac-keyring-package-install-test.sh | 30 ++++++++++++------- 8 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/PKGBUILD create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/README.md create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/SHA256SUMS create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-keyring.install create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-revoked create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-trusted create mode 100644 test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac.gpg diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/PKGBUILD b/test/fixtures/omarchy-mac-keyring-20260913-1/PKGBUILD new file mode 100644 index 00000000000..31e730e6f5c --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/PKGBUILD @@ -0,0 +1,19 @@ +# Maintainer: Omarchy Mac + +pkgname=omarchy-mac-keyring +pkgver=20260913 +pkgrel=1 +pkgdesc='Omarchy Mac package signing keyring' +arch=(any) +url='https://github.com/omarchy-mac/omarchy-mac' +license=('GPL-3.0-or-later') +install=$pkgname.install +source=('omarchy-mac.gpg' 'omarchy-mac-trusted' 'omarchy-mac-revoked') +sha512sums=('0e411148bf58cb1cf3c1ef544ea0936d82b0d7b3996c3ef3d20ac909959129c1d7cb58dc84ac466cd3e2d75db228aff896640899b4bbbef19e7636f3033a7ea8' + '2fb4497f96fd9f446e0177f9dbf8eb34f0ad3dce5fc7fb687312c5e20a1127ceb68f0d6022424f35b37e2a6a91fb097b2212595375fa4d37d7547697b17de86e' + 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e') + +package() { + install -D -m0644 -t "$pkgdir/usr/share/pacman/keyrings/" \ + omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked +} diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/README.md b/test/fixtures/omarchy-mac-keyring-20260913-1/README.md new file mode 100644 index 00000000000..4221bebc2d7 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/README.md @@ -0,0 +1,5 @@ +# Shipped quattro keyring package fixture + +These five package sources are copied byte-for-byte from `origin/quattro` commit `04862a02c556b5c4305b2ea3430e9d2cfaab66c4`: `build-inputs/omarchy-mac-keyring/{PKGBUILD,omarchy-mac-keyring.install}` and `default/pacman/keyrings/{omarchy-mac.gpg,omarchy-mac-trusted,omarchy-mac-revoked}`. The package version is `20260913-1`. This provenance identifier is informational; running the test requires no Git history or remote access. + +The fixture contains only public certificates, public trust metadata, and package source. It models the old-primary-only package shipped on the quattro baseline, does not revoke keys, and is the real predecessor for upgrade testing. `SHA256SUMS` binds the five original files. The native test verifies this manifest before building and checks that upgrading adds the new primary without disturbing existing old or unrelated client trust. diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/SHA256SUMS b/test/fixtures/omarchy-mac-keyring-20260913-1/SHA256SUMS new file mode 100644 index 00000000000..5f5f1d0b7d5 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/SHA256SUMS @@ -0,0 +1,5 @@ +c749142502810e7a0e002d1a01b34cfe43169c57cd1b820415eb1c6e4c123826 PKGBUILD +604333c2027bf219ad759d7c1e8f8238a6fc3405f8fdb7ab8757c51c27b1cf60 omarchy-mac-keyring.install +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 omarchy-mac-revoked +aad605b6a64277974467fe9c6c74c345d3e095cf19eb8853eec4803faf180268 omarchy-mac-trusted +6e2801dc5c790b817fa5fc99cb2f4b93063042cbc94b2eb9c0e80026d97c6ca0 omarchy-mac.gpg diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-keyring.install b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-keyring.install new file mode 100644 index 00000000000..40a474972ff --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-keyring.install @@ -0,0 +1,11 @@ +post_upgrade() { + if [ -x usr/bin/pacman-key ] && usr/bin/pacman-key -l >/dev/null 2>&1; then + usr/bin/pacman-key --populate omarchy-mac + else + echo ' >>> Initialize pacman-key, then run pacman-key --populate omarchy-mac.' + fi +} + +post_install() { + post_upgrade +} diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-revoked b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-revoked new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-trusted b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-trusted new file mode 100644 index 00000000000..557aa18eee6 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac-trusted @@ -0,0 +1 @@ +F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7:4: diff --git a/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac.gpg b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac.gpg new file mode 100644 index 00000000000..24fbc026b91 --- /dev/null +++ b/test/fixtures/omarchy-mac-keyring-20260913-1/omarchy-mac.gpg @@ -0,0 +1,16 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEaqbKtRYJKwYBBAHaRw8BAQdAI7gUCEOd14QplbZ12KYIV7XkM+/wyPPG9QBz +mPvV5bW0G09tYXJjaHkgTWFjIFBhY2thZ2UgU2lnbmluZ4iWBBMWCgA+FiEE88Wu +P8/8c4wwHjCo8MVIwNJyefcFAmqmyrUCGwEFCQlmAYAFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQ8MVIwNJyefc4HgEArUgsUks8G6WRTBV5Ef52/djRyIGu7Uai +e5Ih8U3XB9gA/RjtlKVqC3E00ZhN5m4Oc2IbfPNZr8Oh7ZlYdvFq148BuDMEaqbK +3xYJKwYBBAHaRw8BAQdAWA4H8/FHdnZP1Z9NeFkFNmMNIJyDT66oSHcGCxBjLIOI +9AQYFgoAJhYhBPPFrj/P/HOMMB4wqPDFSMDScnn3BQJqpsrfAhsCBQkB4TOAAIAJ +EPDFSMDScnn3dSAEGRYKAB0WIQRsJZfG5p/EiY09Vg4zEwdpYDAoXgUCaqbK3wAK +CRAzEwdpYDAoXvBSAPMGvUcm9mi64yMqX3nkMsi1H7HuvdXmBaRgyllBrqYRAQDA +xFiDGYOtJqPZwpcIupeEn0Z62ScAwjBS5ONqXNfdAjvBAP41jIblDA42gFKa/G8b +xL9fy7HGsg/zpEFBnGsA/EB03wEA02k2hYO0FuV7qo3pmqVqR0MIVygZLEA2sGWB +Jyvh8wU= +=iEoD +-----END PGP PUBLIC KEY BLOCK----- diff --git a/test/shell.d/omarchy-mac-keyring-package-install-test.sh b/test/shell.d/omarchy-mac-keyring-package-install-test.sh index 2ece0db920f..a5590f97f57 100644 --- a/test/shell.d/omarchy-mac-keyring-package-install-test.sh +++ b/test/shell.d/omarchy-mac-keyring-package-install-test.sh @@ -7,7 +7,7 @@ if [[ ${OMARCHY_RUN_NATIVE_KEYRING_TEST:-0} != 1 ]]; then printf 'ok - native keyring package install # SKIP set OMARCHY_RUN_NATIVE_KEYRING_TEST=1\n' exit 0 fi -for tool in bwrap makepkg pacman gpg findmnt; do require_command "$tool"; done +for tool in bwrap makepkg pacman gpg gpgconf findmnt; do require_command "$tool"; done scratch_parent=${OMARCHY_TEST_TMPDIR:-${TMPDIR:-/var/tmp}} case $(findmnt -n -o FSTYPE -T "$scratch_parent") in ''|tmpfs|ramfs) fail 'native keyring test requires disk-backed scratch' ;; @@ -25,8 +25,8 @@ cp "$ROOT/default/pacman/keyrings/"* "$work/build/" packages=("$work/build/"*.pkg.tar.*) [[ ${#packages[@]} == 1 ]] || fail 'exactly one keyring package was built' cp "${packages[0]}" "$work/keyring.pkg.tar.zst" -# Committed public-only fixture works in shallow checkouts and source archives. -baseline="$ROOT/test/fixtures/omarchy-mac-keyring-20260914-1" +# Exact public-only quattro fixture works in shallow checkouts and source archives. +baseline="$ROOT/test/fixtures/omarchy-mac-keyring-20260913-1" (cd "$baseline" && sha256sum --check SHA256SUMS) || fail 'previous public fixture checksums' mkdir "$work/build-previous" for name in PKGBUILD omarchy-mac-keyring.install omarchy-mac.gpg omarchy-mac-trusted omarchy-mac-revoked; do @@ -60,8 +60,11 @@ mkdir -m700 /work/unrelated gpg --homedir /work/unrelated --batch --passphrase '' --quick-generate-key 'Independent fixture' ed25519 cert 1d >/work/unrelated.log 2>&1 other=$(gpg --homedir /work/unrelated --with-colons --list-keys 2>/dev/null | awk -F: '$1=="fpr" {print $10; exit}') gpg --homedir /work/unrelated --export "$other" >/work/unrelated.gpg -pacman-key --add /work/unrelated.gpg >/work/add.log 2>&1 -pacman-key --lsign-key "$other" >>/work/add.log 2>&1 +add_unrelated() { + pacman-key --add /work/unrelated.gpg >"$1" 2>&1 + pacman-key --lsign-key "$other" >>"$1" 2>&1 +} +add_unrelated /work/add.log trusted() { gpg --homedir /etc/pacman.d/gnupg --batch --with-colons --list-keys "$1" 2>/dev/null | awk -F: '$1=="pub" && ($2=="f" || $2=="u") {ok=1} END {exit !ok}' @@ -74,14 +77,21 @@ done trusted "$key" trusted "$other" printf 'ok - actual package post_install establishes new trust and preserves unrelated trust\n' -# Exercise a real prior-version upgrade, after the fresh-install check. +# Start the upgrade scenario with independent initialized trust. Removing a +# keyring package does not delete keys it previously populated, so reusing the +# fresh-install keyring would manufacture the shipped baseline incorrectly. pacman -R --noconfirm omarchy-mac-keyring >/work/remove-fresh.log 2>&1 +gpgconf --homedir /etc/pacman.d/gnupg --kill all +rm -rf /etc/pacman.d/gnupg +pacman-key --init >/work/reinit.log 2>&1 +add_unrelated /work/readd.log +trusted "$other" +if gpg --homedir /etc/pacman.d/gnupg --list-keys "$key" >/dev/null 2>&1; then exit 1; fi pacman -U --noconfirm /work/previous.pkg.tar.zst >/work/previous.log 2>&1 -[[ $(pacman -Q omarchy-mac-keyring) == 'omarchy-mac-keyring 20260914-1' ]] +[[ $(pacman -Q omarchy-mac-keyring) == 'omarchy-mac-keyring 20260913-1' ]] previous_key=F3C5AE3FCFFC738C301E30A8F0C548C0D27279F7 trusted "$previous_key" -# Remove only the disposable new key; upgrade must restore it via post_upgrade. -pacman-key --delete "$key" >/work/delete.log 2>&1 +# The shipped predecessor contains only the old primary; the upgrade must add the new one. if gpg --homedir /etc/pacman.d/gnupg --list-keys "$key" >/dev/null 2>&1; then exit 1; fi pacman -U --noconfirm /work/keyring.pkg.tar.zst >/work/upgrade.log 2>&1 [[ $(pacman -Q omarchy-mac-keyring) == 'omarchy-mac-keyring 20260914-2' ]] @@ -89,7 +99,7 @@ grep -q 'upgrading omarchy-mac-keyring' /work/upgrade.log trusted "$previous_key" trusted "$key" trusted "$other" -printf 'ok - actual 20260914-1 to -2 package upgrade restores new trust and preserves existing old and unrelated trust\n' +printf 'ok - actual shipped 20260913-1 to 20260914-2 upgrade adds new trust and preserves old and unrelated trust\n' INNER # No host home, /etc, /var, device tree, network, or sockets are exposed. # All writable paths including /tmp live under verified disk-backed scratch.