---
title: "Sign release tags and require verified · Agent Wranglers"
url: https://agentwranglers.se/playbook/engineering/ci-and-supply-chain/signed-commits-and-tags
id: ENG-59
level: Recommended
applies: team
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1171
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Sign release tags and require verified commits on main

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [CI and supply chain](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: ENG-59
- Level: Recommended
- Chapter: [CI and supply chain](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain.md)
- Rule: Sign every release tag. Require a verified signature on every commit that reaches the main branch, so a stolen password alone cannot add code under someone's name.
- Check: The main branch rules include required signatures, such as a `required_signatures` rule in `gh api repos/example/shop/rules/branches/main`. Push an unsigned commit to a test branch with the same rules. The code host refuses it. `git verify-tag` passes on the latest release tag.
- Applies when: The project has two or more developers.
- How: The author name on a commit is free text, so anyone can write any name. A signature ties the commit to a key the person holds. Git signs with GPG, SSH or X.509 keys, and an SSH key that already pushes code is the simplest to set up. Upload the signing key to the code host. Turn on required signatures for the main branch in its branch rules. Commits that the code host creates for a merge are signed by the host. Sign release tags with `git tag -s`, so consumers can check them as [Make release tags and files immutable](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain/immutable-releases.md) says. Signing proves who committed, not that the code is safe, so review still follows [Protect the main branch](https://agentwranglers.se/playbook/engineering/version-control-and-review/protect-the-main-branch.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### Shell example

```sh
# Sign with the SSH key you already use
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# Check: main requires signatures, and the last release tag verifies
gh api repos/example/shop/rules/branches/main --jq '.[].type' | grep -qx required_signatures || { echo "FAIL: signatures not required"; exit 1; }
git verify-tag "$(git describe --tags --abbrev=0)"
```

### Python example

```python
# CI gate: every commit in the pull request carries a good signature
import subprocess, sys

commits = subprocess.run(["git", "rev-list", "origin/main..HEAD"], capture_output=True, text=True, check=True).stdout.split()
unsigned = [c for c in commits if subprocess.run(["git", "verify-commit", c], capture_output=True).returncode != 0]
if unsigned:
    sys.exit(f"FAIL: unsigned commits {', '.join(unsigned)}")
```

### JavaScript example

```js
// The same gate in Node.js
import { execFileSync, spawnSync } from 'node:child_process';

const commits = execFileSync('git', ['rev-list', 'origin/main..HEAD'], { encoding: 'utf8' }).split('\n').filter(Boolean);
const unsigned = commits.filter(commit => spawnSync('git', ['verify-commit', commit]).status !== 0);
if (unsigned.length) throw new Error(`Unsigned commits ${unsigned.join(', ')}`);
```

## Sources

- [GitHub: About commit signature verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
- [Pro Git book on signing your work](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work)

## Related rules

- ENG-42 [Make release tags and files immutable](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain/immutable-releases.md) in Engineering
- ENG-15 [Protect the main branch](https://agentwranglers.se/playbook/engineering/version-control-and-review/protect-the-main-branch.md) in Engineering

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/engineering/ci-and-supply-chain/signed-commits-and-tags">Agent Wranglers engineering playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Block pushes that contain secrets](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain/secret-push-protection.md)
- [CI and supply chain](https://agentwranglers.se/playbook/engineering/ci-and-supply-chain.md)
