Documentation
Git over HTTP
Repositories are served over HTTPS as well as SSH, using git's standard smart HTTP protocol. Clone, fetch and push work with an unmodified git client.
One backend serves three things on one host: the transport described here, the web UI, and the runner protocol that workflows use. They come up together because a runner's instance URL is the host it clones from.
Enabling it
Turn the backend on and give it a host:
/backends enable git-http
/server settings set git.http.vhost git.example.com
git.http.vhost applies live — routing, the runner server URL, the web
UI's instance name, and the vhost publication that binds HTTPS, issues a
certificate and announces a .local name all move to the new host by the time
the command returns. See Server settings.
At process start the same values come from flags:
| CLI flag | Env var | Meaning |
|---|---|---|
--backend-git-http[=BOOL] | EVERLOCK_BACKEND_GIT_HTTP | serve git over HTTP (off by default) |
--backend-git-http-vhost <HOST> | EVERLOCK_BACKEND_GIT_HTTP_VHOST | host the transport answers on; the git.http.vhost setting wins |
The enabled flag persists in config/git-http.toml in the system store,
which is what /backends enable git-http writes. The Git engine itself has no
enable flag: it runs when either transport does, so turning on HTTP also brings
up the repositories, the storage root, and the git.gc and git.retention
jobs.
Clone URLs
Both work. Git appends /info/refs to whatever clone URL it is handed, so the
suffix is optional and each route is served with and without it.
| Path | Serves |
|---|---|
GET /{repo}[.git]/info/refs?service=… | ref advertisement for that service |
POST /{repo}[.git]/git-upload-pack | fetch and clone |
POST /{repo}[.git]/git-receive-pack | push |
Because of this, info/refs, git-upload-pack and git-receive-pack are
reserved second path segments under a repository name.
Credentials
Authenticate with a password or an API key — the same funnel every other Everlock HTTP surface uses. An API key works as the Basic password whatever username accompanies it, which is the convention git's credential helper and forge access tokens rely on:
The key names its own user, so the username is ignored.
Access is checked against http/git/<repo>, or */git/<repo> for a grant that
covers every transport at once. A fetch takes reader; a push takes writer.
See Access control and grants.
Two denial codes, and the difference matters to a git client:
401when no credentials were offered. Git reads the challenge and retries with its credential helper, so an anonymous denial must not be a flat403.403when credentials were offered and are insufficient.
A git-receive-pack advertisement requires write access and answers 401
otherwise, so a client authenticates before uploading a pack rather than
after. Wrong credentials feed the same per-IP failure window SMTP and IMAP use;
a request carrying none is git's ordinary first probe and is not counted against
it.
Protocol details
- The advertisement carries the smart-HTTP prologue — the service name as a
pkt-line, then a flush — with
Cache-Control: no-cache, so no proxy hands a client a stale ref list. - The two services take different advertisements: the fetch one carries
shallowboundary lines, the push one declaresreport-status. - Both POST responses carry
application/x-git-<service>-result. - Request bodies arrive chunked, may carry
Expect: 100-continue, and are gunzipped whenContent-Encoding: gzipis set. - A client offering
Git-Protocol: version=2is answered with the v0 advertisement the engine speaks, and falls back on its own.
Shallow clones work here exactly as they do over SSH: the boundary handling,
deepen negotiation, per-ref capability enforcement and missing-object
rejection live in the protocol engine, which is transport-free. See
History retention.
Load and limits
Pack generation is the one endpoint that does real work for an unauthenticated
caller, so a global semaphore bounds concurrent anonymous fetches at four.
Beyond it a request is answered 503 with Retry-After.
Authenticated actors are not counted against that limit, because a fleet of workflow runners cloning once per task must not be throttled against strangers.
A fetch streams: the pack generator runs on the blocking pool and hands chunks through a bounded channel, so a slow client slows the generator rather than buffering a whole pack in memory, and a client that disappears releases the thread. A push is bounded by what the client sends — the session buffers the pack in memory before unpacking, the same bound the SSH path has.