Governed git: https://api.agentvalet.ai/git/<owner>/<repo>.git
The proxy speaks git’s smart-HTTP protocol. A machine that holds only an AgentValet agent key can clone, fetch and push to the repositories that agent is allowed to reach, and nothing else. There is no GitHub token on the machine to copy, and every operation is a governed call: same agent identity, same grant, same policies and guardrails, same audit row, same instant revocation as a REST call through POST /v1/actions.
Setup (once per machine)
npx @agentvalet/register # if the agent is not registered yet
agentvalet git-setup # writes credential.https://api.agentvalet.ai.helper
git clone https://api.agentvalet.ai/git/speculo/agentvalet.gitgit-setup scopes the credential helper to the proxy origin only; your other remotes are untouched. --local writes to the current repository instead of the global config, --print shows the git config command without running it.
The helper (agentvalet git-credential) answers git’s credential prompt with a freshly signed, five-minute agent JWT as the password. It answers only for the proxy host, and store / erase are no-ops: the agent key is the credential.
What is checked
Each git operation is two HTTP calls (ref discovery, then the pack transfer) and each is authorised exactly like a /v1/actions call on platform github with the real path:
| git does | Proxy checks | Scope |
|---|---|---|
GET /git/o/r/info/refs?service=… | ref advertisement (a read, for fetch and push discovery) | github:contents.read |
POST /git/o/r/git-upload-pack | clone / fetch / pull | github:contents.read |
POST /git/o/r/git-receive-pack | push | github:contents.write |
That includes the grant’s repository allow-list (Restrict which repositories an agent can reach): a fetch of, or push to, a repository outside allowed_repos fails with repo_not_allowed before anything reaches GitHub. Reads are constrained too; the allow-list is “what this agent can reach”, not “where it can write”.
If the organisation’s GitHub connection is a GitHub App installation, the token the proxy uses upstream is minted for contents: read|write on that one repository, so even the upstream credential cannot reach another repo during the call.
What the audit row records
One audit_log row per call with action = the scope, result, the policy that decided, and metadata.git:
{ "service": "receive-pack", "upstream_status": 200, "request_bytes": 48213,
"refs": [ { "ref": "refs/heads/main", "op": "update" } ] }For a push, refs is parsed from git’s command header: which refs, and whether each was created, updated or deleted. Pack data and file contents are never inspected.
Errors, as git prints them
Responses other than success are one plain-text line, which git shows verbatim.
| Situation | Status | Line |
|---|---|---|
| No credentials sent yet | 401 | AgentValet: authenticate with your agent key (agentvalet git-credential) |
| Repository not in the allow-list | 403 | AgentValet: repo_not_allowed — owner/repo is not in this agent's allowed repositories |
| Agent acts as a GitHub App installation that does not include the repo | 403 | AgentValet: repo_not_in_installation — the GitHub App installation this agent acts as does not include owner/repo |
| Policy or guardrail denial | 403 | AgentValet: denied_by_policy (or the specific reason) |
| An approval rule applies | 403 | AgentValet: approval_required_unsupported_for_git — … |
| GitHub not connected for the organisation | 412 | AgentValet: platform_not_connected — connect GitHub in the dashboard |
| Push larger than the limit | 413 | AgentValet: git_request_too_large — limit N bytes |
| GitHub refused the stored credential | 502 | AgentValet: upstream_auth_failed — … |
Approvals cannot hold a git operation: the client is blocked on a socket and there is no body to replay later. If a scope on the grant needs owner approval, git pushes on that grant are refused (and audited) rather than queued. Use the REST path for that agent, or remove the approval requirement for github:contents.write.
Limits and non-goals
- Request bodies (fetch negotiation, the pushed pack) are capped at 64 MiB by default (
GIT_MAX_REQUEST_BYTESon the proxy). Responses stream without a cap. - Git LFS is not proxied (
/info/lfs/…is 404). - Branch or path restrictions inside a repository are not enforced here yet; the audit row already carries the pushed refs. GitHub branch protection and rulesets apply as usual.
- This is GitHub only.
The locked-down machine
The passthrough makes AgentValet the only path to GitHub when the machine has no other one. The checklist that turns “governed” into “the one true path”:
- No GitHub credential on the machine: no
gh auth login, no PAT, no SSH key, no Git Credential Manager account. - Egress allow-list permits
api.agentvalet.aiand deniesgithub.com,api.github.com,*.githubusercontent.com. - One registered agent per machine (or per developer), with a
githubgrant carryingallowed_repos, and no other platform with an upload or send scope on that agent. The grant set is the egress surface. - On the GitHub side: Enterprise Managed Users or SAML-enforced SSO, forking disabled, PAT creation restricted, and an organisation IP allow-list containing the proxy’s egress address.
What this does not do: stop a person who can read code from copying it by other means (that is the device-management perimeter’s job), or stop an AI assistant on the machine from sending source to its model provider.