betterwithage Perplexity Computer Agent commited on
Commit
af07b79
·
verified ·
1 Parent(s): b23387b

fix(warhacker): make shallow launch endpoint genuinely mode-aware + real per-run receipts

Browse files

Root cause: the self-contained /warhacker/launch/{problem} handler returned an IDENTICAL receipt for mode=nominal and mode=tamper (constant id, no real tamper test). It now DELEGATES to the REAL, mode-aware exhaustive-demo engine (szl_warhacker_demos): STL robustness rho, PolyCARP-style geofence, conformal interval, SHA-256 Merkle chain + Rekor-style inclusion proof, DSSE-signed breach event with the in-image ECDSA-P256 key. Receipt id is a real SHA-256 over the per-run DSSE PAE + chain hash + mode + decision, so it differs every run. Added chain_self (live-run own chain re-verified intact, NO tamper) to all 5 demos and a 'live chain' panel block + renderChainSelf() in pages/warhacker.html. Honesty labels preserved: cannonico=REAL TODAY; tychee/hangar2apps/cyber_rts/raven=ROADMAP (substrate real / vertical = sample data). Sovereign: zero CDN.

Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>

Files changed (3) hide show
  1. pages/warhacker.html +18 -0
  2. serve.py +72 -16
  3. szl_warhacker_demos.py +40 -5
pages/warhacker.html CHANGED
@@ -349,6 +349,8 @@
349
  '<ul class="tl" id="tl-'+p.key+'"><li class="empty">— run a demo to populate the timeline —</li></ul></div>'+
350
  '<div class="block" id="ctblock-'+p.key+'"><div class="block-h"><span class="pillc">catch tree</span> first failing condition auto-expanded</div>'+
351
  '<ul class="ct" id="ct-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
 
 
352
  '<div class="block" id="tamperblock-'+p.key+'"><div class="block-h"><span class="pillc">tamper test</span> one byte flipped → chain breaks</div>'+
353
  '<div id="tamper-'+p.key+'"><div class="empty">— run the tamper test to flip one byte and watch the signed chain break —</div></div></div>'+
354
  '<div class="block"><div class="block-h"><span class="pillc">formula-proof panel</span> exact formulas + honest status</div>'+
@@ -459,6 +461,20 @@
459
  return '<span class="b4">before: '+esc(b)+'</span><br><span class="af">after:&nbsp; '+pre+'<span class="hl">'+ch+'</span>'+post+'</span>';
460
  }
461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  function renderTamper(key, tt, problem){
463
  var box = $("tamper-"+key);
464
  if(!tt){ box.innerHTML = '<div class="empty">— no tamper data —</div>'; return false; }
@@ -538,6 +554,8 @@
538
  var anim = renderTimeline(key, d.timeline, true);
539
  // catch tree
540
  renderCatchTree(key, d.catch_tree, d.first_failing_node);
 
 
541
  // tamper test (always present — the negative test)
542
  var caught = renderTamper(key, d.tamper_test, key);
543
  // formula panel
 
349
  '<ul class="tl" id="tl-'+p.key+'"><li class="empty">— run a demo to populate the timeline —</li></ul></div>'+
350
  '<div class="block" id="ctblock-'+p.key+'"><div class="block-h"><span class="pillc">catch tree</span> first failing condition auto-expanded</div>'+
351
  '<ul class="ct" id="ct-'+p.key+'"><li class="empty">— run a demo —</li></ul></div>'+
352
+ '<div class="block" id="chainselfblock-'+p.key+'"><div class="block-h"><span class="pillc">live chain</span> this run\u2019s own signed chain — re-verified intact</div>'+
353
+ '<div id="chainself-'+p.key+'"><div class="empty">— run a demo to verify the live signed chain —</div></div></div>'+
354
  '<div class="block" id="tamperblock-'+p.key+'"><div class="block-h"><span class="pillc">tamper test</span> one byte flipped → chain breaks</div>'+
355
  '<div id="tamper-'+p.key+'"><div class="empty">— run the tamper test to flip one byte and watch the signed chain break —</div></div></div>'+
356
  '<div class="block"><div class="block-h"><span class="pillc">formula-proof panel</span> exact formulas + honest status</div>'+
 
461
  return '<span class="b4">before: '+esc(b)+'</span><br><span class="af">after:&nbsp; '+pre+'<span class="hl">'+ch+'</span>'+post+'</span>';
462
  }
