EVE Core/ Docs/ Decision Certificates
Reference

Decision Certificates

Every governance decision CoreGuard makes is a signed, verifiable record — not just a log entry. A Decision Certificate is a cryptographically bound document that proves exactly what was decided, by which policy version, at what time, and why. This page explains what a certificate contains, how it is signed, how to verify it independently, and how long to retain it for regulatory purposes.

What Is a Decision Certificate?

When you call POST /v1/decisions/evaluate, CoreGuard does two things: it evaluates the action against the requested policy pack, and it generates a Decision Certificate that permanently records that evaluation. The certificate is issued regardless of whether the decision is ALLOWED, BLOCKED, or MODIFIED — every governance decision has a certificate.

The certificate's purpose is regulatory proof. If your organization is audited by a financial regulator, a healthcare compliance officer, or a state attorney general, the certificate is the evidence that your AI system applied governance controls before the output reached an end user. The HMAC-SHA256 signature on every certificate makes it tamper-evident — any post-hoc modification of the decision record invalidates the signature, providing mathematical proof of record integrity.

Decision Certificates differ from conventional audit logs in three critical ways:

HMAC-SHA256 Signing Process

CoreGuard uses HMAC-SHA256 to sign each Decision Certificate. HMAC (Hash-based Message Authentication Code) with SHA-256 provides strong integrity guarantees and is widely accepted as a compliance-grade signing mechanism in financial services, healthcare, and government contexts.

The signing process follows these steps:

1
Assemble the canonical payload

The audit_record object is serialized using RFC 8785 JSON Canonicalization Scheme (JCS): all keys are sorted lexicographically, all whitespace is removed, and Unicode characters are normalized to NFC form. This ensures that the same logical record always produces the same byte sequence regardless of how it was constructed or transmitted.

2
Compute the HMAC

HMAC-SHA256 is computed over the canonical payload bytes using the organization's signing key. The signing key is a 256-bit symmetric key provisioned at onboarding and stored in an HSM-backed key management system. The key never appears in cleartext in any CoreGuard component or API response.

3
Encode and attach

The HMAC output is hex-encoded and stored in the hmac_signature field of the audit_record object. The key_id field records which key version was used, supporting key rotation without invalidating historical certificates.

4
Chain to previous certificate

Each certificate's prev_hash field contains the SHA-256 hash of the immediately preceding certificate in the organization's audit chain. This creates a hash-chained audit log — any deletion or reordering of certificates breaks the chain and is detectable.

Key rotation: Signing keys can be rotated without invalidating historical certificates. Each certificate records the key_id used to sign it. To verify historical certificates after a key rotation, provide the historical key version to the verification function. Enterprise key management documentation is available on request — contact our team.

Certificate Schema Reference

The complete Decision Certificate is returned in the audit_record field of every POST /v1/decisions/evaluate response. The structure is as follows:

FieldTypeDescription
decision_idstringGlobally unique identifier for this decision. Format: dec_<26-char ULID>. Stable and immutable.
timestampstring (ISO 8601)UTC timestamp of the decision evaluation, with millisecond precision. Example: 2026-05-05T14:22:11.342Z
policy_setstringThe policy pack identifier and version used for this evaluation. Example: lending_v1
policy_versionstring (semver)Exact rule version within the pack. Example: 1.4.2. Enables precise rule audit.
dispositionstring (enum)Final decision outcome: ALLOWED, BLOCKED, or MODIFIED.
risk_levelstring (enum)Assessed risk level: LOW, MEDIUM, or HIGH.
risk_scorenumber (0–100)Numeric risk score computed by the policy evaluator. Higher scores indicate greater policy risk.
violationsarrayArray of PolicyViolation objects describing each rule that fired. Empty array for ALLOWED decisions.
violations[].rule_idstringThe specific rule identifier within the policy pack. Example: lending.prohibited_basis
violations[].severitystring (enum)Rule severity: LOW, MEDIUM, or HIGH.
violations[].descriptionstringHuman-readable description of the violation, including statutory or policy citation where applicable.
violations[].remediationstringRecommended remediation action for the violation.
modificationsobject or nullFor MODIFIED decisions: structured description of required modifications. Null for ALLOWED and BLOCKED.
actor_idstringThe user.id from the evaluation request. Identifies the human or system that initiated the action.
actor_rolestringThe user.role from the evaluation request.
org_idstringYour organization's CoreGuard tenant identifier. Scopes the certificate to your audit namespace.
prev_hashstring (hex)SHA-256 hash of the immediately preceding certificate in your organization's audit chain. Used for chain integrity verification.
key_idstringIdentifier of the HMAC signing key version used to produce hmac_signature.
hmac_signaturestring (hex)HMAC-SHA256 signature over the JCS-canonical form of all other fields in this object. 64 hex characters (256-bit output).

Full Certificate Example

{ "decision": { "status": "BLOCKED", "risk_level": "HIGH", "violations": [ { "rule_id": "lending.prohibited_basis", "severity": "HIGH", "description": "Decision rationale references applicant age, a prohibited basis under ECOA.", "remediation": "Remove age-based reasoning and re-evaluate." } ], "modifications": null }, "audit_record": { "decision_id": "dec_01hwz3mq4k9p8xjr7nv2df6cts", "timestamp": "2026-05-05T14:22:11.342Z", "policy_set": "lending_v1", "policy_version": "1.4.2", "disposition": "BLOCKED", "risk_level": "HIGH", "risk_score": 92, "violations": [ { "rule_id": "lending.prohibited_basis", "severity": "HIGH", "description": "Decision rationale references applicant age, a prohibited basis under ECOA.", "remediation": "Remove age-based reasoning and re-evaluate." } ], "modifications": null, "actor_id": "u_892", "actor_role": "loan_officer", "org_id": "org_regional_lending", "prev_hash": "a3f8c2d7e9b1045f6a8e2c4d7f9b1234e5678901a2b3c4d5e6f78901a2b3c4d5", "key_id": "key_v3_2026q2", "hmac_signature": "7b4e2f9a1c3d5e8f0a2b4c6d8e0f2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f" } }

