https://github.com/decionis/govern
Govern any GitHub workflow step on a signed Decionis Decision Dossier β gate deploys, releases, and infra changes; shadow or enforce.
https://github.com/decionis/govern
ci-cd compliance deployment github-actions governance security
Last synced: 23 days ago
JSON representation
Govern any GitHub workflow step on a signed Decionis Decision Dossier β gate deploys, releases, and infra changes; shadow or enforce.
- Host: GitHub
- URL: https://github.com/decionis/govern
- Owner: decionis
- License: mit
- Created: 2026-05-31T20:03:15.000Z (23 days ago)
- Default Branch: main
- Last Pushed: 2026-05-31T21:57:54.000Z (23 days ago)
- Last Synced: 2026-05-31T22:10:28.785Z (23 days ago)
- Topics: ci-cd, compliance, deployment, github-actions, governance, security
- Language: JavaScript
- Homepage: https://decionis.com/marketplace/github
- Size: 21.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# π‘οΈ Decionis Action Gate
**Govern high-risk actions before they execute.**
[](https://github.com/marketplace/actions/decionis-action-gate)
[](https://github.com/decionis/govern)
[](./LICENSE)
> **Before software executes, Decionis decides whether it's allowed to.**
GitHub Actions runs the code. **Decionis decides whether the run is authorized.** Add one step and every deploy, migration, infra change, or AI-generated PR is evaluated against your policy, approvals, and risk β then **allowed, blocked, or escalated** before it executes, with a signed Decision Dossier as audit-ready proof.
Start in **shadow mode**, where it _never fails your build_ β observe what it would have caught, then flip one line to `enforce`.
---
## The question every pipeline now faces
AI coding agents β **Claude Code, Copilot, Cursor, Codex, OpenHands** β now open PRs, edit workflows, write migrations, and trigger deploys. The hard question is no longer _"who wrote this code?"_ It's:
> **Should this action be allowed to execute?**
That's the Action Gate.
## 30-second quickstart
**Wrap the command you want to govern.** Decionis runs it _through_ the gate, so it can't execute without an authorizing verdict:
```yaml
- uses: decionis/govern@v1
with:
api-key: ${{ secrets.DECIONIS_API_KEY }}
org-id: ${{ secrets.DECIONIS_ORG_ID }}
workflow-key: github_deploy_approval
action: production-deploy
run: ./deploy.sh # β Decionis runs this ONLY if it authorizes the action
```
On `allow` the command runs. On `block`/`escalate` it **never runs** and the step fails. Try it risk-free with `mode: shadow` β the command still runs, but Decionis only records the verdict (never fails the build):
```yaml
- uses: decionis/govern@v1
with:
api-key: ${{ secrets.DECIONIS_API_KEY }}
org-id: ${{ secrets.DECIONIS_ORG_ID }}
workflow-key: github_deploy_approval
action: production-deploy
run: ./deploy.sh
mode: shadow # observe-only; records the verdict, never blocks
comment-pr: "true" # posts the verdict + verify link on the PR
```
Need keys? Create them free at **[decionis.com/quickstart?source=github_action](https://decionis.com/quickstart?source=github_action)** β no card, no call.
### Why a wrapper, not an `if:`
A common pattern is to gate on the output:
```yaml
- uses: decionis/govern@v1
id: gate
- run: ./deploy.sh
if: steps.gate.outputs.decision == 'allow' # β οΈ advisory β can be deleted
```
That `if:` is **advisory**. Anyone β or any AI agent editing the workflow β can delete one line and the deploy runs ungoverned. With `run:`, **Decionis owns the execution path**: the command only exists inside the gate, so bypassing it means rewriting the step β a visible, reviewable diff (protect `.github/workflows/**` with CODEOWNERS + branch protection to close that too). The verdict-only + `if:` form still works for cases where you can't wrap the command, but reach for `run:` whenever you actually need to _enforce_.
---
## Three concepts
### 1. π¦ Action Gate
One verdict before execution β **`allow`**, **`block`**, or **`escalate`**. Composable into any later step via `steps..outputs.decision`.
### 2. π£ Shadow Mode
`mode: shadow` records what _would_ have been blocked without ever failing a build. Watch the would-have-blocked numbers, then enforce when the evidence convinces you.
### 3. π§Ύ Decision Dossiers
Every verdict produces a signed, public-verifiable [Decision Dossier](https://decionis.com/dossier-example?source=github_action): **why** it happened, **who** approved it, **which policy** applied, and the **risk**. The verify link unfurls as an OG card in Slack / Teams / LinkedIn β paste it in an incident, a change ticket, or an audit and it holds up.
---
## Lead use case β govern AI-generated changes
When an AI agent opens a PR or triggers a deploy, gate it **before** it merges or ships:
```yaml
- uses: decionis/govern@v1
id: gate
with:
api-key: ${{ secrets.DECIONIS_API_KEY }}
org-id: ${{ secrets.DECIONIS_ORG_ID }}
workflow-key: ai_change_gate
action: ai-generated-pr
comment-pr: "true"
payload: |
{ "author": "${{ github.actor }}", "agent_generated": true }
```
See [`examples/gate-ai-agent-pr.yml`](./examples/gate-ai-agent-pr.yml) for the full recipe (auto-detects agent authorship and requires a human verdict on risky changes).
## Also governs
- **Deployments** β production releases, blue/green cutovers
- **Infrastructure** β `terraform apply`, Pulumi, CDK
- **Data** β database migrations, destructive jobs
- **Privileged workflows** β release pipelines, secrets rotation, IAM changes
## What reviewers see on the PR
A single, **self-updating** comment (re-runs edit it in place β no thread spam):

## π Add the badge
Show your pipeline is governed β and let other devs discover the gate. Also emitted as the `badge-markdown` output (pointing at the live verify URL):
```markdown
[](https://github.com/decionis/govern)
```
[](https://github.com/decionis/govern)
---
## Recipes
Copy-paste workflows in [`examples/`](./examples/):
| Recipe | What it gates |
| ------------------------------------------------------------------- | -------------------------------------------------------------- |
| [`gate-ai-agent-pr.yml`](./examples/gate-ai-agent-pr.yml) | AI-generated PRs (Claude Code, Copilot, Cursorβ¦) before merge. |
| [`gate-deploy.yml`](./examples/gate-deploy.yml) | A production deploy on a `block` verdict (enforce). |
| [`gate-terraform.yml`](./examples/gate-terraform.yml) | `terraform apply` on the plan's blast radius. |
| [`gate-release.yml`](./examples/gate-release.yml) | A verdict before a tagged release ships. |
| [`auto-merge-dependabot.yml`](./examples/auto-merge-dependabot.yml) | Auto-merge a dependency PR only when the verdict is `allow`. |
| [`gate-pr-comment.yml`](./examples/gate-pr-comment.yml) | Shadow-mode evaluator that comments without failing the build. |
## Inputs
| Input | Required | Default | Description |
| -------------------- | -------- | ----------------------------- | ------------------------------------------------------------------ |
| `api-key` | yes | β | Decionis API key with `protocol:evaluate` scope. Pass as a secret. |
| `org-id` | yes | β | Decionis org id (UUID). |
| `workflow-key` | yes | β | Workflow key registered in Decionis policy. |
| `action` | no | β | Short label for what's being gated (e.g. `production-deploy`). |
| `run` | no | β | Command Decionis runs **only if authorized** (the enforcing path). |
| `shell` | no | `bash` | Shell for `run` β `bash` or `sh`. |
| `payload` | no | _built from workflow context_ | JSON object describing the action being gated. |
| `fail-on` | no | `block` | `block` / `escalate` / `block_or_escalate` / `never`. |
| `mode` | no | `enforce` | `enforce` or `shadow`. Shadow never fails the step. |
| `comment-pr` | no | `false` | Post (and update in place) the verdict as a PR comment. |
| `show-attribution` | no | `true` | Include the "Governed by Decionis" footer on the PR comment. |
| `api-base-url` | no | `https://api.decionis.com` | Override for staging / self-host. |
| `site-base-url` | no | `https://decionis.com` | Override for staging / self-host. |
| `request-timeout-ms` | no | `20000` | Timeout for the evaluate-decision call. |
## Outputs
| Output | Description |
| ---------------- | --------------------------------------------------------------------------- |
| `decision` | `allow` / `block` / `escalate` / `restrain` |
| `dossier-id` | Signed Decision Dossier id for this evaluation. |
| `verify-url` | Public verify URL (`?sig=` so unfurls render the verdict OG card). |
| `policy-version` | Policy version (string) that produced the verdict. |
| `reason-code` | Stable reason code (string), if returned. |
| `badge-markdown` | Ready-to-paste "Governed by Decionis" badge linking to the live verify URL. |
| `executed` | `true` if a `run` command was authorized and executed, `false` if blocked. |
## Permissions
Default (`contents: read`) is enough. To enable `comment-pr: 'true'`:
```yaml
permissions:
contents: read
pull-requests: write
```
## How it works
The action calls `POST /v1/protocol/evaluate-decision` with the action label + payload (or a payload built from the workflow context), and returns the signed verdict. Inputs are echoed into the dossier so you can audit exactly what produced it. `shadow` mode **never** fails the step; a non-200 from the API fails the step with the status β no silent green builds.
---
Built by [Decionis](https://decionis.com?source=github_action_readme) Β· [Quickstart](https://decionis.com/quickstart?source=github_action) Β· [Dossier example](https://decionis.com/dossier-example?source=github_action) Β· MIT licensed