Provenance + Audit
Every Hub record carries a SHA-256 provenance hash over its canonical JSON
form. The control plane co-signs the hash so the data plane and control
plane bind cryptographically without leaking content. The
governance_event chain reconstructs “what did the AI do for whom on what
date?” — for 7 years back.
Why provenance matters
Section titled “Why provenance matters”Three audit questions Claresia must answer end-to-end:
- “Did this Hub record really originate from a Claresia skill invocation?” → Provenance hash + co-sign verification.
- “Has this Hub record been tampered with?” → Re-compute provenance hash on read; compare to stored hash + co-sign.
- “Who took the privileged action that changed this RBAC / config / skill?”
→
governance_eventchain, ordered by monotoniccreated_at.
SHA-256 canonical-JSON algorithm
Section titled “SHA-256 canonical-JSON algorithm”The exact procedure (mirrored byte-for-byte in Python and TypeScript):
- Take the record dict
- Exclude
provenance_hash,provenance_cosign - Convert to canonical JSON:
- Sort keys lexicographically (UTF-8 byte order)
- No whitespace
- Whole-valued floats normalized to ints (
1.0→1) nullfor missing optional fields- UTF-8 encoding
- SHA-256 the bytes
- Base64-encode the digest (URL-safe, no padding)
# Python reference (claresia_hub/canonical.py)def compute_provenance_hash(record: dict) -> str: payload = {k: _normalize(v) for k, v in record.items() if k not in ("provenance_hash", "provenance_cosign")} canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")) digest = hashlib.sha256(canonical.encode("utf-8")).digest() return base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")// TypeScript reference (claresia-hub/canonical.ts)export function computeProvenanceHash(record: Record<string, unknown>): string { const { provenance_hash: _h, provenance_cosign: _c, ...rest } = record; const normalized = normalize(rest); const canonical = canonicalStringify(normalized); const digest = createHash("sha256").update(canonical, "utf8").digest(); return digest .toString("base64") .replace(/=+$/, "") .replace(/\+/g, "-") .replace(/\//g, "_");}Cross-language fixtures (cross_language_fixtures.json) verify byte-equality
in CI on every commit.
Co-signing
Section titled “Co-signing”After the data plane writes a record + provenance hash, the control plane co-signs the hash with its per-tenant signing key (Ed25519). The co-sign binds the data plane to the control plane:
- Data plane cannot forge a record (no control-plane signing key)
- Control plane cannot forge a record (no data-plane content)
- Tampering with either side breaks verification
The signing key is stored in HSM (AWS KMS / Azure Key Vault HSM tier).
Verification on read
Section titled “Verification on read”When you read a record via the Hub API with ?verify=true:
- Hub server fetches the record
- Computes the provenance hash over the canonical JSON
- Compares to the stored
provenance_hash - Verifies the
provenance_cosignagainst the control-plane signing key - Returns the record + a
verification_statusfield (verified,tampered,cosign_invalid)
If verification_status != "verified", a governance_event of kind
hub.tamper_detected is automatically emitted.
The governance_event audit chain
Section titled “The governance_event audit chain”Every privileged action emits a governance_event Hub record. Together they
form the canonical audit log. Common kinds:
| Kind | Triggered by |
|---|---|
auth.login, auth.failed | WorkOS callback |
rbac.role_assigned, rbac.archetype_overridden | IT admin action in Command Center |
connector.credential_rotated | IT admin rotates LLM API key |
skill.entitlement_changed | IT admin toggles a skill |
cmek.key_rotated, cmek.key_revoked | Customer rotates CMEK |
hub.record_purged | Retention policy execution |
hub.tamper_detected | Verification failure on read |
tenant.config_changed | Any tenant settings change |
Each event includes:
- Actor (user_id, source IP, session_id)
- Subject (resource_id, resource_type)
- Outcome (success / failure + error code)
- Payload (full diff of the change)
- Timestamp (microsecond precision)
- Co-signed provenance hash
Reconstructing “what happened” for an auditor
Section titled “Reconstructing “what happened” for an auditor”Typical audit query (using the Hub API):
# All actions on tenant=dainese, last 30 days, by user=it.admincurl -X GET 'https://api.claresia.com/hub/v1/records' \ -H 'Authorization: Bearer $JWT' \ -G \ --data-urlencode 'tenant_id=dainese' \ --data-urlencode 'record_type=governance_event' \ --data-urlencode 'created_by=user:it.admin@dainese.it' \ --data-urlencode 'created_at__gte=2026-04-01T00:00:00Z' \ --data-urlencode 'verify=true'Returns a paginated list, every record verified, every record co-signed. Export as CSV or JSON-Lines for your auditor.
Retention
Section titled “Retention”The governance_event table has a fixed 7-year retention (regulatory
requirement for SOC 2, GDPR Article 30, NIS2). Records cannot be deleted
before then via the Hub API. The only way to delete governance_event records
before 7 years is a legal hold release signed off by Claresia’s CTO + the
customer’s compliance officer.
Other record types follow per-table retention defaults (overridable per tenant). See Retention.
Schrems II + GDPR posture
Section titled “Schrems II + GDPR posture”- Right to erasure (GDPR Art. 17) is honored: end-user records can be
purged on request, except for
governance_eventrecords which fall under the legal-obligation lawful basis (Art. 17(3)(b)). - Data Processing Agreement ships with Schrems II Standard Contractual Clauses + technical supplementary measures. See DPA template.
- Sub-processors disclosed at trust.claresia.com.
Verification CLI (for customer DBAs)
Section titled “Verification CLI (for customer DBAs)”In Mode C, customers can verify the Hub themselves:
# Install the verifier (open source)pip install claresia-hub-verifier
# Verify all records in a tenantclaresia-hub-verifier verify \ --backend postgres \ --dsn 'postgres://...' \ --tenant dainese \ --cosign-public-key ~/.claresia/cosign-pub.pem
# Sample output:# 1,234,567 records verified# 0 tampered# 0 cosign invalid# Verification took 3m 41sRun this as part of your regular compliance evidence-gathering — the output is audit-grade and machine-readable (JSON-Lines).