chore: initialize monorepo with project scaffolding, configs, and CI setup

This commit is contained in:
oib
2025-09-27 06:05:25 +02:00
commit c1926136fb
171 changed files with 13708 additions and 0 deletions

View File

@ -0,0 +1,4 @@
"""AITBC crypto utilities package."""
from . import receipt # noqa: F401
from . import signing # noqa: F401

View File

@ -0,0 +1,11 @@
"""AITBC cryptographic helpers for receipts."""
from .receipt import canonical_json, receipt_hash
from .signing import ReceiptSigner, ReceiptVerifier
__all__ = [
"canonical_json",
"receipt_hash",
"ReceiptSigner",
"ReceiptVerifier",
]

View File

@ -0,0 +1,23 @@
from __future__ import annotations
from typing import Any, Dict
import json
from hashlib import sha256
def canonical_json(receipt: Dict[str, Any]) -> str:
def remove_none(obj: Any) -> Any:
if isinstance(obj, dict):
return {k: remove_none(v) for k, v in obj.items() if v is not None}
if isinstance(obj, list):
return [remove_none(x) for x in obj if x is not None]
return obj
cleaned = remove_none(receipt)
return json.dumps(cleaned, separators=(",", ":"), sort_keys=True)
def receipt_hash(receipt: Dict[str, Any]) -> bytes:
data = canonical_json(receipt).encode("utf-8")
return sha256(data).digest()

View File

@ -0,0 +1,51 @@
from __future__ import annotations
from typing import Any, Dict
import base64
from nacl.signing import SigningKey, VerifyKey
from .receipt import canonical_json
def _urlsafe_b64encode(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")
def _urlsafe_b64decode(data: str) -> bytes:
padding = '=' * (-len(data) % 4)
return base64.urlsafe_b64decode(data + padding)
class ReceiptSigner:
def __init__(self, signing_key: bytes):
self._key = SigningKey(signing_key)
def sign(self, payload: Dict[str, Any]) -> Dict[str, Any]:
message = canonical_json(payload).encode("utf-8")
signed = self._key.sign(message)
return {
"alg": "Ed25519",
"key_id": _urlsafe_b64encode(self._key.verify_key.encode()),
"sig": _urlsafe_b64encode(signed.signature),
}
class ReceiptVerifier:
def __init__(self, verify_key: bytes):
self._key = VerifyKey(verify_key)
def verify(self, payload: Dict[str, Any], signature: Dict[str, Any]) -> bool:
if signature.get("alg") != "Ed25519":
return False
sig_field = signature.get("sig")
if not isinstance(sig_field, str):
return False
message = canonical_json(payload).encode("utf-8")
sig_bytes = _urlsafe_b64decode(sig_field)
try:
self._key.verify(message, sig_bytes)
return True
except Exception:
return False

View File

@ -0,0 +1,47 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict
import json
from hashlib import sha256
from pydantic import BaseModel
class Receipt(BaseModel):
version: str
receipt_id: str
job_id: str
provider: str
client: str
units: float
unit_type: str
started_at: int
completed_at: int
price: float | None = None
model: str | None = None
prompt_hash: str | None = None
duration_ms: int | None = None
artifact_hash: str | None = None
coordinator_id: str | None = None
nonce: str | None = None
chain_id: int | None = None
metadata: Dict[str, Any] | None = None
def canonical_json(receipt: Dict[str, Any]) -> str:
def remove_none(obj: Any) -> Any:
if isinstance(obj, dict):
return {k: remove_none(v) for k, v in obj.items() if v is not None}
if isinstance(obj, list):
return [remove_none(x) for x in obj if x is not None]
return obj
cleaned = remove_none(receipt)
return json.dumps(cleaned, separators=(",", ":"), sort_keys=True)
def receipt_hash(receipt: Dict[str, Any]) -> bytes:
data = canonical_json(receipt).encode("utf-8")
return sha256(data).digest()

View File

@ -0,0 +1,40 @@
from __future__ import annotations
from typing import Dict, Any
import base64
from hashlib import sha256
from nacl.signing import SigningKey, VerifyKey
from .receipt import canonical_json
class ReceiptSigner:
def __init__(self, signing_key: bytes):
self._key = SigningKey(signing_key)
def sign(self, payload: Dict[str, Any]) -> Dict[str, Any]:
message = canonical_json(payload).encode("utf-8")
signature = self._key.sign(message)
return {
"alg": "Ed25519",
"key_id": base64.urlsafe_b64encode(self._key.verify_key.encode()).decode("utf-8").rstrip("="),
"sig": base64.urlsafe_b64encode(signature.signature).decode("utf-8").rstrip("="),
}
class ReceiptVerifier:
def __init__(self, verify_key: bytes):
self._key = VerifyKey(verify_key)
def verify(self, payload: Dict[str, Any], signature: Dict[str, Any]) -> bool:
if signature.get("alg") != "Ed25519":
return False
sig_bytes = base64.urlsafe_b64decode(signature["sig"] + "==")
message = canonical_json(payload).encode("utf-8")
try:
self._key.verify(message, sig_bytes)
return True
except Exception:
return False