463
 
464
+ function renderChainSelf(key, cs){
465
+ var box = $("chainself-"+key);
466
+ if(!box) return;
467
+ if(!cs){ box.innerHTML = ""; return; }
468
+ var intact = (cs.chain_intact===true) && (cs.merkle_root_matches===true);
469
+ box.innerHTML =
470
+ '<div class="tamper-grid">'+
471
+ '<div class="tamper-cell"><div class="tk">live-run chain intact</div><div class="tv '+(intact?"good":"bad")+'">'+esc(cs.chain_intact)+'</div></div>'+
472
+ '<div class="tamper-cell"><div class="tk">merkle root matches</div><div class="tv '+(cs.merkle_root_matches?"good":"bad")+'">'+esc(cs.merkle_root_matches)+'</div></div>'+
473
+ '<div class="tamper-cell"><div class="tk">committed merkle root</div><div class="tv">'+esc(String(cs.merkle_root_committed||"").slice(0,24))+'…</div></div>'+
474
+ '<div class="tamper-cell"><div class="tk">chain depth</div><div class="tv">'+esc(cs.depth)+'</div></div>'+
475
+ '</div>';
476
+ }
477
+
478
  function renderTamper(key, tt, problem){
479
  var box = $("tamper-"+key);
480
  if(!tt){ box.innerHTML = '<div class="empty">— no tamper data —</div>'; return false; }
 
554
  var anim = renderTimeline(key, d.timeline, true);
555
  // catch tree
556
  renderCatchTree(key, d.catch_tree, d.first_failing_node);
557
+ // live-run chain self-verification (this run's OWN chain, intact)
558
+ renderChainSelf(key, d.chain_self);
559
  // tamper test (always present — the negative test)
560
  var caught = renderTamper(key, d.tamper_test, key);
561
  // formula panel
serve.py CHANGED
@@ -3086,34 +3086,90 @@ try:
3086
  "slsa": "SLSA Build Level 2",
3087
  })
3088
 
 
 
 
 
 
 
3089
  @app.post("/api/a11oy/v1/warhacker/launch/{problem}")
3090
  @app.post("/v1/warhacker/launch/{problem}")
3091
  @app.get("/api/a11oy/v1/warhacker/launch/{problem}")
3092
  @app.get("/v1/warhacker/launch/{problem}")
3093
- async def _wh_launch_sc(problem: str):
 
 
 
 
 
 
 
 
 
 
3094
  d = _WH_BY_KEY.get(problem)
3095
- if not d:
 
3096
  return _SCJSON({"ok": False, "error": "unknown scenario", "problem": problem}, status_code=404)
3097
- lam = _wh_lambda(_WH_AXES)
3098
- gate = "pass" if lam >= 0.90 else "review"
3099
- payload = {"scenario": d["id"], "capability": d["capability"], "lambda": lam,
3100
- "gate": gate, "axes": _WH_AXES}
3101
- rid = "a11oy-rcpt-" + _wh_hl.sha256(
3102
- (d["key"] + ":" + _wh_json.dumps(payload, sort_keys=True)).encode()).hexdigest()[:16]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3103
  result = {
3104
  "ok": True, "self_contained": True, "capability": d["capability"],
3105
- "status": "ok", "http_code": 200,
3106
  "scenario": {"id": d["id"], "title": d["title"], "proves": d["proves"]},
3107
- "decision": {"lambda": lam, "gate": gate,
3108
- "note": "Lambda is Conjecture 1 \u2014 an advisory score, not a pass/fail oracle."},
3109
- "axes": _WH_AXES,
3110
- "receipt": {"receipt_id": rid, "signed": True,
 
 
 
 
 
 
 
 
 
 
3111
  "verify_at": "/api/a11oy/v1/receipt/export",
3112
- "public_key": "/cosign.pub"},
3113
- "honesty": "Deterministic in-image computation \u2014 no external service is called.",
 
 
 
 
 
 
3114
  }
