No receipt. No merge.
Permission Protocol is the approval layer for autonomous systems. Deploy Gate is its GitHub Action.
One workflow. Human approval required. No exceptions.

Blocks merges to main until a human approves.
Every PR to a protected branch creates a Permission Protocol request. Approval state is enforced via commit status, and protected-path matches are sent as risk metadata (not used for gating).
π Full install guide with screenshots and troubleshooting.
Quick version:
# .github/workflows/deploy-gate.yml
name: Deploy Gate
on:
pull_request:
branches: [main]
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: permission-protocol/deploy-gate@v1
with:
pp-api-key: ${{ secrets.PP_API_KEY }}- Get API key from app.permissionprotocol.com
- Add secret:
gh secret set PP_API_KEY -b "pp_live_..." - Add workflow above
- Open PR β Permission request created automatically β approve if needed β merge
PR opened
β
βΌ
Deploy Gate verifies/creates PP request
β
ββββββββββββββββ Auto-approved / verified ββββββββββββββββΊ β
Merge OK
β
ββββββββββββββββ Approval required ββββββββββββββββββββββββΊ β³ Pending status + PR comment with review link
β
βΌ
Human approves in dashboard
β
βΌ
Re-run CI β β
Merge OK
Sign up at app.permissionprotocol.com and create an API key.
gh secret set PP_API_KEY -b "pp_live_your_key_here"Create .github/workflows/deploy-gate.yml:
name: Deploy Gate
on:
pull_request:
branches: [main]
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: permission-protocol/deploy-gate@v1
with:
pp-api-key: ${{ secrets.PP_API_KEY }}Open any PR to the protected branch. Deploy Gate always creates/verifies a request and posts a PR comment with the receipt/review link.
To avoid "merge loops" where approvals go stale when main advances, we recommend a two-ruleset pattern if you use GitHub Repository Rulesets:
- Ruleset 1 (Permission Protocol): Require
Permission Protocolstatus check with Strict mode: OFF. - Ruleset 2 (Build Protection): Require your build/test checks with Strict mode: ON.
This ensures your code is up-to-date with main, but your human approvals stick once granted. See full guide β
| Input | Description | Default |
|---|---|---|
pp-api-key |
Your Permission Protocol API key | Required |
pp-base-url |
PP API base URL | https://app.permissionprotocol.com |
pp-request-create-token |
Optional token to auto-create approval requests when receipts are missing | '' |
environment |
Environment bound to the receipt scope | production |
capability |
Capability bound to the receipt scope | deploy:production |
redeem |
Redeem receipt on verify (false for PR gate, true for deploy workflow) |
false |
protected-paths |
Regex used for risk assessment metadata only (not gating) | `^(deploy/ |
fail-on-missing |
Fail if no receipt | true |
fail-open-timeout |
Seconds to wait before PP API fail-open | 30 |
post-comment |
Post/update PR comment with receipt or approval link | true |
- uses: permission-protocol/deploy-gate@v1
with:
pp-api-key: ${{ secrets.PP_API_KEY }}
protected-paths: '^(src/critical/|infra/|\.env)'Protected path matches are forwarded to PP as protectedPathsChanged + changedFiles metadata for risk scoring.
Use this when you want custom scope values and auto-request creation in one workflow.
- uses: permission-protocol/deploy-gate@v1
with:
pp-api-key: ${{ secrets.PP_API_KEY }}
pp-request-create-token: ${{ secrets.PP_REQUEST_CREATE_TOKEN }}
environment: production
capability: deploy:production
redeem: false
fail-on-missing: true| Output | Description |
|---|---|
approved |
true if approved, false otherwise |
receipt-id |
Receipt ID when a receipt is found |
decision |
Decision from receipt (APPROVED, DENIED, PENDING) |
error-code |
API error code when verification fails |
error-message |
API error message when verification fails |
request-id |
Deploy request ID (if created) |
approval-url |
URL to approve the deploy |
- uses: permission-protocol/deploy-gate@v1
id: gate
with:
pp-api-key: ${{ secrets.PP_API_KEY }}
- run: echo "Approval URL: ${{ steps.gate.outputs.approval-url }}"
if: failure()Auto-approved / verified:
β
**Permission Protocol:** Approved
[View receipt β](https://app.permissionprotocol.com/pp/deploy-requests/{requestId})Approval required:
β³ **Permission Protocol:** Approval required
[Review & approve β](https://app.permissionprotocol.com/pp/deploy-requests/{requestId})Your AI agent just pushed to main.
It passed CI.
It deployed to production.
Who approved it?
Not a human. Not a policy. Nobody.
Deploy Gate closes that gap.
See it in action in the demo repo: permission-protocol/pp-demo
MIT. See LICENSE.
Built by Permission Protocol Β· The Signer of Record for Autonomous Systems