Claude Code and Codex Audit Trail

Asqav gives Claude Code and OpenAI Codex sessions an audit trail made of signed receipts. The coding tool runs the Asqav hooks itself, so recording does not depend on the model choosing to log anything. Each receipt is signed with ML-DSA-65 and can be checked against Asqav's public key by someone who does not trust the machine it came from.

Per tool call: asqav hook posttool signs a receipt after each matched tool call. It never blocks.

Per session: the asqav-claude-code plugin signs one code-authorship receipt when the session stops.

Before a tool call: asqav hook pretool signs first and blocks a call it cannot get a verified receipt for.

Why a session transcript is not enough

Claude Code keeps each session's transcript as a file on the machine it runs on. Anyone with access to that machine can edit the file, so it shows what happened but cannot show that nobody changed it later. OpenTelemetry export feeds dashboards, and its events are not signed. A receipt is different: change one byte and its signature check fails, and the check needs only the receipt and a public key. See platform logs vs independent evidence for the longer comparison.

Sign each tool call after it runs

The audit hook runs after each tool call Claude Code makes, signs a receipt for it and lets the session carry on. If the signer is slow or down, the call has already run and stays unsigned, so this hook is evidence, not a gate. Install the SDK with both extras and set the agent the hook signs as:

bash
pip install "asqav[cli,verify]"
export ASQAV_API_KEY=<paste your key here>
export ASQAV_AGENT_ID="your-agent-id"

Then wire the hook into the PostToolUse event of your Claude Code settings.json:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "asqav hook posttool",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

Each receipt names the tool, as tool:bash for a shell command, and carries the Claude Code session id. From asqav 0.10.12 the hooks send a SHA-256 digest of the tool call, not its arguments. Pass --clear-context when you want the platform to hold the arguments in clear.

Sign one receipt per session

The asqav-claude-code plugin buffers what the hooks observe and signs one code_authorship receipt when the session stops. It records the path and the SHA-256 of each edited file before and after the change, the SHA-256 and program name of each shell command, and the git context. File contents, command lines, command output and prompts stay on the machine.

bash
git clone https://github.com/jagmarques/asqav-claude-code.git
claude --plugin-dir ./asqav-claude-code

The plugin needs Python 3.10 or newer, git and network access to api.asqav.com, with no pip packages. Every failure path warns and exits 0, so a missing key or a network outage never blocks the session, and a failed signing at session stop shows as a system message. When signing works, Claude Code shows the receipt's verify link, and that page is public. Code-authorship receipts describes the receipt type.

OpenAI Codex

Codex uses the same hook protocol, so the plugin's script signs Codex sessions when it runs with --harness codex. Paste the example from the plugin into ~/.codex/config.toml or a trusted project .codex/config.toml, set the absolute script path and approve the hooks once with /hooks. File edits arrive as apply_patch, and every file a patch touches is hashed before and after. Codex hooks cover shell, apply_patch, MCP and local function tools, not hosted tools such as web search, so the receipt covers what ran on your machine.

Block a tool call that cannot be signed

For enforcement, asqav hook pretool runs in the PreToolUse event before each matched tool call. It sends the call to be signed, checks the receipt against Asqav's published keys and exits with code 2, which Claude Code treats as a block, when it cannot get a receipt it can verify. Its reach, its failure cases and the settings that can switch hooks off are set out in Enforcement. Read that page before you rely on the gate.

Which one to use

OptionWhat it signsCan blockWhat leaves the machine
asqav hook posttoolEach tool call, after it runsNoA digest of each call
asqav-claude-code pluginOne receipt per session, at stopNoHashes of files and commands
asqav hook pretoolEach tool call, before it runsYes, without a verified receiptA digest of each call

Start with the audit hook or the plugin. Add the gate only where an unsigned tool call must not run, because an unreachable signer then stops the agent.

Verify the trail

Anyone holding a receipt id can check it on the public verify page, or with GET /api/v1/verify/{signature_id}, with no account. For an audit, asqav audit-pack export writes the evidence for a time range to one file that verifies offline against the same public key. Independent verification lists what each check proves.

What the receipts do not prove

A receipt shows that a record was signed and has not changed since. It records what the hooks observed while they ran: a hook that was not installed, failed to start or was switched off by a setting records nothing. The plugin's receipt is asserted by you as the producer, and its model attribution carries attestation_source: "claude-code-hooks", so nobody can read it as an Asqav-verified claim about which model wrote the code. Receipts support an audit; they do not make a system compliant on their own.

Questions

Can I get a tamper-evident audit log of what Claude Code did?

Yes. Add asqav hook posttool to the PostToolUse event and Claude Code sends each matched tool call to be signed with ML-DSA-65 after it runs; a call made while the signer is unreachable stays unsigned. The asqav-claude-code plugin adds one receipt per session. Changing a receipt after it is signed makes its signature check fail.

Does the audit trail send my code to Asqav?

Not by default. From asqav 0.10.12 the hooks send a SHA-256 digest of each tool call, and the plugin sends hashes of the files and commands it saw. File contents, command lines and prompts stay on your machine unless you pass --clear-context to the hooks.

Can the audit trail block Claude Code?

The audit hook and the plugin never block: when signing fails, the session carries on and the call goes unsigned. Only the opt-in asqav hook pretool gate blocks: when it cannot get a receipt it can verify before its deadline, 5 seconds by default, or when the verified receipt carries a deny or rate_limit decision.

Does it work with OpenAI Codex?

Yes. The plugin's hook script signs Codex sessions when it runs with --harness codex from Codex's config.toml. Codex hooks cover shell, apply_patch, MCP and local function tools, not hosted tools such as web search.

Can an auditor check the receipts without an Asqav account?

Yes. The verify page and the public verify endpoint need no account, and an exported audit pack verifies offline against Asqav's published key with the open verifier in the asqav SDK.

Related

References