informed-decision/informed_decision/intake_hub.py
tegwick ff59199c3b Complete intake delivery wiring and narrow remaining owner blockers
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1029c-c003-7283-acc9-930b434e6cae
2026-10-03 21:34:50 +02:00

57 lines
3.1 KiB
Python

"""Bounded delivery to State Hub's retained memo-notification receipts."""
import hashlib
import json
import re
import uuid
from .http_transport import JSONTransport, fixed_origin
class HubDelivery:
def __init__(self, origin, *, transport=None):
self.origin = fixed_origin(origin, allow_internal_http=True)
self.transport = transport or JSONTransport(allow_internal_http=True)
def _request(self, method, url, payload, key):
body = json.dumps(payload, sort_keys=True, separators=(',', ':'), allow_nan=False).encode()
return self.transport.request(method, url, body=body if method == 'POST' else None,
headers={'Content-Type': 'application/json', 'Idempotency-Key': key,
'X-StateHub-Source-Agent': 'informed-decision'})
def notify(self, delivery_id, metadata):
uuid.UUID(delivery_id)
if set(metadata) != {'memo_id', 'question', 'memo_class', 'submitter'}:
raise ValueError('invalid notification metadata')
if any(not isinstance(v, str) or not v or len(v) > 4096 for v in metadata.values()):
raise ValueError('invalid notification metadata')
if not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._:-]{0,255}', metadata['memo_id']):
raise ValueError('invalid memo id')
status, receipt = self._request('POST', self.origin + '/informed-decision/notifications',
metadata, delivery_id)
# A queued relay receipt is pending, not successful central delivery.
if status not in (200, 201) or not isinstance(receipt, dict):
return False
try:
uuid.UUID(receipt['message_id'])
except (ValueError, TypeError, KeyError, AttributeError):
return False
return (receipt.get('delivery_id') == delivery_id and receipt.get('status') == 'accepted'
and receipt.get('authoritative_review') == 'signed-in-overview'
and all(receipt.get(k) == v for k, v in metadata.items()))
def resolution(self, method, url, payload):
suffix = '/resolve' if method == 'POST' else ''
pattern = r'/decisions/[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}' + suffix
if (method not in ('POST', 'GET') or not url.startswith(self.origin + '/decisions/')
or not re.fullmatch(pattern, url[len(self.origin):])):
raise ValueError('invalid hub resolution route')
material = json.dumps([method, url, payload], sort_keys=True, separators=(',', ':')).encode()
status, receipt = self._request(method, url, payload,
'infd-resolution:' + hashlib.sha256(material).hexdigest())
if 200 <= status < 300:
ident = url[len(self.origin + '/decisions/'):].split('/')[0]
if (status != 200 or not isinstance(receipt, dict) or receipt.get('id') != ident
or (method == 'POST' and (receipt.get('status') != 'resolved'
or any(receipt.get(k) != payload[k] for k in ('rationale', 'decided_by'))))):
return 503, {} # Queued or mismatched response is not a resolution receipt.
return status, receipt