Skip to main content
An agent on Amazon Bedrock AgentCore in VPC mode runs on subnets in your VPC. It is a managed harness, where AWS runs the agent loop from your configuration, or your own Runtime agent in a container. The Sidecar runs on ECS Fargate in the same VPC, in front of one REST API. The agent’s subnets have no route out, so every request the agent sends to the API passes through your guardrails, masking rules and audit trail. The Sidecar is free to run. One CloudFormation stack from the deploy/agentcore/http template of the hoop repository creates the VPC, the Sidecar and the harness. Start with the quick start, then run it against your API.
Bedrock Agents Classic has been closed to new customers since July 30, 2026, and AWS recommends a move to AgentCore: the harness, or your own agent on Runtime. See Bedrock Agents Classic maintenance mode. This setup does not cover a Bedrock Agents Classic agent. It applies once the agent runs on AgentCore.
An AgentCore agent, a harness or your own Runtime agent, runs in agent subnets with no route out. It sends plain HTTP requests on port 8080 to the Sidecar on ECS Fargate in the service subnet. The Sidecar checks the caller token, refuses writes, masks emails, phone numbers and card numbers in responses and records each request with the session id in CloudWatch, then forwards to the REST API over HTTPS. The agent's model calls and AWS API calls go to the VPC endpoints. A connection to any other address times out, and any other name gets NXDOMAIN.An AgentCore agent, a harness or your own Runtime agent, runs in agent subnets with no route out. It sends plain HTTP requests on port 8080 to the Sidecar on ECS Fargate in the service subnet. The Sidecar checks the caller token, refuses writes, masks emails, phone numbers and card numbers in responses and records each request with the session id in CloudWatch, then forwards to the REST API over HTTPS. The agent's model calls and AWS API calls go to the VPC endpoints. A connection to any other address times out, and any other name gets NXDOMAIN.
The Sidecar sees the requests that the agent sends to its listener. These do not pass through it:
  • The model calls. They go to Bedrock through the VPC’s bedrock-runtime endpoint.
  • AWS API calls on the VPC endpoints. The agent’s shell holds the execution role. See Limits.
  • A tool that the caller of InvokeHarness adds to a session. See Limits.

Why the agent cannot skip the Sidecar

Four controls are the VPC’s. One stack holds them, and scripts/ci/check-agentcore-egress.py in the hoop repository fails CI if one goes away:
  • No route out. The agent subnets’ route table has no NAT gateway and no internet gateway.
  • One security group. The agent’s security group reaches the VPC endpoints and the S3 gateway on 443, and the Sidecar’s listener on 8080. Nothing else. The endpoints serve ECR, Bedrock Runtime and CloudWatch Logs only.
  • A scoped S3 endpoint. The S3 gateway endpoint allows only s3:GetObject on the bucket where ECR keeps image layers. AWS’s default policy would let the agent write to any bucket, an outside one included.
  • A DNS Firewall. A Route 53 Resolver DNS Firewall answers AWS names, hoop.internal and the Sidecar’s own hosts, and NXDOMAIN for the rest. Without it, the VPC resolver answers any public name, so a DNS query can carry data out.
A connection that the security group does not open times out, the API’s own included. The API’s host name resolves, because the Sidecar needs it, but the agent has no route to it. The Sidecar also checks a caller token on every request, before any byte of it reaches the API. The agent holds the token, so the token does not keep the agent out. It names the caller in the audit trail.

Get the template

Both setups below use $T, so run them in the shell that sets it:

Quick start

The Launch Stack link creates the stack in us-east-1 with UpstreamUrl set to demo. The Sidecar then serves hoop’s demo API, a set of invented users with emails, phones and cards, from inside its task. You need no API of your own. You need:
  • An AWS account where you can create IAM roles, with access to the Bedrock model us.anthropic.claude-haiku-5-5 in us-east-1.
  • A Sidecar token. The Sidecar runs logged in, in the demo too: sign in to use.hoop.dev, open Sidecars, choose Connect an existing Sidecar, and copy the token. A free use.hoop.dev organization holds one Sidecar.
  • For steps 2 to 4: git, the AWS CLI v2 signed in to that account, jq, python3, curl 7.75 or later, and $T from Get the template.
