Arena
hb arena runs intentionally vulnerable AI agents on your machine, so you can practise testing agents and see what hb test finds. No hb login is needed.
Concept and Architecture
Each agent is a packaged application with planted, documented vulnerabilities. You pull one from a catalog, run it locally, and attack it.
hb test / other tools
β A2A v1.0 or OpenAI-compatible, with your access token
βΌ
gateway (127.0.0.1:11500)
β
βΌ
door βββΊ agent containers network without a route out
β
ββββββΊ the agent's model, and the hosts its manifest lists
| Part | What it does |
|---|---|
| Catalog | An index of agents, each described by an arena.yaml manifest. The default one is humanbound-arena. |
| Agent | One or more Docker containers, started without privileges, on a network that has no route out. |
| Door | A small proxy hb runs next to each agent: the way in from 127.0.0.1, and the agent's only way out. |
| Gateway | A local server that presents every running agent the same way and requires your access token. |
Warning
Arena agents are built to be exploited. Read the disclaimer before you run one.
Network and Privilege Isolation
Agents are built to be exploited, so hb limits what a hijacked one can reach and do.
Network. An agent's network has no route out. Through the door it may reach only:
| Destination | Source |
|---|---|
| Its model | The host and port of OPENAI_BASE_URL, or api.openai.com:443 when unset. Only for agents that declare an OPENAI_* key. |
| Hosts its manifest lists | runtime.egress in arena.yaml. |
| Its own services | The other containers of a multi-container agent. |
Everything else is refused, including your machine and your local network. hb arena logs <id> --door shows what was allowed and what was blocked.
Privileges. Every container of an agent must meet the container baseline:
| Rule id | The container⦠|
|---|---|
non-root |
runs as a user other than root |
not-privileged |
is not privileged |
capabilities |
drops all capabilities and adds none |
no-new-privileges |
cannot gain new privileges |
host-namespaces |
shares no namespace with the host |
host-mounts |
mounts no host path or device |
loopback-ports |
publishes ports on 127.0.0.1 only |
internal-networks |
is only on networks with no route out |
Containers are also limited to 512 processes, 2 GB of memory and 2 CPUs.
hb checks the baseline before it reports an agent ready and again before a test, and stops an agent that fails. hb arena check <id> runs the check on demand: exit code 0 all rules met, 1 problems found, 2 agent not running or Docker unusable.
Neither protection can be switched off. Both have limits:
- An allowed destination is still a way out. hb filters by host and port, not by content.
- Only web traffic from clients that honour
HTTP_PROXYandHTTPS_PROXYpasses. Anything else fails to connect. - The baseline covers how containers are configured, not what an agent does.
- On a Docker that cannot hide the network's gateway address, the agent may reach what the Docker host serves on all interfaces.
hb arena runwarns when that is so.
Usage
Quick start
You need Docker running and an LLM key for the agent.
pip install humanbound
hb arena ls # browse the catalog
hb arena info <agent> # keys, network, planted vulnerabilities
hb arena config set OPENAI_API_KEY=sk-... # the agent's model key
hb arena run <agent> # pull, start, wait until healthy
hb test --target arena://<agent> # test it on the local engine
hb arena stop --all
Model configuration
Two models are involved, configured separately.
| Model | Used for | Configured with |
|---|---|---|
| The agent's | The agent's own answers | hb arena config set, stored in ~/.humanbound/arena/arena.env |
| The test engine's | The attacker and the judge in hb test |
HB_PROVIDER, HB_API_KEY, HB_MODEL in your shell |
The agent's model. hb arena info <id> lists the keys an agent accepts. Most take:
| Key | Meaning |
|---|---|
OPENAI_API_KEY |
The key for the model's API. Usually required. |
OPENAI_MODEL |
The model name. The agent's default applies when unset. |
OPENAI_BASE_URL |
Any OpenAI-compatible server. https://api.openai.com/v1 when unset. |
hb arena config set OPENAI_API_KEY=sk-... OPENAI_MODEL=gpt-4.1-mini
# a local model, for example Ollama on this machine
hb arena config set OPENAI_BASE_URL=http://host.docker.internal:11434/v1
hb arena config set OPENAI_API_KEY=ollama OPENAI_MODEL=llama3.1:8b
- Changes apply at the next
hb arena run. - The host and port of
OPENAI_BASE_URLare the only model destination the agent may reach. localhostinOPENAI_BASE_URLwould be the agent's own container; usehost.docker.internal.- On Linux the local server must listen on the Docker bridge address (usually
172.17.0.1), not on127.0.0.1.
The test engine's model.
export HB_PROVIDER=openai HB_API_KEY=sk-... # model defaults to gpt-4.1
export HB_PROVIDER=ollama # or fully local; defaults to llama3.1:8b
Commands
| Command | What it does |
|---|---|
hb arena ls [--installed] |
List the catalog, or only installed agents. |
hb arena info <id> [--agent-yaml] |
Show an agent's details. Never installs. |
hb arena pull <id> |
Install an agent and pull its images. |
hb arena run <id> [--env-file FILE] [--yes] |
Start an agent and the gateway. |
hb arena ps |
List running agents and their URLs. |
hb arena logs <id> [-f] [--tail N] [--door] |
Show an agent's logs; --door shows what it reached or was refused. |
hb arena check <id> [--json] |
Check the agent's containers against the container baseline. |
hb arena reset <id> |
Recreate the agent. Drops its conversations. |
hb arena stop <id> / --all / --all --any-owner |
Stop one agent, all of yours, or every user's on this Docker. |
hb arena rm <id> |
Stop an agent and remove its images and manifest. |
hb arena endpoint <id> |
Print the agent's URLs, your token and a ready-to-use bot config. |
hb arena token |
Print only your access token. |
hb arena config set/get/unset |
Manage the keys agents receive. |
hb arena validate <path> |
Validate an arena.yaml. |
hb arena serve [--host H] [--port P] |
Run the gateway in the foreground. |
HB_ARENA_PORT changes the gateway port; set it for every hb arena and hb test command. Agents belong to the home directory that started them, so several users can share one Docker.
Keys
Keys are stored in ~/.humanbound/arena/arena.env, readable only by you.
- An agent receives only the keys its manifest declares.
hb arena runprints their names, never their values. - Precedence, later wins:
arena.env, your shell,--env-file. - Only LLM keys such as
OPENAI_API_KEYare read from your shell, and only for agents from the default catalog. HB_*andHUMANBOUND_*names are never passed to an agent.
Testing with hb test
- The agent must be running. It is reset to a clean state first;
--no-resetskips that. - The test scope and the judge's context come from the agent's manifest.
- Runs are whitebox when the agent reports its tool calls, otherwise blackbox.
- Results are saved under
.humanbound/results/<experiment-id>/:meta.json(run, configuration, target) andlogs.jsonl(one conversation per line, with tool executions for whitebox runs).
Calling agents from other tools
Every call needs your token, as Authorization: Bearer <token> or X-Arena-Token: <token>. Treat it like a password.
TOKEN=$(hb arena token)
# A2A v1.0
curl -s http://127.0.0.1:11500/a2a/<agent> -H "Authorization: Bearer $TOKEN" \
-H 'A2A-Version: 1.0' -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"SendMessage","params":{"message":{"role":"ROLE_USER","messageId":"m1","parts":[{"text":"Hello"}]}}}'
# OpenAI-compatible: base URL http://127.0.0.1:11500/v1, model arena/<agent>, API key = token
curl -s http://127.0.0.1:11500/v1/chat/completions -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"model":"arena/<agent>","messages":[{"role":"user","content":"Hello"}]}'
- A2A replies carry the text in
result.message.parts[0].text. Send backresult.message.contextIdto continue a conversation. - The Agent Card is at
/a2a/<agent>/.well-known/agent-card.json. - Agent failures return a non-200 status: 404 not running, 502 agent error, 503 Docker down, 504 timeout.
- The OpenAI-compatible endpoint starts a new conversation per request and does not stream.
hb arena endpoint <id> prints a bot config you can adapt for your own A2A agent with hb test --endpoint. It uses two placeholders: $UUID (a fresh id each time) and $humanbound_conversation_id (stable within a conversation).
Other catalogs
HB_ARENA_INDEX points hb at another catalog: a URL, a fork, a local checkout or an index.json path.
- Agents from any catalog other than the default one never read keys from your shell.
- They are confirmed once before their first run, with their developer, images, key names and network access shown.
--yesaccepts without asking and is required in scripts. - When a catalog's index lists image digests, hb pulls exactly those images and refuses a different local image under the same tag.
Writing an agent
An agent is an arena.yaml in a folder named after its id. The Humanbound agent.yaml sits unchanged under agent:. Check it with hb arena validate <path>.
developer:
name: ACME Security Research
url: https://acme.example/arena
source:
image: ghcr.io/acme/arena-bank:1.0.0 # or compose: docker-compose.yml + service:
runtime:
port: 8080
env: {required: [OPENAI_API_KEY]}
egress: [files.example.com] # optional: hosts besides the model
- Images must run as a non-root user, listen on port 1024 or above, and work without capabilities.
- HTTP clients must honour the proxy variables. Do not declare them under
runtime.env. - Compose files may use prebuilt images only, with no published ports, host mounts or extra privileges.
ground_truthdeclares each planted vulnerability, optionally withreferencessuch asowasp-llm:LLM02.- For whitebox runs, point
integration.response.tool_callsat a list of{name, parameters, result}items in the agent's reply.
Platform notes
- Linux: Docker Engine 28 or later is recommended. Compose agents need the Compose v2 plugin.
- macOS and Windows: Docker Desktop.
- Not supported: Podman, and running
hb arenainside the hb Docker image.
Disclaimer
The arena is maintained for educational purposes. Its agents are deliberately vulnerable, and in places deliberately malicious: they exist to be attacked, so that you can learn how agents fail and how to test them.
- Never deploy an arena agent, and never run one where there is sensitive data or access to critical systems.
- Isolation is best effort. A container is not a virtual machine, and whatever an agent sends to a destination it may reach leaves your machine.
- Agents in the default catalog hold fake data only. Give an agent no real credentials beyond the LLM key it needs.
- You use the arena at your own risk. It comes with no warranty, and Humanbound accepts no liability for damage arising from its use.