Spaces:
Running
Running
| // SPDX-License-Identifier: Apache-2.0 | |
| // © 2026 Lutar, Stephen P. Jr. — SZL Holdings · Doctrine v11 | |
| // | |
| // surfaces/counter-uas.js — COUNTER-UAS / killinchu holographic surface (Dev4). | |
| // | |
| // Leader/technique modeled on: Anduril Lattice + CesiumJS + Dedrone. | |
| // 3D globe + track entities + restricted-airspace SDF volumes + radar sweep cone | |
| // + signed-verdict beam. (We are *modeled on* the leader; we never claim to BE it.) | |
| // | |
| // VENDORING NOTE (Dev0 contract §6): the contract offered an escape hatch — full | |
| // CesiumJS@1.123 OR a three.js globe (textured sphere + lat/long plotting). We took | |
| // the three.js globe: it reuses the already-vendored three.js r170 (0 new MB, 0 CDN), | |
| // keeps this PR lean, and is sufficient for track plotting + SDF volumes + sweep cone. | |
| // See VENDOR_MANIFEST.md §Dev4 for the documented decision. | |
| // | |
| // HONESTY (killinchu charter / JIATF-401 C4 crosswalk): killinchu SENSES & EVIDENCES — | |
| // it does NOT defeat (no jamming, no GNSS spoofing, no takeover, no kinetic). Every | |
| // visual here is detect / track / classify / evidence + the signed verdict ONLY. There | |
| // is NO kinetic effect, NO jam beam, NO interceptor-to-target engagement geometry. The | |
| // "verdict beam" is the SIGNED DECISION locking onto a track — an evidence act, not a | |
| // weapon. Λ = Conjecture 1 (advisory, NOT proven trust). All labels are read from the | |
| // live JSON; values trace to real killinchu endpoints via ctx.live.poll, never hardcoded. | |
| // | |
| // Live data (same-origin bridge -> killinchu Space, see szl_counter_uas_proxy.py): | |
| // /api/a11oy/v1/counter-uas/evaluate live Λ decision + REAL ECDSA-P256 DSSE sig | |
| // /api/a11oy/v1/counter-uas/telemetry friendly fleet + threat tracks (honest data_kind) | |
| // /api/a11oy/v1/counter-uas/cued-tracks externally-cued threat tracks (cuing_sensor) | |
| // /api/a11oy/v1/counter-uas/air-picture real cooperative ADS-B (airplanes.live) | |
| // /api/a11oy/v1/counter-uas/gates 13-axis Λ-gate spec | |
| // /static/3d/surfaces/data/drones_db.json 53 verified drone fingerprints (vendored) | |
| import { createShowcase } from "./_showcase.js"; | |
| const ID = "counter-uas"; | |
| const TITLE = "Counter-UAS · killinchu"; | |
| const EP = { | |
| evaluate: "/api/a11oy/v1/counter-uas/evaluate", | |
| telemetry: "/api/a11oy/v1/counter-uas/telemetry", | |
| cued: "/api/a11oy/v1/counter-uas/cued-tracks", | |
| airpicture: "/api/a11oy/v1/counter-uas/air-picture", | |
| gates: "/api/a11oy/v1/counter-uas/gates", | |
| // Governed in-request MODELED compute: evaluates the C-UAS formula stack + the | |
| // Λ-ROE advisory gate (Conjecture 1, gray never green; effector SIMULATED). This | |
| // is the surface's honest runtime-default label source (szl_cuas_formulas.py). | |
| compute: "/api/a11oy/v1/counter-uas/compute", | |
| dronesDb: "/static/3d/surfaces/data/drones_db.json", | |
| }; | |
| // Honest surface state. The headline label is READ VERBATIM from the live compute | |
| // endpoint and defaults to MODELED (the governed compute is a deterministic model of | |
| // the C-UAS formulas — no live sensor/effector on the demo floor). Λ-ROE is Conjecture | |
| // 1 and renders GRAY, never green. Never fabricated; never upgraded past what the | |
| // endpoint declares. | |
| const S = { label: null, compute: null, roe: null }; | |
| const LAMBDA_GRAY = 0x9aa7b0; // Λ = Conjecture 1 → GRAY (never green). Doctrine v11. | |
| // Globe radius (world units) + altitude scale (alt_m -> world units above surface). | |
| const R = 3.0; | |
| const ALT_SCALE = 1.0 / 18000; // ~18 km ceiling maps to ~+1 globe radius of lift | |
| const ALT_MAX_LIFT = 0.9; | |
| // Classification → color (benign / unknown / hostile / friendly). NEVER a "defeat" color. | |
| const CLS = { | |
| friendly: 0x39d3c4, // teal — own fleet | |
| benign: 0x2fd07a, // green — cooperative / allied | |
| unknown: 0xe8c074, // amber — uncharacterized | |
| suspect: 0xffa54a, // orange — Λ below threshold | |
| hostile: 0xff6b6b, // red — Λ below floor (still only SENSED, never engaged) | |
| }; | |
| let _ctx = null, _stage = null, THREE = null; | |
| let _group = null, _globe = null, _handles = [], _frameOff = null, _overlay = null, _show = null; | |
| let _disposables = []; | |
| // Live caches (honest: empty until a real fetch lands). | |
| let _state = { | |
| evaluate: null, telemetry: null, cued: null, airpicture: null, gates: null, db: null, | |
| compute: null, meta: {}, dbCount: 0, | |
| }; | |
| // ── geo helpers ──────────────────────────────────────────────────────────── | |
| function llToVec(lat, lon, lift) { | |
| const phi = (90 - lat) * Math.PI / 180; | |
| const theta = (lon + 180) * Math.PI / 180; | |
| const r = R + (lift || 0); | |
| return new THREE.Vector3( | |
| -r * Math.sin(phi) * Math.cos(theta), | |
| r * Math.cos(phi), | |
| r * Math.sin(phi) * Math.sin(theta) | |
| ); | |
| } | |
| function altLift(alt_m) { | |
| if (alt_m == null) return 0.02; | |
| return Math.min(ALT_MAX_LIFT, Math.max(0.0, alt_m * ALT_SCALE)); | |
| } | |
| function classify(track) { | |
| // Honest mapping straight off the live fields — no invented severity. | |
| if (track.threat_category == null && track.role) return "friendly"; | |
| const v = (track.lambda_verdict || "").toUpperCase(); | |
| if (v.indexOf("THREAT") >= 0) return "hostile"; | |
| if (v.indexOf("SUSPECT") >= 0) return "suspect"; | |
| const s = (track.side || "").toLowerCase(); | |
| if (s === "allied") return "benign"; | |
| if (track.threat_category) return "unknown"; | |
| return "unknown"; | |
| } | |
| function _track(fn) { _disposables.push(fn); } | |
| // ── demo builders (each returns {group, update?} and is added to _group) ───── | |
| // D1: textured-feel globe (procedural lat/long graticule sphere; no external image, | |
| // 0 CDN). Sense-only base for the air picture. | |
| function buildGlobe() { | |
| const g = new THREE.Group(); | |
| const sphere = new THREE.Mesh( | |
| new THREE.SphereGeometry(R, 64, 48), | |
| new THREE.MeshStandardMaterial({ color: 0x0a1626, emissive: 0x06101c, | |
| emissiveIntensity: 0.5, metalness: 0.1, roughness: 0.9, transparent: true, opacity: 0.96 }) | |
| ); | |
| g.add(sphere); | |
| _globe = sphere; | |
| // graticule (lat/long lines) — gives the globe orientation without a texture/CDN | |
| const grat = new THREE.Group(); | |
| const lineMat = new THREE.LineBasicMaterial({ color: 0x1d3a52, transparent: true, opacity: 0.5 }); | |
| for (let lat = -60; lat <= 60; lat += 30) { | |
| const pts = []; | |
| for (let lon = -180; lon <= 180; lon += 6) pts.push(llToVec(lat, lon, 0.005)); | |
| grat.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), lineMat)); | |
| } | |
| for (let lon = -180; lon < 180; lon += 30) { | |
| const pts = []; | |
| for (let lat = -90; lat <= 90; lat += 6) pts.push(llToVec(lat, lon, 0.005)); | |
| grat.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), lineMat)); | |
| } | |
| g.add(grat); | |
| // D2: atmospheric SDF-style glow halo (additive shell) | |
| const halo = new THREE.Mesh( | |
| new THREE.SphereGeometry(R * 1.06, 48, 32), | |
| new THREE.MeshBasicMaterial({ color: 0x2a6f9e, transparent: true, opacity: 0.08, | |
| side: THREE.BackSide, blending: THREE.AdditiveBlending, depthWrite: false }) | |
| ); | |
| g.add(halo); | |
| _track(() => { sphere.geometry.dispose(); sphere.material.dispose(); halo.geometry.dispose(); halo.material.dispose(); }); | |
| return { group: g }; | |
| } | |
| // D3: radar sweep cone — animated sector that rotates around the site (Dedrone/Anduril | |
| // coverage sweep). This is a SENSOR coverage sweep, not an effector. | |
| function buildRadarSweep(siteLat, siteLon) { | |
| const g = new THREE.Group(); | |
| const center = llToVec(siteLat, siteLon, 0.0); | |
| // D4: sensor coverage cone (dome of regard) | |
| const coneGeo = new THREE.ConeGeometry(0.9, 1.2, 32, 1, true); | |
| const coneMat = new THREE.MeshBasicMaterial({ color: 0x39d3c4, transparent: true, | |
| opacity: 0.07, side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending }); | |
| const cone = new THREE.Mesh(coneGeo, coneMat); | |
| cone.position.copy(center.clone().multiplyScalar(1.18)); | |
| cone.lookAt(0, 0, 0); | |
| cone.rotateX(Math.PI); | |
| g.add(cone); | |
| // sweep wedge | |
| const sweepGeo = new THREE.CircleGeometry(0.85, 24, 0, Math.PI / 5); | |
| const sweepMat = new THREE.MeshBasicMaterial({ color: 0x6fb1ff, transparent: true, | |
| opacity: 0.22, side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending }); | |
| const sweep = new THREE.Mesh(sweepGeo, sweepMat); | |
| sweep.position.copy(center.clone().multiplyScalar(1.02)); | |
| sweep.lookAt(center.clone().multiplyScalar(2)); | |
| g.add(sweep); | |
| _track(() => { coneGeo.dispose(); coneMat.dispose(); sweepGeo.dispose(); sweepMat.dispose(); }); | |
| return { group: g, update: (t) => { sweep.rotation.z = t * 1.4; } }; | |
| } | |
| // Build a single track entity: marker + altitude stalk + history trail + label. | |
| function buildTrackEntity(track, isFriendly) { | |
| const g = new THREE.Group(); | |
| const cls = isFriendly ? "friendly" : classify(track); | |
| const color = CLS[cls] || CLS.unknown; | |
| const lift = altLift(track.alt_m); | |
| const pos = llToVec(track.lat, track.lon, lift); | |
| // marker (octahedron = aircraft track glyph) | |
| const mGeo = new THREE.OctahedronGeometry(0.055, 0); | |
| const mMat = new THREE.MeshStandardMaterial({ color, emissive: color, emissiveIntensity: 0.85, metalness: 0.3, roughness: 0.4 }); | |
| const marker = new THREE.Mesh(mGeo, mMat); | |
| marker.position.copy(pos); | |
| g.add(marker); | |
| // D5: altitude stalk to surface (3D altitude encoding) | |
| const surf = llToVec(track.lat, track.lon, 0.0); | |
| const stalk = new THREE.Line( | |
| new THREE.BufferGeometry().setFromPoints([surf, pos]), | |
| new THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.4 }) | |
| ); | |
| g.add(stalk); | |
| // D6: classification ring around hostile/suspect (SENSED status, never an engagement) | |
| let ring = null; | |
| if (cls === "hostile" || cls === "suspect") { | |
| const rGeo = new THREE.RingGeometry(0.09, 0.11, 24); | |
| const rMat = new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 0.5, side: THREE.DoubleSide, depthWrite: false }); | |
| ring = new THREE.Mesh(rGeo, rMat); | |
| ring.position.copy(pos); | |
| ring.lookAt(0, 0, 0); | |
| g.add(ring); | |
| _track(() => { rGeo.dispose(); rMat.dispose(); }); | |
| } | |
| _track(() => { mGeo.dispose(); mMat.dispose(); stalk.geometry.dispose(); stalk.material.dispose(); }); | |
| return { group: g, marker, ring, cls, track }; | |
| } | |
| // ── HUD / overlay ──────────────────────────────────────────────────────────── | |
| function hud(html) { | |
| const d = document.createElement("div"); | |
| d.innerHTML = html; | |
| return d; | |
| } | |
| function row(label, value, labelToken) { | |
| const r = document.createElement("div"); | |
| r.style.cssText = "display:flex;align-items:center;gap:8px;justify-content:space-between;font:11px ui-monospace,Menlo,monospace;color:#9fb1bf"; | |
| const l = document.createElement("span"); l.textContent = label; l.style.color = "#cfe0ec"; | |
| const v = document.createElement("span"); v.textContent = value; v.style.color = "#eef3f6"; v.style.fontWeight = "600"; | |
| r.appendChild(l); r.appendChild(v); | |
| if (labelToken && _ctx) { const c = _ctx.label.chip(labelToken); c.style.marginLeft = "6px"; r.appendChild(c); } | |
| return r; | |
| } | |
| // ── live scene state ────────────────────────────────────────────────────────── | |
| let _trackGroup = null; // holds all track entities, rebuilt on telemetry/cued change | |
| let _verdictBeam = null; // signed-verdict beam (decision lock), not a weapon | |
| let _dsseLock = null; // DSSE signature lock animation node | |
| let _fingerprintCallout = null; | |
| let _restrictedZones = null; // restricted-airspace SDF volumes | |
| let _ridPanel = null, _gatesPanel = null, _verdictPanel = null, _posturePanel = null, _fpPanel = null; | |
| let _computePanel = null; // governed MODELED compute + Λ-ROE advisory gate (gray) | |
| let _beamPhase = 0, _lockPhase = 0; | |
| function rebuildTracks() { | |
| if (!_trackGroup) { _trackGroup = new THREE.Group(); _group.add(_trackGroup); } | |
| // clear old | |
| while (_trackGroup.children.length) { | |
| const c = _trackGroup.children.pop(); | |
| _trackGroup.remove(c); | |
| } | |
| const entities = []; | |
| const tele = _state.telemetry; | |
| const cued = _state.cued; | |
| // D7: friendly fleet tracks (own fleet, teal) — telemetry.friendly_drones | |
| if (tele && Array.isArray(tele.friendly_drones)) { | |
| tele.friendly_drones.forEach((d) => { | |
| const e = buildTrackEntity(d, true); _trackGroup.add(e.group); entities.push(e); | |
| }); | |
| } | |
| // D8: SENSED threat tracks (telemetry.threat_tracks) — classification color | |
| if (tele && Array.isArray(tele.threat_tracks)) { | |
| tele.threat_tracks.forEach((d) => { | |
| const e = buildTrackEntity(d, false); _trackGroup.add(e.group); entities.push(e); | |
| }); | |
| } | |
| // D9: externally-cued tracks (cued-tracks) — carry cuing_sensor (we own no sensor) | |
| if (cued && Array.isArray(cued.tracks)) { | |
| cued.tracks.forEach((d) => { | |
| const e = buildTrackEntity(d, false); _trackGroup.add(e.group); entities.push(e); | |
| }); | |
| } | |
| // D10: real cooperative ADS-B air picture (airplanes.live) — small benign dots, | |
| // capped for perf. Cooperative manned aircraft = green (benign), per AirSight pattern. | |
| if (_state.airpicture && Array.isArray(_state.airpicture.aircraft)) { | |
| const ac = _state.airpicture.aircraft.slice(0, 120); | |
| const geo = new THREE.BufferGeometry(); | |
| const verts = []; | |
| ac.forEach((a) => { | |
| if (a.lat == null || a.lon == null) return; | |
| const p = llToVec(a.lat, a.lon, altLift((a.alt_baro || 0) * 0.3048)); | |
| verts.push(p.x, p.y, p.z); | |
| }); | |
| geo.setAttribute("position", new THREE.Float32BufferAttribute(verts, 3)); | |
| const pts = new THREE.Points(geo, new THREE.PointsMaterial({ color: CLS.benign, size: 0.04, transparent: true, opacity: 0.7 })); | |
| _trackGroup.add(pts); | |
| _track(() => { geo.dispose(); pts.material.dispose(); }); | |
| } | |
| _trackGroup.userData.entities = entities; | |
| return entities; | |
| } | |
| // D11: restricted-airspace SDF volumes — translucent glowing boxes/spheres around | |
| // protected sites. Breach = pulse (SENSED breach, not an engagement). | |
| function buildRestrictedZones(centerLat, centerLon) { | |
| const g = new THREE.Group(); | |
| const sites = [ | |
| { lat: centerLat, lon: centerLon, r: 0.42, name: "PROTECTED SITE" }, | |
| { lat: centerLat + 0.02, lon: centerLon + 0.03, r: 0.26, name: "INNER KEEP-OUT" }, | |
| ]; | |
| const meshes = []; | |
| sites.forEach((s) => { | |
| const geo = new THREE.SphereGeometry(s.r, 24, 18); | |
| const mat = new THREE.MeshBasicMaterial({ color: 0xff6b6b, transparent: true, opacity: 0.08, | |
| side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending }); | |
| const m = new THREE.Mesh(geo, mat); | |
| m.position.copy(llToVec(s.lat, s.lon, s.r * 0.4)); | |
| g.add(m); | |
| // wireframe outline (SDF boundary) | |
| const wf = new THREE.LineSegments(new THREE.WireframeGeometry(geo), | |
| new THREE.LineBasicMaterial({ color: 0xff8a8a, transparent: true, opacity: 0.35 })); | |
| wf.position.copy(m.position); | |
| g.add(wf); | |
| meshes.push(m); | |
| _track(() => { geo.dispose(); mat.dispose(); wf.geometry.dispose(); wf.material.dispose(); }); | |
| }); | |
| return { group: g, update: (t) => { meshes.forEach((m, i) => { m.material.opacity = 0.06 + 0.05 * (0.5 + 0.5 * Math.sin(t * 1.5 + i)); }); } }; | |
| } | |
| // D12: Λ-gate signed-verdict beam — when /evaluate returns, a beam from the globe | |
| // core to the decision plane "locks" the verdict. This is the SIGNED DECISION | |
| // (an evidence act). Color = decision (ALLOW=green / CLASSIFY=amber / HALT=red). | |
| // It is NOT a weapon: nothing is emitted at a track, nothing is defeated. | |
| function buildVerdictBeam() { | |
| const g = new THREE.Group(); | |
| const geo = new THREE.CylinderGeometry(0.02, 0.08, 2.6, 16, 1, true); | |
| const mat = new THREE.MeshBasicMaterial({ color: CLS.benign, transparent: true, opacity: 0.0, | |
| side: THREE.DoubleSide, depthWrite: false, blending: THREE.AdditiveBlending }); | |
| const beam = new THREE.Mesh(geo, mat); | |
| beam.position.set(0, R + 1.3, 0); | |
| g.add(beam); | |
| _verdictBeam = beam; | |
| _track(() => { geo.dispose(); mat.dispose(); }); | |
| return { group: g, update: (t) => { | |
| if (!_state.evaluate) return; | |
| const dec = (_state.evaluate.decision || "").toUpperCase(); | |
| const col = dec === "ALLOW" ? CLS.benign : dec === "HALT" ? CLS.hostile : CLS.unknown; | |
| beam.material.color.setHex(col); | |
| beam.material.opacity = 0.18 + 0.12 * (0.5 + 0.5 * Math.sin(t * 2.2)); | |
| } }; | |
| } | |
| // D13: DSSE-signature lock animation — when the real ECDSA-P256 DSSE signature is | |
| // present (lambda_receipt.dsse.signed === true), an orbiting ring "locks" closed | |
| // around the verdict, with the signature truncation shown in the HUD. The lock is | |
| // the cryptographic seal of the SIGNED VERDICT — the evidence, not an effect. | |
| function buildDsseLock() { | |
| const g = new THREE.Group(); | |
| const geo = new THREE.TorusGeometry(0.5, 0.018, 12, 48); | |
| const mat = new THREE.MeshStandardMaterial({ color: 0x6fb1ff, emissive: 0x6fb1ff, emissiveIntensity: 0.6, metalness: 0.6, roughness: 0.3 }); | |
| const ring = new THREE.Mesh(geo, mat); | |
| ring.position.set(0, R + 1.3, 0); | |
| g.add(ring); | |
| _dsseLock = ring; | |
| _track(() => { geo.dispose(); mat.dispose(); }); | |
| return { group: g, update: (t) => { | |
| const signed = !!(_state.evaluate && _state.evaluate.lambda_receipt && | |
| _state.evaluate.lambda_receipt.dsse && _state.evaluate.lambda_receipt.dsse.signed); | |
| ring.rotation.x = Math.PI / 2; | |
| ring.rotation.z = t * (signed ? 0.9 : 0.15); | |
| ring.material.color.setHex(signed ? 0x2fd07a : 0x8a97a3); // green when signed, gray when not | |
| ring.material.emissive.setHex(signed ? 0x2fd07a : 0x8a97a3); | |
| ring.scale.setScalar(signed ? 1.0 : 0.92 + 0.06 * Math.sin(t * 3)); | |
| } }; | |
| } | |
| // ── overlay panels (the HUD reads live values + honest labels) ──────────────── | |
| function buildOverlay() { | |
| const badge = _ctx.live.createBadge(); | |
| // Shared collapsible showcase: title + badge + legend live in the compact chrome; | |
| // the verdict / RID / posture / gates / fingerprint panels fold into the (collapsed) | |
| // body so the 3D globe stays the star and text never spans the viewport. | |
| _show = createShowcase(_ctx, { | |
| id: ID, title: TITLE, accent: "#5b8dee", badge, legend: true, | |
| }); | |
| // panels flow (wrap) inside the body instead of a full-width two-column banner | |
| _overlay = document.createElement("div"); | |
| Object.assign(_overlay.style, { | |
| display: "flex", flexWrap: "wrap", gap: "8px", | |
| fontFamily: "ui-monospace,Menlo,monospace", | |
| }); | |
| const card = (title) => { | |
| const c = document.createElement("div"); | |
| c.style.cssText = "flex:1 1 220px;background:rgba(8,14,20,.82);border:1px solid #1d2a36;border-radius:9px;padding:9px 11px;display:flex;flex-direction:column;gap:5px"; | |
| const h = document.createElement("div"); | |
| h.style.cssText = "font:600 11px ui-sans-serif,system-ui;color:#eef3f6;letter-spacing:.5px;text-transform:uppercase"; | |
| h.textContent = title; c.appendChild(h); | |
| return c; | |
| }; | |
| // D14: 13-axis Λ-gate verdict panel | |
| _verdictPanel = card("Λ-Gate Verdict · senses-and-evidences"); | |
| _overlay.appendChild(_verdictPanel); | |
| // Governed MODELED compute + Λ-ROE advisory gate (Conjecture 1, gray never green). | |
| _computePanel = card("Governed compute · Λ-ROE (Conjecture 1)"); | |
| _overlay.appendChild(_computePanel); | |
| // D15: DSSE signature panel (real ECDSA-P256) | |
| _fpPanel = null; | |
| _ridPanel = card("Remote-ID / RID Validator"); | |
| _overlay.appendChild(_ridPanel); | |
| _posturePanel = card("JIATF-401 posture"); | |
| _overlay.appendChild(_posturePanel); | |
| _gatesPanel = card("13-Axis Λ-Gates"); | |
| _overlay.appendChild(_gatesPanel); | |
| _fingerprintCallout = card("Fingerprint match · 53-DB"); | |
| _overlay.appendChild(_fingerprintCallout); | |
| _show.body.appendChild(_overlay); | |
| return badge; | |
| } | |
| function clearCard(card) { while (card.children.length > 1) card.removeChild(card.lastChild); } | |
| function renderVerdict() { | |
| const ev = _state.evaluate; | |
| clearCard(_verdictPanel); | |
| if (!ev || ev.degraded) { | |
| _verdictPanel.appendChild(row("decision", ev && ev.degraded ? "DEGRADED" : "—", "STRUCTURAL-ONLY")); | |
| return; | |
| } | |
| const dec = ev.decision || "—"; | |
| const lam = ev.lambda != null ? ev.lambda.toFixed(6) : "—"; | |
| const floor = ev.lambda_floor != null ? ev.lambda_floor : "—"; | |
| _verdictPanel.appendChild(row("decision", dec)); | |
| _verdictPanel.appendChild(row("Λ score", lam + " (floor " + floor + ")")); | |
| _verdictPanel.appendChild(row("Λ status", "Conjecture 1 · advisory")); | |
| // D16: real DSSE signature lock-on (truncated sig + keyid) | |
| const dsse = ev.lambda_receipt && ev.lambda_receipt.dsse; | |
| if (dsse) { | |
| const sig = (dsse.signatures && dsse.signatures[0] && dsse.signatures[0].sig) || ""; | |
| const sigShort = sig ? (sig.slice(0, 18) + "…" + sig.slice(-8)) : "—"; | |
| _verdictPanel.appendChild(row("DSSE", dsse.signed ? "SIGNED ✓" : "UNSIGNED", dsse.signed ? "MEASURED" : "STRUCTURAL-ONLY")); | |
| _verdictPanel.appendChild(row("keyid", dsse.keyid || "—")); | |
| _verdictPanel.appendChild(row("ECDSA-P256 sig", sigShort)); | |
| } | |
| // D17: CI signing honesty disclosure (PLACEHOLDER per killinchu honest posture) | |
| if (ev.signature) { | |
| const ci = row("CI sign", ev.signature.indexOf("PLACEHOLDER") >= 0 ? "PLACEHOLDER" : "wired", "STRUCTURAL-ONLY"); | |
| _verdictPanel.appendChild(ci); | |
| } | |
| } | |
| // Governed MODELED compute + Λ-ROE advisory gate. The headline label is read VERBATIM | |
| // from the endpoint (defaults MODELED). Λ-ROE is Conjecture 1 — its chip is ALWAYS the | |
| // gray Conjecture token, NEVER green; the effector is SIMULATED, human-on-loop. We honor | |
| // the endpoint's explicit render contract (render_green must be false). | |
| function renderCompute() { | |
| if (!_computePanel) return; | |
| clearCard(_computePanel); | |
| const j = _state.compute; | |
| // Runtime-default honesty label, read verbatim from the live compute endpoint. This | |
| // is the census single-source-of-truth (a11oy_frontier_page._derive_label): the | |
| // headline label is `(endpoint.label || "MODELED")`, never upgraded past what the | |
| // endpoint declares. Λ-ROE stays Conjecture 1 (gray) regardless. | |
| const jl = j && j.label; | |
| S.label = (jl || "MODELED").toUpperCase(); | |
| if (!j || j.degraded || j.ok === false) { | |
| _computePanel.appendChild(row("compute", j && j.degraded ? "DEGRADED" : "—", S.label)); | |
| // Λ-ROE remains Conjecture 1 (gray) even when the compute is unavailable. | |
| _computePanel.appendChild(row("Λ-ROE", "Conjecture 1 · advisory", "MODELED")); | |
| return; | |
| } | |
| const roe = j.lambda_roe_gate || {}; | |
| S.roe = roe; | |
| _computePanel.appendChild(row("data", String(j.data_label || "MODELED"), S.label)); | |
| const eng = (j.results && j.results.engageability) || {}; | |
| if (eng.feasible != null) { | |
| _computePanel.appendChild(row("engageability", eng.feasible ? "feasible" : "infeasible", | |
| eng.effector === "SIMULATED" ? "SIMULATED" : "MODELED")); | |
| } | |
| const cons = (j.results && j.results.consensus) || {}; | |
| if (cons.lambda2 != null) _computePanel.appendChild(row("consensus λ₂", String(cons.lambda2))); | |
| // Λ-ROE gate row — ALWAYS the gray Conjecture chip, NEVER green. Doctrine v11. | |
| const posture = roe.posture || "advisory"; | |
| const roeRow = row("Λ-ROE", String(posture), "MODELED"); | |
| // Force the Λ posture pill gray (Conjecture 1) regardless of sub-gate pass state. | |
| const pill = document.createElement("span"); | |
| pill.textContent = "Conjecture 1 · gray"; | |
| pill.style.cssText = "margin-left:6px;padding:1px 6px;border-radius:6px;font:10px ui-monospace,monospace;" + | |
| "color:#0d1117;background:#9aa7b0;"; // gray (#9aa7b0) — NEVER green | |
| roeRow.appendChild(pill); | |
| _computePanel.appendChild(roeRow); | |
| _computePanel.appendChild(row("authorization", String(roe.authorization || "advisory") + | |
| " · " + String(roe.control_mode || "human_on_loop"))); | |
| _computePanel.appendChild(row("effector", String(roe.effector || "SIMULATED"), "SIMULATED")); | |
| _computePanel.appendChild(row("autonomous engage", roe.autonomous_engage ? "YES" : "NO — never")); | |
| } | |
| function renderRid() { | |
| clearCard(_ridPanel); | |
| const tele = _state.telemetry, cued = _state.cued; | |
| // RID validator: count tracks broadcasting a Remote-ID vs those without (RID-OFF = | |
| // spoofable/uncooperative). Honest: a broadcast ID is a CLAIM, not ground truth. | |
| let withRid = 0, withoutRid = 0; | |
| const scan = (arr, key) => (arr || []).forEach((t) => { (t[key]) ? withRid++ : withoutRid++; }); | |
| if (tele) { scan(tele.friendly_drones, "remote_id"); scan(tele.threat_tracks, "remote_id"); } | |
| if (cued) scan(cued.tracks, "remote_id"); | |
| _ridPanel.appendChild(row("RID present", String(withRid))); | |
| _ridPanel.appendChild(row("RID OFF (uncoop)", String(withoutRid))); | |
| _ridPanel.appendChild(row("note", "broadcast ID = claim")); | |
| if (tele && tele.data_kind) _ridPanel.appendChild(row("data_kind", String(tele.data_kind), tele.data_kind === "demo_mock" ? "SAMPLE" : "MEASURED")); | |
| } | |
| function renderPosture() { | |
| clearCard(_posturePanel); | |
| _posturePanel.appendChild(row("charter", "SENSE + EVIDENCE")); | |
| _posturePanel.appendChild(row("defeat", "OUT-OF-SCOPE")); | |
| _posturePanel.appendChild(row("no jam/spoof", "✓")); | |
| _posturePanel.appendChild(row("no takeover", "✓")); | |
| _posturePanel.appendChild(row("no kinetic", "✓")); | |
| _posturePanel.appendChild(row("human-on-loop", "ROE gate")); | |
| _posturePanel.appendChild(row("C4 strongest fit", "§6–§7 receipts")); | |
| } | |
| function renderGates() { | |
| clearCard(_gatesPanel); | |
| const g = _state.gates; | |
| if (!g || g.degraded || !Array.isArray(g.gates)) { | |
| _gatesPanel.appendChild(row("gates", g && g.degraded ? "DEGRADED" : "—", "STRUCTURAL-ONLY")); | |
| return; | |
| } | |
| _gatesPanel.appendChild(row("axes", String(g.count != null ? g.count : g.gates.length))); | |
| // show first few axis names live | |
| g.gates.slice(0, 5).forEach((ax) => _gatesPanel.appendChild(row("Λ" + ax.axis, ax.name || "—"))); | |
| _gatesPanel.appendChild(row("status", "Conjecture 1 — OPEN")); | |
| } | |
| function renderFingerprint() { | |
| clearCard(_fingerprintCallout); | |
| const db = _state.db; | |
| if (!db || !Array.isArray(db)) { | |
| _fingerprintCallout.appendChild(row("DB", "loading…", "STRUCTURAL-ONLY")); | |
| return; | |
| } | |
| _fingerprintCallout.appendChild(row("fingerprints", String(db.length))); | |
| // D: attempt to match a SENSED threat track type to the DB (classification callout) | |
| const tele = _state.telemetry; | |
| let matched = null; | |
| if (tele && Array.isArray(tele.threat_tracks)) { | |
| for (const t of tele.threat_tracks) { | |
| const ty = (t.type || "").toUpperCase(); | |
| matched = db.find((d) => ty && (String(d.model).toUpperCase().indexOf(ty.split("-")[0]) >= 0)); | |
| if (matched) break; | |
| } | |
| } | |
| // fall back to a representative adversary fingerprint to show the callout shape | |
| const ex = matched || db.find((d) => d.side === "adversary") || db[0]; | |
| if (ex) { | |
| _fingerprintCallout.appendChild(row("model", String(ex.model))); | |
| _fingerprintCallout.appendChild(row("mfr", String(ex.manufacturer))); | |
| _fingerprintCallout.appendChild(row("group", String(ex.group || "—"))); | |
| _fingerprintCallout.appendChild(row("side", String(ex.side || "—"))); | |
| } | |
| _fingerprintCallout.appendChild(row("source", "drones_db.json")); | |
| } | |
| // ── mount / unmount ─────────────────────────────────────────────────────────── | |
| function mount(ctx) { | |
| _ctx = ctx; _stage = ctx.stage; THREE = ctx.THREE; | |
| _group = new THREE.Group(); | |
| _stage.scene.add(_group); | |
| // lighting so the globe reads in 3D | |
| const amb = new THREE.AmbientLight(0x223344, 1.1); | |
| const key = new THREE.DirectionalLight(0xbfe0ff, 1.3); key.position.set(5, 6, 7); | |
| _group.add(amb); _group.add(key); | |
| _track(() => { /* lights freed with group removal */ }); | |
| // build the static scene demos | |
| const updaters = []; | |
| const globe = buildGlobe(); _group.add(globe.group); | |
| // protected site (matches killinchu demo fleet vicinity ~ 37.42, -122.17) | |
| const siteLat = 37.4275, siteLon = -122.1697; | |
| const sweep = buildRadarSweep(siteLat, siteLon); _group.add(sweep.group); updaters.push(sweep.update); | |
| const zones = buildRestrictedZones(siteLat, siteLon); _group.add(zones.group); updaters.push(zones.update); | |
| const beam = buildVerdictBeam(); _group.add(beam.group); updaters.push(beam.update); | |
| const lock = buildDsseLock(); _group.add(lock.group); updaters.push(lock.update); | |
| // floating honesty billboard | |
| try { | |
| const bb = ctx.label.billboard(THREE, "MODELED", { text: "senses · evidences · NOT defeats · Λ-ROE Conjecture 1", scale: 0.42, position: [0, R + 2.0, 0] }); | |
| _group.add(bb); | |
| _track(() => { if (bb.material.map) bb.material.map.dispose(); bb.material.dispose(); }); | |
| } catch (_) {} | |
| const badge = buildOverlay(); | |
| renderPosture(); renderVerdict(); renderCompute(); renderRid(); renderGates(); renderFingerprint(); | |
| // per-frame animation (rotate the estate slowly + run demo updaters). The shell's | |
| // Stage has no offFrame(), so we self-guard: once unmounted, _group is null and the | |
| // callback no-ops (the shell disposes the renderer/loop on tab switch). | |
| let _t = 0; | |
| _frameOff = () => { | |
| if (!_group) return; | |
| _t += 0.016; | |
| _group.rotation.y += 0.0009; | |
| for (const u of updaters) { try { u(_t); } catch (_) {} } | |
| // pulse SENSED hostile/suspect rings | |
| if (_trackGroup && _trackGroup.userData.entities) { | |
| for (const e of _trackGroup.userData.entities) { | |
| if (e.ring) { e.ring.material.opacity = 0.35 + 0.3 * (0.5 + 0.5 * Math.sin(_t * 3)); } | |
| if (e.marker) e.marker.rotation.y += 0.02; | |
| } | |
| } | |
| }; | |
| _stage.onFrame(_frameOff); | |
| // ── wire LIVE data (never fabricate; degrade gracefully) ── | |
| // primary endpoint drives the badge | |
| _handles.push(ctx.live.poll(EP.evaluate, 6000, (json, meta) => { | |
| _state.evaluate = json; _state.meta.evaluate = meta; renderVerdict(); | |
| }, { badge })); | |
| _handles.push(ctx.live.poll(EP.telemetry, 5000, (json, meta) => { | |
| _state.telemetry = json; _state.meta.telemetry = meta; rebuildTracks(); renderRid(); renderFingerprint(); | |
| })); | |
| _handles.push(ctx.live.poll(EP.cued, 7000, (json) => { | |
| _state.cued = json; rebuildTracks(); renderRid(); | |
| })); | |
| _handles.push(ctx.live.poll(EP.airpicture, 12000, (json) => { | |
| _state.airpicture = json; rebuildTracks(); | |
| })); | |
| _handles.push(ctx.live.poll(EP.gates, 30000, (json) => { | |
| _state.gates = json; renderGates(); | |
| })); | |
| // Governed MODELED compute + Λ-ROE advisory gate (Conjecture 1, gray never green). | |
| _handles.push(ctx.live.poll(EP.compute, 8000, (json) => { | |
| _state.compute = json; renderCompute(); | |
| })); | |
| // 53-fingerprint DB (static vendored; fetched once) | |
| fetch(EP.dronesDb, { headers: { accept: "application/json" } }) | |
| .then((r) => r.ok ? r.json() : null) | |
| .then((db) => { if (db) { _state.db = db; _state.dbCount = db.length; renderFingerprint(); } }) | |
| .catch(() => {}); | |
| return { id: ID, started: true }; | |
| } | |
| function unmount() { | |
| for (const h of _handles) { try { h.stop(); } catch (_) {} } | |
| _handles = []; | |
| _frameOff = null; | |
| for (const d of _disposables) { try { d(); } catch (_) {} } | |
| _disposables = []; | |
| try { if (_show) _show.destroy(); } catch (_) {} | |
| try { if (_overlay && _overlay.parentNode) _overlay.parentNode.removeChild(_overlay); } catch (_) {} | |
| try { if (_group && _stage) _stage.scene.remove(_group); } catch (_) {} | |
| _overlay = _show = null; _group = null; _globe = null; _trackGroup = null; | |
| _verdictBeam = null; _dsseLock = null; _stage = null; THREE = null; _ctx = null; | |
| _computePanel = null; S.label = null; S.compute = null; S.roe = null; | |
| _state = { evaluate: null, telemetry: null, cued: null, airpicture: null, gates: null, db: null, compute: null, meta: {}, dbCount: 0 }; | |
| } | |
| export default { id: ID, title: TITLE, endpoints: [EP.evaluate, EP.telemetry, EP.cued, EP.airpicture, EP.gates, EP.compute], mount, unmount }; | |