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

# Git Operations

> Use standard Git commands with Code Storage over HTTPS. Clone, push, pull, and fetch with JWT-authenticated remotes.

Code Storage supports standard Git operations over HTTPS. You can use any Git client with
JWT-authenticated remote URLs.

You can also create commits and update refs directly through the SDK or the HTTP API, without a
local clone. See the [SDK page](https://code.storage/docs/sdks.md).

## Authentication format

Git commands use HTTP Basic Auth with a JWT-backed remote. The format is consistent across every
repository:

* **Username**: Always `t` (for "token")
* **Password**: Your JWT token
* **Repository**: Must match the `repo` claim in your JWT

Example:

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git clone https://t:eyJhbGciOiJSUzI1NiI...@your-name.code.storage/repo-id.git
```

The SDK and dashboard mint these URLs for you, but you can also construct them manually once you
have a JWT. If authentication fails, confirm that:

* The JWT `repo` claim matches the repository you are cloning or pushing.
* The token includes the scopes required for the command (`git:read`, `git:write`, or `repo:write`).
* The token has not expired (`exp` claim)

## Supported commands

Every command uses the same remote syntax and JWT authentication scheme.

### Read operations (`git:read`)

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git clone https://t:JWT@your-name.code.storage/repo-id.git
git fetch origin
git pull origin main
```

### Write operations (`git:write`)

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git push origin main
git push --tags
git push --force
```

The SDK's [`remoteUrl`](https://code.storage/docs/sdks#git-remote-urls) helper (`remote_url` in Python, `git.RemoteURL` in
Go) generates the JWT-backed URL for you. You can also mint JWTs manually through the authentication
flow described in [Authentication](https://code.storage/docs/platform/authentication.md#how-to-sign-a-jwt). Once you have the
URL, Git behaves exactly the way it does against any other HTTPS remote.

## Integration patterns

To give your users Git access with your own authentication and access logs, give them a remote on
your domain that forwards to Code Storage. See
[Proxy Git with Your Own Auth](https://code.storage/docs/guides/proxy-git-with-your-own-auth.md).

Use scoped JWTs to differentiate between automation, developers, and product-level access. Short
TTLs keep CI credentials disposable. The token of a remote URL lives 15 minutes by default and 1
hour at most. For developer machines or [preview environments](https://code.storage/docs/repos/ephemeral-branches.md) that keep
a remote, use the credential helper of the CLI.

### CI/CD pipeline

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 { execFileSync } from 'node:child_process';

import { Pierre } from '@pierre/sdk';
import { remoteUrl } from '@pierre/sdk/git';

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

// Generate a short-lived URL for CI.
const ciUrl = await remoteUrl(pierre, {
  repo_name: 'acme/web',
  scopes: ['git:read', 'git:write'],
  ttl: 3600,
});

execFileSync('git', ['clone', ciUrl, 'repo'], { stdio: 'inherit' });
execFileSync('pnpm', ['--dir', 'repo', 'test'], { stdio: 'inherit' });
execFileSync('git', ['-C', 'repo', 'push', 'origin', 'main'], { stdio: 'inherit' });
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import subprocess

from pierre import Pierre
from pierre.git import remote_url

pierre = Pierre()  # reads PIERRE_ORG and PIERRE_PRIVATE_KEY

ci_url = remote_url(
    pierre,
    repo_name="acme/web",
    scopes=["git:read", "git:write"],
    ttl=3600,
)

subprocess.run(["git", "clone", ci_url, "repo"], check=True)
subprocess.run(["pnpm", "--dir", "repo", "test"], check=True)
subprocess.run(["git", "-C", "repo", "push", "origin", "main"], check=True)
```

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

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

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

ciURL, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "acme/web",
	Scopes:   []string{"git:read", "git:write"},
	TTL:      time.Hour,
})
if err != nil {
	return err
}

commands := []*exec.Cmd{
	exec.Command("git", "clone", ciURL, "repo"),
	exec.Command("pnpm", "--dir", "repo", "test"),
	exec.Command("git", "-C", "repo", "push", "origin", "main"),
}
for _, command := range commands {
	if err := command.Run(); err != nil {
		return err
	}
}
```

### Development environment

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
// Generate a URL for a development session. Its token lives 1 hour.
const devUrl = await remoteUrl(pierre, {
  repo_name: 'acme/web',
  scopes: ['git:read', 'git:write'],
  ttl: 3600,
});

console.log(`Add to .git/config: ${devUrl}`);
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
dev_url = remote_url(
    pierre,
    repo_name="acme/web",
    scopes=["git:read", "git:write"],
    ttl=3600,
)

print(f"Add to .git/config: {dev_url}")
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
devURL, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "acme/web",
	Scopes:   []string{"git:read", "git:write"},
	TTL:      time.Hour,
})
if err != nil {
	return err
}

fmt.Printf("Add to .git/config: %s\n", devURL)
```

For a machine that keeps the remote longer, let Git get its credential from the CLI. The helper
signs a new token for each Git command, and no token appears in a URL or in a file:

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
pierre auth setup-git
git clone https://your-name.code.storage/repo-id.git
```

The helper uses the key that `pierre auth login` stores, or `PIERRE_PRIVATE_KEY`. See
[Sign in](https://code.storage/docs/cli.md#sign-in) and [`pierre auth setup-git`](https://code.storage/docs/cli#pierre-auth-setup-git).

When in doubt, mint tokens with the SDK. Its helpers sign the token and build the URL for you. See
[Git remote URLs](https://code.storage/docs/sdks.md#git-remote-urls).


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