hdk-audit — Hedera Detection Key

A Python library, not an ML model (no weights).

Model card summary

What this repository is A Python library for tamper-evident audit trails of language-model calls. Not an ML model; no weights.
Developed by Jakub Ćwirlej
Origin Spun off from the author's AI newsroom editor
Requirements Python 3.10+; no GPU; optional anchoring on Hedera Consensus Service
Status Alpha. It ran for a while in a demo application, anchoring its records on Hedera; the application did not go to production, so HDK is not used in production today
Intended use Keeping a verifiable record of prompts, answers and other events of an LLM-based application (e.g. for the record-keeping duties of the EU AI Act)
Out of scope Commercial use (licence); it does not filter, block or change prompts or answers; it is not a security product on its own
Licence CC BY-NC 4.0 from version 0.1.3 (versions 0.1.0–0.1.2 remain under Apache-2.0)

In plain words

When an application uses a language model, its logs of what was asked and answered sit in a database the operator controls: they can be edited or deleted and nobody outside can tell. HDK records each event together with a cryptographic fingerprint that also covers the events before it, so editing, deleting or inserting an entry later shows up when the record is verified. Optionally, it publishes a fingerprint of the whole record to the public Hedera ledger, so even the operator cannot rewrite history without it being noticed. Only hashes leave your system, never the content.

Status

Alpha. HDK was used for a while in a demo application, anchoring its records on Hedera; that application did not go to production, so HDK is not used in production today. An offline reference implementation, not an audited security product.

What it does

The distribution is hdk-audit; the import name is hdk. The package called hdk on PyPI is a different, unrelated project (by Zama), so do not pip install hdk for this.

  • Each event becomes a node in a hash DAG stored in SQLite: self_hash = H(content hash ‖ parent node hashes) and node_hash = H(canonical JSON of all stored fields). verify() recomputes both, checks that every parent exists and recomputes each anchor's Merkle root (RFC 6962: leaf = H(0x00 ‖ data), node = H(0x01 ‖ left ‖ right)). Editing any field of a node is reported.
  • Canary: a counter and a commitment SHA-256(hex(secret) ‖ counter) advance with every node. The secret is kept outside the database: canary_key_file (created with mode 0600 if missing), HDK_CANARY_KEY (64 hex chars) or HDK_CANARY_KEY_FILE. Without a key the canary is disabled and verify() says so. With a key, an edited or reset counter, or a rollback below the last anchored checkpoint, is reported.
  • Keyed node hashes (since 0.1.2): with a canary key, every node_hash is an HMAC-SHA256 under a key derived from it, so a node cannot be rewritten and re-hashed without the key. The canary state also records how many nodes it wove and the last generation, sealed by a MAC: deleting the newest event (before it is anchored), deleting any other event or inserting one is reported.
  • Hedera anchoring (optional): await hdk.anchor_now() records an anchor — the Merkle root of the un-anchored nodes, a canary checkpoint and a link to the previous anchor — and submits it to the Hedera Consensus Service when credentials and the hedera extra are present; otherwise it stays local and anchor.status says why.
  • Provider wrappers: hdk.complete(provider="openai" | "anthropic" | "google", model=..., messages=..., api_key=...) makes the call and records prompt and response as two linked nodes.
  • CLI hdk-audit: verify, anchor, stats, tree, inspect, export, certificate, version.

Requirements

Python 3.10+; httpx. Optional extras: hedera (hiero-sdk-python>=0.3,<1), openai, anthropic, google (google-generativeai). No GPU.

Install

git clone https://huggingface.co/JakCwi/hdk-audit
pip install ./hdk-audit                  # core: hash DAG, canary, CLI
pip install "./hdk-audit[hedera]"        # + Hedera anchoring
pip install "./hdk-audit[openai]"        # + a provider wrapper (also: anthropic, google)

The same package is downloadable as hdk-audit-0.1.3.zip from https://jakubcwirlej.com (unzip, then pip install ./hdk-audit-0.1.3).

Quick start

