1---
2title: Cloning & pushing
3description: Standard git clients over smart HTTP. What the wire supports, where credentials go, and what to do when something hangs.
4---
5
6The host speaks git's smart HTTP protocol. Standard clients work
7unmodified, with no extensions and no wrapper CLI:
8
9```sh
10git clone "https://$HOST/acme/web.git"
11```
12
13## Credentials
14
15`git` sends HTTP Basic. The credential goes in the password field of
16the remote URL and the username is ignored:
17
18```sh
19git remote add origin "https://x:$TOKEN@$HOST/acme/web.git"
20```
21
22What `$TOKEN` is depends on the middleware the host runs.
23
24### The starter secret
25
26The host from [Getting Started](/git/getting-started) accepts one
27secret, an `Alchemy.Random` the stack outputs. `alchemy deploy` prints
28it:
29
30```sh
31bun alchemy deploy
32# { url: "https://…workers.dev", secret: "9f2c…" }
33export TOKEN=9f2c…
34```
35
36### A Better Auth API key
37
38The host from [Part 4](/git/tutorial/part-4) verifies API keys. A
39signed-in user mints one; it is shown once:
40
41```sh
42curl -s -b jar -X POST "$HOST/api/auth/api-key/create" \
43 -H "Origin: $HOST" -H "Content-Type: application/json" \
44 -d '{"name":"laptop"}' | jq -r .key
45```
46
47The example app has a settings page that does the same.
48
49### Your own
50
51The token is whatever your middleware accepts. The one
52[Part 4](/git/tutorial/part-4) put in front of the host decodes the
53Basic password and asks Better Auth whose key it is:
54
55```typescript
56const { password } = yield* HttpApiBuilder.securityDecode(
57 HttpApiSecurity.basic,
58);
59const key = Redacted.value(password);
60if (key !== "") {
61 const verified = yield* auth.api.verifyApiKey({ body: { key } });
62 return verified.valid && verified.key
63 ? { id: verified.key.referenceId }
64 : undefined;
65}
66```
67
68A token issued by anything else is verified the same way: read it
69from the request, run the route or refuse. [Auth](/git/blocks/auth)
70has the pattern.
71
72### Keeping it out of `.git/config`
73
74Leave the credential out of the URL and let a credential helper
75supply it. A middleware that answers an anonymous request with `401`
76and `WWW-Authenticate` makes `git` prompt:
77
78```sh
79git config credential.helper osxkeychain # or store, or your manager
80git clone "https://$HOST/acme/web.git"
81Password for 'https://x@…': <paste $TOKEN>
82```
83
84A middleware that honors `public` serves reads of a public repository
85with no credential at all; the tutorial's does.
86
87## What is supported
88
89| Capability | Notes |
90| --- | --- |
91| Clone | served from a precomputed bundle when one is current |
92| Incremental fetch | `multi_ack_detailed` with `no-done` |
93| Shallow clone | `--depth`, including deepening later |
94| Push | thin packs with offset and reference deltas |
95| Atomic push | all refs move or none do |
96| Force push | compare-and-swap on the old value |
97| Tags | lightweight and annotated, with peeled advertisement |
98| Status | `report-status`, `report-status-v2`, side-band |
99
100Every push runs a full connectivity check. The objects it references
101must already exist or arrive in the same pack, so a repository is never
102left with dangling references.
103
104Protocol v0 only. Clients that prefer v2 negotiate down on their own.
105
106## Large pushes
107
108Push size is not capped. The body streams through the Worker and into
109the blob store as it arrives, and the repository's Durable Object
110receives rows rather than the pack. Peak memory is a fixed window,
111whatever the push size. [Hasher](/git/blocks/hasher) has the path and
112[Scaling](/git/recipes/scaling) the numbers.
113
114## Troubleshooting
115
116**A credential prompt in a script.** Set `GIT_TERMINAL_PROMPT=0` so a
117missing credential fails at once instead of hanging.
118
119**A push rejected with a ref message.** That is the protocol working:
120a non-fast-forward, a stale compare-and-swap, or a read-only
121repository. The message names the ref.
122
123**A hung clone behind a proxy.** The wire endpoints must be forwarded
124verbatim, with the request body streamed and the `Host` header kept.
125A proxy that buffers bodies breaks streaming pushes.