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

# Commit Signing

> Register OpenPGP or SSH public keys. Use a ref policy to require signed commits on selected refs.

Git creates each commit signature. Code Storage checks the signature when a `verify-sig` ref policy
applies to an update.

The **Commit signing keys** page stores public keys for your organization. A registered key does not
enable a check by itself.

## How the parts work together

| Part | Purpose |
| - | - |
| Git | Create an OpenPGP or SSH signature in the commit object. |
| **Commit signing keys** | Store the public keys that Code Storage trusts for your organization. |
| `verify-sig` ref policy | Require a valid signature when a token updates a matched ref. |
| `repos.commits.get` | Return the signature and its exact payload for a client check. |

## Require signed commits

### 1. Create a signed commit

Configure Git for OpenPGP or SSH signatures. Then use `-S` to sign a commit.

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git commit -S -m "Add the release manifest"
git log --show-signature -1
```

### 2. Register the public key

Open **Commit signing keys** in the dashboard. Select **New signing key**, then paste an OpenPGP or
SSH public key.

Signing keys belong to the organization. Code Storage reads the current key list for each ref
update.

### 3. Add `verify-sig` to a ref policy

This policy requires a registered signature on each commit that enters `main`.

Set `PIERRE_ORG` and `PIERRE_PRIVATE_KEY` as shown in the
[Quick Start](https://code.storage/docs/getting-started/quick-start.md).

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Pierre } from '@pierre/sdk';
import { remoteUrl } from '@pierre/sdk/git';

const pierre = new Pierre(); // reads PIERRE_ORG and PIERRE_PRIVATE_KEY

const url = await remoteUrl(pierre, {
  repo_name: 'acme/web',
  scopes: ['git:read', 'git:write'],
  ref_policies: [{ pattern: 'main', ops: ['verify-sig'] }],
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from pierre import Pierre
from pierre.git import remote_url

pierre = Pierre()  # reads PIERRE_ORG and PIERRE_PRIVATE_KEY

url = remote_url(
    pierre,
    repo_name="acme/web",
    scopes=["git:read", "git:write"],
    ref_policies=[{"pattern": "main", "ops": ["verify-sig"]}],
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import (
	"context"
	"fmt"

	pierre "github.com/pierrecomputer/sdk/go"
	"github.com/pierrecomputer/sdk/go/git"
	"github.com/pierrecomputer/sdk/go/option"
)

ctx := context.Background()
client, err := pierre.NewClient() // reads PIERRE_ORG and PIERRE_PRIVATE_KEY
if err != nil {
	return err
}

url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "acme/web",
	Scopes:   []string{"git:read", "git:write"},
	RefPolicies: []option.RefPolicy{
		{Pattern: "main", Ops: []string{"verify-sig"}},
	},
})
if err != nil {
	return err
}
fmt.Println(url)
```

Use `ops: ['verify-sig', 'no-force-push']` to require signatures and reject a history rewrite.

See [Ref Policies](https://code.storage/docs/repos/ref-policies.md) for rule order, patterns, and ephemeral refs.

### 4. Push the signed commit

Use the URL with this policy as a Git remote. Code Storage rejects the complete update if one
introduced commit fails the check.

## What Code Storage checks

Code Storage checks commits that the update introduces to the matched ref.

* For a new ref, Code Storage checks every commit that the new tip can reach.
* For a ref that already exists, Code Storage checks commits that its new tip can reach but its
  prior tip cannot reach.
* Code Storage checks a commit again when it first enters another protected ref.

Code Storage rejects the update in these cases:

* A commit has no signature.
* No registered key matches the signature.
* The signature data is invalid or uses an unsupported format.
* The organization has no registered signing keys.

A ref deletion introduces no commits. The `verify-sig` operation does not reject that deletion.

## Server-created commits

> [!WARNING]
>
> Do not target a `verify-sig` ref with `repos.commits.create`, `repos.commits.createFromDiff`, or
> `repos.merge`. These methods create unsigned commits.

Use a signed Git push when a ref requires a signature. Code Storage does not create a signature for
a server-created commit.

## Read signature data

You can read signature data without a ref policy. `repos.commits.get` returns an armored `signature`
and the exact `payload` for a signed commit.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
async function readSignature(
  sha: string,
  checkSignature: (payload: string, signature: string) => void,
) {
  const { commit } = await pierre.repos.commits.get({ repo_name: 'acme/web', ref: sha });

  if (commit.signature && commit.payload) {
    checkSignature(commit.payload, commit.signature);
  }
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from collections.abc import Callable


def read_signature(sha: str, check_signature: Callable[[str, str], None]) -> None:
    result = pierre.repos.commits.get(repo_name="acme/web", ref=sha)
    commit = result["commit"]

    if commit.get("signature") and commit.get("payload"):
        check_signature(commit["payload"], commit["signature"])
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
func readSignature(
	ctx context.Context,
	client *pierre.Client,
	sha string,
	checkSignature func(payload, signature string) error,
) error {
	result, err := client.Repos.Commits.Get(ctx, pierre.ReposCommitsGetParams{
		RepoName: "acme/web",
		Ref:      pierre.String(sha),
	})
	if err != nil {
		return err
	}

	signature, signed := result.Commit.Signature.Get()
	payload, hasPayload := result.Commit.Payload.Get()
	if signed && hasPayload {
		return checkSignature(payload, signature)
	}
	return nil
}
```

This method does not return a trust result. Check the pair with an OpenPGP or SSH library. The
example takes that check as a function.

See [`repos.commits.get`](https://code.storage/docs/api/repos/commit-get) for the complete response.

## Remove a key

Delete a key. Code Storage rejects a signature from that key on the next ref update.

The deletion does not change stored commits or their signatures.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.