A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go

add monitor script

evan.jarrett.net c4a9e4bf a09453c6

verified
Changed files
+101
scripts
+101
scripts/dpop-monitor.sh
··· 1 + #!/bin/bash 2 + # Monitor PDS logs for DPoP JWTs and compare iat timestamps 3 + # Usage: ./dpop-monitor.sh [pod-name] 4 + 5 + POD="${1:-atproto-pds-6d5c45457d-wcmhc}" 6 + 7 + echo "Monitoring DPoP JWTs from pod: $POD" 8 + echo "Press Ctrl+C to stop" 9 + echo "-------------------------------------------" 10 + 11 + kubectl logs -f "$POD" 2>/dev/null | while read -r line; do 12 + # Extract DPoP JWT from the line 13 + dpop=$(echo "$line" | grep -oP '"dpop":"[^"]+' | sed 's/"dpop":"//') 14 + 15 + if [ -n "$dpop" ]; then 16 + # Extract log timestamp (milliseconds) 17 + log_time_ms=$(echo "$line" | grep -oP '"time":\d+' | grep -oP '\d+') 18 + 19 + # Extract URL 20 + url=$(echo "$line" | grep -oP '"url":"[^"]+' | sed 's/"url":"//') 21 + 22 + # Extract status code 23 + status=$(echo "$line" | grep -oP '"statusCode":\d+' | grep -oP '\d+') 24 + 25 + # Extract client IP (cf-connecting-ip) 26 + client_ip=$(echo "$line" | grep -oP '"cf-connecting-ip":"[^"]+' | sed 's/"cf-connecting-ip":"//') 27 + 28 + # Extract user-agent to identify the source 29 + user_agent=$(echo "$line" | grep -oP '"user-agent":"[^"]+' | sed 's/"user-agent":"//') 30 + 31 + # Extract referer (often contains the source app) 32 + referer=$(echo "$line" | grep -oP '"referer":"[^"]+' | sed 's/"referer":"//' | grep -oP 'https://[^/]+' | sed 's|https://||') 33 + 34 + # Decode JWT payload (second part between dots) 35 + payload=$(echo "$dpop" | cut -d. -f2) 36 + 37 + # Add padding if needed for base64 38 + padding=$((4 - ${#payload} % 4)) 39 + if [ $padding -ne 4 ]; then 40 + payload="${payload}$(printf '=%.0s' $(seq 1 $padding))" 41 + fi 42 + 43 + # Decode and extract iat 44 + decoded=$(echo "$payload" | base64 -d 2>/dev/null) 45 + iat=$(echo "$decoded" | grep -oP '"iat":\d+' | grep -oP '\d+') 46 + exp=$(echo "$decoded" | grep -oP '"exp":\d+' | grep -oP '\d+') 47 + htu=$(echo "$decoded" | grep -oP '"htu":"[^"]+' | sed 's/"htu":"//') 48 + 49 + if [ -n "$iat" ] && [ -n "$log_time_ms" ]; then 50 + # Convert log time to seconds 51 + log_time_s=$((log_time_ms / 1000)) 52 + 53 + # Calculate difference (positive = token from future, negative = token from past) 54 + diff=$((iat - log_time_s)) 55 + 56 + # Determine source - prefer referer, then htu domain, then user-agent 57 + if [ -n "$referer" ]; then 58 + source="$referer" 59 + else 60 + # Extract domain from htu (the target of the DPoP request) 61 + htu_domain=$(echo "$htu" | grep -oP 'https://[^/]+' | sed 's|https://||') 62 + 63 + # For server-to-server calls, try to identify by known IPs 64 + case "$client_ip" in 65 + 152.44.36.124) source="atcr.io" ;; 66 + 2a04:3541:8000:1000:*) source="tangled.org" ;; 67 + *) 68 + if echo "$user_agent" | grep -q "indigo-sdk"; then 69 + source="indigo-sdk" 70 + elif echo "$user_agent" | grep -q "Go-http-client"; then 71 + source="Go-app" 72 + else 73 + source="${user_agent:0:30}" 74 + fi 75 + source="$source ($client_ip)" 76 + ;; 77 + esac 78 + fi 79 + 80 + # Color coding 81 + if [ $diff -gt 0 ]; then 82 + color="\033[31m" # Red - future token (problem!) 83 + status_text="FUTURE" 84 + elif [ $diff -lt -5 ]; then 85 + color="\033[33m" # Yellow - old token 86 + status_text="OLD" 87 + else 88 + color="\033[32m" # Green - ok 89 + status_text="OK" 90 + fi 91 + reset="\033[0m" 92 + 93 + echo "" 94 + echo -e "${color}[$status_text]${reset} Diff: ${diff}s | Source: $source | Status: $status" 95 + echo " iat (token): $iat ($(date -d @$iat -u '+%H:%M:%S UTC'))" 96 + echo " PDS received: $log_time_s ($(date -d @$log_time_s -u '+%H:%M:%S UTC'))" 97 + echo " URL: $url" 98 + [ -n "$client_ip" ] && echo " Client IP: $client_ip" 99 + fi 100 + fi 101 + done