A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
1#!/bin/bash
2# Monitor PDS logs for DPoP JWTs and compare iat timestamps
3# Usage: ./dpop-monitor.sh [pod-name]
4
5POD="${1:-atproto-pds-6d5c45457d-wcmhc}"
6
7echo "Monitoring DPoP JWTs from pod: $POD"
8echo "Press Ctrl+C to stop"
9echo "-------------------------------------------"
10
11kubectl 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
101done