@sanctuary-framework/mcp-server 0.5.5 → 0.5.6
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/dist/cli.cjs +1919 -1352
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1919 -1352
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1920 -1351
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +1918 -1352
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -867,6 +867,8 @@ tier3_always_allow:
|
|
|
867
867
|
- handshake_respond
|
|
868
868
|
- handshake_complete
|
|
869
869
|
- handshake_status
|
|
870
|
+
- handshake_exchange
|
|
871
|
+
- handshake_verify_attestation
|
|
870
872
|
- reputation_query_weighted
|
|
871
873
|
- federation_peers
|
|
872
874
|
- federation_trust_evaluate
|
|
@@ -968,6 +970,8 @@ var init_loader = __esm({
|
|
|
968
970
|
"handshake_respond",
|
|
969
971
|
"handshake_complete",
|
|
970
972
|
"handshake_status",
|
|
973
|
+
"handshake_exchange",
|
|
974
|
+
"handshake_verify_attestation",
|
|
971
975
|
"reputation_query_weighted",
|
|
972
976
|
"federation_peers",
|
|
973
977
|
"federation_trust_evaluate",
|
|
@@ -1162,174 +1166,255 @@ function generateLoginHTML(options) {
|
|
|
1162
1166
|
return `<!DOCTYPE html>
|
|
1163
1167
|
<html lang="en">
|
|
1164
1168
|
<head>
|
|
1165
|
-
<meta charset="
|
|
1166
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1167
|
-
<title>Sanctuary
|
|
1168
|
-
<
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1169
|
+
<meta charset="UTF-8">
|
|
1170
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1171
|
+
<title>Sanctuary Dashboard</title>
|
|
1172
|
+
<style>
|
|
1173
|
+
:root {
|
|
1174
|
+
--bg: #0d1117;
|
|
1175
|
+
--surface: #161b22;
|
|
1176
|
+
--border: #30363d;
|
|
1177
|
+
--text-primary: #e6edf3;
|
|
1178
|
+
--text-secondary: #8b949e;
|
|
1179
|
+
--green: #3fb950;
|
|
1180
|
+
--amber: #d29922;
|
|
1181
|
+
--red: #f85149;
|
|
1182
|
+
--blue: #58a6ff;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
* {
|
|
1186
|
+
margin: 0;
|
|
1187
|
+
padding: 0;
|
|
1188
|
+
box-sizing: border-box;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
body {
|
|
1192
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
1193
|
+
background-color: var(--bg);
|
|
1194
|
+
color: var(--text-primary);
|
|
1195
|
+
min-height: 100vh;
|
|
1196
|
+
display: flex;
|
|
1197
|
+
align-items: center;
|
|
1198
|
+
justify-content: center;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.login-container {
|
|
1202
|
+
width: 100%;
|
|
1203
|
+
max-width: 400px;
|
|
1204
|
+
padding: 20px;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.login-card {
|
|
1208
|
+
background-color: var(--surface);
|
|
1209
|
+
border: 1px solid var(--border);
|
|
1210
|
+
border-radius: 8px;
|
|
1211
|
+
padding: 40px 32px;
|
|
1212
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
.login-header {
|
|
1216
|
+
display: flex;
|
|
1217
|
+
align-items: center;
|
|
1218
|
+
gap: 12px;
|
|
1219
|
+
margin-bottom: 32px;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.logo {
|
|
1223
|
+
font-size: 24px;
|
|
1224
|
+
font-weight: 700;
|
|
1225
|
+
color: var(--blue);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.logo-text {
|
|
1229
|
+
display: flex;
|
|
1230
|
+
flex-direction: column;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.logo-text .title {
|
|
1234
|
+
font-size: 18px;
|
|
1235
|
+
font-weight: 600;
|
|
1236
|
+
letter-spacing: -0.5px;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.logo-text .version {
|
|
1240
|
+
font-size: 12px;
|
|
1241
|
+
color: var(--text-secondary);
|
|
1242
|
+
margin-top: 2px;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.form-group {
|
|
1246
|
+
margin-bottom: 24px;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
label {
|
|
1250
|
+
display: block;
|
|
1251
|
+
font-size: 14px;
|
|
1252
|
+
font-weight: 500;
|
|
1253
|
+
margin-bottom: 8px;
|
|
1254
|
+
color: var(--text-primary);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
input[type="text"],
|
|
1258
|
+
input[type="password"] {
|
|
1259
|
+
width: 100%;
|
|
1260
|
+
padding: 10px 12px;
|
|
1261
|
+
background-color: var(--bg);
|
|
1262
|
+
border: 1px solid var(--border);
|
|
1263
|
+
border-radius: 6px;
|
|
1264
|
+
color: var(--text-primary);
|
|
1265
|
+
font-size: 14px;
|
|
1266
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1267
|
+
transition: border-color 0.2s;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
input[type="text"]:focus,
|
|
1271
|
+
input[type="password"]:focus {
|
|
1272
|
+
outline: none;
|
|
1273
|
+
border-color: var(--blue);
|
|
1274
|
+
box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.1);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.error-message {
|
|
1278
|
+
display: none;
|
|
1279
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
1280
|
+
border: 1px solid var(--red);
|
|
1281
|
+
color: #ff9999;
|
|
1282
|
+
padding: 12px;
|
|
1283
|
+
border-radius: 6px;
|
|
1284
|
+
font-size: 13px;
|
|
1285
|
+
margin-bottom: 20px;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.error-message.show {
|
|
1289
|
+
display: block;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
button {
|
|
1293
|
+
width: 100%;
|
|
1294
|
+
padding: 10px 16px;
|
|
1295
|
+
background-color: var(--blue);
|
|
1296
|
+
color: var(--bg);
|
|
1297
|
+
border: none;
|
|
1298
|
+
border-radius: 6px;
|
|
1299
|
+
font-size: 14px;
|
|
1300
|
+
font-weight: 600;
|
|
1301
|
+
cursor: pointer;
|
|
1302
|
+
transition: background-color 0.2s;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
button:hover {
|
|
1306
|
+
background-color: #79c0ff;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
button:active {
|
|
1310
|
+
background-color: #4184e4;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
button:disabled {
|
|
1314
|
+
background-color: var(--text-secondary);
|
|
1315
|
+
cursor: not-allowed;
|
|
1316
|
+
opacity: 0.5;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
.info-text {
|
|
1320
|
+
font-size: 12px;
|
|
1321
|
+
color: var(--text-secondary);
|
|
1322
|
+
margin-top: 16px;
|
|
1323
|
+
text-align: center;
|
|
1324
|
+
}
|
|
1325
|
+
</style>
|
|
1279
1326
|
</head>
|
|
1280
1327
|
<body>
|
|
1281
|
-
<div class="login-container">
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1328
|
+
<div class="login-container">
|
|
1329
|
+
<div class="login-card">
|
|
1330
|
+
<div class="login-header">
|
|
1331
|
+
<div class="logo">\u25C6</div>
|
|
1332
|
+
<div class="logo-text">
|
|
1333
|
+
<div class="title">SANCTUARY</div>
|
|
1334
|
+
<div class="version">v${options.serverVersion}</div>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
|
|
1338
|
+
<div id="error-message" class="error-message"></div>
|
|
1339
|
+
|
|
1340
|
+
<form id="login-form">
|
|
1341
|
+
<div class="form-group">
|
|
1342
|
+
<label for="auth-token">Bearer Token</label>
|
|
1343
|
+
<input
|
|
1344
|
+
type="text"
|
|
1345
|
+
id="auth-token"
|
|
1346
|
+
name="token"
|
|
1347
|
+
placeholder="Paste your session token..."
|
|
1348
|
+
autocomplete="off"
|
|
1349
|
+
spellcheck="false"
|
|
1350
|
+
required
|
|
1351
|
+
/>
|
|
1352
|
+
</div>
|
|
1353
|
+
|
|
1354
|
+
<button type="submit" id="login-button">Open Dashboard</button>
|
|
1355
|
+
</form>
|
|
1356
|
+
|
|
1357
|
+
<div class="info-text">
|
|
1358
|
+
Session tokens expire after 1 hour of inactivity
|
|
1359
|
+
</div>
|
|
1360
|
+
</div>
|
|
1294
1361
|
</div>
|
|
1295
|
-
|
|
1296
|
-
<script>
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1362
|
+
|
|
1363
|
+
<script>
|
|
1364
|
+
const loginForm = document.getElementById('login-form');
|
|
1365
|
+
const authTokenInput = document.getElementById('auth-token');
|
|
1366
|
+
const errorMessage = document.getElementById('error-message');
|
|
1367
|
+
const loginButton = document.getElementById('login-button');
|
|
1368
|
+
|
|
1369
|
+
loginForm.addEventListener('submit', async (e) => {
|
|
1370
|
+
e.preventDefault();
|
|
1371
|
+
const token = authTokenInput.value.trim();
|
|
1372
|
+
|
|
1373
|
+
if (!token) {
|
|
1374
|
+
showError('Token is required');
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
loginButton.disabled = true;
|
|
1379
|
+
loginButton.textContent = 'Verifying...';
|
|
1380
|
+
errorMessage.classList.remove('show');
|
|
1381
|
+
|
|
1382
|
+
try {
|
|
1383
|
+
const response = await fetch('/auth/session', {
|
|
1384
|
+
method: 'POST',
|
|
1385
|
+
headers: {
|
|
1386
|
+
'Content-Type': 'application/json',
|
|
1387
|
+
'Authorization': 'Bearer ' + token,
|
|
1388
|
+
},
|
|
1389
|
+
body: JSON.stringify({ token }),
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
if (response.ok) {
|
|
1393
|
+
const data = await response.json();
|
|
1394
|
+
sessionStorage.setItem('authToken', token);
|
|
1395
|
+
window.location.href = '/dashboard';
|
|
1396
|
+
} else if (response.status === 401) {
|
|
1397
|
+
showError('Invalid token. Please check and try again.');
|
|
1398
|
+
} else {
|
|
1399
|
+
showError('Authentication failed. Please try again.');
|
|
1400
|
+
}
|
|
1401
|
+
} catch (err) {
|
|
1402
|
+
showError('Connection error. Please check your network.');
|
|
1403
|
+
} finally {
|
|
1404
|
+
loginButton.disabled = false;
|
|
1405
|
+
loginButton.textContent = 'Open Dashboard';
|
|
1406
|
+
}
|
|
1310
1407
|
});
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
'; path=/; SameSite=Strict; max-age=' + maxAge;
|
|
1322
|
-
// Reload to enter the dashboard
|
|
1323
|
-
window.location.reload();
|
|
1324
|
-
} catch (err) {
|
|
1325
|
-
errEl.textContent = err.message || 'Authentication failed. Check your token.';
|
|
1326
|
-
errEl.style.display = 'block';
|
|
1327
|
-
btn.disabled = false;
|
|
1328
|
-
btn.textContent = 'Open Dashboard';
|
|
1329
|
-
}
|
|
1330
|
-
return false;
|
|
1331
|
-
}
|
|
1332
|
-
</script>
|
|
1408
|
+
|
|
1409
|
+
function showError(message) {
|
|
1410
|
+
errorMessage.textContent = message;
|
|
1411
|
+
errorMessage.classList.add('show');
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
authTokenInput.addEventListener('input', () => {
|
|
1415
|
+
errorMessage.classList.remove('show');
|
|
1416
|
+
});
|
|
1417
|
+
</script>
|
|
1333
1418
|
</body>
|
|
1334
1419
|
</html>`;
|
|
1335
1420
|
}
|
|
@@ -1337,1412 +1422,1648 @@ function generateDashboardHTML(options) {
|
|
|
1337
1422
|
return `<!DOCTYPE html>
|
|
1338
1423
|
<html lang="en">
|
|
1339
1424
|
<head>
|
|
1340
|
-
<meta charset="
|
|
1341
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1342
|
-
<title>Sanctuary
|
|
1343
|
-
<
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1425
|
+
<meta charset="UTF-8">
|
|
1426
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1427
|
+
<title>Sanctuary Dashboard</title>
|
|
1428
|
+
<style>
|
|
1429
|
+
:root {
|
|
1430
|
+
--bg: #0d1117;
|
|
1431
|
+
--surface: #161b22;
|
|
1432
|
+
--border: #30363d;
|
|
1433
|
+
--text-primary: #e6edf3;
|
|
1434
|
+
--text-secondary: #8b949e;
|
|
1435
|
+
--green: #3fb950;
|
|
1436
|
+
--amber: #d29922;
|
|
1437
|
+
--red: #f85149;
|
|
1438
|
+
--blue: #58a6ff;
|
|
1439
|
+
--success: #3fb950;
|
|
1440
|
+
--warning: #d29922;
|
|
1441
|
+
--error: #f85149;
|
|
1442
|
+
--muted: #21262d;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
* {
|
|
1446
|
+
margin: 0;
|
|
1447
|
+
padding: 0;
|
|
1448
|
+
box-sizing: border-box;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
html, body {
|
|
1452
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
1453
|
+
background-color: var(--bg);
|
|
1454
|
+
color: var(--text-primary);
|
|
1455
|
+
height: 100%;
|
|
1456
|
+
overflow: hidden;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
body {
|
|
1460
|
+
display: flex;
|
|
1461
|
+
flex-direction: column;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
/* Status Bar */
|
|
1465
|
+
.status-bar {
|
|
1466
|
+
position: fixed;
|
|
1467
|
+
top: 0;
|
|
1468
|
+
left: 0;
|
|
1469
|
+
right: 0;
|
|
1470
|
+
height: 56px;
|
|
1471
|
+
background-color: var(--surface);
|
|
1472
|
+
border-bottom: 1px solid var(--border);
|
|
1473
|
+
display: flex;
|
|
1474
|
+
align-items: center;
|
|
1475
|
+
padding: 0 24px;
|
|
1476
|
+
gap: 24px;
|
|
1477
|
+
z-index: 100;
|
|
1478
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
.status-bar-left {
|
|
1482
|
+
display: flex;
|
|
1483
|
+
align-items: center;
|
|
1484
|
+
gap: 12px;
|
|
1485
|
+
flex: 0 0 auto;
|
|
1486
|
+
}
|
|
1365
1487
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
body {
|
|
1373
|
-
font-family: var(--sans);
|
|
1374
|
-
background: var(--bg);
|
|
1375
|
-
color: var(--text-primary);
|
|
1376
|
-
display: flex;
|
|
1377
|
-
flex-direction: column;
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
/* \u2500\u2500 Top Status Bar (fixed) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
1488
|
+
.logo-icon {
|
|
1489
|
+
font-size: 20px;
|
|
1490
|
+
color: var(--blue);
|
|
1491
|
+
font-weight: 700;
|
|
1492
|
+
}
|
|
1381
1493
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
right: 0;
|
|
1387
|
-
height: 56px;
|
|
1388
|
-
background: var(--surface);
|
|
1389
|
-
border-bottom: 1px solid var(--border);
|
|
1390
|
-
display: flex;
|
|
1391
|
-
align-items: center;
|
|
1392
|
-
padding: 0 20px;
|
|
1393
|
-
gap: 24px;
|
|
1394
|
-
z-index: 1000;
|
|
1395
|
-
}
|
|
1494
|
+
.logo-info {
|
|
1495
|
+
display: flex;
|
|
1496
|
+
flex-direction: column;
|
|
1497
|
+
}
|
|
1396
1498
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1499
|
+
.logo-title {
|
|
1500
|
+
font-size: 13px;
|
|
1501
|
+
font-weight: 600;
|
|
1502
|
+
line-height: 1;
|
|
1503
|
+
color: var(--text-primary);
|
|
1504
|
+
}
|
|
1403
1505
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
}
|
|
1506
|
+
.logo-version {
|
|
1507
|
+
font-size: 11px;
|
|
1508
|
+
color: var(--text-secondary);
|
|
1509
|
+
margin-top: 2px;
|
|
1510
|
+
}
|
|
1410
1511
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1512
|
+
.status-bar-center {
|
|
1513
|
+
flex: 1;
|
|
1514
|
+
display: flex;
|
|
1515
|
+
justify-content: center;
|
|
1516
|
+
}
|
|
1414
1517
|
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1518
|
+
.sovereignty-badge {
|
|
1519
|
+
display: flex;
|
|
1520
|
+
align-items: center;
|
|
1521
|
+
gap: 8px;
|
|
1522
|
+
padding: 8px 16px;
|
|
1523
|
+
background-color: rgba(63, 185, 80, 0.1);
|
|
1524
|
+
border: 1px solid rgba(63, 185, 80, 0.3);
|
|
1525
|
+
border-radius: 6px;
|
|
1526
|
+
font-size: 13px;
|
|
1527
|
+
font-weight: 500;
|
|
1528
|
+
}
|
|
1420
1529
|
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
justify-content: center;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
.sovereignty-badge {
|
|
1429
|
-
display: flex;
|
|
1430
|
-
align-items: center;
|
|
1431
|
-
gap: 8px;
|
|
1432
|
-
padding: 6px 12px;
|
|
1433
|
-
background: rgba(88, 166, 255, 0.1);
|
|
1434
|
-
border: 1px solid var(--blue);
|
|
1435
|
-
border-radius: 20px;
|
|
1436
|
-
font-size: 13px;
|
|
1437
|
-
font-weight: 600;
|
|
1438
|
-
}
|
|
1530
|
+
.sovereignty-badge.degraded {
|
|
1531
|
+
background-color: rgba(210, 153, 34, 0.1);
|
|
1532
|
+
border-color: rgba(210, 153, 34, 0.3);
|
|
1533
|
+
}
|
|
1439
1534
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
width: 28px;
|
|
1445
|
-
height: 28px;
|
|
1446
|
-
border-radius: 50%;
|
|
1447
|
-
font-family: var(--mono);
|
|
1448
|
-
font-weight: 700;
|
|
1449
|
-
font-size: 12px;
|
|
1450
|
-
background: var(--blue);
|
|
1451
|
-
color: var(--bg);
|
|
1452
|
-
}
|
|
1535
|
+
.sovereignty-badge.inactive {
|
|
1536
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
1537
|
+
border-color: rgba(248, 81, 73, 0.3);
|
|
1538
|
+
}
|
|
1453
1539
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1540
|
+
.sovereignty-score {
|
|
1541
|
+
font-weight: 700;
|
|
1542
|
+
color: var(--green);
|
|
1543
|
+
}
|
|
1457
1544
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1545
|
+
.sovereignty-badge.degraded .sovereignty-score {
|
|
1546
|
+
color: var(--amber);
|
|
1547
|
+
}
|
|
1461
1548
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1549
|
+
.sovereignty-badge.inactive .sovereignty-score {
|
|
1550
|
+
color: var(--red);
|
|
1551
|
+
}
|
|
1465
1552
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1553
|
+
.status-bar-right {
|
|
1554
|
+
display: flex;
|
|
1555
|
+
align-items: center;
|
|
1556
|
+
gap: 16px;
|
|
1557
|
+
flex: 0 0 auto;
|
|
1558
|
+
}
|
|
1472
1559
|
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1560
|
+
.status-item {
|
|
1561
|
+
display: flex;
|
|
1562
|
+
align-items: center;
|
|
1563
|
+
gap: 6px;
|
|
1564
|
+
font-size: 12px;
|
|
1565
|
+
color: var(--text-secondary);
|
|
1566
|
+
}
|
|
1481
1567
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1568
|
+
.status-item strong {
|
|
1569
|
+
color: var(--text-primary);
|
|
1570
|
+
font-weight: 500;
|
|
1571
|
+
}
|
|
1486
1572
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
font-family: var(--mono);
|
|
1494
|
-
}
|
|
1573
|
+
.status-dot {
|
|
1574
|
+
width: 8px;
|
|
1575
|
+
height: 8px;
|
|
1576
|
+
border-radius: 50%;
|
|
1577
|
+
background-color: var(--green);
|
|
1578
|
+
}
|
|
1495
1579
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
border-radius: 50%;
|
|
1500
|
-
background: var(--green);
|
|
1501
|
-
animation: pulse 2s ease-in-out infinite;
|
|
1502
|
-
}
|
|
1580
|
+
.status-dot.disconnected {
|
|
1581
|
+
background-color: var(--red);
|
|
1582
|
+
}
|
|
1503
1583
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1584
|
+
.pending-badge {
|
|
1585
|
+
display: flex;
|
|
1586
|
+
align-items: center;
|
|
1587
|
+
gap: 6px;
|
|
1588
|
+
padding: 4px 8px;
|
|
1589
|
+
background-color: var(--blue);
|
|
1590
|
+
color: var(--bg);
|
|
1591
|
+
border-radius: 4px;
|
|
1592
|
+
font-size: 11px;
|
|
1593
|
+
font-weight: 600;
|
|
1594
|
+
cursor: pointer;
|
|
1595
|
+
}
|
|
1508
1596
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1597
|
+
/* Main Content */
|
|
1598
|
+
.main-content {
|
|
1599
|
+
flex: 1;
|
|
1600
|
+
margin-top: 56px;
|
|
1601
|
+
overflow-y: auto;
|
|
1602
|
+
padding: 24px;
|
|
1603
|
+
}
|
|
1513
1604
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
min-width: 24px;
|
|
1519
|
-
height: 24px;
|
|
1520
|
-
padding: 0 6px;
|
|
1521
|
-
background: var(--red);
|
|
1522
|
-
color: white;
|
|
1523
|
-
border-radius: 12px;
|
|
1524
|
-
font-size: 11px;
|
|
1525
|
-
font-weight: 700;
|
|
1526
|
-
animation: pulse 1s ease-in-out infinite;
|
|
1527
|
-
}
|
|
1605
|
+
.grid {
|
|
1606
|
+
display: grid;
|
|
1607
|
+
gap: 20px;
|
|
1608
|
+
}
|
|
1528
1609
|
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1610
|
+
/* Row 1: Sovereignty Layers */
|
|
1611
|
+
.sovereignty-layers {
|
|
1612
|
+
display: grid;
|
|
1613
|
+
grid-template-columns: repeat(4, 1fr);
|
|
1614
|
+
gap: 16px;
|
|
1615
|
+
}
|
|
1532
1616
|
|
|
1533
|
-
|
|
1617
|
+
.layer-card {
|
|
1618
|
+
background-color: var(--surface);
|
|
1619
|
+
border: 1px solid var(--border);
|
|
1620
|
+
border-radius: 8px;
|
|
1621
|
+
padding: 20px;
|
|
1622
|
+
display: flex;
|
|
1623
|
+
flex-direction: column;
|
|
1624
|
+
gap: 12px;
|
|
1625
|
+
}
|
|
1534
1626
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
overflow: hidden;
|
|
1540
|
-
}
|
|
1627
|
+
.layer-card.degraded {
|
|
1628
|
+
border-color: var(--amber);
|
|
1629
|
+
background-color: rgba(210, 153, 34, 0.05);
|
|
1630
|
+
}
|
|
1541
1631
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
border-right: 1px solid var(--border);
|
|
1547
|
-
overflow: hidden;
|
|
1548
|
-
}
|
|
1632
|
+
.layer-card.inactive {
|
|
1633
|
+
border-color: var(--red);
|
|
1634
|
+
background-color: rgba(248, 81, 73, 0.05);
|
|
1635
|
+
}
|
|
1549
1636
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
font-weight: 600;
|
|
1558
|
-
text-transform: uppercase;
|
|
1559
|
-
letter-spacing: 0.5px;
|
|
1560
|
-
color: var(--text-secondary);
|
|
1561
|
-
}
|
|
1637
|
+
.layer-name {
|
|
1638
|
+
font-size: 12px;
|
|
1639
|
+
font-weight: 600;
|
|
1640
|
+
color: var(--text-secondary);
|
|
1641
|
+
text-transform: uppercase;
|
|
1642
|
+
letter-spacing: 0.5px;
|
|
1643
|
+
}
|
|
1562
1644
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
}
|
|
1645
|
+
.layer-title {
|
|
1646
|
+
font-size: 14px;
|
|
1647
|
+
font-weight: 600;
|
|
1648
|
+
color: var(--text-primary);
|
|
1649
|
+
}
|
|
1569
1650
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1651
|
+
.layer-status {
|
|
1652
|
+
display: inline-flex;
|
|
1653
|
+
align-items: center;
|
|
1654
|
+
gap: 6px;
|
|
1655
|
+
padding: 4px 8px;
|
|
1656
|
+
background-color: rgba(63, 185, 80, 0.15);
|
|
1657
|
+
color: var(--green);
|
|
1658
|
+
border-radius: 4px;
|
|
1659
|
+
font-size: 11px;
|
|
1660
|
+
font-weight: 600;
|
|
1661
|
+
width: fit-content;
|
|
1662
|
+
}
|
|
1575
1663
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
font-family: var(--mono);
|
|
1581
|
-
cursor: pointer;
|
|
1582
|
-
transition: background 0.15s;
|
|
1583
|
-
display: flex;
|
|
1584
|
-
align-items: flex-start;
|
|
1585
|
-
gap: 10px;
|
|
1586
|
-
}
|
|
1664
|
+
.layer-card.degraded .layer-status {
|
|
1665
|
+
background-color: rgba(210, 153, 34, 0.15);
|
|
1666
|
+
color: var(--amber);
|
|
1667
|
+
}
|
|
1587
1668
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1669
|
+
.layer-card.inactive .layer-status {
|
|
1670
|
+
background-color: rgba(248, 81, 73, 0.15);
|
|
1671
|
+
color: var(--red);
|
|
1672
|
+
}
|
|
1591
1673
|
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1674
|
+
.layer-detail {
|
|
1675
|
+
font-size: 12px;
|
|
1676
|
+
color: var(--text-secondary);
|
|
1677
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1678
|
+
padding: 8px;
|
|
1679
|
+
background-color: var(--bg);
|
|
1680
|
+
border-radius: 4px;
|
|
1681
|
+
border-left: 2px solid var(--blue);
|
|
1682
|
+
}
|
|
1600
1683
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1684
|
+
/* Row 2: Info Cards */
|
|
1685
|
+
.info-cards {
|
|
1686
|
+
display: grid;
|
|
1687
|
+
grid-template-columns: repeat(3, 1fr);
|
|
1688
|
+
gap: 16px;
|
|
1689
|
+
}
|
|
1605
1690
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1691
|
+
.info-card {
|
|
1692
|
+
background-color: var(--surface);
|
|
1693
|
+
border: 1px solid var(--border);
|
|
1694
|
+
border-radius: 8px;
|
|
1695
|
+
padding: 20px;
|
|
1696
|
+
}
|
|
1611
1697
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1698
|
+
.card-header {
|
|
1699
|
+
font-size: 12px;
|
|
1700
|
+
font-weight: 600;
|
|
1701
|
+
color: var(--text-secondary);
|
|
1702
|
+
text-transform: uppercase;
|
|
1703
|
+
letter-spacing: 0.5px;
|
|
1704
|
+
margin-bottom: 16px;
|
|
1705
|
+
}
|
|
1618
1706
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
font-weight: 700;
|
|
1627
|
-
border-radius: 3px;
|
|
1628
|
-
text-transform: uppercase;
|
|
1629
|
-
flex: 0 0 auto;
|
|
1630
|
-
}
|
|
1707
|
+
.card-row {
|
|
1708
|
+
display: flex;
|
|
1709
|
+
justify-content: space-between;
|
|
1710
|
+
align-items: center;
|
|
1711
|
+
margin-bottom: 12px;
|
|
1712
|
+
font-size: 13px;
|
|
1713
|
+
}
|
|
1631
1714
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1715
|
+
.card-row:last-child {
|
|
1716
|
+
margin-bottom: 0;
|
|
1717
|
+
}
|
|
1636
1718
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1719
|
+
.card-label {
|
|
1720
|
+
color: var(--text-secondary);
|
|
1721
|
+
}
|
|
1641
1722
|
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1723
|
+
.card-value {
|
|
1724
|
+
color: var(--text-primary);
|
|
1725
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1726
|
+
font-weight: 500;
|
|
1727
|
+
}
|
|
1646
1728
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1729
|
+
.identity-badge {
|
|
1730
|
+
display: inline-flex;
|
|
1731
|
+
align-items: center;
|
|
1732
|
+
gap: 4px;
|
|
1733
|
+
padding: 2px 6px;
|
|
1734
|
+
background-color: rgba(88, 166, 255, 0.15);
|
|
1735
|
+
color: var(--blue);
|
|
1736
|
+
border-radius: 3px;
|
|
1737
|
+
font-size: 10px;
|
|
1738
|
+
font-weight: 600;
|
|
1739
|
+
text-transform: uppercase;
|
|
1740
|
+
}
|
|
1651
1741
|
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1742
|
+
.trust-tier-badge {
|
|
1743
|
+
display: inline-flex;
|
|
1744
|
+
align-items: center;
|
|
1745
|
+
gap: 4px;
|
|
1746
|
+
padding: 2px 6px;
|
|
1747
|
+
background-color: rgba(63, 185, 80, 0.15);
|
|
1748
|
+
color: var(--green);
|
|
1749
|
+
border-radius: 3px;
|
|
1750
|
+
font-size: 10px;
|
|
1751
|
+
font-weight: 600;
|
|
1752
|
+
}
|
|
1655
1753
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1754
|
+
.truncated {
|
|
1755
|
+
max-width: 200px;
|
|
1756
|
+
overflow: hidden;
|
|
1757
|
+
text-overflow: ellipsis;
|
|
1758
|
+
white-space: nowrap;
|
|
1759
|
+
}
|
|
1659
1760
|
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1761
|
+
/* Row 3: SHR & Activity */
|
|
1762
|
+
.main-panels {
|
|
1763
|
+
display: grid;
|
|
1764
|
+
grid-template-columns: 1fr 1fr;
|
|
1765
|
+
gap: 16px;
|
|
1766
|
+
min-height: 400px;
|
|
1767
|
+
}
|
|
1665
1768
|
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1769
|
+
.panel {
|
|
1770
|
+
background-color: var(--surface);
|
|
1771
|
+
border: 1px solid var(--border);
|
|
1772
|
+
border-radius: 8px;
|
|
1773
|
+
display: flex;
|
|
1774
|
+
flex-direction: column;
|
|
1775
|
+
overflow: hidden;
|
|
1776
|
+
}
|
|
1674
1777
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1778
|
+
.panel-header {
|
|
1779
|
+
padding: 16px 20px;
|
|
1780
|
+
border-bottom: 1px solid var(--border);
|
|
1781
|
+
display: flex;
|
|
1782
|
+
justify-content: space-between;
|
|
1783
|
+
align-items: center;
|
|
1784
|
+
}
|
|
1683
1785
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1786
|
+
.panel-title {
|
|
1787
|
+
font-size: 14px;
|
|
1788
|
+
font-weight: 600;
|
|
1789
|
+
color: var(--text-primary);
|
|
1790
|
+
}
|
|
1688
1791
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1792
|
+
.panel-action {
|
|
1793
|
+
background: none;
|
|
1794
|
+
border: none;
|
|
1795
|
+
color: var(--blue);
|
|
1796
|
+
cursor: pointer;
|
|
1797
|
+
font-size: 12px;
|
|
1798
|
+
padding: 0;
|
|
1799
|
+
font-weight: 500;
|
|
1800
|
+
transition: color 0.2s;
|
|
1801
|
+
}
|
|
1692
1802
|
|
|
1693
|
-
|
|
1803
|
+
.panel-action:hover {
|
|
1804
|
+
color: #79c0ff;
|
|
1805
|
+
}
|
|
1694
1806
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
overflow: hidden;
|
|
1701
|
-
}
|
|
1807
|
+
.panel-content {
|
|
1808
|
+
flex: 1;
|
|
1809
|
+
overflow-y: auto;
|
|
1810
|
+
padding: 20px;
|
|
1811
|
+
}
|
|
1702
1812
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
color: var(--text-secondary);
|
|
1711
|
-
display: flex;
|
|
1712
|
-
align-items: center;
|
|
1713
|
-
gap: 8px;
|
|
1714
|
-
}
|
|
1813
|
+
/* SHR Viewer */
|
|
1814
|
+
.shr-json {
|
|
1815
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1816
|
+
font-size: 12px;
|
|
1817
|
+
line-height: 1.6;
|
|
1818
|
+
color: var(--text-secondary);
|
|
1819
|
+
}
|
|
1715
1820
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
padding: 16px 16px;
|
|
1720
|
-
display: grid;
|
|
1721
|
-
grid-template-columns: 1fr 1fr;
|
|
1722
|
-
gap: 12px;
|
|
1723
|
-
}
|
|
1821
|
+
.shr-section {
|
|
1822
|
+
margin-bottom: 12px;
|
|
1823
|
+
}
|
|
1724
1824
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1825
|
+
.shr-section-header {
|
|
1826
|
+
display: flex;
|
|
1827
|
+
align-items: center;
|
|
1828
|
+
gap: 8px;
|
|
1829
|
+
cursor: pointer;
|
|
1830
|
+
font-weight: 600;
|
|
1831
|
+
color: var(--text-primary);
|
|
1832
|
+
padding: 8px;
|
|
1833
|
+
background-color: var(--bg);
|
|
1834
|
+
border-radius: 4px;
|
|
1835
|
+
user-select: none;
|
|
1836
|
+
}
|
|
1734
1837
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1838
|
+
.shr-section-header:hover {
|
|
1839
|
+
background-color: var(--muted);
|
|
1840
|
+
}
|
|
1738
1841
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1842
|
+
.shr-toggle {
|
|
1843
|
+
width: 16px;
|
|
1844
|
+
height: 16px;
|
|
1845
|
+
display: flex;
|
|
1846
|
+
align-items: center;
|
|
1847
|
+
justify-content: center;
|
|
1848
|
+
font-size: 10px;
|
|
1849
|
+
transition: transform 0.2s;
|
|
1850
|
+
}
|
|
1746
1851
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
gap: 6px;
|
|
1751
|
-
font-size: 12px;
|
|
1752
|
-
font-weight: 600;
|
|
1753
|
-
}
|
|
1852
|
+
.shr-section.collapsed .shr-toggle {
|
|
1853
|
+
transform: rotate(-90deg);
|
|
1854
|
+
}
|
|
1754
1855
|
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1856
|
+
.shr-section-content {
|
|
1857
|
+
padding: 8px 16px;
|
|
1858
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
1859
|
+
border-radius: 4px;
|
|
1860
|
+
margin-top: 4px;
|
|
1861
|
+
}
|
|
1758
1862
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1863
|
+
.shr-section.collapsed .shr-section-content {
|
|
1864
|
+
display: none;
|
|
1865
|
+
}
|
|
1762
1866
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
margin-top: 4px;
|
|
1768
|
-
}
|
|
1867
|
+
.shr-item {
|
|
1868
|
+
display: flex;
|
|
1869
|
+
margin-bottom: 4px;
|
|
1870
|
+
}
|
|
1769
1871
|
|
|
1770
|
-
|
|
1872
|
+
.shr-key {
|
|
1873
|
+
color: var(--blue);
|
|
1874
|
+
margin-right: 8px;
|
|
1875
|
+
min-width: 120px;
|
|
1876
|
+
}
|
|
1771
1877
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
bottom: 0;
|
|
1777
|
-
width: 0;
|
|
1778
|
-
background: var(--surface);
|
|
1779
|
-
border-left: 1px solid var(--border);
|
|
1780
|
-
z-index: 999;
|
|
1781
|
-
overflow-y: auto;
|
|
1782
|
-
transition: width 0.3s ease-out;
|
|
1783
|
-
display: flex;
|
|
1784
|
-
flex-direction: column;
|
|
1785
|
-
}
|
|
1878
|
+
.shr-value {
|
|
1879
|
+
color: var(--green);
|
|
1880
|
+
word-break: break-all;
|
|
1881
|
+
}
|
|
1786
1882
|
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1883
|
+
/* Activity Feed */
|
|
1884
|
+
.activity-feed {
|
|
1885
|
+
display: flex;
|
|
1886
|
+
flex-direction: column;
|
|
1887
|
+
gap: 12px;
|
|
1888
|
+
}
|
|
1790
1889
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1890
|
+
.activity-item {
|
|
1891
|
+
padding: 12px;
|
|
1892
|
+
background-color: var(--bg);
|
|
1893
|
+
border-left: 2px solid var(--border);
|
|
1894
|
+
border-radius: 4px;
|
|
1895
|
+
font-size: 12px;
|
|
1796
1896
|
}
|
|
1797
|
-
}
|
|
1798
1897
|
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
display: flex;
|
|
1803
|
-
align-items: center;
|
|
1804
|
-
justify-content: space-between;
|
|
1805
|
-
flex: 0 0 auto;
|
|
1806
|
-
}
|
|
1898
|
+
.activity-item.tool-call {
|
|
1899
|
+
border-left-color: var(--blue);
|
|
1900
|
+
}
|
|
1807
1901
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
text-transform: uppercase;
|
|
1812
|
-
letter-spacing: 0.5px;
|
|
1813
|
-
color: var(--text-primary);
|
|
1814
|
-
}
|
|
1902
|
+
.activity-item.context-gate {
|
|
1903
|
+
border-left-color: var(--amber);
|
|
1904
|
+
}
|
|
1815
1905
|
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
color: var(--text-secondary);
|
|
1820
|
-
cursor: pointer;
|
|
1821
|
-
font-size: 18px;
|
|
1822
|
-
padding: 0;
|
|
1823
|
-
display: flex;
|
|
1824
|
-
align-items: center;
|
|
1825
|
-
justify-content: center;
|
|
1826
|
-
}
|
|
1906
|
+
.activity-item.injection {
|
|
1907
|
+
border-left-color: var(--red);
|
|
1908
|
+
}
|
|
1827
1909
|
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1910
|
+
.activity-item.protection {
|
|
1911
|
+
border-left-color: var(--green);
|
|
1912
|
+
}
|
|
1831
1913
|
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1914
|
+
.activity-type {
|
|
1915
|
+
font-weight: 600;
|
|
1916
|
+
color: var(--text-primary);
|
|
1917
|
+
margin-bottom: 4px;
|
|
1918
|
+
text-transform: uppercase;
|
|
1919
|
+
font-size: 11px;
|
|
1920
|
+
letter-spacing: 0.5px;
|
|
1921
|
+
}
|
|
1836
1922
|
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
}
|
|
1923
|
+
.activity-content {
|
|
1924
|
+
color: var(--text-secondary);
|
|
1925
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1926
|
+
margin-bottom: 4px;
|
|
1927
|
+
word-break: break-all;
|
|
1928
|
+
}
|
|
1844
1929
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}
|
|
1930
|
+
.activity-time {
|
|
1931
|
+
font-size: 11px;
|
|
1932
|
+
color: var(--text-secondary);
|
|
1933
|
+
}
|
|
1850
1934
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1935
|
+
.empty-state {
|
|
1936
|
+
display: flex;
|
|
1937
|
+
align-items: center;
|
|
1938
|
+
justify-content: center;
|
|
1939
|
+
height: 100%;
|
|
1940
|
+
color: var(--text-secondary);
|
|
1941
|
+
font-size: 13px;
|
|
1942
|
+
}
|
|
1858
1943
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
font-weight: 700;
|
|
1867
|
-
border-radius: 3px;
|
|
1868
|
-
text-transform: uppercase;
|
|
1869
|
-
color: white;
|
|
1870
|
-
}
|
|
1944
|
+
/* Row 4: Handshake History */
|
|
1945
|
+
.handshake-table {
|
|
1946
|
+
background-color: var(--surface);
|
|
1947
|
+
border: 1px solid var(--border);
|
|
1948
|
+
border-radius: 8px;
|
|
1949
|
+
overflow: hidden;
|
|
1950
|
+
}
|
|
1871
1951
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1952
|
+
.table-header {
|
|
1953
|
+
display: grid;
|
|
1954
|
+
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr 1.5fr;
|
|
1955
|
+
gap: 16px;
|
|
1956
|
+
padding: 16px 20px;
|
|
1957
|
+
border-bottom: 1px solid var(--border);
|
|
1958
|
+
background-color: var(--bg);
|
|
1959
|
+
font-size: 12px;
|
|
1960
|
+
font-weight: 600;
|
|
1961
|
+
color: var(--text-secondary);
|
|
1962
|
+
text-transform: uppercase;
|
|
1963
|
+
letter-spacing: 0.5px;
|
|
1964
|
+
}
|
|
1875
1965
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1966
|
+
.table-rows {
|
|
1967
|
+
max-height: 300px;
|
|
1968
|
+
overflow-y: auto;
|
|
1969
|
+
}
|
|
1879
1970
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1971
|
+
.table-row {
|
|
1972
|
+
display: grid;
|
|
1973
|
+
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr 1.5fr;
|
|
1974
|
+
gap: 16px;
|
|
1975
|
+
padding: 14px 20px;
|
|
1976
|
+
border-bottom: 1px solid var(--border);
|
|
1977
|
+
align-items: center;
|
|
1978
|
+
font-size: 12px;
|
|
1979
|
+
cursor: pointer;
|
|
1980
|
+
transition: background-color 0.2s;
|
|
1981
|
+
}
|
|
1884
1982
|
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
gap: 6px;
|
|
1889
|
-
font-size: 11px;
|
|
1890
|
-
font-family: var(--mono);
|
|
1891
|
-
color: var(--text-secondary);
|
|
1892
|
-
}
|
|
1983
|
+
.table-row:hover {
|
|
1984
|
+
background-color: var(--bg);
|
|
1985
|
+
}
|
|
1893
1986
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
background: rgba(48, 54, 61, 0.8);
|
|
1898
|
-
border-radius: 2px;
|
|
1899
|
-
overflow: hidden;
|
|
1900
|
-
}
|
|
1987
|
+
.table-row:last-child {
|
|
1988
|
+
border-bottom: none;
|
|
1989
|
+
}
|
|
1901
1990
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1991
|
+
.table-cell {
|
|
1992
|
+
color: var(--text-secondary);
|
|
1993
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1994
|
+
}
|
|
1907
1995
|
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1996
|
+
.table-cell.strong {
|
|
1997
|
+
color: var(--text-primary);
|
|
1998
|
+
font-weight: 500;
|
|
1999
|
+
}
|
|
1911
2000
|
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2001
|
+
.table-empty {
|
|
2002
|
+
padding: 40px 20px;
|
|
2003
|
+
text-align: center;
|
|
2004
|
+
color: var(--text-secondary);
|
|
2005
|
+
font-size: 13px;
|
|
2006
|
+
}
|
|
1916
2007
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2008
|
+
/* Pending Overlay */
|
|
2009
|
+
.pending-overlay {
|
|
2010
|
+
position: fixed;
|
|
2011
|
+
top: 0;
|
|
2012
|
+
right: -400px;
|
|
2013
|
+
width: 400px;
|
|
2014
|
+
height: 100vh;
|
|
2015
|
+
background-color: var(--surface);
|
|
2016
|
+
border-left: 1px solid var(--border);
|
|
2017
|
+
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
|
|
2018
|
+
z-index: 200;
|
|
2019
|
+
transition: right 0.3s ease;
|
|
2020
|
+
display: flex;
|
|
2021
|
+
flex-direction: column;
|
|
2022
|
+
overflow-y: auto;
|
|
2023
|
+
}
|
|
1928
2024
|
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
}
|
|
2025
|
+
.pending-overlay.show {
|
|
2026
|
+
right: 0;
|
|
2027
|
+
}
|
|
1933
2028
|
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2029
|
+
.pending-header {
|
|
2030
|
+
padding: 16px 20px;
|
|
2031
|
+
border-bottom: 1px solid var(--border);
|
|
2032
|
+
font-weight: 600;
|
|
2033
|
+
color: var(--text-primary);
|
|
2034
|
+
}
|
|
1937
2035
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
2036
|
+
.pending-items {
|
|
2037
|
+
flex: 1;
|
|
2038
|
+
overflow-y: auto;
|
|
2039
|
+
padding: 16px;
|
|
2040
|
+
}
|
|
1942
2041
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2042
|
+
.pending-item {
|
|
2043
|
+
background-color: var(--bg);
|
|
2044
|
+
border: 1px solid var(--border);
|
|
2045
|
+
border-radius: 6px;
|
|
2046
|
+
padding: 16px;
|
|
2047
|
+
margin-bottom: 12px;
|
|
2048
|
+
}
|
|
1946
2049
|
|
|
1947
|
-
|
|
2050
|
+
.pending-title {
|
|
2051
|
+
font-weight: 600;
|
|
2052
|
+
color: var(--text-primary);
|
|
2053
|
+
margin-bottom: 8px;
|
|
2054
|
+
word-break: break-word;
|
|
2055
|
+
}
|
|
1948
2056
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
border-top: 1px solid var(--border);
|
|
1956
|
-
max-height: 240px;
|
|
1957
|
-
z-index: 500;
|
|
1958
|
-
display: flex;
|
|
1959
|
-
flex-direction: column;
|
|
1960
|
-
transition: max-height 0.3s ease-out;
|
|
1961
|
-
}
|
|
2057
|
+
.pending-countdown {
|
|
2058
|
+
font-size: 12px;
|
|
2059
|
+
color: var(--amber);
|
|
2060
|
+
margin-bottom: 12px;
|
|
2061
|
+
font-weight: 500;
|
|
2062
|
+
}
|
|
1962
2063
|
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2064
|
+
.pending-actions {
|
|
2065
|
+
display: flex;
|
|
2066
|
+
gap: 8px;
|
|
2067
|
+
}
|
|
1966
2068
|
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
color: var(--text-secondary);
|
|
1978
|
-
flex: 0 0 auto;
|
|
1979
|
-
}
|
|
2069
|
+
.pending-btn {
|
|
2070
|
+
flex: 1;
|
|
2071
|
+
padding: 8px 12px;
|
|
2072
|
+
border: none;
|
|
2073
|
+
border-radius: 4px;
|
|
2074
|
+
font-size: 12px;
|
|
2075
|
+
font-weight: 600;
|
|
2076
|
+
cursor: pointer;
|
|
2077
|
+
transition: background-color 0.2s;
|
|
2078
|
+
}
|
|
1980
2079
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2080
|
+
.pending-approve {
|
|
2081
|
+
background-color: var(--green);
|
|
2082
|
+
color: var(--bg);
|
|
2083
|
+
}
|
|
1984
2084
|
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2085
|
+
.pending-approve:hover {
|
|
2086
|
+
background-color: #3fa040;
|
|
2087
|
+
}
|
|
1988
2088
|
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
display: flex;
|
|
1994
|
-
flex-direction: column;
|
|
1995
|
-
gap: 10px;
|
|
1996
|
-
}
|
|
2089
|
+
.pending-deny {
|
|
2090
|
+
background-color: var(--red);
|
|
2091
|
+
color: var(--bg);
|
|
2092
|
+
}
|
|
1997
2093
|
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
border-left: 2px solid var(--red);
|
|
2002
|
-
border-radius: 4px;
|
|
2003
|
-
font-size: 11px;
|
|
2004
|
-
color: var(--text-secondary);
|
|
2005
|
-
}
|
|
2094
|
+
.pending-deny:hover {
|
|
2095
|
+
background-color: #e03c3c;
|
|
2096
|
+
}
|
|
2006
2097
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2098
|
+
/* Threat Panel */
|
|
2099
|
+
.threat-panel {
|
|
2100
|
+
background-color: var(--surface);
|
|
2101
|
+
border: 1px solid var(--border);
|
|
2102
|
+
border-radius: 8px;
|
|
2103
|
+
margin-top: 20px;
|
|
2104
|
+
overflow: hidden;
|
|
2105
|
+
}
|
|
2012
2106
|
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2107
|
+
.threat-header {
|
|
2108
|
+
padding: 16px 20px;
|
|
2109
|
+
border-bottom: 1px solid var(--border);
|
|
2110
|
+
display: flex;
|
|
2111
|
+
justify-content: space-between;
|
|
2112
|
+
align-items: center;
|
|
2113
|
+
cursor: pointer;
|
|
2114
|
+
user-select: none;
|
|
2115
|
+
}
|
|
2019
2116
|
|
|
2020
|
-
|
|
2117
|
+
.threat-title {
|
|
2118
|
+
font-weight: 600;
|
|
2119
|
+
color: var(--text-primary);
|
|
2120
|
+
}
|
|
2021
2121
|
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2122
|
+
.threat-toggle {
|
|
2123
|
+
font-size: 10px;
|
|
2124
|
+
color: var(--text-secondary);
|
|
2125
|
+
transition: transform 0.2s;
|
|
2126
|
+
}
|
|
2025
2127
|
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2128
|
+
.threat-panel.collapsed .threat-toggle {
|
|
2129
|
+
transform: rotate(-90deg);
|
|
2130
|
+
}
|
|
2029
2131
|
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2132
|
+
.threat-content {
|
|
2133
|
+
padding: 16px 20px;
|
|
2134
|
+
max-height: 300px;
|
|
2135
|
+
overflow-y: auto;
|
|
2136
|
+
}
|
|
2034
2137
|
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2138
|
+
.threat-panel.collapsed .threat-content {
|
|
2139
|
+
display: none;
|
|
2140
|
+
}
|
|
2038
2141
|
|
|
2039
|
-
|
|
2142
|
+
.threat-alert {
|
|
2143
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
2144
|
+
border: 1px solid var(--red);
|
|
2145
|
+
border-radius: 4px;
|
|
2146
|
+
padding: 12px;
|
|
2147
|
+
margin-bottom: 8px;
|
|
2148
|
+
font-size: 12px;
|
|
2149
|
+
}
|
|
2040
2150
|
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
display: none;
|
|
2151
|
+
.threat-alert:last-child {
|
|
2152
|
+
margin-bottom: 0;
|
|
2044
2153
|
}
|
|
2045
2154
|
|
|
2046
|
-
.
|
|
2047
|
-
|
|
2155
|
+
.threat-type {
|
|
2156
|
+
font-weight: 600;
|
|
2157
|
+
color: var(--red);
|
|
2158
|
+
margin-bottom: 4px;
|
|
2159
|
+
text-transform: uppercase;
|
|
2160
|
+
font-size: 10px;
|
|
2161
|
+
letter-spacing: 0.5px;
|
|
2048
2162
|
}
|
|
2049
|
-
}
|
|
2050
2163
|
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
padding: 0 12px;
|
|
2054
|
-
gap: 12px;
|
|
2055
|
-
height: 48px;
|
|
2164
|
+
.threat-message {
|
|
2165
|
+
color: var(--text-secondary);
|
|
2056
2166
|
}
|
|
2057
2167
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2168
|
+
/* Scrollbar */
|
|
2169
|
+
::-webkit-scrollbar {
|
|
2170
|
+
width: 8px;
|
|
2060
2171
|
}
|
|
2061
2172
|
|
|
2062
|
-
|
|
2063
|
-
|
|
2173
|
+
::-webkit-scrollbar-track {
|
|
2174
|
+
background-color: transparent;
|
|
2064
2175
|
}
|
|
2065
2176
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2177
|
+
::-webkit-scrollbar-thumb {
|
|
2178
|
+
background-color: var(--border);
|
|
2179
|
+
border-radius: 4px;
|
|
2068
2180
|
}
|
|
2069
2181
|
|
|
2070
|
-
|
|
2071
|
-
|
|
2182
|
+
::-webkit-scrollbar-thumb:hover {
|
|
2183
|
+
background-color: var(--text-secondary);
|
|
2072
2184
|
}
|
|
2073
2185
|
|
|
2074
|
-
|
|
2075
|
-
|
|
2186
|
+
/* Responsive */
|
|
2187
|
+
@media (max-width: 1400px) {
|
|
2188
|
+
.sovereignty-layers {
|
|
2189
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
.main-panels {
|
|
2193
|
+
grid-template-columns: 1fr;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
.pending-overlay {
|
|
2197
|
+
width: 100%;
|
|
2198
|
+
right: -100%;
|
|
2199
|
+
}
|
|
2076
2200
|
}
|
|
2077
2201
|
|
|
2078
|
-
|
|
2079
|
-
|
|
2202
|
+
@media (max-width: 768px) {
|
|
2203
|
+
.status-bar {
|
|
2204
|
+
flex-wrap: wrap;
|
|
2205
|
+
height: auto;
|
|
2206
|
+
padding: 12px;
|
|
2207
|
+
gap: 12px;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
.status-bar-center {
|
|
2211
|
+
order: 3;
|
|
2212
|
+
flex-basis: 100%;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
.main-content {
|
|
2216
|
+
margin-top: auto;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
.info-cards {
|
|
2220
|
+
grid-template-columns: 1fr;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
.table-header,
|
|
2224
|
+
.table-row {
|
|
2225
|
+
grid-template-columns: 1fr;
|
|
2226
|
+
}
|
|
2080
2227
|
}
|
|
2081
|
-
|
|
2082
|
-
</style>
|
|
2228
|
+
</style>
|
|
2083
2229
|
</head>
|
|
2084
2230
|
<body>
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
<div class="status-bar">
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
<div class="sovereignty-badge">
|
|
2094
|
-
<div class="sovereignty-score" id="sovereigntyScore">85</div>
|
|
2095
|
-
<span>Sovereignty Health</span>
|
|
2096
|
-
</div>
|
|
2097
|
-
</div>
|
|
2098
|
-
<div class="status-bar-right">
|
|
2099
|
-
<div class="protections-indicator">
|
|
2100
|
-
<span class="count" id="activeProtections">6</span>/6 protections
|
|
2101
|
-
</div>
|
|
2102
|
-
<div class="uptime">
|
|
2103
|
-
<span id="uptimeText">\u2014</span>
|
|
2231
|
+
<!-- Status Bar -->
|
|
2232
|
+
<div class="status-bar">
|
|
2233
|
+
<div class="status-bar-left">
|
|
2234
|
+
<div class="logo-icon">\u25C6</div>
|
|
2235
|
+
<div class="logo-info">
|
|
2236
|
+
<div class="logo-title">SANCTUARY</div>
|
|
2237
|
+
<div class="logo-version">v${options.serverVersion}</div>
|
|
2238
|
+
</div>
|
|
2104
2239
|
</div>
|
|
2105
|
-
|
|
2106
|
-
<div class="
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
<!-- Activity Feed -->
|
|
2113
|
-
<div class="activity-feed">
|
|
2114
|
-
<div class="feed-header">
|
|
2115
|
-
<div class="feed-header-dot"></div>
|
|
2116
|
-
Live Activity
|
|
2240
|
+
|
|
2241
|
+
<div class="status-bar-center">
|
|
2242
|
+
<div id="sovereignty-badge" class="sovereignty-badge">
|
|
2243
|
+
<span>Sovereignty Health:</span>
|
|
2244
|
+
<span class="sovereignty-score" id="sovereignty-score">\u2014</span>
|
|
2245
|
+
<span>/ 100</span>
|
|
2246
|
+
</div>
|
|
2117
2247
|
</div>
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
<
|
|
2248
|
+
|
|
2249
|
+
<div class="status-bar-right">
|
|
2250
|
+
<div class="status-item">
|
|
2251
|
+
<strong id="protections-count">\u2014</strong>
|
|
2252
|
+
<span>Protections</span>
|
|
2253
|
+
</div>
|
|
2254
|
+
<div class="status-item">
|
|
2255
|
+
<strong id="uptime-value">\u2014</strong>
|
|
2256
|
+
<span>Uptime</span>
|
|
2257
|
+
</div>
|
|
2258
|
+
<div class="status-dot" id="connection-status"></div>
|
|
2259
|
+
<div id="pending-item-badge" class="pending-badge" style="display: none;">
|
|
2260
|
+
<span>\u23F3</span>
|
|
2261
|
+
<span id="pending-count">0</span>
|
|
2122
2262
|
</div>
|
|
2123
2263
|
</div>
|
|
2124
2264
|
</div>
|
|
2125
2265
|
|
|
2126
|
-
<!--
|
|
2127
|
-
<div class="
|
|
2128
|
-
<div class="
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
<div class="
|
|
2266
|
+
<!-- Main Content -->
|
|
2267
|
+
<div class="main-content">
|
|
2268
|
+
<div class="grid">
|
|
2269
|
+
<!-- Row 1: Sovereignty Layers -->
|
|
2270
|
+
<div class="sovereignty-layers" id="sovereignty-layers">
|
|
2271
|
+
<div class="layer-card" data-layer="l1">
|
|
2272
|
+
<div class="layer-name">Layer 1</div>
|
|
2273
|
+
<div class="layer-title">Cognitive Sovereignty</div>
|
|
2274
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l1-status">\u2014</span></div>
|
|
2275
|
+
<div class="layer-detail" id="l1-detail">Loading...</div>
|
|
2276
|
+
</div>
|
|
2277
|
+
<div class="layer-card" data-layer="l2">
|
|
2278
|
+
<div class="layer-name">Layer 2</div>
|
|
2279
|
+
<div class="layer-title">Operational Isolation</div>
|
|
2280
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l2-status">\u2014</span></div>
|
|
2281
|
+
<div class="layer-detail" id="l2-detail">Loading...</div>
|
|
2282
|
+
</div>
|
|
2283
|
+
<div class="layer-card" data-layer="l3">
|
|
2284
|
+
<div class="layer-name">Layer 3</div>
|
|
2285
|
+
<div class="layer-title">Selective Disclosure</div>
|
|
2286
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l3-status">\u2014</span></div>
|
|
2287
|
+
<div class="layer-detail" id="l3-detail">Loading...</div>
|
|
2288
|
+
</div>
|
|
2289
|
+
<div class="layer-card" data-layer="l4">
|
|
2290
|
+
<div class="layer-name">Layer 4</div>
|
|
2291
|
+
<div class="layer-title">Verifiable Reputation</div>
|
|
2292
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l4-status">\u2014</span></div>
|
|
2293
|
+
<div class="layer-detail" id="l4-detail">Loading...</div>
|
|
2294
|
+
</div>
|
|
2144
2295
|
</div>
|
|
2145
2296
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
<div class="
|
|
2149
|
-
|
|
2150
|
-
|
|
2297
|
+
<!-- Row 2: Info Cards -->
|
|
2298
|
+
<div class="info-cards">
|
|
2299
|
+
<div class="info-card">
|
|
2300
|
+
<div class="card-header">Identity</div>
|
|
2301
|
+
<div class="card-row">
|
|
2302
|
+
<span class="card-label">Primary</span>
|
|
2303
|
+
<span class="card-value" id="identity-label">\u2014</span>
|
|
2304
|
+
</div>
|
|
2305
|
+
<div class="card-row">
|
|
2306
|
+
<span class="card-label">DID</span>
|
|
2307
|
+
<span class="card-value truncated" id="identity-did" title="">\u2014</span>
|
|
2308
|
+
</div>
|
|
2309
|
+
<div class="card-row">
|
|
2310
|
+
<span class="card-label">Public Key</span>
|
|
2311
|
+
<span class="card-value truncated" id="identity-pubkey" title="">\u2014</span>
|
|
2312
|
+
</div>
|
|
2313
|
+
<div class="card-row">
|
|
2314
|
+
<span class="card-label">Type</span>
|
|
2315
|
+
<span class="identity-badge">Ed25519</span>
|
|
2316
|
+
</div>
|
|
2317
|
+
<div class="card-row">
|
|
2318
|
+
<span class="card-label">Created</span>
|
|
2319
|
+
<span class="card-value" id="identity-created">\u2014</span>
|
|
2320
|
+
</div>
|
|
2321
|
+
<div class="card-row">
|
|
2322
|
+
<span class="card-label">Identities</span>
|
|
2323
|
+
<span class="card-value" id="identity-count">\u2014</span>
|
|
2324
|
+
</div>
|
|
2325
|
+
</div>
|
|
2326
|
+
|
|
2327
|
+
<div class="info-card">
|
|
2328
|
+
<div class="card-header">Handshakes</div>
|
|
2329
|
+
<div class="card-row">
|
|
2330
|
+
<span class="card-label">Total</span>
|
|
2331
|
+
<span class="card-value" id="handshake-count">\u2014</span>
|
|
2332
|
+
</div>
|
|
2333
|
+
<div class="card-row">
|
|
2334
|
+
<span class="card-label">Latest Peer</span>
|
|
2335
|
+
<span class="card-value truncated" id="handshake-latest">\u2014</span>
|
|
2336
|
+
</div>
|
|
2337
|
+
<div class="card-row">
|
|
2338
|
+
<span class="card-label">Trust Tier</span>
|
|
2339
|
+
<span class="trust-tier-badge" id="handshake-tier">Unverified</span>
|
|
2340
|
+
</div>
|
|
2341
|
+
<div class="card-row">
|
|
2342
|
+
<span class="card-label">Timestamp</span>
|
|
2343
|
+
<span class="card-value" id="handshake-time">\u2014</span>
|
|
2344
|
+
</div>
|
|
2345
|
+
</div>
|
|
2346
|
+
|
|
2347
|
+
<div class="info-card">
|
|
2348
|
+
<div class="card-header">Reputation</div>
|
|
2349
|
+
<div class="card-row">
|
|
2350
|
+
<span class="card-label">Weighted Score</span>
|
|
2351
|
+
<span class="card-value" id="reputation-score">\u2014</span>
|
|
2352
|
+
</div>
|
|
2353
|
+
<div class="card-row">
|
|
2354
|
+
<span class="card-label">Attestations</span>
|
|
2355
|
+
<span class="card-value" id="reputation-attestations">\u2014</span>
|
|
2356
|
+
</div>
|
|
2357
|
+
<div class="card-row">
|
|
2358
|
+
<span class="card-label">Verified Sovereign</span>
|
|
2359
|
+
<span class="card-value" id="reputation-verified">\u2014</span>
|
|
2360
|
+
</div>
|
|
2361
|
+
<div class="card-row">
|
|
2362
|
+
<span class="card-label">Verified Degraded</span>
|
|
2363
|
+
<span class="card-value" id="reputation-degraded">\u2014</span>
|
|
2364
|
+
</div>
|
|
2365
|
+
<div class="card-row">
|
|
2366
|
+
<span class="card-label">Unverified</span>
|
|
2367
|
+
<span class="card-value" id="reputation-unverified">\u2014</span>
|
|
2368
|
+
</div>
|
|
2369
|
+
</div>
|
|
2151
2370
|
</div>
|
|
2152
2371
|
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
<div class="
|
|
2156
|
-
|
|
2157
|
-
|
|
2372
|
+
<!-- Row 3: SHR & Activity -->
|
|
2373
|
+
<div class="main-panels">
|
|
2374
|
+
<div class="panel">
|
|
2375
|
+
<div class="panel-header">
|
|
2376
|
+
<div class="panel-title">Sovereignty Health Report</div>
|
|
2377
|
+
<button class="panel-action" id="copy-shr-btn">Copy JSON</button>
|
|
2378
|
+
</div>
|
|
2379
|
+
<div class="panel-content">
|
|
2380
|
+
<div class="shr-json" id="shr-viewer">
|
|
2381
|
+
<div class="empty-state">Loading SHR...</div>
|
|
2382
|
+
</div>
|
|
2383
|
+
</div>
|
|
2384
|
+
</div>
|
|
2385
|
+
|
|
2386
|
+
<div class="panel">
|
|
2387
|
+
<div class="panel-header">
|
|
2388
|
+
<div class="panel-title">Activity Feed</div>
|
|
2389
|
+
</div>
|
|
2390
|
+
<div class="panel-content">
|
|
2391
|
+
<div id="activity-feed" class="activity-feed">
|
|
2392
|
+
<div class="empty-state">Waiting for activity...</div>
|
|
2393
|
+
</div>
|
|
2394
|
+
</div>
|
|
2395
|
+
</div>
|
|
2158
2396
|
</div>
|
|
2159
2397
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
<div class="
|
|
2163
|
-
|
|
2164
|
-
|
|
2398
|
+
<!-- Row 4: Handshake History -->
|
|
2399
|
+
<div class="handshake-table">
|
|
2400
|
+
<div class="table-header">
|
|
2401
|
+
<div>Counterparty</div>
|
|
2402
|
+
<div>Trust Tier</div>
|
|
2403
|
+
<div>Sovereignty</div>
|
|
2404
|
+
<div>Verified</div>
|
|
2405
|
+
<div>Completed</div>
|
|
2406
|
+
<div>Expires</div>
|
|
2407
|
+
</div>
|
|
2408
|
+
<div class="table-rows" id="handshake-table">
|
|
2409
|
+
<div class="table-empty">No handshakes completed yet</div>
|
|
2410
|
+
</div>
|
|
2165
2411
|
</div>
|
|
2166
2412
|
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
<div class="
|
|
2170
|
-
|
|
2171
|
-
|
|
2413
|
+
<!-- Threat Panel -->
|
|
2414
|
+
<div class="threat-panel collapsed">
|
|
2415
|
+
<div class="threat-header">
|
|
2416
|
+
<div class="threat-title">Security Threats</div>
|
|
2417
|
+
<div class="threat-toggle">\u25B6</div>
|
|
2418
|
+
</div>
|
|
2419
|
+
<div class="threat-content" id="threat-alerts">
|
|
2420
|
+
<div class="empty-state">No threats detected</div>
|
|
2421
|
+
</div>
|
|
2172
2422
|
</div>
|
|
2173
2423
|
</div>
|
|
2174
2424
|
</div>
|
|
2175
|
-
</div>
|
|
2176
2425
|
|
|
2177
|
-
<!-- Pending
|
|
2178
|
-
<div class="pending-overlay" id="
|
|
2179
|
-
|
|
2180
|
-
<div class="pending-
|
|
2181
|
-
<button class="pending-overlay-close" onclick="closePendingOverlay()">\xD7</button>
|
|
2182
|
-
</div>
|
|
2183
|
-
<div class="pending-list" id="pendingList"></div>
|
|
2184
|
-
</div>
|
|
2185
|
-
|
|
2186
|
-
<!-- Threat Panel (collapsible footer) -->
|
|
2187
|
-
<div class="threat-panel collapsed" id="threatPanel">
|
|
2188
|
-
<div class="threat-header" onclick="toggleThreatPanel()">
|
|
2189
|
-
<span class="threat-icon">\u26A0</span>
|
|
2190
|
-
Recent Threats
|
|
2191
|
-
<span id="threatCount" style="margin-left: auto; color: var(--red); font-weight: 700;">0</span>
|
|
2426
|
+
<!-- Pending Overlay -->
|
|
2427
|
+
<div class="pending-overlay" id="pending-overlay">
|
|
2428
|
+
<div class="pending-header">Pending Approvals</div>
|
|
2429
|
+
<div class="pending-items" id="pending-items"></div>
|
|
2192
2430
|
</div>
|
|
2193
|
-
<div class="threat-content" id="threatContent">
|
|
2194
|
-
<div class="threat-empty">No threats detected</div>
|
|
2195
|
-
</div>
|
|
2196
|
-
</div>
|
|
2197
2431
|
|
|
2198
|
-
<script>
|
|
2199
|
-
|
|
2200
|
-
|
|
2432
|
+
<script>
|
|
2433
|
+
// Constants
|
|
2434
|
+
const AUTH_TOKEN = '${options.authToken || ""}' || sessionStorage.getItem('authToken') || '';
|
|
2435
|
+
const TIMEOUT_SECONDS = ${options.timeoutSeconds};
|
|
2436
|
+
const API_BASE = '';
|
|
2437
|
+
|
|
2438
|
+
// State
|
|
2439
|
+
let apiState = {
|
|
2440
|
+
sovereignty: null,
|
|
2441
|
+
identity: null,
|
|
2442
|
+
handshakes: [],
|
|
2443
|
+
shr: null,
|
|
2444
|
+
status: null,
|
|
2445
|
+
};
|
|
2201
2446
|
|
|
2202
|
-
|
|
2447
|
+
let pendingRequests = new Map();
|
|
2448
|
+
let activityLog = [];
|
|
2449
|
+
const maxActivityItems = 50;
|
|
2203
2450
|
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2451
|
+
// Helpers
|
|
2452
|
+
function esc(text) {
|
|
2453
|
+
if (!text) return '';
|
|
2454
|
+
const div = document.createElement('div');
|
|
2455
|
+
div.textContent = text;
|
|
2456
|
+
return div.innerHTML;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
function formatTime(isoString) {
|
|
2460
|
+
if (!isoString) return '\u2014';
|
|
2461
|
+
const date = new Date(isoString);
|
|
2462
|
+
return date.toLocaleString('en-US', {
|
|
2463
|
+
month: 'short',
|
|
2464
|
+
day: 'numeric',
|
|
2465
|
+
hour: '2-digit',
|
|
2466
|
+
minute: '2-digit',
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2210
2469
|
|
|
2211
|
-
|
|
2470
|
+
function truncate(str, len = 16) {
|
|
2471
|
+
if (!str) return '\u2014';
|
|
2472
|
+
if (str.length <= len) return str;
|
|
2473
|
+
return str.slice(0, len) + '...';
|
|
2474
|
+
}
|
|
2212
2475
|
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
let threatCount = 0;
|
|
2218
|
-
const pendingRequests = new Map();
|
|
2219
|
-
const activityItems = [];
|
|
2220
|
-
const threatItems = [];
|
|
2221
|
-
let sovereigntyScore = 85;
|
|
2222
|
-
let sessionRenewalTimer = null;
|
|
2476
|
+
function calculateSovereigntyScore(shr) {
|
|
2477
|
+
if (!shr || !shr.layers) return 0;
|
|
2478
|
+
const layers = shr.layers;
|
|
2479
|
+
let score = 100;
|
|
2223
2480
|
|
|
2224
|
-
|
|
2481
|
+
if (layers.l1?.status === 'degraded') score -= 20;
|
|
2482
|
+
if (layers.l1?.status === 'inactive') score -= 35;
|
|
2483
|
+
if (layers.l2?.status === 'degraded') score -= 15;
|
|
2484
|
+
if (layers.l2?.status === 'inactive') score -= 25;
|
|
2485
|
+
if (layers.l3?.status === 'degraded') score -= 15;
|
|
2486
|
+
if (layers.l3?.status === 'inactive') score -= 25;
|
|
2487
|
+
if (layers.l4?.status === 'degraded') score -= 10;
|
|
2488
|
+
if (layers.l4?.status === 'inactive') score -= 20;
|
|
2225
2489
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
if (AUTH_TOKEN) h['Authorization'] = 'Bearer ' + AUTH_TOKEN;
|
|
2229
|
-
return h;
|
|
2230
|
-
}
|
|
2490
|
+
return Math.max(0, Math.min(100, score));
|
|
2491
|
+
}
|
|
2231
2492
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2493
|
+
async function fetchAPI(endpoint) {
|
|
2494
|
+
try {
|
|
2495
|
+
const response = await fetch(API_BASE + endpoint, {
|
|
2496
|
+
headers: {
|
|
2497
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
2498
|
+
},
|
|
2499
|
+
});
|
|
2237
2500
|
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2501
|
+
if (response.status === 401) {
|
|
2502
|
+
redirectToLogin();
|
|
2503
|
+
return null;
|
|
2504
|
+
}
|
|
2242
2505
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
// Schedule renewal at 80% of TTL
|
|
2254
|
-
if (sessionRenewalTimer) clearTimeout(sessionRenewalTimer);
|
|
2255
|
-
sessionRenewalTimer = setTimeout(function() {
|
|
2256
|
-
exchangeSession().then(function() { reconnectSSE(); });
|
|
2257
|
-
}, ttl * 800);
|
|
2258
|
-
} else if (resp.status === 401) {
|
|
2259
|
-
// Token invalid or expired \u2014 show non-destructive re-login overlay
|
|
2260
|
-
showSessionExpired();
|
|
2261
|
-
}
|
|
2262
|
-
} catch (e) {
|
|
2263
|
-
// Network error \u2014 retry in 30s
|
|
2264
|
-
if (sessionRenewalTimer) clearTimeout(sessionRenewalTimer);
|
|
2265
|
-
sessionRenewalTimer = setTimeout(function() {
|
|
2266
|
-
exchangeSession().then(function() { reconnectSSE(); });
|
|
2267
|
-
}, 30000);
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
function showSessionExpired() {
|
|
2272
|
-
// Clear stored token
|
|
2273
|
-
try { sessionStorage.removeItem('sanctuary_token'); } catch(_) {}
|
|
2274
|
-
// Redirect to login page
|
|
2275
|
-
document.cookie = 'sanctuary_session=; path=/; max-age=0';
|
|
2276
|
-
window.location.reload();
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
// \u2500\u2500 UI Utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2280
|
-
|
|
2281
|
-
function esc(s) {
|
|
2282
|
-
const d = document.createElement('div');
|
|
2283
|
-
d.textContent = String(s || '');
|
|
2284
|
-
return d.innerHTML;
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
|
-
function closePendingOverlay() {
|
|
2288
|
-
document.getElementById('pendingOverlay').classList.remove('active');
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
function toggleThreatPanel() {
|
|
2292
|
-
document.getElementById('threatPanel').classList.toggle('collapsed');
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
function updateUptime() {
|
|
2296
|
-
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
2297
|
-
const hours = Math.floor(elapsed / 3600);
|
|
2298
|
-
const mins = Math.floor((elapsed % 3600) / 60);
|
|
2299
|
-
const secs = elapsed % 60;
|
|
2300
|
-
let uptimeStr = '';
|
|
2301
|
-
if (hours > 0) uptimeStr += hours + 'h ';
|
|
2302
|
-
if (mins > 0) uptimeStr += mins + 'm ';
|
|
2303
|
-
uptimeStr += secs + 's';
|
|
2304
|
-
document.getElementById('uptimeText').textContent = uptimeStr;
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
// \u2500\u2500 Sovereignty Score \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2308
|
-
|
|
2309
|
-
function updateSovereigntyScore(score) {
|
|
2310
|
-
sovereigntyScore = Math.min(100, Math.max(0, score || 85));
|
|
2311
|
-
const badge = document.getElementById('sovereigntyScore');
|
|
2312
|
-
badge.textContent = sovereigntyScore;
|
|
2313
|
-
badge.className = 'sovereignty-score';
|
|
2314
|
-
if (sovereigntyScore >= 80) {
|
|
2315
|
-
badge.classList.add('high');
|
|
2316
|
-
} else if (sovereigntyScore >= 50) {
|
|
2317
|
-
badge.classList.add('medium');
|
|
2318
|
-
} else {
|
|
2319
|
-
badge.classList.add('low');
|
|
2506
|
+
if (!response.ok) {
|
|
2507
|
+
console.error('API Error:', response.status);
|
|
2508
|
+
return null;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
return await response.json();
|
|
2512
|
+
} catch (err) {
|
|
2513
|
+
console.error('Fetch error:', err);
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2320
2516
|
}
|
|
2321
|
-
}
|
|
2322
2517
|
|
|
2323
|
-
|
|
2518
|
+
function redirectToLogin() {
|
|
2519
|
+
sessionStorage.removeItem('authToken');
|
|
2520
|
+
window.location.href = '/';
|
|
2521
|
+
}
|
|
2324
2522
|
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
tool,
|
|
2330
|
-
outcome,
|
|
2331
|
-
detail,
|
|
2332
|
-
hasInjection,
|
|
2333
|
-
isContextGated
|
|
2334
|
-
} = data;
|
|
2335
|
-
|
|
2336
|
-
const item = {
|
|
2337
|
-
id: 'activity-' + activityCount++,
|
|
2338
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
2339
|
-
tier: tier || 1,
|
|
2340
|
-
tool: tool || 'unknown_tool',
|
|
2341
|
-
outcome: outcome || 'executed',
|
|
2342
|
-
detail: detail || '',
|
|
2343
|
-
hasInjection: !!hasInjection,
|
|
2344
|
-
isContextGated: !!isContextGated
|
|
2345
|
-
};
|
|
2523
|
+
// API Updates
|
|
2524
|
+
async function updateSovereignty() {
|
|
2525
|
+
const data = await fetchAPI('/api/sovereignty');
|
|
2526
|
+
if (!data) return;
|
|
2346
2527
|
|
|
2347
|
-
|
|
2348
|
-
if (activityItems.length > MAX_ACTIVITY_ITEMS) {
|
|
2349
|
-
activityItems.pop();
|
|
2350
|
-
}
|
|
2528
|
+
apiState.sovereignty = data;
|
|
2351
2529
|
|
|
2352
|
-
|
|
2353
|
-
|
|
2530
|
+
const score = calculateSovereigntyScore(data.shr);
|
|
2531
|
+
const badge = document.getElementById('sovereignty-badge');
|
|
2532
|
+
const scoreEl = document.getElementById('sovereignty-score');
|
|
2354
2533
|
|
|
2355
|
-
|
|
2356
|
-
const list = document.getElementById('activityList');
|
|
2534
|
+
scoreEl.textContent = score;
|
|
2357
2535
|
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2536
|
+
badge.classList.remove('degraded', 'inactive');
|
|
2537
|
+
if (score < 70) badge.classList.add('degraded');
|
|
2538
|
+
if (score < 40) badge.classList.add('inactive');
|
|
2539
|
+
|
|
2540
|
+
updateLayerCards(data.shr);
|
|
2361
2541
|
}
|
|
2362
2542
|
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
const tr = document.createElement('div');
|
|
2366
|
-
tr.className = 'activity-item';
|
|
2367
|
-
tr.id = item.id;
|
|
2368
|
-
|
|
2369
|
-
const time = new Date(item.timestamp);
|
|
2370
|
-
const timeStr = time.toLocaleTimeString();
|
|
2371
|
-
|
|
2372
|
-
const tierClass = 't' + item.tier;
|
|
2373
|
-
const outcomeClass = item.outcome === 'denied' ? 'outcome denied' : 'outcome';
|
|
2374
|
-
|
|
2375
|
-
let icon = '\u25CF';
|
|
2376
|
-
if (item.isContextGated) icon = '\u{1F3AF}';
|
|
2377
|
-
else if (item.hasInjection) icon = '\u26A0';
|
|
2378
|
-
else if (item.outcome === 'denied') icon = '\u2717';
|
|
2379
|
-
else icon = '\u2713';
|
|
2380
|
-
|
|
2381
|
-
tr.innerHTML =
|
|
2382
|
-
'<div class="activity-item-icon">' + esc(icon) + '</div>' +
|
|
2383
|
-
'<div class="activity-item-content">' +
|
|
2384
|
-
'<div class="activity-time">' + esc(timeStr) + '</div>' +
|
|
2385
|
-
'<div class="activity-main">' +
|
|
2386
|
-
'<span class="activity-tier ' + tierClass + '">T' + item.tier + '</span>' +
|
|
2387
|
-
'<span class="activity-tool">' + esc(item.tool) + '</span>' +
|
|
2388
|
-
'<span class="activity-outcome ' + (outcomeClass === 'outcome denied' ? 'denied' : '') + '">' + (item.outcome === 'denied' ? '\u2717 denied' : '\u2713 allowed') + '</span>' +
|
|
2389
|
-
'</div>' +
|
|
2390
|
-
'<div class="activity-detail">' + esc(item.detail) + '</div>' +
|
|
2391
|
-
'</div>' +
|
|
2392
|
-
'';
|
|
2393
|
-
|
|
2394
|
-
tr.addEventListener('click', () => {
|
|
2395
|
-
tr.classList.toggle('expanded');
|
|
2396
|
-
});
|
|
2543
|
+
function updateLayerCards(shr) {
|
|
2544
|
+
if (!shr || !shr.layers) return;
|
|
2397
2545
|
|
|
2398
|
-
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2546
|
+
const layers = shr.layers;
|
|
2401
2547
|
|
|
2402
|
-
|
|
2548
|
+
updateLayerCard('l1', layers.l1, layers.l1?.encryption || 'AES-256-GCM');
|
|
2549
|
+
updateLayerCard('l2', layers.l2, layers.l2?.isolation_type || 'Process-level');
|
|
2550
|
+
updateLayerCard('l3', layers.l3, layers.l3?.proof_system || 'Schnorr-Pedersen');
|
|
2551
|
+
updateLayerCard('l4', layers.l4, layers.l4?.reputation_mode || 'Weighted');
|
|
2552
|
+
}
|
|
2403
2553
|
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
request_id,
|
|
2407
|
-
operation,
|
|
2408
|
-
tier,
|
|
2409
|
-
reason,
|
|
2410
|
-
context,
|
|
2411
|
-
timestamp
|
|
2412
|
-
} = data;
|
|
2413
|
-
|
|
2414
|
-
const pending = {
|
|
2415
|
-
id: request_id,
|
|
2416
|
-
operation: operation || 'unknown',
|
|
2417
|
-
tier: tier || 1,
|
|
2418
|
-
reason: reason || '',
|
|
2419
|
-
context: context || {},
|
|
2420
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
2421
|
-
remaining: TIMEOUT_SECONDS
|
|
2422
|
-
};
|
|
2554
|
+
function updateLayerCard(layer, layerData, detail) {
|
|
2555
|
+
if (!layerData) return;
|
|
2423
2556
|
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
}
|
|
2557
|
+
const card = document.querySelector(\`[data-layer="\${layer}"]\`);
|
|
2558
|
+
if (!card) return;
|
|
2427
2559
|
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
updatePendingUI();
|
|
2431
|
-
}
|
|
2560
|
+
const status = layerData.status || 'inactive';
|
|
2561
|
+
card.classList.remove('degraded', 'inactive');
|
|
2432
2562
|
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2563
|
+
if (status === 'degraded') {
|
|
2564
|
+
card.classList.add('degraded');
|
|
2565
|
+
} else if (status === 'inactive') {
|
|
2566
|
+
card.classList.add('inactive');
|
|
2567
|
+
}
|
|
2436
2568
|
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
badge.textContent = count;
|
|
2440
|
-
document.getElementById('pendingOverlay').classList.add('active');
|
|
2441
|
-
} else {
|
|
2442
|
-
badge.classList.add('hidden');
|
|
2443
|
-
document.getElementById('pendingOverlay').classList.remove('active');
|
|
2569
|
+
document.getElementById(\`\${layer}-status\`).textContent = status.toUpperCase();
|
|
2570
|
+
document.getElementById(\`\${layer}-detail\`).textContent = detail;
|
|
2444
2571
|
}
|
|
2445
2572
|
|
|
2446
|
-
|
|
2447
|
-
|
|
2573
|
+
async function updateIdentity() {
|
|
2574
|
+
const data = await fetchAPI('/api/identity');
|
|
2575
|
+
if (!data) return;
|
|
2448
2576
|
|
|
2449
|
-
|
|
2450
|
-
const list = document.getElementById('pendingList');
|
|
2451
|
-
list.innerHTML = '';
|
|
2577
|
+
apiState.identity = data;
|
|
2452
2578
|
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2579
|
+
const primary = data.primary || {};
|
|
2580
|
+
document.getElementById('identity-label').textContent = primary.label || '\u2014';
|
|
2581
|
+
document.getElementById('identity-did').textContent = truncate(primary.did, 24);
|
|
2582
|
+
document.getElementById('identity-did').title = primary.did || '';
|
|
2583
|
+
document.getElementById('identity-pubkey').textContent = truncate(primary.publicKey, 24);
|
|
2584
|
+
document.getElementById('identity-pubkey').title = primary.publicKey || '';
|
|
2585
|
+
document.getElementById('identity-created').textContent = formatTime(primary.createdAt);
|
|
2586
|
+
document.getElementById('identity-count').textContent = data.identities?.length || '\u2014';
|
|
2587
|
+
}
|
|
2456
2588
|
|
|
2457
|
-
|
|
2458
|
-
const
|
|
2459
|
-
|
|
2460
|
-
const isUrgent = req.remaining <= 30;
|
|
2589
|
+
async function updateHandshakes() {
|
|
2590
|
+
const data = await fetchAPI('/api/handshakes');
|
|
2591
|
+
if (!data) return;
|
|
2461
2592
|
|
|
2462
|
-
|
|
2463
|
-
'<div class="pending-item-header">' +
|
|
2464
|
-
'<div class="pending-item-op">' + esc(req.operation) + '</div>' +
|
|
2465
|
-
'<div class="pending-item-tier ' + tierClass + '">T' + tier + '</div>' +
|
|
2466
|
-
'</div>' +
|
|
2467
|
-
'<div class="pending-item-reason">' + esc(req.reason) + '</div>' +
|
|
2468
|
-
'<div class="pending-item-timer ' + (isUrgent ? 'urgent' : '') + '">' +
|
|
2469
|
-
'<div class="pending-item-timer-bar">' +
|
|
2470
|
-
'<div class="pending-item-timer-fill" style="width: ' + pct + '%"></div>' +
|
|
2471
|
-
'</div>' +
|
|
2472
|
-
'<span id="timer-' + id + '">' + req.remaining + 's</span>' +
|
|
2473
|
-
'</div>' +
|
|
2474
|
-
'<div class="pending-item-actions">' +
|
|
2475
|
-
'<button class="btn btn-approve" onclick="handleApprove('' + id + '')">Approve</button>' +
|
|
2476
|
-
'<button class="btn btn-deny" onclick="handleDeny('' + id + '')">Deny</button>' +
|
|
2477
|
-
'</div>' +
|
|
2478
|
-
'';
|
|
2593
|
+
apiState.handshakes = data.handshakes || [];
|
|
2479
2594
|
|
|
2480
|
-
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2595
|
+
document.getElementById('handshake-count').textContent = data.handshakes?.length || '0';
|
|
2483
2596
|
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2597
|
+
if (data.handshakes && data.handshakes.length > 0) {
|
|
2598
|
+
const latest = data.handshakes[0];
|
|
2599
|
+
document.getElementById('handshake-latest').textContent = truncate(latest.counterpartyId, 20);
|
|
2600
|
+
document.getElementById('handshake-latest').title = latest.counterpartyId || '';
|
|
2601
|
+
document.getElementById('handshake-tier').textContent = (latest.trustTier || 'Unverified').toUpperCase();
|
|
2602
|
+
document.getElementById('handshake-time').textContent = formatTime(latest.completedAt);
|
|
2603
|
+
} else {
|
|
2604
|
+
document.getElementById('handshake-latest').textContent = '\u2014';
|
|
2605
|
+
document.getElementById('handshake-tier').textContent = 'Unverified';
|
|
2606
|
+
document.getElementById('handshake-time').textContent = '\u2014';
|
|
2607
|
+
}
|
|
2489
2608
|
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
removePendingRequest(id);
|
|
2493
|
-
}).catch(() => {});
|
|
2494
|
-
};
|
|
2609
|
+
updateHandshakeTable(data.handshakes || []);
|
|
2610
|
+
}
|
|
2495
2611
|
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
details
|
|
2504
|
-
} = data;
|
|
2505
|
-
|
|
2506
|
-
const threat = {
|
|
2507
|
-
id: 'threat-' + threatCount++,
|
|
2508
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
2509
|
-
severity: severity || 'medium',
|
|
2510
|
-
type: type || 'unknown',
|
|
2511
|
-
details: details || ''
|
|
2512
|
-
};
|
|
2612
|
+
function updateHandshakeTable(handshakes) {
|
|
2613
|
+
const table = document.getElementById('handshake-table');
|
|
2614
|
+
|
|
2615
|
+
if (!handshakes || handshakes.length === 0) {
|
|
2616
|
+
table.innerHTML = '<div class="table-empty">No handshakes completed yet</div>';
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2513
2619
|
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2620
|
+
table.innerHTML = handshakes
|
|
2621
|
+
.map(
|
|
2622
|
+
(hs) => \`
|
|
2623
|
+
<div class="table-row">
|
|
2624
|
+
<div class="table-cell strong">\${esc(truncate(hs.counterpartyId, 24))}</div>
|
|
2625
|
+
<div class="table-cell">\${esc(hs.trustTier || 'Unverified')}</div>
|
|
2626
|
+
<div class="table-cell">\${esc(hs.sovereigntyLevel || '\u2014')}</div>
|
|
2627
|
+
<div class="table-cell">\${hs.verified ? 'Yes' : 'No'}</div>
|
|
2628
|
+
<div class="table-cell">\${formatTime(hs.completedAt)}</div>
|
|
2629
|
+
<div class="table-cell">\${formatTime(hs.expiresAt)}</div>
|
|
2630
|
+
</div>
|
|
2631
|
+
\`
|
|
2632
|
+
)
|
|
2633
|
+
.join('');
|
|
2517
2634
|
}
|
|
2518
2635
|
|
|
2519
|
-
|
|
2520
|
-
|
|
2636
|
+
async function updateSHR() {
|
|
2637
|
+
const data = await fetchAPI('/api/shr');
|
|
2638
|
+
if (!data) return;
|
|
2639
|
+
|
|
2640
|
+
apiState.shr = data;
|
|
2641
|
+
renderSHRViewer(data);
|
|
2521
2642
|
}
|
|
2522
2643
|
|
|
2523
|
-
|
|
2524
|
-
|
|
2644
|
+
function renderSHRViewer(shr) {
|
|
2645
|
+
const viewer = document.getElementById('shr-viewer');
|
|
2646
|
+
|
|
2647
|
+
if (!shr) {
|
|
2648
|
+
viewer.innerHTML = '<div class="empty-state">No SHR available</div>';
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2525
2651
|
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2652
|
+
let html = '';
|
|
2653
|
+
|
|
2654
|
+
// Implementation
|
|
2655
|
+
html += \`
|
|
2656
|
+
<div class="shr-section">
|
|
2657
|
+
<div class="shr-section-header">
|
|
2658
|
+
<div class="shr-toggle">\u25BC</div>
|
|
2659
|
+
<div>Implementation</div>
|
|
2660
|
+
</div>
|
|
2661
|
+
<div class="shr-section-content">
|
|
2662
|
+
<div class="shr-item">
|
|
2663
|
+
<div class="shr-key">sanctuary_version:</div>
|
|
2664
|
+
<div class="shr-value">\${esc(shr.implementation?.sanctuary_version || '\u2014')}</div>
|
|
2665
|
+
</div>
|
|
2666
|
+
<div class="shr-item">
|
|
2667
|
+
<div class="shr-key">node_version:</div>
|
|
2668
|
+
<div class="shr-value">\${esc(shr.implementation?.node_version || '\u2014')}</div>
|
|
2669
|
+
</div>
|
|
2670
|
+
<div class="shr-item">
|
|
2671
|
+
<div class="shr-key">generated_by:</div>
|
|
2672
|
+
<div class="shr-value">\${esc(shr.implementation?.generated_by || '\u2014')}</div>
|
|
2673
|
+
</div>
|
|
2674
|
+
</div>
|
|
2675
|
+
</div>
|
|
2676
|
+
\`;
|
|
2677
|
+
|
|
2678
|
+
// Metadata
|
|
2679
|
+
html += \`
|
|
2680
|
+
<div class="shr-section">
|
|
2681
|
+
<div class="shr-section-header">
|
|
2682
|
+
<div class="shr-toggle">\u25BC</div>
|
|
2683
|
+
<div>Metadata</div>
|
|
2684
|
+
</div>
|
|
2685
|
+
<div class="shr-section-content">
|
|
2686
|
+
<div class="shr-item">
|
|
2687
|
+
<div class="shr-key">instance_id:</div>
|
|
2688
|
+
<div class="shr-value">\${esc(truncate(shr.instance_id, 20))}</div>
|
|
2689
|
+
</div>
|
|
2690
|
+
<div class="shr-item">
|
|
2691
|
+
<div class="shr-key">generated_at:</div>
|
|
2692
|
+
<div class="shr-value">\${formatTime(shr.generated_at)}</div>
|
|
2693
|
+
</div>
|
|
2694
|
+
<div class="shr-item">
|
|
2695
|
+
<div class="shr-key">expires_at:</div>
|
|
2696
|
+
<div class="shr-value">\${formatTime(shr.expires_at)}</div>
|
|
2697
|
+
</div>
|
|
2698
|
+
</div>
|
|
2699
|
+
</div>
|
|
2700
|
+
\`;
|
|
2701
|
+
|
|
2702
|
+
// Layers
|
|
2703
|
+
if (shr.layers) {
|
|
2704
|
+
html += \`<div class="shr-section">
|
|
2705
|
+
<div class="shr-section-header">
|
|
2706
|
+
<div class="shr-toggle">\u25BC</div>
|
|
2707
|
+
<div>Layers</div>
|
|
2708
|
+
</div>
|
|
2709
|
+
<div class="shr-section-content">
|
|
2710
|
+
\`;
|
|
2711
|
+
|
|
2712
|
+
for (const [key, layer] of Object.entries(shr.layers)) {
|
|
2713
|
+
html += \`
|
|
2714
|
+
<div style="margin-bottom: 12px;">
|
|
2715
|
+
<div style="color: var(--blue); font-weight: 600; margin-bottom: 4px;">\${esc(key)}</div>
|
|
2716
|
+
<div style="padding-left: 12px;">
|
|
2717
|
+
\`;
|
|
2718
|
+
|
|
2719
|
+
for (const [lkey, lvalue] of Object.entries(layer || {})) {
|
|
2720
|
+
const displayValue =
|
|
2721
|
+
typeof lvalue === 'boolean'
|
|
2722
|
+
? lvalue
|
|
2723
|
+
? 'true'
|
|
2724
|
+
: 'false'
|
|
2725
|
+
: esc(String(lvalue));
|
|
2726
|
+
html += \`
|
|
2727
|
+
<div class="shr-item">
|
|
2728
|
+
<div class="shr-key">\${esc(lkey)}:</div>
|
|
2729
|
+
<div class="shr-value">\${displayValue}</div>
|
|
2730
|
+
</div>
|
|
2731
|
+
\`;
|
|
2732
|
+
}
|
|
2529
2733
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2734
|
+
html += \`
|
|
2735
|
+
</div>
|
|
2736
|
+
</div>
|
|
2737
|
+
\`;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
html += \`
|
|
2741
|
+
</div>
|
|
2742
|
+
</div>
|
|
2743
|
+
\`;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
// Capabilities
|
|
2747
|
+
if (shr.capabilities) {
|
|
2748
|
+
html += \`
|
|
2749
|
+
<div class="shr-section">
|
|
2750
|
+
<div class="shr-section-header">
|
|
2751
|
+
<div class="shr-toggle">\u25BC</div>
|
|
2752
|
+
<div>Capabilities</div>
|
|
2753
|
+
</div>
|
|
2754
|
+
<div class="shr-section-content">
|
|
2755
|
+
\`;
|
|
2756
|
+
|
|
2757
|
+
for (const [key, value] of Object.entries(shr.capabilities)) {
|
|
2758
|
+
const displayValue = value ? 'true' : 'false';
|
|
2759
|
+
html += \`
|
|
2760
|
+
<div class="shr-item">
|
|
2761
|
+
<div class="shr-key">\${esc(key)}:</div>
|
|
2762
|
+
<div class="shr-value">\${displayValue}</div>
|
|
2763
|
+
</div>
|
|
2764
|
+
\`;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
html += \`
|
|
2768
|
+
</div>
|
|
2769
|
+
</div>
|
|
2770
|
+
\`;
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
// Signature
|
|
2774
|
+
html += \`
|
|
2775
|
+
<div class="shr-section">
|
|
2776
|
+
<div class="shr-section-header">
|
|
2777
|
+
<div class="shr-toggle">\u25BC</div>
|
|
2778
|
+
<div>Signature</div>
|
|
2779
|
+
</div>
|
|
2780
|
+
<div class="shr-section-content">
|
|
2781
|
+
<div class="shr-item">
|
|
2782
|
+
<div class="shr-key">signed_by:</div>
|
|
2783
|
+
<div class="shr-value">\${esc(truncate(shr.signed_by, 20))}</div>
|
|
2784
|
+
</div>
|
|
2785
|
+
<div class="shr-item">
|
|
2786
|
+
<div class="shr-key">signature:</div>
|
|
2787
|
+
<div class="shr-value">\${esc(truncate(shr.signature, 32))}</div>
|
|
2788
|
+
</div>
|
|
2789
|
+
</div>
|
|
2790
|
+
</div>
|
|
2791
|
+
\`;
|
|
2792
|
+
|
|
2793
|
+
viewer.innerHTML = html;
|
|
2794
|
+
|
|
2795
|
+
// Add collapse functionality
|
|
2796
|
+
document.querySelectorAll('.shr-section-header').forEach((header) => {
|
|
2797
|
+
header.addEventListener('click', () => {
|
|
2798
|
+
header.closest('.shr-section').classList.toggle('collapsed');
|
|
2799
|
+
});
|
|
2800
|
+
});
|
|
2534
2801
|
}
|
|
2535
2802
|
|
|
2536
|
-
|
|
2537
|
-
|
|
2803
|
+
async function updateStatus() {
|
|
2804
|
+
const data = await fetchAPI('/api/status');
|
|
2805
|
+
if (!data) return;
|
|
2538
2806
|
|
|
2539
|
-
|
|
2540
|
-
const div = document.createElement('div');
|
|
2541
|
-
div.className = 'threat-item';
|
|
2542
|
-
const time = new Date(threat.timestamp).toLocaleTimeString();
|
|
2543
|
-
div.innerHTML =
|
|
2544
|
-
'<div style="margin-bottom: 3px;">' +
|
|
2545
|
-
'<span class="threat-item-type">' + esc(threat.type) + '</span>' +
|
|
2546
|
-
'<span style="font-size: 10px; color: var(--text-secondary); margin-left: 6px;">' + esc(time) + '</span>' +
|
|
2547
|
-
'</div>' +
|
|
2548
|
-
'<div>' + esc(threat.details) + '</div>' +
|
|
2549
|
-
'';
|
|
2550
|
-
content.appendChild(div);
|
|
2551
|
-
}
|
|
2552
|
-
}
|
|
2807
|
+
apiState.status = data;
|
|
2553
2808
|
|
|
2554
|
-
|
|
2809
|
+
document.getElementById('protections-count').textContent = data.protectionsCount || '0';
|
|
2810
|
+
document.getElementById('uptime-value').textContent = formatUptime(data.uptime);
|
|
2555
2811
|
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
}
|
|
2812
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
2813
|
+
connectionStatus.classList.toggle('disconnected', !data.connected);
|
|
2814
|
+
}
|
|
2560
2815
|
|
|
2561
|
-
|
|
2562
|
-
|
|
2816
|
+
function formatUptime(seconds) {
|
|
2817
|
+
if (!seconds) return '\u2014';
|
|
2818
|
+
const hours = Math.floor(seconds / 3600);
|
|
2819
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
2820
|
+
if (hours > 0) return \`\${hours}h \${minutes}m\`;
|
|
2821
|
+
return \`\${minutes}m\`;
|
|
2822
|
+
}
|
|
2563
2823
|
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2824
|
+
// SSE Setup
|
|
2825
|
+
function setupSSE() {
|
|
2826
|
+
const eventSource = new EventSource(API_BASE + '/api/events', {
|
|
2827
|
+
headers: {
|
|
2828
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
2829
|
+
},
|
|
2830
|
+
});
|
|
2567
2831
|
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2832
|
+
eventSource.addEventListener('init', (e) => {
|
|
2833
|
+
console.log('Connected to SSE');
|
|
2834
|
+
});
|
|
2571
2835
|
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
updateBaseline(data.baseline);
|
|
2576
|
-
}
|
|
2577
|
-
if (data.policy) {
|
|
2578
|
-
updatePolicy(data.policy);
|
|
2579
|
-
}
|
|
2580
|
-
if (data.pending) {
|
|
2581
|
-
data.pending.forEach(addPendingRequest);
|
|
2582
|
-
}
|
|
2583
|
-
});
|
|
2836
|
+
eventSource.addEventListener('sovereignty-update', () => {
|
|
2837
|
+
updateSovereignty();
|
|
2838
|
+
});
|
|
2584
2839
|
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
});
|
|
2840
|
+
eventSource.addEventListener('handshake-update', () => {
|
|
2841
|
+
updateHandshakes();
|
|
2842
|
+
});
|
|
2589
2843
|
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2844
|
+
eventSource.addEventListener('tool-call', (e) => {
|
|
2845
|
+
const data = JSON.parse(e.data);
|
|
2846
|
+
addActivityItem({
|
|
2847
|
+
type: 'tool-call',
|
|
2848
|
+
title: 'Tool Call',
|
|
2849
|
+
content: data.toolName,
|
|
2850
|
+
timestamp: new Date().toISOString(),
|
|
2851
|
+
});
|
|
2852
|
+
});
|
|
2594
2853
|
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2854
|
+
eventSource.addEventListener('context-gate-decision', (e) => {
|
|
2855
|
+
const data = JSON.parse(e.data);
|
|
2856
|
+
addActivityItem({
|
|
2857
|
+
type: 'context-gate',
|
|
2858
|
+
title: 'Context Gate',
|
|
2859
|
+
content: data.decision,
|
|
2860
|
+
timestamp: new Date().toISOString(),
|
|
2861
|
+
});
|
|
2603
2862
|
});
|
|
2604
|
-
});
|
|
2605
2863
|
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2864
|
+
eventSource.addEventListener('injection-alert', (e) => {
|
|
2865
|
+
const data = JSON.parse(e.data);
|
|
2866
|
+
addActivityItem({
|
|
2867
|
+
type: 'injection',
|
|
2868
|
+
title: 'Injection Alert',
|
|
2869
|
+
content: data.pattern,
|
|
2870
|
+
timestamp: new Date().toISOString(),
|
|
2871
|
+
});
|
|
2872
|
+
addThreatAlert(data);
|
|
2615
2873
|
});
|
|
2616
|
-
});
|
|
2617
2874
|
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
timestamp: data.timestamp,
|
|
2622
|
-
tier: data.tier || 2,
|
|
2623
|
-
tool: data.tool || 'unknown',
|
|
2624
|
-
outcome: data.allowed ? 'allowed' : 'denied',
|
|
2625
|
-
detail: data.signal || 'Injection detected',
|
|
2626
|
-
hasInjection: true
|
|
2875
|
+
eventSource.addEventListener('pending-request', (e) => {
|
|
2876
|
+
const data = JSON.parse(e.data);
|
|
2877
|
+
addPendingRequest(data);
|
|
2627
2878
|
});
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
details: data.signal || 'Suspicious pattern detected'
|
|
2879
|
+
|
|
2880
|
+
eventSource.addEventListener('request-resolved', (e) => {
|
|
2881
|
+
const data = JSON.parse(e.data);
|
|
2882
|
+
removePendingRequest(data.requestId);
|
|
2633
2883
|
});
|
|
2634
|
-
});
|
|
2635
2884
|
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2885
|
+
eventSource.onerror = () => {
|
|
2886
|
+
console.error('SSE error');
|
|
2887
|
+
setTimeout(setupSSE, 5000);
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2640
2890
|
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2891
|
+
// Activity Feed
|
|
2892
|
+
function addActivityItem(item) {
|
|
2893
|
+
activityLog.unshift(item);
|
|
2894
|
+
if (activityLog.length > maxActivityItems) {
|
|
2895
|
+
activityLog.pop();
|
|
2896
|
+
}
|
|
2645
2897
|
|
|
2646
|
-
|
|
2647
|
-
const
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2898
|
+
const feed = document.getElementById('activity-feed');
|
|
2899
|
+
const html = \`
|
|
2900
|
+
<div class="activity-item \${item.type}">
|
|
2901
|
+
<div class="activity-type">\${esc(item.title)}</div>
|
|
2902
|
+
<div class="activity-content">\${esc(item.content)}</div>
|
|
2903
|
+
<div class="activity-time">\${formatTime(item.timestamp)}</div>
|
|
2904
|
+
</div>
|
|
2905
|
+
\`;
|
|
2651
2906
|
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
}
|
|
2907
|
+
if (feed.querySelector('.empty-state')) {
|
|
2908
|
+
feed.innerHTML = '';
|
|
2909
|
+
}
|
|
2656
2910
|
|
|
2657
|
-
|
|
2658
|
-
if (!policy) return;
|
|
2659
|
-
// Update policy-derived stats
|
|
2660
|
-
if (policy.approval_channel) {
|
|
2661
|
-
// Policy info updated
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2911
|
+
feed.insertAdjacentHTML('afterbegin', html);
|
|
2664
2912
|
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
}
|
|
2669
|
-
if (status.active_protections !== undefined) {
|
|
2670
|
-
document.getElementById('activeProtections').textContent = status.active_protections;
|
|
2671
|
-
}
|
|
2672
|
-
// Update individual protection cards
|
|
2673
|
-
if (status.encryption !== undefined) {
|
|
2674
|
-
const el = document.getElementById('encryptionStatus');
|
|
2675
|
-
el.className = 'protection-card-status ' + (status.encryption ? 'active' : 'inactive');
|
|
2676
|
-
el.textContent = status.encryption ? '\u2713 Active' : '\u2717 Inactive';
|
|
2677
|
-
}
|
|
2678
|
-
if (status.approval_gate !== undefined) {
|
|
2679
|
-
const el = document.getElementById('approvalStatus');
|
|
2680
|
-
el.className = 'protection-card-status ' + (status.approval_gate ? 'active' : 'inactive');
|
|
2681
|
-
el.textContent = status.approval_gate ? '\u2713 Active' : '\u2717 Inactive';
|
|
2682
|
-
}
|
|
2683
|
-
if (status.context_gating !== undefined) {
|
|
2684
|
-
const el = document.getElementById('contextStatus');
|
|
2685
|
-
el.className = 'protection-card-status ' + (status.context_gating ? 'active' : 'inactive');
|
|
2686
|
-
el.textContent = status.context_gating ? '\u2713 Active' : '\u2717 Inactive';
|
|
2687
|
-
}
|
|
2688
|
-
if (status.injection_detection !== undefined) {
|
|
2689
|
-
const el = document.getElementById('injectionStatus');
|
|
2690
|
-
el.className = 'protection-card-status ' + (status.injection_detection ? 'active' : 'inactive');
|
|
2691
|
-
el.textContent = status.injection_detection ? '\u2713 Active' : '\u2717 Inactive';
|
|
2913
|
+
if (feed.children.length > maxActivityItems) {
|
|
2914
|
+
feed.lastChild.remove();
|
|
2915
|
+
}
|
|
2692
2916
|
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2917
|
+
|
|
2918
|
+
// Pending Requests
|
|
2919
|
+
function addPendingRequest(request) {
|
|
2920
|
+
pendingRequests.set(request.requestId, {
|
|
2921
|
+
id: request.requestId,
|
|
2922
|
+
title: request.title,
|
|
2923
|
+
details: request.details,
|
|
2924
|
+
expiresAt: new Date(Date.now() + TIMEOUT_SECONDS * 1000),
|
|
2925
|
+
});
|
|
2926
|
+
|
|
2927
|
+
updatePendingDisplay();
|
|
2697
2928
|
}
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2929
|
+
|
|
2930
|
+
function removePendingRequest(requestId) {
|
|
2931
|
+
pendingRequests.delete(requestId);
|
|
2932
|
+
updatePendingDisplay();
|
|
2702
2933
|
}
|
|
2703
|
-
}
|
|
2704
2934
|
|
|
2705
|
-
|
|
2935
|
+
function updatePendingDisplay() {
|
|
2936
|
+
const badge = document.getElementById('pending-item-badge');
|
|
2937
|
+
const count = pendingRequests.size;
|
|
2938
|
+
|
|
2939
|
+
if (count > 0) {
|
|
2940
|
+
document.getElementById('pending-count').textContent = count;
|
|
2941
|
+
badge.style.display = 'flex';
|
|
2942
|
+
} else {
|
|
2943
|
+
badge.style.display = 'none';
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
const overlay = document.getElementById('pending-overlay');
|
|
2947
|
+
const items = document.getElementById('pending-items');
|
|
2948
|
+
|
|
2949
|
+
if (count === 0) {
|
|
2950
|
+
items.innerHTML = '';
|
|
2951
|
+
overlay.classList.remove('show');
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
let html = '';
|
|
2956
|
+
for (const req of pendingRequests.values()) {
|
|
2957
|
+
const remaining = Math.max(0, Math.floor((req.expiresAt - Date.now()) / 1000));
|
|
2958
|
+
html += \`
|
|
2959
|
+
<div class="pending-item">
|
|
2960
|
+
<div class="pending-title">\${esc(req.title)}</div>
|
|
2961
|
+
<div class="pending-countdown">Expires in \${remaining}s</div>
|
|
2962
|
+
<div class="pending-actions">
|
|
2963
|
+
<button class="pending-btn pending-approve" data-id="\${req.id}">Approve</button>
|
|
2964
|
+
<button class="pending-btn pending-deny" data-id="\${req.id}">Deny</button>
|
|
2965
|
+
</div>
|
|
2966
|
+
</div>
|
|
2967
|
+
\`;
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
items.innerHTML = html;
|
|
2706
2971
|
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2972
|
+
document.querySelectorAll('.pending-approve').forEach((btn) => {
|
|
2973
|
+
btn.addEventListener('click', async () => {
|
|
2974
|
+
const id = btn.getAttribute('data-id');
|
|
2975
|
+
await fetchAPI(\`/api/approve/\${id}\`);
|
|
2976
|
+
});
|
|
2977
|
+
});
|
|
2978
|
+
|
|
2979
|
+
document.querySelectorAll('.pending-deny').forEach((btn) => {
|
|
2980
|
+
btn.addEventListener('click', async () => {
|
|
2981
|
+
const id = btn.getAttribute('data-id');
|
|
2982
|
+
await fetchAPI(\`/api/deny/\${id}\`);
|
|
2983
|
+
});
|
|
2984
|
+
});
|
|
2712
2985
|
}
|
|
2713
|
-
connect();
|
|
2714
2986
|
|
|
2715
|
-
//
|
|
2716
|
-
|
|
2717
|
-
|
|
2987
|
+
// Threat Panel
|
|
2988
|
+
function addThreatAlert(alert) {
|
|
2989
|
+
const panel = document.querySelector('.threat-panel');
|
|
2990
|
+
const content = document.getElementById('threat-alerts');
|
|
2718
2991
|
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
for (const [id, req] of pendingRequests) {
|
|
2722
|
-
req.remaining = Math.max(0, req.remaining - 1);
|
|
2723
|
-
const el = document.getElementById('timer-' + id);
|
|
2724
|
-
if (el) {
|
|
2725
|
-
el.textContent = req.remaining + 's';
|
|
2726
|
-
}
|
|
2992
|
+
if (content.querySelector('.empty-state')) {
|
|
2993
|
+
content.innerHTML = '';
|
|
2727
2994
|
}
|
|
2728
|
-
}, 1000);
|
|
2729
2995
|
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2996
|
+
panel.classList.remove('collapsed');
|
|
2997
|
+
|
|
2998
|
+
const html = \`
|
|
2999
|
+
<div class="threat-alert">
|
|
3000
|
+
<div class="threat-type">\${esc(alert.type || 'Injection Alert')}</div>
|
|
3001
|
+
<div class="threat-message">\${esc(alert.message || alert.pattern || '\u2014')}</div>
|
|
3002
|
+
</div>
|
|
3003
|
+
\`;
|
|
3004
|
+
|
|
3005
|
+
content.insertAdjacentHTML('afterbegin', html);
|
|
3006
|
+
|
|
3007
|
+
const alerts = content.querySelectorAll('.threat-alert');
|
|
3008
|
+
if (alerts.length > 10) {
|
|
3009
|
+
alerts[alerts.length - 1].remove();
|
|
2737
3010
|
}
|
|
2738
|
-
} catch (e) {
|
|
2739
|
-
// Ignore
|
|
2740
3011
|
}
|
|
2741
|
-
})();
|
|
2742
3012
|
|
|
2743
|
-
|
|
2744
|
-
|
|
3013
|
+
// Threat Panel Toggle
|
|
3014
|
+
document.querySelector('.threat-header').addEventListener('click', () => {
|
|
3015
|
+
document.querySelector('.threat-panel').classList.toggle('collapsed');
|
|
3016
|
+
});
|
|
3017
|
+
|
|
3018
|
+
// SHR Copy Button
|
|
3019
|
+
document.getElementById('copy-shr-btn').addEventListener('click', async () => {
|
|
3020
|
+
if (!apiState.shr) return;
|
|
3021
|
+
|
|
3022
|
+
const json = JSON.stringify(apiState.shr, null, 2);
|
|
3023
|
+
try {
|
|
3024
|
+
await navigator.clipboard.writeText(json);
|
|
3025
|
+
const btn = document.getElementById('copy-shr-btn');
|
|
3026
|
+
const original = btn.textContent;
|
|
3027
|
+
btn.textContent = 'Copied!';
|
|
3028
|
+
setTimeout(() => {
|
|
3029
|
+
btn.textContent = original;
|
|
3030
|
+
}, 2000);
|
|
3031
|
+
} catch (err) {
|
|
3032
|
+
console.error('Copy failed:', err);
|
|
3033
|
+
}
|
|
3034
|
+
});
|
|
3035
|
+
|
|
3036
|
+
// Pending Overlay Toggle
|
|
3037
|
+
document.getElementById('pending-item-badge').addEventListener('click', () => {
|
|
3038
|
+
document.getElementById('pending-overlay').classList.toggle('show');
|
|
3039
|
+
});
|
|
3040
|
+
|
|
3041
|
+
// Initialize
|
|
3042
|
+
async function initialize() {
|
|
3043
|
+
if (!AUTH_TOKEN) {
|
|
3044
|
+
redirectToLogin();
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
// Initial data fetch
|
|
3049
|
+
await Promise.all([
|
|
3050
|
+
updateSovereignty(),
|
|
3051
|
+
updateIdentity(),
|
|
3052
|
+
updateHandshakes(),
|
|
3053
|
+
updateSHR(),
|
|
3054
|
+
updateStatus(),
|
|
3055
|
+
]);
|
|
3056
|
+
|
|
3057
|
+
// Setup SSE for real-time updates
|
|
3058
|
+
setupSSE();
|
|
3059
|
+
|
|
3060
|
+
// Refresh status periodically
|
|
3061
|
+
setInterval(updateStatus, 30000);
|
|
3062
|
+
}
|
|
2745
3063
|
|
|
3064
|
+
// Start
|
|
3065
|
+
initialize();
|
|
3066
|
+
</script>
|
|
2746
3067
|
</body>
|
|
2747
3068
|
</html>`;
|
|
2748
3069
|
}
|
|
@@ -8284,6 +8605,155 @@ function deriveTrustTier(level) {
|
|
|
8284
8605
|
}
|
|
8285
8606
|
}
|
|
8286
8607
|
|
|
8608
|
+
// src/handshake/attestation.ts
|
|
8609
|
+
init_encoding();
|
|
8610
|
+
init_key_derivation();
|
|
8611
|
+
init_encoding();
|
|
8612
|
+
var ATTESTATION_VERSION = "1.0";
|
|
8613
|
+
function deriveTrustTier2(level) {
|
|
8614
|
+
switch (level) {
|
|
8615
|
+
case "full":
|
|
8616
|
+
return "verified-sovereign";
|
|
8617
|
+
case "degraded":
|
|
8618
|
+
return "verified-degraded";
|
|
8619
|
+
default:
|
|
8620
|
+
return "unverified";
|
|
8621
|
+
}
|
|
8622
|
+
}
|
|
8623
|
+
function generateAttestation(opts) {
|
|
8624
|
+
const {
|
|
8625
|
+
attesterSHR,
|
|
8626
|
+
subjectSHR,
|
|
8627
|
+
verificationResult,
|
|
8628
|
+
mutual = false,
|
|
8629
|
+
identityManager,
|
|
8630
|
+
masterKey,
|
|
8631
|
+
identityId
|
|
8632
|
+
} = opts;
|
|
8633
|
+
const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
|
|
8634
|
+
if (!identity) {
|
|
8635
|
+
return { error: "No identity available for signing attestation" };
|
|
8636
|
+
}
|
|
8637
|
+
const now = /* @__PURE__ */ new Date();
|
|
8638
|
+
const attesterExpiry = new Date(attesterSHR.body.expires_at);
|
|
8639
|
+
const subjectExpiry = new Date(subjectSHR.body.expires_at);
|
|
8640
|
+
const earliestExpiry = attesterExpiry < subjectExpiry ? attesterExpiry : subjectExpiry;
|
|
8641
|
+
const sovereigntyLevel = verificationResult.valid ? verificationResult.sovereignty_level : "unverified";
|
|
8642
|
+
const body = {
|
|
8643
|
+
attestation_version: ATTESTATION_VERSION,
|
|
8644
|
+
attester_id: attesterSHR.body.instance_id,
|
|
8645
|
+
subject_id: subjectSHR.body.instance_id,
|
|
8646
|
+
attester_shr: attesterSHR,
|
|
8647
|
+
subject_shr: subjectSHR,
|
|
8648
|
+
verification: {
|
|
8649
|
+
subject_shr_valid: verificationResult.valid,
|
|
8650
|
+
subject_sovereignty_level: sovereigntyLevel,
|
|
8651
|
+
subject_trust_tier: deriveTrustTier2(sovereigntyLevel),
|
|
8652
|
+
mutual,
|
|
8653
|
+
errors: verificationResult.errors,
|
|
8654
|
+
warnings: verificationResult.warnings
|
|
8655
|
+
},
|
|
8656
|
+
attested_at: now.toISOString(),
|
|
8657
|
+
expires_at: earliestExpiry.toISOString()
|
|
8658
|
+
};
|
|
8659
|
+
const canonical = JSON.stringify(deepSortKeys(body));
|
|
8660
|
+
const payload = stringToBytes(canonical);
|
|
8661
|
+
const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
|
|
8662
|
+
const signatureBytes = sign(
|
|
8663
|
+
payload,
|
|
8664
|
+
identity.encrypted_private_key,
|
|
8665
|
+
encryptionKey
|
|
8666
|
+
);
|
|
8667
|
+
const summary = generateSummary(body);
|
|
8668
|
+
return {
|
|
8669
|
+
body,
|
|
8670
|
+
signed_by: identity.public_key,
|
|
8671
|
+
signature: toBase64url(signatureBytes),
|
|
8672
|
+
summary
|
|
8673
|
+
};
|
|
8674
|
+
}
|
|
8675
|
+
function layerLine(label, status) {
|
|
8676
|
+
const icon = status === "active" ? "\u2713" : status === "degraded" ? "~" : "x";
|
|
8677
|
+
return ` ${icon} ${label}: ${status}`;
|
|
8678
|
+
}
|
|
8679
|
+
function generateSummary(body) {
|
|
8680
|
+
const v = body.verification;
|
|
8681
|
+
const sLayers = body.subject_shr.body.layers;
|
|
8682
|
+
const aLayers = body.attester_shr.body.layers;
|
|
8683
|
+
const tierLabel = v.subject_trust_tier === "verified-sovereign" ? "Verified Sovereign" : v.subject_trust_tier === "verified-degraded" ? "Verified (Degraded)" : "Unverified";
|
|
8684
|
+
const lines = [
|
|
8685
|
+
`--- Sovereignty Attestation ---`,
|
|
8686
|
+
``,
|
|
8687
|
+
`Attester: ${body.attester_id.slice(0, 16)}...`,
|
|
8688
|
+
`Subject: ${body.subject_id.slice(0, 16)}...`,
|
|
8689
|
+
`Result: ${tierLabel}`,
|
|
8690
|
+
``,
|
|
8691
|
+
`Subject Sovereignty Posture:`,
|
|
8692
|
+
layerLine("L1 Cognitive Sovereignty", sLayers.l1.status),
|
|
8693
|
+
layerLine("L2 Operational Isolation", sLayers.l2.status),
|
|
8694
|
+
layerLine("L3 Selective Disclosure", sLayers.l3.status),
|
|
8695
|
+
layerLine("L4 Verifiable Reputation", sLayers.l4.status),
|
|
8696
|
+
``,
|
|
8697
|
+
`Attester Sovereignty Posture:`,
|
|
8698
|
+
layerLine("L1 Cognitive Sovereignty", aLayers.l1.status),
|
|
8699
|
+
layerLine("L2 Operational Isolation", aLayers.l2.status),
|
|
8700
|
+
layerLine("L3 Selective Disclosure", aLayers.l3.status),
|
|
8701
|
+
layerLine("L4 Verifiable Reputation", aLayers.l4.status),
|
|
8702
|
+
``,
|
|
8703
|
+
`Mutual: ${v.mutual ? "Yes" : "One-sided"}`,
|
|
8704
|
+
`Attested: ${body.attested_at}`,
|
|
8705
|
+
`Expires: ${body.expires_at}`,
|
|
8706
|
+
`Signature: ${body.attestation_version} / Ed25519`
|
|
8707
|
+
];
|
|
8708
|
+
if (v.warnings.length > 0) {
|
|
8709
|
+
lines.push(``, `Warnings: ${v.warnings.join("; ")}`);
|
|
8710
|
+
}
|
|
8711
|
+
if (v.errors.length > 0) {
|
|
8712
|
+
lines.push(``, `Errors: ${v.errors.join("; ")}`);
|
|
8713
|
+
}
|
|
8714
|
+
lines.push(``, `--- Verify: compare signed_by against attester's known public key ---`);
|
|
8715
|
+
return lines.join("\n");
|
|
8716
|
+
}
|
|
8717
|
+
function verifyAttestation(attestation, now) {
|
|
8718
|
+
const errors = [];
|
|
8719
|
+
const currentTime = /* @__PURE__ */ new Date();
|
|
8720
|
+
if (attestation.body.attestation_version !== ATTESTATION_VERSION) {
|
|
8721
|
+
errors.push(
|
|
8722
|
+
`Unsupported attestation version: ${attestation.body.attestation_version}`
|
|
8723
|
+
);
|
|
8724
|
+
}
|
|
8725
|
+
if (!attestation.body.attester_id || !attestation.body.subject_id) {
|
|
8726
|
+
errors.push("Missing attester_id or subject_id");
|
|
8727
|
+
}
|
|
8728
|
+
if (!attestation.body.attester_shr || !attestation.body.subject_shr) {
|
|
8729
|
+
errors.push("Missing attester or subject SHR");
|
|
8730
|
+
}
|
|
8731
|
+
const expired = new Date(attestation.body.expires_at) <= currentTime;
|
|
8732
|
+
if (expired) {
|
|
8733
|
+
errors.push("Attestation has expired");
|
|
8734
|
+
}
|
|
8735
|
+
try {
|
|
8736
|
+
const publicKey = fromBase64url(attestation.signed_by);
|
|
8737
|
+
const canonical = JSON.stringify(deepSortKeys(attestation.body));
|
|
8738
|
+
const payload = stringToBytes(canonical);
|
|
8739
|
+
const signatureBytes = fromBase64url(attestation.signature);
|
|
8740
|
+
const signatureValid = verify(payload, signatureBytes, publicKey);
|
|
8741
|
+
if (!signatureValid) {
|
|
8742
|
+
errors.push("Attestation signature is invalid");
|
|
8743
|
+
}
|
|
8744
|
+
} catch (e) {
|
|
8745
|
+
errors.push(`Signature verification error: ${e.message}`);
|
|
8746
|
+
}
|
|
8747
|
+
return {
|
|
8748
|
+
valid: errors.length === 0,
|
|
8749
|
+
errors,
|
|
8750
|
+
attester_id: attestation.body.attester_id ?? "unknown",
|
|
8751
|
+
subject_id: attestation.body.subject_id ?? "unknown",
|
|
8752
|
+
trust_tier: errors.length === 0 ? attestation.body.verification.subject_trust_tier : "unverified",
|
|
8753
|
+
expired
|
|
8754
|
+
};
|
|
8755
|
+
}
|
|
8756
|
+
|
|
8287
8757
|
// src/handshake/tools.ts
|
|
8288
8758
|
function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
8289
8759
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -8469,6 +8939,103 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
|
8469
8939
|
result: session.result ?? null
|
|
8470
8940
|
});
|
|
8471
8941
|
}
|
|
8942
|
+
},
|
|
8943
|
+
// ─── Streamlined Exchange ─────────────────────────────────────────
|
|
8944
|
+
{
|
|
8945
|
+
name: "sanctuary/handshake_exchange",
|
|
8946
|
+
description: "One-shot sovereignty exchange. Accepts a counterparty's signed SHR, verifies it, generates our SHR, and produces a signed attestation artifact \u2014 all in a single call. Returns a shareable attestation with human-readable summary. Use this instead of the 4-step handshake protocol when you want a quick, portable sovereignty verification (e.g., for social posting or async exchanges).",
|
|
8947
|
+
inputSchema: {
|
|
8948
|
+
type: "object",
|
|
8949
|
+
properties: {
|
|
8950
|
+
counterparty_shr: {
|
|
8951
|
+
type: "object",
|
|
8952
|
+
description: "The counterparty's signed SHR (SignedSHR object with body, signed_by, signature)."
|
|
8953
|
+
},
|
|
8954
|
+
identity_id: {
|
|
8955
|
+
type: "string",
|
|
8956
|
+
description: "Identity to use for the exchange. Defaults to primary identity."
|
|
8957
|
+
}
|
|
8958
|
+
},
|
|
8959
|
+
required: ["counterparty_shr"]
|
|
8960
|
+
},
|
|
8961
|
+
handler: async (args) => {
|
|
8962
|
+
const counterpartySHR = args.counterparty_shr;
|
|
8963
|
+
const ourSHR = generateSHR(args.identity_id, shrOpts);
|
|
8964
|
+
if (typeof ourSHR === "string") {
|
|
8965
|
+
return toolResult({ error: ourSHR });
|
|
8966
|
+
}
|
|
8967
|
+
const verificationResult = verifySHR(counterpartySHR);
|
|
8968
|
+
const attestation = generateAttestation({
|
|
8969
|
+
attesterSHR: ourSHR,
|
|
8970
|
+
subjectSHR: counterpartySHR,
|
|
8971
|
+
verificationResult,
|
|
8972
|
+
mutual: false,
|
|
8973
|
+
identityManager,
|
|
8974
|
+
masterKey,
|
|
8975
|
+
identityId: args.identity_id
|
|
8976
|
+
});
|
|
8977
|
+
if ("error" in attestation) {
|
|
8978
|
+
auditLog.append("l4", "handshake_exchange", ourSHR.body.instance_id, void 0, "failure");
|
|
8979
|
+
return toolResult({ error: attestation.error });
|
|
8980
|
+
}
|
|
8981
|
+
if (verificationResult.valid) {
|
|
8982
|
+
const sovereigntyLevel = verificationResult.sovereignty_level;
|
|
8983
|
+
const trustTier = sovereigntyLevel === "full" ? "verified-sovereign" : sovereigntyLevel === "degraded" ? "verified-degraded" : "unverified";
|
|
8984
|
+
handshakeResults.set(verificationResult.counterparty_id, {
|
|
8985
|
+
counterparty_id: verificationResult.counterparty_id,
|
|
8986
|
+
counterparty_shr: counterpartySHR,
|
|
8987
|
+
verified: true,
|
|
8988
|
+
sovereignty_level: sovereigntyLevel,
|
|
8989
|
+
trust_tier: trustTier,
|
|
8990
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8991
|
+
expires_at: verificationResult.expires_at,
|
|
8992
|
+
errors: []
|
|
8993
|
+
});
|
|
8994
|
+
}
|
|
8995
|
+
auditLog.append("l4", "handshake_exchange", ourSHR.body.instance_id);
|
|
8996
|
+
return toolResult({
|
|
8997
|
+
attestation,
|
|
8998
|
+
our_shr: ourSHR,
|
|
8999
|
+
verification: {
|
|
9000
|
+
counterparty_valid: verificationResult.valid,
|
|
9001
|
+
counterparty_sovereignty: verificationResult.sovereignty_level,
|
|
9002
|
+
counterparty_id: verificationResult.counterparty_id,
|
|
9003
|
+
errors: verificationResult.errors,
|
|
9004
|
+
warnings: verificationResult.warnings
|
|
9005
|
+
},
|
|
9006
|
+
instructions: "The 'attestation' object is a signed, portable sovereignty verification artifact. Share it with the counterparty or post attestation.summary publicly. The counterparty can verify the attestation signature using your public key. Our SHR is included so the counterparty can perform their own verification of us.",
|
|
9007
|
+
_content_trust: "external"
|
|
9008
|
+
});
|
|
9009
|
+
}
|
|
9010
|
+
},
|
|
9011
|
+
{
|
|
9012
|
+
name: "sanctuary/handshake_verify_attestation",
|
|
9013
|
+
description: "Verify a signed attestation artifact from another agent. Checks the Ed25519 signature, temporal validity, and structural integrity.",
|
|
9014
|
+
inputSchema: {
|
|
9015
|
+
type: "object",
|
|
9016
|
+
properties: {
|
|
9017
|
+
attestation: {
|
|
9018
|
+
type: "object",
|
|
9019
|
+
description: "The SignedAttestation object to verify (body, signed_by, signature, summary)."
|
|
9020
|
+
}
|
|
9021
|
+
},
|
|
9022
|
+
required: ["attestation"]
|
|
9023
|
+
},
|
|
9024
|
+
handler: async (args) => {
|
|
9025
|
+
const attestation = args.attestation;
|
|
9026
|
+
const result = verifyAttestation(attestation);
|
|
9027
|
+
auditLog.append(
|
|
9028
|
+
"l4",
|
|
9029
|
+
"handshake_verify_attestation",
|
|
9030
|
+
result.attester_id,
|
|
9031
|
+
void 0,
|
|
9032
|
+
result.valid ? "success" : "failure"
|
|
9033
|
+
);
|
|
9034
|
+
return toolResult({
|
|
9035
|
+
...result,
|
|
9036
|
+
_content_trust: "external"
|
|
9037
|
+
});
|
|
9038
|
+
}
|
|
8472
9039
|
}
|
|
8473
9040
|
];
|
|
8474
9041
|
return { tools, handshakeResults };
|