> ## 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.

# Ref Policies

> Use JWT ref policies for branch protection. Limit the refs that a token can update and the update types it permits.

Code Storage uses ref policies for branch protection. Each ref policy limits writes from one JWT.

A ref policy is not a repository setting. It applies only to a request that uses its JWT. Give each
client or task a JWT with the limits it needs.

> [!NOTE]
>
> A `git:write` scope grants write access. A ref policy can limit that access, but it cannot grant
> access.

The remote URL helper of each SDK accepts the rules for each remote `kind`: `ref_policies` on
`remoteUrl` in TypeScript and `remote_url` in Python, and `RefPolicies` on `git.RemoteURL` in Go.
Each SDK method also accepts them as a request option, and a client option sets them for every call.
See [Ref policies](https://code.storage/docs/sdks.md#ref-policies) on the SDK page.

## Policy operations

Each SDK takes an operation as a plain string in `ops`, for example `'no-push'`.

| Operation | Effect |
| - | - |
| `no-force-push` | Reject a non-fast-forward move when the ref already exists. Permit ref creation, ref deletion, and a fast-forward move. |
| `no-push` | Reject all updates to the matched ref. This includes ref creation and deletion. |
| `verify-sig` | Reject an update when an introduced commit lacks a valid signature from a registered key. |

## Add a ref policy

The ref policies option is an ordered list of rules. Each rule has a `pattern` and an optional `ops`
list.

This policy permits branches under `agents/` in the default namespace. It rejects all other refs.

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, type RefPolicy } from '@pierre/sdk';
import { remoteUrl } from '@pierre/sdk/git';

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

const agentPolicies: RefPolicy[] = [
  { pattern: 'agents/*' }, // Permit these branches.
  { pattern: '*', ops: ['no-push'] }, // Reject every other ref.
];

const url = await remoteUrl(pierre, {
  repo_name: 'acme/web',
  scopes: ['git:read', 'git:write'],
  ref_policies: agentPolicies,
});
```

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

pierre = Pierre()  # reads PIERRE_ORG and PIERRE_PRIVATE_KEY

agent_policies: list[RefPolicy] = [
    {"pattern": "agents/*"},  # Permit these branches.
    {"pattern": "*", "ops": ["no-push"]},  # Reject every other ref.
]

url = remote_url(
    pierre,
    repo_name="acme/web",
    scopes=["git:read", "git:write"],
    ref_policies=agent_policies,
)
```

```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
}

agentPolicies := []option.RefPolicy{
	{Pattern: "agents/*"},                    // Permit these branches.
	{Pattern: "*", Ops: []string{"no-push"}}, // Reject every other ref.
}

url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName:    "acme/web",
	Scopes:      []string{"git:read", "git:write"},
	RefPolicies: agentPolicies,
})
if err != nil {
	return err
}
fmt.Println(url)
```

A rule with no `ops` permits its matched ref. Code Storage also permits a write when no rule
matches.

Add a final `*` rule with `no-push` when you need an allowlist. This rule rejects each ref that no
earlier rule permits.

## Match refs

Use an exact ref or a prefix glob. A prefix glob has one final `*` after a slash. Use `*` to match
all refs.

Code Storage treats a pattern without `refs/` as a branch name in the default namespace.

| Pattern | Pattern after normalization | Match |
| - | - | - |
| `main` | `refs/heads/main` | The `main` branch in the default namespace |
| `agents/*` | `refs/heads/agents/*` | Branches under `agents/` in the default namespace |
| `refs/heads/*` | `refs/heads/*` | All branches in the default namespace |
| `refs/tags/*` | `refs/tags/*` | All tags in the default namespace |
| `refs/namespaces/ephemeral/refs/heads/*` | unchanged | All ephemeral branches |
| `*` | unchanged | All refs in all namespaces |

Code Storage uses the first rule whose pattern matches the ref. It does not check a later rule when
the first rule has no `ops`.

> [!WARNING]
>
> Put a broad rule after each specific rule. An earlier `*` rule hides all later rules.

## Apply policies to ephemeral branches

Ref policies apply to ephemeral branches. Code Storage stores them in the `ephemeral` ref namespace.

The policy sees the complete stored ref. For example, the ephemeral branch `preview/pr-123` has this
ref:

```text theme={"theme":{"light":"github-light","dark":"min-dark"}}
refs/namespaces/ephemeral/refs/heads/preview/pr-123
```

A short `preview/*` pattern becomes `refs/heads/preview/*`. It matches branches in the default
namespace only.

| Target | Pattern |
| - | - |
| One ephemeral branch | `refs/namespaces/ephemeral/refs/heads/preview/pr-123` |
| Ephemeral branches under `preview/` | `refs/namespaces/ephemeral/refs/heads/preview/*` |
| All ephemeral branches | `refs/namespaces/ephemeral/refs/heads/*` |
| Refs in the default and ephemeral namespaces | `*` |

