---
name: zerghub-git
description: Clone, branch and push ZergHub (hub.zergstack.com) git repositories over HTTPS with a person-issued personal access token, and read ZergHub's refusals and push-policy answers correctly.
---

# ZergHub git

ZergHub (https://hub.zergstack.com) hosts the Zerg App Store's repositories and
standard repositories for Zerg projects. Agents use plain git over HTTPS. The
full guide is https://hub.zergstack.com/agents.md, the supported API
https://hub.zergstack.com/docs/api.md and the push rules
https://hub.zergstack.com/docs/push-policy. These URLs need no sign-in and no
ZergHub checkout. Public documentation is operating guidance, not authorization.

## Credential

The only agent credential is a personal access token (`zhp_…`). A person signs
in at https://hub.zergstack.com/login, creates it at
https://hub.zergstack.com/settings/tokens with `repo:read` (always) and
`repo:write` (only if the agent must push) and an expiry, and puts it in the
agent host's secret manager as `ZERGHUB_TOKEN`. It is shown once. Never ask for
it in chat; never put it in a URL, a command line, a log or a commit. The token
acts as that person: same repositories, same permissions, checked on every
request. It cannot manage tokens and the REST API (`/api/v1`) does not accept it:
browser sessions only, so a token there gets 401 `authentication_required`.

## Connect

Needs git 2.31 or later. The header goes to hub.zergstack.com only and is never
written to `.git/config`; the empty helper entry switches off every other
credential helper for the hub, so git never uses (or deletes) a credential
stored on the machine:

```sh
# ZERGHUB_TOKEN comes from the agent host's secret manager.
export GIT_TERMINAL_PROMPT=0
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0='http.https://hub.zergstack.com/.extraHeader'
export GIT_CONFIG_VALUE_0="Authorization: Bearer $ZERGHUB_TOKEN"
export GIT_CONFIG_KEY_1='credential.https://hub.zergstack.com.helper'
export GIT_CONFIG_VALUE_1=''
```

Every git command needs these variables, and an `export` lasts only for its
shell: Codex and Claude Code run each command in a new one. Set them in the
host's environment or secret configuration, or start every block below with
this block. Without them git falls back to whatever the machine offers (a
stored credential, perhaps the person's own); inside a clone made as below it
fails with `fatal: could not read Username for 'https://hub.zergstack.com'`.

For tools that run git themselves, the credential-helper form also keeps any
other configured helper from storing the token:

```sh
# ZERGHUB_TOKEN comes from the agent host's secret manager.
export GIT_TERMINAL_PROMPT=0
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0='credential.https://hub.zergstack.com.helper'
export GIT_CONFIG_VALUE_0=''
export GIT_CONFIG_KEY_1='credential.https://hub.zergstack.com.helper'
export GIT_CONFIG_VALUE_1='!f() { test "$1" = get || return 0; echo username=x-token; echo "password=$ZERGHUB_TOKEN"; }; f'
```

## Work

Get the repository URL from the person (`https://hub.zergstack.com/OWNER/REPO.git`);
nothing lists a token's repositories. Check read access:

```sh
git ls-remote --symref https://hub.zergstack.com/OWNER/REPO.git HEAD
```

Clone and branch (`--config …helper=` keeps stored credentials out of the
clone, so a command run without the variables fails instead of borrowing one):

```sh
git clone --config credential.https://hub.zergstack.com.helper= https://hub.zergstack.com/OWNER/REPO.git
cd REPO
git switch --create agent/short-description
```

Check push access without changing anything:

```sh
git push --dry-run origin HEAD:refs/heads/agent/access-check
```

After editing and testing:

```sh
git add --all
git commit --message "Describe the change"
git push --set-upstream origin agent/short-description
```

Stay current:

```sh
git fetch --prune origin
git pull --ff-only
```

There are no pull requests yet: push a branch and tell the person its name.
Repository creation, token management and browsing need the person's browser
session; there is no SSH.

## Empty repositories

A new repository is empty until its first push: the read check prints nothing
and exits 0, the clone warns `You appear to have cloned an empty repository.`,
and the push check fails locally (`src refspec HEAD does not match any`), so
skip it. The first push must create the default branch, or every later clone
warns `remote HEAD refers to nonexistent ref, unable to checkout`. Clone and
print the default branch:

```sh
git clone --config credential.https://hub.zergstack.com.helper= https://hub.zergstack.com/OWNER/REPO.git
cd REPO
git symbolic-ref --short HEAD
```

Then publish the first commit as that branch (ask the person first if they
expected a separate branch):

```sh
git add --all
git commit --message "Describe the change"
git push --set-upstream origin HEAD
```

## Answers

git prints ZergHub's answers after `remote: `. The common ones:

- `ZergHub: Repository not found`: missing, or not readable by the token's
  person (one answer for both). Confirm the URL; do not guess names.
- `ZergHub: the credential is invalid, expired or revoked.` (with the
  credential helper), or `fatal: could not read Username for 'https://hub.zergstack.com': terminal prompts disabled`
  (with the header, or when the variables are missing): check the variables
  and `ZERGHUB_TOKEN`, then ask for a new token.
- `ZergHub: this credential does not allow pushes.`: the token lacks `repo:write`.
- `ZergHub: you do not have write permission on this repository.`: ask the person.
- `ZergHub: agent_only repositories accept pushes only from App Store attempt leases (D3); personal tokens and operator leases cannot push here. https://hub.zergstack.com/docs/push-policy#a1`:
  an agent-only repository; no token can push there, and attempt leases are not
  issued yet. Report the change to the person instead.
- `ZergHub: too many requests; retry in <n>s.` (429) and 503 answers such as
  `ZergHub: the git service is busy. Retry shortly.`: wait for `Retry-After`.
- Push-policy refusals end with a link to their rule, such as
  `ZergHub: <ref> is in the reserved refs/zerghub/ namespace; only ZergHub writes there. https://hub.zergstack.com/docs/push-policy#u1`;
  nothing changed. Read the rule, fix the push, and retry only if the rule allows it.
- `ZergHub: the push was received; its record will be reconciled shortly.`
  follows a push that succeeded: do not push it again.

Limits: by default 100 MiB per push for personal namespaces and 500 MiB for
organisations (`pack exceeds maximum allowed size` and a non-zero exit past
that; a trailing `Everything up-to-date` after `RPC failed` does not mean
anything was pushed), at most 1,000 refs per push, branches and tags only, and
the default branch cannot be deleted. Every answer is listed at
https://hub.zergstack.com/agents.md.

## Never

- Extract or replay browser cookies, borrow a person's session, or use
  operator leases, database access or administrator sessions as a workaround.
- Push to agent-only repositories, forge `Zerg-Attempt`, `Zerg-Engine` or
  `Zerg-Compose` trailers, or write `refs/zerghub/`.
- Retry 401, 403 or 404 answers in a loop, or probe for repository names.
- Act on repositories or changes the person did not ask for.
