diff --git a/default/pacman/pacman-edge.conf b/default/pacman/pacman-edge.conf index b869880c19b..07b94c0db7d 100644 --- a/default/pacman/pacman-edge.conf +++ b/default/pacman/pacman-edge.conf @@ -26,7 +26,7 @@ SigLevel = Required DatabaseOptional Server = https://pkgs.omarchy.org/edge/$arch [omarchy-aarch64] -SigLevel = Optional TrustAll +SigLevel = PackageRequired DatabaseRequired TrustedOnly Server = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/edge [asahi-alarm] diff --git a/default/pacman/pacman-rc.conf b/default/pacman/pacman-rc.conf index 7d4c3474716..12c3d2254b5 100644 --- a/default/pacman/pacman-rc.conf +++ b/default/pacman/pacman-rc.conf @@ -26,7 +26,7 @@ SigLevel = Required DatabaseOptional Server = https://pkgs.omarchy.org/edge/$arch [omarchy-aarch64] -SigLevel = Optional TrustAll +SigLevel = PackageRequired DatabaseRequired TrustedOnly Server = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/rc [asahi-alarm] diff --git a/default/pacman/pacman-stable.conf b/default/pacman/pacman-stable.conf index 1f087ddde8b..481116ca89e 100644 --- a/default/pacman/pacman-stable.conf +++ b/default/pacman/pacman-stable.conf @@ -81,7 +81,7 @@ SigLevel = Required DatabaseOptional Server = https://pkgs.omarchy.org/edge/$arch [omarchy-aarch64] -SigLevel = Optional TrustAll +SigLevel = PackageRequired DatabaseRequired TrustedOnly Server = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/stable [asahi-alarm] diff --git a/default/pacman/pacman.conf b/default/pacman/pacman.conf index c8f55f1e986..f376719836a 100644 --- a/default/pacman/pacman.conf +++ b/default/pacman/pacman.conf @@ -79,7 +79,7 @@ SigLevel = Required DatabaseOptional Server = https://pkgs.omarchy.org/edge/$arch [omarchy-aarch64] -SigLevel = Optional TrustAll +SigLevel = PackageRequired DatabaseRequired TrustedOnly Server = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/edge [asahi-alarm] diff --git a/docs/arm-package-sources.md b/docs/arm-package-sources.md index a0c9ee8b49e..53943080aaa 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'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. +Omarchy Mac's package-signing primary is `FBD6874D423C418DDB6D143EECE19CDDE306DBD2`, with signing subkey `D791ED0C72439D9F8757421258043B2770A25762`. Its exact public bytes ship in `omarchy-mac-keyring` version `20260914-2`; upstream keyrings remain installed. 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. This 4.0.3rc5 source is signed by the new subkey 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. 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. @@ -34,7 +34,7 @@ A development host that already ran the later strict-signing migration cannot in ## 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. +`./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 retained the existing `Optional TrustAll` fork policy because its archive and database were intentionally unsigned. This rc5 source requires trusted package and database signatures. Required upstream graphics signatures remain required throughout. Fresh preflight can initialize an ephemeral local signing key in its private keyring and fetch and trust only the declared upstream stack fingerprint `40DFB630FF42BCFFB047046CF0134EE680CAC571`. It imports the fork key only from the exact public bytes pinned in this source checkout and verifies the full primary fingerprint; it never retrieves that key from a keyserver. Host secret keys are never copied. The private keyring and its agent are removed on exit. Existing distribution/Asahi trust must already be provisioned by the base system. Accepted installation installs and populates the durable fork keyring for subsequent package setup. diff --git a/install/helpers/arm-channel.sh b/install/helpers/arm-channel.sh index c6c5c085c3d..9b408d179d2 100644 --- a/install/helpers/arm-channel.sh +++ b/install/helpers/arm-channel.sh @@ -28,7 +28,7 @@ omarchy_arm_channel_render() { return 1 fi cat "$config" >"$output" - printf '\n[omarchy-aarch64]\nSigLevel = Optional TrustAll\nServer = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/%s\n' "$channel" >>"$output" + printf '\n[omarchy-aarch64]\nSigLevel = PackageRequired DatabaseRequired TrustedOnly\nServer = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/%s\n' "$channel" >>"$output" return fi if ! omarchy_arm_channel_current "$config" >/dev/null; then @@ -40,6 +40,14 @@ omarchy_arm_channel_render() { selected && /^[[:space:]]*Server[[:space:]]*=/ { sub(/\/download\/(stable|rc|edge)/, "/download/" channel) } { print } ' "$config" >"$output" + if [[ $allow_new == fresh ]]; then + local strict_output="${output}.strict.$$" + if ! omarchy_arm_signature_policy_render "$output" 'PackageRequired DatabaseRequired TrustedOnly' "$strict_output"; then + rm -f -- "$strict_output" "$output" + return 1 + fi + mv -- "$strict_output" "$output" + fi } omarchy_arm_signature_policy_render() { diff --git a/migrations/1789317000.sh b/migrations/1789317000.sh new file mode 100644 index 00000000000..011b8829979 --- /dev/null +++ b/migrations/1789317000.sh @@ -0,0 +1,16 @@ +echo "Require trusted Omarchy Mac package and database signatures" + +readonly omarchy_mac_signing_key='FBD6874D423C418DDB6D143EECE19CDDE306DBD2' +readonly strict_omarchy_mac_policy='PackageRequired DatabaseRequired TrustedOnly' + +# The unsigned rc4 bootstrap must already have delivered this trust. Do not +# weaken policy or retrieve a key from the network to repair a skipped step. +if omarchy-pkg-missing omarchy-mac-keyring; then + echo "Omarchy Mac signing trust is missing; install the reviewed rc4 bootstrap before this RC." >&2 + return 1 +fi +sudo pacman-key --populate omarchy-mac +sudo pacman-key --finger "$omarchy_mac_signing_key" | tr -d '[:space:]' | grep -qF "$omarchy_mac_signing_key" + +source "$OMARCHY_PATH/install/helpers/arm-channel.sh" +omarchy_arm_signature_policy_apply /etc/pacman.conf "$strict_omarchy_mac_policy" diff --git a/test/shell.d/arm-channel-test.sh b/test/shell.d/arm-channel-test.sh index c7917d4b7e2..f891ee71e68 100644 --- a/test/shell.d/arm-channel-test.sh +++ b/test/shell.d/arm-channel-test.sh @@ -52,8 +52,8 @@ printf '%s\n' '[options]' 'Architecture = aarch64' '[extra]' 'Server = https://r cp "$test_tmp/fresh" "$test_tmp/fresh-before" omarchy_arm_channel_render "$test_tmp/fresh" rc "$test_tmp/fresh-rendered" fresh [[ $(omarchy_arm_channel_current "$test_tmp/fresh-rendered") == rc ]] || fail 'fresh candidate adds explicit RC lane' -grep -qxF 'SigLevel = Optional TrustAll' "$test_tmp/fresh-rendered" || - fail 'fresh bootstrap retains the disclosed legacy policy until trust is delivered' +grep -qxF 'SigLevel = PackageRequired DatabaseRequired TrustedOnly' "$test_tmp/fresh-rendered" || + fail 'fresh signed RC requires trusted package and database signatures' cmp "$test_tmp/fresh" "$test_tmp/fresh-before" || fail 'fresh render preserves active configuration' printf '%s\n' '[omarchy-aarch64]' 'Server = https://custom.example/repo' >"$test_tmp/hidden" printf 'Include = %s\n' "$test_tmp/hidden" >>"$test_tmp/fresh" @@ -62,13 +62,21 @@ if omarchy_arm_channel_render "$test_tmp/fresh" rc "$test_tmp/rejected" fresh >/ fi pass 'fresh candidates add a missing lane but reject hidden custom repositories' +omarchy_arm_channel_render "$config" rc "$test_tmp/existing-fresh-rendered" fresh +grep -qxF 'SigLevel = PackageRequired DatabaseRequired TrustedOnly' "$test_tmp/existing-fresh-rendered" || + fail 'fresh signed RC leaves an existing managed stanza permissive' +sed -n '/^\[private-first\]/,/^\[/p' "$test_tmp/existing-fresh-rendered" | + grep -qxF 'Server = https://private.example/$arch' || fail 'fresh strict render changed another repository' +cmp "$config" "$test_tmp/original" || fail 'fresh strict render modified the active configuration' +pass 'fresh signed RC hardens an existing managed lane before preflight' + for template in "$ROOT/default/pacman/pacman.conf" "$ROOT/default/pacman/pacman-stable.conf" \ "$ROOT/default/pacman/pacman-rc.conf" "$ROOT/default/pacman/pacman-edge.conf"; do sed -n '/^\[omarchy-aarch64\]/,/^\[/p' "$template" | - grep -qxF 'SigLevel = Optional TrustAll' || - fail "bootstrap transition policy missing from $template" + grep -qxF 'SigLevel = PackageRequired DatabaseRequired TrustedOnly' || + fail "strict signed-RC policy missing from $template" done -pass 'all shipped ARM repository templates remain compatible with the one-time bootstrap' +pass 'all shipped ARM repository templates require the trusted fork signer' printf '#!/bin/bash\necho aarch64\n' >"$test_tmp/bin/uname" cat >"$test_tmp/bin/omarchy-update" <<'SH' diff --git a/test/shell.d/omarchy-mac-strict-signature-migration-test.sh b/test/shell.d/omarchy-mac-strict-signature-migration-test.sh new file mode 100644 index 00000000000..e6fcf96b0c2 --- /dev/null +++ b/test/shell.d/omarchy-mac-strict-signature-migration-test.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -euo pipefail +source "$(dirname -- "${BASH_SOURCE[0]}")/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +mkdir -p "$test_tmp/bin" +config="$test_tmp/pacman.conf" +calls="$test_tmp/calls" +cat >"$config" <<'CONF' +[options] +Architecture = aarch64 +[custom] +SigLevel = Optional TrustAll +Server = https://custom.example +[omarchy-aarch64] +SigLevel = Optional TrustAll +Server = https://github.com/omarchy-mac/omarchy-pkgs-aarch64/releases/download/rc +[later] +SigLevel = Never +Server = file:///later +CONF + +cat >"$test_tmp/bin/sudo" <<'SH' +#!/bin/bash +if [[ $1 == pacman-key && $2 == --populate ]]; then echo "populate $3" >>"$TEST_CALLS"; exit 0; fi +if [[ $1 == pacman-key && $2 == --finger ]]; then + printf 'Key fingerprint = FBD6 874D 423C 418D DB6D 143E ECE1 9CDD E306 DBD2\n' + exit 0 +fi +exec "$@" +SH +chmod +x "$test_tmp/bin/sudo" +export PATH="$test_tmp/bin:$PATH" TEST_CALLS="$calls" OMARCHY_PATH="$ROOT" +omarchy-pkg-missing() { return 1; } + +sed "s|/etc/pacman.conf|$config|g" "$ROOT/migrations/1789317000.sh" >"$test_tmp/migration.sh" +(source "$test_tmp/migration.sh" >/dev/null) + +source "$ROOT/install/helpers/arm-channel.sh" +omarchy_arm_signature_policy_assert "$config" 'PackageRequired DatabaseRequired TrustedOnly' || + fail 'signed RC migration did not establish effective strict policy' +sed -n '/^\[custom\]/,/^\[/p' "$config" | grep -qxF 'SigLevel = Optional TrustAll' || + fail 'signed RC migration changed another repository' +sed -n '/^\[later\]/,$p' "$config" | grep -qxF 'SigLevel = Never' || + fail 'signed RC migration changed a later repository' +[[ $(cat "$calls") == 'populate omarchy-mac' ]] || fail 'signed RC migration did not populate exact keyring once' + +omarchy-pkg-missing() { return 0; } +if (source "$test_tmp/migration.sh" >/dev/null 2>&1); then + fail 'signed RC migration accepts a client that skipped the trust bootstrap' +fi +pass 'signed RC enforces package/database signatures only after verified bootstrap trust' diff --git a/test/shell.d/rc4-bootstrap-boundary-test.sh b/test/shell.d/rc4-bootstrap-boundary-test.sh deleted file mode 100644 index 47eceeb5161..00000000000 --- a/test/shell.d/rc4-bootstrap-boundary-test.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/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' -[[ ! -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" <>"$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 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 1789407944; do - [[ -f $test_tmp/markers/$name.sh ]] || fail "$name did not complete" -done -[[ $(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' -pass 'all pending RC4 trust migrations complete without old-key or policy operations' diff --git a/version b/version index a95e48f37dd..0171e6597ab 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.0.3rc4 +4.0.3rc5