Clear the onboard phase flags on offboard, and let the mirror signal the owner before revoking the key it signals with

This commit is contained in:
Gmer4Lfe
2026-08-17 05:44:35 -04:00
parent fe58fb7826
commit bdc2cdbadb
2 changed files with 55 additions and 4 deletions
+39 -4
View File
@@ -37,7 +37,8 @@
# Step 5: Disarm sync gates — same four gates as the owner path
# Step 6: Revoke Emby admin — remove own admin account from local Emby instance
# Step 7: Restart own stack — bring up own parked containers
# Step 8: SSH revocation — revoke keys both directions, write state, signal owner
# Step 8: Finalise — write INACTIVE, clear phase flags, signal owner, THEN revoke
# keys. Revocation is last because the signal needs the key
#
# ==============================================================================================
# DESIGN PRINCIPLES
@@ -406,19 +407,31 @@ if [[ "$AM_MIRROR" == true ]]; then
echo ""
echo "━━━ $ICON_SHIELD Step 8/8 — SSH Revocation + State ━━━"
do_ssh_key_revocation "${OWNER_IP:-}"
# State first, keys last — same ordering the owner path needed. Revocation used to run here,
# before the push below, so the mirror destroyed the key and then tried to tell the owner it
# had left using that key. The owner never heard, and the notify promised it would "finalise
# on next check" — a check that now had no way in.
NOW=$(date '+%Y-%m-%d %H:%M:%S')
if [[ "$DRY_RUN" == false ]]; then
write_state_file "$LOCAL_STATE_FILE" \
"INACTIVE" "" "$NOW" "$LOCAL_SERVER_NAME" "$REASON"
echo "Local state: INACTIVE ✅"
add_to_blocklist "$OWNER" "$REASON"
# Inverse of onboard's write_onboard_phase. MIRROR_ID is this host on this path, and the
# flags are named for the mirror on both sides, so the same names clear here.
_setup_db="$(platform_setup_db_path)"
for _flag in "${MIRROR_ID}_PHASE1_DONE" "${MIRROR_ID}_PHASE2_DONE" "${MIRROR_ID}_KEY_READY"; do
clear_state_var "$_setup_db" "$_flag"
done
echo "Onboard phase flags cleared ✅"
unset _setup_db _flag
else
warn "DRY RUN — would write INACTIVE state and blocklist $OWNER"
warn "DRY RUN — would write INACTIVE state, clear phase flags and blocklist $OWNER"
fi
if [[ "$OWNER_REACHABLE" == true ]]; then
platform_push_setup_state 2>/dev/null || warn "Could not push cleared setup state to $OWNER"
push_state_to_remote "$LOCAL_STATE_FILE" "$OWNER_IP" "$OWNER_SSH_KEY"
notify "Partnership offboard requested by $MIRROR$OWNER will finalise on next check" \
"Partnership" "normal"
@@ -426,6 +439,9 @@ if [[ "$AM_MIRROR" == true ]]; then
warn "$OWNER unreachable — state written locally, owner will see it when reachable"
fi
# Last, for the reason above: everything before it needs the key.
do_ssh_key_revocation "${OWNER_IP:-}"
# ── Summary ───────────────────────────────────────────────────────────────────────────────
END=$(date +%s)
echo ""
@@ -605,7 +621,26 @@ if [[ "$DRY_RUN" == false ]]; then
STEP_STATE_WRITE_OK=false
fi
add_to_blocklist "$MIRROR" "$REASON"
# The inverse of onboard's write_onboard_phase, which had none. Without this a completed
# offboard leaves the state file reading INACTIVE beside HOST*_PHASE1_DONE / _PHASE2_DONE
# still set — and the setup checklist and partnership card read the flags, not the state
# file, so a torn-down partnership went on presenting itself as fully onboarded.
#
# KEY_READY goes too: it means "a key is generated and waiting to be installed", which stops
# being true the moment Step 11 revokes both sides.
_setup_db="$(platform_setup_db_path)"
for _flag in "${MIRROR_ID}_PHASE1_DONE" "${MIRROR_ID}_PHASE2_DONE" "${MIRROR_ID}_KEY_READY"; do
clear_state_var "$_setup_db" "$_flag"
done
echo "Onboard phase flags cleared ✅"
unset _setup_db _flag
if [[ "$MIRROR_REACHABLE" == true ]]; then
# Pushed after the flags are cleared, so the mirror receives the cleared file rather than
# the version that still claimed a finished onboard.
platform_push_setup_state 2>/dev/null || \
warn "Could not push cleared setup state to $MIRROR"
if push_state_to_remote "$LOCAL_STATE_FILE" "$MIRROR_IP" "$MIRROR_SSH_KEY"; then
STEP_STATE_PUSH_OK=true
else