Outbound rules
Decide what the sandboxes on a template may reach: everything, nothing, or a list of addresses.
For your agent
agental.io/docs/outbound-rules.mdA sandbox reaches the internet unless its template says otherwise. The rule is set on a template, and every sandbox on it is held to it. A sandbox can have addresses of its own on top. The rule is enforced on the server the sandbox runs on, outside the virtual machine, so nothing running in the sandbox can change it, root included.
| Rule | What it lets out |
|---|---|
open | Everything. Every template is open until its rule is set, default included. |
blocked | Nothing. |
allowlist | Only what goes to an address in allow: IPv4 addresses and ranges, over any protocol and port. |
Set a template's rule
curl -X PUT https://agental.io/v1/templates/{id}/network \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"outbound":"allowlist","allow":["203.0.113.7","198.51.100.0/24"]}'{
"outbound": "allowlist",
"allow": ["203.0.113.7", "198.51.100.0/24"]
}- The rule that is sent takes the place of the one the template had. An allowlist is replaced, not added to: send every address each time.
- The answer is the rule as it is kept: a range by its first address, so
198.51.100.7/24comes back as198.51.100.0/24, and no address twice. - It raises the template's
revision, as a change to its secrets does. The same rule sent again changes nothing, so the request is safe to repeat. - Saving it changes no sandbox that is running. Each takes it at its next start, or now when you apply the template.
GET /v1/templates/{id}/networkreads it and changes nothing.GET /v1/templates/{id}has it asnetwork.
Give a sandbox addresses of its own
curl -X PUT https://agental.io/v1/sandboxes/{id}/network \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"allow":["192.0.2.99"]}'{
"outbound": "allowlist",
"allow": ["203.0.113.7", "198.51.100.0/24", "192.0.2.99"],
"template": {
"templateId": "tpl_0123456789abcdef",
"name": "coding-agent",
"outbound": "allowlist",
"allow": ["203.0.113.7", "198.51.100.0/24"]
},
"own": { "allow": ["192.0.2.99"] }
}- The list that is sent takes the place of the one the sandbox had. Send
{"allow":[]}to remove them all. - It holds from when the request answers, on a sandbox that is running or stopped. The sandbox is not restarted, and connections that are already open are not cut.
outboundandalloware what the sandbox is held to now.templateis its template's rule as the sandbox has it, andownis its own addresses.- The rule is the template's. Sending
outboundto a sandbox is refused with400 rule_on_template. GET /v1/sandboxes/{id}/networkanswers the same and changes nothing.GET /v1/sandboxes/{id}has what the sandbox is held to asnetwork.
What a sandbox is held to
Its template's rule, with its own addresses on top. Its own add to what the template lets out, and never take any away.
| Its template's rule | What the sandbox is held to |
|---|---|
open | open, whatever its own addresses. |
blocked | An allowlist of its own addresses, or blocked if it has none. |
allowlist | An allowlist of the template's addresses and its own. |
The template's addresses and a sandbox's own come to 100 at most. More is refused with 409 too_many_addresses, and nothing is changed: when the template's rule is saved, and message then names the sandbox; when a sandbox's own addresses are set; when a sandbox is made or forked; and when it is put on another template.
Make a sandbox
curl -X POST https://agental.io/v1/sandboxes \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Idempotency-Key: fix-login-bug-1" \
-H "Content-Type: application/json" \
-d '{"name":"fix-login-bug","templateId":"tpl_0123456789abcdef","network":{"allow":["192.0.2.99"]}}'- The machine is made held to its template's rule, with its own addresses on top, so it never runs open first, not even while it boots.
- Leave
networkout and it has no addresses of its own. - A fork is on its source's template, held to the template's rule as it is now, with a copy of its source's own addresses on top. Changing one sandbox's addresses afterwards does not change the other's.
- A sandbox made from a snapshot is held to the rule of the template it is made on.
When a change reaches a sandbox
A sandbox has its template's rule as it was when it last started, as it has its template's variables and files. A stopped sandbox is held to the template's rule as it is at its next start. A running one is held to it now when you apply the template.
curl -X POST https://agental.io/v1/templates/{id}/apply \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"templateId": "tpl_0123456789abcdef",
"revision": 4,
"applied": ["sbx_2a9a47e50daec64a"],
"failed": []
}- Each running sandbox on the template is held to its rule, with its own addresses on top, from when the request answers. Its server is told first, one sandbox at a time. Nothing is restarted, and connections that are already open are not cut.
- A sandbox whose server does not take the rule is listed in
failed, with what went wrong, and is left as it was: held to the rule it had, and not up to date.appliedlists the rest. - Send
{"sandboxIds":[…]}to apply it to those sandboxes only. - Put a sandbox on another template with
PUT /v1/sandboxes/{id}/template, and it is held to that template's rule at once: a running one from when the request answers, a stopped one from its next start. GET /v1/sandboxes/{id}/templatesays whether a sandbox has its template as it is now, asupToDate.
What a rule leaves alone
- What is sent to the sandbox. Commands and files reach it as before, and so do its public URLs and SSH. A connection that was let in is answered, whatever the rule.
- What a sandbox is held to, through a roll-back to a snapshot, archiving and a move to another server. Only a change of its own addresses, its next start,
apply, and putting it on another template change it. - What no sandbox reaches, whatever its rule: another sandbox, a private network, and mail delivery on port 25. An address in
allowthat lies inside a private network is refused for that reason.
What it does not do
- It takes addresses, not names.
"api.example.com"inallowis refused. Look the name up and list its addresses, and remember that a service behind a content network changes them. - Names still resolve under any rule. A blocked sandbox opens no connection, and a program in it can still ask for names, which is a narrow way for data to leave.
- It keeps no record of what a rule refused.
- Any key to the account can change a template's rule and a sandbox's own addresses. Do not give a sandbox that is held to a rule one of the account's keys.
- IPv4 only. Sandboxes have no IPv6.
Errors
| Answer | Meaning |
|---|---|
400 invalid_network_policy | What was sent cannot be a rule or an address. message says which entry and why: a name, an IPv6 address, a private range, allow without allowlist, an allowlist with nothing in it, or more than 100 addresses. |
400 rule_on_template | outbound was sent to a sandbox. Set the rule on its template, with PUT /v1/templates/{id}/network, and send a sandbox only allow. |
409 too_many_addresses | A list of 100 or fewer that, with the template's addresses, would come to more than 100. message says how many of each. Nothing was changed. |
409 template_changed | The template's rule changed three times while a sandbox's server was being told it, when the sandbox was put on the template. Nothing was changed: send it again. |
409 sandbox_archived | The sandbox's disk is in storage. Start it, wait until it is running, and send its addresses again. |
409 sandbox_migrating | The sandbox is being moved to another server. Wait a minute and send its addresses again. |
500 host_error | The server the sandbox is on did not take the rule. The sandbox is held to the rule it had, with the addresses it had. |
The full machine-readable reference is OpenAPI 3.1. Every page as Markdown, for an agent: llms.txt. Questions: [email protected]