rezabarkhordary commited on
Commit
8063d41
·
verified ·
1 Parent(s): bd10a9b

Create sovereign_freeze_bridge.py

Browse files
Files changed (1) hide show
  1. sovereign_freeze_bridge.py +492 -0
sovereign_freeze_bridge.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # sovereign_freeze_bridge.py
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import uuid
8
+ from dataclasses import dataclass, asdict
9
+ from datetime import datetime, timezone
10
+ from typing import Any, Dict, Optional, List, Tuple
11
+
12
+
13
+ FREEZE_CASES_FILE = os.getenv("SOV_FREEZE_CASES_FILE", "sovereign_freeze_cases.json")
14
+ FREEZE_INDEX_FILE = os.getenv("SOV_FREEZE_INDEX_FILE", "sovereign_freeze_index.json")
15
+ FREEZE_AUDIT_FILE = os.getenv("SOV_FREEZE_AUDIT_FILE", "sovereign_freeze_audit.jsonl")
16
+
17
+
18
+ def _utc_now() -> str:
19
+ return datetime.now(timezone.utc).isoformat()
20
+
21
+
22
+ def _safe_read_json(path: str, default: Any) -> Any:
23
+ try:
24
+ if not os.path.exists(path):
25
+ return default
26
+ with open(path, "r", encoding="utf-8") as f:
27
+ return json.load(f)
28
+ except Exception:
29
+ return default
30
+
31
+
32
+ def _safe_write_json(path: str, data: Any) -> None:
33
+ directory = os.path.dirname(path)
34
+ if directory:
35
+ os.makedirs(directory, exist_ok=True)
36
+ tmp = f"{path}.tmp"
37
+ with open(tmp, "w", encoding="utf-8") as f:
38
+ json.dump(data, f, ensure_ascii=False, indent=2, sort_keys=True)
39
+ os.replace(tmp, path)
40
+
41
+
42
+ def _append_jsonl(path: str, row: Dict[str, Any]) -> None:
43
+ directory = os.path.dirname(path)
44
+ if directory:
45
+ os.makedirs(directory, exist_ok=True)
46
+ with open(path, "a", encoding="utf-8") as f:
47
+ f.write(json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n")
48
+
49
+
50
+ def _norm_text(v: Any) -> str:
51
+ return str(v or "").strip()
52
+
53
+
54
+ def _norm_tags(v: Any) -> List[str]:
55
+ if isinstance(v, list):
56
+ return [str(x).strip().lower() for x in v if str(x).strip()]
57
+ if isinstance(v, str):
58
+ return [x.strip().lower() for x in v.split(",") if x.strip()]
59
+ return []
60
+
61
+
62
+ def _bucket_key(payload: Dict[str, Any]) -> str:
63
+ engine = _norm_text(payload.get("engine") or payload.get("engine_name") or "unknown_engine")
64
+ parent = _norm_text(payload.get("parent_model") or payload.get("parent_model_id") or payload.get("agent_id") or "unknown_parent")
65
+ version = _norm_text(payload.get("model_version") or payload.get("build_id") or "unknown_version")
66
+ env = _norm_text(payload.get("deployment_env") or payload.get("environment") or "unspecified")
67
+ tags = ",".join(sorted(_norm_tags(payload.get("data_tags"))))
68
+ return f"{engine}|{parent}|{version}|{env}|{tags}"
69
+
70
+
71
+ @dataclass
72
+ class FreezeCase:
73
+ freeze_case_id: str
74
+ created_at: str
75
+ updated_at: str
76
+ status: str # frozen / released / escalated_block
77
+ engine: str
78
+ parent_model: str
79
+ model_version: str
80
+ deployment_env: str
81
+ data_tags: List[str]
82
+ risk_level: str
83
+ decision_id: str
84
+ reason: str
85
+ source: str # authority_gate / cognition / zero_layer / runtime
86
+ authority_decision: str # FREEZE / BLOCK / ALLOW
87
+ freeze_token: str
88
+ review_required: bool
89
+ released_at: Optional[str] = None
90
+ released_by: Optional[str] = None
91
+ release_note: Optional[str] = None
92
+ escalated_at: Optional[str] = None
93
+ escalated_by: Optional[str] = None
94
+ escalation_note: Optional[str] = None
95
+ audit_trail: Optional[List[Dict[str, Any]]] = None
96
+
97
+
98
+ class SovereignFreezeBridge:
99
+ """
100
+ Bridges FREEZE decisions into a bank-grade freeze case lifecycle.
101
+
102
+ What it does:
103
+ - when decision == FREEZE -> create a persistent freeze case
104
+ - prevents execution while case is still frozen
105
+ - if case gets released -> execution may continue
106
+ - if case gets escalated -> treat as BLOCK
107
+ - writes dedicated freeze audit trail
108
+ """
109
+
110
+ def __init__(
111
+ self,
112
+ cases_file: str = FREEZE_CASES_FILE,
113
+ index_file: str = FREEZE_INDEX_FILE,
114
+ audit_file: str = FREEZE_AUDIT_FILE,
115
+ ) -> None:
116
+ self.cases_file = cases_file
117
+ self.index_file = index_file
118
+ self.audit_file = audit_file
119
+
120
+ # -------------------------
121
+ # storage
122
+ # -------------------------
123
+ def _load_cases(self) -> Dict[str, Dict[str, Any]]:
124
+ obj = _safe_read_json(self.cases_file, {})
125
+ return obj if isinstance(obj, dict) else {}
126
+
127
+ def _save_cases(self, obj: Dict[str, Dict[str, Any]]) -> None:
128
+ _safe_write_json(self.cases_file, obj)
129
+
130
+ def _load_index(self) -> Dict[str, str]:
131
+ obj = _safe_read_json(self.index_file, {})
132
+ return obj if isinstance(obj, dict) else {}
133
+
134
+ def _save_index(self, obj: Dict[str, str]) -> None:
135
+ _safe_write_json(self.index_file, obj)
136
+
137
+ def _audit(self, event_type: str, freeze_case_id: str, payload: Dict[str, Any]) -> None:
138
+ _append_jsonl(
139
+ self.audit_file,
140
+ {
141
+ "ts": _utc_now(),
142
+ "event_type": event_type,
143
+ "freeze_case_id": freeze_case_id,
144
+ "payload": payload,
145
+ },
146
+ )
147
+
148
+ # -------------------------
149
+ # helpers
150
+ # -------------------------
151
+ def _extract_reason(self, decision_bundle: Dict[str, Any]) -> str:
152
+ reason = decision_bundle.get("reason")
153
+ if isinstance(reason, str) and reason.strip():
154
+ return reason.strip()
155
+
156
+ reasons = decision_bundle.get("reasons")
157
+ if isinstance(reasons, list) and reasons:
158
+ return " | ".join(str(x) for x in reasons[:6])
159
+
160
+ authority = decision_bundle.get("authority_gate") or decision_bundle.get("authority") or {}
161
+ if isinstance(authority, dict):
162
+ r = authority.get("reason")
163
+ if isinstance(r, str) and r.strip():
164
+ return r.strip()
165
+
166
+ return "freeze_without_explicit_reason"
167
+
168
+ def _extract_decision(self, decision_bundle: Dict[str, Any]) -> str:
169
+ for key in ("decision", "action", "outcome", "authority_decision"):
170
+ val = decision_bundle.get(key)
171
+ if isinstance(val, str) and val.strip():
172
+ return val.strip().upper()
173
+ auth = decision_bundle.get("authority_gate") or {}
174
+ if isinstance(auth, dict):
175
+ val = auth.get("decision")
176
+ if isinstance(val, str) and val.strip():
177
+ return val.strip().upper()
178
+ return "UNKNOWN"
179
+
180
+ def _extract_source(self, decision_bundle: Dict[str, Any]) -> str:
181
+ if "authority_gate" in decision_bundle:
182
+ return "authority_gate"
183
+ if "zero_layer" in decision_bundle:
184
+ return "zero_layer"
185
+ if "cognition" in decision_bundle:
186
+ return "cognition"
187
+ return "runtime"
188
+
189
+ def _extract_decision_id(self, decision_bundle: Dict[str, Any]) -> str:
190
+ for key in ("decision_id", "event_id", "audit_id"):
191
+ v = decision_bundle.get(key)
192
+ if isinstance(v, str) and v.strip():
193
+ return v.strip()
194
+ auth = decision_bundle.get("authority_gate") or {}
195
+ if isinstance(auth, dict):
196
+ v = auth.get("decision_id")
197
+ if isinstance(v, str) and v.strip():
198
+ return v.strip()
199
+ return str(uuid.uuid4())
200
+
201
+ def _append_case_event(self, case_obj: Dict[str, Any], event_type: str, actor: str, note: str, extra: Optional[Dict[str, Any]] = None) -> None:
202
+ trail = case_obj.get("audit_trail")
203
+ if not isinstance(trail, list):
204
+ trail = []
205
+ trail.append(
206
+ {
207
+ "ts": _utc_now(),
208
+ "event_type": event_type,
209
+ "actor": actor,
210
+ "note": note,
211
+ "extra": extra or {},
212
+ }
213
+ )
214
+ case_obj["audit_trail"] = trail
215
+ case_obj["updated_at"] = _utc_now()
216
+
217
+ # -------------------------
218
+ # public operations
219
+ # -------------------------
220
+ def create_or_link_freeze_case(
221
+ self,
222
+ payload: Dict[str, Any],
223
+ decision_bundle: Dict[str, Any],
224
+ ) -> Dict[str, Any]:
225
+ decision = self._extract_decision(decision_bundle)
226
+ if decision != "FREEZE":
227
+ return {
228
+ "freeze_created": False,
229
+ "freeze_linked": False,
230
+ "freeze_case_id": None,
231
+ "status": None,
232
+ "reason": "decision_is_not_freeze",
233
+ }
234
+
235
+ cases = self._load_cases()
236
+ index = self._load_index()
237
+
238
+ bucket = _bucket_key(payload)
239
+ existing_case_id = index.get(bucket)
240
+
241
+ if existing_case_id and existing_case_id in cases:
242
+ case_obj = cases[existing_case_id]
243
+ status = case_obj.get("status", "frozen")
244
+ self._append_case_event(
245
+ case_obj,
246
+ event_type="freeze_reaffirmed",
247
+ actor="system",
248
+ note="Freeze re-asserted for existing case.",
249
+ extra={"decision_id": self._extract_decision_id(decision_bundle)},
250
+ )
251
+ cases[existing_case_id] = case_obj
252
+ self._save_cases(cases)
253
+ self._audit("freeze_reaffirmed", existing_case_id, {"bucket": bucket, "status": status})
254
+ return {
255
+ "freeze_created": False,
256
+ "freeze_linked": True,
257
+ "freeze_case_id": existing_case_id,
258
+ "status": status,
259
+ "reason": "existing_case_reused",
260
+ }
261
+
262
+ freeze_case_id = str(uuid.uuid4())
263
+ freeze_token = str(uuid.uuid4())
264
+
265
+ case = FreezeCase(
266
+ freeze_case_id=freeze_case_id,
267
+ created_at=_utc_now(),
268
+ updated_at=_utc_now(),
269
+ status="frozen",
270
+ engine=_norm_text(payload.get("engine") or payload.get("engine_name") or "unknown_engine"),
271
+ parent_model=_norm_text(payload.get("parent_model") or payload.get("parent_model_id") or payload.get("agent_id") or "unknown_parent"),
272
+ model_version=_norm_text(payload.get("model_version") or payload.get("build_id") or "unknown_version"),
273
+ deployment_env=_norm_text(payload.get("deployment_env") or payload.get("environment") or "unspecified"),
274
+ data_tags=_norm_tags(payload.get("data_tags")),
275
+ risk_level=_norm_text(payload.get("risk_level") or "unspecified").lower(),
276
+ decision_id=self._extract_decision_id(decision_bundle),
277
+ reason=self._extract_reason(decision_bundle),
278
+ source=self._extract_source(decision_bundle),
279
+ authority_decision="FREEZE",
280
+ freeze_token=freeze_token,
281
+ review_required=True,
282
+ audit_trail=[],
283
+ )
284
+
285
+ case_obj = asdict(case)
286
+ self._append_case_event(
287
+ case_obj,
288
+ event_type="freeze_created",
289
+ actor="system",
290
+ note="Freeze case created from runtime decision.",
291
+ extra={"bucket": bucket},
292
+ )
293
+
294
+ cases[freeze_case_id] = case_obj
295
+ index[bucket] = freeze_case_id
296
+
297
+ self._save_cases(cases)
298
+ self._save_index(index)
299
+ self._audit("freeze_created", freeze_case_id, {"bucket": bucket, "engine": case.engine})
300
+
301
+ return {
302
+ "freeze_created": True,
303
+ "freeze_linked": False,
304
+ "freeze_case_id": freeze_case_id,
305
+ "freeze_token": freeze_token,
306
+ "status": "frozen",
307
+ "reason": case.reason,
308
+ }
309
+
310
+ def get_case(self, freeze_case_id: str) -> Optional[Dict[str, Any]]:
311
+ cases = self._load_cases()
312
+ obj = cases.get(freeze_case_id)
313
+ return obj if isinstance(obj, dict) else None
314
+
315
+ def get_case_by_payload(self, payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
316
+ index = self._load_index()
317
+ cases = self._load_cases()
318
+ case_id = index.get(_bucket_key(payload))
319
+ if not case_id:
320
+ return None
321
+ obj = cases.get(case_id)
322
+ return obj if isinstance(obj, dict) else None
323
+
324
+ def effective_state(self, payload: Dict[str, Any]) -> Dict[str, Any]:
325
+ """
326
+ Returns the effective enforcement state for the payload:
327
+ - no_case
328
+ - frozen
329
+ - released
330
+ - escalated_block
331
+ """
332
+ case_obj = self.get_case_by_payload(payload)
333
+ if not case_obj:
334
+ return {
335
+ "state": "no_case",
336
+ "allow_execution": True,
337
+ "freeze_case_id": None,
338
+ "reason": "no_freeze_case",
339
+ }
340
+
341
+ status = case_obj.get("status", "frozen")
342
+ case_id = case_obj.get("freeze_case_id")
343
+
344
+ if status == "released":
345
+ return {
346
+ "state": "released",
347
+ "allow_execution": True,
348
+ "freeze_case_id": case_id,
349
+ "reason": "freeze_released",
350
+ }
351
+
352
+ if status == "escalated_block":
353
+ return {
354
+ "state": "escalated_block",
355
+ "allow_execution": False,
356
+ "freeze_case_id": case_id,
357
+ "reason": "freeze_escalated_to_block",
358
+ }
359
+
360
+ return {
361
+ "state": "frozen",
362
+ "allow_execution": False,
363
+ "freeze_case_id": case_id,
364
+ "reason": "freeze_active",
365
+ }
366
+
367
+ def can_execute(self, payload: Dict[str, Any]) -> Tuple[bool, Dict[str, Any]]:
368
+ state = self.effective_state(payload)
369
+ return bool(state.get("allow_execution")), state
370
+
371
+ def release_case(self, freeze_case_id: str, released_by: str, note: str = "") -> Dict[str, Any]:
372
+ cases = self._load_cases()
373
+ obj = cases.get(freeze_case_id)
374
+ if not isinstance(obj, dict):
375
+ return {"ok": False, "error": "freeze_case_not_found"}
376
+
377
+ status = obj.get("status")
378
+ if status == "escalated_block":
379
+ return {"ok": False, "error": "case_already_escalated_block"}
380
+ if status == "released":
381
+ return {"ok": True, "freeze_case_id": freeze_case_id, "status": "released", "idempotent": True}
382
+
383
+ obj["status"] = "released"
384
+ obj["released_at"] = _utc_now()
385
+ obj["released_by"] = _norm_text(released_by) or "unknown_reviewer"
386
+ obj["release_note"] = _norm_text(note)
387
+
388
+ self._append_case_event(
389
+ obj,
390
+ event_type="freeze_released",
391
+ actor=obj["released_by"],
392
+ note=obj["release_note"] or "Freeze released.",
393
+ )
394
+
395
+ cases[freeze_case_id] = obj
396
+ self._save_cases(cases)
397
+ self._audit("freeze_released", freeze_case_id, {"released_by": obj["released_by"]})
398
+
399
+ return {
400
+ "ok": True,
401
+ "freeze_case_id": freeze_case_id,
402
+ "status": "released",
403
+ "released_at": obj["released_at"],
404
+ }
405
+
406
+ def escalate_case(self, freeze_case_id: str, escalated_by: str, note: str = "") -> Dict[str, Any]:
407
+ cases = self._load_cases()
408
+ obj = cases.get(freeze_case_id)
409
+ if not isinstance(obj, dict):
410
+ return {"ok": False, "error": "freeze_case_not_found"}
411
+
412
+ if obj.get("status") == "escalated_block":
413
+ return {"ok": True, "freeze_case_id": freeze_case_id, "status": "escalated_block", "idempotent": True}
414
+
415
+ obj["status"] = "escalated_block"
416
+ obj["authority_decision"] = "BLOCK"
417
+ obj["escalated_at"] = _utc_now()
418
+ obj["escalated_by"] = _norm_text(escalated_by) or "unknown_reviewer"
419
+ obj["escalation_note"] = _norm_text(note)
420
+
421
+ self._append_case_event(
422
+ obj,
423
+ event_type="freeze_escalated_block",
424
+ actor=obj["escalated_by"],
425
+ note=obj["escalation_note"] or "Freeze escalated to block.",
426
+ )
427
+
428
+ cases[freeze_case_id] = obj
429
+ self._save_cases(cases)
430
+ self._audit("freeze_escalated_block", freeze_case_id, {"escalated_by": obj["escalated_by"]})
431
+
432
+ return {
433
+ "ok": True,
434
+ "freeze_case_id": freeze_case_id,
435
+ "status": "escalated_block",
436
+ "escalated_at": obj["escalated_at"],
437
+ }
438
+
439
+ def register_decision(self, payload: Dict[str, Any], decision_bundle: Dict[str, Any]) -> Dict[str, Any]:
440
+ """
441
+ Main bridge entrypoint.
442
+ Call this right after authority/runtime decision is made.
443
+ """
444
+ decision = self._extract_decision(decision_bundle)
445
+
446
+ if decision == "FREEZE":
447
+ out = self.create_or_link_freeze_case(payload, decision_bundle)
448
+ return {
449
+ "bridge_status": "freeze_registered",
450
+ "decision": "FREEZE",
451
+ "freeze": out,
452
+ }
453
+
454
+ # For non-freeze decisions, still check if an existing freeze case blocks execution
455
+ exec_ok, state = self.can_execute(payload)
456
+
457
+ if not exec_ok:
458
+ forced = "BLOCK" if state.get("state") == "escalated_block" else "FREEZE"
459
+ return {
460
+ "bridge_status": "existing_freeze_overrides_execution",
461
+ "decision": forced,
462
+ "freeze": state,
463
+ }
464
+
465
+ return {
466
+ "bridge_status": "no_freeze_intervention",
467
+ "decision": decision,
468
+ "freeze": state,
469
+ }
470
+
471
+
472
+ if __name__ == "__main__":
473
+ bridge = SovereignFreezeBridge()
474
+
475
+ sample_payload = {
476
+ "engine": "banking_runtime_v1",
477
+ "parent_model": "agent_alpha",
478
+ "model_version": "v1",
479
+ "deployment_env": "production",
480
+ "data_tags": ["pii", "payments", "customer_chat"],
481
+ "risk_level": "high",
482
+ }
483
+
484
+ sample_decision = {
485
+ "decision_id": str(uuid.uuid4()),
486
+ "decision": "FREEZE",
487
+ "reason": "suspicious_cross_account_instruction",
488
+ "authority_gate": {"decision": "FREEZE", "reason": "authority_freeze"},
489
+ }
490
+
491
+ print(json.dumps(bridge.register_decision(sample_payload, sample_decision), indent=2, ensure_ascii=False))
492
+