From Create stack to the agent’s answer took 8 min 53 s in our run: 8 min 20 s for the stack, 33 s for the answer. While the stack exists it costs about $0.095 per hour, plus each session’s model charges.
1

Launch the stack

Launch StackThe link fills the stack name hoop-agentcore-quickstart, UpstreamUrl demo and the harness hoop_agentcore_quickstart. Paste your token into HoopSidecarToken, accept that the stack creates IAM resources, and choose Create stack. Wait for CREATE_COMPLETE, about 8 minutes.
2

Ask the agent

With no question, session asks the demo one: list the users, then delete user 1. It prints each command the agent ran, its output and the answer. The users come back with [REDACTED:EMAIL_ADDRESS], [REDACTED:PHONE_NUMBER] and [REDACTED:CREDIT_CARD], and the DELETE gets 403 and this agent may only read from the API.
3

Read the audit trail

Each row names the principal agentcore and the session id that session printed:
4

Delete the stack

Then delete the Sidecar at use.hoop.dev (Sidecars). If the delete ends in DELETE_FAILED, see Clean up.

Run it against your API

For a harness, follow steps 1 to 3. For your own Runtime agent, follow steps 1 and 2 with AGENT=none, then Bring your own Runtime agent.

Requirements

  • An AWS account and a Region where AgentCore runs. In us-east-1 the defaults work. Elsewhere, set AGENT_AZ_IDS to two zone ids that AgentCore supports there.
  • Access to the Bedrock model in ModelId, by default us.anthropic.claude-haiku-5-5.
  • The AWS CLI v2, jq, python3 and curl 7.75 or later. Docker with buildx for the Runtime agent.
  • A Sidecar token or a license. Copy the token at use.hoop.dev, as in the quick start. A machine that already logged in prints it with hoop sidecar status --token. Without a token or a license, CloudFormation refuses the stack: the Sidecar would wait for a person to approve a login code.
  • hoophq/hoopsidecar 1.272.0 or later. template.yaml pins 1.272.0 by digest. Older releases report the template’s install source as other.
1

Keep the secrets outside the clone

If your API takes a token, put it in Secrets Manager. The harness sends it as Authorization: Bearer:
2

Create the stack

For a harness, edit its instructions first. SystemPrompt in template.yaml names the headers to send and uses /users/1 as an example.Replace https://api.example.com with your API: a scheme and a host, an optional port, and no path.
create looks up the Region’s S3 prefix list, deploys template.yaml as the stack hoop-agentcore-http, and prints its outputs. It takes about 8 minutes. Set STACK to choose another name: each stack names its harness after it, so two stacks can share a Region.The Sidecar reaches your API from the service subnet, through an internet gateway. These are the parts of its config, SIDECAR_YAML in the task definition of template.yaml, that decide what the agent can do:
SIDECAR_YAML
  • listen is plain HTTP inside the VPC. See Limits for why.
  • caller_token admits a request only when its X-Hoop-Token header holds the caller token. The stack generates the token in Secrets Manager and gives it to the Sidecar and the harness. Audit rows name the principal agentcore. See Caller token.
  • upstream is your UPSTREAM_URL. With https, the hop to the API uses TLS and verifies the API’s certificate. The listener forwards the Authorization header unchanged.
  • guardrails refuses every method but GET, HEAD and OPTIONS. other is any method that HTTP does not define, such as MOVE.
  • mask redacts emails, phone numbers and card numbers in responses, and no other value.
  • http.headers records the agent’s X-AgentCore-Session-Id on each audit row.
  • HOOP_SIDECAR_INSTALL_SOURCE, in the same task definition, names this template in the Sidecar’s usage analytics. Add HOOP_SIDECAR_ANALYTICS=off there to send no analytics.
To change the config, edit SIDECAR_YAML and run create again. The stack registers a new task revision, and ECS replaces the running Sidecar.AGENT=none creates the network and the Sidecar without a harness.
3

Ask the harness

No variable in the harness holds its session id, so the first message gives it to the agent. The script prints each command the agent ran, its output and the answer.

Bring your own Runtime agent

