Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 84 additions & 130 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,38 @@
<img src="./assets/gate-blocked.svg" alt="Deploy Gate blocked symbol" width="64">
</p>

<p align="center">
<strong>No receipt. No merge.</strong>
</p>

<h1 align="center">Deploy Gate</h1>

<p align="center">
<strong>Permission Protocol is the approval layer for autonomous systems. Deploy Gate is its GitHub Action.</strong>
<strong>AI tried to deploy. It got blocked.</strong>
</p>

<p align="center">
<strong>One workflow. Human approval required. No exceptions.</strong>
No AI action executes without an explicit human signer.<br>
One workflow. One secret. Works on your first PR.
</p>

<p align="center">
<a href="https://github.com/permission-protocol/deploy-gate/actions">
<img src="https://img.shields.io/github/actions/workflow/status/permission-protocol/deploy-gate/test.yml?style=flat-square&label=tests" alt="Tests">
</a>
<a href="https://github.com/marketplace/actions/deploy-gate">
<img src="https://img.shields.io/badge/GitHub_Marketplace-Deploy_Gate-blue?style=flat-square" alt="Marketplace">
</a>
</p>

---

## Badge Usage

<p align="left">
<img src="./assets/badge-blocked.svg" alt="deploy gate blocked badge">
<img src="./assets/badge-approved.svg" alt="deploy gate approved badge">
</p>

<p align="left">
<img src="./assets/gate-blocked.svg" alt="blocked gate symbol" width="64">
<img src="./assets/gate-signed.svg" alt="approved gate symbol" width="64">
</p>
## How it works

```markdown
![deploy gate blocked](./assets/badge-blocked.svg)
![deploy gate approved](./assets/badge-approved.svg)
```
PR opened → ❌ Deploy blocked → Human signs → ✅ Deploy proceeds
```

---

## What It Does

**Blocks merges to `main` until a human approves.**
Open a PR. Deploy Gate blocks the merge. Click the approval link. Sign. Merge proceeds. Receipt recorded.

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).
**Takes ~3 minutes to install. One secret.**

---

## Install (3 minutes)

**👉 [Full install guide](./INSTALL.md)** with screenshots and troubleshooting.

**Quick version:**
## Install

```yaml
# .github/workflows/deploy-gate.yml
Expand All @@ -81,11 +56,37 @@ jobs:
1. Get API key from [app.permissionprotocol.com](https://app.permissionprotocol.com)
2. Add secret: `gh secret set PP_API_KEY -b "pp_live_..."`
3. Add workflow above
4. Open PR → Permission request created automatically → approve if needed → merge
4. Open a PR → see it blocked → approve → merge

**[Full install guide →](./INSTALL.md)**

---

## How It Works
## Why this exists

AI agents can deploy code, delete data, and modify infrastructure.

Today, they do this with:

- ❌ No explicit approval
- ❌ No accountability
- ❌ No audit trail

"Approved" is a mutable DB flag. An agent, a backend, or a bug can flip it.

Deploy Gate enforces:

- ✅ Explicit human signer (Ed25519)
- ✅ Signature bound to exact args (commit, repo, environment)
- ✅ Single-use receipt (replay fails)
- ✅ Tamper-evident — any post-signing mutation fails verification
- ✅ Verifiable audit trail

It does not trust database state. Only the signed receipt.

---

## Flow

<p align="left">
<img src="./assets/flow-diagram.svg" alt="PR Created to Deploy Gate flow">
Expand All @@ -95,68 +96,36 @@ jobs:
PR opened
Deploy Gate verifies/creates PP request
Deploy Gate verifies/creates request
├─────────────── Auto-approved / verified ───────────────► ✅ Merge OK
├── Receipt exists + valid ──────────────► ✅ Merge OK
└─────────────── Approval required ───────────────────────► ⏳ Pending status + PR comment with review link
Human approves in dashboard
Re-run CI → ✅ Merge OK
└── No receipt ──────────────────────────► ⏳ Blocked + PR comment with approval link
Human approves in dashboard
Re-run CI → ✅ Merge OK
```

---

## Advanced Setup
## PR Comments

### 1. Get API Key
Deploy Gate posts directly on your PR:

Sign up at [app.permissionprotocol.com](https://app.permissionprotocol.com) and create an API key.

### 2. Add Secret

```bash
gh secret set PP_API_KEY -b "pp_live_your_key_here"
Approval required:
```

### 3. Add Workflow

Create `.github/workflows/deploy-gate.yml`:

```yaml
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 }}
⏳ Permission Protocol: Approval required
Review & approve → https://app.permissionprotocol.com/pp/deploy-requests/{id}
```

### 4. Open a PR

Open any PR to the protected branch. Deploy Gate always creates/verifies a request and posts a PR comment with the receipt/review link.

---

## ⚠️ Recommended: GitHub Ruleset Pattern

To avoid "merge loops" where approvals go stale when `main` advances, we recommend a **two-ruleset pattern** if you use GitHub Repository Rulesets:

1. **Ruleset 1 (Permission Protocol):** Require `Permission Protocol` status check with **Strict mode: OFF**.
2. **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 →](./INSTALL.md#%EF%B8%8F-critical-github-rulesets--strict-mode)
Approved:
```
✅ Permission Protocol: Approved
View receipt → https://app.permissionprotocol.com/pp/deploy-requests/{id}
```

---

Expand All @@ -166,43 +135,31 @@ This ensures your code is up-to-date with `main`, but your human approvals stick
|-------|-------------|---------|
| `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/|\.github/workflows/)` |
| `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` |

### Risk Metadata Paths

```yaml
- 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.
<details>
<summary><strong>Advanced configuration</strong></summary>

