declarative relay deployment on hetzner relay-eval.waow.tech
atproto relay
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

add zlay reconnect cronjob

every 4 hours, feeds PDS hosts from mary-ext/atproto-scraping into
zlay's requestCrawl endpoint. same pattern as the Go relay cronjob
but uses zlay's unauthenticated XRPC endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

zzstoatzz a9061036 8590edd1

+65
+65
deploy/zlay-reconnect-cronjob.yaml
··· 1 + apiVersion: batch/v1 2 + kind: CronJob 3 + metadata: 4 + name: zlay-reconnect 5 + namespace: zlay 6 + spec: 7 + schedule: "0 */4 * * *" # every 4 hours 8 + concurrencyPolicy: Forbid 9 + successfulJobsHistoryLimit: 3 10 + failedJobsHistoryLimit: 3 11 + jobTemplate: 12 + spec: 13 + backoffLimit: 1 14 + activeDeadlineSeconds: 1800 # 30 min max 15 + template: 16 + spec: 17 + restartPolicy: Never 18 + containers: 19 + - name: reconnect 20 + image: python:3.12-alpine 21 + env: 22 + - name: PYTHONUNBUFFERED 23 + value: "1" 24 + command: 25 + - python3 26 + - -c 27 + - | 28 + import json, urllib.request, time, sys 29 + 30 + PDS_LIST_URL = "https://raw.githubusercontent.com/mary-ext/atproto-scraping/refs/heads/trunk/state.json" 31 + ZLAY_URL = "http://zlay.zlay.svc.cluster.local:3001" 32 + 33 + print(f"fetching PDS list from {PDS_LIST_URL}...") 34 + with urllib.request.urlopen(PDS_LIST_URL, timeout=30) as resp: 35 + data = json.loads(resp.read()) 36 + hosts = [url.rstrip("/") for url in data.get("pdses", {}).keys() if url.startswith("https://")] 37 + print(f"found {len(hosts)} PDS hosts") 38 + 39 + ok = errors = 0 40 + start = time.time() 41 + 42 + for i, host in enumerate(hosts): 43 + # strip scheme — zlay's requestCrawl expects bare hostname 44 + hostname = host.replace("https://", "").replace("http://", "") 45 + payload = json.dumps({"hostname": hostname}).encode() 46 + req = urllib.request.Request( 47 + f"{ZLAY_URL}/xrpc/com.atproto.sync.requestCrawl", 48 + data=payload, 49 + headers={"Content-Type": "application/json"}, 50 + method="POST", 51 + ) 52 + try: 53 + with urllib.request.urlopen(req, timeout=10) as resp: 54 + ok += 1 55 + except urllib.error.HTTPError: 56 + errors += 1 57 + except (ConnectionError, OSError, urllib.error.URLError): 58 + errors += 1 59 + 60 + if (i + 1) % 500 == 0: 61 + print(f" {i + 1}/{len(hosts)} ({ok} ok, {errors} errors, {time.time() - start:.0f}s)") 62 + 63 + time.sleep(0.05) 64 + 65 + print(f"done: {ok} ok, {errors} errors, {time.time() - start:.0f}s")