diff --git a/docs/development-oauth-tunnel.md b/docs/development-oauth-tunnel.md index 82f7254..090facd 100644 --- a/docs/development-oauth-tunnel.md +++ b/docs/development-oauth-tunnel.md @@ -49,6 +49,11 @@ pnpm tunnel:logs pnpm tunnel:verify ``` +`pnpm tunnel:verify` checks that the public metadata is reachable, that it +uses the active stable origin for `client_id`, `redirect_uris`, and +`client_uri`, that DPoP is enabled, and that latest-play XRPC returns a JSON +`plays` array. + Confirm the OAuth client metadata is served from the stable host: ```bash diff --git a/scripts/dev-tunnel.sh b/scripts/dev-tunnel.sh index a036c55..8fe0f18 100755 --- a/scripts/dev-tunnel.sh +++ b/scripts/dev-tunnel.sh @@ -83,11 +83,58 @@ curl_preview() { curl --fail --show-error --silent --resolve "$TUNNEL_HOST:443:$ip" "$url" >/dev/null } +fetch_preview() { + local url="$1" + + if curl --fail --show-error --silent "$url"; then + return 0 + fi + + local ip + ip="$(dig +short "$TUNNEL_HOST" @1.1.1.1 | grep -E '^[0-9.]+$' | head -n 1 || true)" + if [[ -z "$ip" ]]; then + echo "Could not resolve $TUNNEL_HOST with the local resolver or Cloudflare DNS." >&2 + return 1 + fi + + echo "Local DNS has not caught up for $TUNNEL_HOST; retrying verification through $ip." >&2 + curl --fail --show-error --silent --resolve "$TUNNEL_HOST:443:$ip" "$url" +} + verify_preview() { echo "Verifying client metadata..." - curl_preview "$PUBLIC_ORIGIN/client-metadata.json" + fetch_preview "$PUBLIC_ORIGIN/client-metadata.json" | python3 -c ' +import json +import sys + +origin = sys.argv[1] +metadata = json.load(sys.stdin) +expected_client_id = f"{origin}/client-metadata.json" +expected_redirect_uri = f"{origin}/auth/callback" +actual_client_id = metadata.get("client_id") +actual_client_uri = metadata.get("client_uri") + +if actual_client_id != expected_client_id: + raise SystemExit(f"client_id mismatch: expected {expected_client_id!r}, got {actual_client_id!r}") +if expected_redirect_uri not in metadata.get("redirect_uris", []): + raise SystemExit(f"redirect_uris missing {expected_redirect_uri!r}") +if actual_client_uri != origin: + raise SystemExit(f"client_uri mismatch: expected {origin!r}, got {actual_client_uri!r}") +if metadata.get("token_endpoint_auth_method") != "none": + raise SystemExit("token_endpoint_auth_method must be none") +if metadata.get("dpop_bound_access_tokens") is not True: + raise SystemExit("dpop_bound_access_tokens must be true") +' "$PUBLIC_ORIGIN" echo "Verifying latest plays..." - curl_preview "$PUBLIC_ORIGIN/xrpc/fm.teal.alpha.stats.getLatest?limit=1" + fetch_preview "$PUBLIC_ORIGIN/xrpc/fm.teal.alpha.stats.getLatest?limit=1" | python3 -c ' +import json +import sys + +payload = json.load(sys.stdin) +plays = payload.get("plays") +if not isinstance(plays, list): + raise SystemExit("latest plays response must contain a plays array") +' } case "${1:-}" in diff --git a/todo.md b/todo.md index 4078f9a..5ab70a0 100644 --- a/todo.md +++ b/todo.md @@ -15,7 +15,7 @@ Last synced with GitHub and Linear issues: 2026-06-14. ## Local Open Work - [ ] Complete ATProto OAuth sign-in and callback QA through `https://sigilyph.teal.fm`. - - Verified again on 2026-06-15 that `https://sigilyph.teal.fm/client-metadata.json` serves the stable-origin `client_id` and callback URI, `pnpm tunnel:verify` passes, and latest plays XRPC returns live data. Remaining QA requires an interactive ATProto login/callback with a real account session. + - Verified again on 2026-06-15 that `pnpm tunnel:verify` validates the stable-origin `client_id`, callback URI, `client_uri`, DPoP setting, and latest plays XRPC response. Remaining QA requires an interactive ATProto login/callback with a real account session. - [x] Drain the in-flight CAR import backfill queue for users with stale ingestion from the 2026-06-07 through 2026-06-10 Cadet outage. Redis/Garnet `LLEN car_import_jobs` returned `0` on 2026-06-14, local Cadet was running, and recent Cadet logs showed no CAR import job failures. - [x] Backfill the `fm.teal.alpha.feed.play` records present in `did:plc:tas6hj2xjrqben5653v5kohk`'s PDS repo but missing from the preview Postgres index. A focused CAR backfill completed on 2026-06-15 via `lightrail-backfill`; the preview index now has 10,215 plays for that DID, up from 10,172 immediately before the run and above the older 10,193-record comparison from 2026-06-11. - [x] Handle Jetstream account lifecycle events in Cadet, including deletes, takedowns, suspensions, activations, and tombstones. Cadet now tracks upstream account state, purges indexed public profile/social/play rows when an account becomes inactive, treats activation as the gate for future commit ingestion, and ignores legacy tombstone event kinds because modern Jetstream/account-hosting statuses replace them.