Files
openlaunch/app/scripts/fly-secrets.sh
Kevin Codex 3d8ce76840 fix(review 2): metadata key ownership, independent deploy secrets, locked repair scripts
- Metadata ownership is per (chain, launcher, meta_key), the thing the public URI is keyed by:
  the first registration under a key fixes its content; later rows under the same key must match
  exactly (the salt search) or get 409. Serialized with a transaction-scoped advisory lock so two
  concurrent first writes cannot both win (tested: 10/10 races → one 200, one 409). Reads prefer the
  launched row, then the FIRST registration, never a newer one.
- fly-secrets.sh reads every key independently from the secrets file (no RPC derivation), fails on
  missing required keys, supports --dry-run; verified against mocked files.
- rebuild-launch-totals.sql / rebuild-holders.sql run in one transaction holding SHARE locks on the
  raw event tables and EXCLUSIVE on the target table, so ingestion waits instead of racing (tested:
  an ingestion started mid-rebuild blocked 3s, then applied on top; totals consistent).
2026-09-07 12:51:08 +08:00

29 lines
2.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Stage runtime secrets on Fly for this app, reading EVERY value independently from one env file
# (default ./.env.production — see .env.example for the keys; never committed, never printed).
# DATABASE_URL is set by `fly postgres attach`, the image bucket by `fly storage create` — not here.
# Staged secrets apply on the next `fly deploy`. Re-runnable.
# FLY_APP = Fly app name (default: basebid — the live openlaunch.lol app)
# SECRETS_ENV = file to read (default: .env.production)
# --dry-run = print what would be staged, do not call fly
set -euo pipefail
cd "$(dirname "$0")/.."
APP=${FLY_APP:-basebid}
FILE=${SECRETS_ENV:-.env.production}
DRY=0; [[ "${1:-}" == "--dry-run" ]] && DRY=1
[ -f "$FILE" ] || { echo "missing $FILE — copy .env.example to $FILE and fill in the production values"; exit 1; }
val() { { grep -E "^$1=" "$FILE" || true; } | head -1 | cut -d= -f2- | sed 's/^"//;s/"$//'; }
REQUIRED=(BASE_RPC_URL ROBINHOOD_RPC_URL NEXT_PUBLIC_SITE_URL NEXT_PUBLIC_LAUNCH_FACTORY NEXT_PUBLIC_LAUNCH_LOCKER LAUNCH_DEPLOY_BLOCK NEXT_PUBLIC_LAUNCH_FACTORY_ROBINHOOD NEXT_PUBLIC_LAUNCH_LOCKER_ROBINHOOD LAUNCH_DEPLOY_BLOCK_ROBINHOOD)
OPTIONAL=(BASE_B20_RPC_URL LAUNCH_SYNC_CONFIRMATIONS LAUNCH_SYNC_LOOP ADMIN_WALLETS IMAGE_PUBLIC_BASE)
args=(); missing=()
for k in "${REQUIRED[@]}"; do v=$(val "$k"); if [ -n "$v" ]; then args+=("$k=$v"); echo " staged $k"; else missing+=("$k"); fi; done
for k in "${OPTIONAL[@]}"; do v=$(val "$k"); if [ -n "$v" ]; then args+=("$k=$v"); echo " staged $k"; else echo " skip $k (not set)"; fi; done
if [ ${#missing[@]} -gt 0 ]; then echo "missing required keys in $FILE: ${missing[*]}"; exit 1; fi
if [ $DRY -eq 1 ]; then echo "dry run: ${#args[@]} secrets would be staged on app '$APP' (values not shown)"; exit 0; fi
fly secrets set -a "$APP" --stage "${args[@]}" >/dev/null
echo "done: ${#args[@]} secrets staged on '$APP'. NEXT_PUBLIC_LAUNCH_* are also BUILD args (see the fly.toml header)."