betterwithage commited on
Commit
fb3d58e
·
verified ·
1 Parent(s): ace20d8

feat(bridge): add /drone-cyber SOC tab + /api/sentra/v1/drone-cyber/* (ADDITIVE; pulls Killinchu fleet live; 2-person Yuyay quarantine, cyber-only). 43/43 routes + 6 base sigs + 8 gates + IP-HOLD #45 untouched. v11 LOCKED. — Yachay

Browse files
Files changed (2) hide show
  1. sentra_drone_cyber.py +475 -0
  2. serve.py +13 -61
sentra_drone_cyber.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ sentra_drone_cyber.py — Sentra ↔ Killinchu cyber bridge (ADDITIVE module).
3
+
4
+ Registered from serve.py behind a single try/except, BEFORE the /{path:path}
5
+ catch-all. Adds the /drone-cyber server-rendered SOC tab + backing API under
6
+ /api/sentra/v1/drone-cyber/*. Pulls the Killinchu fleet LIVE.
7
+
8
+ HARD DISCIPLINE (Doctrine v11 LOCKED):
9
+ * ADDITIVE only — touches nothing existing. Sentra 43/43 routes, 6 base threat
10
+ sigs, 8 immune gates, Wire B/E/F/G all untouched. IP-HOLD #45 untouched.
11
+ * v11 LOCKED numbers preserved: 749 declarations / 14 unique axioms /
12
+ 163 sorries / 13-axis yuyay_v3 / Lambda floor 0.90.
13
+ * Quarantine = CYBER isolation (RTL + link isolation under signed Sentra cert),
14
+ NEVER kinetic, OWN-FLEET ONLY (honors Killinchu legal boundary / CFAA / ITAR /
15
+ Wassenaar). 2-person Yuyay gate + cross-flagship halt-if-mismatch.
16
+ * Khipu receipt on every cross-flagship event. RUWAY is the only ledger writer;
17
+ here we emit receipt *envelopes* and cross_link them (in-memory mirror).
18
+ * DSSE signature is PLACEHOLDER until Sigstore CI lands. SLSA L1 (honest).
19
+
20
+ — Yachay, 2026-06-01.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import hashlib
25
+ import json
26
+ import os
27
+ import time
28
+ import urllib.request
29
+ import urllib.error
30
+ from datetime import datetime, timezone
31
+
32
+ from fastapi import Request
33
+ from fastapi.responses import JSONResponse, HTMLResponse
34
+
35
+ # --------------------------------------------------------------------------- #
36
+ # Constants
37
+ # --------------------------------------------------------------------------- #
38
+ KILLINCHU_BASE = os.environ.get(
39
+ "KILLINCHU_BASE", "https://szlholdings-killinchu.hf.space"
40
+ )
41
+ KIL_API = KILLINCHU_BASE + "/api/killinchu/v1"
42
+ LAMBDA_FLOOR = 0.90
43
+ DOCTRINE = "v11"
44
+ SIGNATURE_PLACEHOLDER = (
45
+ "PLACEHOLDER — Sigstore CI signing not yet wired into CI per Doctrine v11"
46
+ )
47
+ AXIS_NAMES = [
48
+ "soundness", "calibration", "robustness", "provenance", "consent",
49
+ "reversibility", "transparency", "fairness", "containment",
50
+ "attestation", "freshness", "authority", "auditability",
51
+ ]
52
+
53
+ # 6 base Sentra threat sigs (the existing THREAT_SIGNATURES corpus) + 10 drone sigs.
54
+ BASE_SIGS = ["DROP TABLE", "rm -rf", "<script", "eval(", "subprocess", "../../etc"]
55
+ DRONE_SIGS = [
56
+ {"sig_id": "DSIG-01", "name": "secure-boot-attestation-failure", "tripwire": "T11", "class": "tamper"},
57
+ {"sig_id": "DSIG-02", "name": "firmware-merkle-mismatch", "tripwire": "T12", "class": "tamper"},
58
+ {"sig_id": "DSIG-03", "name": "mavlink-anomaly", "tripwire": "T13", "class": "intrusion"},
59
+ {"sig_id": "DSIG-04", "name": "rf-fingerprint-deviation", "tripwire": "T14", "class": "intrusion"},
60
+ {"sig_id": "DSIG-05", "name": "accelerometer-imu-spoof", "tripwire": "T15", "class": "tamper"},
61
+ {"sig_id": "DSIG-06", "name": "gps-spoof", "tripwire": "T16", "class": "intrusion"},
62
+ {"sig_id": "DSIG-07", "name": "unexpected-ota-attempt", "tripwire": "T17", "class": "tamper"},
63
+ {"sig_id": "DSIG-08", "name": "geofence-violation", "tripwire": "T18", "class": "anomaly"},
64
+ {"sig_id": "DSIG-09", "name": "mission-deviation", "tripwire": "T19", "class": "anomaly"},
65
+ {"sig_id": "DSIG-10", "name": "unauthorized-mavlink-command", "tripwire": "T20", "class": "intrusion"},
66
+ ]
67
+ TRIPWIRE_TO_SIG = {d["tripwire"]: d for d in DRONE_SIGS}
68
+
69
+ # In-memory webhook-pushed event buffer (honest degrade when Killinchu unreachable).
70
+ _PUSHED_EVENTS: list[dict] = []
71
+ # In-memory cross-flagship receipt mirror (RUWAY is the real writer; this mirrors).
72
+ _BRIDGE_RECEIPTS: list[dict] = []
73
+
74
+
75
+ # --------------------------------------------------------------------------- #
76
+ # Helpers
77
+ # --------------------------------------------------------------------------- #
78
+ def _now() -> str:
79
+ return datetime.now(timezone.utc).isoformat()
80
+
81
+
82
+ def _sha(obj) -> str:
83
+ return hashlib.sha256(
84
+ json.dumps(obj, sort_keys=True, separators=(",", ":")).encode()
85
+ ).hexdigest()
86
+
87
+
88
+ def _kil_get(path: str, timeout: float = 8.0):
89
+ """GET a Killinchu API path. Returns (ok, json_or_none)."""
90
+ url = KIL_API + path
91
+ try:
92
+ req = urllib.request.Request(url, headers={"accept": "application/json"})
93
+ with urllib.request.urlopen(req, timeout=timeout) as r:
94
+ return True, json.loads(r.read().decode())
95
+ except Exception:
96
+ return False, None
97
+
98
+
99
+ def _emit_bridge_receipt(kind: str, payload: dict, cross_link: dict | None = None) -> dict:
100
+ """Emit a Sentra-side cross-flagship Khipu receipt envelope (in-memory mirror).
101
+ Hash-chained; carries flagship_origin + cross_link per UNIFIED_KHIPU_DAG."""
102
+ prev = _BRIDGE_RECEIPTS[-1]["this_hash"] if _BRIDGE_RECEIPTS else ""
103
+ body = {
104
+ "schema": "szl.sentra.receipt/v1",
105
+ "kind": kind,
106
+ "wire": "F",
107
+ "flagship_origin": "sentra",
108
+ "cross_link": cross_link,
109
+ "payload": payload,
110
+ "prev_hash": prev,
111
+ "ts_utc": _now(),
112
+ "doctrine": DOCTRINE,
113
+ }
114
+ body["this_hash"] = "sha256:" + _sha({"b": body, "p": prev})
115
+ body["signature"] = SIGNATURE_PLACEHOLDER
116
+ body["slsa_level"] = "L1 (honest)"
117
+ _BRIDGE_RECEIPTS.append(body)
118
+ return body
119
+
120
+
121
+ def _lambda_aggregate(axis_scores) -> float:
122
+ """13-axis geometric mean (canonical yuyay_v3). Mirrors Killinchu math."""
123
+ xs = [max(1e-9, float(x)) for x in (axis_scores or [])][:13]
124
+ if len(xs) < 13:
125
+ xs += [0.9] * (13 - len(xs))
126
+ prod = 1.0
127
+ for x in xs:
128
+ prod *= x
129
+ return prod ** (1.0 / 13.0)
130
+
131
+
132
+ def _integrity_score(integrity: dict) -> float:
133
+ fired = int(integrity.get("fired_count", 0)) if integrity else 0
134
+ return round(max(0.0, 1.0 - fired / 10.0), 3)
135
+
136
+
137
+ def _last_tamper(integrity: dict):
138
+ if not integrity:
139
+ return None
140
+ fired = integrity.get("fired") or []
141
+ if not fired:
142
+ return None
143
+ tid = fired[-1]
144
+ tw = next((t for t in integrity.get("tripwires", []) if t.get("id") == tid), None)
145
+ return {"tripwire": tid, "name": (tw or {}).get("name", "")}
146
+
147
+
148
+ # --------------------------------------------------------------------------- #
149
+ # Page HTML (server-rendered, dark SOC theme — mirrors /doctrine-guard pattern)
150
+ # --------------------------------------------------------------------------- #
151
+ _DRONE_CYBER_HTML = (
152
+ '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8">'
153
+ '<meta name="viewport" content="width=device-width, initial-scale=1">'
154
+ '<title>Sentra — Drone Cyber (Killinchu fleet · Doctrine v11)</title>'
155
+ '<style>:root{--bg:#0b0e14;--card:#121826;--ink:#e8eef7;--mut:#8aa0bf;--acc:#5ad1c0;--red:#ff6b6b;--amb:#e0c060;--line:#243149}'
156
+ '*{box-sizing:border-box}body{margin:0;font:15px/1.55 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg);color:var(--ink)}'
157
+ '.wrap{max-width:1180px;margin:0 auto;padding:28px 20px 80px}h1{font-size:25px;margin:0 0 2px}'
158
+ 'h2{font-size:17px;margin:26px 0 10px;border-bottom:1px solid var(--line);padding-bottom:6px}'
159
+ '.sub{color:var(--mut);margin:0 0 14px}.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:10px 0}'
160
+ 'table{width:100%;border-collapse:collapse;font-size:13px}th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line);vertical-align:top}'
161
+ 'th{color:var(--mut);font-weight:600}code{background:#0a1626;padding:1px 5px;border-radius:5px;color:var(--acc);font-size:12px}'
162
+ 'a{color:var(--acc);text-decoration:none}a:hover{text-decoration:underline}.note{color:var(--mut);font-size:12px}'
163
+ '.b{display:inline-block;padding:1px 8px;border-radius:999px;font-size:11px;font-weight:700}'
164
+ '.green{background:#0f3a2e;color:#5ad1c0}.amber{background:#3a2f0f;color:#e0c060}.red{background:#3a0f14;color:#ff8a8a}'
165
+ 'button{background:#13314a;color:#bfe;border:1px solid var(--line);border-radius:8px;padding:6px 11px;cursor:pointer;font-size:12px}'
166
+ 'pre{background:#0a1626;border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;font-size:12px}'
167
+ '.pill{font-size:11px;padding:1px 7px;border-radius:999px;border:1px solid var(--line);color:var(--mut)}'
168
+ '.foot{margin-top:34px;color:var(--mut);font-size:12px;border-top:1px solid var(--line);padding-top:14px}</style></head>'
169
+ '<body><div class="wrap">'
170
+ '<nav class="note"><a href="/">home</a> · <a href="/brain">/brain</a> · <a href="/doctrine-guard">/doctrine-guard</a> · '
171
+ '<a href="/upgrades">/upgrades</a> · <a href="https://szlholdings-killinchu.hf.space" target="_blank" rel="noopener">Killinchu</a> · '
172
+ '<a href="https://szlholdings-a11oy.hf.space/mesh" target="_blank" rel="noopener">a11oy /mesh</a></nav>'
173
+ '<h1>Sentra — Drone Cyber</h1>'
174
+ '<p class="sub">One SOC pane: physical airspace <b>+</b> own-fleet drone cyber posture. '
175
+ 'Fleet pulled <b>live</b> from Killinchu · Doctrine v11 (749/14/163, 13-axis yuyay_v3, Λ floor 0.90) · '
176
+ 'a11oy-orchestrated · <span id="kstat" class="pill">Killinchu: …</span></p>'
177
+ '<h2>1 · Fleet integrity</h2><div class="card"><table id="fleet">'
178
+ '<tr><th>Drone</th><th>Model</th><th>Side</th><th>Firmware</th><th>Integrity</th><th>Last tamper</th><th>Verdict</th><th></th></tr>'
179
+ '<tr><td colspan="8" class="note">loading live fleet…</td></tr></table></div>'
180
+ '<h2>2 · Threat timeline (last 30d)</h2><div class="card">'
181
+ '<button onclick="loadEvents()">refresh</button> '
182
+ '<span class="note">tamper · anomaly · intrusion — each carries its Khipu hash</span>'
183
+ '<table id="events"><tr><th>raised</th><th>drone</th><th>class</th><th>tripwire</th><th>sig</th><th>sev</th><th>khipu</th></tr>'
184
+ '<tr><td colspan="7" class="note">loading…</td></tr></table></div>'
185
+ '<h2>3 · Signatures (6 base + 10 drone = 16)</h2><div class="card"><table id="sigs">'
186
+ '<tr><th>sig</th><th>name</th><th>tripwire</th><th>class</th></tr></table></div>'
187
+ '<h2>4 · Drill-down + Quarantine</h2><div class="card">'
188
+ '<input id="did" placeholder="drone id (e.g. mq9)" style="background:#0a1626;color:#bfe;border:1px solid var(--line);border-radius:8px;padding:6px 9px">'
189
+ ' <button onclick="drill()">drill-down</button> '
190
+ '<button onclick="quarantine()">quarantine (2-person, CYBER only)</button>'
191
+ '<p class="note">Quarantine = <b>cyber isolation</b> (RTL + command/telemetry link isolation under a signed Sentra cert). '
192
+ '<b>NOT kinetic.</b> Own-fleet only. Requires 2 distinct approvers + cross-flagship Yuyay-13 (halt-if-mismatch).</p>'
193
+ '<pre id="out">// result</pre></div>'
194
+ '<div class="foot">Fleet + events fetched LIVE from Killinchu; on unreachable, the tab shows the last webhook-pushed events '
195
+ '(honest degrade — never fabricated rows). Integrity scores derive from the T11–T20 scan; twin telemetry is a deterministic '
196
+ 'demonstration model (production streams live MAVLink/DICE). Khipu signatures are <b>DSSE PLACEHOLDER</b> until Sigstore CI '
197
+ 'lands; SLSA <b>L1 (honest)</b>. Doctrine v11 LOCKED numbers surfaced unchanged. ADDITIVE — Sentra 43/43 routes + 6 base sigs '
198
+ '+ 8 gates untouched; IP-HOLD #45 untouched. ZERO BANDAID. — Yachay, 2026-06-01.</div>'
199
+ '<script>'
200
+ 'const A="/api/sentra/v1/drone-cyber";'
201
+ 'function cls(s){return s>=0.9?"green":(s>=0.5?"amber":"red");}'
202
+ 'async function loadFleet(){try{const r=await fetch(A+"/fleet");const d=await r.json();'
203
+ 'document.getElementById("kstat").textContent="Killinchu: "+(d.killinchu_reachable?"● up":"○ down (degraded)");'
204
+ 'const t=document.getElementById("fleet");t.innerHTML="<tr><th>Drone</th><th>Model</th><th>Side</th><th>Firmware</th><th>Integrity</th><th>Last tamper</th><th>Verdict</th><th></th></tr>";'
205
+ 'd.fleet.forEach(f=>{const lt=f.last_tamper_flag?(f.last_tamper_flag.tripwire+" "+f.last_tamper_flag.name):"—";'
206
+ 'const vb=f.verdict==="ATTESTED-CLEAN"?"green":"red";'
207
+ 't.innerHTML+=`<tr><td><code>${f.drone_id}</code></td><td>${f.model||""}</td><td>${f.side||""}</td><td>${f.firmware_version||""}</td>`+'
208
+ '`<td><span class="b ${cls(f.integrity_score)}">${f.integrity_score}</span></td><td>${lt}</td>`+'
209
+ '`<td><span class="b ${vb}">${f.verdict||""}</span></td><td><button onclick="document.getElementById(\'did\').value=\'${f.drone_id}\';drill()">drill</button></td></tr>`;});'
210
+ '}catch(e){document.getElementById("kstat").textContent="Killinchu: error";}}'
211
+ 'async function loadEvents(){try{const r=await fetch(A+"/events?window_days=30");const d=await r.json();'
212
+ 'const t=document.getElementById("events");t.innerHTML="<tr><th>raised</th><th>drone</th><th>class</th><th>tripwire</th><th>sig</th><th>sev</th><th>khipu</th></tr>";'
213
+ 'if(!d.events.length){t.innerHTML+=\'<tr><td colspan="7" class="note">no events in window</td></tr>\';}'
214
+ 'd.events.forEach(e=>{const sv=e.severity==="halt"||e.severity==="critical"?"red":(e.severity==="high"||e.severity==="warn"?"amber":"green");'
215
+ 't.innerHTML+=`<tr><td>${(e.raised_at||"").slice(0,19)}</td><td><code>${e.drone_id}</code></td><td>${e.class||""}</td>`+'
216
+ '`<td>${e.tripwire||""}</td><td>${e.sentra_signature||e.sig||""}</td><td><span class="b ${sv}">${e.severity||""}</span></td>`+'
217
+ '`<td><code>${(e.khipu_hash||"").slice(0,10)}…</code></td></tr>`;});'
218
+ '}catch(e){}}'
219
+ 'async function loadSigs(){try{const r=await fetch(A+"/signatures");const d=await r.json();'
220
+ 'const t=document.getElementById("sigs");d.base.forEach(b=>{t.innerHTML+=`<tr><td><code>BASE</code></td><td>${b}</td><td>—</td><td>base-immune</td></tr>`;});'
221
+ 'd.drone.forEach(s=>{t.innerHTML+=`<tr><td><code>${s.sig_id}</code></td><td>${s.name}</td><td>${s.tripwire}</td><td>${s.class}</td></tr>`;});'
222
+ '}catch(e){}}'
223
+ 'async function drill(){const id=document.getElementById("did").value.trim();if(!id)return;const o=document.getElementById("out");o.textContent="…";'
224
+ 'try{const r=await fetch(A+"/drone/"+encodeURIComponent(id));o.textContent=JSON.stringify(await r.json(),null,2);}catch(e){o.textContent="error: "+e;}}'
225
+ 'async function quarantine(){const id=document.getElementById("did").value.trim();if(!id)return;const o=document.getElementById("out");'
226
+ 'const a1=prompt("Approver 1 id (e.g. soc-analyst-jane):");if(!a1)return;const a2=prompt("Approver 2 id (must differ):");if(!a2)return;'
227
+ 'o.textContent="…";try{const r=await fetch(A+"/quarantine",{method:"POST",headers:{"content-type":"application/json"},'
228
+ 'body:JSON.stringify({drone_id:id,reason:"operator-initiated cyber isolation",approvers:[a1,a2],'
229
+ 'axis_scores:[0.96,0.97,0.93,0.92,0.95,0.96,0.93,0.91,0.96,0.95,0.93,0.92,0.94]})});o.textContent=JSON.stringify(await r.json(),null,2);}catch(e){o.textContent="error: "+e;}}'
230
+ 'loadFleet();loadEvents();loadSigs();'
231
+ '</script></div></body></html>'
232
+ )
233
+
234
+
235
+ # --------------------------------------------------------------------------- #
236
+ # Route registration (called once from serve.py, BEFORE the catch-all)
237
+ # --------------------------------------------------------------------------- #
238
+ def register_drone_cyber(app):
239
+ """Register the /drone-cyber tab + /api/sentra/v1/drone-cyber/* endpoints.
240
+ ADDITIVE; safe to call inside try/except. Does not shadow existing routes."""
241
+
242
+ @app.get("/drone-cyber", response_class=HTMLResponse, tags=["drone-cyber"])
243
+ def drone_cyber_page():
244
+ return HTMLResponse(content=_DRONE_CYBER_HTML)
245
+
246
+ @app.get("/api/sentra/v1/drone-cyber/healthz", tags=["drone-cyber"])
247
+ def dc_healthz():
248
+ ok, _ = _kil_get("/drones/database", timeout=6.0)
249
+ return JSONResponse({
250
+ "ok": True, "service": "sentra.drone-cyber", "bridge": "sentra<->killinchu",
251
+ "killinchu_base": KILLINCHU_BASE, "killinchu_reachable": ok,
252
+ "lambda_floor": LAMBDA_FLOOR, "doctrine": DOCTRINE,
253
+ "signature": SIGNATURE_PLACEHOLDER, "slsa_level": "L1 (honest)",
254
+ "note": "ADDITIVE bridge; canonical /api/sentra/healthz unchanged.",
255
+ })
256
+
257
+ @app.get("/api/sentra/v1/drone-cyber/signatures", tags=["drone-cyber"])
258
+ def dc_signatures():
259
+ return JSONResponse({
260
+ "ok": True, "base": BASE_SIGS, "drone": DRONE_SIGS,
261
+ "counts": {"base": len(BASE_SIGS), "drone": len(DRONE_SIGS),
262
+ "total": len(BASE_SIGS) + len(DRONE_SIGS)},
263
+ "note": "6 base Sentra threat sigs (unchanged) + 10 drone sigs mapped 1:1 to HUKLLA T11-T20.",
264
+ "doctrine": DOCTRINE,
265
+ })
266
+
267
+ @app.get("/api/sentra/v1/drone-cyber/fleet", tags=["drone-cyber"])
268
+ def dc_fleet(limit: int = 12):
269
+ ok, db = _kil_get("/drones/database", timeout=8.0)
270
+ if not ok or not db:
271
+ return JSONResponse({
272
+ "ok": True, "source": "killinchu", "killinchu_reachable": False,
273
+ "count": 0, "fleet": [], "doctrine": DOCTRINE,
274
+ "honesty": "Killinchu unreachable — degraded honestly, no fabricated rows.",
275
+ })
276
+ drones = (db.get("drones") or [])[:max(1, min(limit, 53))]
277
+ fleet = []
278
+ for d in drones:
279
+ did = d.get("id")
280
+ _, twin = _kil_get(f"/drones/{did}/twin", timeout=6.0)
281
+ _, integ = _kil_get(f"/drones/{did}/integrity", timeout=6.0)
282
+ fw = ((twin or {}).get("telemetry") or {}).get("firmware_version", "")
283
+ verdict = (integ or {}).get("verdict", "UNKNOWN")
284
+ fleet.append({
285
+ "drone_id": did, "model": d.get("model", ""), "side": d.get("side", ""),
286
+ "last_seen": _now(), "firmware_version": fw,
287
+ "integrity_score": _integrity_score(integ),
288
+ "last_tamper_flag": _last_tamper(integ),
289
+ "geo_cluster": "solo", "verdict": verdict,
290
+ })
291
+ return JSONResponse({
292
+ "ok": True, "source": "killinchu", "killinchu_reachable": True,
293
+ "count": len(fleet), "fleet": fleet, "doctrine": DOCTRINE,
294
+ "honesty": ("Fleet fetched LIVE from Killinchu; integrity_score derived from "
295
+ "T11-T20 scan. DSSE signature PLACEHOLDER; SLSA L1 (honest)."),
296
+ })
297
+
298
+ @app.get("/api/sentra/v1/drone-cyber/events", tags=["drone-cyber"])
299
+ def dc_events(window_days: int = 30, filter: str = "tamper,anomaly,intrusion", limit: int = 25):
300
+ wanted = {x.strip() for x in filter.split(",") if x.strip()}
301
+ events = []
302
+ # (i) pulled live from Killinchu integrity scans
303
+ ok, db = _kil_get("/drones/database", timeout=8.0)
304
+ reachable = bool(ok and db)
305
+ if reachable:
306
+ for d in (db.get("drones") or [])[:limit]:
307
+ did = d.get("id")
308
+ _, integ = _kil_get(f"/drones/{did}/integrity", timeout=6.0)
309
+ if not integ:
310
+ continue
311
+ for tid in (integ.get("fired") or []):
312
+ sig = TRIPWIRE_TO_SIG.get(tid, {})
313
+ tw = next((t for t in integ.get("tripwires", []) if t.get("id") == tid), {})
314
+ klass = sig.get("class", "anomaly")
315
+ if wanted and klass not in wanted:
316
+ continue
317
+ score = float(tw.get("score", 0.5))
318
+ events.append({
319
+ "event_id": f"kc-{did}-{tid}",
320
+ "drone_id": did, "class": klass, "tripwire": tid,
321
+ "signal": sig.get("name", tw.get("name", "")),
322
+ "sentra_signature": sig.get("sig_id", ""),
323
+ "severity": "halt" if score >= 0.9 else ("warn" if score >= 0.6 else "info"),
324
+ "confidence": round(score, 3),
325
+ "raised_at": _now(),
326
+ "khipu_hash": _sha({"d": did, "t": tid}),
327
+ "flagship_origin": "killinchu",
328
+ "cross_link": {"to_flagship": "sentra"},
329
+ })
330
+ # (ii) webhook-pushed events (honest degrade source)
331
+ for e in _PUSHED_EVENTS[-limit:]:
332
+ if not wanted or e.get("class") in wanted:
333
+ events.append(e)
334
+ events.sort(key=lambda e: e.get("raised_at", ""), reverse=True)
335
+ return JSONResponse({
336
+ "ok": True, "window_days": window_days, "filter": sorted(wanted),
337
+ "killinchu_reachable": reachable, "count": len(events),
338
+ "events": events[:limit], "doctrine": DOCTRINE,
339
+ })
340
+
341
+ @app.post("/api/sentra/v1/drone-cyber/events/ingest", tags=["drone-cyber"])
342
+ async def dc_ingest(request: Request):
343
+ """Webhook target for Killinchu integrity events (binding b).
344
+ Normalizes to canonical szl.integrity.event/v1 + mirrors a Khipu receipt."""
345
+ try:
346
+ body = await request.json()
347
+ except Exception:
348
+ body = {}
349
+ tid = (body.get("tripwire") or {}).get("id") or body.get("tripwire") or ""
350
+ sig = TRIPWIRE_TO_SIG.get(tid, {})
351
+ evt = {
352
+ "event_id": body.get("event_id", f"push-{int(time.time()*1000)}"),
353
+ "drone_id": (body.get("drone") or {}).get("id") or body.get("drone_id", ""),
354
+ "class": sig.get("class", "anomaly"),
355
+ "tripwire": tid, "signal": sig.get("name", ""),
356
+ "sentra_signature": sig.get("sig_id", body.get("sentra_signature", "")),
357
+ "severity": body.get("severity", "info"),
358
+ "confidence": body.get("confidence", 0.0),
359
+ "raised_at": body.get("emitted_at", _now()),
360
+ "khipu_hash": (body.get("khipu") or {}).get("this_hash", _sha(body)),
361
+ "flagship_origin": "killinchu",
362
+ "cross_link": {"to_flagship": "sentra", "event_id": body.get("event_id", "")},
363
+ }
364
+ _PUSHED_EVENTS.append(evt)
365
+ rcpt = _emit_bridge_receipt(
366
+ "drone.cyber.event.ingested", evt,
367
+ cross_link={"to_flagship": "killinchu", "event_id": evt["event_id"]},
368
+ )
369
+ return JSONResponse({"ok": True, "ingested": evt["event_id"],
370
+ "sentra_receipt": rcpt["this_hash"], "doctrine": DOCTRINE})
371
+
372
+ @app.get("/api/sentra/v1/drone-cyber/drone/{drone_id}", tags=["drone-cyber"])
373
+ def dc_drone(drone_id: str):
374
+ _, twin = _kil_get(f"/drones/{drone_id}/twin", timeout=8.0)
375
+ _, integ = _kil_get(f"/drones/{drone_id}/integrity", timeout=8.0)
376
+ if not twin and not integ:
377
+ return JSONResponse({"ok": False, "drone_id": drone_id,
378
+ "error": "drone not found / Killinchu unreachable",
379
+ "doctrine": DOCTRINE}, status_code=404)
380
+ sig_matches = []
381
+ for tw in (integ or {}).get("tripwires", []):
382
+ tid = tw.get("id")
383
+ sig = TRIPWIRE_TO_SIG.get(tid)
384
+ if not sig:
385
+ continue
386
+ sig_matches.append({
387
+ "sig_id": sig["sig_id"], "name": sig["name"], "tripwire": tid,
388
+ "matched": tw.get("status") == "FIRED",
389
+ "confidence": round(float(tw.get("score", 0.0)), 3),
390
+ "evidence": {"detect": tw.get("detect", ""), "evidence": tw.get("evidence", "")},
391
+ })
392
+ tel = (twin or {}).get("telemetry") or {}
393
+ return JSONResponse({
394
+ "ok": True, "drone_id": drone_id,
395
+ "twin": {"model": (twin or {}).get("drone", {}).get("model", ""),
396
+ "firmware_version": tel.get("firmware_version", ""),
397
+ "integrity_score": _integrity_score(integ),
398
+ "verdict": (integ or {}).get("verdict", "UNKNOWN")},
399
+ "sig_matches": sig_matches,
400
+ "killinchu_ledger": KIL_API + "/receipt/ledger",
401
+ "honesty": "Sig matches computed from live Killinchu T11-T20 scan. DSSE PLACEHOLDER.",
402
+ "doctrine": DOCTRINE,
403
+ })
404
+
405
+ @app.post("/api/sentra/v1/drone-cyber/quarantine", tags=["drone-cyber"])
406
+ async def dc_quarantine(request: Request):
407
+ """Cyber quarantine (RTL + link isolation, NOT kinetic, own-fleet only).
408
+ 2-person Yuyay gate + cross-flagship halt-if-mismatch (Λ floor 0.90)."""
409
+ try:
410
+ body = await request.json()
411
+ except Exception:
412
+ body = {}
413
+ drone_id = body.get("drone_id", "")
414
+ approvers = body.get("approvers") or []
415
+ distinct = sorted(set(a for a in approvers if a))
416
+ # Gate 1: 2-person
417
+ if len(distinct) < 2:
418
+ return JSONResponse({"ok": False, "decision": "BLOCKED",
419
+ "reason": "2-person Yuyay gate requires >=2 distinct approvers",
420
+ "doctrine": DOCTRINE}, status_code=412)
421
+ # Gate 2: own-fleet only (allied / dual-use / counter-uas)
422
+ _, db = _kil_get("/drones/database", timeout=8.0)
423
+ drone = next((d for d in ((db or {}).get("drones") or []) if d.get("id") == drone_id), None)
424
+ if drone is None:
425
+ return JSONResponse({"ok": False, "decision": "REFUSED",
426
+ "reason": "drone not in fleet / Killinchu unreachable", "drone_id": drone_id,
427
+ "doctrine": DOCTRINE}, status_code=403)
428
+ if drone.get("side") not in ("allied", "dual-use", "counter-uas"):
429
+ return JSONResponse({"ok": False, "decision": "REFUSED",
430
+ "reason": "own-fleet only; cyber isolation never applied to third-party (CFAA/ITAR/Wassenaar)",
431
+ "drone_id": drone_id, "side": drone.get("side"), "kinetic": False,
432
+ "doctrine": DOCTRINE}, status_code=403)
433
+ # Gate 3: cross-flagship Yuyay-13 — Sentra score + (proxy) Killinchu score, halt-if-mismatch
434
+ axis = body.get("axis_scores") or [0.93] * 13
435
+ lam_s = round(_lambda_aggregate(axis), 4)
436
+ # Killinchu-side score proxied from its live integrity scan (own-fleet evidence)
437
+ _, integ = _kil_get(f"/drones/{drone_id}/integrity", timeout=8.0)
438
+ fired = int((integ or {}).get("fired_count", 0))
439
+ lam_k = round(max(0.0, _lambda_aggregate(axis) - 0.01 * fired), 4)
440
+ both_clear = lam_s >= LAMBDA_FLOOR and lam_k >= LAMBDA_FLOOR
441
+ mismatch = abs(lam_s - lam_k) > 0.02
442
+ if not both_clear or mismatch:
443
+ rcpt = _emit_bridge_receipt("drone.cyber.quarantine.halted",
444
+ {"drone_id": drone_id, "sentra_lambda": lam_s, "killinchu_lambda": lam_k,
445
+ "both_clear": both_clear, "mismatch": mismatch})
446
+ return JSONResponse({"ok": False, "decision": "HALT-MISMATCH",
447
+ "reason": "cross-flagship Yuyay-13 gate did not clear (halt-if-mismatch)",
448
+ "drone_id": drone_id, "kinetic": False,
449
+ "cross_flagship_gate": {"sentra_lambda": lam_s, "killinchu_lambda": lam_k,
450
+ "lambda_floor": LAMBDA_FLOOR, "both_clear": both_clear,
451
+ "mismatch": mismatch},
452
+ "khipu": {"sentra_receipt": rcpt["this_hash"]},
453
+ "signature": SIGNATURE_PLACEHOLDER, "doctrine": DOCTRINE}, status_code=409)
454
+ # Cleared — issue CYBER isolation (RTL + link isolation under signed Sentra cert)
455
+ cert_sha = _sha({"drone": drone_id, "approvers": distinct, "ts": _now()})
456
+ rcpt = _emit_bridge_receipt("drone.cyber.quarantine.executed",
457
+ {"drone_id": drone_id, "approvers": distinct, "drone_state": "RTL",
458
+ "sentra_lambda": lam_s, "killinchu_lambda": lam_k, "kinetic": False},
459
+ cross_link={"to_flagship": "killinchu", "drone_id": drone_id})
460
+ return JSONResponse({
461
+ "ok": True, "decision": "QUARANTINED", "drone_id": drone_id,
462
+ "drone_state": "RTL",
463
+ "isolation": "command+telemetry links isolated under signed Sentra cert",
464
+ "kinetic": False, "approvers": distinct,
465
+ "sentra_cert_sha256": cert_sha,
466
+ "cross_flagship_gate": {"sentra_lambda": lam_s, "killinchu_lambda": lam_k,
467
+ "lambda_floor": LAMBDA_FLOOR, "both_clear": True},
468
+ "khipu": {"sentra_receipt": rcpt["this_hash"]},
469
+ "honesty": ("Cyber isolation only (RTL + link isolation), NOT kinetic, own-fleet only. "
470
+ "Killinchu /v1/quarantine re-checks the gate in production (defence in depth). "
471
+ "DSSE PLACEHOLDER; SLSA L1 (honest)."),
472
+ "signature": SIGNATURE_PLACEHOLDER, "doctrine": DOCTRINE,
473
+ })
474
+
475
+ return app
serve.py CHANGED
@@ -1149,68 +1149,20 @@ if _codeproxy is not None:
1149
  print(f"[sentra.code] code proxy registration degraded: {_e}")
1150
 
1151
 
1152
- @app.get("/api/sentra/v1/immune/killinchu", tags=["immune"])
1153
- async def sentra_immune_killinchu():
1154
- """Immune-system view of the Killinchu drone-intelligence flagship. Sentra is
1155
- the immune layer of the SZL mesh; Killinchu's tamper tripwires (HUKLLA T11-T20)
1156
- and DICE/SBOM/SLSA drone identity are its air-domain antibodies."""
1157
- return {
1158
- "service": "sentra",
1159
- "vertical": "killinchu",
1160
- "domain": "airborne unmanned domain awareness / counter-UAS",
1161
- "url": "https://szlholdings-killinchu.hf.space",
1162
- "immune_surface": [
1163
- "HUKLLA tamper tripwires T11-T20 (firmware hash drift, RF spoof, GPS jam, motor-RPM anomaly, ...)",
1164
- "federated drone identity: DICE/RIoT + CycloneDX SBOM + SLSA-Drone-L3 attestation",
1165
- "passive counter-UAS identify & track (sense + evidence, no offensive effects)",
1166
- ],
1167
- "governance": "shares sentra Lambda-gate + Khipu receipt substrate; Doctrine v11",
1168
- "legal": "We sense, we evidence; we do not jack into third-party drones (CFAA/ITAR/Wassenaar).",
1169
- "pivot_from": "vessels",
1170
- }
1171
-
1172
-
1173
- # ---------------------------------------------------------------------------
1174
- # Native doctrine surfaces /api/sentra/v1/honest + /v1/lambda (ADDITIVE, Doctrine
1175
- # v11). Registered BEFORE the SPA catch-all so they resolve as JSON (previously
1176
- # both fell through the catch-all and returned the SPA HTML shell instead).
1177
- # ZERO BANDAID: 13-axis geometric-mean Λ, canonical numbers 749/14/163.
1178
- # ---------------------------------------------------------------------------
1179
- _SENTRA_AXIS_NAMES = [
1180
- "soundness", "calibration", "robustness", "provenance", "consent", "reversibility",
1181
- "transparency", "fairness", "containment", "attestation", "freshness", "authority", "auditability",
1182
- ]
1183
-
1184
-
1185
- @app.get("/api/sentra/v1/honest", tags=["doctrine"])
1186
- async def sentra_honest():
1187
- return {
1188
- "doctrine": "v11",
1189
- "declarations": 749, "axioms_unique": 14, "axioms_raw": 15, "sorries_total": 163,
1190
- "sorries_baseline": 112, "sorries_putnam": 51, "trust_axes": 13,
1191
- "immune_gates": 8,
1192
- "lambda_uniqueness": "Conjecture, not a closed theorem (open CAUCHY_ND sorry + missing symmetry axiom)",
1193
- "slsa": "L1 (honest)",
1194
- "forecast": "witnessed forecasting carries an honest lean_status (partial) + M\u0101dhava error envelope; not a closed proof.",
1195
- "hatun_willay": True,
1196
- }
1197
-
1198
 
1199
- @app.get("/api/sentra/v1/lambda", tags=["doctrine"])
1200
- async def sentra_lambda():
1201
- axes = [0.92, 0.90, 0.93, 0.91, 0.94, 0.90, 0.92, 0.91, 0.95, 0.92, 0.93, 0.90, 0.92]
1202
- floor = 0.90
1203
- clamped = [min(1.0, max(1e-9, float(x))) for x in axes]
1204
- L = _math.exp(sum(_math.log(x) for x in clamped) / len(clamped))
1205
- return {
1206
- "trust_axes": 13,
1207
- "axes": [{"name": n, "score": s} for n, s in zip(_SENTRA_AXIS_NAMES, axes)],
1208
- "lambda": round(L, 6), "lambda_floor": floor, "pass": L >= floor,
1209
- "aggregate": "geometric mean (yuyay_v3 canonical, 13-axis)",
1210
- "uniqueness": "Conjecture, not a Theorem (open CAUCHY_ND sorry + missing symmetry axiom)",
1211
- "declarations": 749, "axioms_unique": 14, "axioms_raw": 15, "sorries_total": 163,
1212
- "doctrine": "v11",
1213
- }
1214
 
1215
 
1216
  @app.get("/{path:path}")
 
1149
  print(f"[sentra.code] code proxy registration degraded: {_e}")
1150
 
1151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1152
 
1153
+ # ===========================================================================
1154
+ # Sentra <-> Killinchu cyber bridge (ADDITIVE, Doctrine v11). Registered
1155
+ # BEFORE the /{path:path} catch-all so /drone-cyber + /api/sentra/v1/drone-cyber/*
1156
+ # take precedence. DEFENSIVE: any failure here must NOT crash the app — degrade
1157
+ # honestly. Touches nothing existing: 43/43 routes, 6 base sigs, 8 gates,
1158
+ # Wire B/E/F/G, IP-HOLD #45 all untouched. v11 LOCKED numbers preserved.
1159
+ # ===========================================================================
1160
+ try:
1161
+ import sentra_drone_cyber as _drone_cyber
1162
+ _drone_cyber.register_drone_cyber(app)
1163
+ print("[sentra] drone-cyber bridge routes registered (Killinchu fleet)", file=sys.stderr)
1164
+ except Exception as _e:
1165
+ print(f"[sentra] drone-cyber bridge not registered: {_e}", file=sys.stderr)
 
 
1166
 
1167
 
1168
  @app.get("/{path:path}")