Implement private Q2 signal contract and durable reference receiver
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
parent
31884baf4e
commit
cecef79f31
14 changed files with 655 additions and 20 deletions
44
scripts/platform_event.py
Normal file
44
scripts/platform_event.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Translate S3-owned state classifications without redefining their meaning."""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from receiver import read_json, instant, STATES
|
||||
|
||||
|
||||
def translate(report, contract):
|
||||
if (set(report) != {'schema', 'cluster_uid', 'evaluated_at', 'signals', 'transport',
|
||||
'guarantees', 'threshold_status', 'healthy'}
|
||||
or report['schema'] != 'railiance-platform.assurance-signal.v1'
|
||||
or report['cluster_uid'] != 'a553c742-0115-43d4-99a4-a5ca56fe0786'
|
||||
or report['transport'] != 'unmonitored'
|
||||
or report['guarantees'] != 'unsupported'
|
||||
or report['threshold_status'] != 'local-diagnostic-only'
|
||||
or set(report['signals']) != set(contract['signals'])):
|
||||
raise ValueError('unexpected platform report')
|
||||
instant(report['evaluated_at'])
|
||||
states = {}
|
||||
for name, value in report['signals'].items():
|
||||
if (set(value) != {'state', 'owner'} or value['state'] not in STATES
|
||||
or value['owner'] not in ('railiance-platform', 'rapp-postgres')):
|
||||
raise ValueError('unexpected signal')
|
||||
states[name] = value['state']
|
||||
if type(report['healthy']) is not bool or report['healthy'] != all(s == 'healthy' for s in states.values()):
|
||||
raise ValueError('inconsistent summary')
|
||||
return dict(schema='railiance-telemetry.signal.v1', id=str(uuid.uuid4()),
|
||||
stream=contract['stream'], producer=contract['producer'],
|
||||
observed_at=report['evaluated_at'], states=states)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--contract', required=True, type=Path)
|
||||
p.add_argument('report', type=Path)
|
||||
a = p.parse_args()
|
||||
try:
|
||||
print(json.dumps(translate(read_json(a.report), read_json(a.contract))))
|
||||
except (OSError, ValueError, KeyError, TypeError, AttributeError):
|
||||
print(json.dumps({'status': 'rejected', 'error': 'invalid-platform-report'}))
|
||||
sys.exit(2)
|
||||
189
scripts/receiver.py
Normal file
189
scripts/receiver.py
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Private reference receiver. No listener, credentials or notification sending."""
|
||||
import argparse
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
STATES = {'healthy', 'stale', 'missing', 'unavailable', 'failed'}
|
||||
MAX_BYTES = 32768
|
||||
|
||||
|
||||
def instant(value):
|
||||
parsed = datetime.fromisoformat(value.replace('Z', '+00:00'))
|
||||
if parsed.tzinfo is None:
|
||||
raise ValueError('timezone required')
|
||||
return parsed.astimezone(timezone.utc)
|
||||
|
||||
|
||||
def strict_json(raw):
|
||||
def pairs(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
if key in result:
|
||||
raise ValueError('duplicate field')
|
||||
result[key] = value
|
||||
return result
|
||||
if len(raw) > MAX_BYTES:
|
||||
raise ValueError('payload too large')
|
||||
return json.loads(raw, object_pairs_hook=pairs)
|
||||
|
||||
|
||||
def read_json(path):
|
||||
with path.open('rb') as stream:
|
||||
return strict_json(stream.read(MAX_BYTES + 1))
|
||||
|
||||
|
||||
def contract_check(c):
|
||||
if set(c) != {'schema', 'stream', 'producer', 'recipient', 'signals',
|
||||
'max_event_age_seconds', 'heartbeat_seconds', 'retention_days'}:
|
||||
raise ValueError('contract fields')
|
||||
if c['schema'] != 'railiance-telemetry.stream.v1':
|
||||
raise ValueError('contract version')
|
||||
for key in ('stream', 'producer', 'recipient'):
|
||||
if not isinstance(c[key], str) or not c[key] or len(c[key]) > 100:
|
||||
raise ValueError('identity missing')
|
||||
if (not isinstance(c['signals'], list) or not c['signals']
|
||||
or not all(isinstance(s, str) and 0 < len(s) <= 100 for s in c['signals'])
|
||||
or len(c['signals']) != len(set(c['signals']))):
|
||||
raise ValueError('signal inventory')
|
||||
for key in ('max_event_age_seconds', 'heartbeat_seconds', 'retention_days'):
|
||||
if type(c[key]) is not int or not 0 < c[key] <= 31536000:
|
||||
raise ValueError('invalid budget')
|
||||
|
||||
|
||||
class Receiver:
|
||||
def __init__(self, database, contract):
|
||||
contract_check(contract)
|
||||
self.contract = contract
|
||||
self.db = sqlite3.connect(database, timeout=10)
|
||||
self.db.executescript('''
|
||||
CREATE TABLE IF NOT EXISTS binding (id INTEGER PRIMARY KEY, digest TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id TEXT PRIMARY KEY, digest TEXT NOT NULL, emitted REAL NOT NULL,
|
||||
received REAL NOT NULL, states TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS notices (
|
||||
id INTEGER PRIMARY KEY, kind TEXT NOT NULL, created REAL NOT NULL,
|
||||
event_id TEXT, acknowledged REAL);
|
||||
''')
|
||||
digest = hashlib.sha256(json.dumps(contract, sort_keys=True).encode()).hexdigest()
|
||||
with self.db:
|
||||
self.db.execute('INSERT OR IGNORE INTO binding VALUES (1, ?)', (digest,))
|
||||
if self.db.execute('SELECT digest FROM binding WHERE id=1').fetchone()[0] != digest:
|
||||
self.db.close()
|
||||
raise ValueError('contract drift: explicit migration required')
|
||||
|
||||
def close(self):
|
||||
self.db.close()
|
||||
|
||||
def ingest(self, event, now):
|
||||
c = self.contract
|
||||
if set(event) != {'schema', 'id', 'stream', 'producer', 'observed_at', 'states'}:
|
||||
raise ValueError('event fields')
|
||||
if (event['schema'] != 'railiance-telemetry.signal.v1'
|
||||
or event['stream'] != c['stream'] or event['producer'] != c['producer']
|
||||
or str(uuid.UUID(event['id'])) != event['id']):
|
||||
raise ValueError('event identity')
|
||||
states = event['states']
|
||||
if (not isinstance(states, dict) or set(states) != set(c['signals'])
|
||||
or not all(isinstance(s, str) and s in STATES for s in states.values())):
|
||||
raise ValueError('signal scope')
|
||||
emitted = instant(event['observed_at']).timestamp()
|
||||
age = now.timestamp() - emitted
|
||||
if not 0 <= age <= c['max_event_age_seconds']:
|
||||
raise ValueError('stale or future event')
|
||||
digest = hashlib.sha256(json.dumps(event, sort_keys=True).encode()).hexdigest()
|
||||
with self.db:
|
||||
self.db.execute('BEGIN IMMEDIATE')
|
||||
existing = self.db.execute('SELECT digest FROM events WHERE id=?', (event['id'],)).fetchone()
|
||||
if existing:
|
||||
if existing[0] != digest:
|
||||
raise ValueError('event id collision')
|
||||
return {'status': 'duplicate', 'id': event['id']}
|
||||
latest = self.db.execute('SELECT MAX(emitted) FROM events').fetchone()[0]
|
||||
if latest is not None and emitted <= latest:
|
||||
raise ValueError('out of order event')
|
||||
if self.db.execute('SELECT COUNT(*) FROM events').fetchone()[0] >= 10000:
|
||||
raise ValueError('capacity reached; explicit retention required')
|
||||
self.db.execute('INSERT INTO events VALUES (?, ?, ?, ?, ?)',
|
||||
(event['id'], digest, emitted, now.timestamp(), json.dumps(states, sort_keys=True)))
|
||||
self.db.execute('INSERT INTO notices(kind, created, event_id) VALUES (?, ?, ?)',
|
||||
('healthy' if all(s == 'healthy' for s in states.values()) else 'unhealthy',
|
||||
now.timestamp(), event['id']))
|
||||
return {'status': 'accepted', 'id': event['id'], 'delivery': 'local-inbox-only'}
|
||||
|
||||
def check(self, now):
|
||||
"""Must be invoked by an independent scheduler; absence is not self-executing."""
|
||||
with self.db:
|
||||
self.db.execute('BEGIN IMMEDIATE')
|
||||
last = self.db.execute('SELECT MAX(emitted) FROM events').fetchone()[0]
|
||||
if last is not None and now.timestamp() < last:
|
||||
raise ValueError('clock moved backwards')
|
||||
absent = last is None or now.timestamp() - last > self.contract['heartbeat_seconds']
|
||||
previous = self.db.execute('SELECT kind FROM notices ORDER BY id DESC LIMIT 1').fetchone()
|
||||
if absent and (previous is None or previous[0] != 'missing-emission'):
|
||||
self.db.execute('INSERT INTO notices(kind, created) VALUES (?, ?)',
|
||||
('missing-emission', now.timestamp()))
|
||||
return {'state': 'missing-emission' if absent else 'receiving',
|
||||
'recipient': self.contract['recipient'], 'delivery': 'local-inbox-only'}
|
||||
|
||||
def inbox(self):
|
||||
rows = self.db.execute('''SELECT n.id, n.kind, n.created, n.event_id, e.states
|
||||
FROM notices n LEFT JOIN events e ON e.id=n.event_id
|
||||
WHERE n.acknowledged IS NULL ORDER BY n.id''').fetchall()
|
||||
return {'recipient': self.contract['recipient'], 'delivery': 'local-inbox-only',
|
||||
'notices': [dict(id=r[0], kind=r[1], created_at=datetime.fromtimestamp(r[2], timezone.utc).isoformat(),
|
||||
event_id=r[3], states=json.loads(r[4]) if r[4] else None) for r in rows]}
|
||||
|
||||
def ack(self, notice_id, now):
|
||||
with self.db:
|
||||
updated = self.db.execute('UPDATE notices SET acknowledged=? WHERE id=? AND acknowledged IS NULL',
|
||||
(now.timestamp(), notice_id)).rowcount
|
||||
return {'acknowledged': bool(updated), 'id': notice_id}
|
||||
|
||||
def prune(self, now):
|
||||
cutoff = now.timestamp() - self.contract['retention_days'] * 86400
|
||||
with self.db:
|
||||
self.db.execute('BEGIN IMMEDIATE')
|
||||
# Keep the latest observation and notice as replay/absence anchors.
|
||||
self.db.execute('''DELETE FROM notices WHERE created < ? AND acknowledged IS NOT NULL
|
||||
AND id != (SELECT MAX(id) FROM notices)''', (cutoff,))
|
||||
deleted = self.db.execute('''DELETE FROM events WHERE received < ?
|
||||
AND emitted != (SELECT MAX(emitted) FROM events)
|
||||
AND id NOT IN (SELECT event_id FROM notices WHERE event_id IS NOT NULL)''', (cutoff,)).rowcount
|
||||
return {'events_pruned': deleted}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('--contract', required=True, type=Path)
|
||||
p.add_argument('--database', required=True, type=Path)
|
||||
sub = p.add_subparsers(dest='command', required=True)
|
||||
sub.add_parser('ingest').add_argument('event', type=Path)
|
||||
for name in ('check', 'inbox', 'prune'):
|
||||
sub.add_parser(name)
|
||||
sub.add_parser('ack').add_argument('notice_id', type=int)
|
||||
a = p.parse_args()
|
||||
receiver = None
|
||||
try:
|
||||
receiver = Receiver(a.database, read_json(a.contract))
|
||||
now = datetime.now(timezone.utc)
|
||||
if a.command == 'ingest': result = receiver.ingest(read_json(a.event), now)
|
||||
elif a.command == 'inbox': result = receiver.inbox()
|
||||
elif a.command == 'ack': result = receiver.ack(a.notice_id, now)
|
||||
else: result = getattr(receiver, a.command)(now)
|
||||
print(json.dumps(result))
|
||||
return 0
|
||||
except (ValueError, TypeError, KeyError, AttributeError, OSError, sqlite3.Error):
|
||||
print(json.dumps({'status': 'rejected', 'error': 'invalid-input-or-unavailable'}))
|
||||
return 2
|
||||
finally:
|
||||
if receiver is not None: receiver.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue