<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SHIVYA: The Non-Dual Substrate | Observability Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #F8FAFC;
--card-bg: #FFFFFF;
--text-primary: #0F172A;
--text-secondary: #475569;
--border-color: #E2E8F0;
--accent-indigo: #6366F1;
--accent-teal: #14B8A6;
--accent-rose: #F43F5E;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
padding: 2.5rem;
line-height: 1.5;
}
.header {
max-width: 1200px;
margin: 0 auto 2.5rem auto;
border-bottom: 1px solid var(--border-color);
padding-bottom: 1.5rem;
}
.header h1 {
font-family: 'Outfit', sans-serif;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.02em;
}
.header p {
color: var(--text-secondary);
font-size: 1.1rem;
margin-top: 0.25rem;
}
.dashboard-grid {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: 2rem;
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 2rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
margin-bottom: 2rem;
}
.card h2 {
font-family: 'Outfit', sans-serif;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.75rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.svg-container {
display: flex;
justify-content: center;
align-items: center;
background-color: #F8FAFC;
border: 1px dashed var(--border-color);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1.5rem;
}
.control-group {
display: flex;
gap: 0.75rem;
margin-bottom: 1.25rem;
}
.control-label {
font-size: 0.85rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.5rem;
display: block;
}
input, select {
width: 100%;
padding: 0.625rem 0.875rem;
border: 1px solid var(--border-color);
border-radius: 6px;
font-family: inherit;
font-size: 0.95rem;
background-color: #FCFDFE;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent-indigo);
}
button {
padding: 0.625rem 1.25rem;
border: none;
border-radius: 6px;
font-family: inherit;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
}
.btn-primary {
background-color: var(--accent-indigo);
color: white;
}
.btn-primary:hover {
background-color: #4F46E5;
}
.btn-secondary {
background-color: #F1F5F9;
color: var(--text-primary);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background-color: #E2E8F0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.stat-box {
background: #F8FAFC;
padding: 1rem;
border-radius: 8px;
text-align: center;
border: 1px solid var(--border-color);
}
.stat-value {
font-family: 'Outfit', sans-serif;
font-size: 1.5rem;
font-weight: 700;
color: var(--accent-indigo);
}
.stat-label {
font-size: 0.75rem;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 0.25rem;
}
.node-list {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 1.5rem;
}
.node-card {
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
background: #FCFDFE;
}
.node-card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
margin-bottom: 0.5rem;
}
.node-badge {
font-size: 0.7rem;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-weight: bold;
text-transform: uppercase;
}
.badge-active { background: rgba(20, 184, 166, 0.1); color: var(--accent-teal); }
.badge-hotswap { background: rgba(244, 63, 94, 0.1); color: var(--accent-rose); animation: pulse 1s infinite alternate; }
.node-meta {
font-size: 0.8rem;
color: var(--text-secondary);
margin-top: 0.25rem;
}
.node-code {
font-family: monospace;
background: #F8FAFC;
padding: 0.4rem;
border-radius: 4px;
font-size: 0.75rem;
margin-top: 0.5rem;
border: 1px solid var(--border-color);
overflow-x: auto;
}
@keyframes pulse {
0% { opacity: 0.6; }
100% { opacity: 1; }
}
.console {
background: #0F172A;
color: #38BDF8;
font-family: monospace;
padding: 1.25rem;
border-radius: 8px;
font-size: 0.85rem;
height: 220px;
overflow-y: auto;
white-space: pre-wrap;
border: 1px solid #1E293B;
}
</style>
</head>
<body>
<div class="header">
<h1>SHIVYA: The Non-Dual Substrate</h1>
<p>Unified 4-Layer Symbiont Execution & Telemetry Pipeline (Rust / WASM)</p>
</div>
<div class="dashboard-grid">
<div class="left-col">
<div class="card">
<h2>
<span>Layer 0: HodgeMesh Complex</span>
<span style="font-size: 0.8rem; font-weight: normal; color: var(--text-secondary);">Ensemble Topology</span>
</h2>
<div class="svg-container">
<svg id="topology-svg" width="400" height="300" style="overflow: visible;">
<polygon id="triangle-poly" points="200,50 100,250 300,250" fill="rgba(99, 102, 241, 0.08)" stroke="rgba(99, 102, 241, 0.2)" stroke-dasharray="4"/>
<line x1="200" y1="50" x2="100" y2="250" stroke="#94A3B8" stroke-width="2"/>
<line x1="100" y1="250" x2="300" y2="250" stroke="#94A3B8" stroke-width="2"/>
<line x1="200" y1="50" x2="300" y2="250" stroke="#94A3B8" stroke-width="2"/>
<circle cx="200" cy="50" r="18" fill="white" stroke="#6366F1" stroke-width="3"/>
<text x="200" y="55" text-anchor="middle" font-family="'Outfit'" font-weight="bold" font-size="12" fill="#0F172A">Node0</text>
<circle cx="100" cy="250" r="18" fill="white" stroke="#6366F1" stroke-width="3"/>
<text x="100" y="255" text-anchor="middle" font-family="'Outfit'" font-weight="bold" font-size="12" fill="#0F172A">Node1</text>
<circle cx="300" cy="250" r="18" fill="white" stroke="#6366F1" stroke-width="3"/>
<text x="300" y="255" text-anchor="middle" font-family="'Outfit'" font-weight="bold" font-size="12" fill="#0F172A">Node2</text>
</svg>
</div>
<div class="stats-grid">
<div class="stat-box">
<div class="stat-value" id="stat-collective-f">-</div>
<div class="stat-label">Collective F</div>
</div>
<div class="stat-box">
<div class="stat-value" id="stat-curl-dev">-</div>
<div class="stat-label">Curl Deviation</div>
</div>
<div class="stat-box">
<div class="stat-value" id="stat-step-count">0</div>
<div class="stat-label">Steps Run</div>
</div>
</div>
<div class="control-group">
<button class="btn-primary" style="width: 100%;" id="btn-orchestrate">Execute Unified Simulation Step</button>
</div>
</div>
</div>
<div class="right-col">
<div class="card">
<h2>Ensemble Nodes (Layer 1, 2, & 3)</h2>
<div style="margin-bottom: 1.5rem;">
<label class="control-label">Load & Latency Observations (Node Inputs)</label>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem;">
<div>
<span style="font-size: 0.75rem; color: var(--text-secondary); font-weight: 500;">Node 0 (Load/Latency):</span>
<input type="text" id="node-0-obs" value="1.5, 0.8">
</div>
<div>
<span style="font-size: 0.75rem; color: var(--text-secondary); font-weight: 500;">Node 1 (Load/Latency):</span>
<input type="text" id="node-1-obs" value="2.0, 1.2">
</div>
<div>
<span style="font-size: 0.75rem; color: var(--text-secondary); font-weight: 500;">Node 2 (Load/Latency):</span>
<input type="text" id="node-2-obs" value="1.8, 1.0">
</div>
</div>
</div>
<div class="node-list" id="nodes-container">
<div class="node-card">
<div class="node-card-header">
<span>Node 0</span>
<span class="node-badge badge-active">Active</span>
</div>
<div class="node-meta">Free Energy: - | Dimension: 2D</div>
<div class="node-meta">Beliefs: -</div>
<div class="node-code">Expr::Mul(Const(1.0), Var(0))</div>
</div>
</div>
</div>
<div class="card">
<h2>Live Symbiont Telemetry</h2>
<div class="console" id="console-log">Initializing WASM Substrate Orchestrator...</div>
</div>
</div>
</div>
<script type="module">
import init, { SubstrateOrchestrator } from "./pkg/telemetry_wasm.js";
const logElem = document.getElementById("console-log");
function log(msg) {
logElem.innerText += `\n[${new Date().toLocaleTimeString()}] ${msg}`;
logElem.scrollTop = logElem.scrollHeight;
}
const svg = document.getElementById("topology-svg");
const activeElements = {
nodes: {}, edges: {}, polygons: {} };
function clearPlaceholderSVG() {
svg.innerHTML = "";
}
function updateSVG(activePool, edges) {
const center = { x: 200, y: 150 };
const radius = 95;
const N = activePool.length;
const nodeCoords = {};
activePool.forEach((nodeId, idx) => {
const angle = (idx * 2 * Math.PI) / N - Math.PI / 2;
nodeCoords[nodeId] = {
x: center.x + radius * Math.cos(angle),
y: center.y + radius * Math.sin(angle)
};
});
Object.keys(activeElements.nodes).forEach(nodeIdStr => {
const nodeId = parseInt(nodeIdStr, 10);
if (!activePool.includes(nodeId)) {
const el = activeElements.nodes[nodeIdStr];
svg.removeChild(el.group);
delete activeElements.nodes[nodeIdStr];
}
});
activePool.forEach(nodeId => {
const nodeIdStr = String(nodeId);
if (!activeElements.nodes[nodeIdStr]) {
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.style.transition = "all 0.5s cubic-bezier(0.4, 0, 0.2, 1)";
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("r", "18");
circle.setAttribute("fill", "white");
circle.setAttribute("stroke", "var(--accent-indigo)");
circle.setAttribute("stroke-width", "3");
circle.style.transition = "stroke 0.3s ease, stroke-width 0.3s ease";
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-family", "'Outfit', sans-serif");
text.setAttribute("font-weight", "bold");
text.setAttribute("font-size", "11");
text.setAttribute("fill", "#0F172A");
text.textContent = `Node${nodeId}`;
group.appendChild(circle);
group.appendChild(text);
svg.appendChild(group);
activeElements.nodes[nodeIdStr] = { group, circle, text };
}
});
const activeEdgeKeys = new Set(edges.map(e => {
const sorted = [...e].sort((a, b) => a - b);
return `${sorted[0]}-${sorted[1]}`;
}));
Object.keys(activeElements.edges).forEach(key => {
if (!activeEdgeKeys.has(key)) {
svg.removeChild(activeElements.edges[key]);
delete activeElements.edges[key];
}
});
edges.forEach(edge => {
const sorted = [...edge].sort((a, b) => a - b);
const key = `${sorted[0]}-${sorted[1]}`;
if (!activeElements.edges[key]) {
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute("stroke", "#94A3B8");
line.setAttribute("stroke-width", "2");
line.style.transition = "all 0.5s cubic-bezier(0.4, 0, 0.2, 1)";
const firstGroup = svg.querySelector("g");
if (firstGroup) {
svg.insertBefore(line, firstGroup);
} else {
svg.appendChild(line);
}
activeElements.edges[key] = line;
}
});
activePool.forEach(nodeId => {
const nodeIdStr = String(nodeId);
const coords = nodeCoords[nodeId];
const el = activeElements.nodes[nodeIdStr];
if (el && coords) {
el.circle.setAttribute("cx", coords.x);
el.circle.setAttribute("cy", coords.y);
el.text.setAttribute("x", coords.x);
el.text.setAttribute("y", coords.y + 4);
}
});
edges.forEach(edge => {
const sorted = [...edge].sort((a, b) => a - b);
const key = `${sorted[0]}-${sorted[1]}`;
const line = activeElements.edges[key];
const coords1 = nodeCoords[sorted[0]];
const coords2 = nodeCoords[sorted[1]];
if (line && coords1 && coords2) {
line.setAttribute("x1", coords1.x);
line.setAttribute("y1", coords1.y);
line.setAttribute("x2", coords2.x);
line.setAttribute("y2", coords2.y);
}
});
const activeTriangleKeys = new Set();
for (let i = 0; i < activePool.length; i++) {
for (let j = i + 1; j < activePool.length; j++) {
for (let k = j + 1; k < activePool.length; k++) {
const u = activePool[i];
const v = activePool[j];
const w = activePool[k];
const hasUV = activeEdgeKeys.has(`${Math.min(u,v)}-${Math.max(u,v)}`);
const hasVW = activeEdgeKeys.has(`${Math.min(v,w)}-${Math.max(v,w)}`);
const hasUW = activeEdgeKeys.has(`${Math.min(u,w)}-${Math.max(u,w)}`);
if (hasUV && hasVW && hasUW) {
const sortedTri = [u, v, w].sort((a, b) => a - b);
activeTriangleKeys.add(`${sortedTri[0]}-${sortedTri[1]}-${sortedTri[2]}`);
}
}
}
}
Object.keys(activeElements.polygons).forEach(key => {
if (!activeTriangleKeys.has(key)) {
svg.removeChild(activeElements.polygons[key]);
delete activeElements.polygons[key];
}
});
activeTriangleKeys.forEach(key => {
const indices = key.split("-").map(Number);
const coords0 = nodeCoords[indices[0]];
const coords1 = nodeCoords[indices[1]];
const coords2 = nodeCoords[indices[2]];
if (coords0 && coords1 && coords2) {
let poly = activeElements.polygons[key];
if (!poly) {
poly = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
poly.setAttribute("fill", "rgba(99, 102, 241, 0.08)");
poly.setAttribute("stroke", "rgba(99, 102, 241, 0.2)");
poly.setAttribute("stroke-dasharray", "4");
const firstLine = svg.querySelector("line");
if (firstLine) {
svg.insertBefore(poly, firstLine);
} else {
const firstGroup = svg.querySelector("g");
if (firstGroup) {
svg.insertBefore(poly, firstGroup);
} else {
svg.appendChild(poly);
}
}
activeElements.polygons[key] = poly;
}
poly.setAttribute("points", `${coords0.x},${coords0.y} ${coords1.x},${coords1.y} ${coords2.x},${coords2.y}`);
}
});
}
let ws = null;
let orchestrator = null;
let isWSConnected = false;
let lastNodeEquations = {};
function renderState(data) {
document.getElementById("stat-collective-f").innerText = data.collective_free_energy.toFixed(4);
document.getElementById("stat-curl-dev").innerText = data.curl_deviation.toFixed(4);
document.getElementById("stat-step-count").innerText = data.step_count || 0;
let activePool = data.active_pool || data.nodes.map(n => n.id);
let edges = data.edges || [];
if (edges.length === 0 && activePool.length === 3) {
edges = [[0, 1], [1, 2], [0, 2]];
}
updateSVG(activePool, edges);
const nodesContainer = document.getElementById("nodes-container");
nodesContainer.innerHTML = "";
data.nodes.forEach(node => {
if (node.active === false) return;
const card = document.createElement("div");
card.className = "node-card";
const isSwapped = lastNodeEquations[node.id] && lastNodeEquations[node.id] !== node.morphic_equation;
lastNodeEquations[node.id] = node.morphic_equation;
const badgeClass = isSwapped || node.hotswapped ? "badge-hotswap" : "badge-active";
const badgeText = isSwapped || node.hotswapped ? "Hotswapped 🦀" : "Stable";
const nodeEl = activeElements.nodes[String(node.id)];
if (nodeEl) {
if (isSwapped || node.hotswapped) {
nodeEl.circle.setAttribute("stroke", "var(--accent-rose)");
nodeEl.circle.setAttribute("stroke-width", "4");
} else {
nodeEl.circle.setAttribute("stroke", "var(--accent-indigo)");
nodeEl.circle.setAttribute("stroke-width", "3");
}
}
const beliefsStr = node.beliefs.map(val => val.toFixed(4)).join(", ");
card.innerHTML = `
<div class="node-card-header">
<span>Node ${node.id}</span>
<span class="node-badge ${badgeClass}">${badgeText}</span>
</div>
<div class="node-meta">Free Energy (F): ${node.free_energy.toFixed(4)} | Dim: ${node.belief_dim}D</div>
<div class="node-meta">Belief Coordinates (mu_q): [${beliefsStr}]</div>
${node.morphogen_u !== undefined ? `<div class="node-meta">Morphogens (U / V): ${node.morphogen_u.toFixed(4)} / ${node.morphogen_v.toFixed(4)}</div>` : ''}
<div class="node-code">${node.morphic_equation}</div>
`;
nodesContainer.appendChild(card);
});
}
async function tryWebSocket() {
return new Promise((resolve) => {
log("Attempting handshake with native WebSocket daemon on port 9002...");
const socket = new WebSocket("ws://127.0.0.1:9002");
socket.onopen = () => {
isWSConnected = true;
ws = socket;
log("Handshake successful! Live WebSocket connected.");
const wsStatus = document.getElementById("ws-status");
wsStatus.innerText = "● LIVE WEBSOCKET";
wsStatus.style.color = "var(--accent-teal)";
wsStatus.style.fontWeight = "600";
wsStatus.style.textShadow = "0 0 8px rgba(20, 184, 166, 0.4)";
const btn = document.getElementById("btn-orchestrate");
btn.innerText = "Native Daemon Active (Streaming)";
btn.disabled = true;
btn.style.backgroundColor = "#F1F5F9";
btn.style.color = "#94A3B8";
btn.style.border = "1px solid var(--border-color)";
btn.style.cursor = "default";
clearPlaceholderSVG();
resolve(true);
};
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
renderState(data);
} catch (e) {
log(`Error parsing WebSocket frame: ${e}`);
}
};
socket.onclose = () => {
if (isWSConnected) {
log("WebSocket connection closed. Switching back to local WASM fallback...");
switchToWASM();
} else {
resolve(false);
}
};
socket.onerror = () => {
resolve(false);
};
});
}
async function switchToWASM() {
isWSConnected = false;
if (ws) {
ws.close();
ws = null;
}
const wsStatus = document.getElementById("ws-status");
wsStatus.innerText = "● OFFLINE WASM MODE";
wsStatus.style.color = "var(--accent-indigo)";
wsStatus.style.fontWeight = "600";
wsStatus.style.textShadow = "0 0 8px rgba(99, 102, 241, 0.4)";
const btn = document.getElementById("btn-orchestrate");
btn.innerText = "Execute Unified Simulation Step";
btn.disabled = false;
btn.style.backgroundColor = "var(--accent-indigo)";
btn.style.color = "white";
btn.style.cursor = "pointer";
btn.style.border = "none";
if (!orchestrator) {
try {
await init();
orchestrator = new SubstrateOrchestrator();
log("WASM Substrate Orchestrator initialized successfully.");
} catch (err) {
log(`Failed to initialize WASM fallback: ${err}`);
}
}
clearPlaceholderSVG();
const initialWASMData = {
collective_free_energy: 0.0,
curl_deviation: 0.0,
step_count: 0,
nodes: [
{ id: 0, free_energy: 0.0, belief_dim: 2, beliefs: [0.1, 0.0], morphic_equation: "Expr::Mul(Const(1.0), Var(0))", hotswapped: false },
{ id: 1, free_energy: 0.0, belief_dim: 2, beliefs: [0.2, 0.0], morphic_equation: "Expr::Mul(Const(1.0), Var(0))", hotswapped: false },
{ id: 2, free_energy: 0.0, belief_dim: 2, beliefs: [0.15, 0.0], morphic_equation: "Expr::Mul(Const(1.0), Var(0))", hotswapped: false }
]
};
renderState(initialWASMData);
}
let localStepCounter = 0;
document.getElementById("btn-orchestrate").addEventListener("click", () => {
if (isWSConnected) return;
localStepCounter++;
const n0 = document.getElementById("node-0-obs").value.split(",").map(parseFloat);
const n1 = document.getElementById("node-1-obs").value.split(",").map(parseFloat);
const n2 = document.getElementById("node-2-obs").value.split(",").map(parseFloat);
const inputs = [...n0, ...n1, ...n2];
log(`Loaded Observations: N0=[${n0.join(",")}] N1=[${n1.join(",")}] N2=[${n2.join(",")}]`);
try {
const jsonPayload = orchestrator.step(inputs);
const data = JSON.parse(jsonPayload);
data.step_count = localStepCounter; renderState(data);
log(`Step ${localStepCounter} Completed. Collective F: ${data.collective_free_energy.toFixed(4)} | Curl Deviation: ${data.curl_deviation.toFixed(4)}`);
} catch (err) {
log(`Error executing local step: ${err}`);
}
});
async function start() {
const hasWS = await tryWebSocket();
if (!hasWS) {
log("Handshake failed. Falling back to browser WASM mode.");
await switchToWASM();
}
}
start();
</script>
</body>
</html>