Audit Chain and Signed Reports
The Ed25519-signed audit chain, how every outbound batch gets signed, signature verification, the prove-benchmark.sh attestation system, hardware fingerprinting, and generating compliance reports.
Published Jun 2, 2026
Tamper-Evident Logging at the Edge
Compliance is not a document you write once and file away. It is a continuous property of your data pipeline that must be demonstrable under audit. When a supervisory authority asks, "How do we know this temperature reading was not modified after collection?" or "Prove that no patient identifiers left the device on Tuesday," you need more than a verbal assurance. You need a cryptographically verifiable chain of evidence.
The Pyvorin Edge Runtime provides this through two complementary mechanisms: the
PrivacyAudit hash chain in pyv_edge_agent/privacy_firewall/audit.py,
which creates a Merkle-tree-like sequence of SHA-256-linked records for every privacy-related
action; and the Ed25519 bundle-signing system in
edge_sdk/pyvorin_edge/packaging/signer.py, which cryptographically signs benchmark
and attestation reports so that third parties can verify their integrity. Together they give you
non-repudiation, forward integrity, and hardware-bound provenance.
The PrivacyAudit Hash Chain
The PrivacyAudit class stores every privacy-relevant event in a dedicated SQLite
table named privacy_audit. Each record contains:
id— Auto-incrementing primary key.timestamp— Unix epoch seconds with fractional precision.action— A short string such as"policy_reloaded","field_redacted", or"batch_signed".details— JSON blob with structured context.prev_hash— Therecord_hashof the previous record in the chain.record_hash— SHA-256 of the canonical JSON of{action, details, timestamp, prev_hash}.
By linking each record to its predecessor, the chain becomes tamper-evident. If an attacker
gains root access to the device and attempts to delete or modify an old audit record, the
record_hash of the next record will no longer match the recomputed value. The
verify_chain() method detects this instantly.
from pyv_edge_agent.privacy_firewall.audit import PrivacyAudit
audit = PrivacyAudit(db_path="edge_store.db")
# Log a policy change
record_id = audit.log_action(
action="policy_reloaded",
details={
"purpose": "GDPR Article 30 record of processing",
"data_subjects": ["patients", "visitors"],
"data_categories": ["health_telemetry", "location"],
"recipients": ["cloud_analytics", "backup_storage"],
"retention_period": "90_days",
"security_measures": ["sha256_hash_chain", "ed25519_signing"],
},
)
print(f"Logged record {record_id}")
# Verify integrity
if audit.verify_chain():
print("Audit chain is intact.")
else:
print("AUDIT CHAIN TAMPERING DETECTED!")
How the Chain Is Constructed
When PrivacyAudit is initialised, it loads the most recent
record_hash from the database. If the table is empty, it seeds the chain with
"0" * 64 — a string of sixty-four zeros that acts as the genesis hash. Every
subsequent call to log_action() performs the following steps atomically under a
threading.Lock:
- Captures the current time via
time.time(). - Retrieves
prev_hashfrom the in-memory_last_hashattribute. - Serialises
{action, details, timestamp, prev_hash}to canonical JSON (sorted keys, no whitespace separators) and computes its SHA-256 digest. This is therecord_hash. - Inserts the record into SQLite.
- Updates
_last_hashto the newrecord_hashso that the next record links to it.
Because the lock is held across the entire sequence, concurrent threads cannot insert records that fork the chain. The result is a strict linear history — exactly what auditors expect.
Ed25519-Signed Outbound Batches
The hash chain protects local history, but what about data that leaves the device? The Edge SDK
includes a full Ed25519 signing and verification stack in
edge_sdk/pyvorin_edge/packaging/signer.py. Ed25519 was chosen because it provides
fast single-signature verification (≈ 50 µs on ARM64), small 64-byte signatures, and 32-byte
public keys — ideal for constrained IoT payloads.
from pyvorin_edge.packaging.signer import BundleSigner
# Generate a one-time key pair for a benchmark run
private_key, public_key = BundleSigner.generate_keypair()
# Sign a directory of evidence files
BundleSigner.sign_bundle(
bundle_dir="./evidence/batch_2024_05_30",
private_key=private_key,
output_manifest="./evidence/batch_2024_05_30/manifest.json",
)
# Verify on the receiver side
is_valid = BundleSigner.verify_bundle(
bundle_dir="./evidence/batch_2024_05_30",
manifest_path="./evidence/batch_2024_05_30/manifest.json",
public_key=public_key,
)
print("Bundle valid:", is_valid)
The signed manifest contains a SHA-256 hash of every file in the bundle, plus an Ed25519 signature over the canonical JSON of the manifest. An attacker who modifies a single byte in any file will change its SHA-256 hash, which will mismatch the manifest, which will fail signature verification. The defence is end-to-end: from the sensor reading on the device to the evidence package in your compliance archive.
The prove-benchmark.sh Attestation System
The prove-benchmark.sh script (located in pifiles/) orchestrates a
complete attestation workflow on a Raspberry Pi 5 or any Linux gateway. It performs six steps:
- Generate an Ed25519 key pair. The private key is held in memory only
(base64-encoded); the public key is written to
proof/public_key.b64. - Check asciinema. Optionally sets up screen recording for video proof.
- Hardware sanity check. Collects CPU temperature, serial number, model,
and throttling status via the
attestationmodule. - Run attested benchmarks. Executes all vertical benchmarks with telemetry collection and signs the resulting JSON with the private key.
- Generate a markdown report. Produces a human-readable summary with hardware details, benchmark results, and embedded verification instructions.
- Output recording instructions. Creates
RECORDING.txtwith guidance for asciinema orscriptsessions.
# On the Pi 5
cd ~/pyvorin-edge
bash pifiles/prove-benchmark.sh
# Outputs:
# proof/benchmark_attested.json — signed, attested results
# proof/benchmark_report.md — human-readable summary
# proof/public_key.b64 — key to verify signature
# proof/RECORDING.txt — recording instructions
Verifying Signatures Off-Device
A compliance auditor or customer can verify the attestation package without trusting the device that generated it. The only trusted artefact is the public key, which can be distributed out of band (e.g., pinned on your website, embedded in a PDF, or loaded from a hardware TPM).
import json
import base64
from pathlib import Path
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
# Load the attestation package
pkg = json.loads(Path("proof/benchmark_attested.json").read_text())
pub = Ed25519PublicKey.from_public_bytes(base64.b64decode(pkg["public_key_b64"]))
# Reconstruct the signed payload (everything except signature and public_key_b64)
payload = {k: v for k, v in pkg.items() if k not in ("signature", "public_key_b64")}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
# Verify
pub.verify(base64.b64decode(pkg["signature"]), canonical)
print("SIGNATURE VALID — this report was signed by the private key corresponding to the public key.")
Hardware Fingerprinting
A signature proves that a specific private key signed a message. It does not prove which
physical device held that key. To close that gap, the attestation system collects a
hardware fingerprint via edge_sdk/pyvorin_edge/attestation.py. The
collect_hardware_info() function gathers:
platform,machine,processor— fromplatformmodule.cpu_count— logical cores.cpu_model— parsed from/proc/cpuinfo.serial_number— from/proc/device-tree/serial-numberon Raspberry Pi, or the CPU serial fallback.hardware_revision— device tree model or CPU model name.cpu_temp_c— fromvcgencmd measure_tempor thermal zones.cpu_throttled— throttling flags fromvcgencmd get_throttled.uptime_seconds— from/proc/uptime.total_memory_kb,available_memory_kb— from/proc/meminfo.
These values are embedded in the attestation report and covered by the integrity hash. If an attacker moves the private key to a different device, the hardware fingerprint will change, breaking the attestation chain. This binds the cryptographic proof to the physical world.
from pyvorin_edge.attestation import collect_hardware_info
hw = collect_hardware_info()
print(f"Device: {hw.hardware_revision}")
print(f"Serial: {hw.serial_number[:24]}...")
print(f"CPU temp: {hw.cpu_temp_c:.1f}°C")
print(f"Throttled: {hw.cpu_throttled or 'No'}")
Generating Compliance Reports
The PrivacyAudit class includes a dedicated export_for_gdpr() method
that transforms the raw audit chain into the structured format recommended by GDPR Article 30
(Records of Processing Activities). Each record is enriched with fields such as
processing_activity, purpose, data_subjects,
data_categories, recipients, retention_period, and
security_measures.
from pyv_edge_agent.privacy_firewall.audit import PrivacyAudit
import json
audit = PrivacyAudit("edge_store.db")
gdpr_records = audit.export_for_gdpr()
with open("gdpr_article_30_records.json", "w") as f:
json.dump(gdpr_records, f, indent=2)
# Each record looks like:
# {
# "record_id": 42,
# "processing_activity": "policy_reloaded",
# "timestamp_iso": "2024-05-30T09:15:00Z",
# "purpose": "GDPR Article 30 record of processing",
# "data_subjects": ["patients", "visitors"],
# "data_categories": ["health_telemetry", "location"],
# "recipients": ["cloud_analytics", "backup_storage"],
# "retention_period": "90_days",
# "security_measures": ["sha256_hash_chain", "ed25519_signing"]
# }
For HIPAA, you can filter the same export to include only actions whose
data_categories contain "phi" or "health_telemetry". For
NIS2, append a section that maps each security_measure to the corresponding NIS2
Article 21 technical measure (e.g., encryption, access control, incident response).
Integrity Hash: The Top-Level Seal
The attestation report contains one final line of defence: an integrity_hash that
covers the entire payload (hardware, runtime, and results). This hash is computed by
create_attestation_report() in attestation.py:
from pyvorin_edge.attestation import create_attestation_report
results = {
"vertical": "healthcare_vitals",
"total_readings": 1_200_000,
"events_triggered": 340,
"reduction_percent": 94.5,
"latency_p50_ms": 0.0032,
}
report = create_attestation_report(results)
print("Integrity hash:", report["integrity_hash"])
Verifying the integrity hash is straightforward: strip the integrity_hash key
from the report, canonicalise the remainder, and recompute SHA-256. If the digests match, you
have cryptographic assurance that no field — not even a single digit of the CPU temperature —
was altered after the report was generated.
Operational Best Practices
- Run
verify_chain()daily. Schedule a cron job that loads the audit database and asserts chain integrity. If it fails, trigger an alert immediately; the device may be compromised. - Backup audit databases off-device. The SQLite file is small (a few megabytes per month on typical workloads). rsync it nightly to a tamper-evident object-store bucket with object-lock enabled.
- Rotate signing keys per quarter. The
prove-benchmark.shscript generates ephemeral keys, but for long-lived attestation pipelines, establish a key-rotation calendar. Store old public keys in a public transparency log so that historical reports remain verifiable. - Never log secrets in
details. The audit chain is integrity- protected, not encrypted. If you need to record that a hash key was rotated, log the event name and timestamp, not the key itself.
Summary
The Pyvorin Edge Runtime combines a local SHA-256 hash chain with Ed25519 digital signatures to
provide end-to-end tamper evidence. The PrivacyAudit class gives you GDPR-ready
records of processing activities with sub-millisecond insertion latency. The
BundleSigner and prove-benchmark.sh system let you generate hardware-
attested, cryptographically signed benchmark reports that customers and regulators can verify
independently. By binding signatures to hardware fingerprints and covering the entire report
with a top-level integrity hash, Pyvorin Edge turns compliance from a paperwork exercise into a
mathematical guarantee.