Something went wrong. Try again.
A community based topic aggregation platform built on atproto
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169#!/usr/bin/env bash# Seeds the CI stack: registers both PDSes with the relay, then creates the# instance account and two moderation admin accounts before the AppView boots.## Both halves must happen after the infrastructure is healthy and before the# AppView boots, which is why they share a stage in scripts/lib/ci-stack.sh# rather than being two `compose run` invocations.## WHY THE PDS ACCOUNT STEP EXISTS## The AppView writes community records to the PDS as PDS_INSTANCE_HANDLE (see# PDSConfig.HasInstanceCredentials in internal/config). The dev stack's PDS# volume persists, so that account was created once — by hand, or by a# scripts/create-test-account.sh that no longer exists in the tree — and has# been there ever since.## CI starts from an empty PDS on every run, so the account has to be created# each time, and it has to exist before the AppView boots. That ordering is why# this is a separate stage in scripts/ci.sh rather than part of ci-runner.sh.## This is exactly the class of hidden dependency on accumulated dev-stack state# that a hermetic gate is supposed to surface.set -euo pipefail
PDS_URL=${PDS_URL:-http://localhost:3001}PDS2_URL=${PDS2_URL:-http://localhost:3011}RELAY_URL=${RELAY_URL:-http://localhost:2470}RELAY_ADMIN_KEY=${RELAY_ADMIN_KEY:-ci-relay-admin-key}HANDLE=${PDS_INSTANCE_HANDLE:?PDS_INSTANCE_HANDLE must be set (see .env.ci)}PASSWORD=${PDS_INSTANCE_PASSWORD:?PDS_INSTANCE_PASSWORD must be set (see .env.ci)}EMAIL=${COVES_CI_INSTANCE_EMAIL:-instance@local.coves.dev}ADMIN_ONE_HANDLE=${CI_MODERATION_ADMIN_ONE_HANDLE:?CI_MODERATION_ADMIN_ONE_HANDLE must be set (see .env.ci)}ADMIN_ONE_PASSWORD=${CI_MODERATION_ADMIN_ONE_PASSWORD:?CI_MODERATION_ADMIN_ONE_PASSWORD must be set (see .env.ci)}ADMIN_TWO_HANDLE=${CI_MODERATION_ADMIN_TWO_HANDLE:?CI_MODERATION_ADMIN_TWO_HANDLE must be set (see .env.ci)}ADMIN_TWO_PASSWORD=${CI_MODERATION_ADMIN_TWO_PASSWORD:?CI_MODERATION_ADMIN_TWO_PASSWORD must be set (see .env.ci)}CI_PROJECT=${COVES_CI_PROJECT:?COVES_CI_PROJECT must be set by the CI runner}
# ---------------------------------------------------------------------------# The relay: raise the crawl limit, then announce both PDSes# ---------------------------------------------------------------------------# A FRESH BigSky refuses every non-admin requestCrawl. Its slurper config# starts with new_pds_per_day_limit = 0 and that limiter is checked BEFORE the# trusted-domain list, so there is no configuration-only way around it: the# admin API has to raise it first. The failure without this step is a 401 from# requestCrawl, which reads like an auth problem with the announcement rather# than a limit on the relay.echo "▶ Raising the relay's new-host-per-day limit..."limit_code=$( curl -sS -o /tmp/setPerDayLimit.out -w '%{http_code}' \ -X POST "$RELAY_URL/admin/subs/setPerDayLimit?limit=1000" \ -H "Authorization: Bearer $RELAY_ADMIN_KEY")if [[ $limit_code != 200 ]]; then echo " ✗ relay refused the admin limit change (HTTP $limit_code): $(cat /tmp/setPerDayLimit.out)" >&2 exit 1fiecho " ✓ per-day host limit raised"
# requestCrawl is how a PDS tells a relay it exists. The relay validates the# hostname by calling back into that host's com.atproto.server.describeServer# (over http, because of --crawl-insecure-ws) and only then subscribes to its# firehose. A hostname MAY carry a port — BigSky keeps `u.Host` verbatim and# special-cases a `localhost:` prefix back to http when it later resolves DID# documents — which is what makes the shared-namespace topology work at all.## Idempotent: re-announcing a host the relay already has is a no-op, so a# COVES_CI_KEEP_STACK re-run needs no special case.announce_host() { local label=$1 hostport=$2 code echo "▶ Announcing $label ($hostport) to the relay..." code=$( curl -sS -o /tmp/requestCrawl.out -w '%{http_code}' \ -X POST "$RELAY_URL/xrpc/com.atproto.sync.requestCrawl" \ -H 'Content-Type: application/json' \ -d "{\"hostname\":\"$hostport\"}" ) if [[ $code != 200 ]]; then echo " ✗ relay refused to crawl $hostport (HTTP $code): $(cat /tmp/requestCrawl.out)" >&2 exit 1 fi echo " ✓ crawling $hostport"}
announce_host "the AppView's PDS" "${PDS_URL#http://}"announce_host "the federated PDS" "${PDS2_URL#http://}"
# ---------------------------------------------------------------------------# PDS accounts# ---------------------------------------------------------------------------provision_account() { local label=$1 handle=$2 email=$3 password=$4 response body session_code account_did echo "▶ Creating the $label PDS account ($handle)..."
# The PDS runs with PDS_INVITE_REQUIRED=false, so no invite code is needed. response=$( curl -sS -o /tmp/createAccount.out -w '%{http_code}' \ -X POST "$PDS_URL/xrpc/com.atproto.server.createAccount" \ -H 'Content-Type: application/json' \ -d "{\"handle\":\"$handle\",\"email\":\"$email\",\"password\":\"$password\"}" ) body=$(cat /tmp/createAccount.out)
case "$response" in 200 | 201) echo " ✓ created" ;; 400) # A kept stack can already have this handle. Any other 400 is a real # configuration error, not a successful retry. if grep -qi 'handle.*taken\|already.*exists\|AlreadyExists' <<<"$body"; then echo " ✓ already exists (reusing)" else echo " ✗ PDS rejected the account creation: $body" >&2 return 1 fi ;; *) echo " ✗ unexpected HTTP $response from the PDS: $body" >&2 return 1 ;; esac
# Creating an account and authenticating as it are different claims. echo "▶ Verifying the $label credentials authenticate..." session_code=$( curl -sS -o /tmp/createSession.out -w '%{http_code}' \ -X POST "$PDS_URL/xrpc/com.atproto.server.createSession" \ -H 'Content-Type: application/json' \ -d "{\"identifier\":\"$handle\",\"password\":\"$password\"}" ) if [[ $session_code != 200 ]]; then echo " ✗ could not authenticate as $handle (HTTP $session_code): $(cat /tmp/createSession.out)" >&2 return 1 fi echo " ✓ credentials valid"
# The runner has GNU grep but no jq. Do not print the createSession body: # it contains access and refresh tokens alongside the DID. if ! account_did=$(grep -oE '"did"[[:space:]]*:[[:space:]]*"did:[a-z]+:[^"]+"' /tmp/createSession.out | cut -d '"' -f 4) || [[ ! $account_did =~ ^did:[a-z]+:[a-zA-Z0-9._:%-]+$ ]]; then echo " ✗ PDS session for $handle did not contain a valid DID" >&2 return 1 fi PROVISIONED_DID=$account_did}
provision_account "instance" "$HANDLE" "$EMAIL" "$PASSWORD"provision_account "moderation admin one" "$ADMIN_ONE_HANDLE" "${CI_MODERATION_ADMIN_ONE_EMAIL:-modadmin-one@local.coves.dev}" "$ADMIN_ONE_PASSWORD"admin_one_did=$PROVISIONED_DIDprovision_account "moderation admin two" "$ADMIN_TWO_HANDLE" "${CI_MODERATION_ADMIN_TWO_EMAIL:-modadmin-two@local.coves.dev}" "$ADMIN_TWO_PASSWORD"admin_two_did=$PROVISIONED_DID
if [[ -z $admin_one_did || -z $admin_two_did || $admin_one_did == "$admin_two_did" ]]; then echo " ✗ moderation admin accounts must have distinct, non-empty DIDs" >&2 exit 1fi
admins_dir=/src/.ci-outadmins_file="$admins_dir/moderation-admins-${CI_PROJECT}.env"mkdir -p "$admins_dir"admins_tmp=$(mktemp "$admins_file.tmp.XXXXXX")trap 'rm -f "$admins_tmp"' EXITprintf 'MODERATION_ADMINS=%s,%s\n' "$admin_one_did" "$admin_two_did" >"$admins_tmp"# The runner writes as root, but host-side Compose must be able to read it.chmod 644 "$admins_tmp"mv "$admins_tmp" "$admins_file"trap - EXITecho " ✓ moderation admin DIDs saved for the AppView"