diff --git a/deploy/scripts/register-challenge-backends.sh b/deploy/scripts/register-challenge-backends.sh index fc8074e69..d3d2e14a3 100755 --- a/deploy/scripts/register-challenge-backends.sh +++ b/deploy/scripts/register-challenge-backends.sh @@ -52,9 +52,12 @@ register_one() { local payload http body token local -a auth=() token="$(resolve_admin_token || true)" - if [[ -n "${token}" ]]; then - auth=(-H "Authorization: Bearer ${token}") + if [[ -z "${token}" ]]; then + echo "register-challenge-backends: missing gateway admin token" >&2 + echo " set BASE_GATEWAY_ADMIN_TOKEN or deploy/secrets/gateway_admin_token" >&2 + return 1 fi + auth=(-H "Authorization: Bearer ${token}") payload="$(printf '{"challenge_id":"%s","base_url":"%s","weight":1}' "$challenge_id" "$base_url")" if [[ "$COMPOSE_MODE" -eq 1 ]]; then body="$(docker compose -f docker-compose.yml \ @@ -62,12 +65,12 @@ register_one() { exec -T gateway \ curl -sS -w '\n%{http_code}' -X POST http://127.0.0.1:8080/v1/admin/backends \ -H 'content-type: application/json' \ - ${auth[@]+"${auth[@]}"} \ + "${auth[@]}" \ -d "$payload" 2>/dev/null || true)" else body="$(curl -sS -w '\n%{http_code}' -X POST "${GATEWAY_URL%/}/v1/admin/backends" \ -H 'content-type: application/json' \ - ${auth[@]+"${auth[@]}"} \ + "${auth[@]}" \ -d "$payload" 2>/dev/null || true)" fi http="$(printf '%s' "$body" | tail -n1)" diff --git a/deploy/scripts/remote-deploy.sh b/deploy/scripts/remote-deploy.sh index 0dbb25aed..bc17f0d99 100755 --- a/deploy/scripts/remote-deploy.sh +++ b/deploy/scripts/remote-deploy.sh @@ -442,11 +442,49 @@ if [[ '$ROLE' == 'master' ]]; then # The gateway races this script on boot, so retry until registration sticks, # then prove proxy routing end-to-end: a missed reseed leaves /challenge/* # at 503 while /healthz stays green. Both must fail the deploy loudly. + # Gateway /v1/admin/* requires Authorization: Bearer (gateway_admin_token). echo "remote-deploy: registering challenge backends" reseed_ok=0 for attempt in \$(seq 1 15); do if python3 - <<'PY' -import json, sys, urllib.request, urllib.error +import json, os, sys, urllib.error, urllib.request +from pathlib import Path + +def resolve_admin_token() -> str: + token = (os.environ.get("BASE_GATEWAY_ADMIN_TOKEN") or "").strip() + if token: + return token + candidates = [] + env_file = (os.environ.get("BASE_GATEWAY_ADMIN_TOKEN_FILE") or "").strip() + if env_file: + candidates.append(Path(env_file)) + # remote-deploy cds to REMOTE_DIR (/opt/base); secrets live beside the tree. + candidates.extend( + [ + Path("deploy/secrets/gateway_admin_token"), + Path("/opt/base/deploy/secrets/gateway_admin_token"), + ] + ) + for path in candidates: + if path.is_file(): + token = path.read_text(encoding="utf-8").strip() + if token: + return token + return "" + +token = resolve_admin_token() +if not token: + print( + "ERROR: gateway admin token missing " + "(set BASE_GATEWAY_ADMIN_TOKEN or deploy/secrets/gateway_admin_token)", + flush=True, + ) + sys.exit(1) + +headers = { + "content-type": "application/json", + "Authorization": f"Bearer {token}", +} backends = [ ("prism", "http://prism-challenge:8092"), ("design", "http://design-challenge:8093"), @@ -457,7 +495,7 @@ for cid, url in backends: req = urllib.request.Request( "http://127.0.0.1:8080/v1/admin/backends", data=payload, - headers={"content-type": "application/json"}, + headers=headers, method="POST", ) try: diff --git a/docs/runbooks/staging-testnet-e2e.md b/docs/runbooks/staging-testnet-e2e.md index 8e14b35d1..c257c0bf1 100644 --- a/docs/runbooks/staging-testnet-e2e.md +++ b/docs/runbooks/staging-testnet-e2e.md @@ -55,16 +55,20 @@ are registered. `remote-deploy.sh` (master) re-seeds automatically; to do it by hand: ```bash -# From this repo (against a reachable gateway): +# From this repo (against a reachable gateway; reads deploy/secrets/gateway_admin_token): GATEWAY_URL=http://staging.api.joinbase.ai ./deploy/scripts/register-challenge-backends.sh -# Or on the droplet: +# Or on the droplet (admin bearer required after #100): +TOKEN=$(tr -d '[:space:]'