import json import math import time import hashlib from pathlib import Path from datetime import datetime, timezone from typing import Any, Dict, List, Tuple import gradio as gr import requests from huggingface_hub import HfApi, hf_hub_download # ----------------------------------------------------------------------------- # CROVIA — CEP TERMINAL v2: EVIDENCE MACHINE # World's first AI forensic evidence console # Temporal proof + cryptographic anchoring + regulatory mapping + citation # ----------------------------------------------------------------------------- CEP_DATASET_ID = "Crovia/cep-capsules" REGISTRY_URL = "https://registry.croviatrust.com" OPEN_EVIDENCE_MODE = True # --- Caches --- _CACHE = { "tpa": {"ts": 0.0, "data": None}, "lineage": {"ts": 0.0, "data": None}, "outreach": {"ts": 0.0, "data": None}, "capsules": {"ts": 0.0, "data": []}, } _TTL = 300 # 5 min def _now(): return time.time() def _nowz(): return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") def _sha256_hex(b: bytes) -> str: return hashlib.sha256(b).hexdigest() def _canonical_json(obj): return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False) def _canonical_hash(obj): return _sha256_hex(_canonical_json(obj).encode("utf-8")) # --- Registry Data Fetchers --- def _fetch_cached(key, url): now = _now() if now - _CACHE[key]["ts"] < _TTL and _CACHE[key]["data"] is not None: return _CACHE[key]["data"] try: resp = requests.get(url, timeout=8) data = resp.json() _CACHE[key]["ts"] = now _CACHE[key]["data"] = data return data except Exception: return _CACHE[key]["data"] or {} def fetch_tpa(): return _fetch_cached("tpa", f"{REGISTRY_URL}/registry/data/tpa_latest.json") def fetch_lineage(): return _fetch_cached("lineage", f"{REGISTRY_URL}/registry/data/lineage_graph.json") def fetch_outreach(): return _fetch_cached("outreach", f"{REGISTRY_URL}/registry/data/outreach_status.json") def _list_capsules() -> List[str]: now = _now() if (now - _CACHE["capsules"]["ts"]) < _TTL and _CACHE["capsules"]["data"]: return _CACHE["capsules"]["data"] items = [] try: files = HfApi().list_repo_files(repo_id=CEP_DATASET_ID, repo_type="dataset") for f in files: if f.endswith(".json") and f.startswith("CEP-") and not f.lower().endswith("index.json"): items.append(Path(f).stem) items = sorted(set(items))[:350] except Exception: items = [] _CACHE["capsules"]["ts"] = now _CACHE["capsules"]["data"] = items return items # --- Evidence Computation --- def get_model_tpa(model_id: str) -> dict: """Get TPA data for a specific model.""" tpa = fetch_tpa() tpas = tpa.get("tpas", []) for t in tpas: if t.get("model_id", "").lower() == model_id.lower(): return t return {} def get_model_lineage(model_id: str) -> dict: """Get lineage node for a model.""" lg = fetch_lineage() for node in lg.get("nodes", []): if node.get("id", "").lower() == model_id.lower(): return node return {} def get_model_outreach(model_id: str) -> dict: """Get outreach status for a model's org.""" org = model_id.split("/")[0] if "/" in model_id else "" if not org: return {} outreach = fetch_outreach() entries = outreach if isinstance(outreach, list) else outreach.get("entries", outreach.get("organizations", [])) if isinstance(entries, list): for e in entries: oid = e.get("org", e.get("organization", "")) if oid.lower() == org.lower(): return e return {} def compute_evidence_strength(tpa_entry: dict) -> dict: """Compute evidence strength from NEC# observations.""" obs = tpa_entry.get("observations", []) if not obs: return {"score": 0, "total": 0, "present": 0, "absent": 0, "critical_gaps": 0} total = len(obs) present = sum(1 for o in obs if o.get("is_present")) absent = total - present critical = sum(1 for o in obs if not o.get("is_present") and o.get("severity_label") == "CRITICAL") score = round((present / total) * 100, 1) if total > 0 else 0 return { "score": score, "total": total, "present": present, "absent": absent, "critical_gaps": critical, } def compute_peer_context(model_id: str) -> dict: """Compute peer comparison context.""" tpa = fetch_tpa() tpas = tpa.get("tpas", []) org = model_id.split("/")[0] if "/" in model_id else "" all_scores = [] org_scores = [] for t in tpas: obs = t.get("observations", []) if not obs: continue s = sum(1 for o in obs if o.get("is_present")) / len(obs) * 100 all_scores.append(s) tid = t.get("model_id", "") if "/" in tid and tid.split("/")[0].lower() == org.lower(): org_scores.append(s) return { "industry_avg": round(sum(all_scores) / len(all_scores), 1) if all_scores else 0, "org_avg": round(sum(org_scores) / len(org_scores), 1) if org_scores else 0, "total_models": len(all_scores), "org_models": len(org_scores), } # --- Main Evidence Function --- def generate_evidence(model_id: str) -> str: """Generate complete forensic evidence package for a model.""" model_id = (model_id or "").split("|")[0].strip() if not model_id: return "" tpa_entry = get_model_tpa(model_id) lineage = get_model_lineage(model_id) outreach = get_model_outreach(model_id) strength = compute_evidence_strength(tpa_entry) peer = compute_peer_context(model_id) tpa_data = fetch_tpa() chain_height = tpa_data.get("chain_height", 0) # Build observations detail obs_detail = [] jurisdictions = set() for o in tpa_entry.get("observations", []): obs_detail.append({ "nec_id": o.get("necessity_id", ""), "name": o.get("necessity_name", ""), "present": o.get("is_present", False), "severity": o.get("severity_label", ""), "jurisdictions": o.get("jurisdictions_affected", 0), "jurisdiction_hints": o.get("jurisdictions_hint", []), "commitment_x": o.get("commitment_x", ""), "commitment_y": o.get("commitment_y", ""), }) for j in o.get("jurisdictions_hint", []): jurisdictions.add(j) # Trust level if strength["score"] >= 80: trust = "GREEN" elif strength["score"] >= 40: trust = "YELLOW" else: trust = "RED" org = model_id.split("/")[0] if "/" in model_id else "unknown" # Citation text citation = ( f"As of {_nowz()}, model {model_id} published by {org} " f"has been monitored by the Crovia Temporal Proof Registry. " f"{strength['absent']}/{strength['total']} NEC# documentation requirements " f"remain absent ({strength['critical_gaps']} critical). " f"Cryptographic anchor: chain height {chain_height}, " f"TPA-ID {tpa_entry.get('tpa_id', 'N/A')}. " f"Source: registry.croviatrust.com" ) payload = { "model_id": model_id, "org": org, "timestamp": _nowz(), "found": bool(tpa_entry), "trust_level": trust if tpa_entry else "UNKNOWN", "tpa_id": tpa_entry.get("tpa_id", ""), "chain_height": chain_height, "strength": strength, "peer": peer, "observations": obs_detail, "jurisdictions": sorted(jurisdictions), "lineage": { "compliance_score": lineage.get("compliance_score"), "severity": lineage.get("severity"), "nec_absent": lineage.get("nec_absent"), "card_length": lineage.get("card_length"), } if lineage else None, "outreach": { "status": outreach.get("status", outreach.get("outreach_status", "unknown")), "contacted": outreach.get("contacted", outreach.get("discussion_sent", False)), "response": outreach.get("response", outreach.get("response_received", False)), } if outreach else None, "citation": citation, } return json.dumps(payload, ensure_ascii=False) # --- Startup data --- def get_targets_list() -> str: """Get list of all monitored targets for autocomplete.""" tpa = fetch_tpa() tpas = tpa.get("tpas", []) targets = [] for t in tpas: mid = t.get("model_id", "") if mid: obs = t.get("observations", []) absent = sum(1 for o in obs if not o.get("is_present")) targets.append({"id": mid, "gaps": absent}) targets.sort(key=lambda x: -x["gaps"]) return json.dumps(targets, ensure_ascii=False) def get_registry_stats() -> str: """Get registry-wide stats for the header.""" tpa = fetch_tpa() tpas = tpa.get("tpas", []) lg = fetch_lineage() models = set() orgs = set() total_gaps = 0 for t in tpas: mid = t.get("model_id", "") models.add(mid) if "/" in mid: orgs.add(mid.split("/")[0]) for o in t.get("observations", []): if not o.get("is_present"): total_gaps += 1 return json.dumps({ "models": len(models), "orgs": len(orgs), "chain_height": tpa.get("chain_height", 0), "total_gaps": total_gaps, "lineage_nodes": len(lg.get("nodes", [])), }, ensure_ascii=False) # --- Live Capsule Generator --- def generate_live_capsule(model_id: str) -> str: """Generate a live evidence capsule from TPA registry data.""" try: model_id = (model_id or "").split("|")[0].strip() if not model_id: return "" tpa_entry = get_model_tpa(model_id) if not tpa_entry: return json.dumps({"error": f"No TPA data found for {model_id}"}) lineage = get_model_lineage(model_id) outreach = get_model_outreach(model_id) strength = compute_evidence_strength(tpa_entry) tpa_data = fetch_tpa() chain_height = tpa_data.get("chain_height", 0) org = model_id.split("/")[0] if "/" in model_id else "unknown" ts = _nowz() # Build observations with full commitment data observations = [] for o in tpa_entry.get("observations", []): observations.append({ "necessity_id": o.get("necessity_id", ""), "necessity_name": o.get("necessity_name", ""), "is_present": o.get("is_present", False), "severity_label": o.get("severity_label", ""), "severity_weight": o.get("severity_weight", 0), "jurisdictions_affected": o.get("jurisdictions_affected", 0), "commitment": { "x": o.get("commitment_x", ""), "y": o.get("commitment_y", ""), }, }) # Compute capsule content hash capsule_content = { "model_id": model_id, "tpa_id": tpa_entry.get("tpa_id", ""), "observations": observations, "chain_height": chain_height, "generated": ts, } content_hash = _canonical_hash(capsule_content) # Build CRC-1-like capsule capsule = { "type": "live_capsule", "schema": "crovia_evidence_capsule.v2", "capsule_id": f"CEP-LIVE-{content_hash[:8].upper()}", "generated": ts, "mode": "OPEN_EVIDENCE", "model": { "model_id": model_id, "organization": org, "source": "huggingface.co", }, "evidence": { "tpa_id": tpa_entry.get("tpa_id", ""), "chain_height": chain_height, "anchor_hash": tpa_entry.get("anchor_hash", ""), "epoch": tpa_entry.get("epoch", ""), "observation_count": len(observations), "present_count": strength["present"], "absent_count": strength["absent"], "critical_gaps": strength["critical_gaps"], "evidence_strength": strength["score"], "observations": observations, }, "cryptographic_anchors": { "type": "pedersen_secp256k1", "commitment_count": sum(1 for o in observations if o["commitment"]["x"]), "chain_type": "sha256_temporal", "chain_height": chain_height, "anchor_hash": tpa_entry.get("anchor_hash", ""), "merkle_root": tpa_entry.get("merkle_root", ""), }, "lineage": { "compliance_score": lineage.get("compliance_score"), "severity": lineage.get("severity"), "card_length": lineage.get("card_length"), } if lineage else None, "outreach": { "status": outreach.get("status", outreach.get("outreach_status", "unknown")), "contacted": outreach.get("contacted", outreach.get("discussion_sent", False)), } if outreach else None, "content_hash": content_hash, "trust_level": "GREEN" if strength["score"] >= 80 else "YELLOW" if strength["score"] >= 40 else "RED", } return json.dumps(capsule, ensure_ascii=False) except Exception as e: return json.dumps({"error": f"capsule_gen: {str(e)}"}) # --- Capsule Inspector (backward compat) --- def fetch_capsule(cep_id: str) -> Dict[str, Any]: path = hf_hub_download(repo_id=CEP_DATASET_ID, filename=f"{cep_id}.json", repo_type="dataset") with open(path, "r", encoding="utf-8") as f: return json.load(f) def inspect_capsule(cep_id: str) -> str: cep_id = (cep_id or "").strip() if not cep_id: return json.dumps({"error": "empty"}) try: cap = fetch_capsule(cep_id) schema = cap.get("schema", "unknown") model = cap.get("model", {}) model_id = model.get("model_id", "unknown") if isinstance(model, dict) else "unknown" evidence = cap.get("evidence", {}) if isinstance(cap.get("evidence"), dict) else {} meta = cap.get("meta", {}) if isinstance(cap.get("meta"), dict) else {} hashchain_root = meta.get("hashchain_sha256", "") sig_present = "signature" in cap cap_sha = _sha256_hex(_canonical_json(cap).encode("utf-8")) return json.dumps({ "type": "capsule", "cep_id": cep_id, "schema": schema, "model_id": model_id, "evidence_nodes": len(evidence), "signature": sig_present, "hashchain": bool(hashchain_root), "hashchain_short": hashchain_root[:16] if hashchain_root else "", "capsule_sha256": cap_sha, "evidence_keys": list(evidence.keys())[:20], }, ensure_ascii=False) except Exception as e: return json.dumps({"error": f"{type(e).__name__}: {e}"}) # ============================================================================= # CSS # ============================================================================= CSS = """ """ # ============================================================================= # HTML # ============================================================================= UI_HTML = """
Crovia · Evidence Machine
AI Forensic Evidence Console
LIVE · models · chain:
INVESTIGATING:
Trust Level
Evidence Strength
Chain Height
NEC# Gaps
Jurisdictions
NEC# Constellation
NEC# Element Grid
20 elements
Forensic Report
LIVE
Select a model to generate forensic evidence report.
Jurisdictions & Outreach
Applicable Jurisdictions
Outreach Status
Select a model to see outreach status
Peer Context
Citation Engine
LEGAL-GRADE
Select a model to generate a citation-ready evidence statement.
Evidence Package ready — Download the complete forensic evidence as JSON for legal, journalistic, or audit use.
CROVIA EVIDENCE MACHINE · Observation, not judgment. All data derived from publicly observable artifacts. No model audit. No legal claim. Presence/absence only. Cryptographic commitments anchor observations immutably.
Source: registry.croviatrust.com · Crovia/cep-capsules
""" # ============================================================================= # JAVASCRIPT # ============================================================================= JS = r""" () => { const $ = q => document.querySelector(q); const $$ = q => document.querySelectorAll(q); // --- Tab switching --- window.switchTab = function(tab) { document.querySelectorAll('.ev-tab').forEach(t => t.classList.remove('active')); document.querySelector('#tab-' + tab).classList.add('active'); document.querySelector('#panel-evidence').style.display = tab === 'evidence' ? 'block' : 'none'; document.querySelector('#panel-capsules').style.display = tab === 'capsules' ? 'block' : 'none'; }; // --- Current payloads for download --- let _currentPayload = null; let _currentLiveCapsule = null; let _pendingCapsuleModel = null; // --- Copy citation --- window.copyCitation = function() { const text = $('#ev-citation').textContent; navigator.clipboard.writeText(text).then(() => { const btn = $('#ev-citation-copy'); btn.textContent = 'COPIED \u2713'; setTimeout(() => btn.textContent = 'COPY', 2000); }); }; // --- Download evidence package --- window.downloadEvidence = function() { if (!_currentPayload) return; const blob = new Blob([JSON.stringify(_currentPayload, null, 2)], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const safe = (_currentPayload.model_id || 'unknown').replace(/\//g, '_'); a.download = 'crovia_evidence_' + safe + '_' + new Date().toISOString().slice(0,10) + '.json'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; // --- Download live capsule --- window.downloadLiveCapsule = function() { if (!_currentLiveCapsule) return; const blob = new Blob([JSON.stringify(_currentLiveCapsule, null, 2)], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const cid = _currentLiveCapsule.capsule_id || 'unknown'; a.download = cid + '.json'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; // --- Build live capsule report text --- function buildLiveCapsuleReport(c) { const lines = []; lines.push('CROVIA \u00b7 LIVE EVIDENCE CAPSULE'); lines.push('\u2550'.repeat(50)); lines.push(''); lines.push('CAPSULE-ID: ' + c.capsule_id); lines.push('SCHEMA: ' + c.schema); lines.push('MODE: ' + c.mode); lines.push('GENERATED: ' + c.generated); lines.push('CONTENT HASH: ' + c.content_hash); lines.push(''); lines.push('\u2500\u2500\u2500 MODEL \u2500\u2500\u2500'); lines.push('ID: ' + c.model.model_id); lines.push('ORG: ' + c.model.organization); lines.push('SOURCE: ' + c.model.source); lines.push('TRUST LEVEL: ' + c.trust_level); lines.push(''); lines.push('\u2500\u2500\u2500 EVIDENCE \u2500\u2500\u2500'); lines.push('TPA-ID: ' + c.evidence.tpa_id); lines.push('CHAIN HEIGHT: ' + c.evidence.chain_height); lines.push('EPOCH: ' + (c.evidence.epoch || 'N/A')); lines.push('OBSERVATIONS: ' + c.evidence.observation_count); lines.push('PRESENT: ' + c.evidence.present_count + '/' + c.evidence.observation_count); lines.push('ABSENT: ' + c.evidence.absent_count + ' (' + c.evidence.critical_gaps + ' critical)'); lines.push('STRENGTH: ' + c.evidence.evidence_strength + '%'); lines.push(''); lines.push('\u2500\u2500\u2500 NEC# OBSERVATIONS \u2500\u2500\u2500'); (c.evidence.observations || []).forEach(function(o) { var icon = o.is_present ? '\u2713' : '\u2717'; var sev = o.is_present ? '' : ' [' + o.severity_label + ']'; lines.push(icon + ' ' + (o.necessity_id || '').padEnd(7) + (o.necessity_name || '').substring(0,45) + sev); }); lines.push(''); lines.push('\u2500\u2500\u2500 CRYPTOGRAPHIC ANCHORS \u2500\u2500\u2500'); lines.push('TYPE: ' + c.cryptographic_anchors.type); lines.push('COMMITMENTS: ' + c.cryptographic_anchors.commitment_count); lines.push('CHAIN TYPE: ' + c.cryptographic_anchors.chain_type); lines.push('CHAIN HEIGHT: ' + c.cryptographic_anchors.chain_height); if (c.cryptographic_anchors.anchor_hash) { lines.push('ANCHOR: ' + c.cryptographic_anchors.anchor_hash.substring(0,32) + '...'); } if (c.cryptographic_anchors.merkle_root) { lines.push('MERKLE ROOT: ' + c.cryptographic_anchors.merkle_root.substring(0,32) + '...'); } lines.push(''); var withCx = (c.evidence.observations || []).filter(function(o) { return o.commitment && o.commitment.x; }); if (withCx.length > 0) { lines.push('\u2500\u2500\u2500 PEDERSEN COMMITMENTS (sample) \u2500\u2500\u2500'); withCx.slice(0, 4).forEach(function(o) { lines.push(o.necessity_id + ':'); lines.push(' Cx: ' + o.commitment.x.substring(0,40) + '...'); lines.push(' Cy: ' + o.commitment.y.substring(0,40) + '...'); }); if (withCx.length > 4) lines.push(' ... + ' + (withCx.length - 4) + ' more commitments'); lines.push(''); } if (c.lineage) { lines.push('\u2500\u2500\u2500 LINEAGE \u2500\u2500\u2500'); lines.push('COMPLIANCE: ' + c.lineage.compliance_score); lines.push('SEVERITY: ' + c.lineage.severity); lines.push(''); } if (c.outreach) { lines.push('\u2500\u2500\u2500 OUTREACH \u2500\u2500\u2500'); lines.push('STATUS: ' + c.outreach.status); lines.push('CONTACTED: ' + (c.outreach.contacted ? 'YES' : 'NO')); lines.push(''); } lines.push('\u2500\u2500\u2500 DISCLAIMER \u2500\u2500\u2500'); lines.push('Observation, not judgment. All data from public artifacts.'); lines.push('Capsule generated in OPEN EVIDENCE MODE (read-only).'); return lines.join('\n'); } // --- Reset search --- window.resetSearch = function() { _currentPayload = null; $('#ev-search').value = ''; $('#ev-active-model').style.display = 'none'; $('#sig-trust-val').textContent = '\u2014'; $('#sig-trust').className = 'ev-signal trust-unknown'; $('#sig-strength-val').textContent = '\u2014'; $('#sig-chain').textContent = '\u2014'; $('#sig-gaps').textContent = '\u2014'; $('#sig-juris').textContent = '\u2014'; $('#ev-meter').style.display = 'none'; drawConstellation([]); buildNecGrid([]); $('#ev-report').textContent = 'Select a model to generate forensic evidence report.'; $('#ev-juris').innerHTML = '\u2014'; $('#ev-outreach').innerHTML = '
Select a model to see outreach status'; $('#ev-peer').innerHTML = '\u2014'; $('#ev-citation').textContent = 'Select a model to generate a citation-ready evidence statement.'; const dlBar = $('#ev-download-bar'); if (dlBar) dlBar.style.display = 'none'; $('#nec-badge').textContent = '\u2014'; $('#nec-badge').style.background = 'var(--bg2)'; $('#nec-badge').style.color = 'var(--text3)'; $('#ev-search').focus(); }; // --- Draw NEC# Constellation --- function drawConstellation(obs) { const svg = $('#ev-constellation'); if (!svg) return; while (svg.firstChild) svg.removeChild(svg.firstChild); const W = 600, H = 380; const cx = W / 2, cy = H / 2; const R = 140; // Background glow const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); const rg = document.createElementNS("http://www.w3.org/2000/svg", "radialGradient"); rg.id = "cg"; rg.setAttribute("cx","50%"); rg.setAttribute("cy","50%"); rg.setAttribute("r","50%"); const s1 = document.createElementNS("http://www.w3.org/2000/svg","stop"); s1.setAttribute("offset","0%"); s1.setAttribute("stop-color","rgba(34,211,238,0.08)"); const s2 = document.createElementNS("http://www.w3.org/2000/svg","stop"); s2.setAttribute("offset","100%"); s2.setAttribute("stop-color","transparent"); rg.appendChild(s1); rg.appendChild(s2); defs.appendChild(rg); svg.appendChild(defs); const bgc = document.createElementNS("http://www.w3.org/2000/svg","circle"); bgc.setAttribute("cx",cx); bgc.setAttribute("cy",cy); bgc.setAttribute("r", R + 30); bgc.setAttribute("fill","url(#cg)"); svg.appendChild(bgc); // Center node const cc = document.createElementNS("http://www.w3.org/2000/svg","circle"); cc.setAttribute("cx",cx); cc.setAttribute("cy",cy); cc.setAttribute("r",16); cc.setAttribute("fill","#22d3ee"); cc.setAttribute("opacity","0.9"); svg.appendChild(cc); const ct = document.createElementNS("http://www.w3.org/2000/svg","text"); ct.setAttribute("x",cx); ct.setAttribute("y",cy+4); ct.setAttribute("text-anchor","middle"); ct.setAttribute("fill","#030712"); ct.setAttribute("font-size","9"); ct.setAttribute("font-weight","800"); ct.textContent = "MODEL"; svg.appendChild(ct); if (!obs || !obs.length) { const nt = document.createElementNS("http://www.w3.org/2000/svg","text"); nt.setAttribute("x",cx); nt.setAttribute("y",cy+45); nt.setAttribute("text-anchor","middle"); nt.setAttribute("fill","#9ca3af"); nt.setAttribute("font-size","12"); nt.textContent = "Select a model to illuminate the constellation"; svg.appendChild(nt); return; } const n = obs.length; obs.forEach((o, i) => { const angle = (Math.PI * 2 * i / n) - Math.PI / 2; const x = cx + R * Math.cos(angle); const y = cy + R * Math.sin(angle); // Connection line const line = document.createElementNS("http://www.w3.org/2000/svg","line"); line.setAttribute("x1",cx); line.setAttribute("y1",cy); line.setAttribute("x2",x); line.setAttribute("y2",y); line.setAttribute("stroke", o.present ? "rgba(34,197,94,0.25)" : "rgba(239,68,68,0.25)"); line.setAttribute("stroke-width", o.severity === "CRITICAL" && !o.present ? "2" : "1"); svg.appendChild(line); // Node const r = o.severity === "CRITICAL" && !o.present ? 14 : 10; const nc = document.createElementNS("http://www.w3.org/2000/svg","circle"); nc.setAttribute("cx",x); nc.setAttribute("cy",y); nc.setAttribute("r",r); if (o.present) { nc.setAttribute("fill","rgba(34,197,94,0.2)"); nc.setAttribute("stroke","#22c55e"); nc.setAttribute("stroke-width","2"); } else if (o.severity === "CRITICAL") { nc.setAttribute("fill","rgba(239,68,68,0.2)"); nc.setAttribute("stroke","#ef4444"); nc.setAttribute("stroke-width","2"); } else { nc.setAttribute("fill","rgba(245,158,11,0.15)"); nc.setAttribute("stroke","#f59e0b"); nc.setAttribute("stroke-width","1.5"); } svg.appendChild(nc); // Label const lt = document.createElementNS("http://www.w3.org/2000/svg","text"); lt.setAttribute("x",x); lt.setAttribute("y", y + (y < cy ? -r-6 : r+14)); lt.setAttribute("text-anchor","middle"); lt.setAttribute("fill", o.present ? "#22c55e" : o.severity==="CRITICAL" ? "#ef4444" : "#f59e0b"); lt.setAttribute("font-size","9"); lt.setAttribute("font-weight","700"); lt.setAttribute("font-family","JetBrains Mono, monospace"); lt.textContent = o.nec_id; svg.appendChild(lt); }); } // --- Build NEC# Grid --- function buildNecGrid(obs) { const grid = $('#nec-grid'); if (!grid) return; grid.innerHTML = ''; if (!obs || !obs.length) { grid.innerHTML = '
Select a model
'; return; } obs.forEach(o => { const cls = o.present ? 'present' : (o.severity === 'CRITICAL' ? 'critical' : 'absent'); const cell = document.createElement('div'); cell.className = 'nec-cell ' + cls; cell.title = o.name + ' | ' + o.severity + ' | ' + o.jurisdictions + ' jurisdictions'; cell.innerHTML = '
' + o.nec_id + '
' + '
' + (o.present ? '✓' : '✗') + '
' + '
' + o.severity + '
'; grid.appendChild(cell); }); } // --- Build Forensic Report --- function buildReport(p) { const lines = []; lines.push('CROVIA EVIDENCE MACHINE — FORENSIC REPORT'); lines.push('═══════════════════════════════════════════'); lines.push(''); lines.push('TARGET: ' + p.model_id); lines.push('ORG: ' + p.org); lines.push('TIMESTAMP: ' + p.timestamp); lines.push('TPA-ID: ' + (p.tpa_id || 'N/A')); lines.push('CHAIN: ' + p.chain_height); lines.push(''); lines.push('TRUST LEVEL: ' + p.trust_level); lines.push('EVIDENCE: ' + p.strength.present + '/' + p.strength.total + ' NEC# present'); lines.push('GAPS: ' + p.strength.absent + ' (' + p.strength.critical_gaps + ' critical)'); lines.push('STRENGTH: ' + p.strength.score + '%'); lines.push(''); lines.push('─── NEC# OBSERVATIONS ───'); (p.observations || []).forEach(o => { const icon = o.present ? '✓' : '✗'; const sev = o.present ? '' : ' [' + o.severity + ']'; lines.push(icon + ' ' + o.nec_id.padEnd(7) + o.name.substring(0,50) + sev); }); lines.push(''); lines.push('─── CRYPTOGRAPHIC ANCHORS ───'); (p.observations || []).filter(o => o.commitment_x).slice(0,5).forEach(o => { lines.push(o.nec_id + ' Cx: ' + o.commitment_x.substring(0,24) + '...'); lines.push(' Cy: ' + o.commitment_y.substring(0,24) + '...'); }); if (p.observations && p.observations.length > 5) { lines.push('... + ' + (p.observations.length - 5) + ' more Pedersen commitments'); } lines.push(''); if (p.lineage) { lines.push('─── LINEAGE DATA ───'); lines.push('Compliance Score: ' + p.lineage.compliance_score); lines.push('Severity: ' + p.lineage.severity); lines.push('NEC Absent: ' + p.lineage.nec_absent); lines.push(''); } lines.push('─── DISCLAIMER ───'); lines.push('Observation, not judgment. All data from public artifacts.'); return lines.join('\n'); } // --- Update UI from payload --- function updateUI(p) { if (!p || p.error) { $('#ev-report').textContent = 'Error: ' + (p ? p.error : 'unknown'); return; } // Store for download _currentPayload = p; // --- LIVE CAPSULE: if pending, build capsule from this payload --- if (_pendingCapsuleModel && p.model_id === _pendingCapsuleModel) { const capsule = { type: 'live_capsule', schema: 'crovia_evidence_capsule.v2', capsule_id: 'CEP-LIVE-' + (p.content_hash || '').substring(0,8).toUpperCase(), generated: p.timestamp || new Date().toISOString(), mode: 'OPEN_EVIDENCE', model: { model_id: p.model_id, organization: p.org || '', source: 'huggingface.co' }, evidence: { tpa_id: p.tpa_id || '', chain_height: p.chain_height || 0, observation_count: (p.observations || []).length, present_count: p.strength ? p.strength.present : 0, absent_count: p.strength ? p.strength.absent : 0, critical_gaps: p.strength ? p.strength.critical_gaps : 0, evidence_strength: p.strength ? p.strength.score : 0, observations: (p.observations || []).map(o => ({ necessity_id: o.necessity_id || '', necessity_name: o.necessity_name || '', is_present: o.is_present || false, severity_label: o.severity_label || '', severity_weight: o.severity_weight || 0, commitment: { x: o.commitment_x || '', y: o.commitment_y || '' } })), }, cryptographic_anchors: { type: 'pedersen_secp256k1', chain_type: 'sha256_temporal', chain_height: p.chain_height || 0, anchor_hash: p.anchor_hash || '', merkle_root: p.merkle_root || '', }, trust_level: p.trust_level || 'UNKNOWN', content_hash: p.content_hash || '', jurisdictions: p.jurisdictions || [], citation: p.citation || '', }; _currentLiveCapsule = capsule; const report = $('#live-capsule-report'); if (report) report.textContent = buildLiveCapsuleReport(capsule); const dlBtn = $('#live-capsule-download'); if (dlBtn) dlBtn.style.display = 'inline-block'; const badge = $('#live-capsule-badge'); if (badge) { const tl = capsule.trust_level; badge.textContent = tl; badge.style.background = tl === 'GREEN' ? 'rgba(34,197,94,0.15)' : tl === 'RED' ? 'rgba(239,68,68,0.15)' : 'rgba(245,158,11,0.15)'; badge.style.color = tl === 'GREEN' ? '#22c55e' : tl === 'RED' ? '#ef4444' : '#f59e0b'; } _pendingCapsuleModel = null; } // Active model banner $('#ev-active-id').textContent = p.model_id; $('#ev-active-model').style.display = 'flex'; // Download bar const dlBar = $('#ev-download-bar'); if (dlBar) dlBar.style.display = 'flex'; // Signals const trustEl = $('#sig-trust'); trustEl.className = 'ev-signal trust-' + p.trust_level.toLowerCase(); $('#sig-trust-val').textContent = p.trust_level; $('#sig-strength-val').textContent = p.strength.score + '%'; $('#sig-chain').textContent = p.chain_height.toLocaleString(); $('#sig-gaps').textContent = p.strength.absent + '/' + p.strength.total; $('#sig-juris').textContent = p.jurisdictions.length; // Meter const meter = $('#ev-meter'); meter.style.display = 'flex'; $('#meter-val').textContent = p.strength.score + '%'; const barColor = p.strength.score >= 60 ? '#22c55e' : p.strength.score >= 30 ? '#f59e0b' : '#ef4444'; $('#meter-bar').style.width = p.strength.score + '%'; $('#meter-bar').style.background = barColor; $('#meter-val').style.color = barColor + '!important'; // NEC badge $('#nec-badge').textContent = p.strength.present + '/' + p.strength.total + ' present'; $('#nec-badge').style.background = p.trust_level === 'GREEN' ? 'rgba(34,197,94,0.15)' : 'rgba(239,68,68,0.15)'; $('#nec-badge').style.color = p.trust_level === 'GREEN' ? '#22c55e' : '#ef4444'; // Constellation + Grid drawConstellation(p.observations); buildNecGrid(p.observations); // Report $('#ev-report').textContent = buildReport(p); // Jurisdictions const jurisEl = $('#ev-juris'); jurisEl.innerHTML = ''; (p.jurisdictions || []).forEach(j => { const tag = document.createElement('span'); tag.className = 'ev-juris-tag'; tag.textContent = j; jurisEl.appendChild(tag); }); if (!p.jurisdictions || !p.jurisdictions.length) { jurisEl.innerHTML = 'No jurisdictions identified'; } // Outreach const outEl = $('#ev-outreach'); if (p.outreach) { const contacted = p.outreach.contacted; const responded = p.outreach.response; const color = responded ? '#22c55e' : contacted ? '#f59e0b' : '#ef4444'; const text = responded ? 'Contacted · Response received' : contacted ? 'Contacted · Awaiting response' : 'Not yet contacted'; outEl.innerHTML = '
' + '' + text + ''; } else { outEl.innerHTML = '
' + 'No outreach data for this organization'; } // Peer context const peerEl = $('#ev-peer'); if (p.peer) { peerEl.innerHTML = '
Industry avg: ' + p.peer.industry_avg + '% (' + p.peer.total_models + ' models)
' + '
Org avg (' + p.peer.org_models + ' models): ' + p.peer.org_avg + '%
'; } // Citation $('#ev-citation').textContent = p.citation; } // --- Payload watcher --- function attachWatcher() { const root = document.querySelector('#ev_payload'); if (!root) return false; const input = root.querySelector('textarea, input'); if (!input) return false; let last = ''; const tick = () => { const val = input.value || ''; if (val && val !== last) { last = val; try { updateUI(JSON.parse(val)); } catch(e) {} } }; input.addEventListener('input', tick); setInterval(tick, 200); return true; } // --- Stats watcher --- function attachStatsWatcher() { const root = document.querySelector('#ev_stats'); if (!root) return false; const input = root.querySelector('textarea, input'); if (!input || !input.value) return false; try { const s = JSON.parse(input.value); $('#ev-stats-models').textContent = s.models; $('#ev-stats-chain').textContent = s.chain_height.toLocaleString(); } catch(e) {} return true; } // --- Targets watcher --- let _targetsPopulated = false; function attachTargetsWatcher() { const root = document.querySelector('#ev_targets'); if (!root) return false; const input = root.querySelector('textarea, input'); if (!input || !input.value) return false; if (_targetsPopulated) return true; try { const targets = JSON.parse(input.value); // Quick target buttons (top 8) const quick = $('#ev-quick-targets'); if (quick) { targets.slice(0, 8).forEach(t => { const btn = document.createElement('button'); btn.className = 'ev-quick-btn'; btn.textContent = t.id.split('/').pop().substring(0,16) + ' (' + t.gaps + ' gaps)'; btn.title = t.id; btn.addEventListener('click', () => { $('#ev-search').value = t.id; triggerSearch(t.id); }); quick.appendChild(btn); }); } // Model browser dropdown const sel = $('#ev-model-select'); if (sel) { // Group by org const byOrg = {}; targets.forEach(t => { const parts = t.id.split('/'); const org = parts.length > 1 ? parts[0] : 'other'; if (!byOrg[org]) byOrg[org] = []; byOrg[org].push(t); }); // Sort orgs alphabetically Object.keys(byOrg).sort().forEach(org => { const grp = document.createElement('optgroup'); grp.label = org + ' (' + byOrg[org].length + ' models)'; byOrg[org].forEach(t => { const opt = document.createElement('option'); opt.value = t.id; const name = t.id.split('/').pop(); opt.textContent = name + ' — ' + t.gaps + ' gaps'; grp.appendChild(opt); }); sel.appendChild(grp); }); sel.addEventListener('change', () => { if (sel.value) { $('#ev-search').value = sel.value; triggerSearch(sel.value); } }); } // Datalist for autocomplete const dl = $('#ev-model-list'); if (dl) { targets.forEach(t => { const opt = document.createElement('option'); opt.value = t.id; dl.appendChild(opt); }); } _targetsPopulated = true; } catch(e) {} return true; } // --- Connect command bar to Gradio --- function attachCommandBar() { const searchRoot = document.querySelector('#ev_search_in'); if (!searchRoot) return false; const searchInput = searchRoot.querySelector('textarea, input'); if (!searchInput) return false; window.triggerSearch = function(val) { // Force re-trigger by clearing first, then setting after a tick searchInput.value = ''; searchInput.dispatchEvent(new Event('input', {bubbles:true})); searchInput.dispatchEvent(new Event('change', {bubbles:true})); setTimeout(() => { searchInput.value = val; searchInput.dispatchEvent(new Event('input', {bubbles:true})); searchInput.dispatchEvent(new Event('change', {bubbles:true})); }, 50); }; const doSearch = () => { const val = $('#ev-search').value.trim(); if (val) { $('#ev-search').value = val; triggerSearch(val); } }; $('#ev-go').addEventListener('click', doSearch); $('#ev-search').addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); }); return true; } // --- Capsule tab (live + reference) --- let _capsuleTabReady = false; let _liveCapsuleDropdownPopulated = false; function attachCapsuleTab() { if (_capsuleTabReady) return true; // --- LIVE CAPSULES: populate dropdown (once) --- if (!_liveCapsuleDropdownPopulated) { const targetsRoot = document.querySelector('#ev_targets'); const targetsInput = targetsRoot ? targetsRoot.querySelector('textarea, input') : null; if (!targetsInput || !targetsInput.value) return false; const liveSel = $('#live-capsule-select'); if (!liveSel) return false; try { const targets = JSON.parse(targetsInput.value); const countEl = $('#live-capsule-count'); if (countEl) countEl.textContent = targets.length + ' models'; const byOrg = {}; targets.forEach(t => { const parts = t.id.split('/'); const org = parts.length > 1 ? parts[0] : 'other'; if (!byOrg[org]) byOrg[org] = []; byOrg[org].push(t); }); Object.keys(byOrg).sort().forEach(org => { const grp = document.createElement('optgroup'); grp.label = org + ' (' + byOrg[org].length + ')'; byOrg[org].forEach(t => { const opt = document.createElement('option'); opt.value = t.id; opt.textContent = t.id.split('/').pop() + ' \u2014 ' + t.gaps + ' gaps'; grp.appendChild(opt); }); liveSel.appendChild(grp); }); } catch(e) {} // Wire dropdown: call Gradio API directly via fetch() liveSel.addEventListener('change', () => { if (!liveSel.value) return; const modelId = liveSel.value; $('#live-capsule-report').textContent = 'Generating live capsule for ' + modelId + '...'; const dlBtn = $('#live-capsule-download'); if (dlBtn) dlBtn.style.display = 'none'; // Call Gradio named API endpoint directly fetch('./api/live_capsule', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({data: [modelId]}) }) .then(r => r.json()) .then(result => { // Handle both Gradio response formats let raw = ''; if (result.data) raw = result.data[0]; else if (Array.isArray(result)) raw = result[0]; else if (typeof result === 'string') raw = result; if (!raw) { $('#live-capsule-report').textContent = 'API response: ' + JSON.stringify(result).substring(0, 200); return; } const data = typeof raw === 'string' ? JSON.parse(raw) : raw; if (data.error) { $('#live-capsule-report').textContent = 'Error: ' + data.error; return; } _currentLiveCapsule = data; $('#live-capsule-report').textContent = buildLiveCapsuleReport(data); var dlBar = $('#live-capsule-download-bar'); if (dlBar) dlBar.style.display = 'block'; const badge = $('#live-capsule-badge'); if (badge) { const tl = data.trust_level; badge.textContent = tl; badge.style.background = tl === 'GREEN' ? 'rgba(34,197,94,0.15)' : tl === 'RED' ? 'rgba(239,68,68,0.15)' : 'rgba(245,158,11,0.15)'; badge.style.color = tl === 'GREEN' ? '#22c55e' : tl === 'RED' ? '#ef4444' : '#f59e0b'; } }) .catch(err => { $('#live-capsule-report').textContent = 'Fetch error: ' + err.message; }); }); _liveCapsuleDropdownPopulated = true; } // --- REFERENCE CAPSULES --- const capsRoot = document.querySelector('#ev_capsules_json'); const capsInput = capsRoot ? capsRoot.querySelector('textarea, input') : null; if (!capsInput || !capsInput.value) return false; const sel = $('#capsule-select'); if (!sel) return false; if (sel.children.length <= 1) { try { const caps = JSON.parse(capsInput.value); const refCount = $('#ref-capsule-count'); if (refCount) refCount.textContent = caps.length + ' capsules'; caps.forEach(c => { const opt = document.createElement('option'); opt.value = c; opt.textContent = c; sel.appendChild(opt); }); } catch(e) {} } const cepRoot = document.querySelector('#ev_capsule_in'); const cepInput = cepRoot ? cepRoot.querySelector('textarea, input') : null; if (!cepInput) return false; sel.addEventListener('change', () => { cepInput.value = sel.value; cepInput.dispatchEvent(new Event('input', {bubbles:true})); }); const resRoot = document.querySelector('#ev_capsule_result'); const resInput = resRoot ? resRoot.querySelector('textarea, input') : null; if (!resInput) return false; let lastCap = ''; setInterval(() => { const val = resInput.value || ''; if (val && val !== lastCap) { lastCap = val; try { const data = JSON.parse(val); if (data.error) { $('#capsule-report').textContent = 'Error: ' + data.error; } else { const lines = []; lines.push('CROVIA \u00b7 CEP CAPSULE INSPECTOR'); lines.push('\u2550'.repeat(32)); lines.push(''); lines.push('CEP-ID: ' + data.cep_id); lines.push('Schema: ' + data.schema); lines.push('Model: ' + data.model_id); lines.push('Evidence: ' + data.evidence_nodes + ' nodes'); lines.push('Signature: ' + (data.signature ? 'PRESENT \u2713' : 'MISSING \u2717')); lines.push('Hashchain: ' + (data.hashchain ? 'sha256:' + data.hashchain_short + '...' : 'MISSING \u2717')); lines.push('Capsule SHA: ' + data.capsule_sha256); lines.push(''); lines.push('Evidence keys: ' + (data.evidence_keys || []).join(', ')); $('#capsule-report').textContent = lines.join('\n'); } } catch(e) {} } }, 200); _capsuleTabReady = true; return true; } // --- URL params --- function checkUrlParam() { const params = new URLSearchParams(window.location.search); const model = params.get('model'); if (model) { $('#ev-search').value = model; setTimeout(() => { if (window.triggerSearch) triggerSearch(model); }, 500); } } // --- Boot --- drawConstellation([]); buildNecGrid([]); const boot = setInterval(() => { const ok1 = attachWatcher(); const ok2 = attachStatsWatcher(); const ok3 = attachTargetsWatcher(); const ok4 = attachCommandBar(); const ok5 = attachCapsuleTab(); if (ok1 && ok2 && ok3 && ok4 && ok5) { clearInterval(boot); checkUrlParam(); } }, 200); return []; } """ # ============================================================================= # GRADIO APP # ============================================================================= capsules = _list_capsules() default_cap = capsules[0] if capsules else "CEP-2511-K4I7X2" stats_json = get_registry_stats() targets_json = get_targets_list() with gr.Blocks(title="CROVIA · Evidence Machine", css=CSS, js=JS) as demo: gr.HTML(UI_HTML) # Hidden Gradio components (JS reads/writes via DOM) ev_search_in = gr.Textbox(value="", visible=False, elem_id="ev_search_in") ev_payload = gr.Textbox(value="", visible=False, elem_id="ev_payload") ev_stats = gr.Textbox(value=stats_json, visible=False, elem_id="ev_stats") ev_targets = gr.Textbox(value=targets_json, visible=False, elem_id="ev_targets") # Capsule tab — reference ev_capsules_json = gr.Textbox(value=json.dumps(capsules), visible=False, elem_id="ev_capsules_json") ev_capsule_in = gr.Textbox(value=default_cap, visible=False, elem_id="ev_capsule_in") ev_capsule_result = gr.Textbox(value="", visible=False, elem_id="ev_capsule_result") # Live capsule API — exposed as named endpoint, called via fetch() from JS ev_live_in = gr.Textbox(visible=False, elem_id="ev_live_in") ev_live_out = gr.Textbox(visible=False, elem_id="ev_live_out") # Events ev_search_in.change(generate_evidence, inputs=ev_search_in, outputs=ev_payload) ev_capsule_in.change(inspect_capsule, inputs=ev_capsule_in, outputs=ev_capsule_result) ev_live_in.change(generate_live_capsule, inputs=ev_live_in, outputs=ev_live_out, api_name="live_capsule", queue=False) demo.queue() demo.launch()