3115
  # console renders d.organ + d.organ_response; provide neutral, self-contained shapes
3116
- return _SCJSON({"ok": True, "capability": d["capability"],
 
3117
  "organ": d["capability"], "organ_response": result,
3118
  "receipt": result["receipt"]})
3119
 
 
3086
  "slsa": "SLSA Build Level 2",
3087
  })
3088
 
3089
+ # Map the launch keys to the REAL exhaustive-demo engine keys (the demos
3090
+ # module uses 'cyber_rts' with an underscore).
3091
+ _WH_LAUNCH_TO_DEMO = {"cannonico": "cannonico", "tychee": "tychee",
3092
+ "hangar2apps": "hangar2apps", "cyber-rts": "cyber_rts",
3093
+ "cyber_rts": "cyber_rts", "raven": "raven"}
3094
+
3095
  @app.post("/api/a11oy/v1/warhacker/launch/{problem}")
3096
  @app.post("/v1/warhacker/launch/{problem}")
3097
  @app.get("/api/a11oy/v1/warhacker/launch/{problem}")
3098
  @app.get("/v1/warhacker/launch/{problem}")
3099
+ async def _wh_launch_sc(problem: str, request: Request):
3100
+ # ROOT-CAUSE FIX (2026-06-06): the previous self-contained launch handler
3101
+ # returned an IDENTICAL receipt for mode=nominal and mode=tamper (no real
3102
+ # tamper test). It now DELEGATES to the REAL, mode-aware exhaustive-demo
3103
+ # engine (szl_warhacker_demos), which computes STL robustness rho, the
3104
+ # PolyCARP-style geofence, the conformal interval, a SHA-256 Merkle chain
3105
+ # + Rekor-style inclusion proof, and DSSE-signs the breach event with the
3106
+ # in-image ECDSA-P256 key. nominal => AUTHORIZED + rho>=0 + intact chain;
3107
+ # tamper => UNAUTHORIZED + rho<0 + named crossed axis + chain/inclusion
3108
+ # FAIL. Receipt id is a real hash over the real per-run payload, so it
3109
+ # differs every run. Honest by construction.
3110
  d = _WH_BY_KEY.get(problem)
3111
+ demo_key = _WH_LAUNCH_TO_DEMO.get(problem)
3112
+ if not d or not demo_key:
3113
  return _SCJSON({"ok": False, "error": "unknown scenario", "problem": problem}, status_code=404)
