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)"