intelligence-radar/intelligence_radar/store.py
tegwick 29ceb77938 Build reproducible capability advisor and scheduled source capture
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09c77-b1a9-7eb3-b501-6cb24b458c97
2026-09-13 23:07:41 +02:00

53 lines
1.7 KiB
Python

"""Atomic content-addressed JSON and source artifact storage."""
import hashlib
import json
import os
from pathlib import Path
import tempfile
def canonical(value):
return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False,
allow_nan=False).encode('utf-8')
def atomic(path, data, immutable=False):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(dir=path.parent, prefix='.write-')
try:
with os.fdopen(fd, 'wb') as stream:
stream.write(data)
stream.flush()
os.fsync(stream.fileno())
if immutable:
try:
os.link(name, path)
except FileExistsError:
if path.read_bytes() != data:
raise ValueError(f'immutable content mismatch: {path}') from None
else:
os.replace(name, path)
finally:
Path(name).unlink(missing_ok=True)
def put(root, kind, value):
content = canonical(value)
ident = hashlib.sha256(content).hexdigest()
atomic(Path(root) / 'data' / kind / f'{ident}.json', content + b'\n', immutable=True)
return ident
def get(root, kind, ident):
if len(ident) != 64 or any(c not in '0123456789abcdef' for c in ident):
raise ValueError('invalid content ID')
value = json.loads((Path(root) / 'data' / kind / f'{ident}.json').read_text())
if hashlib.sha256(canonical(value)).hexdigest() != ident:
raise ValueError(f'integrity mismatch: {kind}/{ident}')
return value
def all_records(root, kind):
return [(p.stem, get(root, kind, p.stem))
for p in sorted((Path(root) / 'data' / kind).glob('*.json'))]