⚘ use your pds as a git remote if you want to ⚘
Rust 76.5%
Python 13.2%
Shell 9.3%
Nix 1.1%
20 1 0

Clone this repository

https://tangled.org/notplants.bsky.social/git-remote-pds https://tangled.org/did:plc:3nogfd4smhmbrv4wo6kl7zg2/git-remote-pds
git@knot.tangled.wizardry.systems:notplants.bsky.social/git-remote-pds git@knot.tangled.wizardry.systems:did:plc:3nogfd4smhmbrv4wo6kl7zg2/git-remote-pds

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

git-remote-pds#

This is not the most efficient way to do git, and is not meant as a replacement for tangled, or for anything really.

However it seems to work, and it can be used by anyone with a PDS or a bluesky account, without installing anything additionally on their server.

Tangled currently requires the use of a knot server as well as the use of ssh keys for git access (although from talking with the developers, there is some discussion of changing things in the future to allow authentication without ssh keys). git-remote-pds skips these requirements as well as efficiency.

git-remote-pds is a git remote helper that stores repositories on an AT Protocol Personal Data Server (PDS) directly.

# log in to your PDS via OAuth (opens browser)
git-remote-pds auth oauth-login --handle alice.example.com

# push an existing repo
cd my-project
git remote add pds pds://alice.example.com/my-project
git push pds main

# clone it elsewhere
git clone pds://alice.example.com/my-project

Repositories are stored as chains of incremental git bundles uploaded as PDS blobs, tracked by a mutable state record.

wip#

  • I have done basic tests to confirm that it works,
  • the design and code could be reviewed
  • haven't done any benchmarking

This was actually created as a subtask for a mostly unrelated project (more soon).

git-ssb <3#

primary inspiration was git-ssb. another tool that may not have been the most efficient way to do git, but I really loved for multiple reasons (link)

quickstart#

# install
cargo install --path .

# log in to your PDS (opens browser for authorization)
git-remote-pds auth oauth-login --handle alice.example.com

# push an existing repo
cd my-project
git remote add pds pds://alice.example.com/my-project
git push pds main

# clone it elsewhere
git clone pds://alice.example.com/my-project

how it works#

Git invokes git-remote-pds automatically when it sees a pds:// URL. The remote helper speaks git's remote helper protocol over stdin/stdout.

Push: creates a git bundle of new commits, uploads it as a PDS blob, and writes a state record tracking the bundle chain and current refs.

Fetch/Clone: reads the remote state record, downloads bundles the local repo doesn't have yet, and applies them with git bundle unbundle.

Bundles larger than 40 MB are automatically chunked into multiple blobs to stay within PDS upload limits.

State record#

Each repository is stored under the net.commoninternet.pdsgit.state collection as a single record. The record contains:

  • refs — current branch tips (name + SHA)
  • bundles — ordered chain of bundle entries, each with blob CIDs, prerequisite commits, and tip commits

Authentication#

git-remote-pds supports two ways to authenticate with a PDS. Credentials are stored per-handle in ~/.config/git-remote-pds/auth.json. If you have multiple accounts, each handle has its own credential — git-remote-pds uses the matching handle from the pds://handle/repo URL.

git-remote-pds auth oauth-login --handle alice.example.com

Opens your browser to authorize with your PDS via AT Protocol OAuth. This uses the loopback client flow (no server required) and stores DPoP-bound tokens locally. Tokens are short-lived (~5 minutes) but include a refresh token for renewal.

For a PDS that isn't discoverable via handle resolution (e.g. local dev), pass --pds-url:

git-remote-pds auth oauth-login --handle alice.example.com --pds-url https://your-pds.example.com

The --port flag controls which localhost port the OAuth callback listens on (default: 8271).

Password login#

git-remote-pds auth login --pds-url https://your-pds.example.com --handle alice.example.com

Authenticates with handle + password via com.atproto.server.createSession and stores a Bearer token. This is simpler but requires your password (or an app password), and AT Protocol is moving toward deprecating password-based auth in favor of OAuth.

auth status#

git-remote-pds auth status

Shows stored credentials, auth method, and token status:

alice.example.com
  did:     did:plc:abc123
  pds:     https://pds.example.com
  auth:    OAuth (DPoP)
  token:   valid (expires in 3m 20s)

bob.example.com
  did:     did:plc:def456
  pds:     https://other-pds.example.com
  auth:    createSession (Bearer)

auth logout#

git-remote-pds auth logout --handle alice.example.com

Environment variables#

For CI or scripting, you can authenticate without stored credentials:

Variable Description
PDS_HANDLE + PDS_PASSWORD Log in on the fly via createSession
PDS_ACCESS_TOKEN + PDS_DID Use a token directly (Bearer)
PDS_ACCESS_TOKEN + PDS_DID + PDS_DPOP_KEY Use a DPoP-bound token directly
PDS_URL Override PDS endpoint (skips identity resolution)

Environment variables take priority over stored credentials.

Identity resolution#

The pds://handle/repo URL format uses AT Protocol identity resolution:

  1. Resolve handle to DID via com.atproto.identity.resolveHandle
  2. Resolve DID to PDS endpoint via PLC directory or did:web

Set PDS_URL to skip resolution and point directly at a PDS (useful for local development).

Development#

Running tests#

# unit and integration tests (no PDS required)
cargo test

# e2e tests (used via scripts with docker, see below)
cargo test --features e2e

Playwright tests (OAuth flow)#

End-to-end tests for the OAuth login and push flow using Playwright:

./e2e-tests/playwright-tests/run.sh

Tests oauth-login with browser automation, then pushes and clones to verify.

Local PDS (Docker)#

./e2e-tests/pds-dev/start.sh                          # start PDS at localhost:3000
./e2e-tests/pds-dev/create-account.sh alice secret123  # create a test account
./e2e-tests/pds-dev/run-e2e.sh                         # full test harness
./e2e-tests/pds-dev/stop.sh                            # tear down

Remote PDS testing#

Create a tests/.testenv file:

PDS_URL=https://your-pds.example.com
PDS_HANDLE=you.your-pds.example.com
PDS_PASSWORD=your-password

Then run:

./e2e-tests/remote-test/run.sh

This tests push, clone, and incremental fetch against the configured PDS.

License#

MIT