k8s/cf-zones: adopt zoo.network, zoo.exchange, zoo.ngo DNS zone YAMLs
Coordination with the Lux universe DX cleanup — Cloudflare zone YAMLs
are pure brand assets and belong with their owning universe. Moved
2026-06-03 from lux/universe/k8s/dns/{zoo-exchange,zoo-network,zoo-ngo}.yaml.
k8s/cf-zones/sync.sh is the per-brand drift-detect-and-PATCH script
(same logic as the legacy lux/universe scripts/dns-sync.sh, scoped to
this directory). Dry-run by default; set CF_API_TOKEN and pass --apply
to push changes.
Single-zone usage:
./k8s/cf-zones/sync.sh --zone zoo-ngo
This commit is contained in:
Executable
+148
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# infra/cf-zones/sync.sh — Zoo brand Cloudflare zone sync.
|
||||
#
|
||||
# Mirrors the per-zone YAML files in this directory into the matching
|
||||
# Cloudflare zone via the CF API. Skips proxied records (managed via
|
||||
# the Cloudflare UI). Only updates records that have drifted from the
|
||||
# YAML source-of-truth.
|
||||
#
|
||||
# Usage:
|
||||
# ./sync.sh # Dry run (show what would change)
|
||||
# ./sync.sh --apply # Apply changes to Cloudflare
|
||||
# ./sync.sh --zone hanzo-id # Sync a single zone (filename stem)
|
||||
#
|
||||
# Prerequisites:
|
||||
# CF_API_TOKEN env var (Cloudflare API token with DNS edit permission)
|
||||
# python3 with stdlib only (no PyYAML — uses inline parser)
|
||||
#
|
||||
# Moved 2026-06-03 from lux/universe/scripts/dns-sync.sh as part of
|
||||
# brand-boundary cleanup. Lux universe keeps its own copy that only
|
||||
# reads lux-*.yaml; per-brand zones are owned by per-brand universes.
|
||||
set -euo pipefail
|
||||
|
||||
DNS_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
DRY_RUN=true
|
||||
SINGLE_ZONE=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--apply) DRY_RUN=false ;;
|
||||
--zone=*) SINGLE_ZONE="${arg#*=}" ;;
|
||||
--zone) shift; SINGLE_ZONE="${1:-}" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
CF_TOKEN="${CF_API_TOKEN:-${CLOUDFLARE_DNS_TOKEN:-}}"
|
||||
if [ -z "$CF_TOKEN" ] && [ "$DRY_RUN" = false ]; then
|
||||
echo "ERROR: CF_API_TOKEN or CLOUDFLARE_DNS_TOKEN required for --apply"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Zoo CF Zone Sync $([ "$DRY_RUN" = true ] && echo '(DRY RUN)' || echo '(APPLYING)') ==="
|
||||
echo "Zone dir: $DNS_DIR"
|
||||
echo ""
|
||||
|
||||
for zone_file in "$DNS_DIR"/*.yaml; do
|
||||
zone_name=$(basename "$zone_file" .yaml)
|
||||
|
||||
if [ -n "$SINGLE_ZONE" ] && [ "$zone_name" != "$SINGLE_ZONE" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
python3 - "$zone_file" "$CF_TOKEN" "$DRY_RUN" <<'PY'
|
||||
import json, os, subprocess, sys, urllib.request
|
||||
|
||||
zone_file = sys.argv[1]
|
||||
token = sys.argv[2]
|
||||
dry_run = sys.argv[3] == "true"
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
sys.stderr.write("pyyaml not installed; install with: pip install pyyaml\n")
|
||||
sys.exit(2)
|
||||
|
||||
with open(zone_file) as f:
|
||||
d = yaml.safe_load(f) or {}
|
||||
|
||||
zone = d.get("zone", "")
|
||||
zone_id = d.get("zone_id", "")
|
||||
records = d.get("records", [])
|
||||
|
||||
if not zone or not zone_id:
|
||||
print(f"SKIP: {zone_file} (no zone/zone_id)")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"--- {zone} (ID: {zone_id[:8]}...) ---")
|
||||
|
||||
checked = drifted = updated = 0
|
||||
for r in records:
|
||||
rtype = r.get("type", "")
|
||||
name = r.get("name", "")
|
||||
content = r.get("content", "")
|
||||
proxied = r.get("proxied", False)
|
||||
|
||||
if rtype not in ("A", "CNAME"):
|
||||
continue
|
||||
if name == "@":
|
||||
continue
|
||||
if proxied:
|
||||
continue
|
||||
|
||||
fqdn = f"{name}.{zone}" if name != zone else zone
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["dig", "+short", fqdn, "@1.1.1.1"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
)
|
||||
live = result.stdout.strip().split("\n")[0] if result.stdout.strip() else ""
|
||||
except Exception:
|
||||
live = ""
|
||||
|
||||
checked += 1
|
||||
if live == content:
|
||||
continue
|
||||
|
||||
drifted += 1
|
||||
print(f" DRIFT: {fqdn} live={live or '(none)'} yaml={content}")
|
||||
|
||||
if dry_run or not token:
|
||||
continue
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
||||
url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records?type={rtype}&name={fqdn}"
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
cf_data = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||
cf_records = cf_data.get("result", [])
|
||||
if cf_records:
|
||||
rec_id = cf_records[0]["id"]
|
||||
patch_url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec_id}"
|
||||
patch_data = json.dumps({"content": content, "proxied": proxied}).encode()
|
||||
patch_req = urllib.request.Request(patch_url, data=patch_data, headers=headers, method="PATCH")
|
||||
patch_result = json.loads(urllib.request.urlopen(patch_req, timeout=10).read())
|
||||
if patch_result.get("success"):
|
||||
print(f" UPDATED: {fqdn} -> {content}")
|
||||
updated += 1
|
||||
else:
|
||||
print(f" FAILED: {patch_result.get('errors')}")
|
||||
else:
|
||||
create_url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records"
|
||||
create_data = json.dumps({"type": rtype, "name": fqdn, "content": content, "proxied": proxied}).encode()
|
||||
create_req = urllib.request.Request(create_url, data=create_data, headers=headers, method="POST")
|
||||
create_result = json.loads(urllib.request.urlopen(create_req, timeout=10).read())
|
||||
if create_result.get("success"):
|
||||
print(f" CREATED: {fqdn} -> {content}")
|
||||
updated += 1
|
||||
else:
|
||||
print(f" FAILED: {create_result.get('errors')}")
|
||||
except Exception as e:
|
||||
print(f" ERROR: {e}")
|
||||
|
||||
print(f" Checked: {checked}, Drifted: {drifted}, Updated: {updated}")
|
||||
PY
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Done ==="
|
||||
[ "$DRY_RUN" = true ] && echo "Run with --apply to push changes to Cloudflare"
|
||||
@@ -0,0 +1,37 @@
|
||||
# zoo-exchange.yaml -- Cloudflare zone config for zoo.exchange
|
||||
# Source of truth for zoo.exchange DNS records
|
||||
# Last synced: 2026-03-26
|
||||
---
|
||||
zone: zoo.exchange
|
||||
|
||||
records:
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: www
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: gw
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
# Wildcard for gateway subdomain routing
|
||||
- type: A
|
||||
name: "*.gw"
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: docs
|
||||
content: "2d14d338f3-hosting.gitbook.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: api
|
||||
content: "cname.vercel-dns.com"
|
||||
proxied: false
|
||||
@@ -0,0 +1,88 @@
|
||||
# zoo-network.yaml — Cloudflare zone config for zoo.network
|
||||
# Zone ID: d9c81c5b54f0f713105d483ddbd61656
|
||||
# Source of truth for zoo.network DNS records
|
||||
# Last synced: 2026-03-24
|
||||
---
|
||||
zone: zoo.network
|
||||
zone_id: "d9c81c5b54f0f713105d483ddbd61656"
|
||||
|
||||
records:
|
||||
- type: A
|
||||
name: api-explore
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api-explorer
|
||||
content: "67.205.158.227"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: exchange
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: explore
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: iam
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: mpc-api
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: mpc
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: status
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.111.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.110.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.109.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.108.153"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: id
|
||||
content: "hanzo-id.pages.dev"
|
||||
proxied: true
|
||||
|
||||
- type: CNAME
|
||||
name: www
|
||||
content: "zoo-labs.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: TXT
|
||||
name: "_acme-challenge.mpc"
|
||||
content: '3xcQ9lIkiW2iU0lB1f2ZSjLTo7G9StXvLC6jKR59B7M'
|
||||
ttl: 120
|
||||
@@ -0,0 +1,277 @@
|
||||
# zoo-ngo.yaml — Cloudflare zone config for zoo.ngo
|
||||
# Zone ID: 9c6372f69d53e64bc2ba21b96263fceb
|
||||
# Source of truth for zoo.ngo DNS records
|
||||
# Last synced: 2026-03-12
|
||||
---
|
||||
zone: zoo.ngo
|
||||
zone_id: "9c6372f69d53e64bc2ba21b96263fceb"
|
||||
|
||||
records:
|
||||
- type: A
|
||||
name: api-explore
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api.explore-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api.explore
|
||||
content: "34.22.248.74"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: api.web3
|
||||
content: "134.199.136.205"
|
||||
proxied: false
|
||||
ttl: 300
|
||||
|
||||
- type: A
|
||||
name: api
|
||||
content: "34.22.248.74"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: bridge
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: explore-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: explore
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: explorer
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: kms
|
||||
content: "134.199.138.27"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: sig.explore-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: sig.explore
|
||||
content: "34.22.248.74"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: stats.explore-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: stats.explore
|
||||
content: "34.22.248.74"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: viz.explore-test
|
||||
content: "34.22.245.55"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: viz.explore
|
||||
content: "34.22.248.74"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: web3
|
||||
content: "134.199.136.205"
|
||||
proxied: false
|
||||
ttl: 300
|
||||
|
||||
- type: A
|
||||
name: ws.web3
|
||||
content: "134.199.136.205"
|
||||
proxied: false
|
||||
ttl: 300
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.111.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.110.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.109.153"
|
||||
proxied: false
|
||||
|
||||
- type: A
|
||||
name: "@"
|
||||
content: "185.199.108.153"
|
||||
proxied: false
|
||||
|
||||
- type: AAAA
|
||||
name: cdn
|
||||
content: "100::"
|
||||
proxied: true
|
||||
|
||||
- type: AAAA
|
||||
name: "@"
|
||||
content: "2606:50c0:8003::153"
|
||||
proxied: false
|
||||
|
||||
- type: AAAA
|
||||
name: "@"
|
||||
content: "2606:50c0:8001::153"
|
||||
proxied: false
|
||||
|
||||
- type: AAAA
|
||||
name: "@"
|
||||
content: "2606:50c0:8000::153"
|
||||
proxied: false
|
||||
|
||||
- type: AAAA
|
||||
name: "@"
|
||||
content: "2606:50c0:8002::153"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: ai
|
||||
content: "zooai-chat.pages.dev"
|
||||
proxied: true
|
||||
|
||||
- type: CNAME
|
||||
name: blog
|
||||
content: "zoo-blog.pages.dev"
|
||||
proxied: true
|
||||
|
||||
- type: CNAME
|
||||
name: chat
|
||||
content: "zooai-chat.pages.dev"
|
||||
proxied: true
|
||||
|
||||
- type: CNAME
|
||||
name: donate
|
||||
content: "hosted-checkout.stripecdn.com"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: download
|
||||
content: "public.r2.dev"
|
||||
proxied: true
|
||||
|
||||
- type: CNAME
|
||||
name: foundation
|
||||
content: "zoo-apps.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: gym
|
||||
content: "zooai.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: papers
|
||||
content: "zooai.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: ui
|
||||
content: "zoo-labs.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: vote
|
||||
content: "zoo-apps.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: www
|
||||
content: "zoo-apps.github.io"
|
||||
proxied: false
|
||||
|
||||
- type: CNAME
|
||||
name: zips
|
||||
content: "zooai.github.io"
|
||||
proxied: true
|
||||
|
||||
- type: MX
|
||||
name: "@"
|
||||
content: "alt3.aspmx.l.google.com"
|
||||
ttl: 3600
|
||||
|
||||
- type: MX
|
||||
name: "@"
|
||||
content: "aspmx.l.google.com"
|
||||
ttl: 3600
|
||||
|
||||
- type: MX
|
||||
name: "@"
|
||||
content: "alt1.aspmx.l.google.com"
|
||||
ttl: 3600
|
||||
|
||||
- type: MX
|
||||
name: "@"
|
||||
content: "alt2.aspmx.l.google.com"
|
||||
ttl: 3600
|
||||
|
||||
- type: MX
|
||||
name: "@"
|
||||
content: "alt4.aspmx.l.google.com"
|
||||
ttl: 3600
|
||||
|
||||
- type: TXT
|
||||
name: "_acme-challenge.api-explore"
|
||||
content: 'aTIhdbcShrcYpSFE2r_4C6J_11pE89XdKDOpknbv_Os'
|
||||
ttl: 120
|
||||
|
||||
- type: TXT
|
||||
name: "_acme-challenge.donate"
|
||||
content: 'mGXhVquq8VRifQvM3q_x3zYD1yk00RvpmwU_WSroCzQ'
|
||||
|
||||
- type: TXT
|
||||
name: "_acme-challenge.explore"
|
||||
content: 'p3a8omZVV0qfpdq9YEAJ4uY6nexvzZBJcL5OcCqOP2A'
|
||||
ttl: 120
|
||||
|
||||
- type: TXT
|
||||
name: "_vercel"
|
||||
content: 'vc-domain-verify=ai.zoo.ngo,4b57259b626ea7fedef2'
|
||||
|
||||
- type: TXT
|
||||
name: "@"
|
||||
content: 'slack-domain-verification=DRNH0gwucEBt7vpjJZWfkMt7rIWmGJRx6aCoz4UF'
|
||||
|
||||
- type: TXT
|
||||
name: "@"
|
||||
content: 'b3c899da-4a5e-4e4e-ada1-450fdefbb131=fe1b76ad366126398732fc4cd8489b520d01174ceec3fe2a6defa8cdfc84fb26'
|
||||
|
||||
- type: TXT
|
||||
name: "@"
|
||||
content: 'google-site-verification=PI_SalILhgyYqTKFOoSzV9FkIeaKSsVSOeBwEXT9reI'
|
||||
ttl: 3600
|
||||
|
||||
- type: TXT
|
||||
name: "@"
|
||||
content: 'v=spf1 include:_spf.google.com -all'
|
||||
ttl: 3600
|
||||
|
||||
- type: TXT
|
||||
name: "@"
|
||||
content: 'google-site-verification=YZ6z_uNp6bkHHKxDXnBKSEr6tVMR4mai2q35AJ_Gu-A'
|
||||
ttl: 3600
|
||||
Reference in New Issue
Block a user