#!/usr/bin/env bash # Renews the gateway's T3 sessions, which last 30 days and cannot be refreshed. For each host it # makes a one-time pairing credential on that host and hands it straight to `wristcode host add` # on the gateway, so the credential is never printed. Run it from a machine that can SSH to every # host by name. The watch warns a week before a session expires. # # scripts/renew-hosts.sh renew every host below # scripts/renew-hosts.sh "Mac mini" renew one host by label set -euo pipefail GATEWAY_SSH="${GATEWAY_SSH:-junior.gib}" # The container runtime on the gateway host: podman on junior, docker on the home server. GATEWAY_RUNTIME="${GATEWAY_RUNTIME:-podman}" GATEWAY_CONTAINER="${GATEWAY_CONTAINER:-wristcode-gateway}" # label | SSH host, or "local" for the machine running this script | T3 URLs # URLs are space separated, preferred first. The gateway uses the first one that answers. Direct # routes come first: from junior they cross WireGuard straight to the machine, so they skip # Cloudflare and the home server's tunnel and proxy. The public URLs are the fallback. LittleMan is # junior itself, reached at its own WireGuard address. HOSTS=( "Desktop|local|http://desktop.gib:3773 https://t3desktop.gbrown.org" "Mac mini|mac.gib|http://mac.gib:3773 https://t3mac.gbrown.org" "server-gib|server.gib|http://server.gib:3773 https://t3sr.gbrown.org" "LittleMan|junior.gib|http://192.168.2.2:3773 https://t3jr.gbrown.org" "Laptop|laptop.gib|http://laptop.gib:3773 http://laptopvpn.gib:3773 https://t3laptop.gbrown.org https://t3laptopvpn.gbrown.org" ) # Prints a pairing credential. Runs on the T3 host. The CLI is not on PATH in non-login shells, # so it looks where T3 Code installs it. MINT=' set -e export T3CODE_HOME="${T3CODE_HOME:-$HOME/.t3}" for candidate in "$(command -v t3 || true)" "$HOME/.t3/bin/t3" "$HOME/.local/bin/t3" $(ls -d "$HOME"/.t3/runtime/versions/*/t3 2>/dev/null | sort -V -r); do if [ -n "$candidate" ] && [ -x "$candidate" ]; then t3="$candidate"; break; fi done [ -n "${t3:-}" ] || { echo "no t3 CLI found" >&2; exit 1; } "$t3" auth pairing create --scope orchestration:read --scope orchestration:operate \ --label "WristCode gateway" --ttl 10m --json | sed -n "s/.*\"credential\": *\"\\([^\"]*\\)\".*/\\1/p" ' renew() { local label="$1" ssh_host="$2" url alternates alternate credential alt_args="" read -r url alternates <<<"$3" for alternate in $alternates; do alt_args+=" --alt-url '$alternate'"; done if [ "$ssh_host" = "local" ]; then credential="$(bash -c "$MINT")" else credential="$(ssh -o BatchMode=yes "$ssh_host" "bash -s" <<<"$MINT")" fi if [ -z "$credential" ]; then echo "$label: could not make a pairing credential" >&2 return 1 fi # The credential travels on stdin, not on a command line other processes could read. ssh -o BatchMode=yes "$GATEWAY_SSH" \ "read -r credential && $GATEWAY_RUNTIME exec $GATEWAY_CONTAINER wristcode host add '$url' \"\$credential\" --label '$label'$alt_args" \ <<<"$credential" } failed=0 for entry in "${HOSTS[@]}"; do IFS='|' read -r label ssh_host url <<<"$entry" if [ $# -gt 0 ] && [ "$1" != "$label" ]; then continue; fi renew "$label" "$ssh_host" "$url" || failed=1 done ssh -o BatchMode=yes "$GATEWAY_SSH" "$GATEWAY_RUNTIME exec $GATEWAY_CONTAINER wristcode host list" | cut -f1,4 exit "$failed"