Download audit/worm.py from Snapkitty/vault-live: direct link, hf CLI and curl.
- Browser
- Download file 4.75 kB
-
https://huggingface.co/Snapkitty/vault-live/resolve/main/audit/worm.py
- Command line
-
hf download hf://Snapkitty/vault-live/audit/worm.py
-
curl -L -o worm.py https://huggingface.co/Snapkitty/vault-live/resolve/main/audit/worm.py
4.75 kB
| """ | |
| audit/worm.py | |
| Append-only WORM audit chain. Each record is chained via SHA-256. | |
| The Semantic Hash from the NAND gate is the immutable identity anchor. | |
| """ | |
| import hashlib | |
| import json | |
| from dataclasses import dataclass, field | |
| from typing import Optional | |
| class AuditRecord: | |
| timestamp: str | |
| semantic_hash: str | |
| assertion_id: str | |
| issuer: str | |
| subject: str | |
| gate_result: bool | |
| entropy: float | |
| attributes_hash: str # SHA-256 of sorted attr JSON — no raw values | |
| event: str # ASSERTION_ACCEPTED | ASSERTION_REJECTED | ... | |
| def canonical_json(self) -> str: | |
| d = { | |
| 'assertion_id': self.assertion_id, | |
| 'attributes_hash': self.attributes_hash, | |
| 'entropy': round(self.entropy, 6), | |
| 'event': self.event, | |
| 'gate_result': self.gate_result, | |
| 'issuer': self.issuer, | |
| 'semantic_hash': self.semantic_hash, | |
| 'subject': self.subject, | |
| 'timestamp': self.timestamp, | |
| } | |
| return json.dumps(d, sort_keys=True, separators=(',', ':')) | |
| class ChainEntry: | |
| sequence: int | |
| prev_hash: str | |
| entry_hash: str | |
| record: AuditRecord | |
| class ChainVerifyResult: | |
| valid: bool | |
| broken_at: Optional[int] | |
| total_entries: int | |
| error: Optional[str] = None | |
| class WORMAuditChain: | |
| def __init__(self, path: str): | |
| self._path = path | |
| self._sequence = 0 | |
| self._prev_hash = '0' * 64 | |
| # Load last entry to restore chain state | |
| try: | |
| with open(path, 'r', encoding='utf-8') as f: | |
| lines = [l.strip() for l in f if l.strip()] | |
| if lines: | |
| last = json.loads(lines[-1]) | |
| self._prev_hash = last['entry_hash'] | |
| self._sequence = last['sequence'] + 1 | |
| except FileNotFoundError: | |
| pass | |
| def append(self, record: AuditRecord) -> ChainEntry: | |
| entry_hash = hashlib.sha256( | |
| (self._prev_hash + record.canonical_json()).encode('utf-8') | |
| ).hexdigest() | |
| entry = ChainEntry( | |
| sequence=self._sequence, | |
| prev_hash=self._prev_hash, | |
| entry_hash=entry_hash, | |
| record=record, | |
| ) | |
| line = json.dumps({ | |
| 'sequence': entry.sequence, | |
| 'prev_hash': entry.prev_hash, | |
| 'entry_hash': entry.entry_hash, | |
| 'record': json.loads(record.canonical_json()), | |
| }, sort_keys=True, separators=(',', ':')) | |
| with open(self._path, 'a', encoding='utf-8') as f: | |
| f.write(line + '\n') | |
| self._prev_hash = entry_hash | |
| self._sequence += 1 | |
| return entry | |
| def verify_chain(path: str) -> ChainVerifyResult: | |
| try: | |
| with open(path, 'r', encoding='utf-8') as f: | |
| lines = [l.strip() for l in f if l.strip()] | |
| except FileNotFoundError: | |
| return ChainVerifyResult(valid=True, broken_at=None, total_entries=0) | |
| prev_hash = '0' * 64 | |
| for i, line in enumerate(lines): | |
| try: | |
| entry = json.loads(line) | |
| except json.JSONDecodeError as e: | |
| return ChainVerifyResult(valid=False, broken_at=i, total_entries=len(lines), | |
| error=f"JSON parse error: {e}") | |
| if entry.get('prev_hash') != prev_hash: | |
| return ChainVerifyResult(valid=False, broken_at=i, total_entries=len(lines), | |
| error="prev_hash mismatch") | |
| record_json = json.dumps(entry['record'], sort_keys=True, separators=(',', ':')) | |
| expected_hash = hashlib.sha256( | |
| (prev_hash + record_json).encode('utf-8') | |
| ).hexdigest() | |
| if entry.get('entry_hash') != expected_hash: | |
| return ChainVerifyResult(valid=False, broken_at=i, total_entries=len(lines), | |
| error="entry_hash mismatch") | |
| prev_hash = entry['entry_hash'] | |
| return ChainVerifyResult(valid=True, broken_at=None, total_entries=len(lines)) | |
| def attributes_hash(attrs: dict) -> str: | |
| """SHA-256 of sorted attribute JSON. No raw values stored in audit records.""" | |
| sorted_attrs = {k: sorted(v) for k, v in sorted(attrs.items())} | |
| return hashlib.sha256( | |
| json.dumps(sorted_attrs, sort_keys=True, separators=(',', ':')).encode('utf-8') | |
| ).hexdigest() | |