README.md
| 1 | # pqc-reasoning-ledger |
| 2 | |
| 3 |  |
| 4 |  |
| 5 |  |
| 6 |  |
| 7 |  |
| 8 |  |
| 9 | |
| 10 | **PQC-signed neurosymbolic reasoning ledger.** Sign chain-of-thought steps in real time |
| 11 | during AI inference. Produces legally defensible, quantum-safe reasoning trails for |
| 12 | regulated industries where the reasoning chain itself must survive a decade of |
| 13 | adversarial review. |
| 14 | |
| 15 | As AI is used for high-stakes reasoning (legal analysis, medical diagnosis, financial |
| 16 | risk, regulatory decisions), courts and regulators are asking *"how did the model reach |
| 17 | this conclusion?"* This library records every step of the model's thought process as a |
| 18 | hash-chained, Merkle-rooted, ML-DSA-65 signed **reasoning trace** that is tamper-evident |
| 19 | end-to-end and verifiable by third parties without any trust in the original recorder. |
| 20 | |
| 21 | ## Install |
| 22 | |
| 23 | ```bash |
| 24 | pip install pqc-reasoning-ledger |
| 25 | ``` |
| 26 | |
| 27 | ## Quick start |
| 28 | |
| 29 | ```python |
| 30 | from quantumshield.identity.agent import AgentIdentity |
| 31 | from pqc_reasoning_ledger import ReasoningRecorder, TraceVerifier |
| 32 | |
| 33 | identity = AgentIdentity.create("legal-advisor-signer") |
| 34 | rec = ReasoningRecorder(identity) |
| 35 | rec.begin_trace( |
| 36 | model_did="did:pqaid:gpt-legal", |
| 37 | model_version="2.1", |
| 38 | task="contract-review", |
| 39 | domain="legal", |
| 40 | ) |
| 41 | |
| 42 | rec.record_observation("Contract contains a liquidated damages clause capping at $1M.") |
| 43 | rec.record_retrieval("NY CPLR 3215; Truck Rent-A-Center v. Puritan Farms 2nd (1977).") |
| 44 | rec.record_hypothesis("Clause is likely enforceable if sum is reasonable relative to harm.") |
| 45 | rec.record_deduction("Given $1M cap and projected $1.2M harm, clause is reasonable.") |
| 46 | rec.record_self_critique("Should also check whether plaintiff must mitigate damages.") |
| 47 | rec.record_decision("Recommend signing with carve-out for force-majeure events.") |
| 48 | |
| 49 | sealed = rec.seal() |
| 50 | |
| 51 | # External verifier, days or years later: |
| 52 | result = TraceVerifier.verify(sealed) |
| 53 | assert result.fully_verified |
| 54 | ``` |
| 55 | |
| 56 | The `sealed` value is a self-contained, post-quantum-signed proof of the *exact sequence |
| 57 | of reasoning steps* the model took. Altering any step, adding a step, dropping a step, |
| 58 | reordering steps, or forging the signature will all cause `TraceVerifier.verify()` to |
| 59 | return `valid=False` with a specific error string. |
| 60 | |
| 61 | ## Architecture |
| 62 | |
| 63 | ``` |
| 64 | +---------------------+ record_* +---------------------+ |
| 65 | | Model inference | -------------> | ReasoningRecorder | |
| 66 | | (chain-of-thought)| | | |
| 67 | +---------------------+ | ReasoningTrace | |
| 68 | | step_1 --+ | |
| 69 | | step_2 --+ hash | |
| 70 | | step_3 --+ chain | |
| 71 | | ... | |
| 72 | +---------+-----------+ |
| 73 | | seal() |
| 74 | v |
| 75 | +----------------+-----------------+ |
| 76 | | SealedTrace | |
| 77 | | final_chain_hash | |
| 78 | | merkle_root (over step_hashes) | |
| 79 | | ML-DSA-65 signature | |
| 80 | | signer public key | |
| 81 | +----------------+-----------------+ |
| 82 | | transport / archive |
| 83 | v |
| 84 | +----------------+-----------------+ |
| 85 | | TraceVerifier (independent) | |
| 86 | | 1. chain integrity | |
| 87 | | 2. merkle root | |
| 88 | | 3. ML-DSA signature | |
| 89 | +----------------------------------+ |
| 90 | | |
| 91 | v |
| 92 | fully_verified: True / False |
| 93 | ``` |
| 94 | |
| 95 | Each step records `SHA3-256(previous_step_hash || canonical_bytes(step))`, so tampering |
| 96 | with any intermediate step breaks every downstream hash. The `merkle_root` over step |
| 97 | hashes enables **selective disclosure**: you can prove a single step was in the trace |
| 98 | (e.g. "the model considered case law X") via `ReasoningProver.prove_step()` without |
| 99 | revealing the other steps. |
| 100 | |
| 101 | ## Step kinds |
| 102 | |
| 103 | The symbolic vocabulary for reasoning steps (`StepKind`): |
| 104 | |
| 105 | | Kind | Meaning | |
| 106 | | --- | --- | |
| 107 | | `thought` | Free-form reasoning statement | |
| 108 | | `observation` | Observation about input or retrieved data | |
| 109 | | `hypothesis` | A tentative conclusion to evaluate | |
| 110 | | `deduction` | Logical deduction from prior steps | |
| 111 | | `retrieval` | Fetching external knowledge (RAG, memory, citation) | |
| 112 | | `tool-call` | Calling an external tool or function | |
| 113 | | `tool-result` | Result returned by a tool call | |
| 114 | | `self-critique` | Model critiquing its own prior step | |
| 115 | | `refinement` | Updated answer after critique | |
| 116 | | `decision` | Final decision or answer | |
| 117 | | `meta` | Metadata about the run itself | |
| 118 | |
| 119 | ## Cryptography |
| 120 | |
| 121 | | Layer | Primitive | |
| 122 | | --- | --- | |
| 123 | | Content hash | SHA3-256 of UTF-8 step text | |
| 124 | | Step hash | SHA3-256( previous_step_hash \|\| canonical_json(step_payload) ) | |
| 125 | | Merkle tree | SHA3-256 with RFC6962-style domain separation (0x00 leaves, 0x01 internal) | |
| 126 | | Trace seal signature | ML-DSA-65 (NIST FIPS 204) over SHA3-256 of sealed canonical bytes | |
| 127 | | Identity | Quantumshield `AgentIdentity` + DID (`did:pqaid:...`) | |
| 128 | |
| 129 | All signatures are produced via `quantumshield.core.signatures.sign` and verify via |
| 130 | `quantumshield.core.signatures.verify`, so the verification path has no dependency on |
| 131 | the original signer process. |
| 132 | |
| 133 | ## Threat model |
| 134 | |
| 135 | | Attack | Detected by | |
| 136 | | --- | --- | |
| 137 | | Retroactive edit of an intermediate step | Chain check: step hash and all downstream hashes break | |
| 138 | | Swapped step at position k | Chain check: previous_step_hash at k+1 no longer matches | |
| 139 | | Inserted step | Chain check: step_number off by one; step_hash invalid | |
| 140 | | Dropped step | `final_chain_hash`, Merkle root, and signature all mismatch | |
| 141 | | Re-ordered steps | Chain check fails; Merkle root fails | |
| 142 | | Forged ML-DSA signature | ML-DSA-65 verify fails (PQ-secure) | |
| 143 | | Substituted public key | Signature still has to verify against attached key, but the DID is bound in the signed payload and independent public-key-infrastructure should be consulted for signer provenance | |
| 144 | |
| 145 | What is **not** in scope: confidentiality of reasoning steps (this library is about |
| 146 | integrity, not secrecy - encrypt separately if needed), and mitigations against a |
| 147 | compromised signer (rotate and revoke via your identity infrastructure). |
| 148 | |
| 149 | ## API reference |
| 150 | |
| 151 | ### `ReasoningRecorder(identity: AgentIdentity)` |
| 152 | |
| 153 | - `begin_trace(model_did, model_version, task="", actor_did="", session_id="", domain="")` |
| 154 | - `record(kind, content, references=None, confidence=1.0, metadata=None)` |
| 155 | - `record_thought(content, **kw)` / `record_observation` / `record_hypothesis` |
| 156 | / `record_deduction` / `record_retrieval` / `record_tool_call` / `record_tool_result` |
| 157 | / `record_self_critique` / `record_refinement` / `record_decision` |
| 158 | - `seal() -> SealedTrace` |
| 159 | |
| 160 | ### `ReasoningTrace` |
| 161 | |
| 162 | - `metadata: TraceMetadata`, `steps: list[ReasoningStep]`, `sealed: bool` |
| 163 | - `current_hash -> str` (chain-tip hash) |
| 164 | - `append(step)` - raises `ChainBrokenError` / `TraceSealedError` on violation |
| 165 | - `to_dict() -> dict` |
| 166 | |
| 167 | ### `SealedTrace` |
| 168 | |
| 169 | - Dataclass with `metadata`, `steps`, `final_chain_hash`, `merkle_root`, `step_count`, |
| 170 | `sealed_at`, `signer_did`, `algorithm`, `signature`, `public_key` |
| 171 | - `to_dict()` / `to_json()` / `from_dict()` / `from_json()` |
| 172 | - `canonical_bytes() -> bytes` (deterministic payload that is signed) |
| 173 | |
| 174 | ### `TraceVerifier` |
| 175 | |
| 176 | - `verify(sealed) -> VerificationResult` with `valid`, `signature_valid`, |
| 177 | `chain_intact`, `merkle_root_valid`, `step_count`, `error`, and `fully_verified` |
| 178 | - `verify_or_raise(sealed)` - raises `SignatureVerificationError` on failure |
| 179 | |
| 180 | ### `ReasoningProver` |
| 181 | |
| 182 | - `prove_step(sealed, step_id) -> StepInclusionProof` |
| 183 | - `verify_proof(proof) -> bool` |
| 184 | |
| 185 | ## Why PQC for reasoning trails |
| 186 | |
| 187 | AI liability law is accelerating. The EU AI Act, NYC Local Law 144, the FDA's AI/ML |
| 188 | Software Pre-Cert guidance, and state-level medical and insurance rules all push toward |
| 189 | **retrospective explainability of AI decisions**. A signed reasoning trace has to remain |
| 190 | verifiable for the full litigation horizon - often 6-10+ years - which is well inside |
| 191 | the timeline where cryptographically relevant quantum computers become a risk to |
| 192 | classical-signature archives. ML-DSA-65 is NIST-standardized (FIPS 204) and provides |
| 193 | integrity guarantees that outlive the archival window, without requiring re-signing |
| 194 | the corpus under a new algorithm. |
| 195 | |
| 196 | This library is **ahead of the curve**: the reasoning trails it produces today will |
| 197 | still be defensible evidence when quantum-capable adversaries exist. |
| 198 | |
| 199 | ## Examples |
| 200 | |
| 201 | - `examples/legal_contract_review.py` - 6-step legal reasoning trace |
| 202 | - `examples/medical_diagnosis.py` - 7-step medical reasoning with inclusion proof |
| 203 | - `examples/tamper_detection.py` - demonstrate detection of a flipped byte |
| 204 | |
| 205 | ## License |
| 206 | |
| 207 | Apache 2.0 - see `LICENSE`. |
| 208 | |