Concepts
Egress
Every connection out of an environment goes through one proxy that enforces a host allowlist and audits each request.
The agent's sandbox has exactly one way out to the internet: an egress proxy inside the VM. Every outbound connection goes through it, and it only lets through hosts on the environment's allowlist. A prompt-injected agent can't post your user's data to a server you never approved, and every attempt, allowed or refused, ends up in the audit log.
How the proxy works
The agent's environment already points at the proxy (HTTPS_PROXY and HTTP_PROXY are set), so ordinary HTTP clients such as
curl, Python requests or Node's fetch with proxy support work unchanged. For each request the proxy:
- checks that the target host is on the allowlist and the port is allowed;
- resolves the host itself and refuses addresses in private ranges, loopback, link-local and cloud metadata addresses, so an allowed name that resolves to an internal address is still blocked;
- connects only to the addresses it checked, without resolving the name again;
- writes an audit record when the connection opens and another when it closes.
Connections that try to skip the proxy and go straight to an IP address fail. If any check fails, the request is refused.
Requests to hosts that a credential is bound to are not opened from the VM. The proxy hands them to the credential gateway on the host, which swaps the stand-in token for the real value.
What is allowed
The hosts an environment can reach are the union of:
- a small platform allowlist that Temper manages;
- your allowlist: the environment's own list if it has one, otherwise your developer-level list;
- the hosts of every secret the environment has, developer-level or environment-level;
- the hosts of the end user's connections.
You don't need to list a secret's hosts or a connector's API hosts yourself. They are allowed as soon as the secret or connection exists, and removed when it is deleted or revoked.
Entries
An allowlist has up to 200 entries. Each entry is either:
- an exact host name, such as
api.example.com, which matches only that host; or - a wildcard, such as
*.example.com, which matches any subdomain at any depth but notexample.comitself.
Entries are lowercase, with no scheme, port or path. Patterns that are too broad, such as * or *.com, are rejected with 400.
Developer-level allowlist
Your developer-level allowlist applies to every environment that doesn't have its own. Setting it replaces the whole list, and setting
it to null (or an empty list) clears it.
await temper.secrets.setEgress(["api.github.com", "*.githubusercontent.com"]);
const allow = await temper.secrets.getEgress();temper.secrets.set_egress(["api.github.com", "*.githubusercontent.com"])
allow = temper.secrets.get_egress()_, err = client.Secrets.SetEgress(ctx, []string{"api.github.com", "*.githubusercontent.com"})
if err != nil {
log.Fatal(err)
}
allow, err := client.Secrets.GetEgress(ctx)Environment-level allowlist
An environment can have its own list, which replaces your developer-level list for that environment (it is not merged with it).
Set allow to null to drop the environment's list and go back to the developer-level one. getEnvironmentEgress returns the list the
environment uses and inherited, which is true when it is using your developer-level list.
const egress = await temper.secrets.setEnvironmentEgress(env.id, ["pypi.org", "*.pythonhosted.org"]);
console.log(egress.allow, egress.inherited); // [...] false
await temper.secrets.setEnvironmentEgress(env.id, null); // back to the developer-level listegress = temper.secrets.set_environment_egress(env.id, ["pypi.org", "*.pythonhosted.org"])
print(egress.allow, egress.inherited) # [...] False
temper.secrets.set_environment_egress(env.id, None) # back to the developer-level listegress, err := client.Secrets.SetEnvironmentEgress(ctx, env.ID, []string{"pypi.org", "*.pythonhosted.org"})
if err != nil {
log.Fatal(err)
}
fmt.Println(egress.Allow, egress.Inherited) // [...] false
_, err = client.Secrets.SetEnvironmentEgress(ctx, env.ID, nil) // back to the developer-level listThe egress endpoints are part of the secrets API: getEgress,
setEgress, getEnvironmentEgress and
setEnvironmentEgress.
Changes apply immediately
Allowlist changes reach running VMs straight away, with no restart or VM replacement. Connections that are already open are not cut; new connections follow the new list. The same is true when you add or delete a secret or a connection.
Every request is audited
The proxy records every outbound request: time, host, port, whether it was allowed and why not, and for allowed connections the bytes transferred. Requests through the credential gateway also record the method, the path (without the query string), the credential name and the approval decision. Real tokens and request bodies are never recorded. Records leave the VM as they are written, so the agent can't edit them. See Audit log.