from hdk import HDK, HDKConfig
from hdk.models import EventType

hdk = HDK(HDKConfig(db_path="audit.sqlite", canary_key_file="canary.key"))   # keep the key away from the DB
hdk.record_event(EventType.PASTE_EXTERNAL, "pasted text", session_id="doc-1")
hdk.record_event(EventType.PUBLISH, "final text", session_id="doc-1")
report = hdk.verify()
print(report["dag_valid"], report["canary_valid"], report["anchor_mode"])
hdk.close()
hdk-audit verify --db audit.sqlite --canary-key-file canary.key
hdk-audit anchor --now --db audit.sqlite   # Hedera via HDK_HEDERA_OPERATOR_ID, _OPERATOR_KEY, _TOPIC_ID

Parameters and how to choose them

setting default how to choose
HDKConfig.db_path ~/.hdk/dag.sqlite one database per application or audited workspace; ":memory:" for tests.
HDKConfig.canary_key_file none (then HDK_CANARY_KEY / HDK_CANARY_KEY_FILE) set it: without a key, un-anchored entries are protected by anchoring only. Store it on a different machine/volume than the database; back it up — losing it means keyed nodes can no longer be checked.
HDKConfig.canary_enabled True leave on.
HederaConfig.network testnet testnet for trials, mainnet for real anchors (HDK_HEDERA_NETWORK).
operator_id, operator_key, topic_id none all three are needed for submission (HDK_HEDERA_OPERATOR_ID, _OPERATOR_KEY, _TOPIC_ID); creating the topic is up to you.
how often to anchor — anchoring is explicit: call anchor_now() (or the CLI) on your own schedule. More often = shorter window in which un-anchored entries can be rewritten by someone holding the key; each submission costs a small HCS fee.

Pitfalls and limitations

  • Without a canary key, entries not yet anchored are protected by anchoring only: anyone who can write the database can change such an entry and recompute its hashes, and verify() cannot tell (its unanchored_protection field says so). Use a key, or anchor often.
  • Someone who can rewrite the whole database and holds the canary key can rebuild a consistent history; so can someone who restores a complete older copy of the database (until it is compared with an anchor). Only anchors confirmed on Hedera are out of their reach.
  • The canary works at the application level: copying the database file at the OS level leaves no trace (it cannot forge history, though).
  • HDKConfig.auto_anchor and HederaConfig.anchor_interval exist but are not used to trigger anchoring in this version: anchor explicitly.
  • Databases from 0.1.0 do not verify under 0.1.1+ (hashes are computed differently); 0.1.0 also kept the canary secret in plain text in the database, later versions never read it.
  • A 0.1.1 database opened by 0.1.2+ with its key is adopted once as it is; every node written afterwards is keyed. rotate_secret() keys later nodes under the new key; earlier nodes are then checked by anchoring only (verify()["keyed_unchecked"] counts them). Opened without the key, keyed nodes are counted there too instead of being reported as altered.
  • The google wrapper uses the google-generativeai SDK (configured globally per call).
  • Guarantees are computational (SHA-256, HMAC, external anchoring), not absolute. Alpha software, not audited.

Changes in 0.1.3

  • Licence: CC BY-NC 4.0 (versions up to 0.1.2 were released under Apache-2.0 and remain under it).
  • Certificates: the hashscan.io link follows the configured Hedera network (it was always testnet), and a local-only anchor no longer gets a link.

How to test

pip install pytest pytest-asyncio
pytest            # never touches a network (the Hedera client is mocked)

How it works in detail: TECHNICAL.md.

Licence

Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0), from version 0.1.3 — free for non-commercial use with attribution. Full text in LICENSE. Earlier versions (0.1.0–0.1.2) were released under Apache-2.0 and remain available under it.

Citation and contact

Jakub Ćwirlej, HDK (Hedera Detection Key): tamper-evident audit trails for LLM calls, 2026, https://huggingface.co/JakCwi/hdk-audit; technical note: https://www.researchgate.net/publication/400762996. Contact: https://jakubcwirlej.com.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support