# Git Push is Hard

> Source: <https://julin.ai/2026/08/28/let-ai-agent-push-code-safely/>
> Published: 2026-08-27 12:00:00+00:00

# Git Push is Hard

If you run a coding agent inside a sandbox VM, the usual advice is simple: isolate the filesystem, restrict network access, don’t mount your home directory, don’t expose SSH keys or cloud credentials.

But that leads to one question: without an SSH key, how would the agent run `git push`

safely?

You could give the VM an SSH key or a GitHub token. That basically diminishes the purpose of the VM, and gives the agent more access than it needs. So, **don’t do this.** AI agents had [bad reputation](https://openai.com/index/hugging-face-incident-and-the-road-ahead/).

### Use a short-lived token instead

Basically, configure a proxy outside the sandbox.

Inside the VM, let `git push`

talk to the remote with a scoped, synthetic credential. A custom proxy running outside the sandbox attaches the real GitHub OAuth token before forwarding the request — so the real token is invisible to the model.

A GitHub App works well for this. The host keeps the app’s private key and creates a short-lived installation token for the repo. The VM uses that token to push its branch.

The broker can enforce rules such as:

- allow fetch
- allow push
`agent/run-123`

- deny push
`main`

- deny force push
- deny other repos

The agent does not need to hold the real GitHub credential at all.

### A practical guide

In practice, the proxy can be as simple as an nginx docker process listening on `127.0.0.1:8082->80/tcp`

. It injects `gh auth token`

into outgoing requests as the auth header.

Inside the VM, the push command looks like `git push https://github.com/name/repo.git main`

.

Getting an agent to use it doesn’t take much: just hint it. Something like “push via GitHub broker on host port 8082 instead of origin” works well — the agent figures out the rest on its own.

Here’s [a script that keeps that nginx conf fresh](https://gist.github.com/soasme/0f93509db16e6a5a950cff26769ae11c): pull a short-lived token with `gh auth token`

, write it into `$XDG_CONFIG_HOME/ghe-proxy/nginx.conf`

as a `Basic`

auth header, lock the file down to `0600`

, then tear down and recreate the container — a plain restart would keep serving the old, stale token — and check `docker inspect`

to confirm it actually came back up.

### The general pattern

This is a useful way to think about agent permissions in general. A runtime does not need your identity. It needs a small set of capabilities for the current task. For `git push`

, that capability is usually: this repo, this branch, for a short time.
