Skip to content

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.

  • TypeScript
  • Git
  • HTTP proxying
  • Linux sandboxing
  • Short-lived tokens

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.

Outcomes and their evidence: Giving an untrusted agent git without giving it credentials
OutcomeResultEvidence
Credential containmentEnabledEnabled agent git access with no credential in the agent environmentSeparating 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 closedEnabledEnabled deny-by-default git with auditable refusalsDefense 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

Policy and credentials are separate boundaries

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.