Are you an LLM? You can read better optimized documentation at /docs/agent-guard/network/allow-a-host.md for this page in Markdown format
Allow a host
This guide takes an agent from a refused host to a working one. You find the host in the session's egress log, allow it for one run, and then make it permanent in the agent's definition. The example uses Claude Code and pip, which needs two hosts the default list does not carry. The same steps apply to any agent and any host.
Prerequisites
- Agent Guard installed (see Quickstart).
jq, to read the egress log.- For step 4, the
kitCLI and the agent definition from Run with an agent definition.
Step 1: Let the agent hit the refusal
Start Claude Code and ask it to install a Python package:
bash
agentguard run claude-code --workspace ~/projects/sample-appInstall the requests package with pip.
pip cannot resolve pypi.org, and the agent reports that the package index is unreachable. A name that is not on the list does not resolve inside the microVM, so this looks like a DNS failure rather than a refused connection.
Exit the agent.
Step 2: Find the host in the egress log
Every session writes an egress log next to its policy audit log. Point a variable at the newest one:
bash
log="$(ls -t ~/Library/Logs/AgentGuard/egress_audit.*.jsonl | head -1)"List the names the microVM refused to resolve:
bash
jq -r 'select(.kind == "dns" and .answer == "denied") | .name' "$log"pypi.orgThe list may include other names: hosts the agent tries for update checks or telemetry, which the baseline leaves out on purpose. Allow only the names the task needs.
A connection the list refused shows up as a reject record, for a port the list does not open, or a closed record, for an HTTPS connection whose name the list does not carry, such as one to a hard-coded address:
bash
jq -c 'select(.kind == "reject" or .kind == "closed")' "$log"json
{"kind":"reject","timestamp":"2026-10-09T15:54:30.714959634Z","sessionId":"20228-1791561250419316000","destination":"151.101.67.5:80","protocol":"tcp","uid":1001,"names":["cnn.com"]}names is the name the address was resolved from, so a refused connection can be traced back to the host the agent meant. A connection to a hard-coded address has no names.
Step 3: Allow it for one run
Pass the name with --allow-host:
bash
agentguard run claude-code --workspace ~/projects/sample-app \
--allow-host pypi.orgAsk for the install again. pip now reads the index from pypi.org, and fails on the download: the package files are served from files.pythonhosted.org. A host often depends on others that appear only once the first is reachable, so repeat steps 2 and 3 until the task completes:
bash
agentguard run claude-code --workspace ~/projects/sample-app \
--allow-host pypi.org \
--allow-host files.pythonhosted.orgThe install succeeds.
Step 4: Make it permanent in the agent definition
--allow-host lasts one run. For hosts the agent needs every time, add them to the definition under spec.egress, in the same syntax:
yaml
# my-agent/agent-definition.yaml
apiVersion: agentguard.jozu.dev/v1
kind: AgentDefinition
metadata:
name: my-claude-agent
spec:
framework: claude-code
egress:
- pypi.org
- files.pythonhosted.org
includes:
- name: vm-policies
type: policy
source:
modelkit: jozu.ml/jozu/agentguard-policies:vm-standardPack it under a new tag and run it:
bash
kit pack . -t my-claude-agent:v2
agentguard run my-claude-agent:v2 --workspace ~/projects/sample-appStep 5: Confirm the list
The first record in every egress log is the list the run enforced, with the source of each entry:
bash
log="$(ls -t ~/Library/Logs/AgentGuard/egress_audit.*.jsonl | head -1)"
jq -c 'select(.kind == "boot") | .entries[]' "$log"json
{"host":"deb.debian.org","port":443,"origins":["system"]}
{"host":"files.pythonhosted.org","port":443,"origins":["declared"]}
{"host":"platform.claude.com","port":443,"origins":["framework"]}
{"host":"pypi.org","port":443,"origins":["declared"]}declared entries come from the definition, framework from Claude Code's baseline, and system from the Debian mirror every run carries.
What just happened
- The microVM's resolver refused
pypi.orgbecause no source put it on the list, and recorded the refusal. --allow-hostadded the name for one run. Agent Guard resolved it on your desktop before boot, the resolver inside the microVM answered it, andpip's HTTPS connections passed the Jozu gateway's name check.spec.egressmoved the names into the definition, where they apply to every run and are reviewed and signed with the rest of the agent.
Common hosts
Git over SSH
HTTPS to GitHub is the name alone. SSH is a second port:
bash
--allow-host github.com --allow-host tcp://github.com:22Nothing checks what travels over port 22, so prefer HTTPS remotes where you can.
GitHub with a token
--allow-host api.github.com lets the agent reach the API with whatever token it has. To give it a token without putting the token in its environment, declare a header entry in the definition, and the Jozu gateway sets Authorization on every request. Credentials for allowed hosts walks through it.
Package registries
| Ecosystem | Entries |
|---|---|
| pip | pypi.org, files.pythonhosted.org |
| npm | registry.npmjs.org |
| Docker Hub (podman) | oci://docker.io |
| GitHub Container Registry | oci://ghcr.io |
| Jozu Hub (kit) | oci://jozu.ml |
A container registry takes the oci:// form rather than its host names. The registry's token service and content hosts differ by registry and are named in its own responses, so the Jozu gateway admits them as the pull reaches them, and signs the agent in with the login your desktop's docker or podman holds for the registry. Do not also list auth.docker.io as a plain name: the gateway then leaves it as listed and sends it no login. See Registry logins.
A service on your desktop
Your desktop is 192.168.127.254 inside the microVM, and every port on it is closed until you list it:
bash
--allow-host tcp://192.168.127.254:5432The connection arrives on your desktop's loopback interface, so a database listening on 127.0.0.1:5432 is reachable as 192.168.127.254:5432.
A host on your network
Name it if it has a name. A definition can carry tcp://db.internal:5432, and it resolves on the networks where db.internal exists. An IPv4 address is accepted by --allow-host only, because an address names a different machine on every network:
bash
--allow-host tcp://10.20.3.4:5432Next steps
- How the list is enforced, and what it does not cover: How network access works
- Tokens and registry logins the agent never holds: Credentials for allowed hosts
- Every entry form: Network access reference
- Every egress record kind and field: Logs and audit
