Securing CI/CD Pipelines with Human Approval Gates
Every team has a story about a deployment that shouldn’t have happened. A merge to main that bypassed review. A config change pushed to production at 2 AM by an automated pipeline no one was watching. The fix is always the same: “we need an approval gate.”
But most approval gates are cosmetic. A GitHub PR requires one review? Developers approve their own PRs from a second browser. A deployment pipeline pauses for manual approval? Someone clicks “Continue” without verifying what they’re approving. The gate exists, but it provides no real proof that a qualified human made a deliberate decision.
SignedApproval integrates with GitHub, Bitbucket, and GitLab to add cryptographic approval gates to your CI/CD pipeline. When a PR is opened or a deployment is triggered, the developer receives a push notification, authenticates with their passkey or Face ID, and the pipeline gets a signed proof of authorization — not just a button click.
GitHub integration
The GitHub integration works through the SignedApproval GitHub App. Install it on your repository, configure which branches require approval, and every PR targeting those branches gets a commit status check that blocks merge until a human approves.
How it works
- Developer opens a PR targeting
main(or any configured protected branch) - SignedApproval receives a webhook from GitHub and creates a pending commit status: “SignedApproval: Awaiting human approval”
- The designated approver receives a push notification with the PR title, branch, and changed file count
- Approver authenticates with passkey, TOTP, or Face ID on their phone
- SignedApproval updates the commit status to “success” and stores the Ed25519 signature as proof
- The PR can now be merged (optionally auto-merged if configured)
Why commit status checks, not PR reviews?
GitHub’s built-in PR review system is designed for code review, not security gates. Reviews can be dismissed, bypassed by admins, or approved without authentication. Commit status checks are binary (pass/fail), can be enforced as required checks in branch protection rules, and cannot be overridden without changing the branch protection settings.
Setup
Install the GitHub App and configure it in your SignedApproval dashboard:
- Go to Dashboard → Integrations → GitHub
- Click “Install GitHub App” and authorize for your organization or repositories
- Configure branch patterns: which branches require approval (default:
main,release/*) - Set the required quorum (number of approvals needed — default is 1)
- Optional: enable auto-merge after approval
- Add “SignedApproval” as a required status check in GitHub’s branch protection settings
Bitbucket integration
The Bitbucket integration follows the same pattern using Bitbucket’s build status API. When a PR is opened against a protected branch, SignedApproval sets a “pending” build status on the head commit. After human approval, the status flips to “SUCCESSFUL” and the PR can be merged.
Configuration
Connect your Bitbucket workspace via OAuth in the dashboard:
# Dashboard configuration (per-repo settings)
{
"workspace": "your-workspace",
"repository": "your-repo",
"branch_patterns": ["main", "release/*"],
"auto_merge": false,
"quorum": 1
}Bitbucket’s merge checks will block the PR until SignedApproval’s build status passes. Unlike Bitbucket’s native approval buttons, which require no authentication beyond being logged into Bitbucket, SignedApproval requires passkey, TOTP, or biometric authentication — and produces a cryptographic proof.
GitLab integration
GitLab uses external status checks and pipeline statuses. The SignedApproval integration creates a commit status on the merge request’s head commit, blocking merge until the human approves. This works with GitLab.com and self-hosted GitLab instances.
# GitLab configuration
{
"project_id": 12345,
"branch_patterns": ["main", "production"],
"required_approval_count": 1,
"auto_merge": false
}Policy configuration
Beyond branch patterns, you can configure granular policies that control when approvals are required and what authentication methods are acceptable:
// Example policy configuration
{
"rules": [
{
"name": "Production deploys require passkey",
"match": {
"branch": "main",
"paths": ["src/**", "deploy/**"]
},
"require": {
"method": ["passkey", "biometric"],
"quorum": 2,
"ttl_max": 600
}
},
{
"name": "Config changes need single approval",
"match": {
"branch": "main",
"paths": ["config/**", ".env*", "*.yml"]
},
"require": {
"method": ["passkey", "totp", "biometric"],
"quorum": 1,
"ttl_max": 1800
}
},
{
"name": "Docs skip approval",
"match": {
"branch": "main",
"paths": ["docs/**", "*.md"]
},
"action": "skip"
}
]
}Policies let you enforce that production deployments require passkey authentication from two approvers, while documentation changes can skip approval entirely. The authentication method constraint is important: for your highest-risk actions, you can require the strongest authentication method available.
The audit trail
Every CI/CD approval produces the same Ed25519 credential as any other SignedApproval action. This means your deployment audit trail isn’t just “PR #142 was merged by @developer on March 15” — it’s “PR #142 was approved by Alex Floyd via passkey authentication at 2026-03-15T14:23:07Z, signed with Ed25519 key abc123, signature verified.”
This level of specificity matters for SOC 2 audits, ISO 27001 compliance, and any regulatory framework that requires documented change management. The signature is independently verifiable — your auditor can confirm the approval without access to your internal systems.
Log sinks for external audit systems
SignedApproval supports log sinks — webhook endpoints that receive a copy of every approval event in real time. Point a log sink at your Datadog, Splunk, or SIEM instance and every CI/CD approval is automatically ingested into your security monitoring pipeline. Configure log sinks in Dashboard → Settings → Log Sinks.
Deployment pipeline integration
Beyond PR gates, you can add approval checks directly to your deployment pipeline. Here is an example GitHub Actions workflow that requires SignedApproval before deploying:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: Request deployment approval
id: approval
run: |
RESPONSE=$(curl -s -X POST \
https://signedapproval.net/api/v1/approvals/request \
-H "Authorization: Bearer ${{ secrets.SA_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"action": "Deploy commit ${{ github.sha }} to production",
"ttl_seconds": 600,
"metadata": {
"repo": "${{ github.repository }}",
"commit": "${{ github.sha }}",
"actor": "${{ github.actor }}"
}
}')
echo "id=$(echo $RESPONSE | jq -r '.id')" >> $GITHUB_OUTPUT
- name: Wait for approval
run: |
for i in $(seq 1 60); do
STATUS=$(curl -s \
https://signedapproval.net/api/v1/approvals/${{ steps.approval.outputs.id }} \
-H "Authorization: Bearer ${{ secrets.SA_API_KEY }}" \
| jq -r '.status')
if [ "$STATUS" = "approved" ]; then
echo "Approved!"
exit 0
elif [ "$STATUS" = "rejected" ]; then
echo "Rejected by approver"
exit 1
fi
sleep 10
done
echo "Approval timed out"
exit 1
- name: Deploy
run: npm run deploy:productionComparing to native platform approvals
| Feature | Native (GitHub/GL/BB) | SignedApproval |
|---|---|---|
| Authentication | Session cookie (already logged in) | Passkey, TOTP, or Face ID per action |
| Proof type | Database record | Ed25519 signature |
| Offline verification | No | Yes, with public key |
| Admin override | Admins can bypass | No bypass without valid signature |
| Cross-platform | Platform-specific | One policy for GitHub + BB + GL |
| Audit export | API/CSV from each platform | Real-time log sinks + verifiable signatures |
Getting started
The fastest path to cryptographic CI/CD approval gates:
- Create a free account (200 approvals/month included)
- Install the SignedApproval GitHub App (or connect Bitbucket/GitLab in your dashboard)
- Configure branch patterns and approval policies
- Add “SignedApproval” as a required status check in your branch protection rules
- Install the iOS app for push notification approvals with Face ID
Your next production deployment will come with a cryptographic proof that a real human authorized it. Not a button click. Not a Slack reaction. A signature.
Related posts
Ready to add cryptographic approvals to your agents?
200 free approvals per month. No credit card required.
Get your API key