How do you control what an AI agent can reach on the network?
How covey uses a proxy allowlist and hard network isolation to control what an agent sandbox can reach.
Create an organisation in covey and its allowlist holds exactly one entry: api.anthropic.com. That is the endpoint of the Claude runtime. A freshly hired agent can talk to the model and to nobody else.
That is deliberate, and it is inconvenient. The first agent to run npm install walks into a wall, and it will not be the last. Thirteen templates are there for that — github, npm, pypi, go, rust, composer, maven-gradle, container and debian-ubuntu among them. Attach the right one to an agent and you have opened that route and no other.
Until July, api.anthropic.com sat hard-wired in the source. Making the base list configurable was one of our first corrections: a list nobody can change gets worked around instead of maintained.
Which leaves one question: how firm is such a list?
A proxy is a promise
We built the first level first, because it is the obvious one. COVEY_EGRESS_ENFORCE turns it on. All traffic from the sandbox runs through a proxy that enforces the allowlist. Anything not on it is refused.
That costs no extra work, and it holds as long as the sandbox plays along.
The limit sits exactly there. Cooperative means the route through the proxy is set up, not enforced. A process that finds its own way out bypasses it.
This is not a weakness in the implementation. A proxy cannot do more. Selling one as a security boundary sells an agreement as a wall.
The second level takes the possibility away
So we added the hard level the same day. COVEY_EGRESS_ISOLATION=network puts the sandbox on an internal Docker network with no internet access. The proxy container is the only exit, and it cannot be bypassed, because no second route exists.
This level costs an image of its own, which make egress-image builds from Dockerfile.egress. The proxy inside loads its allowlist from the database and the environment and keeps it current, rather than freezing it at startup.
The difference between the two is not a configuration detail. One is a rule the sandbox keeps. The other is a property of the network that leaves it no choice.
Network isolation per customer, allowlist per agent
Run covey across several runners and you get the hard isolation per runner, not per agent. The internal network and the proxy container exist once per runner.
A runner serves exactly one organisation, so two customers never share a segment: the --internal flag cuts the way out, not the way sideways. The proxy fetches its allowlist from the control plane, authenticated with the runner token, which is renewed every time the control plane starts.
Only the network segment is shared. Two agents of the same organisation sit on the same internal network, but not on the same list.
On every connection the proxy reads the Proxy-Authorization header and knows which agent is asking. The control plane answers for that single agent. Network isolation separates tenants; the allowlist separates further, per agent and per connection.
Why this is not a side issue
Egress is not the only guard rail. A budget per agent sits beside it and stops the agent once it is used up.
An overrun budget hits the invoice. An open exit carries data outside. That is why the hard level is on the table at all, even though it asks for a second image.
Anyone starting cooperative today should read it as a transition. It is the level you begin with, not the one an agent with access to real data runs on.