Skip to main content

Overview

The receive-pack endpoint implements the Git Smart HTTP protocol for push operations. When you run git push gost main, this endpoint receives your commits, anonymizes all metadata, and creates a pull request on your behalf.

Endpoints

Discovery: Get References

GET /v1/gh/:owner/:repo/info/refs?service=git-receive-pack
Returns available references (branches, tags) and server capabilities.
owner
string
required
GitHub repository owner (user or organization)
repo
string
required
Repository name
service
string
required
Must be git-receive-pack

Response

Content-Type
string
application/x-git-receive-pack-advertisement
Example Response (pkt-line format):
001e# service=git-receive-pack\n
0000
00a527d1f28...c7f refs/heads/main\0report-status delete-refs side-band-64k quiet ofs-delta push-options\n
008f5e8a91b...4d2 refs/heads/dev\n
0000
Implementation: internal/http/handlers.go:79-128

Data Transfer: Push Commits

POST /v1/gh/:owner/:repo/git-receive-pack
Receives a Git packfile containing commits, anonymizes them, and creates a pull request.
owner
string
required
GitHub repository owner
repo
string
required
Repository name

Request Headers

Content-Type
string
required
application/x-git-receive-pack-request
Expect
string
Git clients typically send 100-continue for large pushes

Request Body

Binary Git packfile in pkt-line format:
  1. Command Section: Reference updates
    <old-sha> <new-sha> <ref>\0<capabilities>\n
    
  2. Push Options (optional):
    push-option=pr-hash=<hash>\n
    
  3. Packfile: Binary pack data starting with PACK
Example Structure:
00820000000000000000000000000000000000000000 a3f8c1d2... refs/heads/main\0report-status side-band-64k\n
0000
[binary packfile data: PACK...]

Response

Content-Type
string
application/x-git-receive-pack-result
The response uses the side-band-64k protocol with three channels:
Band 1 (0x01)
protocol
Protocol responses: unpack ok, ok refs/heads/main
Band 2 (0x02)
progress
Progress messages shown to user
Band 3 (0x03)
error
Error messages
Success Response (decoded):
remote: gitGost: Processing your anonymous contribution...
remote: gitGost: Commits anonymized successfully
remote: gitGost: Creating fork...
remote: gitGost: Fork ready at gitgost-bot/example-repo
remote: gitGost: Pushing to fork...
remote: gitGost: Branch 'gitgost-a3f8c1d2' created
remote: gitGost: Creating pull request...
remote: 
remote: ========================================
remote: SUCCESS! Pull Request Created
remote: ========================================
remote: 
remote: PR URL: https://github.com/owner/repo/pull/123
remote: Author: @gitgost-anonymous
remote: Branch: gitgost-a3f8c1d2
remote: PR Hash: a3f8c1d2
remote: 
remote: Subscribe to PR notifications (no account needed):
remote:   https://ntfy.sh/gitgost-pr-a3f8c1d2
remote: 
remote: To update this PR on future pushes, use:
remote:   git push gost <branch>:main -o pr-hash=a3f8c1d2
remote: 
remote: Your identity has been anonymized.
remote: No trace to you remains in the commit history.
remote: 
remote: ========================================

unpack ok
ok refs/heads/main
Implementation: internal/http/handlers.go:130-407

Processing Flow

When you push commits, gitGost performs the following operations:

1. Request Validation

2. Packfile Processing

1

Clone Repository

gitGost clones the target repository from GitHub to establish the object database
git.PlainClone(tempDir, false, &git.CloneOptions{
    URL: "https://github.com/owner/repo.git",
    Auth: &http.BasicAuth{
        Username: "x-access-token",
        Password: token,
    },
})
Source: internal/git/receive.go:153-159
2

Extract Packfile

Parse the pkt-line protocol to extract:
  • Reference updates (old SHA → new SHA)
  • Push options (e.g., pr-hash)
  • Binary packfile data
packfile, refUpdate, prHash, err := ExtractPackfile(body)
Source: internal/git/receive.go:184
3

Unpack Objects

Import objects from packfile into the repository:
git index-pack -v --stdin --fix-thin
# or fallback to:
git unpack-objects -r
Source: internal/git/receive.go:205-224
4

Anonymize Commits

Rewrite commit metadata to remove all identifying information:Only new commits (not in origin/main) are rewritten.Source: internal/git/receive.go:252-258, internal/git/receive.go:262-304

3. GitHub Integration

1

Fork Repository

Create a fork under the gitGost bot account
POST https://api.github.com/repos/:owner/:repo/forks
Returns the fork owner name (e.g., gitgost-bot)
2

Push to Fork

Push the anonymized commits to a uniquely-named branch:
gitgost-a3f8c1d2
Branch name is deterministic based on repo and timestamp.
3

Create Pull Request

