Verify what an AI agent did: signed audit logs
An audit trail is only evidence if a change to it would show. With an identity declared, Constle signs and chains every entry, and constle audit verify names the line where an edit, a deletion or a reordering breaks the chain.
On this page
- What you'll get
- Why sign and chain
- 1. Declare an identity
- 2. Run the agent and find the log
- 3. Read the events that matter
- 4. Verify, pinned
- 5. See what tampering looks like
- 6. Keep a copy somewhere else
- What an intact chain proves, and what it doesn't
- FAQ
- Can the agent edit or delete its own audit log?
- Does verification need a network connection?
- Are tool arguments or secrets written to the log?
- Is this enough for compliance logging requirements?
Source release coming soon
The source and installers are not public yet, so there is nothing to install today. This guide documents Constle as it behaves now. More in Project status.
What you'll getLink to this section
- A JSONL audit log per agent per UTC day, written by the host, with every allow, refusal, tool call, approval and limit in it.
- Ed25519 signatures on every entry and a SHA-256 hash chain between entries, verifiable offline.
- A verification that is pinned to the agent identity and approver key you expect, not just to whatever key the file carries.
Why sign and chainLink to this section
A log the agent could write to is not evidence about the agent, and a log anyone with file access could edit quietly is not much evidence either. Constle writes the log from the host process, outside the sandbox. With identity.did set, each entry is signed with the agent's key and carries the hash of the line before it, so the file is checked as a sequence: change one byte and that line's signature fails; remove a line and the next one points at a hash that no longer exists. Format details: Audit log and verification.
1. Declare an identityLink to this section
constle identity create basic-agent [email protected]Paste the printed DID under identity.did. Without it, the log is still written, but unsigned. With it, constle run refuses to start if the key is missing, so a run cannot silently fall back to an unsigned log. More in Give each AI agent a verifiable identity.
2. Run the agent and find the logLink to this section
constle run agent.yaml
ls ~/.constle/logs/The log is ~/.constle/logs/<identity.name>-<YYYY-MM-DD>.jsonl, one file per agent per UTC day. A new run appends to the day's file, and its first entry chains to the last line already there.
3. Read the events that matterLink to this section
Each line is one JSON object with timestamp, run_id, agent_name, event and details, plus did, prev_hash and sig when signed. A few questions and the lines that answer them:
LOG=~/.constle/logs/basic-agent-$(date -u +%F).jsonl
grep network_blocked "$LOG" # where did it try to go that it wasn't allowed?
grep -E 'tool_call_start|mcp_tool_blocked' "$LOG" # which tools did it call, or try to?
grep -E 'gate_(triggered|approved|denied|timeout)' "$LOG" # what waited for a person, and what was decided?
grep -E 'spending_limit_reached|terminated_by_limit' "$LOG" # did a cap or the time limit stop it?| Event | Means |
|---|---|
run_started, run_finished |
a run began and ended; run_started records the requested and achieved isolation and the credential names granted |
network_allowed, network_blocked |
the proxy allowed or refused a connection (written at the end of the run) |
tool_call_start, tool_call_end |
an MCP tool call was forwarded; the server, the tool and the argument size, never the arguments |
mcp_tool_blocked, mcp_request_blocked |
the MCP gate refused a call or a request |
gate_triggered … gate_timeout |
a human gate opened and how it resolved |
spending_limit_reached, terminated_by_limit |
a cap or the time limit acted |
The full list: Audit log.
4. Verify, pinnedLink to this section
constle audit verify --agentfile=agent.yaml ~/.constle/logs/basic-agent-2026-08-08.jsonl✓ audit log verified: /home/you/.constle/logs/basic-agent-2026-08-08.jsonl
entries: 2 (all signatures valid, hash chain intact)
signed by: did:key:z6MkgroKowQYDZjDmqbn82mJv4YFPKowS2xDhxGYrp4u3P1o
pinned: DID matches the expected identity
pins from: agent.yaml (identity.did, human_gates.approver_pubkey)--agentfile takes both trust anchors from the Agentfile: the agent's identity.did and the human_gates.approver_pubkey that recorded approvals must verify against. --did= and --approver-pubkey= pin them one at a time. The check needs no network: the key is recovered from the DID string itself.
5. See what tampering looks likeLink to this section
Edit one byte of the file and verify again:
error: TAMPERING DETECTED in /home/you/.constle/logs/basic-agent-2026-08-08.jsonl
line 1: invalid_signature — signature does not verify against did:key:z6MkgroKowQYDZjDmqbn82mJv4YFPKowS2xDhxGYrp4u3P1o — the entry was edited after signing| Change | Reported as |
|---|---|
| A line edited | invalid_signature, at that line |
| A line deleted, with another after it | chain_break_missing_entry, at that position |
| Lines reordered | chain_break_reordered, at that position |
| The whole log re-signed with another key | did_mismatch, when the DID is pinned |
6. Keep a copy somewhere elseLink to this section
The log alone cannot show that lines were cut from its end: a shorter chain is still an intact chain. If that matters to you, copy each day's log, or at least its last line, to storage the host cannot rewrite, and compare later. This is a practice, not a Constle feature.
What an intact chain proves, and what it doesn'tLink to this section
| Proves | Does not prove |
|---|---|
| The runtime's own account of the run is unaltered since it was signed | That the account is true: the host wrote it, and the host is trusted |
| The lines present are signed and in order, with none missing between them | That none were cut from the end |
| A recorded webhook approval was signed by the pinned approver key over that call's digest | Which arguments a signed digest was for: the log omits the arguments |
Unpinned, verification only shows the log agrees with its own key, and says so. On Windows an Agentfile that sets identity.did does not run, so there are no signed logs there (limitation 7).
FAQLink to this section
Can the agent edit or delete its own audit log?Link to this section
The log is written by the host constle process to the invoking user's home directory, outside the sandbox; nothing in the sandbox can reach it. Someone with access to the host can change the file; verification then reports the line where the chain breaks, except for lines cut from the end (step 6).
Does verification need a network connection?Link to this section
No. constle audit verify recovers each key from the DID string, with no registry, no resolution service and no network call.
Are tool arguments or secrets written to the log?Link to this section
No. Tool calls are recorded by server, tool name and argument size; credentials are recorded by name only, never by value. Gate decisions record the identifying fields of the request, not its arguments.
Is this enough for compliance logging requirements?Link to this section
It gives you an automatically written, signed and chained record of what the runtime allowed and refused, which you can verify yourself. Whether that meets a particular regulation depends on the regulation and your system; ask whoever is responsible for that assessment.