## Advanced Usage
| Input | Description | Default |
|-------|-------------|---------|
| `pp-request-create-token` | Optional token to auto-create approval requests | `''` |
| `protected-paths` | Regex for risk assessment metadata (not gating) | `^(deploy/\|\.github/workflows/)` |

Use this when you want custom scope values and auto-request creation in one workflow.
### Risk Metadata Paths

```yaml
- 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
protected-paths: '^(src/critical/|infra/|\.env)'
```

---

## Outputs
### Outputs

| Output | Description |
|--------|-------------|
Expand All @@ -224,39 +181,36 @@ Use this when you want custom scope values and auto-request creation in one work
if: failure()
```

## PR Comment Example
### GitHub Ruleset Pattern

Auto-approved / verified:
```markdown
✅ **Permission Protocol:** Approved
[View receipt →](https://app.permissionprotocol.com/pp/deploy-requests/{requestId})
```
To avoid "merge loops" where approvals go stale when `main` advances, use a **two-ruleset pattern**:

Approval required:
```markdown
⏳ **Permission Protocol:** Approval required
[Review & approve →](https://app.permissionprotocol.com/pp/deploy-requests/{requestId})
```

---
1. **Ruleset 1 (Permission Protocol):** Require `Permission Protocol` status check with **Strict mode: OFF**.
2. **Ruleset 2 (Build Protection):** Require your build/test checks with **Strict mode: ON**.

## Why?
[See full guide →](./INSTALL.md#%EF%B8%8F-critical-github-rulesets--strict-mode)

Your AI agent just pushed to main.
It passed CI.
It deployed to production.
</details>

**Who approved it?**
---

Not a human. Not a policy. Nobody.
## Live Demo

Deploy Gate closes that gap.
See it in action: [permission-protocol/pp-demo](https://github.com/permission-protocol/pp-demo)

---

## Live Demo
## Badge Usage

<p align="left">
<img src="./assets/badge-blocked.svg" alt="deploy gate blocked badge">
<img src="./assets/badge-approved.svg" alt="deploy gate approved badge">
</p>

See it in action in the demo repo: [permission-protocol/pp-demo](https://github.com/permission-protocol/pp-demo)
```markdown
![deploy gate blocked](./assets/badge-blocked.svg)
![deploy gate approved](./assets/badge-approved.svg)
```

---

Expand Down
Loading