← back to Homesonspec
TK-10: add finish-tk10.sh turnkey go-live driver
b319e2f0ab4d3300b87c36598e232b28f6c751f8 · 2026-07-28 06:58:44 -0700 · Steve Abrams
Gates on GoDaddy->Cloudflare NS propagation (exits with the DO-THIS-ONE-THING
card if not swapped), then runs the CF-API cutover half + Kamatera deploy +
apex/admin smoke test, and prints (does not run) the old-GoDaddy-key revocation
step. Secret-free; reads creds from secrets-manager at runtime like cutover-dns.sh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit b319e2f0ab4d3300b87c36598e232b28f6c751f8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 28 06:58:44 2026 -0700
TK-10: add finish-tk10.sh turnkey go-live driver
Gates on GoDaddy->Cloudflare NS propagation (exits with the DO-THIS-ONE-THING
card if not swapped), then runs the CF-API cutover half + Kamatera deploy +
apex/admin smoke test, and prints (does not run) the old-GoDaddy-key revocation
step. Secret-free; reads creds from secrets-manager at runtime like cutover-dns.sh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
finish-tk10.sh | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 190 insertions(+)
diff --git a/finish-tk10.sh b/finish-tk10.sh
new file mode 100755
index 0000000..7435540
--- /dev/null
+++ b/finish-tk10.sh
@@ -0,0 +1,190 @@
+#!/usr/bin/env bash
+# ============================================================================
+# finish-tk10.sh — one-command HomesOnSpec.com production go-live finisher
+# ============================================================================
+# TK-10 turnkey driver. Steve does exactly ONE manual thing first (the GoDaddy
+# nameserver swap — GoDaddy's write-API is 404-gated for this domain, so it
+# CANNOT be automated), then runs this ONE script to finish the go-live.
+#
+# DO THIS ONE THING FIRST (GoDaddy dashboard, homesonspec.com → Nameservers):
+# change from ns69.domaincontrol.com + ns70.domaincontrol.com
+# to ezra.ns.cloudflare.com + kimora.ns.cloudflare.com
+# THEN run: bash ~/Projects/homesonspec/finish-tk10.sh
+#
+# WHAT THIS SCRIPT DOES (in order):
+# (a) GATE — verifies `dig NS homesonspec.com` actually shows the two
+# Cloudflare nameservers. If not (swap not done / not propagated yet),
+# it PRINTS the exact GoDaddy step and EXITS 2. Nothing else runs.
+# (b) CLOUDFLARE — runs cutover-dns.sh (API-driven + reversible) to ensure
+# the CF zone / proxied A / email records / SSL=full are all in place.
+# (Idempotent — a no-op if everything is already set.)
+# (c) DEPLOY + SMOKE — runs the project's deploy script (Kamatera) and then
+# HTTP-smoke-tests the apex + admin.
+# (d) REVOKE — PRINTS (does NOT auto-run) the exact old-GoDaddy-key
+# revocation step for Steve to do by hand.
+#
+# ── REQUIREMENTS ────────────────────────────────────────────────────────────
+# * The DEPLOY step (c) SSHes to Kamatera (root@45.61.58.125). Run this from
+# a Kamatera-SSH-CAPABLE session (a plain interactive shell on Mac2 — NOT a
+# sandboxed agent/workflow session, whose SSH is blocked). If SSH is not
+# available, pass --skip-deploy to run only the Cloudflare + gate steps and
+# deploy by hand.
+# * Reads Cloudflare + GoDaddy creds from ~/Projects/secrets-manager/.env
+# (the same source cutover-dns.sh uses). No secrets live in this file.
+#
+# ── PATH NOTE (pre-existing) ────────────────────────────────────────────────
+# The gitignored deploy scripts (deploy-kamatera.sh / golive-homesonspec.sh)
+# still reference the OLD source path `~/Projects/spechomes` and remote
+# `/root/Projects/spechomes` internally (project was renamed to homesonspec).
+# That is intentional-for-now: the Kamatera remote dir is still
+# /root/Projects/spechomes and the local rsync source `~/Projects/spechomes`
+# must exist for deploy-kamatera.sh. This driver runs the scripts AS-IS from
+# ~/Projects/homesonspec (where they physically live). If ~/Projects/spechomes
+# no longer exists, choose --relaunch (golive-homesonspec.sh, which also uses
+# the spechomes source path) or fix the paths in the deploy script first.
+#
+# ── FLAGS ───────────────────────────────────────────────────────────────────
+# --relaunch use golive-homesonspec.sh (demo-purge relaunch on an
+# already-seeded prod DB) instead of deploy-kamatera.sh
+# (first-time full stand-up). Default: first-time stand-up.
+# --skip-deploy run gate + Cloudflare + revoke-notice only; skip step (c).
+# --skip-cf skip step (b) (assume CF already fully cut over).
+# -h | --help show this header.
+# ============================================================================
+set -euo pipefail
+
+DOMAIN=homesonspec.com
+ORIGIN_IP=45.61.58.125
+WEB_PORT=9975
+ADMIN_PORT=9976
+CF_NS1=ezra.ns.cloudflare.com
+CF_NS2=kimora.ns.cloudflare.com
+PROJ_DIR="$HOME/Projects/homesonspec"
+CUTOVER="$PROJ_DIR/cutover-dns.sh"
+DEPLOY="$PROJ_DIR/deploy-kamatera.sh"
+RELAUNCH_SCRIPT="$PROJ_DIR/golive-homesonspec.sh"
+
+RELAUNCH=0
+SKIP_DEPLOY=0
+SKIP_CF=0
+for arg in "$@"; do
+ case "$arg" in
+ --relaunch) RELAUNCH=1 ;;
+ --skip-deploy) SKIP_DEPLOY=1 ;;
+ --skip-cf) SKIP_CF=1 ;;
+ -h|--help) sed -n '2,60p' "$0"; exit 0 ;;
+ *) echo "unknown flag: $arg (see --help)"; exit 64 ;;
+ esac
+done
+
+hr() { printf '────────────────────────────────────────────────────────────\n'; }
+say() { printf '\n\033[1m%s\033[0m\n' "$*"; }
+
+# ── (a) GATE on nameserver propagation ──────────────────────────────────────
+say "(a) GATE — checking homesonspec.com nameservers"
+hr
+CUR_NS=$(dig +short NS "$DOMAIN" | sed 's/\.$//' | sort | tr '\n' ' ' | sed 's/ *$//')
+echo " dig NS $DOMAIN → ${CUR_NS:-<empty>}"
+if echo "$CUR_NS" | grep -qi "$CF_NS1" && echo "$CUR_NS" | grep -qi "$CF_NS2"; then
+ echo " ✓ Cloudflare nameservers are live — the GoDaddy swap is done. Continuing."
+else
+ cat <<GATE
+
+ ✗ NOT YET on Cloudflare — the go-live is blocked on ONE manual step.
+
+ ┌──────────────────────────────────────────────────────────────────┐
+ │ DO THIS ONE THING (GoDaddy dashboard): │
+ │ │
+ │ homesonspec.com → Nameservers → Change → Enter my own │
+ │ set: $CF_NS1
+ │ $CF_NS2
+ │ (was: ns69.domaincontrol.com + ns70.domaincontrol.com) │
+ │ │
+ │ Then re-run: bash ~/Projects/homesonspec/finish-tk10.sh │
+ └──────────────────────────────────────────────────────────────────┘
+
+ (GoDaddy's NS-swap write API returns 404 for this domain, so this step
+ cannot be automated. Propagation after you save takes ~5–30 min — if the
+ dashboard already shows the CF nameservers, just wait a few minutes and
+ re-run this script.)
+GATE
+ exit 2
+fi
+
+# ── (b) CLOUDFLARE half (API-driven, reversible, idempotent) ────────────────
+if [ "$SKIP_CF" -eq 1 ]; then
+ say "(b) CLOUDFLARE — skipped (--skip-cf)"
+else
+ say "(b) CLOUDFLARE — ensuring zone / proxied A / email / SSL (cutover-dns.sh)"
+ hr
+ if [ ! -f "$CUTOVER" ]; then
+ echo " ✗ $CUTOVER not found — cannot run the Cloudflare half. ABORT."; exit 1
+ fi
+ # cutover-dns.sh is idempotent: upserts records, sets SSL=full/always_use_https,
+ # re-writes ROLLBACK-DNS.txt, and (harmlessly) no-ops on the 404-gated GoDaddy PUT.
+ bash "$CUTOVER" || {
+ echo " ⚠ cutover-dns.sh returned non-zero (the 404-gated GoDaddy NS PUT is expected"
+ echo " to fail; the Cloudflare record/SSL upserts above it are what matter). Review"
+ echo " the output above — if only the GoDaddy PUT failed, this is fine."; }
+fi
+
+# ── (c) DEPLOY + smoke test ─────────────────────────────────────────────────
+if [ "$SKIP_DEPLOY" -eq 1 ]; then
+ say "(c) DEPLOY — skipped (--skip-deploy)"
+else
+ if [ "$RELAUNCH" -eq 1 ]; then
+ DEPLOY_SCRIPT="$RELAUNCH_SCRIPT"; DEPLOY_KIND="re-launch (demo-purge on seeded prod DB)"
+ else
+ DEPLOY_SCRIPT="$DEPLOY"; DEPLOY_KIND="first-time full stand-up"
+ fi
+ say "(c) DEPLOY — $DEPLOY_KIND → Kamatera ($ORIGIN_IP)"
+ hr
+ if [ ! -f "$DEPLOY_SCRIPT" ]; then
+ echo " ✗ $DEPLOY_SCRIPT not found. ABORT."; exit 1
+ fi
+ echo " running: bash $DEPLOY_SCRIPT"
+ echo " (SSHes to root@$ORIGIN_IP — requires a Kamatera-SSH-capable session)"
+ bash "$DEPLOY_SCRIPT"
+
+ say " smoke test"
+ hr
+ # Apex through Cloudflare edge (proxied). Allow a few tries for edge warm-up.
+ APEX=000
+ for i in 1 2 3 4 5; do
+ APEX=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "https://$DOMAIN/" || echo 000)
+ [ "$APEX" = "200" ] && break
+ echo " apex try $i → $APEX (retrying in 5s)"; sleep 5
+ done
+ echo -n " apex https://$DOMAIN/ → $APEX"; [ "$APEX" = "200" ] && echo " ✓" || echo " ✗ (edge/cert may still be warming — recheck in a few min)"
+
+ # Admin should fail closed (401) unauthenticated. Admin is origin-only (not in
+ # public DNS), so probe the origin directly via the Host header.
+ ADMINCODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "http://$ORIGIN_IP:$ADMIN_PORT/" || echo 000)
+ echo -n " admin origin :$ADMIN_PORT (401 expected, fails closed) → $ADMINCODE"
+ [ "$ADMINCODE" = "401" ] && echo " ✓" || echo " — (verify: unauthenticated admin must be 401)"
+fi
+
+# ── (d) REVOKE the old GoDaddy key (PRINT ONLY — Steve runs this by hand) ────
+say "(d) FINAL MANUAL STEP — revoke the old GoDaddy sso-key"
+hr
+cat <<'REVOKE'
+ The leaked-then-scrubbed GoDaddy sso-key is git-clean but STILL LIVE at
+ GoDaddy until you revoke it. Every live consumer already prefers the new
+ Bearer PAT (verified under TK-10), so revoking is safe.
+
+ DO THIS (by hand — NOT automated):
+ 1. Go to https://developer.godaddy.com/keys
+ 2. REVOKE the old key (fleet-wide key, sha-prefix 96974d0c).
+ 3. If you also created a fresh key, run: /secrets
+ to fan it across all .env / MCP / skill envs.
+ 4. Delete the pre-scrub backup that still holds old secrets:
+ rm -rf ~/Projects/spechomes.pre-scrub-backup
+
+ ROLLBACK (if the launch needs undoing):
+ GoDaddy dashboard → homesonspec.com → Nameservers → set back to
+ ns69.domaincontrol.com + ns70.domaincontrol.com (manual — the API PUT is
+ 404-gated). If a demo-purge relaunch ran, restore the pg_dump the
+ golive script printed.
+REVOKE
+
+say "✅ finish-tk10.sh complete — homesonspec.com is live once the smoke checks are green."
← 887b38f auto-save: 2026-07-28T06:58:06 (2 files) — collectors/discov
·
back to Homesonspec
·
fix: david-weekley stories/garages must be integer (1.5-stor 40750c0 →