Offline Verification

Decision Certificates are designed to be verified without a network connection and without calling any CoreGuard API. Verification requires:

Verification procedure:

  1. Extract all fields from audit_record except hmac_signature.
  2. Serialize the remaining fields using RFC 8785 JSON Canonicalization Scheme: lexicographic key ordering, no whitespace, NFC Unicode normalization.
  3. Compute HMAC-SHA256 over the canonical byte sequence using the key identified by key_id.
  4. Compare the computed value (hex-encoded) against the stored hmac_signature. A match confirms the record has not been tampered with since issuance.

Chain verification: For high-assurance audit scenarios, verify not just individual certificates but the chain formed by the prev_hash links. A break in the chain — where a certificate's prev_hash does not match the SHA-256 hash of the preceding certificate — indicates that records have been deleted or reordered. The CoreGuard SDK's verify_chain() method automates this check.

Compliance Retention Guidance

How long you retain Decision Certificates depends on the regulatory regimes that govern your AI deployment. The following table summarizes minimum retention requirements by regulatory domain.

Regulatory DomainAuthorityMinimum RetentionNotes
Consumer Lending (ECOA / Reg B)CFPB / Fed25 monthsAdverse action records; Reg B § 202.12. Retain from date of action, not from credit application.
Consumer Reporting (FCRA)CFPB / FTC5 yearsFor AI systems using consumer reports. Permissible purpose documentation.
Mortgage (HMDA)CFPB3 yearsLoan application register records under Reg C.
Securities / Trading (FINRA)FINRA / SEC7 yearsBooks and records rules (FINRA Rule 4511, SEC Rule 17a-4). Governance decisions related to trading activity.
Healthcare (HIPAA)HHS OCR6 yearsFrom date of creation or last effective date. BAA terms may require longer retention.
Insurance (State)State DOI5–7 yearsVaries by state. Underwriting records typically 5 years; claims-related AI decisions 7 years in most jurisdictions.
Federal Government (NARA)NARA / OMB7+ yearsPer agency records schedule. AI decision records may qualify as program records requiring permanent retention in some agencies.
General EnterpriseSOX / internal7 yearsSOX Section 802 general records standard for public companies. Internal policy may require longer.

EVE Core recommendation: Retain all Decision Certificates for a minimum of 7 years regardless of regulatory domain. This matches the most stringent common requirement (FINRA / SOX) and eliminates the operational complexity of maintaining domain-specific retention schedules. For highly regulated deployments, retain indefinitely and rely on storage compression — certificate records are compact (typically under 2 KB each) and compressible.

Python Verification Example

The eve-coreguard Python SDK includes a built-in verify_certificate() function. The following example demonstrates standalone verification without the SDK, using only the Python standard library.

import hmac import hashlib import json from typing import Any, Dict def jcs_canonicalize(obj: Any) -> bytes: """RFC 8785 JSON Canonicalization Scheme — lexicographic key sort, no whitespace.""" if isinstance(obj, dict): sorted_obj = {k: obj[k] for k in sorted(obj.keys())} return json.dumps(sorted_obj, ensure_ascii=False, separators=(',', ':'), sort_keys=True).encode('utf-8') return json.dumps(obj, ensure_ascii=False, separators=(',', ':')).encode('utf-8') def verify_certificate( audit_record: Dict, verification_key_hex: str, ) -> Dict: """ Verify a CoreGuard Decision Certificate. Parameters ---------- audit_record : The audit_record dict from the API response or your store. verification_key_hex : Hex-encoded HMAC key for the key_id in the record. Returns ------- dict with keys: valid (bool) — True if signature matches. decision_id (str) — The decision ID from the record. disposition (str) — ALLOWED / BLOCKED / MODIFIED. error (str or None) — Error message if invalid, else None. """ try: stored_sig = audit_record.get('hmac_signature', '') payload = { k: v for k, v in audit_record.items() if k != 'hmac_signature' } canonical_bytes = jcs_canonicalize(payload) key_bytes = bytes.fromhex(verification_key_hex) computed_sig = hmac.new( key_bytes, canonical_bytes, hashlib.sha256 ).hexdigest() is_valid = hmac.compare_digest(computed_sig, stored_sig) return { 'valid': is_valid, 'decision_id': audit_record.get('decision_id'), 'disposition': audit_record.get('disposition'), 'error': None if is_valid else 'Signature mismatch — record may have been modified.', } except Exception as exc: return {'valid': False, 'decision_id': None, 'disposition': None, 'error': str(exc)} # --- Example usage --- MY_VERIFICATION_KEY_HEX = "your_64_char_hex_key_here" result = verify_certificate(audit_record, MY_VERIFICATION_KEY_HEX) if result['valid']: print(f"Certificate {result['decision_id']} verified — {result['disposition']}") else: print(f"INVALID: {result['error']}") # Raise an alert — do not trust this record

Using the CoreGuard Python SDK, the same verification is available as EveCoreguardClient.verify_certificate(audit_record, key_hex). The SDK additionally supports chain verification via verify_chain(records, key_hex), which validates the entire prev_hash chain across a list of records in chronological order.

Security note: Never transmit your HMAC verification key over the network or store it in application code. Use an HSM, secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), or encrypted key store. The verification_key_hex parameter in the SDK can be loaded from an environment variable or a secrets manager at runtime.