Giving an untrusted agent git without giving it credentials
Podifi · Software Engineer · Aug 2026 — Sep 2026
Gave LLM-driven agents working git access inside tenant machines without handing them a long-lived credential or an unrestricted command surface.
Context
Agents run inside tenant machines and need git to do useful work, but the agent is untrusted by assumption: it must not read long-lived credentials, reach arbitrary remotes, or bypass network policy. Handing it the real git binary with a token in the environment fails every one of those at once, and a single miss is a tenant data-exfiltration path rather than a bug.
Decisions
Should the agent be trusted to use git correctly, or prevented from using it incorrectly?
Chosen
Treat the agent as untrusted and enforce policy outside it
Model guardrails and tool descriptions are instructions to a system that can be argued with, and the cost of one persuasive prompt is a credential. Enforcement that sits outside the agent does not negotiate. The choice flips only if the caller stops being adversarial — which, for a model acting on tenant input, it never is.
Rejected alternatives
Trust the model, and constrain it through tool design and prompts
This is the cheaper and more common answer, and it holds right up until the agent is asked to do something reasonable-sounding that policy forbids. A control that depends on the caller cooperating is not a control against a caller that can be talked into anything.
How should a credential reach the wire without ever being readable by the process that needs it?
Chosen
Inject short-lived credentials at a local proxy the operation is routed through
Separating what may run from how credentials travel is what makes both tractable: the command surface is a policy question, the credential is a transport question, and solving them in one place solves neither well. Injecting at the boundary keeps the secret out of the child process entirely, so reading the environment yields nothing worth having.
Rejected alternatives
Supply the credential through the environment or a credential helper
The documented, obvious approach, and the one most teams ship. It puts a usable secret inside the process under the least trust, where any command that can read its own environment can exfiltrate it — which is precisely the capability being withheld.
What should an egress policy actually compare when it decides whether a connection is allowed?
Chosen
Check the resolved address, not only the requested hostname
A hostname check and the connection that follows it are two separate events, and a name can resolve differently between them. Deciding on the address that will actually be connected to closes the gap between the check and the act, which is the only place the rebinding attack lives.
Rejected alternatives
Match the requested hostname against an allowlist
What almost every allowlist does, and it reads as obviously sufficient. It validates a string rather than a destination, so an allowed name that resolves to an internal address passes the check and reaches somewhere the policy exists to forbid.
What should happen when the isolation the policy depends on is not available?
Chosen
Refuse to start
A degraded mode is indistinguishable from the enforced mode to everything upstream of it, so the failure is silent exactly when it matters most. Refusing turns a security hole into an outage, which is a trade worth making when the alternative is unmediated access that nobody is told about.
Rejected alternatives
Fall back to unmediated access so work can continue
Tempting because it keeps the platform running through an infrastructure fault, and availability arguments are persuasive in the moment. It also means the one condition under which every control is absent is the condition nobody notices.
Outcomes
Every outcome below is shown with the evidence behind it.
| Outcome | Result | Evidence |
|---|---|---|
| Credential containment | Enabled agent git access with no credential in the agent environment | Separating command policy from credential transport is the recognized shape for this problem, because the two have different threat models and different failure modes: a command policy is a question about authority, and a credential is a question about transport. |
| Layered controls that fail closed | Enabled deny-by-default git with auditable refusals | Defense in depth is the known answer when any single control can be defeated, and it only holds if each layer fails closed rather than open — a layer that degrades on failure is a layer that is absent exactly when the others are already under strain. A refusal is only reviewable after the fact if it says which rule produced it, so every decline carries a stable identifier rather than failing opaquely. |
Architecture
An untrusted agent calls git and reaches a policy layer rather than the real binary. The policy layer decides whether the command is permitted and refuses by default, recording a stable reason when it declines. Permitted network operations are routed through a local auth proxy that attaches a short-lived credential the agent never sees, and an egress boundary checks the resolved address of the remote before connecting. The agent environment holds no credential at any point, so the two boundaries — what may run, and what reaches the wire — are enforced independently of each other.