astrid-audit 0.5.0

Chain-linked cryptographic audit logging for Astrid
Documentation

astrid-audit

License: MIT OR Apache-2.0 MSRV: 1.94

If it happened, it is in the chain. If it is not in the chain, it did not happen.

In the OS model, this is the kernel's tamper-evident event log. Every security-relevant event in the Astrid runtime gets recorded as an Ed25519-signed entry that links to the BLAKE3 hash of the entry before it. MCP tool calls, file writes, capability issuance, user approvals, sub-agent spawns. Modify a historical entry and the chain breaks. Delete one and every entry after it becomes invalid.

The audit log is not advisory. The security interceptor (astrid-approval) refuses to execute an action if the audit write fails. The chain is the ground truth.

How the chain works

Each AuditEntry contains:

  • The action, authorization proof, and outcome
  • A BLAKE3 previous_hash linking to the entry before it (genesis uses ContentHash::zero())
  • The runtime's Ed25519 PublicKey that signed this entry
  • An Ed25519 Signature over the signing data

Verification checks three invariants per session: valid genesis (first entry has zero previous hash), valid signatures (each entry's embedded public key verifies its signature), and unbroken links (each entry's previous_hash matches the preceding entry's content hash). Each failure is a typed ChainIssue.

Entries embed the signing key, so verification works across key rotations. A log started under key A and continued under key B verifies correctly because each entry carries the key that signed it.

What gets audited

26 AuditAction variants cover: MCP tool calls, capsule tool calls, MCP resource reads, MCP prompt retrieval, MCP elicitation, MCP URL elicitation, MCP sampling, file reads, file writes, file deletes, capability creation, capability revocation, approval requests, approval grants, approval denials, session start, session end, context summarization, LLM requests, server start, server stop, elicitation sent, elicitation received, security violations, sub-agent spawns, and config reloads.

Tool call arguments are stored as BLAKE3 hashes, not raw content. Proves what happened without leaking what the arguments contained.

Six AuthorizationProof variants record how each action was authorized: User, Capability, UserApproval, NotRequired, System, Denied.

Usage

[dependencies]
astrid-audit = { workspace = true }
use astrid_audit::{AuditLog, AuditAction, AuditOutcome, AuthorizationProof};
use astrid_core::SessionId;
use astrid_crypto::KeyPair;

let runtime_key = KeyPair::generate();
let log = AuditLog::in_memory(runtime_key);
let session_id = SessionId::new();

let entry_id = log.append(
    session_id.clone(),
    AuditAction::McpToolCall {
        server: "filesystem".into(),
        tool: "read_file".into(),
        args_hash: astrid_crypto::ContentHash::hash(b"..."),
    },
    AuthorizationProof::Capability {
        token_id: astrid_core::TokenId::new(),
        token_hash: astrid_crypto::ContentHash::hash(b"token data"),
    },
    AuditOutcome::success(),
)?;

let result = log.verify_chain(&session_id)?;
assert!(result.valid);

AuditLog::open(path, key) for SurrealKV persistence. AuditLog::in_memory(key) for tests.

Development

cargo test -p astrid-audit

License

Dual MIT/Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.