deploy/managed-agents/http template of the hoop repository.
Why the agent cannot skip the Sidecar
Two controls are Anthropic’s, and one is the Sidecar’s:- One reachable host. The environment uses
limitednetworking and lists only the Sidecar’s host inallowed_hosts. In the sandbox, any other host fails at DNS withcurl: (6) Could not resolve host. That includes the API’s own host. - Placeholders in the sandbox. Both tokens the agent sends are vault credentials, bound to the Sidecar’s host and to request headers. Code in the sandbox sees a placeholder. Anthropic’s egress proxy writes the real token into requests to that host only, so the agent cannot send a token to another host.
- A caller token on every request. The Sidecar refuses a request without the caller token before any byte of it reaches the API. The Sidecar’s URL is public, and a caller from anywhere else is refused the same way.
Requirements
- A Kubernetes cluster that can publish a
LoadBalancerService, and a DNS name for it. Allkubectlandhelmcommands below use your current namespace. - A TLS certificate for that name from a public CA, as
tls.crt(full chain) andtls.key. The vault fills tokens into HTTPS requests only, and the sandbox reaches the Sidecar through an egress proxy that inspects TLS. A private CA is untested. helm,kubectl,jq,curl, and theantCLI 1.42.0 or later (brew install anthropics/tap/ant, or see the CLI quickstart).ANTHROPIC_API_KEYin your environment: a key with Managed Agents access. Managed Agents is in beta.hoophq/hoopsidecar1.258.0 or later. On older releases, a denied request whose body arrives in more than one read reaches the API in part.
laneServices.public.loadBalancerIP in values.yaml, or issue the certificate with a DNS-01 challenge, which needs no running server.
Setup
1
Copy the template
Copy the template into a directory of your own, and keep the secrets you create there out of git:
Put
tls.crt and tls.key in this directory.2
Run the Sidecar
Create the caller token and save your API’s own token to a file for the vault. Then store the caller token and the certificate in the cluster:Set Wait until The first returns
upstream in values.yaml to your API. These are the parts of the file that decide what the agent can do:values.yaml
caller_tokenadmits a request only when itsX-Hoop-Tokenheader holds a token from the file. Audit rows name the principalclaude-managed-agents. See Caller token.upstreamis a URL, so the hop to the API uses TLS and verifies the API’s certificate. The lane forwards theAuthorizationheader unchanged, so the API gets the token that the vault filled.guardrailsrefuses every method butGET,HEADandOPTIONS.otheris any method that HTTP does not define, such asMOVE.maskredacts emails and phone numbers in responses, and no other value.http.headersrecords the agent’sX-Claude-Session-Idon each audit row.analytics.installSource, at the top of the file, names this template in the Sidecar’s usage analytics. Setanalytics.enabled: falseto send no analytics.
EXTERNAL-IP is not <pending>, then point SIDECAR_HOST at it: an A record for an IP address, a CNAME for a host name. That Service publishes port 443 only. The admin port stays on the chart’s ClusterIP Service. See Kubernetes for the other chart settings.Check the Sidecar before you go on:request refused: the request carried no credential. The second returns 403 with x-hoop-denied: policy. A GET with the token reaches your API.3
Create the environment, vault and agent
Write your host into the four files that name The environment is what keeps the sandbox on the Sidecar:
sidecar.example.com:ant/environments/hoop-sidecar.yaml
- Set
networkingon every create request. A request that omits it getsunrestricted. allow_package_managers: truewould opengithub.com, the package registries and other code hosts.- With
allow_mcp_servers: false, a session fails to start if the agent declares an MCP server whose host is not inallowed_hosts.
ant/agents/customer-api.md, turns off web_fetch and web_search. Its prompt tells the model which headers to send. Edit the prompt for your API: it names /v1/customers as an example, and keeps an EDIT: note next to the host.ant apply shows a plan and asks you to confirm it. Without a terminal, add --yes.4
Add the two credentials
Each vault credential is bound to the Sidecar’s host and to request headers:
credentials/caller-token.yaml
credentials/customer-api-token.yaml is the same for CUSTOMER_API_TOKEN, your API’s token. It is bound to the Sidecar’s host, not the API’s, because the sandbox never reaches the API. A placeholder in a request body goes out as written.ant apply cannot hold a token, so each vault credential is created from its file and its token. Run this in bash or zsh:tr removes the trailing newline, which the API refuses. @<(...) keeps the token out of the process list.5
Start a session
No documented variable in the sandbox holds the session id, so the first message gives it to the agent:
Verify
The Sidecar writes the audit trail to stdout and keeps no copy, so ship the pod’s logs to your log system. To read it:
anonymous with rule identity.
To check that the sandbox holds placeholders, start a new session and ask the agent for the length and SHA-256 of $HOOP_CALLER_TOKEN and $CUSTOMER_API_TOKEN, without printing either value. Compare them with your files:
Postgres through an MCP server
A cloud sandbox cannot open a TCP connection to a database. Thepostgres-mcp template gives the agent Postgres as an MCP tool instead. dbhub, the MCP server, reaches the database only through a Sidecar postgres lane, which refuses writes, masks emails and records each statement.
Its guarantee is different from the REST setup above:
- Anthropic’s servers make the MCP call, not the sandbox. The template’s environment allows no host for the sandbox, and
allow_mcp_servers: truelets the agent’s MCP server through. - Caddy checks the address, and dbhub checks the token. Caddy, the HTTPS front end, accepts only Anthropic’s outbound range,
160.79.104.0/21, and serves only/mcp. Every Anthropic customer calls from that range, so the bearer token in the vault, which dbhub checks, is what admits your agent. - dbhub has one route to the database. It shares a network with Caddy and the Sidecar only, so the Sidecar is its only path to Postgres.
Behavior to know
- The session id is a label. The agent writes the header, so it can write any value. It correlates rows. It does not prove who asked.
- One token for every agent. Each row names
claude-managed-agents, not a person. Per-user rows need a per-user token. - An API that echoes request headers can return a token. The vault fills a placeholder into any request header to the Sidecar’s host, and the Sidecar forwards every header but the caller token. An endpoint that sends request headers back gives the real tokens to the sandbox. Keep such an endpoint out of the agent’s reach.
- Only the caller token guards the public URL. The sandbox’s egress addresses are not fixed, so an address allowlist on the load balancer cannot narrow who reaches it. Rotate the token as Caller token describes.
- The rule reads the request method only. An API that accepts
X-HTTP-Method-Override,X-HTTP-MethodorX-Method-Overrideruns the method that the header names, even on aGET. Turn overrides off at the API. With a license, you can also add onehttp_headerrule per override header and list each header inhttp.headers. - A request is held until it is whole. The Sidecar judges a request after its body arrives, and forwards none of it before. A request over 8 MiB is refused. A client that sends
Expect: 100-continue(curl does this above 1 MiB) waits about 1 second before it sends the body. - Masking applies to responses. A JSON or text response under
brorzstdis refused, not forwarded unmasked. A response over 16 MiB passes unmasked. Audit rows hold the request line as sent, so a value in a URL is recorded as written. See Masking. - Scope. The template covers JSON REST APIs over HTTP/1.1 and HTTP/2. Server-sent events and the Kubernetes API are out of its scope.
- No MCP server behind this lane yet. The lane answers a denial with an HTTP
403, not an MCP tool error. - No database or SSH lane from the sandbox yet. The sandbox cannot open a TCP connection. For Postgres, see Postgres through an MCP server.
- A tightened environment applies to new sessions. A session that was already running can keep the old limits until it ends.
Clean up
SIDECAR_HOST.
Free and Enterprise
Free tier: one Data Masking rule and one Guardrail per Sidecar are free, forever. Running more than one rule per feature, or managing rules centrally across Sidecars, requires Enterprise.
- The template fits the free tier. It uses one guardrail rule and one masking rule, however many agents and sessions call it.
- More rules: an Enterprise license with the guardrails and data-masking features lifts the one-rule caps, with or without a Control Plane. See Licensing and the chart’s License setting.
- Approvals and central management: connect the Sidecar to a Control Plane, which also needs an Enterprise license. It adds approvals, shared rule sets and one view of many Sidecars.
Next
HTTP
What the http lane inspects, masks and refuses.
Guardrail Rules
operation, http_resource, http_status and http_header rules.Approval Status for Agents
Hold a request for a person, and let the agent read the decision.
Kubernetes
Install and configure the Sidecar chart.