Are you an LLM? You can read better optimized documentation at /docs/agent-guard/operations/logs-and-audit.md for this page in Markdown format
Read logs and audit trails
Agent Guard writes six log files. Each captures a different layer of what happened during a run.
| File | What it contains | Path |
|---|---|---|
| Policy audit log | Every policy decision for every tool call (allow, deny, audit, elicit). JSONL, one record per line. | ~/Library/Logs/AgentGuard/policy_audit.<session>.jsonl |
| Egress log | Every name the microVM resolved or refused, every connection it refused, every HTTPS connection the Jozu gateway passed through, signed in to, or closed, and every host a registry's responses led the run to admit. JSONL, one record per line. | ~/Library/Logs/AgentGuard/egress_audit.<session>.jsonl |
| Credential audit | Every credential the Jozu gateway asked Agent Guard for, which consumer asked, and whether it was answered. Never a value. JSONL, one record per line. | ~/.agentguard/host-private/credential-audit/credential_audit.<session>.jsonl |
| Agent definition audit | For each run that applies an agent definition: the definition, its modules, and where each need's value came from and whether it resolved. Never a value. JSONL, one record per run. | ~/Library/Logs/AgentGuard/agent_def_audit.jsonl |
| Policy server log | Operational log for the Jozu AI Gateway inside the microVM: startup, policy loads, evaluation errors. Plain text. | ~/Library/Logs/AgentGuard/policy_server.<session>.log |
| MicroVM serial console | Guest boot output and any kernel-level messages from inside the microVM. Plain text. | ~/.agentguard/vm/serial.log |
With AGENTGUARD_HOME set, the audit, egress, agent definition audit and server logs are written to $AGENTGUARD_HOME/logs/, the credential audit to $AGENTGUARD_HOME/host-private/credential-audit/, and the serial log to $AGENTGUARD_HOME/vm/serial.log.
The audit log is the file that matters most. It is the record of what your agent tried to do and what Agent Guard decided about each call.
Tailing logs
bash
agentguard logsTails the most recent session's audit log and policy server log, and the serial log, together, formatted for terminal reading. The audit and server logs it follows are the newest when it starts, so restart it after starting a new run; the serial log follows across sessions on its own. --no-serial hides the serial log, and --guardrails shows only GuardrailPolicy decisions. Run the agent in one terminal and agentguard logs in another to watch decisions as they are made.
To watch just the most recent audit log:
bash
tail -f "$(ls -t ~/Library/Logs/AgentGuard/policy_audit.*.jsonl | head -1)"Each boot starts a fresh serial log. The previous session's is kept as serial.log.1, the one before as serial.log.2, and so on; agentguard logs -p 1 prints the previous one.
To correlate policy decisions with microVM resource pressure, run agentguard top in one terminal and tail the audit log in another:
bash
tail -f "$(ls -t ~/Library/Logs/AgentGuard/policy_audit.*.jsonl | head -1)" | jq -c '{ts: .timestamp, tool: .tool, action: .action, rule: .rule}'What the audit log records
Each line in an audit log is a single tool call decision. The record captures the timestamp, the agent that issued the call, the tool name, the tool arguments, the policy decision (allow, deny, audit, or elicit), and the rule that matched if any.
Records are appended in real time as the agent makes tool calls. Each session writes its own file, so concurrent runs do not interleave. To look across runs, read every file: cat ~/Library/Logs/AgentGuard/policy_audit.*.jsonl.
What the egress log records
The egress log is where to look when the agent cannot reach a host. Its first record is the allow list the run enforced, with the source of each entry. After that come the names the microVM's resolver answered or refused, the connections it refused, and the HTTPS connections the Jozu gateway passed through or closed. To list the names the agent was refused in the latest session:
bash
jq -r 'select(.kind == "dns" and .answer == "denied") | .name' \
"$(ls -t ~/Library/Logs/AgentGuard/egress_audit.*.jsonl | head -1)"Every record has three fields:
| Field | Meaning |
|---|---|
kind | The record kind, below |
timestamp | When the record was written, in UTC |
sessionId | The session, the same as in the file name |
boot
The first record. It is the list the run enforces.
| Field | Meaning |
|---|---|
mode | allowlist, or open with --dangerously-allow-all-hosts |
entries | Each entry as {"host", "port", "origins"}, ordered by host then port. Absent in open mode |
resolved | Each exact name that resolved on your desktop, with its IPv4 addresses |
unresolved | Names that did not resolve on your desktop |
injections | Each host the gateway terminates to apply a credential. A header entry is {"host", "headers", "needs"}, with the header names and the needs they reference. A registry's API host is {"host", "registry", "credential"}, where credential is oci://<registry> when your desktop has a login for it and absent when it does not. Never a value. Absent when there are none |
digest | SHA-256 of the list as the microVM received it |
For a run with a header entry for api.github.com, oci://ghcr.io with a login, and oci://docker.io without one:
bash
jq -c 'select(.kind == "boot") | .injections[]' \
"$(ls -t ~/Library/Logs/AgentGuard/egress_audit.*.jsonl | head -1)"json
{"host":"api.github.com","headers":["Authorization"],"needs":["GITHUB_TOKEN"]}
{"host":"ghcr.io","credential":"oci://ghcr.io","registry":"ghcr.io"}
{"host":"registry-1.docker.io","registry":"docker.io"}dns
A name the microVM's resolver answered or refused.
| Field | Meaning |
|---|---|
name | The name looked up |
answer | allowed (on the list), resolveOnly (a host reached for the agent), denied (not on the list), or forwarded (open mode) |
Each name is recorded once per session. Records are limited to 10 per second, with bursts of 20; names over the limit are counted in the summary record's dnsSuppressed.
connection
A connection the microVM accepted on a tcp:// entry.
| Field | Meaning |
|---|---|
destination | address:port |
uid | The account inside the microVM that opened it. The agent is 1001 |
names | The listed names whose answers contained the address |
reject
A connection the microVM refused: an address or port the list does not open.
| Field | Meaning |
|---|---|
destination | address:port |
protocol | tcp, udp or icmp |
uid | The account inside the microVM that tried it |
names | The names whose answers contained the address. Absent for a hard-coded address |
Records are limited to 10 per second. The summary record's rejected counts every refusal.
splice
An HTTPS connection the Jozu gateway passed through, written when it ends.
| Field | Meaning |
|---|---|
sni | The name the client asked for |
dialled | The address the client connected to |
resolved | The address the gateway connected to, from resolving sni |
bytesUp, bytesDown | Bytes sent and received |
durationMs | How long the connection was open |
inject
A connection to a host the gateway terminates for a credential, written when it ends. A connection that forwarded no request writes none.
| Field | Meaning |
|---|---|
sni | The host |
dialled | The address the client connected to |
resolved | The address the gateway forwarded to |
headers | The headers the entry sets. For a registry with a login, Authorization, which is sent only on requests that carry none |
needs | The needs those headers reference, or oci://<registry> for a registry login. Absent for a registry with no login |
requests | Requests forwarded to the host |
bytesUp, bytesDown | Bytes sent and received |
durationMs | How long the connection was open |
inject-failed
A request the gateway could not apply its entry to, because it could not obtain the credential or the credential's type cannot be sent in a header. The request is answered 502 Bad Gateway. Fields: sni, dialled, and reason, the error.
closed
An HTTPS connection the Jozu gateway closed: one whose name it refused, or one to a host it terminates for a credential on which it refused a request.
| Field | Meaning |
|---|---|
sni | The name the client asked for, if it sent one |
dialled | The address the client connected to |
reason | Why, below |
durationMs | How long the connection was open |
reason | Meaning |
|---|---|
SNI not allowed | The name is not on the list |
no SNI, not TLS | The client sent no name, or did not speak TLS |
at capacity | The gateway was handling as many connections as it accepts |
dial: followed by an error | The gateway could not connect to the allowed host |
loopback | The allowed name resolves only to loopback, link-local or unspecified addresses |
unresolved | A host the gateway terminates for a credential did not resolve on your desktop before boot |
host-mismatch | A request to a terminated host carried a Host header naming another host. Answered 421 Misdirected Request |
absolute-target | A request to a terminated host used an absolute URL as its target. Answered 421 Misdirected Request |
trace | A TRACE request to a terminated host. Answered 405 Method Not Allowed |
upstream-tls | The host's certificate failed the gateway's verification |
admit
A host a registry's response named, which the run admitted.
| Field | Meaning |
|---|---|
host | The admitted host |
from | The registry host whose response named it |
via | realm, the token service in a 401 Bearer challenge, or redirect, the Location of a 3xx |
as | inject, a host that takes the registry's login; splice, a plain host checked by name; or allowed, a token service the run already reaches as listed, which receives no login |
admit-refused
A host a registry's response named, which the run refused to admit.
| Field | Meaning |
|---|---|
sni | The registry host whose response named it |
host, via | As in admit |
reason | limit, the run has admitted 32 hosts; unresolved, a token service that does not resolve on your desktop; sink-host, a model API host; gateway-host, a host the gateway reaches for the agent, such as an MCP endpoint; or not-login-host, a response from a host that is not a registry's |
admit-failed
An admission that could not complete, with sni, host, via and reason, the error. The response that named the host is passed to the client either way.
gap
Records were lost. source says which component lost them and reason says why. Treat the session's log as incomplete from that point.
summary
The last record, written when the session ends.
| Field | Meaning |
|---|---|
rejected | Every connection the microVM refused, including those over the reject record limit. Absent in open mode |
dnsSuppressed | Names the dns record limit dropped |
What the credential audit records
When an agent definition gives the Jozu gateway a credential, through a header entry, a registry entry, a remote MCP module's headers or an llm module's credential, the gateway asks Agent Guard on your Mac for the value. Each answer and each refusal is a line in the credential audit, naming the need, the consumer that asked, the type, and what produced the value, never the value itself. The file is outside every directory shared with the microVM. See Network access reference for the fields.
What the agent definition audit records
The agent definition audit answers, after the fact, what a run took from an agent definition: which definition and framework, which modules it pulled, and for every value the definition needs, which resolver ran, where it looked, and whether it found a value. It records no values. source names where a value came from, and a text need, whose value is written in the definition, has no source.
A run that applies an agent definition appends one line once its needs have resolved and the definition has passed validation, before the microVM boots. A run with no definition writes nothing, and neither does a run that stops on a required need that did not resolve; its output names the need. So an entry with success: false is an optional need, or a registry reached without a login. Every run appends to the same file, created readable only by your account.
For the definition in Build your first custom runtime:
bash
tail -1 ~/Library/Logs/AgentGuard/agent_def_audit.jsonl | jq -c '.needs[]'json
{"name":"OPENAI_API_KEY","target":"env","kind":"envFrom","source":"OPENAI_API_KEY","success":true,"withheld":true,"type":"secret","consumers":["llm:assistant"]}
{"name":"GREETING","target":"env","kind":"text","success":true,"type":"secret"}Each record has these fields:
| Field | Meaning |
|---|---|
timestamp | When the record was written, in UTC |
agent_ref | The agent definition reference the run was given |
framework | The definition's spec.framework: an agent name, or a custom runtime's image reference. Empty when the definition names none and the agent named on the command line runs |
module_count | The number of llms, mcps, policies and skills modules, with the base chain merged. An llm module skipped because its credential did not resolve is not counted |
policy_refs, mcp_refs, skill_refs | The ModelKit references of the policy, MCP and skill modules. A remote MCP module, which has an endpoint rather than a ModelKit, is not listed. Absent when there are none |
needs | One entry per need, in definition order, then one per oci:// registry entry of the run, from the definition or --allow-host. Absent when there are none |
Each entry in needs:
| Field | Meaning |
|---|---|
name | The need's name, or oci://<registry> for a registry login |
target | env or file. Empty for a registry login |
kind | The resolver: envFrom, file, text or keychain, or registryLogin for a registry login |
source | Where the resolver looked: the host variable for envFrom, or keychain:agentguard/NAME when the variable was unset and the keychain item answered; the path for file; service/account for keychain; absent for text. For a registry login, the Docker or Podman auth file, docker-credential-<helper>, or DOCKER_AUTH_CONFIG that held it |
optional | true for a need marked optional: true. Absent otherwise |
success | Whether a value was found |
error | Why not, when success is false |
withheld | true when the value is kept out of the agent's environment. Absent otherwise |
type | The credential type: secret for a need, basic for a registry login |
consumers | The Jozu gateway consumers that use the value: llm:<module>, mcp:<module> or egress:<host>. Absent when only the agent uses it |
The credential audit is the other half: it records each time the Jozu gateway asked for one of these values during the session.
Log retention
Agent Guard does not prune audit logs, server logs, or rotated serial logs: each session adds files that stay until you remove them. On a machine that runs many sessions, archive older sessions yourself:
bash
# Compress audit, egress and server logs older than 30 days
find ~/Library/Logs/AgentGuard/ \( -name 'policy_*' -o -name 'egress_audit.*' \) -mtime +30 ! -name '*.gz' -exec gzip {} +If you have a log aggregation tool (Splunk, Datadog, Sumo, OpenSearch), point it at ~/Library/Logs/AgentGuard/policy_audit.*.jsonl and let it handle retention.
Forwarding to a centralized audit repository
If Agent Guard is configured with a Hub, audit records sync to the Hub-side audit repository on a recurring schedule, independent of when any individual microVM session exits. Records that fail to sync (network unavailable, Hub down) stay in the local file and are retried on the next scheduled attempt. The local files are the authoritative copy regardless. The egress log and the agent definition audit stay on your Mac and are not part of this sync.
For air-gapped environments where Agent Guard runs without any Hub connectivity, export the JSONL files as part of your existing audit collection process.
Why the audit log is protected
The agent cannot reach the audit log from inside the microVM, and every record carries a hash of the prior record so tampering is detectable on review. See Threat model for the protection guarantees in detail.
agentguard uninstall does not delete ~/Library/Logs/AgentGuard/. The audit trail survives reinstall and is available for forensic review of a compromised machine. To wipe it, delete the directory manually.
Reading the microVM serial log
The serial log is useful for diagnosing microVM boot problems specifically. If the microVM fails to start, the kernel boot output here usually says why.
bash
cat ~/.agentguard/vm/serial.logCommon patterns to look for: filesystem mount failures, DHCP timeouts, init binary crashes. None of these are application-level problems with your agent or policies, so the serial log is mostly relevant when reporting a bug to Agent Guard or when something has gone wrong below the agent layer.