This URL permits only ephemeral branches under `agents/`:

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
const ephemeralUrl = await remoteUrl(pierre, {
  repo_name: 'acme/web',
  kind: 'ephemeral',
  scopes: ['git:read', 'git:write'],
  ref_policies: [
    { pattern: 'refs/namespaces/ephemeral/refs/heads/agents/*' },
    { pattern: '*', ops: ['no-push'] },
  ],
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
ephemeral_url = remote_url(
    pierre,
    repo_name="acme/web",
    kind="ephemeral",
    scopes=["git:read", "git:write"],
    ref_policies=[
        {"pattern": "refs/namespaces/ephemeral/refs/heads/agents/*"},
        {"pattern": "*", "ops": ["no-push"]},
    ],
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
ephemeralURL, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "acme/web",
	Kind:     git.KindEphemeral,
	Scopes:   []string{"git:read", "git:write"},
	RefPolicies: []option.RefPolicy{
		{Pattern: "refs/namespaces/ephemeral/refs/heads/agents/*"},
		{Pattern: "*", Ops: []string{"no-push"}},
	},
})
if err != nil {
	return err
}
fmt.Println(ephemeralURL)
```

See [Ephemeral Namespace](https://code.storage/docs/repos/ephemeral-branches.md) for remote URLs and branch promotion.

## Use a policy with an SDK call

Pass the same rules as a request option to an SDK method that writes a ref: `refPolicies` in
TypeScript, `ref_policies` in Python, and `option.WithRefPolicies` in Go. The SDK puts the rules in
the JWT for that call.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
await pierre.repos.branches.create(
  { repo_name: 'acme/web', base_ref: 'main', target_branch: 'agents/research-bot' },
  { refPolicies: agentPolicies },
);
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
pierre.repos.branches.create(
    repo_name="acme/web",
    base_ref="main",
    target_branch="agents/research-bot",
    ref_policies=agent_policies,
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
_, err = client.Repos.Branches.Create(ctx, pierre.ReposBranchesCreateParams{
	RepoName:     "acme/web",
	BaseRef:      pierre.String("main"),
	TargetBranch: pierre.String("agents/research-bot"),
}, option.WithRefPolicies(agentPolicies...))
if err != nil {
	return err
}
```

A ref policy limits these SDK write methods:

* [`repos.branches.create`](https://code.storage/docs/api/repos/branch-create) and
  [`repos.branches.delete`](https://code.storage/docs/api/repos/branch-delete)
* [`repos.tags.create`](https://code.storage/docs/api/repos/tag-create) and [`repos.tags.delete`](https://code.storage/docs/api/repos/tag-delete)
* [`repos.commits.create`](https://code.storage/docs/api/repos/commit-pack),
  [`repos.commits.createFromDiff`](https://code.storage/docs/api/repos/commit-create-from-diff), and
  [`repos.commits.restore`](https://code.storage/docs/api/repos/commit-restore)
* [`repos.merge`](https://code.storage/docs/api/repos/merge) and [`repos.upstream.pull`](https://code.storage/docs/api/repos/upstream-pull)
* [`repos.notes.create`](https://code.storage/docs/api/repos/note-create-or-append),
  [`repos.notes.append`](https://code.storage/docs/api/repos/note-append), and [`repos.notes.delete`](https://code.storage/docs/api/repos/note-delete)

The policy checks the target ref.

## Add a policy to a JWT

The SDK converts the ref policies to the `refs` JWT claim. Each claim entry is an ordered
`[pattern, ops]` pair.

```json theme={"theme":{"light":"github-light","dark":"min-dark"}}
{
  "refs": [
    ["refs/heads/main", ["no-force-push"]],
    ["*", ["no-push"]]
  ]
}
```

Keep the array order. JSON objects do not represent these rules.

See [Authentication](https://code.storage/docs/platform/authentication.md) for the other JWT claims.

## Commit signature checks

The `verify-sig` operation checks each commit that an update introduces to the matched ref. Code
Storage checks each signature against the registered organization keys.

A registered key does not enable a check by itself. Add `verify-sig` to a ref policy rule.

See [Commit Signing](https://code.storage/docs/repos/commit-signing.md) for key setup, check behavior, and an example.

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

## Where ref policies apply

Code Storage checks ref policies on these write paths:

* A Git push over HTTPS
* A supported SDK or HTTP API method that updates a ref

## Legacy `ops` option

A JWT can also hold a top-level `ops` claim. Code Storage adds these operations to the last `*`
rule, or adds a final `*` rule with them. The SDKs do not set this old claim.

Use the `refs` claim in new tokens. It shows the catch-all rule and supports specific refs.


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