3114
+ # mode from JSON body OR ?mode= query (GET-friendly)
3115
+ mode = None
3116
+ try:
3117
+ _b = await request.json()
3118
+ if isinstance(_b, dict):
3119
+ mode = _b.get("mode")
3120
+ except Exception:
3121
+ mode = None
3122
+ mode = (mode or request.query_params.get("mode") or "nominal").lower()
3123
+ if mode not in ("nominal", "tamper"):
3124
+ mode = "nominal"
3125
+ try:
3126
+ import szl_warhacker_demos as _whd_engine
3127
+ host = {"sign": _a11oy_sign_receipt,
3128
+ "verify": _a11oy_loop_verify if "_a11oy_loop_verify" in dir() else None}
3129
+ run = _whd_engine._DEMOS[demo_key](mode, host)
3130
+ except Exception as _de:
3131
+ return _SCJSON({"ok": False, "error": "engine error: %r" % _de,
3132
+ "problem": problem, "mode": mode}, status_code=500)
3133
+ authorized = bool(run.get("authorized"))
3134
+ ev = run.get("evaluation", {}) or {}
3135
+ sealed = run.get("sealed", {}) or {}
3136
+ # REAL per-run receipt id: hash over the signed envelope PAE + chain hash
3137
+ env = sealed.get("envelope", {}) or {}
3138
+ rid_basis = (str(env.get("_pae_sha256", "")) + "|" + str(sealed.get("chain_hash", ""))
3139
+ + "|" + str(run.get("mode")) + "|" + str(run.get("decision")))
3140
+ rid = "a11oy-rcpt-" + _wh_hl.sha256(rid_basis.encode()).hexdigest()[:16]
3141
+ gate = "pass" if authorized else "fail"
3142
  result = {
3143
  "ok": True, "self_contained": True, "capability": d["capability"],
3144
+ "status": "ok", "http_code": 200, "mode": mode,
3145
  "scenario": {"id": d["id"], "title": d["title"], "proves": d["proves"]},
3146
+ "decision": {"verdict": run.get("decision"), "authorized": authorized, "gate": gate,
3147
+ "stl_robustness_rho": ev.get("stl_robustness_rho"),
3148
+ "stl_binding_axis": ev.get("stl_binding_axis"),
3149
+ "first_failing_axis": run.get("first_failing_node"),
3150
+ "note": "Lambda is Conjecture 1 \u2014 an advisory score, not a pass/fail oracle. "
3151
+ "The conjunctive GATE is P2 gate-soundness PROVEN."},
3152
+ "real_or_roadmap": run.get("real_or_roadmap"),
3153
+ "headline": run.get("headline"),
3154
+ "chain": {"merkle_root": (run.get("chain", {}) or {}).get("merkle_root")
3155
+ or sealed.get("merkle_root"),
3156
+ "depth": (run.get("chain", {}) or {}).get("depth")},
3157
+ "chain_self_verification": run.get("chain_self"),
3158
+ "tamper_test": run.get("tamper_test"),
3159
+ "receipt": {"receipt_id": rid, "signed": bool(sealed.get("signed")),
3160
  "verify_at": "/api/a11oy/v1/receipt/export",
3161
+ "public_key": "/cosign.pub",
3162
+ "dsse": {"payloadType": env.get("payloadType"),
3163
+ "pae_sha256": env.get("_pae_sha256"),
3164
+ "signed": bool(env.get("signed"))}},
3165
+ "timeline": run.get("timeline"),
3166
+ "catch_tree": run.get("catch_tree"),
3167
+ "first_failing_node": run.get("first_failing_node"),
3168
+ "honesty": run.get("honesty"),
3169
  }
3170
  # console renders d.organ + d.organ_response; provide neutral, self-contained shapes
3171
+ return _SCJSON({"ok": True, "capability": d["capability"], "mode": mode,
3172
+ "authorized": authorized,
3173
  "organ": d["capability"], "organ_response": result,
3174
  "receipt": result["receipt"]})
3175
 
szl_warhacker_demos.py CHANGED
@@ -543,7 +543,8 @@ def _demo_cannonico(mode, host):
543
  formulas = _cann_formula_panel()
544
 
545
  # TAMPER test: flip one byte in the sealed breach event -> chain + inclusion break
546
- tamper = chain.verify(tamper_seq=0)
 
547
 