A stack with or without a harness serves your own agent in a container. Run the example first, from ~/hoop-agentcore. Replace api.example.com with your API’s host:
runtime create builds runtime-example/ for linux/arm64, pushes it to the stack’s AgentRepositoryUri, and creates a Runtime agent on the agent subnets. It writes the agent’s ARN to ./.agentcore-ids-<STACK>. runtime session prints each request the agent sent and its result. For your own agent, set these in the agent: On every request to the Sidecar, send:
  • X-Hoop-Token: <caller token>. Put the token in this header only, never in a URL or a body. The Sidecar refuses a request that carries it anywhere else.
  • X-AgentCore-Session-Id: <session id>. AgentCore gives your container the session id in the X-Amzn-Bedrock-AgentCore-Runtime-Session-Id header of each /invocations request. Copy it.
  • Authorization with your API’s own token, if the API takes one. The listener forwards it unchanged.
runtime-example/agent.py sends these headers in code, so the tokens never enter the model’s context.

Verify

To check the last rows from the harness, ask it in a session framed as a test. Asked plainly to skip the Sidecar or the token, the model refuses. Replace api.example.com with your API’s host:
The Sidecar writes the audit trail to the stack’s log group, SidecarLogGroup, which keeps it for 90 days. To read it:
An admitted request’s row names agentcore and the session id the agent sent. The DELETE rows name rule read-only. A request without the token is the anonymous row with rule identity, and carries no session id.

Postgres through the Sidecar

The postgres template gives the agent psql against one Postgres database, through a Sidecar postgres listener. The listener refuses the writes that its SQL classifier finds, masks emails in results and records each statement with the session id. It keeps every network control above, and differs from the REST setup:
  • An image with psql. The harness has no internet to install psql, so it runs the official postgres image for linux/arm64 from ECR Public, through an ECR pull through cache rule in the stack.
  • The session id travels in PGOPTIONS. The agent sends -c agentcore.session.id=<id>, and each audit row names it.
  • The login’s grants stop what the listener misses. The listener passes the client’s login through to the database, so the agent holds the password. Some statements that write pass the classifier as reads. The demo login has SELECT on one table and nothing more.
  • A demo database. The stack runs Postgres on Fargate, and the database admits the Sidecar’s security group only. The README shows how to point the listener at your RDS instance over VPC peering.
Its README lists the steps and the limits, such as COPY ... TO STDOUT returning rows unmasked and a refused write closing the connection.

Kubernetes through the Sidecar

The kubernetes template gives the agent kubectl against one EKS cluster with a private API endpoint, through a Sidecar http listener in front of the API server. The Sidecar refuses DELETE, masks Secret values in kubectl’s JSON and records each request with the session id. It keeps every network control above, and differs from the REST setup:
  • No caller token. kubectl cannot send X-Hoop-Token, so the listener admits any caller that the security groups let in. The API server’s token check is the gate, and rows name the principal anonymous.
  • An image with kubectl. setup.sh builds it for linux/arm64 with Docker and pushes it to the stack’s ECR repository.
  • HTTPS to the listener. kubectl runs in the agent’s container, so it can trust the listener’s self-signed certificate. setup.sh makes the certificate.
  • VPC peering to the cluster. The peering route is in the Sidecar’s route table only, and the cluster’s security group admits the Sidecar, not the agents.
  • The session id travels as an impersonation extra. kubectl sends it in Impersonate-Extra-Hoop-Session, which needs one ClusterRole for userextras/hoop-session.
Its README lists the steps and the limits, such as YAML and Protobuf responses that carry Secret values unmasked, and client-side kubectl apply failing on the masked annotation.

Limits

