dev.site is served by Fly and has been for nine releases. The tunnel scripts were the previous way in and keeping them meant two deployments aimed at one hostname, with nothing but a README paragraph to say which was live. Gone: deploy/setup-tunnel.sh, deploy/run.sh, deploy/env, and the ignore entries for the config the first of those generated. Cloudflare still holds the zone, but only as DNS now — A and AAAA straight at Fly, unproxied. docs/milestones.md keeps its account of M6 running behind the tunnel. That is what happened at the time.
- commit
- c22dbd989823d353e2f66b8c3550ee1e0787b589
- parent
- b7e4299f2666cad628c87ced32709dadb470e942
- author
- Dami <dami.etoile@gmail.com>
| M | .dockerignore | 1 | - |
| M | .gitignore | 2 | - |
| M | deploy/ | 8 | +- |
| D | deploy/ | 21 | ---- |
| D | deploy/ | 48 | -------- |
| D | deploy/ | 171 | ------------------------------ |
@@ -122,9 +122,5 @@ served `immutable`. The manifest naming the current hash, the HTML, and every `/
122122 response are `no-cache` or `no-store`. Redeploying therefore invalidates nothing and never123123 serves a stale bundle.124124 125-## The previous setup126-127-`setup-tunnel.sh`, `run.sh` and `env` publish the control plane from a laptop through128-Cloudflare Tunnel. They still work, and are what `https://dev.site` pointed at before Fly.129-Delete them once the cutover has settled — running both against the same hostname is a way130-to be confused about which one is live.125+Cloudflare still holds the `dev.site` zone, but only as DNS — the `A` and `AAAA` records126+point straight at Fly, unproxied.@@ -1,21 +0,0 @@
1-# Deployment settings for dev.site.2-#3-# DEVSITE_PUBLIC_ORIGIN pins account identity — Shoo derives client_id and each user's4-# pairwise subject from it. Once anyone has signed in, changing this orphans every account.5-export DEVSITE_PUBLIC_ORIGIN="https://dev.site"6-7-# The control plane listens on loopback only. Cloudflare Tunnel is the only way in, so8-# there is no port to expose or firewall.9-export DEVSITE_BIND="127.0.0.1:4000"10-11-export DEVSITE_DB="$DEVSITE_ROOT/data/devsite.db"12-export DEVSITE_STATE_DIR="$DEVSITE_ROOT/data/state"13-export DEVSITE_WEB_ROOT="$DEVSITE_ROOT/web"14-15-# Hostnames served by the tunnel.16-export DEVSITE_HOSTNAME="dev.site"17-export DEVSITE_TUNNEL_NAME="devsite"18-19-# Cloudflare zone holding dev.site. Not a secret — it only names the zone, and every API20-# call against it still requires a token.21-export CLOUDFLARE_ZONE_ID="f0b023d8e68dde02a0e9d3a3728fa890"@@ -1,48 +0,0 @@
1-2-# Run the control plane and the tunnel in front of it.3-set -euo pipefail4-5-DEVSITE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"6-export DEVSITE_ROOT7-# shellcheck source=./env8-source "$DEVSITE_ROOT/deploy/env"9-10-config="$DEVSITE_ROOT/deploy/cloudflared.yml"11-[[ -f "$config" ]] || { echo "error: run ./deploy/setup-tunnel.sh first" >&2; exit 1; }12-13-mkdir -p "$DEVSITE_ROOT/data"14-15-# Build the browser bundle if it is missing, so a fresh checkout serves a working page16-# rather than a blank one.17-if [[ ! -f "$DEVSITE_ROOT/web/pkg/manifest.json" ]]; then18- echo "building the browser bundle…"19- "$DEVSITE_ROOT/scripts/build-wasm.sh" --release20-fi21-22-cargo build --release -p devsite-server23-server="$DEVSITE_ROOT/target/release/devsite-server"24-25-"$server" &26-server_pid=$!27-28-cloudflared tunnel --config "$config" run &29-tunnel_pid=$!30-31-# Either process dying should take the other with it, rather than leaving a tunnel32-# pointing at nothing or a server nobody can reach.33-cleanup() {34- trap - EXIT INT TERM35- kill "$server_pid" "$tunnel_pid" 2>/dev/null || true36- wait "$server_pid" "$tunnel_pid" 2>/dev/null || true37-}38-trap cleanup EXIT INT TERM39-40-echo41-echo " control plane http://$DEVSITE_BIND"42-echo " public $DEVSITE_PUBLIC_ORIGIN"43-echo44-45-# Poll rather than `wait -n`: macOS ships bash 3.2, which does not have it.46-while kill -0 "$server_pid" 2>/dev/null && kill -0 "$tunnel_pid" 2>/dev/null; do47- sleep 248-done@@ -1,171 +0,0 @@
1-2-# Create the Cloudflare Tunnel for dev.site and point DNS at it.3-#4-# Two ways to authenticate, in order of preference:5-#6-# 1. CLOUDFLARE_API_TOKEN — fully headless. Needs Account > Cloudflare Tunnel > Edit and7-# Zone > DNS > Edit on the dev.site zone.8-# 2. `cloudflared tunnel login` — opens a browser and writes ~/.cloudflared/cert.pem.9-#10-# Idempotent either way: re-running adopts an existing tunnel and repoints the records.11-set -euo pipefail12-13-DEVSITE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"14-export DEVSITE_ROOT15-# shellcheck source=./env16-source "$DEVSITE_ROOT/deploy/env"17-18-api() {19- local method="$1" path="$2"; shift 220- curl -sS -X "$method" "https://api.cloudflare.com/client/v4$path" \21- -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \22- -H "Content-Type: application/json" "$@"23-}24-25-# Fail loudly on an API error rather than letting a null id flow into a config file.26-check() {27- python3 -c '28-import json, sys29-body = sys.stdin.read()30-try:31- data = json.loads(body)32-except Exception:33- sys.exit(f"cloudflare returned non-JSON: {body[:200]}")34-if not data.get("success"):35- errors = "; ".join(f'"'"'{e.get("code")}: {e.get("message")}'"'"' for e in data.get("errors", []))36- sys.exit(f"cloudflare API error: {errors or body[:200]}")37-print(json.dumps(data["result"]))38-'39-}40-41-write_config() {42- local tunnel_id="$1" credentials="$2"43- cat > "$DEVSITE_ROOT/deploy/cloudflared.yml" <<EOF44-# Generated by deploy/setup-tunnel.sh — safe to regenerate.45-tunnel: $tunnel_id46-credentials-file: $credentials47-48-ingress:49- - hostname: $DEVSITE_HOSTNAME50- service: http://$DEVSITE_BIND51- - hostname: www.$DEVSITE_HOSTNAME52- service: http://$DEVSITE_BIND53- # cloudflared requires a catch-all; anything unmatched above is refused.54- - service: http_status:40455-EOF56- echo "wrote deploy/cloudflared.yml"57-}58-59-# ---------------------------------------------------------------- API token path60-61-if [[ -n "${CLOUDFLARE_API_TOKEN:-}" ]]; then62- echo "authenticating with CLOUDFLARE_API_TOKEN"63-64- account_id="$(api GET "/zones/$CLOUDFLARE_ZONE_ID" | check \65- | python3 -c 'import json,sys; print(json.load(sys.stdin)["account"]["id"])')"66- echo "account: $account_id"67-68- existing="$(api GET "/accounts/$account_id/cfd_tunnel?name=$DEVSITE_TUNNEL_NAME&is_deleted=false" \69- | check | python3 -c 'import json,sys; r=json.load(sys.stdin); print(r[0]["id"] if r else "")')"70-71- credentials="$HOME/.cloudflared/$DEVSITE_TUNNEL_NAME.json"72- mkdir -p "$HOME/.cloudflared"73-74- if [[ -n "$existing" ]]; then75- tunnel_id="$existing"76- echo "tunnel '$DEVSITE_TUNNEL_NAME' already exists: $tunnel_id"77- if [[ ! -f "$credentials" ]]; then78- cat >&2 <<EOF79-error: the tunnel exists but $credentials is missing.80-81- A tunnel's secret is only returned when it is created. Either restore that file, or82- delete the tunnel and re-run:83-84- curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/$account_id/cfd_tunnel/$tunnel_id" \\85- -H "Authorization: Bearer \$CLOUDFLARE_API_TOKEN"86-EOF87- exit 188- fi89- else90- echo "creating tunnel '$DEVSITE_TUNNEL_NAME'"91- secret="$(head -c 32 /dev/urandom | base64)"92- tunnel_id="$(api POST "/accounts/$account_id/cfd_tunnel" \93- --data "$(python3 -c "import json,sys; print(json.dumps({'name': sys.argv[1], 'tunnel_secret': sys.argv[2], 'config_src': 'local'}))" "$DEVSITE_TUNNEL_NAME" "$secret")" \94- | check | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"95-96- # cloudflared reads the secret from here; it is never retrievable from the API again.97- umask 07798- python3 -c "99-import json, sys100-json.dump({'AccountTag': sys.argv[1], 'TunnelID': sys.argv[2], 'TunnelSecret': sys.argv[3]}, open(sys.argv[4], 'w'))101-" "$account_id" "$tunnel_id" "$secret" "$credentials"102- echo "tunnel created: $tunnel_id"103- echo "credentials written to $credentials — back this up, it cannot be re-fetched"104- fi105-106- write_config "$tunnel_id" "$credentials"107-108- # Proxied CNAME to the tunnel. Cloudflare resolves *.cfargotunnel.com internally; it109- # never appears in public DNS.110- for host in "$DEVSITE_HOSTNAME" "www.$DEVSITE_HOSTNAME"; do111- record_id="$(api GET "/zones/$CLOUDFLARE_ZONE_ID/dns_records?name=$host" | check \112- | python3 -c 'import json,sys; r=json.load(sys.stdin); print(r[0]["id"] if r else "")')"113- payload="$(python3 -c "114-import json, sys115-print(json.dumps({'type': 'CNAME', 'name': sys.argv[1], 'content': sys.argv[2] + '.cfargotunnel.com',116- 'proxied': True, 'ttl': 1, 'comment': 'dev.site control plane via cloudflared'}))117-" "$host" "$tunnel_id")"118-119- if [[ -n "$record_id" ]]; then120- api PUT "/zones/$CLOUDFLARE_ZONE_ID/dns_records/$record_id" --data "$payload" | check > /dev/null121- echo "updated DNS record for $host"122- else123- api POST "/zones/$CLOUDFLARE_ZONE_ID/dns_records" --data "$payload" | check > /dev/null124- echo "created DNS record for $host"125- fi126- done127-128- echo129- echo "done. start everything with: ./deploy/run.sh"130- exit 0131-fi132-133-# ------------------------------------------------------------ browser login path134-135-if [[ ! -f "$HOME/.cloudflared/cert.pem" ]]; then136- cat >&2 <<'EOF'137-error: no Cloudflare credentials found.138-139- Either export an API token with Account > Cloudflare Tunnel > Edit and140- Zone > DNS > Edit:141-142- export CLOUDFLARE_API_TOKEN=...143- ./deploy/setup-tunnel.sh144-145- Or authorize this machine through a browser:146-147- cloudflared tunnel login148- ./deploy/setup-tunnel.sh149-EOF150- exit 1151-fi152-153-if cloudflared tunnel list --output json | grep -q "\"name\":\"$DEVSITE_TUNNEL_NAME\""; then154- echo "tunnel '$DEVSITE_TUNNEL_NAME' already exists, reusing it"155-else156- echo "creating tunnel '$DEVSITE_TUNNEL_NAME'"157- cloudflared tunnel create "$DEVSITE_TUNNEL_NAME"158-fi159-160-tunnel_id="$(cloudflared tunnel list --output json \161- | python3 -c "import json,sys;print(next(t['id'] for t in json.load(sys.stdin) if t['name']=='$DEVSITE_TUNNEL_NAME'))")"162-credentials="$HOME/.cloudflared/$tunnel_id.json"163-[[ -f "$credentials" ]] || { echo "error: missing credentials at $credentials" >&2; exit 1; }164-165-write_config "$tunnel_id" "$credentials"166-167-cloudflared tunnel route dns --overwrite-dns "$DEVSITE_TUNNEL_NAME" "$DEVSITE_HOSTNAME"168-cloudflared tunnel route dns --overwrite-dns "$DEVSITE_TUNNEL_NAME" "www.$DEVSITE_HOSTNAME"169-170-echo171-echo "done. start everything with: ./deploy/run.sh"