How I built a trust scoring hook for Claude Code

Published: (March 8, 2026 at 04:03 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Overview

I’ve been using Claude Code for a while and realized I had zero visibility into what the agent was doing across sessions— which tools it called, whether it touched files it shouldn’t have, and how many calls it made per task. To address this, I built a hook that scores every session.

The hook listens to three Claude Code events:

  • PostToolUse – records every tool call, checks it against an allowlist, and flags protected‑path access.
  • PreToolUse – blocks tool calls that touch sensitive files like .env or SSH keys.
  • Stop – computes the final trust score and logs it.

At the end of each session you get output such as:

[authe.me] Trust Score: 92 (reliability=100 | scope=75 | cost=100)
[authe.me] tools=14 violations=1 failed=0

Scoring Model

The trust score is a weighted combination of three dimensions:

DimensionWeightDescription
Reliability40 %Percentage of tool calls that succeeded.
Scope35 %Whether the agent stayed within allowed tools and paths. Each violation drops the score by 25 points.
Cost25 %Number of tool calls made. Under 20 is fine; over 100 starts flagging.

Implementation Details

Hash‑chaining of tool calls

Every tool‑call event is hashed together with the previous hash, creating a simple chain. If the log is tampered with, the chain breaks— a lightweight blockchain‑style audit trail.

import hashlib, json

def compute_hash(prev_hash, data):
    payload = f"{prev_hash}:{json.dumps(data, sort_keys=True)}"
    return hashlib.sha256(payload.encode()).hexdigest()[:16]

PreToolUse denial example

The PreToolUse hook checks if Claude is about to read or edit a sensitive file. If it matches a protected path, the hook returns a denial decision with a reason that Claude receives as feedback:

result = {
    "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "authe.me policy: .env is a protected path."
    }
}

Claude sees the denial reason and adjusts its plan accordingly.

Installation

mkdir -p ~/.claude/hooks ~/.authe
curl -fsSL https://raw.githubusercontent.com/autheme/claude-code-hook/main/authe-hook.py \
    -o ~/.claude/hooks/authe-hook.py
chmod +x ~/.claude/hooks/authe-hook.py

Hook configuration

Add the following to ~/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "AUTHE_HOOK_EVENT=PostToolUse python3 ~/.claude/hooks/authe-hook.py"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "AUTHE_HOOK_EVENT=Stop python3 ~/.claude/hooks/authe-hook.py"
          }
        ]
      }
    ]
  }
}

The hook is a single file, has zero external dependencies, and is pure Python.

Future Work

  • Remote reporting – aggregate scores across sessions and agents.
  • OpenClaw plugin – provide equivalent functionality for the OpenClaw ecosystem.

The repository is available at:

Built with Claude. I’d love feedback from anyone running Claude Code in production.

0 views
Back to Blog

Related posts

Read more »