AgentCore’s, and this stack’s:
  • The agent holds the tokens. The harness and the Runtime agent keep the caller token and the API token in environment variables, and GetHarness and GetAgentRuntime return them. Moving the API token into the Sidecar is planned.
  • A token that the agent prints lands in CloudWatch. The harness logs each tool result.
  • Each VPC endpoint is an AWS API that the agent’s shell can call with the execution role: Bedrock models, ECR pulls and an ECR authorization token, and CloudWatch Logs writes under /aws/bedrock-agentcore/runtimes/. The role holds no more than a session needs, so add nothing to it. Each endpoint’s policy admits this account’s principals only. AWS APIs without an endpoint, STS and Secrets Manager among them, time out.
  • Keep InvokeHarness for people you trust with the agent. Its caller can add a browser, code interpreter or gateway tool to a session. Such a tool needs a permission on the execution role and the AgentCore endpoint, and this stack grants neither.
  • Bedrock models only. The model call leaves through the bedrock-runtime endpoint. An OpenAI or other LiteLLM model needs a path out: point its apiBase at a Sidecar listener to that provider. A LiteLLM OpenAI model worked this way in our tests. This stack has no such listener.
  • The listener is plain HTTP inside the VPC. The harness loop runs outside the agent’s container and trusts no private CA, and a public certificate needs a domain that you own. The security groups admit only the agents.
  • The agent can resolve the Sidecar’s hosts. The DNS Firewall covers the whole VPC, so its allow list names the API’s host, Docker Hub, use.hoop.dev and api.segment.io for the Sidecar. The agent resolves these names and has no route to them.
  • A missing endpoint hangs a session. Without the Bedrock Runtime or ECR endpoint, a session sends no byte for minutes and ends with no error.
  • Memory is off. The harness sets Memory: Disabled, because AgentCore memory was not tested in VPC mode.
  • A new VPC. The stack does not attach to an existing VPC.
  • Idle cost: about $0.095 per hour, $68 per 30 days, in us-east-1. That is four interface endpoints in two zones ($0.080), the Sidecar’s 0.25 vCPU and 0.5 GB on Fargate ARM ($0.0099), and its public IPv4 address ($0.005). Add $0.40 per month for each secret, $0.50 per month for the hoop.internal zone, $0.60 per million DNS Firewall queries, and each session’s Runtime and model charges.
The Sidecar’s:
  • 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 agentcore, not a person. Per-user rows need a per-user token.
  • The rule reads the request method only. An API that accepts X-HTTP-Method-Override, X-HTTP-Method or X-Method-Override runs the method that the header names, even on a GET. Turn overrides off at the API.
  • 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.
  • Masking applies to responses. A JSON or text response under br or zstd is refused, not forwarded unmasked. A JSON or text response too large to mask is refused too: over 8 MiB as sent, or over 16 MiB once inflated. See Masking.
  • Scope. The template covers JSON REST APIs over HTTP/1.1 and HTTP/2.

Clean up

From ~/hoop-agentcore:
delete removes the Runtime agent, then the stack. Delete the API token secret only after the stack is gone: the stack reads the secret to delete the harness. Then remove ~/hoop-agentcore and the Sidecar at use.hoop.dev (Sidecars).
  • AgentCore can hold the subnets for hours. It releases its network interfaces in the agent subnets late, 8 h 20 min after the agents were gone in our tests. Until then the stack cannot delete the subnets and ends in DELETE_FAILED. Run delete again later.
  • A failed create can leave a namespace. If the create fails while Cloud Map makes the hoop.internal namespace, the rollback can leave it behind. Find it with aws servicediscovery list-namespaces and delete it.

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, read-only, and one masking rule, pii, however many agents and sessions call it. One rule can carry more than one check: read-only names seven methods, and pii three entities.
  • The license comes from use.hoop.dev. With HoopSidecarToken, the Sidecar takes your use.hoop.dev organization’s license on each heartbeat, and ignores HoopLicense. A free organization holds one Sidecar. An Enterprise license with the guardrails and data-masking features lifts the one-rule caps. Your organization can hold it, or you pass it in HoopLicense instead of HoopSidecarToken: with setup.sh, set LICENSE_FILE instead of SIDECAR_TOKEN_FILE. See Licensing.
  • The Sidecar logs its rule limits when it starts. With G from Verify:
    An Enterprise license prints "guardrail_rules":"unlimited","mask_rules":"unlimited".
  • Approvals and central management: the Control Plane adds approvals, shared rule sets and one view of many Sidecars. It also needs an Enterprise license.

Next

OpenAI Agents API

The same setup for an agent in an OpenAI hosted sandbox.

Claude Managed Agents

The same setup for an agent on Anthropic’s cloud sandbox.

HTTP

What the http listener inspects, masks and refuses.

Guardrail Rules

operation, http_resource, http_status and http_header rules.