# .github/workflows/examples/example-verdict-sign.yml # Example: Sign policy verdict with keyless signing # # This example shows how to: # 1. Run StellaOps policy evaluation # 2. Sign the verdict with keyless signing # 3. Use verdict in deployment gate # # Policy verdicts provide: # - Cryptographic proof of policy evaluation result # - Binding to specific image and policy version # - Evidence for audit and compliance name: Policy Verdict Gate on: push: branches: [main] workflow_dispatch: inputs: image: description: 'Container image to evaluate (with digest)' required: true type: string policy: description: 'Policy pack ID' required: false default: 'default' type: string env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: evaluate: runs-on: ubuntu-latest permissions: contents: read packages: read outputs: verdict: ${{ steps.eval.outputs.verdict }} verdict-digest: ${{ steps.eval.outputs.verdict-digest }} image-digest: ${{ steps.resolve.outputs.digest }} passed: ${{ steps.eval.outputs.passed }} steps: - name: Install StellaOps CLI uses: stella-ops/setup-cli@v1 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Resolve Image id: resolve run: | if [[ -n "${{ github.event.inputs.image }}" ]]; then IMAGE="${{ github.event.inputs.image }}" else IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}" fi # Resolve to digest if [[ ! "$IMAGE" =~ @sha256: ]]; then DIGEST=$(docker manifest inspect "$IMAGE" -v | jq -r '.Descriptor.digest') IMAGE="${IMAGE%%:*}@${DIGEST}" else DIGEST="${IMAGE##*@}" fi echo "image=${IMAGE}" >> $GITHUB_OUTPUT echo "digest=${DIGEST}" >> $GITHUB_OUTPUT - name: Run Policy Evaluation id: eval env: STELLAOPS_URL: 'https://api.stella-ops.org' run: | set -euo pipefail IMAGE="${{ steps.resolve.outputs.image }}" POLICY="${{ github.event.inputs.policy || 'default' }}" echo "::group::Evaluating policy '${POLICY}' against ${IMAGE}" RESULT=$(stella policy evaluate \ --image "${IMAGE}" \ --policy "${POLICY}" \ --output json) echo "$RESULT" | jq . echo "::endgroup::" # Extract verdict VERDICT=$(echo "$RESULT" | jq -r '.verdict') VERDICT_DIGEST=$(echo "$RESULT" | jq -r '.verdictDigest') PASSED=$(echo "$RESULT" | jq -r '.passed') echo "verdict=${VERDICT}" >> $GITHUB_OUTPUT echo "verdict-digest=${VERDICT_DIGEST}" >> $GITHUB_OUTPUT echo "passed=${PASSED}" >> $GITHUB_OUTPUT # Save verdict for signing echo "$RESULT" > verdict.json - name: Upload Verdict uses: actions/upload-artifact@v4 with: name: verdict path: verdict.json sign-verdict: needs: evaluate uses: ./.github/workflows/examples/stellaops-sign.yml with: artifact-digest: ${{ needs.evaluate.outputs.verdict-digest }} artifact-type: verdict predicate-type: 'verdict.stella/v1' push-attestation: true permissions: id-token: write contents: read packages: write gate: needs: [evaluate, sign-verdict] runs-on: ubuntu-latest steps: - name: Check Verdict run: | PASSED="${{ needs.evaluate.outputs.passed }}" VERDICT="${{ needs.evaluate.outputs.verdict }}" if [[ "$PASSED" != "true" ]]; then echo "::error::Policy verdict: ${VERDICT}" echo "::error::Deployment blocked by policy" exit 1 fi echo "Policy verdict: ${VERDICT} - Proceeding with deployment" - name: Summary run: | PASSED="${{ needs.evaluate.outputs.passed }}" if [[ "$PASSED" == "true" ]]; then ICON="white_check_mark" STATUS="PASSED" else ICON="x" STATUS="BLOCKED" fi cat >> $GITHUB_STEP_SUMMARY << EOF ## :${ICON}: Policy Verdict: ${STATUS} | Field | Value | |-------|-------| | **Image** | \`${{ needs.evaluate.outputs.image-digest }}\` | | **Verdict** | \`${{ needs.evaluate.outputs.verdict }}\` | | **Verdict Digest** | \`${{ needs.evaluate.outputs.verdict-digest }}\` | | **Attestation** | \`${{ needs.sign-verdict.outputs.attestation-digest }}\` | | **Rekor UUID** | \`${{ needs.sign-verdict.outputs.rekor-uuid }}\` | ### Verify Verdict \`\`\`bash stella attest verify \\ --artifact "${{ needs.evaluate.outputs.verdict-digest }}" \\ --certificate-identity "repo:${{ github.repository }}:ref:${{ github.ref }}" \\ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \`\`\` EOF # Example deployment job - only runs if gate passes deploy: needs: [evaluate, gate] if: needs.evaluate.outputs.passed == 'true' runs-on: ubuntu-latest environment: production steps: - name: Deploy run: | echo "Deploying ${{ needs.evaluate.outputs.image-digest }}" echo "Policy verdict verified and signed" # Add your deployment commands here