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