> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jacobpevans.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Git signing

> Every commit lands GitHub-verified. The mechanism is uniform — Contents API web-flow signing — but the identity depends on who's making the call.

> Every commit is signed. The path is always Contents API web-flow; the identity changes with the execution context.

`required_signatures` is enforced org-wide by Repository Rulesets on every protected branch. A commit without a valid web-flow or GPG signature is rejected at the API. The five rows below are the only ways to land a commit that satisfies the ruleset.

## Identity per context

| Context                                                                                               | Identity                          | Auth                                                                               | Signing path                                                                                                                                            |
| ----------------------------------------------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Local Mac                                                                                             | `JacobPEvans`                     | local user                                                                         | GPG (key `31652F22BF6AC286`); nix-home reads identity from `$XDG_CONFIG_HOME/nix-home/local.nix`                                                        |
| GitHub Actions — deterministic (snake, 3d-contrib, `peter-evans/create-pull-request`, release-please) | `JacobPEvans-github-actions[bot]` | default `GITHUB_TOKEN`                                                             | web-flow via the action's Contents API call                                                                                                             |
| GitHub Actions — AI workflows                                                                         | `JacobPEvans-claude[bot]`         | `JacobPEvans-claude` App installation token (`actions/create-github-app-token@v2`) | web-flow via `anthropics/claude-code-action@v1` with `use_commit_signing: true` and `github_token` set to the App token                                 |
| Native cloud-routine workflows (`claude-code-routines`)                                               | `JacobPEvans-claude[bot]`         | same App installation token                                                        | same Contents API path; routine bodies run inside a regular GHA workflow (not the Anthropic Cloud Routines sandbox, which cannot mint App-class tokens) |
| GitHub bots (Renovate, release-please releases, dependabot)                                           | the bot's GitHub identity         | managed by GitHub                                                                  | web-flow                                                                                                                                                |

Verification for any commit:

```bash theme={null}
gh api repos/<owner>/<repo>/commits/<sha> \
  --jq '{verified: .commit.verification.verified, reason: .commit.verification.reason, login: .author.login}'
```

Expect `verified: true`, `reason: "valid"`, and the `login` from the table above.

## AI workflow App-token pattern

Every reusable AI workflow in `JacobPEvans/ai-workflows` mints a `JacobPEvans-claude` installation token immediately before calling `anthropics/claude-code-action@v1`, then hands the token in as `github_token`. The action's `use_commit_signing: true` sends edits through the Contents API, which web-flow-signs every commit and attributes it to whichever bot owns the token.

```yaml theme={null}
- name: Run Claude Code with bot attribution
  uses: dryvist/ai-workflows/.github/actions/run-claude-code@main
  with:
    ai_provider: ${{ vars.AI_PROVIDER || 'claude_oauth' }}
    ai_token: ${{ secrets.AI_TOKEN }}
    ai_base_url: ${{ vars.AI_BASE_URL || secrets.AI_BASE_URL || '' }}
    allowed_bots: "github-actions"
    use_commit_signing: "true"
    prompt: ${{ steps.prompt.outputs.content }}
    allowed_tools: "Edit,MultiEdit,Write,Read,Glob,Grep,LS,Bash(git diff:*)"
    model: ${{ vars.AI_MODEL_CODE || vars.AI_MODEL || 'sonnet' }}
    app_id: ${{ vars.GH_APP_CLAUDE_BOT_ID }}
    app_private_key: ${{ secrets.GH_APP_CLAUDE_BOT_PRIVATE_KEY }}
```

Consumers do not import the App credentials themselves. [`secrets-sync`](/security/secrets-sync) distributes `vars.GH_APP_CLAUDE_BOT_ID` and `secrets.GH_APP_CLAUDE_BOT_PRIVATE_KEY` to every repo in the `*github_app_repos` set. To add a new consumer repo: append it to that anchor in `JacobPEvans/secrets-sync/secrets-config.yml` and re-run the distribution workflow.

The AI never constructs Contents API payloads itself. It edits files locally inside the workflow; `claude-code-action@v1` translates those edits into Contents API calls on commit. AI workflows enjoy normal file-editing ergonomics without losing signed-commit attribution.

## Deterministic GHA pattern

Workflows where no AI generates content use whichever marketplace action fits. They sign automatically as long as the action makes its commits through the Contents API rather than runner-side `git commit`.

| Producer                                     | Action                                                                                      | Notes                                                        |
| -------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `JacobPEvans/JacobPEvans` snake / 3d-contrib | `peter-evans/create-pull-request@v8` with `sign-commits: true`, then `gh pr merge --squash` | The action wraps the Contents API for arbitrary file updates |
| Release commits (release-please)             | `googleapis/release-please-action@v5`                                                       | Native web-flow signing                                      |
| Built-in commit modes                        | e.g. `lowlighter/metrics` with `output_action: commit`                                      | Action handles signing itself                                |

Never build a custom Contents-API or Git-Data-API helper for content the runner can hand off to a trusted action.

## Adding a new context

Pick a real identity — App, user, or bot. Never anonymous. Decide auth:

* **App installation token** (`actions/create-github-app-token@v2`) when commits must be attributed to `JacobPEvans-claude[bot]`.
* **Default `GITHUB_TOKEN`** when commits should be attributed to `JacobPEvans-github-actions[bot]`.
* **GPG / SSH on the runner** only when a workflow truly needs `git commit` on the runner (rebase, cherry-pick, generated patches that do not fit the Contents API). Document the exception inline.

Add a row to the identity table above; document operator setup in the consuming repo.

## Canonical sources

Single source of truth — link from other repos rather than duplicating prose.

* Architecture: this page
* Cloud-routine operator setup: `docs/CLOUD_ROUTINES_AUTH.md` in [`JacobPEvans/claude-code-routines`](https://github.com/JacobPEvans/claude-code-routines)
* Local Mac identity values: `$XDG_CONFIG_HOME/nix-home/local.nix` (gitignored, out-of-tree)
* AI-action App-token pattern: the reusable workflows in [`JacobPEvans/ai-workflows/.github/workflows/`](https://github.com/JacobPEvans/ai-workflows/tree/main/.github/workflows) (issue-resolver, ci-fix, code-simplifier, post-merge-tests, post-merge-docs-review, final-pr-review)
* Native cloud-routine pattern: [`JacobPEvans/claude-code-routines/.github/workflows/issue-solver.yml`](https://github.com/JacobPEvans/claude-code-routines/blob/main/.github/workflows/issue-solver.yml)
* App credential distribution: the `secrets:` block in [`JacobPEvans/secrets-sync/secrets-config.yml`](https://github.com/JacobPEvans/secrets-sync/blob/main/secrets-config.yml) (`GH_APP_CLAUDE_BOT_PRIVATE_KEY`, `GH_APP_CLAUDE_BOT_ID`)

## Where to go next

<CardGroup cols={2}>
  <Card title="CI/CD policy" icon="scale-balanced" href="/infrastructure/cicd/policy">
    Marketplace actions, release-please, version pinning, runner choice.
  </Card>

  <Card title="secrets-sync" icon="rotate" href="/security/secrets-sync">
    How the GitHub App credentials reach every consumer repo.
  </Card>

  <Card title="Golden laws" icon="shield-halved" href="/security/golden-laws">
    Why every commit is signed, audit-trail rules, MFA on elevated sessions.
  </Card>
</CardGroup>