Open a PR from gitgost-bot:gitgost-a3f8c1d2 to owner:main
POST https://api.github.com/repos/:owner/:repo/pulls
{
  "title": "<first line of commit message>",
  "head": "gitgost-bot:gitgost-a3f8c1d2",
  "base": "main",
  "body": "<commit body>"
}
4

Notify User

Publish notification to ntfy topic:
https://ntfy.sh/gitgost-pr-a3f8c1d2
User can subscribe (no account required) for PR updates.

Push Options

Git 2.10+ allows sending custom options with push commands:

pr-hash Option

Usage:
git push gost main -o pr-hash=a3f8c1d2
When provided, gitGost will:
  1. Check if branch gitgost-a3f8c1d2 exists in the fork
  2. Force-push new commits to that branch
  3. Look for an open PR from that branch
  4. Update the PR (if open) or create a new one (if closed)
Implementation: internal/http/handlers.go:242-292

Metadata Stripping

gitGost completely removes identifying information from commits:

Before Anonymization

commit a3f8c1d2e4b6f8a9c0d1e2f3a4b5c6d7e8f9a0b1
Author: John Doe <[email protected]>
Committer: John Doe <[email protected]>
Date: Mon Mar 5 14:23:45 2026 -0800

Fix typo in documentation

After Anonymization

commit b9e8d7c6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0
Author: @gitgost-anonymous <[email protected]>
Committer: @gitgost-anonymous <[email protected]>
Date: Thu Mar 5 22:30:12 2026 +0000

Fix typo in documentation
What’s preserved: Commit message, file changes, tree structureWhat’s removed: Author name, email, original timestamp, any PGP signatures
The anonymization process creates entirely new commit objects with new SHA hashes. Source: internal/git/receive.go:336-367

Rate Limiting

gitGost implements multiple layers of rate limiting:

Per-IP Rate Limit

When exceeded, the push is rejected with:
remote: Rate limit exceeded: max 5 PRs per hour per IP.
remote: Please try again later.
push rejected: rate limit exceeded
Implementation: internal/http/handlers.go:160-172

Global Burst Detection

Detects coordinated bot attacks: When triggered:
  • Admin receives ntfy alert
  • Admin can activate panic mode
  • Admin can rollback recent PRs
Implementation: internal/http/handlers.go:665-704

Error Handling

All errors are returned via side-band channel 3:

Common Errors

Cause: Panic mode is activeResolution: Wait 15 minutes or contact adminCode: internal/http/handlers.go:142-156
Cause: Too many PRs from your IPResolution: Wait up to 1 hourCode: internal/http/handlers.go:161-172
Cause: Invalid or corrupted packfileResolution: Ensure repository is not corrupted, try re-cloningCode: internal/http/handlers.go:212-218
Cause: GitHub API failure or repository doesn’t existResolution: Verify repository exists and is publicCode: internal/http/handlers.go:225-233
Cause: PR already exists or API failureResolution: Check if PR already exists for your changesCode: internal/http/handlers.go:311-319

Example: Full Push Flow

1. Add gitGost Remote

git remote add gost https://gitgost.leapcell.app/v1/gh/owner/repo.git

2. Make Changes

echo "# Fix typo" >> README.md
git add README.md
git commit -m "Fix typo in README"

3. Push Anonymously

git push gost main
Client Output:
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 356 bytes | 356.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: gitGost: Processing your anonymous contribution...
remote: gitGost: Commits anonymized successfully
remote: gitGost: Creating fork...
remote: gitGost: Fork ready at gitgost-bot/example-repo
remote: gitGost: Pushing to fork...
remote: gitGost: Branch 'gitgost-a3f8c1d2' created
remote: gitGost: Creating pull request...
remote: 
remote: ========================================
remote: SUCCESS! Pull Request Created
remote: ========================================
remote: 
remote: PR URL: https://github.com/owner/repo/pull/42
remote: Author: @gitgost-anonymous
remote: Branch: gitgost-a3f8c1d2
remote: PR Hash: a3f8c1d2
remote: 
remote: Subscribe to PR notifications:
remote:   https://ntfy.sh/gitgost-pr-a3f8c1d2
remote: 
To https://gitgost.leapcell.app/v1/gh/owner/repo.git
 * [new branch]      main -> main

4. Subscribe to Updates

Visit https://ntfy.sh/gitgost-pr-a3f8c1d2 or use the ntfy app to receive notifications when:
  • PR is commented on
  • PR is merged
  • PR is closed

5. Update the PR

# Make more changes
echo "More fixes" >> README.md
git add README.md
git commit -m "Additional fixes"

# Update existing PR
git push gost main -o pr-hash=a3f8c1d2

Git Smart HTTP

Learn about the protocol gitGost implements

Upload-Pack

Fetch operations (git pull/fetch)

Quickstart

Get started in 2 minutes

How It Works

Technical deep dive

Build docs developers (and LLMs) love