Crypto Integrity
Attestation, Merkle verification, hash-chained audit logs.
Cryptographic Integrity — Attestation, Keys, Audit
Every inference is now signed, chained, and auditable.
Model Manifests and Key Management
Built yule-verify with two core pieces:
Model manifests — JSON format describing a model's identity: name, architecture, publisher, merkle root, per-tensor hashes. Supports Ed25519 signatures (with ML-DSA hybrid placeholder for post-quantum readiness). The manifest's signable_bytes() function sets the signature field to Unsigned before hashing, so the signature doesn't sign itself.
Key store at ~/.yule/keys/:
device_key()auto-generates an Ed25519 signing key on first run, persists the 32-byte secret. Also saves the public key asdevice.pubfor easy export.trust_publisher()/publisher_key()/list_publishers()— trusted publisher registry. Save a publisher's Ed25519 public key as{name}.pub.
Key generation uses getrandom (OS CSPRNG) directly. I tried using ed25519-dalek's SigningKey::generate() but that pulls in the entire rand dependency chain. Instead: 32 random bytes from the OS → SigningKey::from_bytes(). Same cryptographic quality, zero extra dependencies.
Signed Inference Records
Built yule-attest. Each inference produces a signed record containing:
session_id— unique per inference (nanosecond timestamp, hex-encoded)model— name, merkle root, publisher, whether signature was verifiedsandbox— platform, active flag, memory limitinference— tokens generated, blake3(prompt), blake3(output), temperature, top_psignature— Ed25519 over all the aboveprev_hash— blake3 of the previous record (chain integrity)
Prompt and output are hashed, not stored in plaintext. You can prove an inference happened without exposing what was said.
Audit Log
Append-only JSON-lines file at ~/.yule/audit.jsonl.
append()— write a signed recordlast_hash()— get chain tip for linking the next recordverify_chain()— walk entire log, verify each record's prev_hash matches blake3 of the previous recordquery_last(n)— read last N records
Why JSON-lines instead of a database? One file, append-only, human-readable, trivially parseable. No SQLite dependency. The hash chain provides integrity guarantees a database wouldn't — you can't silently delete a record without breaking the chain.
API Integration
The /yule/chat endpoint now creates an attestation after each inference, signs it with the device key, appends to the audit log, and returns attestation_id + device_pubkey in the response's integrity block. Both streaming and non-streaming responses include attestation.
Attestation is non-blocking — if the audit log fails (permissions, disk full), inference still succeeds with a warning. The response just won't have an attestation_id. Attestation should never slow down or prevent inference.
CLI: yule audit
yule audit --last 10 # show last 10 records
yule audit --verify-chain # verify entire hash chainDesign Choices
blake3 for hashing — already using it for merkle trees, 2-3x faster than SHA-256 on x86, one dependency covers everything.
Ed25519 for signatures — 64-byte sigs, fast verify, broad support. There's a MlDsa variant in the manifest format for post-quantum hybrid, but Ed25519 is the pragmatic choice today.
Prompts/outputs are hashed, not stored — the audit log proves that an inference happened with specific parameters, without leaking what was said.