Skip to content

Feature: Cryptographic Audit Trail — SHA-256 Hash-Chained Action Log for Tamper-Proof Agent Accountability (inspired by OpenFang) #487

@teknium1

Description

@teknium1

Overview

OpenFang (RightNow-AI/openfang), a Rust-based Agent Operating System, implements a Merkle Hash-Chain Audit Trail (audit.rs) that creates a cryptographically linked, tamper-evident log of every agent action. Each entry is chained to the previous via SHA-256, making it impossible to modify or delete historical actions without breaking the chain. This provides enterprise-grade auditability for autonomous agent operations.

Hermes Agent currently logs conversations comprehensively via SQLite (hermes_state.py — sessions + messages tables) and saves session trajectories as JSON files. However, this logging is conversation-focused (what was said) rather than action-focused (what was done). There's no structured, security-grade record of "agent performed action X on resource Y at time T with result Z" — actions are embedded in tool call/response messages but not indexed or chain-linked. The existing append_audit_log in skills_hub.py only covers skill install/uninstall events.

A cryptographic audit trail would enable: compliance reporting, forensic analysis after incidents, trust verification for autonomous operations, and accountability for agents running on schedules via cron.


Research Findings

How OpenFang's Audit Trail Works

OpenFang's implementation (openfang-runtime/src/audit.rs) uses:

  1. AuditEntry Structure:

    • sequence: Monotonically increasing sequence number
    • timestamp: ISO 8601 UTC timestamp
    • agent_id: Which agent performed the action
    • action_type: Enum (ToolCall, ToolResult, FileWrite, FileRead, NetworkRequest, ShellExec, SecretAccess, ConfigChange)
    • resource: What was acted upon (file path, URL, tool name)
    • details: Serialized action parameters and results
    • prev_hash: SHA-256 hash of the previous entry (genesis entry uses zeros)
    • hash: SHA-256 of (sequence + timestamp + agent_id + action_type + resource + details + prev_hash)
  2. Chain Verification: verify_chain() walks the entire log and recomputes each hash from its components + the previous hash. Any tampering (insertion, deletion, modification) breaks the chain.

  3. Storage: Append-only file (one JSON entry per line) + optional SQLite index for querying.

  4. Query API: Filter by agent_id, action_type, time range, resource pattern.

Key Design Decisions

  • Append-only storage: Entries are never modified or deleted, only appended
  • Hash chaining: Each entry's hash depends on the previous, creating a tamper-evident sequence
  • Action-level granularity: Logs individual actions (tool calls, file writes, network requests), not just conversations
  • Separate from conversation history: The audit trail is a security/compliance artifact, not a conversation replay

Current State in Hermes Agent

What we have:

  • hermes_state.py: SQLite database with sessions and messages tables. Full conversation history with tool calls embedded as message content. FTS5 search. No hash chaining or tamper detection.
  • agent/trajectory.py: JSON session logs in ShareGPT format for RL training. No security properties.
  • gateway/hooks.py: Event hook system with lifecycle events (session:start, agent:step, agent:end, etc.). Python handlers in ~/.hermes/hooks/. This is the ideal integration point for an audit trail — hooks already fire on the right events.
  • tools/skills_hub.py: append_audit_log() for skill install/uninstall only. Simple text log, no chaining.

What's missing:

  • No structured action log (tool call → result as a first-class record)
  • No cryptographic chaining or tamper detection
  • No action-type classification (file operations vs. network vs. shell vs. secrets)
  • No query/filter API for "show me all file writes by this agent in the last hour"
  • No compliance-friendly export format

Relevant existing issues:


Implementation Plan

Classification: Core Codebase Change

This should be a core codebase change, not a skill or tool. Reasons:

  • Requires deterministic processing logic for hash computation and chain verification
  • Must intercept every tool call/result at the harness level
  • Needs custom persistence (append-only log with integrity guarantees)
  • The audit trail itself could be exposed as a read-only tool for the agent to query its own history

What We'd Need

  • New module: agent/audit_trail.py — AuditEntry dataclass, hash chain logic, storage, query API
  • Hook integration: Register audit handlers via gateway/hooks.py event system
  • Agent loop integration: Emit audit events on tool call dispatch and result receipt in run_agent.py
  • Optional read-only tool: audit_query for the agent to search its own action history
  • CLI command: hermes audit for viewing/verifying/exporting the trail

Phased Rollout

Phase 1: Core Audit Engine

  • AuditEntry dataclass with fields: sequence, timestamp, session_id, action_type, tool_name, resource, args_summary, result_summary, prev_hash, hash
  • SHA-256 hash chaining with compute_entry_hash()
  • Append-only JSONL storage at ~/.hermes/audit/trail.jsonl
  • verify_chain() function to validate integrity
  • Action types: TOOL_CALL, TOOL_RESULT, FILE_WRITE, FILE_READ, SHELL_EXEC, WEB_REQUEST
  • Integration: emit audit entries in the tool dispatch section of run_agent.py (around line 2933+)
  • CLI: hermes audit verify (check chain integrity), hermes audit tail (view recent entries)

Phase 2: Query & Filter

  • SQLite index alongside JSONL for efficient querying
  • Filter by: session_id, action_type, tool_name, time_range, resource_pattern
  • CLI: hermes audit search --type SHELL_EXEC --after "2h ago"
  • Optional: expose as read-only audit_query tool so the agent can review its own actions
  • Retention policy: configurable max age / max entries with archival

Phase 3: Compliance & Export


Pros & Cons

Pros

  • Accountability: Know exactly what the agent did, when, and to what — essential for autonomous/cron operations
  • Tamper evidence: Hash chaining means any modification is detectable via verify_chain()
  • Incident forensics: After something goes wrong, query the trail to reconstruct the exact sequence of actions
  • Compliance: Enterprise users need audit trails for SOC 2, ISO 27001, etc.
  • Low overhead: SHA-256 + JSONL append is fast; the main cost is serializing action summaries
  • Natural integration point: gateway/hooks.py already fires events on the right lifecycle moments

Cons / Risks

  • Storage growth: Every tool call generates an entry. High-activity agents could generate large logs. Mitigated by retention policies and JSONL compression.
  • Performance: Hash computation on every tool call adds latency. SHA-256 is fast (~500ns per hash) so this is negligible.
  • Sensitive data in logs: Tool call args may contain secrets, file contents, or PII. Need to integrate with redaction (Security: File Tool Output Redaction Gap — Secrets Exposed via read_file but Masked via Terminal #363) to sanitize entries.
  • Complexity: Another system to maintain, test, and document. Mitigated by keeping Phase 1 simple (~200 lines).

Open Questions

  • Should the audit trail be enabled by default or opt-in? Default-on with configurable verbosity levels seems right for an agent that runs autonomously.
  • What level of detail should be logged? Full tool args + results (comprehensive but large) vs. summaries (compact but less useful for forensics)?
  • Should the audit trail be per-session or global? Global with session_id as a field seems most useful.
  • Should we redact secrets in audit entries or store them (encrypted) for forensic completeness?
  • Is JSONL sufficient for Phase 1, or should we go straight to SQLite? JSONL is simpler and naturally append-only.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions