A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
1# GitHub Actions workflow for verifying ATProto signatures 2 3name: Verify and Deploy 4 5on: 6 push: 7 branches: [main] 8 pull_request: 9 branches: [main] 10 11env: 12 REGISTRY: atcr.io 13 IMAGE_NAME: ${{ github.repository }} 14 15jobs: 16 verify-signature: 17 name: Verify Image Signature 18 runs-on: ubuntu-latest 19 steps: 20 - name: Checkout code 21 uses: actions/checkout@v4 22 23 - name: Set up image tag 24 id: vars 25 run: | 26 echo "IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT 27 28 - name: Install verification tools 29 run: | 30 # Install ORAS 31 curl -LO https://github.com/oras-project/oras/releases/download/v1.0.0/oras_1.0.0_linux_amd64.tar.gz 32 tar -xzf oras_1.0.0_linux_amd64.tar.gz 33 sudo mv oras /usr/local/bin/ 34 35 # Install crane 36 curl -sL "https://github.com/google/go-containerregistry/releases/download/v0.15.2/go-containerregistry_Linux_x86_64.tar.gz" > crane.tar.gz 37 tar -xzf crane.tar.gz 38 sudo mv crane /usr/local/bin/ 39 40 # Install atcr-verify (when available) 41 # curl -LO https://github.com/atcr-io/atcr/releases/latest/download/atcr-verify 42 # chmod +x atcr-verify 43 # sudo mv atcr-verify /usr/local/bin/ 44 45 - name: Check for signature 46 id: check_signature 47 run: | 48 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}" 49 echo "Checking signature for $IMAGE" 50 51 # Get image digest 52 DIGEST=$(crane digest "$IMAGE") 53 echo "Image digest: $DIGEST" 54 55 # Check for ATProto signature using ORAS 56 REPO=$(echo "$IMAGE" | cut -d: -f1) 57 REFERRERS=$(curl -s "https://${{ env.REGISTRY }}/v2/${REPO#${{ env.REGISTRY }}/}/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json") 58 59 SIG_COUNT=$(echo "$REFERRERS" | jq '.manifests | length') 60 61 if [ "$SIG_COUNT" -eq 0 ]; then 62 echo "❌ No ATProto signature found" 63 echo "has_signature=false" >> $GITHUB_OUTPUT 64 exit 1 65 fi 66 67 echo "✓ Found $SIG_COUNT signature(s)" 68 echo "has_signature=true" >> $GITHUB_OUTPUT 69 70 - name: Verify signature (full verification) 71 if: steps.check_signature.outputs.has_signature == 'true' 72 run: | 73 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}" 74 75 # Option 1: Use atcr-verify CLI (when available) 76 # atcr-verify "$IMAGE" --policy .atcr/trust-policy.yaml 77 78 # Option 2: Use shell script 79 chmod +x examples/verification/atcr-verify.sh 80 ./examples/verification/atcr-verify.sh "$IMAGE" 81 82 echo "✓ Signature verified successfully" 83 84 - name: Verify signer DID 85 if: steps.check_signature.outputs.has_signature == 'true' 86 run: | 87 IMAGE="${{ steps.vars.outputs.IMAGE_TAG }}" 88 89 # Get signature metadata 90 DIGEST=$(crane digest "$IMAGE") 91 REPO=$(echo "$IMAGE" | cut -d: -f1) 92 93 REFERRERS=$(curl -s "https://${{ env.REGISTRY }}/v2/${REPO#${{ env.REGISTRY }}/}/referrers/${DIGEST}?artifactType=application/vnd.atproto.signature.v1+json") 94 SIG_DIGEST=$(echo "$REFERRERS" | jq -r '.manifests[0].digest') 95 96 # Pull signature artifact 97 oras pull "${REPO}@${SIG_DIGEST}" -o /tmp/sig 98 99 # Extract DID 100 DID=$(jq -r '.atproto.did' /tmp/sig/atproto-signature.json) 101 echo "Signed by DID: $DID" 102 103 # Check against trusted DIDs 104 TRUSTED_DIDS="${{ secrets.TRUSTED_DIDS }}" # e.g., "did:plc:alice123,did:plc:bob456" 105 106 if [[ ",$TRUSTED_DIDS," == *",$DID,"* ]]; then 107 echo "✓ DID is trusted" 108 else 109 echo "❌ DID $DID is not in trusted list" 110 exit 1 111 fi 112 113 deploy: 114 name: Deploy to Kubernetes 115 needs: verify-signature 116 runs-on: ubuntu-latest 117 if: github.ref == 'refs/heads/main' 118 steps: 119 - name: Checkout code 120 uses: actions/checkout@v4 121 122 - name: Set up image tag 123 id: vars 124 run: | 125 echo "IMAGE_TAG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT 126 127 - name: Set up kubectl 128 uses: azure/setup-kubectl@v3 129 130 - name: Configure kubectl 131 run: | 132 echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > /tmp/kubeconfig 133 export KUBECONFIG=/tmp/kubeconfig 134 135 - name: Deploy to production 136 run: | 137 kubectl set image deployment/myapp \ 138 myapp=${{ steps.vars.outputs.IMAGE_TAG }} \ 139 -n production 140 141 kubectl rollout status deployment/myapp -n production 142 143 - name: Verify deployment 144 run: | 145 kubectl get pods -n production -l app=myapp 146 147 # Wait for rollout to complete 148 kubectl wait --for=condition=available --timeout=300s \ 149 deployment/myapp -n production 150 151 # Alternative: Use atcr-verify action (when available) 152 verify-with-action: 153 name: Verify with ATCR Action 154 runs-on: ubuntu-latest 155 steps: 156 - name: Checkout code 157 uses: actions/checkout@v4 158 159 - name: Verify image signature 160 # uses: atcr-io/atcr-verify-action@v1 161 # with: 162 # image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} 163 # policy: .atcr/trust-policy.yaml 164 # fail-on-error: true 165 run: | 166 echo "TODO: Use official atcr-verify GitHub Action"