meshsig 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +276 -0
- package/dist/crypto.d.ts +43 -0
- package/dist/crypto.js +108 -0
- package/dist/crypto.js.map +1 -0
- package/dist/dashboard.html +635 -0
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +107 -0
- package/dist/demo.js.map +1 -0
- package/dist/discovery.d.ts +24 -0
- package/dist/discovery.js +119 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +459 -0
- package/dist/main.js.map +1 -0
- package/dist/peers.d.ts +35 -0
- package/dist/peers.js +227 -0
- package/dist/peers.js.map +1 -0
- package/dist/registry.d.ts +85 -0
- package/dist/registry.js +311 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +27 -0
- package/dist/server.js +433 -0
- package/dist/server.js.map +1 -0
- package/dist/terminal.d.ts +17 -0
- package/dist/terminal.js +175 -0
- package/dist/terminal.js.map +1 -0
- package/package.json +51 -0
- package/scripts/install.sh +271 -0
- package/scripts/invoke-mesh.sh +190 -0
- package/scripts/register-agent.sh +89 -0
- package/scripts/uninstall.sh +25 -0
- package/scripts/unregister-agent.sh +23 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>MeshSig — Security Operations</title>
|
|
7
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
9
|
+
<style>
|
|
10
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
11
|
+
:root{--bg:#050a12;--bg2:#0a1220;--bg3:#0d1a2d;--cyan:#00d4ff;--blue:#0088ff;--green:#00ff88;
|
|
12
|
+
--amber:#f0b429;--red:#ff3355;--text:#e2ecf5;--muted:#6db3d6;--dim:#3a6a8f;--border:#0d2137;
|
|
13
|
+
--mono:'JetBrains Mono',monospace;--sans:'Outfit',sans-serif}
|
|
14
|
+
body{background:var(--bg);color:var(--text);font-family:var(--sans);height:100vh;overflow:hidden;display:flex}
|
|
15
|
+
body::after{content:'';position:fixed;top:0;left:0;right:0;bottom:0;
|
|
16
|
+
background:repeating-linear-gradient(0deg,rgba(0,0,0,0.02) 0px,rgba(0,0,0,0.02) 1px,transparent 1px,transparent 2px);
|
|
17
|
+
pointer-events:none;z-index:999}
|
|
18
|
+
|
|
19
|
+
/* -- Sidebar ------------------------------------------------------------ */
|
|
20
|
+
#sidebar{width:220px;height:100vh;background:var(--bg2);border-right:1px solid var(--border);
|
|
21
|
+
display:flex;flex-direction:column;flex-shrink:0;z-index:50}
|
|
22
|
+
.sb-logo{padding:20px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
|
|
23
|
+
.sb-logo h1{font:700 15px var(--mono);color:var(--cyan);letter-spacing:0.05em}
|
|
24
|
+
.sb-logo .tag{font:400 9px var(--mono);color:var(--dim);letter-spacing:0.1em}
|
|
25
|
+
.sb-nav{flex:1;padding:12px 0;overflow-y:auto}
|
|
26
|
+
.sb-item{display:flex;align-items:center;gap:12px;padding:10px 20px;cursor:pointer;
|
|
27
|
+
font:500 13px var(--sans);color:var(--text);transition:all 0.2s;border-left:3px solid transparent;
|
|
28
|
+
user-select:none}
|
|
29
|
+
.sb-item:hover{background:rgba(0,212,255,0.03);color:var(--text)}
|
|
30
|
+
.sb-item.active{color:var(--cyan);background:rgba(0,212,255,0.06);border-left-color:var(--cyan)}
|
|
31
|
+
.sb-item .icon{width:18px;text-align:center;font:500 14px var(--mono);flex-shrink:0}
|
|
32
|
+
.sb-section{font:600 9px var(--mono);color:var(--muted);letter-spacing:0.15em;padding:16px 20px 6px}
|
|
33
|
+
.sb-stats{padding:16px 20px;border-top:1px solid var(--border);margin-top:auto}
|
|
34
|
+
.sb-stat{display:flex;justify-content:space-between;margin-bottom:6px}
|
|
35
|
+
.sb-stat .label{font:400 10px var(--mono);color:var(--muted)}
|
|
36
|
+
.sb-stat .value{font:600 11px var(--mono);color:var(--cyan)}
|
|
37
|
+
.sb-ws{padding:12px 20px;border-top:1px solid var(--border);display:flex;align-items:center;gap:8px;
|
|
38
|
+
font:11px var(--mono)}
|
|
39
|
+
.pulse{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 8px var(--green);
|
|
40
|
+
animation:pulse 2s infinite}
|
|
41
|
+
.pulse.off{background:var(--red);box-shadow:0 0 8px var(--red)}
|
|
42
|
+
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}
|
|
43
|
+
|
|
44
|
+
/* -- Main area ---------------------------------------------------------- */
|
|
45
|
+
#main{flex:1;display:flex;flex-direction:column;height:100vh;overflow:hidden}
|
|
46
|
+
|
|
47
|
+
/* Top bar */
|
|
48
|
+
#topbar{height:52px;background:rgba(5,10,18,0.95);border-bottom:1px solid var(--border);
|
|
49
|
+
display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0;
|
|
50
|
+
backdrop-filter:blur(12px)}
|
|
51
|
+
#page-title{font:600 14px var(--sans);color:#fff}
|
|
52
|
+
#clock{font:500 12px var(--mono);color:var(--dim)}
|
|
53
|
+
|
|
54
|
+
/* Pages */
|
|
55
|
+
.page{display:none;flex:1;overflow:hidden}
|
|
56
|
+
.page.active{display:flex;flex-direction:column}
|
|
57
|
+
|
|
58
|
+
/* -- Network Page ------------------------------------------------------- */
|
|
59
|
+
#p-network{position:relative}
|
|
60
|
+
#graph{flex:1;position:relative}
|
|
61
|
+
#graph svg{width:100%;height:100%}
|
|
62
|
+
#events-panel{position:absolute;top:0;right:0;bottom:0;width:380px;
|
|
63
|
+
background:rgba(5,10,18,0.92);border-left:1px solid var(--border);
|
|
64
|
+
display:flex;flex-direction:column;backdrop-filter:blur(8px)}
|
|
65
|
+
#events-header{padding:14px 18px;border-bottom:1px solid var(--border);
|
|
66
|
+
font:600 10px var(--mono);color:var(--dim);letter-spacing:0.12em;
|
|
67
|
+
display:flex;justify-content:space-between}
|
|
68
|
+
#events-header .count{color:var(--cyan);font-size:12px}
|
|
69
|
+
#events{flex:1;overflow-y:auto;padding:0}
|
|
70
|
+
#events::-webkit-scrollbar{width:3px}
|
|
71
|
+
#events::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
|
|
72
|
+
.ev{padding:9px 18px;border-bottom:1px solid #0a1525;animation:evIn 0.4s ease;font:12px var(--mono)}
|
|
73
|
+
@keyframes evIn{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:translateX(0)}}
|
|
74
|
+
.ev .ts{color:var(--muted);font-size:10px}
|
|
75
|
+
.ev .from,.ev .to{color:var(--cyan);font-weight:600}
|
|
76
|
+
.ev .arrow{color:var(--dim);margin:0 4px}
|
|
77
|
+
.ev .badge{display:inline-block;padding:1px 6px;border-radius:3px;font-size:9px;margin-left:6px}
|
|
78
|
+
.ev .badge.ok{background:#00ff8815;color:var(--green);border:1px solid #00ff8833}
|
|
79
|
+
.ev .badge.fail{background:#ff335515;color:var(--red);border:1px solid #ff335533}
|
|
80
|
+
.ev .content{display:block;margin-top:4px;padding:5px 10px;background:rgba(0,212,255,0.04);
|
|
81
|
+
border-left:2px solid rgba(0,212,255,0.2);border-radius:0 4px 4px 0;color:var(--muted);font-size:11px}
|
|
82
|
+
.ev.join{border-left:3px solid var(--green)}
|
|
83
|
+
.ev.handshake{border-left:3px solid #8b5cf6}
|
|
84
|
+
.ev.message{border-left:3px solid var(--amber)}
|
|
85
|
+
.ev.system{border-left:3px solid var(--dim)}
|
|
86
|
+
|
|
87
|
+
/* Tooltip */
|
|
88
|
+
#tip{position:fixed;background:#0a1525;border:1px solid var(--border);border-radius:10px;
|
|
89
|
+
padding:14px 18px;font:12px var(--mono);color:var(--text);pointer-events:none;z-index:100;
|
|
90
|
+
max-width:300px;display:none;box-shadow:0 8px 32px rgba(0,0,0,0.6)}
|
|
91
|
+
|
|
92
|
+
/* Message flash */
|
|
93
|
+
.msg-flash{position:fixed;z-index:15;pointer-events:none;background:rgba(240,180,41,0.1);
|
|
94
|
+
border:1px solid rgba(240,180,41,0.25);border-radius:8px;padding:5px 10px;max-width:220px;
|
|
95
|
+
font:10px var(--mono);color:var(--amber);opacity:0;transition:opacity 0.3s}
|
|
96
|
+
.msg-flash.show{opacity:1}
|
|
97
|
+
|
|
98
|
+
/* -- Agents Page -------------------------------------------------------- */
|
|
99
|
+
.agents-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:16px;
|
|
100
|
+
padding:24px;overflow-y:auto;flex:1}
|
|
101
|
+
.agent-card{background:var(--bg2);border:1px solid var(--border);border-radius:12px;padding:20px;
|
|
102
|
+
transition:border-color 0.3s}
|
|
103
|
+
.agent-card:hover{border-color:rgba(0,212,255,0.2)}
|
|
104
|
+
.agent-card .ac-header{display:flex;align-items:center;gap:12px;margin-bottom:12px}
|
|
105
|
+
.agent-card .ac-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
|
|
106
|
+
.agent-card .ac-name{font:600 16px var(--sans);color:#fff}
|
|
107
|
+
.agent-card .ac-did{font:400 10px var(--mono);color:var(--dim);margin-bottom:12px;word-break:break-all}
|
|
108
|
+
.agent-card .ac-row{display:flex;justify-content:space-between;padding:4px 0;font-size:13px}
|
|
109
|
+
.agent-card .ac-label{color:var(--muted)}
|
|
110
|
+
.agent-card .ac-value{color:var(--text);font:500 13px var(--mono)}
|
|
111
|
+
.agent-card .ac-caps{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
|
|
112
|
+
.agent-card .ac-cap{font:400 10px var(--mono);padding:3px 8px;border-radius:4px;
|
|
113
|
+
background:rgba(0,212,255,0.06);color:var(--cyan);border:1px solid rgba(0,212,255,0.15)}
|
|
114
|
+
.agent-card .ac-remote{background:rgba(240,180,41,0.06);color:var(--amber);border-color:rgba(240,180,41,0.15)}
|
|
115
|
+
|
|
116
|
+
/* -- Messages Page ------------------------------------------------------ */
|
|
117
|
+
.messages-list{flex:1;overflow-y:auto;padding:24px}
|
|
118
|
+
.msg-row{background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:16px 20px;
|
|
119
|
+
margin-bottom:12px;transition:border-color 0.3s}
|
|
120
|
+
.msg-row:hover{border-color:rgba(0,212,255,0.15)}
|
|
121
|
+
.msg-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}
|
|
122
|
+
.msg-from,.msg-to{font:600 13px var(--mono);color:var(--cyan)}
|
|
123
|
+
.msg-arrow{color:var(--dim);font:13px var(--mono)}
|
|
124
|
+
.msg-time{margin-left:auto;font:400 10px var(--mono);color:var(--dim)}
|
|
125
|
+
.msg-body{font:400 13px var(--mono);color:var(--muted);padding:10px 14px;background:rgba(0,212,255,0.03);
|
|
126
|
+
border-radius:6px;margin-bottom:8px;border-left:2px solid rgba(0,212,255,0.15)}
|
|
127
|
+
.msg-sig{font:400 10px var(--mono);color:var(--dim);word-break:break-all}
|
|
128
|
+
.msg-badge{display:inline-block;padding:2px 8px;border-radius:4px;font:500 10px var(--mono);margin-left:8px}
|
|
129
|
+
.msg-badge.valid{background:#00ff8812;color:var(--green);border:1px solid #00ff8830}
|
|
130
|
+
.msg-badge.invalid{background:#ff335512;color:var(--red);border:1px solid #ff335530}
|
|
131
|
+
|
|
132
|
+
/* -- Audit Page --------------------------------------------------------- */
|
|
133
|
+
.audit-content{flex:1;overflow-y:auto;padding:24px}
|
|
134
|
+
.audit-summary{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:14px;margin-bottom:32px}
|
|
135
|
+
.audit-card{background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:16px;text-align:center}
|
|
136
|
+
.audit-card .val{font:700 28px var(--mono);color:var(--cyan)}
|
|
137
|
+
.audit-card .lbl{font:400 10px var(--mono);color:var(--muted);letter-spacing:0.1em;margin-top:4px}
|
|
138
|
+
.audit-card.green .val{color:var(--green)}
|
|
139
|
+
.audit-card.amber .val{color:var(--amber)}
|
|
140
|
+
.audit-card.red .val{color:var(--red)}
|
|
141
|
+
.audit-actions{display:flex;gap:12px;margin-bottom:24px}
|
|
142
|
+
.audit-btn{padding:10px 20px;background:rgba(0,212,255,0.08);border:1px solid rgba(0,212,255,0.2);
|
|
143
|
+
color:var(--cyan);font:500 12px var(--mono);border-radius:6px;cursor:pointer;transition:all 0.3s}
|
|
144
|
+
.audit-btn:hover{background:rgba(0,212,255,0.15)}
|
|
145
|
+
.audit-json{background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:20px;
|
|
146
|
+
font:400 11px var(--mono);color:var(--muted);max-height:400px;overflow-y:auto;white-space:pre-wrap;word-break:break-all}
|
|
147
|
+
|
|
148
|
+
/* -- Verify Page -------------------------------------------------------- */
|
|
149
|
+
.verify-content{flex:1;overflow-y:auto;display:flex;align-items:center;justify-content:center;padding:40px}
|
|
150
|
+
.verify-card{background:var(--bg2);border:1px solid var(--border);border-radius:16px;padding:40px;
|
|
151
|
+
max-width:560px;width:100%}
|
|
152
|
+
.verify-card h2{font:700 20px var(--mono);color:var(--cyan);margin-bottom:6px;letter-spacing:0.04em}
|
|
153
|
+
.verify-card .sub{font:300 13px var(--sans);color:var(--muted);margin-bottom:28px}
|
|
154
|
+
.verify-card label{font:500 10px var(--mono);color:var(--muted);letter-spacing:0.1em;display:block;
|
|
155
|
+
margin-bottom:5px;margin-top:16px}
|
|
156
|
+
.verify-card textarea,.verify-card input{width:100%;background:var(--bg);border:1px solid var(--dim);
|
|
157
|
+
border-radius:8px;padding:10px 14px;color:var(--text);font:400 12px var(--mono);outline:none;
|
|
158
|
+
transition:border-color 0.3s;resize:vertical}
|
|
159
|
+
.verify-card textarea:focus,.verify-card input:focus{border-color:var(--cyan)}
|
|
160
|
+
.verify-card textarea{min-height:70px}
|
|
161
|
+
.verify-card button{width:100%;margin-top:24px;padding:12px;background:var(--cyan);color:var(--bg);
|
|
162
|
+
font:600 13px var(--mono);border:none;border-radius:8px;cursor:pointer;transition:all 0.3s;letter-spacing:0.04em}
|
|
163
|
+
.verify-card button:hover{box-shadow:0 0 24px rgba(0,212,255,0.3)}
|
|
164
|
+
.verify-or{text-align:center;color:var(--dim);font:11px var(--mono);margin:4px 0}
|
|
165
|
+
#verify-result{margin-top:20px;padding:16px;border-radius:8px;display:none;text-align:center;
|
|
166
|
+
font:600 15px var(--mono)}
|
|
167
|
+
#verify-result.valid{display:block;background:rgba(0,255,136,0.06);border:1px solid rgba(0,255,136,0.2);color:var(--green)}
|
|
168
|
+
#verify-result.invalid{display:block;background:rgba(255,51,85,0.06);border:1px solid rgba(255,51,85,0.2);color:var(--red)}
|
|
169
|
+
#verify-result .detail{font:400 10px var(--mono);color:var(--muted);margin-top:6px}
|
|
170
|
+
</style>
|
|
171
|
+
</head>
|
|
172
|
+
<body>
|
|
173
|
+
|
|
174
|
+
<!-- Sidebar -->
|
|
175
|
+
<div id="sidebar">
|
|
176
|
+
<div class="sb-logo">
|
|
177
|
+
<svg width="44" height="34" viewBox="0 0 200 160">
|
|
178
|
+
<defs><linearGradient id="lg" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#0088ff"/><stop offset="50%" stop-color="#00d4ff"/><stop offset="100%" stop-color="#0088ff"/></linearGradient></defs>
|
|
179
|
+
<line x1="55" y1="38" x2="30" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
180
|
+
<line x1="55" y1="38" x2="80" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
181
|
+
<line x1="100" y1="122" x2="80" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
182
|
+
<line x1="100" y1="122" x2="120" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
183
|
+
<line x1="145" y1="38" x2="120" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
184
|
+
<line x1="145" y1="38" x2="170" y2="80" stroke="#00ff88" stroke-width="0.7" opacity="0.3"/>
|
|
185
|
+
<path d="M30 80 Q55 30 80 80 Q105 130 120 80 Q145 30 170 80" fill="none" stroke="url(#lg)" stroke-width="1.8" stroke-linecap="round"/>
|
|
186
|
+
<circle cx="30" cy="80" r="7" fill="#0088ff"/><circle cx="80" cy="80" r="8" fill="#00bbff"/>
|
|
187
|
+
<circle cx="120" cy="80" r="8" fill="#00bbff"/><circle cx="170" cy="80" r="7" fill="#0088ff"/>
|
|
188
|
+
<circle cx="55" cy="38" r="5" fill="#00ff88"/><circle cx="100" cy="122" r="5" fill="#00ff88"/><circle cx="145" cy="38" r="5" fill="#00ff88"/>
|
|
189
|
+
</svg>
|
|
190
|
+
<div><h1>MESHSIG</h1><div class="tag">SECURITY OPS</div></div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="sb-nav">
|
|
193
|
+
<div class="sb-section">MONITOR</div>
|
|
194
|
+
<div class="sb-item active" data-page="network"><span class="icon">◉</span> Network Map</div>
|
|
195
|
+
<div class="sb-item" data-page="agents"><span class="icon">◈</span> Agents</div>
|
|
196
|
+
<div class="sb-item" data-page="messages"><span class="icon">◆</span> Messages</div>
|
|
197
|
+
<div class="sb-section">SECURITY</div>
|
|
198
|
+
<div class="sb-item" data-page="audit"><span class="icon">◧</span> Audit Report</div>
|
|
199
|
+
<div class="sb-item" data-page="verify"><span class="icon">◇</span> Verify Signature</div>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="sb-stats">
|
|
202
|
+
<div class="sb-stat"><span class="label">AGENTS</span><span class="value" id="sb-agents">0</span></div>
|
|
203
|
+
<div class="sb-stat"><span class="label">CONNECTIONS</span><span class="value" id="sb-links">0</span></div>
|
|
204
|
+
<div class="sb-stat"><span class="label">SIGNED MSGS</span><span class="value" id="sb-msgs">0</span></div>
|
|
205
|
+
<div class="sb-stat"><span class="label">AVG TRUST</span><span class="value" id="sb-trust">—</span></div>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="sb-ws"><div class="pulse off" id="wsd"></div><span id="wsl" style="color:var(--muted)">CONNECTING</span></div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<!-- Main area -->
|
|
211
|
+
<div id="main">
|
|
212
|
+
<div id="topbar">
|
|
213
|
+
<span id="page-title">Network Map</span>
|
|
214
|
+
<span id="clock"></span>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<!-- Network Page -->
|
|
218
|
+
<div class="page active" id="p-network">
|
|
219
|
+
<div id="graph"><svg></svg></div>
|
|
220
|
+
<div id="events-panel">
|
|
221
|
+
<div id="events-header"><span>LIVE SECURITY FEED</span><span class="count" id="ev-count">0</span></div>
|
|
222
|
+
<div id="events"></div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- Agents Page -->
|
|
227
|
+
<div class="page" id="p-agents"><div class="agents-grid" id="agents-grid"></div></div>
|
|
228
|
+
|
|
229
|
+
<!-- Messages Page -->
|
|
230
|
+
<div class="page" id="p-messages"><div class="messages-list" id="messages-list"></div></div>
|
|
231
|
+
|
|
232
|
+
<!-- Audit Page -->
|
|
233
|
+
<div class="page" id="p-audit"><div class="audit-content" id="audit-content"></div></div>
|
|
234
|
+
|
|
235
|
+
<!-- Verify Page -->
|
|
236
|
+
<div class="page" id="p-verify">
|
|
237
|
+
<div class="verify-content">
|
|
238
|
+
<div class="verify-card">
|
|
239
|
+
<h2>VERIFY SIGNATURE</h2>
|
|
240
|
+
<p class="sub">Paste a message, its Ed25519 signature, and the signer's public key or DID.</p>
|
|
241
|
+
<label>MESSAGE</label>
|
|
242
|
+
<textarea id="v-msg" placeholder="The original message that was signed"></textarea>
|
|
243
|
+
<label>SIGNATURE (Base64)</label>
|
|
244
|
+
<input id="v-sig" placeholder="HkyrXOPOXF7v422A4iOcg...">
|
|
245
|
+
<label>PUBLIC KEY (Base64)</label>
|
|
246
|
+
<input id="v-pk" placeholder="KGC0lHB6Cwhhg1kyEjrPk0...">
|
|
247
|
+
<div class="verify-or">— or —</div>
|
|
248
|
+
<label>DID</label>
|
|
249
|
+
<input id="v-did" placeholder="did:msig:3icqQkmJWby4...">
|
|
250
|
+
<button onclick="doVerify()">VERIFY SIGNATURE</button>
|
|
251
|
+
<div id="verify-result"><div id="vr-text"></div><div class="detail" id="vr-detail"></div></div>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<!-- Tooltip -->
|
|
258
|
+
<div id="tip"></div>
|
|
259
|
+
<!-- Message flash -->
|
|
260
|
+
<div class="msg-flash" id="mf"></div>
|
|
261
|
+
|
|
262
|
+
<!-- Audio -->
|
|
263
|
+
<script>
|
|
264
|
+
let audioCtx=null;
|
|
265
|
+
function playBeep(f=800,d=0.08,v=0.1){
|
|
266
|
+
if(!audioCtx)audioCtx=new(window.AudioContext||window.webkitAudioContext)();
|
|
267
|
+
const o=audioCtx.createOscillator(),g=audioCtx.createGain();
|
|
268
|
+
o.connect(g);g.connect(audioCtx.destination);o.frequency.value=f;o.type='sine';
|
|
269
|
+
g.gain.setValueAtTime(v,audioCtx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+d);
|
|
270
|
+
o.start();o.stop(audioCtx.currentTime+d);
|
|
271
|
+
}
|
|
272
|
+
function playMsg(){playBeep(1200,0.06,0.08);setTimeout(()=>playBeep(1600,0.04,0.06),70)}
|
|
273
|
+
function playJoin(){playBeep(600,0.1,0.06);setTimeout(()=>playBeep(900,0.08,0.05),120)}
|
|
274
|
+
document.addEventListener('click',()=>{if(!audioCtx)audioCtx=new(window.AudioContext||window.webkitAudioContext)()},{once:true});
|
|
275
|
+
</script>
|
|
276
|
+
|
|
277
|
+
<script>
|
|
278
|
+
// -- State -------------------------------------------------------------------
|
|
279
|
+
let nodes=[],links=[],msgCount=0,sigCount=0,evCount=0;
|
|
280
|
+
const titles={network:'Network Map',agents:'Agents',messages:'Signed Messages',audit:'Audit Report',verify:'Verify Signature'};
|
|
281
|
+
|
|
282
|
+
// -- Navigation --------------------------------------------------------------
|
|
283
|
+
document.querySelectorAll('.sb-item').forEach(el=>{
|
|
284
|
+
el.addEventListener('click',()=>{
|
|
285
|
+
document.querySelectorAll('.sb-item').forEach(e=>e.classList.remove('active'));
|
|
286
|
+
el.classList.add('active');
|
|
287
|
+
const pg=el.dataset.page;
|
|
288
|
+
document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));
|
|
289
|
+
document.getElementById('p-'+pg).classList.add('active');
|
|
290
|
+
document.getElementById('page-title').textContent=titles[pg]||pg;
|
|
291
|
+
if(pg==='agents')renderAgentsPage();
|
|
292
|
+
if(pg==='messages')renderMessagesPage();
|
|
293
|
+
if(pg==='audit')renderAuditPage();
|
|
294
|
+
if(pg==='network')resizeGraph();
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// -- Clock -------------------------------------------------------------------
|
|
299
|
+
setInterval(()=>{document.getElementById('clock').textContent=new Date().toLocaleTimeString('en-US',{hour12:false})+' UTC'},1000);
|
|
300
|
+
|
|
301
|
+
// -- D3 Setup ----------------------------------------------------------------
|
|
302
|
+
const graphEl=document.getElementById('graph');
|
|
303
|
+
let W=graphEl.clientWidth-380,H=graphEl.clientHeight;
|
|
304
|
+
const svg=d3.select('#graph svg');
|
|
305
|
+
const defs=svg.append('defs');
|
|
306
|
+
const glow=defs.append('filter').attr('id','nodeGlow');
|
|
307
|
+
glow.append('feGaussianBlur').attr('stdDeviation','4').attr('result','b');
|
|
308
|
+
const gm=glow.append('feMerge');gm.append('feMergeNode').attr('in','b');gm.append('feMergeNode').attr('in','SourceGraphic');
|
|
309
|
+
const msgGlow=defs.append('filter').attr('id','msgGlow');
|
|
310
|
+
msgGlow.append('feGaussianBlur').attr('stdDeviation','3').attr('result','b');
|
|
311
|
+
const mgm=msgGlow.append('feMerge');mgm.append('feMergeNode').attr('in','b');mgm.append('feMergeNode').attr('in','SourceGraphic');
|
|
312
|
+
|
|
313
|
+
// Grid
|
|
314
|
+
const grid=defs.append('pattern').attr('id','grid').attr('width',50).attr('height',50).attr('patternUnits','userSpaceOnUse');
|
|
315
|
+
grid.append('path').attr('d','M 50 0 L 0 0 0 50').attr('fill','none').attr('stroke','#0a1525').attr('stroke-width','0.5');
|
|
316
|
+
const bgRect=svg.append('rect').attr('width',W).attr('height',H).attr('fill','url(#grid)');
|
|
317
|
+
|
|
318
|
+
const linkG=svg.append('g'),particleG=svg.append('g'),nodeG=svg.append('g'),labelG=svg.append('g'),flowG=svg.append('g');
|
|
319
|
+
|
|
320
|
+
const simulation=d3.forceSimulation(nodes)
|
|
321
|
+
.force('link',d3.forceLink(links).id(d=>d.did).distance(140).strength(0.25))
|
|
322
|
+
.force('charge',d3.forceManyBody().strength(-500))
|
|
323
|
+
.force('center',d3.forceCenter(W/2,H/2))
|
|
324
|
+
.force('collision',d3.forceCollide().radius(50))
|
|
325
|
+
.on('tick',tick);
|
|
326
|
+
|
|
327
|
+
function resizeGraph(){
|
|
328
|
+
W=graphEl.clientWidth-380;H=graphEl.clientHeight;
|
|
329
|
+
bgRect.attr('width',W).attr('height',H);
|
|
330
|
+
simulation.force('center',d3.forceCenter(W/2,H/2)).alpha(0.3).restart();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function tick(){
|
|
334
|
+
linkG.selectAll('line').attr('x1',d=>d.source.x).attr('y1',d=>d.source.y).attr('x2',d=>d.target.x).attr('y2',d=>d.target.y);
|
|
335
|
+
nodeG.selectAll('.glow').attr('cx',d=>d.x).attr('cy',d=>d.y);
|
|
336
|
+
nodeG.selectAll('.core').attr('cx',d=>d.x).attr('cy',d=>d.y);
|
|
337
|
+
nodeG.selectAll('.ring').attr('cx',d=>d.x).attr('cy',d=>d.y);
|
|
338
|
+
labelG.selectAll('.nm').attr('x',d=>d.x).attr('y',d=>d.y+26);
|
|
339
|
+
labelG.selectAll('.cap').attr('x',d=>d.x).attr('y',d=>d.y+38);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function nColor(d){return d.origin==='remote'?'#f0b429':'#00d4ff'}
|
|
343
|
+
|
|
344
|
+
function updateGraph(){
|
|
345
|
+
const lk=linkG.selectAll('line').data(links,d=>(d.source.did||d.source)+'-'+(d.target.did||d.target));
|
|
346
|
+
lk.exit().remove();
|
|
347
|
+
lk.enter().append('line').attr('stroke','#0d2137').attr('stroke-width',d=>1+(d.trustScore||0)*3)
|
|
348
|
+
.attr('stroke-opacity',0).transition().duration(800).attr('stroke-opacity',d=>0.2+(d.trustScore||0)*0.6);
|
|
349
|
+
|
|
350
|
+
const ring=nodeG.selectAll('.ring').data(nodes,d=>d.did);
|
|
351
|
+
ring.exit().remove();
|
|
352
|
+
ring.enter().append('circle').attr('class','ring').attr('r',16).attr('fill','none')
|
|
353
|
+
.attr('stroke',d=>nColor(d)).attr('stroke-width',1).attr('stroke-opacity',0).attr('stroke-dasharray','3,3')
|
|
354
|
+
.transition().duration(600).attr('stroke-opacity',0.2);
|
|
355
|
+
|
|
356
|
+
const gl=nodeG.selectAll('.glow').data(nodes,d=>d.did);
|
|
357
|
+
gl.exit().remove();
|
|
358
|
+
gl.enter().append('circle').attr('class','glow').attr('r',20)
|
|
359
|
+
.attr('fill',d=>nColor(d)).attr('opacity',0).attr('filter','url(#nodeGlow)')
|
|
360
|
+
.transition().duration(600).attr('opacity',0.15);
|
|
361
|
+
|
|
362
|
+
const nd=nodeG.selectAll('.core').data(nodes,d=>d.did);
|
|
363
|
+
nd.exit().transition().duration(300).attr('r',0).remove();
|
|
364
|
+
nd.enter().append('circle').attr('class','core').attr('r',0)
|
|
365
|
+
.attr('fill',d=>nColor(d)).attr('stroke',d=>nColor(d)).attr('stroke-width',2).attr('stroke-opacity',0.4)
|
|
366
|
+
.attr('cursor','pointer')
|
|
367
|
+
.call(d3.drag().on('start',(e,d)=>{if(!e.active)simulation.alphaTarget(0.3).restart();d.fx=d.x;d.fy=d.y})
|
|
368
|
+
.on('drag',(e,d)=>{d.fx=e.x;d.fy=e.y}).on('end',(e,d)=>{if(!e.active)simulation.alphaTarget(0);d.fx=null;d.fy=null}))
|
|
369
|
+
.on('mouseover',function(e,d){d3.select(this).transition().attr('r',12).attr('stroke-opacity',1);showTip(e,d)})
|
|
370
|
+
.on('mouseout',function(e,d){d3.select(this).transition().attr('r',7+d.trustScore*4).attr('stroke-opacity',0.4);hideTip()})
|
|
371
|
+
.transition().duration(600).attr('r',d=>7+d.trustScore*4);
|
|
372
|
+
|
|
373
|
+
const nm=labelG.selectAll('.nm').data(nodes,d=>d.did);
|
|
374
|
+
nm.exit().remove();
|
|
375
|
+
nm.enter().append('text').attr('class','nm').attr('text-anchor','middle')
|
|
376
|
+
.attr('fill',d=>d.origin==='remote'?'#f0b429':'#8ab4d0')
|
|
377
|
+
.attr('font-size','11px').attr('font-weight','600').attr('font-family','JetBrains Mono,monospace')
|
|
378
|
+
.attr('opacity',0).text(d=>d.displayName).transition().duration(600).attr('opacity',1);
|
|
379
|
+
|
|
380
|
+
const cp=labelG.selectAll('.cap').data(nodes,d=>d.did);
|
|
381
|
+
cp.exit().remove();
|
|
382
|
+
cp.enter().append('text').attr('class','cap').attr('text-anchor','middle')
|
|
383
|
+
.attr('fill','#5a8aaa').attr('font-size','9px').attr('font-family','JetBrains Mono,monospace')
|
|
384
|
+
.attr('opacity',0).text(d=>capLabel(d)).transition().duration(600).attr('opacity',1);
|
|
385
|
+
labelG.selectAll('.cap').text(d=>capLabel(d));
|
|
386
|
+
|
|
387
|
+
simulation.nodes(nodes);simulation.force('link').links(links);simulation.alpha(0.5).restart();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function capLabel(d){
|
|
391
|
+
const c=(d.capabilities||[]).map(c=>c.type).join(', ');
|
|
392
|
+
const t=d.trustScore>0?` · ${(d.trustScore*100).toFixed(0)}%`:'';
|
|
393
|
+
const s=d.origin==='remote'&&d.originServer?' ⟐ '+d.originServer:'';
|
|
394
|
+
return c+t+s;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Flowing particles
|
|
398
|
+
setInterval(()=>{
|
|
399
|
+
links.forEach(l=>{
|
|
400
|
+
const s=l.source,t=l.target;if(!s.x||!t.x||Math.random()>0.3)return;
|
|
401
|
+
flowG.append('circle').attr('r',1.5).attr('fill','#00d4ff').attr('opacity',0.4).attr('cx',s.x).attr('cy',s.y)
|
|
402
|
+
.transition().duration(2000+Math.random()*1000).ease(d3.easeLinear).attr('cx',t.x).attr('cy',t.y).attr('opacity',0).remove();
|
|
403
|
+
});
|
|
404
|
+
},800);
|
|
405
|
+
|
|
406
|
+
function animateMsg(fromDid,toDid,preview){
|
|
407
|
+
const s=nodes.find(n=>n.did===fromDid),t=nodes.find(n=>n.did===toDid);if(!s||!t)return;
|
|
408
|
+
const p=particleG.append('circle').attr('r',6).attr('fill','#f0b429').attr('opacity',1)
|
|
409
|
+
.attr('cx',s.x).attr('cy',s.y).attr('filter','url(#msgGlow)');
|
|
410
|
+
for(let i=1;i<=3;i++){
|
|
411
|
+
particleG.append('circle').attr('r',4-i).attr('fill','#f0b429').attr('opacity',0.3/i)
|
|
412
|
+
.attr('cx',s.x).attr('cy',s.y).transition().delay(i*80).duration(1500).ease(d3.easeCubicInOut)
|
|
413
|
+
.attr('cx',t.x).attr('cy',t.y).attr('opacity',0).remove();
|
|
414
|
+
}
|
|
415
|
+
linkG.selectAll('line')
|
|
416
|
+
.filter(d=>(d.source.did===fromDid&&d.target.did===toDid)||(d.source.did===toDid&&d.target.did===fromDid))
|
|
417
|
+
.transition().duration(300).attr('stroke','#f0b429').attr('stroke-width',4)
|
|
418
|
+
.transition().duration(1200).attr('stroke','#0d2137').attr('stroke-width',d=>1+(d.trustScore||0)*3);
|
|
419
|
+
nodeG.selectAll('.glow').filter(d=>d.did===toDid)
|
|
420
|
+
.transition().duration(200).attr('opacity',0.5).transition().duration(800).attr('opacity',0.15);
|
|
421
|
+
p.transition().duration(1500).ease(d3.easeCubicInOut).attr('cx',t.x).attr('cy',t.y).attr('r',10).attr('opacity',0).remove();
|
|
422
|
+
if(preview){const mf=document.getElementById('mf');mf.textContent='"'+preview.slice(0,50)+'"';
|
|
423
|
+
mf.style.left=((s.x+t.x)/2+graphEl.offsetLeft+220)+'px';mf.style.top=((s.y+t.y)/2+52-20)+'px';
|
|
424
|
+
mf.classList.add('show');setTimeout(()=>mf.classList.remove('show'),2500);}
|
|
425
|
+
playMsg();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function flashNode(did,color){
|
|
429
|
+
const n=nodes.find(n=>n.did===did);if(!n)return;
|
|
430
|
+
particleG.append('circle').attr('cx',n.x||W/2).attr('cy',n.y||H/2)
|
|
431
|
+
.attr('r',8).attr('fill','none').attr('stroke',color).attr('stroke-width',2).attr('opacity',1)
|
|
432
|
+
.transition().duration(1200).attr('r',40).attr('opacity',0).remove();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Tooltip
|
|
436
|
+
const tip=document.getElementById('tip');
|
|
437
|
+
function showTip(e,d){
|
|
438
|
+
const isR=d.origin==='remote';
|
|
439
|
+
tip.innerHTML=`
|
|
440
|
+
<div style="font:700 14px var(--mono);color:${nColor(d)};margin-bottom:6px">${d.displayName}</div>
|
|
441
|
+
<div style="color:var(--dim);font-size:10px;margin-bottom:6px">${d.did.slice(0,40)}...</div>
|
|
442
|
+
<div style="margin-bottom:6px"><span style="background:${isR?'#f0b42915':'#00ff8815'};color:${isR?'var(--amber)':'var(--green)'};padding:2px 8px;border-radius:4px;font-size:10px">${isR?'⟐ REMOTE':'● LOCAL'}</span></div>
|
|
443
|
+
<div style="margin-bottom:3px;color:var(--muted)">${d.online?'<span style="color:var(--green)">● Online</span>':'○ Offline'}</div>
|
|
444
|
+
<div style="margin-bottom:3px;color:var(--muted)">Trust: <strong style="color:var(--text)">${(d.trustScore*100).toFixed(1)}%</strong></div>
|
|
445
|
+
<div style="color:var(--dim);margin-top:4px">${(d.capabilities||[]).map(c=>c.type).join(', ')}</div>`;
|
|
446
|
+
tip.style.display='block';tip.style.left=(e.pageX+16)+'px';tip.style.top=(e.pageY-16)+'px';
|
|
447
|
+
}
|
|
448
|
+
function hideTip(){tip.style.display='none'}
|
|
449
|
+
|
|
450
|
+
// Events
|
|
451
|
+
const evDiv=document.getElementById('events');
|
|
452
|
+
function addEv(type,html){
|
|
453
|
+
evCount++;document.getElementById('ev-count').textContent=evCount;
|
|
454
|
+
const el=document.createElement('div');el.className='ev '+type;
|
|
455
|
+
el.innerHTML=`<span class="ts">${new Date().toLocaleTimeString('en-US',{hour12:false})}</span> ${html}`;
|
|
456
|
+
evDiv.prepend(el);while(evDiv.children.length>80)evDiv.removeChild(evDiv.lastChild);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function updateStats(){
|
|
460
|
+
document.getElementById('sb-agents').textContent=nodes.length;
|
|
461
|
+
document.getElementById('sb-links').textContent=links.length;
|
|
462
|
+
document.getElementById('sb-msgs').textContent=msgCount;
|
|
463
|
+
const ts=nodes.map(n=>n.trustScore||0).filter(t=>t>0);
|
|
464
|
+
document.getElementById('sb-trust').textContent=ts.length?(ts.reduce((a,b)=>a+b,0)/ts.length*100).toFixed(0)+'%':'—';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// -- Agents Page -------------------------------------------------------------
|
|
468
|
+
function renderAgentsPage(){
|
|
469
|
+
const g=document.getElementById('agents-grid');
|
|
470
|
+
g.innerHTML='';
|
|
471
|
+
for(const a of nodes){
|
|
472
|
+
const isR=a.origin==='remote';
|
|
473
|
+
const caps=(a.capabilities||[]).map(c=>`<span class="ac-cap ${isR?'ac-remote':''}">${c.type}${c.confidence?' '+Math.round(c.confidence*100)+'%':''}</span>`).join('');
|
|
474
|
+
g.innerHTML+=`<div class="agent-card">
|
|
475
|
+
<div class="ac-header"><div class="ac-dot" style="background:${nColor(a)}"></div><span class="ac-name">${a.displayName}</span></div>
|
|
476
|
+
<div class="ac-did">${a.did}</div>
|
|
477
|
+
<div class="ac-row"><span class="ac-label" style="color:var(--text)">Origin</span><span class="ac-value" style="color:${isR?'var(--amber)':'var(--green)'}">${isR?'⟐ Remote':'● Local'}</span></div>
|
|
478
|
+
<div class="ac-row"><span class="ac-label">Trust</span><span class="ac-value">${(a.trustScore*100).toFixed(1)}%</span></div>
|
|
479
|
+
<div class="ac-row"><span class="ac-label">Interactions</span><span class="ac-value">${a.interactionsTotal||0}</span></div>
|
|
480
|
+
<div class="ac-row"><span class="ac-label">Status</span><span class="ac-value" style="color:${a.online?'var(--green)':'var(--dim)'}">${a.online?'Online':'Offline'}</span></div>
|
|
481
|
+
<div class="ac-caps">${caps}</div>
|
|
482
|
+
</div>`;
|
|
483
|
+
}
|
|
484
|
+
if(nodes.length===0)g.innerHTML='<div style="color:var(--dim);padding:40px;font:14px var(--mono);text-align:center">No agents registered</div>';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// -- Messages Page -----------------------------------------------------------
|
|
488
|
+
async function renderMessagesPage(){
|
|
489
|
+
const l=document.getElementById('messages-list');
|
|
490
|
+
l.innerHTML='<div style="color:var(--dim);padding:20px;font:12px var(--mono)">Loading...</div>';
|
|
491
|
+
try{
|
|
492
|
+
const res=await fetch('/messages?limit=100');
|
|
493
|
+
const data=await res.json();
|
|
494
|
+
const msgs=data.messages||[];
|
|
495
|
+
if(msgs.length===0){l.innerHTML='<div style="color:var(--dim);padding:40px;font:14px var(--mono);text-align:center">No messages yet</div>';return;}
|
|
496
|
+
l.innerHTML='';
|
|
497
|
+
for(const m of msgs.reverse()){
|
|
498
|
+
const from=nodes.find(n=>n.did===m.fromDid)?.displayName||m.fromDid?.slice(0,20)||'?';
|
|
499
|
+
const to=nodes.find(n=>n.did===m.toDid)?.displayName||m.toDid?.slice(0,20)||'?';
|
|
500
|
+
const badge=m.verified?'<span class="msg-badge valid">VERIFIED ✓</span>':'<span class="msg-badge invalid">FAILED ✗</span>';
|
|
501
|
+
l.innerHTML+=`<div class="msg-row">
|
|
502
|
+
<div class="msg-header"><span class="msg-from">${from}</span><span class="msg-arrow">→</span><span class="msg-to">${to}</span>${badge}<span class="msg-time">${m.createdAt||''}</span></div>
|
|
503
|
+
<div class="msg-body">${(m.content||'').slice(0,300)}</div>
|
|
504
|
+
<div class="msg-sig">sig: ${(m.signature||'').slice(0,60)}...</div>
|
|
505
|
+
</div>`;
|
|
506
|
+
}
|
|
507
|
+
}catch(e){l.innerHTML=`<div style="color:var(--red);padding:20px;font:12px var(--mono)">Error: ${e.message}</div>`}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// -- Audit Page --------------------------------------------------------------
|
|
511
|
+
async function renderAuditPage(){
|
|
512
|
+
const c=document.getElementById('audit-content');
|
|
513
|
+
c.innerHTML='<div style="color:var(--dim);padding:20px;font:12px var(--mono)">Loading audit report...</div>';
|
|
514
|
+
try{
|
|
515
|
+
const res=await fetch('/audit/export');
|
|
516
|
+
const data=await res.json();
|
|
517
|
+
const s=data.summary;
|
|
518
|
+
c.innerHTML=`
|
|
519
|
+
<div class="audit-summary">
|
|
520
|
+
<div class="audit-card"><div class="val">${s.totalAgents}</div><div class="lbl">TOTAL AGENTS</div></div>
|
|
521
|
+
<div class="audit-card"><div class="val">${s.localAgents}</div><div class="lbl">LOCAL</div></div>
|
|
522
|
+
<div class="audit-card"><div class="val">${s.remoteAgents}</div><div class="lbl">REMOTE</div></div>
|
|
523
|
+
<div class="audit-card"><div class="val">${s.totalConnections}</div><div class="lbl">CONNECTIONS</div></div>
|
|
524
|
+
<div class="audit-card amber"><div class="val">${s.totalMessages}</div><div class="lbl">TOTAL MESSAGES</div></div>
|
|
525
|
+
<div class="audit-card green"><div class="val">${s.verifiedMessages}</div><div class="lbl">VERIFIED</div></div>
|
|
526
|
+
<div class="audit-card red"><div class="val">${s.failedVerifications}</div><div class="lbl">FAILED</div></div>
|
|
527
|
+
<div class="audit-card green"><div class="val">${(s.averageTrust*100).toFixed(0)}%</div><div class="lbl">AVG TRUST</div></div>
|
|
528
|
+
</div>
|
|
529
|
+
<div class="audit-actions">
|
|
530
|
+
<button class="audit-btn" onclick="downloadAudit()">↓ Download JSON</button>
|
|
531
|
+
<button class="audit-btn" onclick="copyAudit()">Copy to clipboard</button>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="audit-json" id="audit-json">${JSON.stringify(data,null,2)}</div>`;
|
|
534
|
+
window._auditData=data;
|
|
535
|
+
}catch(e){c.innerHTML=`<div style="color:var(--red);padding:20px;font:12px var(--mono)">Error: ${e.message}</div>`}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function downloadAudit(){
|
|
539
|
+
if(!window._auditData)return;
|
|
540
|
+
const blob=new Blob([JSON.stringify(window._auditData,null,2)],{type:'application/json'});
|
|
541
|
+
const a=document.createElement('a');a.href=URL.createObjectURL(blob);
|
|
542
|
+
a.download='meshsig-audit-'+new Date().toISOString().slice(0,10)+'.json';a.click();
|
|
543
|
+
}
|
|
544
|
+
function copyAudit(){
|
|
545
|
+
if(!window._auditData)return;
|
|
546
|
+
navigator.clipboard.writeText(JSON.stringify(window._auditData,null,2));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// -- Verify ------------------------------------------------------------------
|
|
550
|
+
async function doVerify(){
|
|
551
|
+
const msg=document.getElementById('v-msg').value.trim();
|
|
552
|
+
const sig=document.getElementById('v-sig').value.trim();
|
|
553
|
+
const pk=document.getElementById('v-pk').value.trim();
|
|
554
|
+
const did=document.getElementById('v-did').value.trim();
|
|
555
|
+
const r=document.getElementById('verify-result');
|
|
556
|
+
const rt=document.getElementById('vr-text');
|
|
557
|
+
const rd=document.getElementById('vr-detail');
|
|
558
|
+
if(!msg||!sig||(!pk&&!did)){r.className='invalid';r.style.display='block';rt.textContent='Fill all required fields';return}
|
|
559
|
+
const body={message:msg,signature:sig};
|
|
560
|
+
if(did)body.did=did;else body.publicKey=pk;
|
|
561
|
+
try{
|
|
562
|
+
const res=await fetch('/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
|
563
|
+
const data=await res.json();
|
|
564
|
+
if(data.valid){r.className='valid';rt.textContent='✓ SIGNATURE VALID';rd.textContent='Verified at '+data.verifiedAt;}
|
|
565
|
+
else{r.className='invalid';rt.textContent='✗ SIGNATURE INVALID';rd.textContent='Signature does not match message and key.';}
|
|
566
|
+
}catch(e){r.className='invalid';rt.textContent='Error: '+e.message;rd.textContent=''}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// -- WebSocket ---------------------------------------------------------------
|
|
570
|
+
let ws;
|
|
571
|
+
function connectWs(){
|
|
572
|
+
const proto=location.protocol==='https:'?'wss:':'ws:';
|
|
573
|
+
ws=new WebSocket(`${proto}//${location.host}?role=dashboard`);
|
|
574
|
+
ws.onopen=()=>{document.getElementById('wsd').className='pulse';document.getElementById('wsl').textContent='LIVE';document.getElementById('wsl').style.color='var(--green)';addEv('system','<span style="color:var(--green)">● CONNECTED</span> to MeshSig')};
|
|
575
|
+
ws.onmessage=e=>{try{handle(JSON.parse(e.data))}catch{}};
|
|
576
|
+
ws.onclose=()=>{document.getElementById('wsd').className='pulse off';document.getElementById('wsl').textContent='OFFLINE';document.getElementById('wsl').style.color='var(--red)';setTimeout(connectWs,2000)};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function handle(ev){
|
|
580
|
+
switch(ev.type){
|
|
581
|
+
case 'snapshot':
|
|
582
|
+
nodes.length=0;links.length=0;
|
|
583
|
+
for(const a of ev.data.agents)nodes.push({did:a.did,displayName:a.displayName,capabilities:a.capabilities,publicKey:a.publicKey,trustScore:a.trustScore||0,online:a.online!==false,interactionsTotal:a.interactionsTotal||0,origin:a.origin||'local',originServer:a.originServer||''});
|
|
584
|
+
for(const c of ev.data.connections)links.push({source:c.agentADid,target:c.agentBDid,channelId:c.channelId,trustScore:c.trustScore||0,messagesExchanged:c.messagesExchanged||0});
|
|
585
|
+
updateGraph();updateStats();
|
|
586
|
+
addEv('system',`Loaded <strong>${nodes.length}</strong> agents, <strong>${links.length}</strong> connections`);
|
|
587
|
+
break;
|
|
588
|
+
case 'agent:register':{
|
|
589
|
+
const d=ev.data;
|
|
590
|
+
if(!nodes.find(n=>n.did===d.did)){
|
|
591
|
+
nodes.push({did:d.did,displayName:d.name,capabilities:d.capabilities,publicKey:d.publicKey,trustScore:d.trustScore||0,online:true,interactionsTotal:0,origin:d.origin||'local',originServer:d.originServer||'',x:W/2+(Math.random()-0.5)*150,y:H/2+(Math.random()-0.5)*150});
|
|
592
|
+
updateGraph();updateStats();
|
|
593
|
+
setTimeout(()=>flashNode(d.did,d.origin==='remote'?'#f0b429':'#00ff88'),100);
|
|
594
|
+
playJoin();
|
|
595
|
+
}
|
|
596
|
+
addEv('join',`<span class="from">${d.name}</span> joined <span class="badge ok">IDENTITY</span>`);
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case 'connection:established':{
|
|
600
|
+
const d=ev.data;
|
|
601
|
+
if(!links.find(l=>(l.source.did||l.source)===d.agentA.did&&(l.target.did||l.target)===d.agentB.did)){
|
|
602
|
+
links.push({source:d.agentA.did,target:d.agentB.did,channelId:d.channelId,trustScore:0,messagesExchanged:0});
|
|
603
|
+
updateGraph();updateStats();
|
|
604
|
+
}
|
|
605
|
+
addEv('handshake',`<span class="from">${d.agentA.name}</span><span class="arrow">⟷</span><span class="to">${d.agentB.name}</span> <span class="badge ok">HANDSHAKE</span>`);
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
case 'message:sent':{
|
|
609
|
+
const d=ev.data;msgCount++;sigCount++;updateStats();
|
|
610
|
+
animateMsg(d.from.did,d.to.did,d.preview);
|
|
611
|
+
const fn=nodes.find(n=>n.did===d.from.did);
|
|
612
|
+
if(fn&&d.from.trustScore!==undefined)fn.trustScore=d.from.trustScore;
|
|
613
|
+
const lk=links.find(l=>((l.source.did||l.source)===d.from.did&&(l.target.did||l.target)===d.to.did)||((l.source.did||l.source)===d.to.did&&(l.target.did||l.target)===d.from.did));
|
|
614
|
+
if(lk){lk.trustScore=Math.min(1,(lk.trustScore||0)+(d.verified?0.02:-0.05));lk.messagesExchanged=(lk.messagesExchanged||0)+1}
|
|
615
|
+
const v=d.verified?'<span class="badge ok">SIGNED ✓</span>':'<span class="badge fail">UNVERIFIED</span>';
|
|
616
|
+
addEv('message',`<span class="from">${d.from.name||'?'}</span><span class="arrow">→</span><span class="to">${d.to.name||'?'}</span> ${v}<span class="content">${d.preview||''}</span>`);
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case 'handshake:verify':{
|
|
620
|
+
const d=ev.data;sigCount++;updateStats();
|
|
621
|
+
flashNode(d.fromDid,'#8b5cf6');flashNode(d.toDid,'#8b5cf6');
|
|
622
|
+
addEv('handshake',`<span class="from">${d.fromName}</span><span class="arrow">⟷</span><span class="to">${d.toName}</span> <span class="badge ok">Ed25519</span>`);
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
case 'peer:connected':
|
|
626
|
+
addEv('system',`🌐 Peer <strong>${ev.data.name||ev.data.url}</strong> — ${ev.data.agents||0} agents`);break;
|
|
627
|
+
case 'agent:heartbeat':break;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
connectWs();
|
|
632
|
+
window.addEventListener('resize',resizeGraph);
|
|
633
|
+
</script>
|
|
634
|
+
</body>
|
|
635
|
+
</html>
|
package/dist/demo.d.ts
ADDED