From b92e16abc0ae5384a71b170aaf62bee40fa82308 Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 5 Aug 2026 07:48:51 -0400 Subject: [PATCH] =?UTF-8?q?fix(release):=20sign=20v1.7.122=20with=20the=20?= =?UTF-8?q?OLD=20root=20=E2=80=94=20the=20rotation=20moved=20the=20checks?= =?UTF-8?q?=20a=20release=20early?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rotation commit pointed create-release.sh and publish-release-assets.sh at the NEW root in the same commit that pins it in the binary. But the release CARRYING the rotation must be signed with the OLD root: every node is still running the previous binary, which pins the old key. So the tooling would have rejected the only signature the fleet can accept, and the signature it demanded would have ended OTA fleet-wide. Both checks now expect the old DID for this cycle, with the flip to the new one called out for v1.7.123+. sign-manifest.sh documents the ARCHY_RELEASE_ROOT_PUBKEY override needed because the signer built from this tree already pins the new anchor and would fail to verify its own correct output. Co-Authored-By: Claude Fable 5 --- scripts/create-release.sh | 14 +++++++++++++- scripts/publish-release-assets.sh | 6 +++++- scripts/sign-manifest.sh | 13 +++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/scripts/create-release.sh b/scripts/create-release.sh index d836ff9b..c160d545 100755 --- a/scripts/create-release.sh +++ b/scripts/create-release.sh @@ -240,7 +240,19 @@ install -m 0644 "$FRONTEND_ARCHIVE" "$VERSION_DIR/archipelago-frontend-${VERSION # warning and falls through — and the commit then happened anyway. A release # commit carrying a manifest no node will accept has no valid use, so refuse # to create one rather than leave a tag that has to be re-cut. -EXPECTED_DID="did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT" +# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha) — this is the OLD root, deliberately. +# +# The trust anchor in the binary already pins the NEW root +# (z6Mkfu5LT…DLWT), because this release is what installs that pin. But the +# manifest THIS release ships must be signed with the OLD root +# (z6Mkkid…q7ur): every node is still running the previous binary, which +# pins the old key and would reject anything else. Signing this one with the +# new key ends OTA fleet-wide and needs hands-on recovery per node. +# +# ➜ NEXT RELEASE (v1.7.123+): change this to the new DID, and the same line +# in publish-release-assets.sh. By then every node runs a binary pinning +# the new root, and an old-key signature is the one that gets rejected. +EXPECTED_DID="did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur" if ! grep -q '"signature":' "$PROJECT_ROOT/releases/manifest.json" \ || ! grep -q "\"signed_by\": \"$EXPECTED_DID\"" "$PROJECT_ROOT/releases/manifest.json"; then echo "" >&2 diff --git a/scripts/publish-release-assets.sh b/scripts/publish-release-assets.sh index f5ba3d72..1079238a 100755 --- a/scripts/publish-release-assets.sh +++ b/scripts/publish-release-assets.sh @@ -29,7 +29,11 @@ fail() { echo "Error: $*" >&2; exit 1; } # with the pinned release-root anchor refuse to auto-apply unsigned manifests, # and enforcement will tighten to hard-reject — an unsigned publish would # strand them. Grep proves presence; ceremony verify proves the crypto. -EXPECTED_DID="did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT" +# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha) — OLD root on purpose; see the same +# block in create-release.sh. Nodes still run the previous binary and pin the +# old key, so the manifest this release publishes must carry an old-key +# signature. Flip both to z6Mkfu5LT…DLWT for v1.7.123+. +EXPECTED_DID="did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur" grep -q '"signature":' "$PROJECT_ROOT/releases/manifest.json" \ && grep -q "\"signed_by\": \"$EXPECTED_DID\"" "$PROJECT_ROOT/releases/manifest.json" \ || fail "releases/manifest.json is not signed by the release root — run: bash scripts/sign-manifest.sh" diff --git a/scripts/sign-manifest.sh b/scripts/sign-manifest.sh index 7bb0f989..62e7a7d8 100755 --- a/scripts/sign-manifest.sh +++ b/scripts/sign-manifest.sh @@ -11,6 +11,19 @@ # Normally create-release.sh signs the manifest inline; this script exists for # re-signing (e.g. a manifest edited after creation) or signing on a box where # the release run was non-interactive. +# +# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha). This release must be signed with the +# OLD release root, because every node still runs a binary pinning it — but +# the signer built from THIS tree already pins the NEW root, so its own +# verification would reject a correct old-key signature. Pin the old anchor +# for the duration of the ceremony so signing and verification agree: +# +# ARCHY_RELEASE_ROOT_PUBKEY=5d15cbee8a108f7dd288c02d29a1d9d71f198acc99186aad8008b4f28d469951 \ +# bash scripts/sign-manifest.sh +# +# That hex is the OLD root's PUBLIC key (verified to derive to +# did:key:z6Mkkid…q7ur); it is not secret and pins verification only. +# From v1.7.123 the override is unnecessary — drop it and this block. set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"