khipu-consensus 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: khipu-consensus
3
+ Version: 0.1.0
4
+ Summary: Byzantine-fault-tolerant multi-party signed agreement (3-of-4 BFT) over DSSE/cosign per-witness signatures.
5
+ Author-email: Yachay <yachay@szlholdings.dev>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/szl-holdings/khipu-consensus
8
+ Project-URL: Source, https://github.com/szl-holdings/khipu-consensus
9
+ Project-URL: Issues, https://github.com/szl-holdings/khipu-consensus/issues
10
+ Project-URL: Changelog, https://github.com/szl-holdings/khipu-consensus/releases
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: cryptography>=41.0
14
+ Provides-Extra: service
15
+ Requires-Dist: fastapi>=0.100; extra == "service"
16
+ Requires-Dist: uvicorn>=0.23; extra == "service"
17
+ Requires-Dist: pydantic>=2.0; extra == "service"
18
+ Provides-Extra: remote
19
+ Requires-Dist: requests>=2.28; extra == "remote"
20
+ Provides-Extra: spine
21
+ Requires-Dist: szl-receipt-dsse>=0.3.1; extra == "spine"
22
+
23
+ # khipu-consensus (Python)
24
+
25
+ Byzantine-fault-tolerant multi-party signed agreement — a 3-of-4 witness quorum over
26
+ DSSE/cosign per-witness signatures. This is the Python package for the
27
+ [szl-holdings/khipu-consensus](https://github.com/szl-holdings/khipu-consensus) repository;
28
+ the repository README carries the full doctrine, demo links, and evidence boundaries.
29
+
30
+ ```bash
31
+ pip install khipu-consensus
32
+ khipu-verify --help
33
+ ```
34
+
35
+ Doctrine boundary: safety and liveness of the quorum are **Conjecture 2 / Conjecture 3**
36
+ (proof-deferred, not proven). Λ remains Conjecture 1. Nothing in this package asserts a
37
+ theorem it does not carry.
38
+
39
+ Optional extras: `service` (FastAPI witness API), `remote` (HTTP SDK transport),
40
+ `spine` (fold a witnessed decision into one canonical `szl-receipt-dsse` receipt).
41
+
42
+ License: Apache-2.0 · Source: https://github.com/szl-holdings/khipu-consensus
@@ -0,0 +1,20 @@
1
+ # khipu-consensus (Python)
2
+
3
+ Byzantine-fault-tolerant multi-party signed agreement — a 3-of-4 witness quorum over
4
+ DSSE/cosign per-witness signatures. This is the Python package for the
5
+ [szl-holdings/khipu-consensus](https://github.com/szl-holdings/khipu-consensus) repository;
6
+ the repository README carries the full doctrine, demo links, and evidence boundaries.
7
+
8
+ ```bash
9
+ pip install khipu-consensus
10
+ khipu-verify --help
11
+ ```
12
+
13
+ Doctrine boundary: safety and liveness of the quorum are **Conjecture 2 / Conjecture 3**
14
+ (proof-deferred, not proven). Λ remains Conjecture 1. Nothing in this package asserts a
15
+ theorem it does not carry.
16
+
17
+ Optional extras: `service` (FastAPI witness API), `remote` (HTTP SDK transport),
18
+ `spine` (fold a witnessed decision into one canonical `szl-receipt-dsse` receipt).
19
+
20
+ License: Apache-2.0 · Source: https://github.com/szl-holdings/khipu-consensus
@@ -0,0 +1,196 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173
3
+ """
4
+ khipu-consensus — Byzantine-fault-tolerant multi-party signed agreement.
5
+
6
+ The category: multi-party-witnessed AI. Each witness ("organ") signs an action
7
+ hash with its own ECDSA-P256 key. >= threshold valid `allow` signatures over the
8
+ same action hash ⇒ the action is CANONICAL (BFT n witnesses, f = n - threshold
9
+ tolerated faults; 3-of-4 tolerates 1). Every per-witness signature is
10
+ ECDSA-P256-SHA256 over the DSSE (Dead-Simple-Signing-Envelope) Pre-Authentication
11
+ Encoding, so it is verifiable by `cosign verify-blob --key <witness>.pub`.
12
+
13
+ Reference implementation. Mirrors typescript/ and go/ byte-for-byte on the same
14
+ deterministic test vectors (testdata/vectors.json).
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import base64
19
+ import hashlib
20
+ import json
21
+ from dataclasses import dataclass, field
22
+ from typing import Optional
23
+
24
+ ORGAN_VERDICT_PAYLOAD_TYPE = "application/vnd.szl.khipu.organ-verdict+json"
25
+ __version__ = "0.1.0"
26
+
27
+
28
+ def canonical_json(obj) -> bytes:
29
+ """Deterministic canonical JSON: sorted keys, compact separators, UTF-8."""
30
+ return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
31
+
32
+
33
+ def pae(payload_type: str, body: bytes) -> bytes:
34
+ """DSSE Pre-Authentication Encoding (DSSEv1)."""
35
+ t = payload_type.encode("utf-8")
36
+ return b"DSSEv1 " + str(len(t)).encode() + b" " + t + b" " + str(len(body)).encode() + b" " + body
37
+
38
+
39
+ @dataclass
40
+ class OrganVerdict:
41
+ organ: str
42
+ keyid: str
43
+ payload_type: str
44
+ payload_b64: str # base64 of the canonical signed statement
45
+ signature_b64: str # base64 of the ECDSA-P256-SHA256 signature over PAE
46
+ verdict: str = "allow" # informational; the authoritative verdict is inside payload
47
+ reason: str = ""
48
+
49
+ @staticmethod
50
+ def from_dict(d: dict) -> "OrganVerdict":
51
+ return OrganVerdict(
52
+ organ=d.get("organ", ""),
53
+ keyid=d.get("keyid", ""),
54
+ payload_type=d.get("payloadType", ORGAN_VERDICT_PAYLOAD_TYPE),
55
+ payload_b64=d.get("payload", ""),
56
+ signature_b64=d.get("signature", ""),
57
+ verdict=d.get("verdict", "allow"),
58
+ reason=d.get("reason", ""),
59
+ )
60
+
61
+
62
+ @dataclass
63
+ class OrganCheck:
64
+ organ: str
65
+ keyid: str
66
+ valid: bool
67
+ verdict: Optional[str]
68
+ action_hash_match: bool
69
+ counts: bool
70
+ reason: str = ""
71
+
72
+
73
+ @dataclass
74
+ class ConsensusResult:
75
+ action_hash: str
76
+ threshold: int
77
+ n: int
78
+ consensus_count: int
79
+ decision: str
80
+ checks: list = field(default_factory=list)
81
+
82
+ @property
83
+ def khipu_consensus(self) -> str:
84
+ return f"{self.consensus_count}-of-{self.n}"
85
+
86
+
87
+ def sign_verdict(organ: str, action_hash: str, verdict: str, private_key_pem: str,
88
+ reason: str = "", lean_sha: str = "", ts: str = "") -> dict:
89
+ """Produce a DSSE-signed organ verdict dict (the wire shape)."""
90
+ from cryptography.hazmat.primitives.serialization import load_pem_private_key
91
+ from cryptography.hazmat.primitives.asymmetric import ec
92
+ from cryptography.hazmat.primitives import hashes
93
+ from datetime import datetime, timezone
94
+
95
+ keyid = f"{organ}-cosign"
96
+ statement = {
97
+ "schema": "szl.khipu.organ_verdict/v1", "organ": organ, "keyid": keyid,
98
+ "action_hash": action_hash, "verdict": verdict, "reason": reason,
99
+ "lean_sha": lean_sha, "ts": ts or datetime.now(timezone.utc).isoformat(),
100
+ }
101
+ body = canonical_json(statement)
102
+ priv = load_pem_private_key(private_key_pem.encode(), password=None)
103
+ sig = priv.sign(pae(ORGAN_VERDICT_PAYLOAD_TYPE, body), ec.ECDSA(hashes.SHA256()))
104
+ return {
105
+ "organ": organ, "keyid": keyid, "payloadType": ORGAN_VERDICT_PAYLOAD_TYPE,
106
+ "payload": base64.b64encode(body).decode(), "signature": base64.b64encode(sig).decode(),
107
+ "verdict": verdict, "reason": reason,
108
+ }
109
+
110
+
111
+ def verify_verdict(v: OrganVerdict, public_key_pem: str, action_hash: str) -> OrganCheck:
112
+ """Verify one organ's signature against its public key, and check that the
113
+ signed action_hash and verdict are internally consistent."""
114
+ from cryptography.hazmat.primitives.serialization import load_pem_public_key
115
+ from cryptography.hazmat.primitives.asymmetric import ec
116
+ from cryptography.hazmat.primitives import hashes
117
+ from cryptography.exceptions import InvalidSignature
118
+
119
+ if not v.payload_b64 or not v.signature_b64:
120
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "missing payload/signature")
121
+ # A valid signature from the same key in a different DSSE domain is not a
122
+ # Khipu witness verdict. Bind both the protocol and expected wire identity.
123
+ if v.payload_type != ORGAN_VERDICT_PAYLOAD_TYPE:
124
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "payload-type-mismatch")
125
+ expected_keyid = f"{v.organ}-cosign"
126
+ if not v.organ or v.keyid != expected_keyid:
127
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "witness-identity-mismatch")
128
+ try:
129
+ body = base64.b64decode(v.payload_b64)
130
+ to_verify = pae(v.payload_type, body)
131
+ pub = load_pem_public_key(public_key_pem.encode())
132
+ try:
133
+ pub.verify(base64.b64decode(v.signature_b64), to_verify, ec.ECDSA(hashes.SHA256()))
134
+ except InvalidSignature:
135
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "signature mismatch")
136
+ decoded = json.loads(body)
137
+ if not isinstance(decoded, dict) or decoded.get("schema") != "szl.khipu.organ_verdict/v1":
138
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "statement-schema-mismatch")
139
+ if decoded.get("organ") != v.organ or decoded.get("keyid") != expected_keyid:
140
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, "signed-witness-identity-mismatch")
141
+ ah_match = decoded.get("action_hash") == action_hash
142
+ verdict = decoded.get("verdict")
143
+ counts = ah_match and verdict == "allow"
144
+ return OrganCheck(v.organ, v.keyid, True, verdict, ah_match, counts)
145
+ except Exception as e:
146
+ return OrganCheck(v.organ, v.keyid, False, None, False, False, f"{type(e).__name__}: {e}")
147
+
148
+
149
+ def _valid_quorum_counts(threshold: int, n: int) -> bool:
150
+ return type(threshold) is int and type(n) is int and 1 <= threshold <= n
151
+
152
+
153
+ def tally(action_hash: str, verdicts: list, pubkeys: dict,
154
+ threshold: int = 3, n: int = 4) -> ConsensusResult:
155
+ """Count valid + allow signatures over `action_hash`; apply the BFT threshold.
156
+
157
+ Each witness ("organ") counts AT MOST ONCE: consensus is a property of
158
+ DISTINCT witnesses, not of raw verdict rows. A second valid `allow` from an
159
+ organ that already counted is recorded (``counts=False``, reason
160
+ "duplicate-witness (already counted)") but does NOT increment the tally, so
161
+ resubmitting one genuine verdict cannot inflate ``consensus_count`` past the
162
+ honest distinct-witness threshold. Mirrors typescript/ and go/ and the
163
+ distinctness the Lean ``validCount`` model assumes.
164
+
165
+ verdicts: list of dict|OrganVerdict|None (None = abstain/timeout).
166
+ pubkeys : {organ: PEM}.
167
+ Invalid quorum counts reject before verification: n and threshold must be
168
+ positive integers (not booleans), with threshold <= n. They are not coerced.
169
+ """
170
+ if not _valid_quorum_counts(threshold, n):
171
+ return ConsensusResult(action_hash, threshold, n, 0, "rejected", [])
172
+ checks = []
173
+ counted: set = set()
174
+ count = 0
175
+ for item in verdicts:
176
+ if item is None:
177
+ checks.append(OrganCheck(None, None, False, None, False, False, "abstain/timeout"))
178
+ continue
179
+ v = item if isinstance(item, OrganVerdict) else OrganVerdict.from_dict(item)
180
+ pem = pubkeys.get(v.organ, "")
181
+ if not pem:
182
+ checks.append(OrganCheck(v.organ, v.keyid, False, None, False, False, "no public key"))
183
+ continue
184
+ chk = verify_verdict(v, pem, action_hash)
185
+ if chk.counts and v.organ in counted:
186
+ checks.append(OrganCheck(
187
+ v.organ, v.keyid, chk.valid, chk.verdict, chk.action_hash_match,
188
+ False, "duplicate-witness (already counted)",
189
+ ))
190
+ continue
191
+ if chk.counts:
192
+ counted.add(v.organ)
193
+ count += 1
194
+ checks.append(chk)
195
+ decision = "canonical" if count >= threshold else "rejected"
196
+ return ConsensusResult(action_hash, threshold, n, count, decision, checks)