Nº 013/Engineering/4 min read/
Building the Domain Allowlist as a Security Primitive
Most developers encounter the domain allowlist as a configuration step. You add a domain, the proxy permits calls to it, and you move on. It reads like a firewall rule, something administrative that lives at the edge of the setup process rather than at the centre of the security model.
That reading undersells what the allowlist actually does.
The attack it is designed to close
To understand why the allowlist is a security primitive rather than a configuration feature, it helps to start with the specific attack it addresses.
An AI agent that processes external content can be redirected by instructions embedded in that content. This is indirect prompt injection, and it is a real attack class with documented demonstrations against production tools. An agent reading a webpage, a document, or an API response might encounter an instruction telling it to send a request somewhere it was not supposed to go.
If that agent can make authenticated API calls to any domain, the attacker's goal is straightforward. They do not need the credential value itself. They just need to get the agent to send an authenticated request to a server they control. The request arrives carrying valid authentication and the attacker has what they came for.
The credential never needed to be extracted. The agent never needed to be aware it was being manipulated. The attack worked because the agent had the capability to make authenticated calls to arbitrary destinations.
Deny by default
The domain allowlist closes this by removing that capability entirely. The proxy operates deny-by-default, which means every domain that will ever receive an injected credential must be explicitly listed before the first request goes out.
agentsecrets workspace allowlist add api.stripe.com
agentsecrets workspace allowlist add api.sendgrid.com
When a call arrives at the proxy for a domain that is not on that list, the proxy blocks it before credential resolution happens. The value is never looked up, never injected, and the request never reaches the destination. The attacker's server receives nothing because the proxy had nothing to give it.
This is materially different from blocking a request after credential injection. The value existed transiently in the proxy process only during calls to authorized domains. For unauthorized domains it never existed at all, because the block happened earlier in the decision chain.
Why this matters more than it sounds
The distinction between blocking before and blocking after credential resolution is worth dwelling on, because it is the thing that makes the allowlist a security guarantee rather than a security measure.
A security measure reduces risk. A security guarantee eliminates an attack surface. The allowlist eliminates the attack surface of credential exfiltration via domain redirect rather than reducing the probability of it. An attacker who cannot get the proxy to inject into their domain does not have a degraded version of the attack available to them. The attack does not work at the allowlist level regardless of how sophisticated the prompt injection is or how thoroughly the agent has been compromised.
That is a different kind of protection than most security features provide, and it comes from a design decision made early in the architecture: the proxy would be deny-by-default, not permit-by-default with a blocklist. The choice between those two models determines whether security is something you opt into or something you opt out of.
How it interacts with the rest of the architecture
The allowlist does not exist in isolation. It is one layer in a system where multiple layers address different parts of the same problem.
Transport-layer injection means the agent never holds the credential value, which closes the direct extraction path. The allowlist means the agent cannot redirect an authenticated call to an attacker-controlled destination, which closes the redirect path. Response redaction means that if an API echoes a credential back in its response, the proxy catches it before the agent sees it, which closes the echo path.
Each layer closes a specific path. Understanding the allowlist as a security primitive means understanding which path it closes and why that path exists in the first place, rather than treating it as a setting you configure once and forget about.
The operational reality
In practice, the allowlist is one of the first things a developer configures when setting up an AgentSecrets project, and it rarely needs to change after that. The domains an agent needs to call are generally known at build time. Adding them to the allowlist is a few commands that take thirty seconds.
What the allowlist produces is not just security. It is a documented, auditable record of which external services a given agent or project is permitted to call. If something unexpected appears in the audit log, the allowlist is the first place to check, because every call that reached an API was a call to an authorized domain. That constraint makes the audit log more meaningful, because a call in the log is a call that was explicitly permitted, not just a call that happened.
Part 05 covers the Go implementation, the specific decisions made in building the CLI and proxy, and what those decisions cost.
AgentSecrets is open source and MIT licensed. The full architecture is at agentsecrets.theseventeen.co. The repository is at github.com/The-17/agentsecrets.