548
  return {
549
  "ok": True, "problem": "cannonico", "mode": mode,
@@ -563,6 +564,12 @@ def _demo_cannonico(mode, host):
563
  "sealed": sealed,
564
  "chain": {"depth": len(chain.entries), "merkle_root": chain.root(),
565
  "entries": chain.entries},
 
 
 
 
 
 
566
  "tamper_test": tamper,
567
  "formula_panel": formulas,
568
  "honesty": ("Every number is computed in-image at request time: STL robustness rho, the "
@@ -694,7 +701,8 @@ def _demo_tychee(mode, host):
694
  for d in digests]
695
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
696
 
697
- tamper = chain.verify(tamper_seq=0)
 
698
  return {
699
  "ok": True, "problem": "tychee", "mode": mode,
700
  "title": "TYCHEE - reusable air-gap satellite GSW deploy stack (UDS bundle + Zarf)",
@@ -712,6 +720,12 @@ def _demo_tychee(mode, host):
712
  "catch_tree": catch_tree,
713
  "first_failing_node": (first_fail["node"] if first_fail else None),
714
  "sealed": signed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
 
 
 
 
 
 
715
  "tamper_test": tamper,
716
  "formula_panel": [
717
  {"formula": "SHA-256 OCI content digest + Merkle bundle root",
@@ -853,7 +867,8 @@ def _demo_hangar(mode, host):
853
 
854
  catch_tree = [{"node": k, "label": name_of[k], "pass": v} for k, v in axis_vals.items()]
855
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
856
- tamper = chain.verify(tamper_seq=0)
 
857
  return {
858
  "ok": True, "problem": "hangar2apps", "mode": mode,
859
  "title": "HANGAR2APPS - unified deployment health readiness + auditable workflow",
@@ -871,6 +886,12 @@ def _demo_hangar(mode, host):
871
  "timeline": tl.as_list(),
872
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
873
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
 
 
 
 
 
 
874
  "tamper_test": tamper,
875
  "formula_panel": [
876
  {"formula": "Lambda N-axis conjunctive readiness gate", "role": "MR vs NON-DEPLOYABLE",
@@ -1054,7 +1075,8 @@ def _demo_cyber_rts(mode, host):
1054
  ]
1055
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
1056
  safe = not collision_risk and not accuracy_warn
1057
- tamper = chain.verify(tamper_seq=0)
 
1058
  return {
1059
  "ok": True, "problem": "cyber_rts", "mode": mode,
1060
  "title": "CYBER RTS - ingest any trajectory/orbit (TLE/OEM) -> operational context",
@@ -1074,6 +1096,12 @@ def _demo_cyber_rts(mode, host):
1074
  "timeline": tl.as_list(),
1075
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
1076
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
 
 
 
 
 
 
1077
  "tamper_test": tamper,
1078
  "formula_panel": [
1079
  {"formula": "SGP4 mean-element propagation", "role": "TLE -> ECI state vector",
@@ -1197,7 +1225,8 @@ def _demo_raven(mode, host):
1197
  {"node": "mission_authorized", "label": "workload authorized for this node's mission", "pass": mission_authorized},
1198
  ]
1199
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
1200
- tamper = chain.verify(tamper_seq=0)
 
1201
  return {
1202
  "ok": True, "problem": "raven", "mode": mode,
1203
  "title": "RAVEN - deploy + cryptographically authorize a workload at a disconnected edge",
@@ -1215,6 +1244,12 @@ def _demo_raven(mode, host):
1215
  "timeline": tl.as_list(),
1216
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
1217
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
 
 
 
 
 
 
1218
  "tamper_test": tamper,
1219
  "formula_panel": [
1220
  {"formula": "Keylime TPM 2.0 PCR quote", "role": "hardware root of trust at the edge",
 
543
  formulas = _cann_formula_panel()
544
 
545
  # TAMPER test: flip one byte in the sealed breach event -> chain + inclusion break
546
+ chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
547
+ tamper = chain.verify(tamper_seq=0) # explicit negative test: flip 1 byte -> break
548
 
549
  return {
550
  "ok": True, "problem": "cannonico", "mode": mode,
 
564
  "sealed": sealed,
565
  "chain": {"depth": len(chain.entries), "merkle_root": chain.root(),
566
  "entries": chain.entries},
567
+ "chain_self": {"chain_intact": chain_self["chain_intact"],
568
+ "merkle_root_committed": chain_self["merkle_root_committed"],
569
+ "merkle_root_matches": chain_self["merkle_root_matches"],
570
+ "depth": chain_self["depth"],
571
+ "note": "The live run's own signed chain, re-verified with NO tampering: "
572
+ "intact. The 'tamper_test' below is the always-on negative test."},
573
  "tamper_test": tamper,
574
  "formula_panel": formulas,
575
  "honesty": ("Every number is computed in-image at request time: STL robustness rho, the "
 
701
  for d in digests]
702
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
703
 
704
+ chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
705
+ tamper = chain.verify(tamper_seq=0) # explicit negative test: flip 1 byte -> break
706
  return {
707
  "ok": True, "problem": "tychee", "mode": mode,
708
  "title": "TYCHEE - reusable air-gap satellite GSW deploy stack (UDS bundle + Zarf)",
 
720
  "catch_tree": catch_tree,
721
  "first_failing_node": (first_fail["node"] if first_fail else None),
722
  "sealed": signed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
723
+ "chain_self": {"chain_intact": chain_self["chain_intact"],
724
+ "merkle_root_committed": chain_self["merkle_root_committed"],
725
+ "merkle_root_matches": chain_self["merkle_root_matches"],
726
+ "depth": chain_self["depth"],
727
+ "note": "The live run's own signed chain, re-verified with NO tampering: "
728
+ "intact. The 'tamper_test' below is the always-on negative test."},
729
  "tamper_test": tamper,
730
  "formula_panel": [
731
  {"formula": "SHA-256 OCI content digest + Merkle bundle root",
 
867
 
868
  catch_tree = [{"node": k, "label": name_of[k], "pass": v} for k, v in axis_vals.items()]
869
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
870
+ chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
871
+ tamper = chain.verify(tamper_seq=0) # explicit negative test: flip 1 byte -> break
872
  return {
873
  "ok": True, "problem": "hangar2apps", "mode": mode,
874
  "title": "HANGAR2APPS - unified deployment health readiness + auditable workflow",
 
886
  "timeline": tl.as_list(),
887
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
888
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
889
+ "chain_self": {"chain_intact": chain_self["chain_intact"],
890
+ "merkle_root_committed": chain_self["merkle_root_committed"],
891
+ "merkle_root_matches": chain_self["merkle_root_matches"],
892
+ "depth": chain_self["depth"],
893
+ "note": "The live run's own signed chain, re-verified with NO tampering: "
894
+ "intact. The 'tamper_test' below is the always-on negative test."},
895
  "tamper_test": tamper,
896
  "formula_panel": [
897
  {"formula": "Lambda N-axis conjunctive readiness gate", "role": "MR vs NON-DEPLOYABLE",
 
1075
  ]
1076
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
1077
  safe = not collision_risk and not accuracy_warn
1078
+ chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
1079
+ tamper = chain.verify(tamper_seq=0) # explicit negative test: flip 1 byte -> break
1080
  return {
1081
  "ok": True, "problem": "cyber_rts", "mode": mode,
1082
  "title": "CYBER RTS - ingest any trajectory/orbit (TLE/OEM) -> operational context",
 
1096
  "timeline": tl.as_list(),
1097
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
1098
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
1099
+ "chain_self": {"chain_intact": chain_self["chain_intact"],
1100
+ "merkle_root_committed": chain_self["merkle_root_committed"],
1101
+ "merkle_root_matches": chain_self["merkle_root_matches"],
1102
+ "depth": chain_self["depth"],
1103
+ "note": "The live run's own signed chain, re-verified with NO tampering: "
1104
+ "intact. The 'tamper_test' below is the always-on negative test."},
1105
  "tamper_test": tamper,
1106
  "formula_panel": [
1107
  {"formula": "SGP4 mean-element propagation", "role": "TLE -> ECI state vector",
 
1225
  {"node": "mission_authorized", "label": "workload authorized for this node's mission", "pass": mission_authorized},
1226
  ]
1227
  first_fail = next((c for c in catch_tree if not c["pass"]), None)
1228
+ chain_self = chain.verify() # NO tamper: the live run's own chain, proven intact
1229
+ tamper = chain.verify(tamper_seq=0) # explicit negative test: flip 1 byte -> break
1230
  return {
1231
  "ok": True, "problem": "raven", "mode": mode,
1232
  "title": "RAVEN - deploy + cryptographically authorize a workload at a disconnected edge",
 
1244
  "timeline": tl.as_list(),
1245
  "catch_tree": catch_tree, "first_failing_node": (first_fail["node"] if first_fail else None),
1246
  "sealed": sealed, "chain": {"depth": len(chain.entries), "entries": chain.entries},
1247
+ "chain_self": {"chain_intact": chain_self["chain_intact"],
1248
+ "merkle_root_committed": chain_self["merkle_root_committed"],
1249
+ "merkle_root_matches": chain_self["merkle_root_matches"],
1250
+ "depth": chain_self["depth"],
1251
+ "note": "The live run's own signed chain, re-verified with NO tampering: "
1252
+ "intact. The 'tamper_test' below is the always-on negative test."},
1253
  "tamper_test": tamper,
1254
  "formula_panel": [
1255
  {"formula": "Keylime TPM 2.0 PCR quote", "role": "hardware root of trust at the edge",