@sanctuary-framework/mcp-server 0.5.4 → 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 +1936 -1399
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1936 -1399
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1937 -1398
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -2
- package/dist/index.d.ts +97 -2
- package/dist/index.js +1935 -1399
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -51,7 +51,7 @@ function defaultConfig() {
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
disclosure: {
|
|
54
|
-
proof_system: "
|
|
54
|
+
proof_system: "schnorr-pedersen",
|
|
55
55
|
default_policy: "minimum-necessary"
|
|
56
56
|
},
|
|
57
57
|
reputation: {
|
|
@@ -165,7 +165,7 @@ function validateConfig(config) {
|
|
|
165
165
|
`Unimplemented config value: execution.environment = "${config.execution.environment}". Only ${[...implementedEnvironment].map((v) => `"${v}"`).join(", ")} are currently implemented. Using an unimplemented environment would silently degrade security.`
|
|
166
166
|
);
|
|
167
167
|
}
|
|
168
|
-
const implementedProofSystem = /* @__PURE__ */ new Set(["commitment-only"]);
|
|
168
|
+
const implementedProofSystem = /* @__PURE__ */ new Set(["schnorr-pedersen", "commitment-only"]);
|
|
169
169
|
if (!implementedProofSystem.has(config.disclosure.proof_system)) {
|
|
170
170
|
errors.push(
|
|
171
171
|
`Unimplemented config value: disclosure.proof_system = "${config.disclosure.proof_system}". Only ${[...implementedProofSystem].map((v) => `"${v}"`).join(", ")} is currently implemented. Using an unimplemented proof system would silently degrade security.`
|
|
@@ -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>
|
|
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>
|
|
2099
2242
|
</div>
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
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>
|
|
2104
2250
|
</div>
|
|
2105
|
-
|
|
2106
|
-
|
|
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>
|
|
2265
|
+
</div>
|
|
2107
2266
|
</div>
|
|
2108
|
-
<div class="status-dot" id="statusDot"></div>
|
|
2109
|
-
<div class="pending-badge hidden" id="pendingBadge">0</div>
|
|
2110
2267
|
</div>
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
<div class="
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
<div class="
|
|
2124
|
-
|
|
2268
|
+
|
|
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>
|
|
2298
|
+
</div>
|
|
2299
|
+
|
|
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>
|
|
2373
|
+
</div>
|
|
2374
|
+
|
|
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>
|
|
2399
|
+
</div>
|
|
2400
|
+
|
|
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>
|
|
2414
|
+
</div>
|
|
2415
|
+
|
|
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>
|
|
2125
2425
|
</div>
|
|
2126
2426
|
</div>
|
|
2127
2427
|
</div>
|
|
2128
2428
|
|
|
2129
|
-
<!--
|
|
2130
|
-
<div class="
|
|
2131
|
-
<div class="
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
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>
|
|
2433
|
+
</div>
|
|
2434
|
+
|
|
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
|
+
};
|
|
2449
|
+
|
|
2450
|
+
let pendingRequests = new Map();
|
|
2451
|
+
let activityLog = [];
|
|
2452
|
+
const maxActivityItems = 50;
|
|
2453
|
+
|
|
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
|
+
}
|
|
2472
|
+
|
|
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
|
+
}
|
|
2478
|
+
|
|
2479
|
+
function calculateSovereigntyScore(shr) {
|
|
2480
|
+
if (!shr || !shr.layers) return 0;
|
|
2481
|
+
const layers = shr.layers;
|
|
2482
|
+
let score = 100;
|
|
2483
|
+
|
|
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;
|
|
2492
|
+
|
|
2493
|
+
return Math.max(0, Math.min(100, score));
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
async function fetchAPI(endpoint) {
|
|
2497
|
+
try {
|
|
2498
|
+
const response = await fetch(API_BASE + endpoint, {
|
|
2499
|
+
headers: {
|
|
2500
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
2501
|
+
},
|
|
2502
|
+
});
|
|
2503
|
+
|
|
2504
|
+
if (response.status === 401) {
|
|
2505
|
+
redirectToLogin();
|
|
2506
|
+
return null;
|
|
2507
|
+
}
|
|
2508
|
+
|
|
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
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
function redirectToLogin() {
|
|
2522
|
+
sessionStorage.removeItem('authToken');
|
|
2523
|
+
window.location.href = '/';
|
|
2524
|
+
}
|
|
2141
2525
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
<div class="protection-card-stat" id="approvalStat">T1: 2 | T2: 3</div>
|
|
2147
|
-
</div>
|
|
2526
|
+
// API Updates
|
|
2527
|
+
async function updateSovereignty() {
|
|
2528
|
+
const data = await fetchAPI('/api/sovereignty');
|
|
2529
|
+
if (!data) return;
|
|
2148
2530
|
|
|
2149
|
-
|
|
2150
|
-
<div class="protection-card-icon">\u{1F3AF}</div>
|
|
2151
|
-
<div class="protection-card-label">Context Gating</div>
|
|
2152
|
-
<div class="protection-card-status active" id="contextStatus">\u2713 Active</div>
|
|
2153
|
-
<div class="protection-card-stat" id="contextStat">12 filtered</div>
|
|
2154
|
-
</div>
|
|
2531
|
+
apiState.sovereignty = data;
|
|
2155
2532
|
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
<div class="protection-card-status active" id="injectionStatus">\u2713 Active</div>
|
|
2160
|
-
<div class="protection-card-stat" id="injectionStat">3 flags today</div>
|
|
2161
|
-
</div>
|
|
2533
|
+
const score = calculateSovereigntyScore(data.shr);
|
|
2534
|
+
const badge = document.getElementById('sovereignty-badge');
|
|
2535
|
+
const scoreEl = document.getElementById('sovereignty-score');
|
|
2162
2536
|
|
|
2163
|
-
|
|
2164
|
-
<div class="protection-card-icon">\u{1F4CA}</div>
|
|
2165
|
-
<div class="protection-card-label">Behavioral Baseline</div>
|
|
2166
|
-
<div class="protection-card-status active" id="baselineStatus">\u2713 Active</div>
|
|
2167
|
-
<div class="protection-card-stat" id="baselineStat">0 anomalies</div>
|
|
2168
|
-
</div>
|
|
2537
|
+
scoreEl.textContent = score;
|
|
2169
2538
|
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
<div class="protection-card-status active" id="auditStatus">\u2713 Active</div>
|
|
2174
|
-
<div class="protection-card-stat" id="auditStat">284 entries</div>
|
|
2175
|
-
</div>
|
|
2176
|
-
</div>
|
|
2177
|
-
</div>
|
|
2178
|
-
</div>
|
|
2539
|
+
badge.classList.remove('degraded', 'inactive');
|
|
2540
|
+
if (score < 70) badge.classList.add('degraded');
|
|
2541
|
+
if (score < 40) badge.classList.add('inactive');
|
|
2179
2542
|
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
<div class="pending-overlay-header">
|
|
2183
|
-
<div class="pending-overlay-title">Pending Approvals</div>
|
|
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>
|
|
2195
|
-
</div>
|
|
2196
|
-
<div class="threat-content" id="threatContent">
|
|
2197
|
-
<div class="threat-empty">No threats detected</div>
|
|
2198
|
-
</div>
|
|
2199
|
-
</div>
|
|
2543
|
+
updateLayerCards(data.shr);
|
|
2544
|
+
}
|
|
2200
2545
|
|
|
2201
|
-
|
|
2202
|
-
(
|
|
2203
|
-
'use strict';
|
|
2546
|
+
function updateLayerCards(shr) {
|
|
2547
|
+
if (!shr || !shr.layers) return;
|
|
2204
2548
|
|
|
2205
|
-
|
|
2549
|
+
const layers = shr.layers;
|
|
2206
2550
|
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
const MAX_THREAT_ITEMS = 20;
|
|
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
|
+
}
|
|
2213
2556
|
|
|
2214
|
-
|
|
2557
|
+
function updateLayerCard(layer, layerData, detail) {
|
|
2558
|
+
if (!layerData) return;
|
|
2215
2559
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
let startTime = Date.now();
|
|
2219
|
-
let activityCount = 0;
|
|
2220
|
-
let threatCount = 0;
|
|
2221
|
-
const pendingRequests = new Map();
|
|
2222
|
-
const activityItems = [];
|
|
2223
|
-
const threatItems = [];
|
|
2224
|
-
let sovereigntyScore = 85;
|
|
2225
|
-
let sessionRenewalTimer = null;
|
|
2560
|
+
const card = document.querySelector(\`[data-layer="\${layer}"]\`);
|
|
2561
|
+
if (!card) return;
|
|
2226
2562
|
|
|
2227
|
-
|
|
2563
|
+
const status = layerData.status || 'inactive';
|
|
2564
|
+
card.classList.remove('degraded', 'inactive');
|
|
2228
2565
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2566
|
+
if (status === 'degraded') {
|
|
2567
|
+
card.classList.add('degraded');
|
|
2568
|
+
} else if (status === 'inactive') {
|
|
2569
|
+
card.classList.add('inactive');
|
|
2570
|
+
}
|
|
2234
2571
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
return url + sep + 'session=' + SESSION_ID;
|
|
2239
|
-
}
|
|
2572
|
+
document.getElementById(\`\${layer}-status\`).textContent = status.toUpperCase();
|
|
2573
|
+
document.getElementById(\`\${layer}-detail\`).textContent = detail;
|
|
2574
|
+
}
|
|
2240
2575
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
}
|
|
2576
|
+
async function updateIdentity() {
|
|
2577
|
+
const data = await fetchAPI('/api/identity');
|
|
2578
|
+
if (!data) return;
|
|
2245
2579
|
|
|
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');
|
|
2580
|
+
apiState.identity = data;
|
|
2581
|
+
|
|
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';
|
|
2323
2590
|
}
|
|
2324
|
-
}
|
|
2325
2591
|
|
|
2326
|
-
|
|
2592
|
+
async function updateHandshakes() {
|
|
2593
|
+
const data = await fetchAPI('/api/handshakes');
|
|
2594
|
+
if (!data) return;
|
|
2327
2595
|
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
tool: tool || 'unknown_tool',
|
|
2344
|
-
outcome: outcome || 'executed',
|
|
2345
|
-
detail: detail || '',
|
|
2346
|
-
hasInjection: !!hasInjection,
|
|
2347
|
-
isContextGated: !!isContextGated
|
|
2348
|
-
};
|
|
2596
|
+
apiState.handshakes = data.handshakes || [];
|
|
2597
|
+
|
|
2598
|
+
document.getElementById('handshake-count').textContent = data.handshakes?.length || '0';
|
|
2599
|
+
|
|
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
|
+
}
|
|
2349
2611
|
|
|
2350
|
-
|
|
2351
|
-
if (activityItems.length > MAX_ACTIVITY_ITEMS) {
|
|
2352
|
-
activityItems.pop();
|
|
2612
|
+
updateHandshakeTable(data.handshakes || []);
|
|
2353
2613
|
}
|
|
2354
2614
|
|
|
2355
|
-
|
|
2356
|
-
|
|
2615
|
+
function updateHandshakeTable(handshakes) {
|
|
2616
|
+
const table = document.getElementById('handshake-table');
|
|
2357
2617
|
|
|
2358
|
-
|
|
2359
|
-
|
|
2618
|
+
if (!handshakes || handshakes.length === 0) {
|
|
2619
|
+
table.innerHTML = '<div class="table-empty">No handshakes completed yet</div>';
|
|
2620
|
+
return;
|
|
2621
|
+
}
|
|
2360
2622
|
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
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('');
|
|
2364
2637
|
}
|
|
2365
2638
|
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
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
|
-
});
|
|
2639
|
+
async function updateSHR() {
|
|
2640
|
+
const data = await fetchAPI('/api/shr');
|
|
2641
|
+
if (!data) return;
|
|
2400
2642
|
|
|
2401
|
-
|
|
2643
|
+
apiState.shr = data;
|
|
2644
|
+
renderSHRViewer(data);
|
|
2402
2645
|
}
|
|
2403
|
-
}
|
|
2404
2646
|
|
|
2405
|
-
|
|
2647
|
+
function renderSHRViewer(shr) {
|
|
2648
|
+
const viewer = document.getElementById('shr-viewer');
|
|
2406
2649
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
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
|
-
};
|
|
2650
|
+
if (!shr) {
|
|
2651
|
+
viewer.innerHTML = '<div class="empty-state">No SHR available</div>';
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2426
2654
|
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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
|
+
}
|
|
2430
2736
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
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
|
+
});
|
|
2804
|
+
}
|
|
2435
2805
|
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2806
|
+
async function updateStatus() {
|
|
2807
|
+
const data = await fetchAPI('/api/status');
|
|
2808
|
+
if (!data) return;
|
|
2439
2809
|
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
document.getElementById('
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2810
|
+
apiState.status = data;
|
|
2811
|
+
|
|
2812
|
+
document.getElementById('protections-count').textContent = data.protectionsCount || '0';
|
|
2813
|
+
document.getElementById('uptime-value').textContent = formatUptime(data.uptime);
|
|
2814
|
+
|
|
2815
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
2816
|
+
connectionStatus.classList.toggle('disconnected', !data.connected);
|
|
2447
2817
|
}
|
|
2448
2818
|
|
|
2449
|
-
|
|
2450
|
-
|
|
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
|
+
}
|
|
2451
2826
|
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2827
|
+
// SSE Setup
|
|
2828
|
+
function setupSSE() {
|
|
2829
|
+
const eventSource = new EventSource(API_BASE + '/api/events', {
|
|
2830
|
+
headers: {
|
|
2831
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
2832
|
+
},
|
|
2833
|
+
});
|
|
2455
2834
|
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2835
|
+
eventSource.addEventListener('init', (e) => {
|
|
2836
|
+
console.log('Connected to SSE');
|
|
2837
|
+
});
|
|
2459
2838
|
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
const isUrgent = req.remaining <= 30;
|
|
2839
|
+
eventSource.addEventListener('sovereignty-update', () => {
|
|
2840
|
+
updateSovereignty();
|
|
2841
|
+
});
|
|
2464
2842
|
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
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
|
-
'';
|
|
2843
|
+
eventSource.addEventListener('handshake-update', () => {
|
|
2844
|
+
updateHandshakes();
|
|
2845
|
+
});
|
|
2482
2846
|
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
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
|
+
});
|
|
2486
2856
|
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
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
|
+
});
|
|
2865
|
+
});
|
|
2492
2866
|
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
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);
|
|
2876
|
+
});
|
|
2498
2877
|
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
timestamp,
|
|
2504
|
-
severity,
|
|
2505
|
-
type,
|
|
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
|
-
};
|
|
2878
|
+
eventSource.addEventListener('pending-request', (e) => {
|
|
2879
|
+
const data = JSON.parse(e.data);
|
|
2880
|
+
addPendingRequest(data);
|
|
2881
|
+
});
|
|
2516
2882
|
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2883
|
+
eventSource.addEventListener('request-resolved', (e) => {
|
|
2884
|
+
const data = JSON.parse(e.data);
|
|
2885
|
+
removePendingRequest(data.requestId);
|
|
2886
|
+
});
|
|
2521
2887
|
|
|
2522
|
-
|
|
2523
|
-
|
|
2888
|
+
eventSource.onerror = () => {
|
|
2889
|
+
console.error('SSE error');
|
|
2890
|
+
setTimeout(setupSSE, 5000);
|
|
2891
|
+
};
|
|
2524
2892
|
}
|
|
2525
2893
|
|
|
2526
|
-
|
|
2527
|
-
|
|
2894
|
+
// Activity Feed
|
|
2895
|
+
function addActivityItem(item) {
|
|
2896
|
+
activityLog.unshift(item);
|
|
2897
|
+
if (activityLog.length > maxActivityItems) {
|
|
2898
|
+
activityLog.pop();
|
|
2899
|
+
}
|
|
2528
2900
|
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
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
|
+
\`;
|
|
2532
2909
|
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
return;
|
|
2537
|
-
}
|
|
2910
|
+
if (feed.querySelector('.empty-state')) {
|
|
2911
|
+
feed.innerHTML = '';
|
|
2912
|
+
}
|
|
2538
2913
|
|
|
2539
|
-
|
|
2540
|
-
content.innerHTML = '';
|
|
2914
|
+
feed.insertAdjacentHTML('afterbegin', html);
|
|
2541
2915
|
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
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);
|
|
2916
|
+
if (feed.children.length > maxActivityItems) {
|
|
2917
|
+
feed.lastChild.remove();
|
|
2918
|
+
}
|
|
2554
2919
|
}
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
|
-
// \u2500\u2500 SSE Connection \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
|
|
2558
2920
|
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
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
|
+
});
|
|
2563
2929
|
|
|
2564
|
-
|
|
2565
|
-
|
|
2930
|
+
updatePendingDisplay();
|
|
2931
|
+
}
|
|
2566
2932
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2933
|
+
function removePendingRequest(requestId) {
|
|
2934
|
+
pendingRequests.delete(requestId);
|
|
2935
|
+
updatePendingDisplay();
|
|
2936
|
+
}
|
|
2570
2937
|
|
|
2571
|
-
|
|
2572
|
-
document.getElementById('
|
|
2573
|
-
|
|
2938
|
+
function updatePendingDisplay() {
|
|
2939
|
+
const badge = document.getElementById('pending-item-badge');
|
|
2940
|
+
const count = pendingRequests.size;
|
|
2574
2941
|
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
if (data.policy) {
|
|
2581
|
-
updatePolicy(data.policy);
|
|
2942
|
+
if (count > 0) {
|
|
2943
|
+
document.getElementById('pending-count').textContent = count;
|
|
2944
|
+
badge.style.display = 'flex';
|
|
2945
|
+
} else {
|
|
2946
|
+
badge.style.display = 'none';
|
|
2582
2947
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
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;
|
|
2585
2956
|
}
|
|
2586
|
-
});
|
|
2587
2957
|
|
|
2588
|
-
|
|
2589
|
-
const
|
|
2590
|
-
|
|
2591
|
-
|
|
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
|
+
}
|
|
2592
2972
|
|
|
2593
|
-
|
|
2594
|
-
const data = JSON.parse(e.data);
|
|
2595
|
-
removePendingRequest(data.request_id);
|
|
2596
|
-
});
|
|
2973
|
+
items.innerHTML = html;
|
|
2597
2974
|
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
tool: data.tool || 'unknown',
|
|
2604
|
-
outcome: data.outcome || 'executed',
|
|
2605
|
-
detail: data.detail || ''
|
|
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
|
+
});
|
|
2606
2980
|
});
|
|
2607
|
-
});
|
|
2608
2981
|
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
tool: data.tool || 'unknown',
|
|
2615
|
-
outcome: data.outcome || 'gated',
|
|
2616
|
-
detail: data.fields_filtered ? 'Filtered ' + data.fields_filtered + ' fields' : data.reason || '',
|
|
2617
|
-
isContextGated: true
|
|
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
|
+
});
|
|
2618
2987
|
});
|
|
2619
|
-
}
|
|
2988
|
+
}
|
|
2620
2989
|
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
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
|
|
2630
|
-
});
|
|
2631
|
-
addThreat({
|
|
2632
|
-
timestamp: data.timestamp,
|
|
2633
|
-
severity: data.severity || 'medium',
|
|
2634
|
-
type: 'Injection Alert',
|
|
2635
|
-
details: data.signal || 'Suspicious pattern detected'
|
|
2636
|
-
});
|
|
2637
|
-
});
|
|
2990
|
+
// Threat Panel
|
|
2991
|
+
function addThreatAlert(alert) {
|
|
2992
|
+
const panel = document.querySelector('.threat-panel');
|
|
2993
|
+
const content = document.getElementById('threat-alerts');
|
|
2638
2994
|
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
});
|
|
2995
|
+
if (content.querySelector('.empty-state')) {
|
|
2996
|
+
content.innerHTML = '';
|
|
2997
|
+
}
|
|
2643
2998
|
|
|
2644
|
-
|
|
2645
|
-
const data = JSON.parse(e.data);
|
|
2646
|
-
// Audit entries don't show in activity by default, but we could add them
|
|
2647
|
-
});
|
|
2999
|
+
panel.classList.remove('collapsed');
|
|
2648
3000
|
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
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
|
+
\`;
|
|
2654
3007
|
|
|
2655
|
-
|
|
2656
|
-
if (!baseline) return;
|
|
2657
|
-
// Update baseline-derived stats if needed
|
|
2658
|
-
}
|
|
3008
|
+
content.insertAdjacentHTML('afterbegin', html);
|
|
2659
3009
|
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
// Policy info updated
|
|
3010
|
+
const alerts = content.querySelectorAll('.threat-alert');
|
|
3011
|
+
if (alerts.length > 10) {
|
|
3012
|
+
alerts[alerts.length - 1].remove();
|
|
3013
|
+
}
|
|
2665
3014
|
}
|
|
2666
|
-
}
|
|
2667
3015
|
|
|
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';
|
|
2695
|
-
}
|
|
2696
|
-
if (status.baseline !== undefined) {
|
|
2697
|
-
const el = document.getElementById('baselineStatus');
|
|
2698
|
-
el.className = 'protection-card-status ' + (status.baseline ? 'active' : 'inactive');
|
|
2699
|
-
el.textContent = status.baseline ? '\u2713 Active' : '\u2717 Inactive';
|
|
2700
|
-
}
|
|
2701
|
-
if (status.audit_trail !== undefined) {
|
|
2702
|
-
const el = document.getElementById('auditStatus');
|
|
2703
|
-
el.className = 'protection-card-status ' + (status.audit_trail ? 'active' : 'inactive');
|
|
2704
|
-
el.textContent = status.audit_trail ? '\u2713 Active' : '\u2717 Inactive';
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
3016
|
+
// Threat Panel Toggle
|
|
3017
|
+
document.querySelector('.threat-header').addEventListener('click', () => {
|
|
3018
|
+
document.querySelector('.threat-panel').classList.toggle('collapsed');
|
|
3019
|
+
});
|
|
2707
3020
|
|
|
2708
|
-
|
|
3021
|
+
// SHR Copy Button
|
|
3022
|
+
document.getElementById('copy-shr-btn').addEventListener('click', async () => {
|
|
3023
|
+
if (!apiState.shr) return;
|
|
2709
3024
|
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
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
|
+
});
|
|
2717
3038
|
|
|
2718
|
-
//
|
|
2719
|
-
|
|
2720
|
-
|
|
3039
|
+
// Pending Overlay Toggle
|
|
3040
|
+
document.getElementById('pending-item-badge').addEventListener('click', () => {
|
|
3041
|
+
document.getElementById('pending-overlay').classList.toggle('show');
|
|
3042
|
+
});
|
|
2721
3043
|
|
|
2722
|
-
//
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
if (el) {
|
|
2728
|
-
el.textContent = req.remaining + 's';
|
|
2729
|
-
}
|
|
3044
|
+
// Initialize
|
|
3045
|
+
async function initialize() {
|
|
3046
|
+
if (!AUTH_TOKEN) {
|
|
3047
|
+
redirectToLogin();
|
|
3048
|
+
return;
|
|
2730
3049
|
}
|
|
2731
|
-
}, 1000);
|
|
2732
3050
|
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
//
|
|
2743
|
-
|
|
2744
|
-
})();
|
|
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();
|
|
2745
3062
|
|
|
2746
|
-
|
|
2747
|
-
|
|
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
|
}
|
|
@@ -7636,15 +7957,6 @@ function generateSHR(identityId, opts) {
|
|
|
7636
7957
|
mitigation: "TEE attestation planned for a future release"
|
|
7637
7958
|
});
|
|
7638
7959
|
}
|
|
7639
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
7640
|
-
degradations.push({
|
|
7641
|
-
layer: "l3",
|
|
7642
|
-
code: "COMMITMENT_ONLY",
|
|
7643
|
-
severity: "info",
|
|
7644
|
-
description: "Commitment schemes only (no ZK proofs)",
|
|
7645
|
-
mitigation: "ZK proof support planned for future release"
|
|
7646
|
-
});
|
|
7647
|
-
}
|
|
7648
7960
|
const body = {
|
|
7649
7961
|
shr_version: "1.0",
|
|
7650
7962
|
implementation: {
|
|
@@ -7670,9 +7982,9 @@ function generateSHR(identityId, opts) {
|
|
|
7670
7982
|
attestation_available: config.execution.attestation
|
|
7671
7983
|
},
|
|
7672
7984
|
l3: {
|
|
7673
|
-
status:
|
|
7985
|
+
status: "active",
|
|
7674
7986
|
proof_system: config.disclosure.proof_system,
|
|
7675
|
-
selective_disclosure:
|
|
7987
|
+
selective_disclosure: true
|
|
7676
7988
|
},
|
|
7677
7989
|
l4: {
|
|
7678
7990
|
status: "active",
|
|
@@ -7885,7 +8197,7 @@ function extractAuthorizationSignals(body) {
|
|
|
7885
8197
|
behavioral_baseline_active: false,
|
|
7886
8198
|
// Would need explicit field in SHR v1.1
|
|
7887
8199
|
identity_verified: l1.identity_type === "ed25519" || l1.identity_type !== "none",
|
|
7888
|
-
zero_knowledge_capable: l3.status === "active"
|
|
8200
|
+
zero_knowledge_capable: l3.status === "active",
|
|
7889
8201
|
selective_disclosure_active: l3.selective_disclosure,
|
|
7890
8202
|
reputation_portable: l4.reputation_portable,
|
|
7891
8203
|
handshake_capable: body.capabilities.handshake
|
|
@@ -7963,14 +8275,6 @@ function generateAuthorizationConstraints(body, _degradations) {
|
|
|
7963
8275
|
priority: "high"
|
|
7964
8276
|
});
|
|
7965
8277
|
}
|
|
7966
|
-
if (layers.l3.proof_system === "commitment-only") {
|
|
7967
|
-
constraints.push({
|
|
7968
|
-
type: "restricted_scope",
|
|
7969
|
-
description: "No zero-knowledge proofs available \u2014 entire state context may be visible",
|
|
7970
|
-
rationale: "Proof system is commitment-only (no ZK)",
|
|
7971
|
-
priority: "medium"
|
|
7972
|
-
});
|
|
7973
|
-
}
|
|
7974
8278
|
if (layers.l4.status === "degraded") {
|
|
7975
8279
|
constraints.push({
|
|
7976
8280
|
type: "known_agents_only",
|
|
@@ -8304,6 +8608,155 @@ function deriveTrustTier(level) {
|
|
|
8304
8608
|
}
|
|
8305
8609
|
}
|
|
8306
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
|
+
|
|
8307
8760
|
// src/handshake/tools.ts
|
|
8308
8761
|
function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
8309
8762
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -8489,6 +8942,103 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
|
8489
8942
|
result: session.result ?? null
|
|
8490
8943
|
});
|
|
8491
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
|
+
}
|
|
8492
9042
|
}
|
|
8493
9043
|
];
|
|
8494
9044
|
return { tools, handshakeResults };
|
|
@@ -12059,11 +12609,6 @@ async function createSanctuaryServer(options) {
|
|
|
12059
12609
|
degradations.push(
|
|
12060
12610
|
"L2 isolation is process-level only; no TEE available"
|
|
12061
12611
|
);
|
|
12062
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
12063
|
-
degradations.push(
|
|
12064
|
-
"L3 proofs are commitment-based only; ZK proofs not yet available"
|
|
12065
|
-
);
|
|
12066
|
-
}
|
|
12067
12612
|
return toolResult({
|
|
12068
12613
|
attestation: {
|
|
12069
12614
|
environment_type: config.execution.environment,
|
|
@@ -12089,7 +12634,7 @@ async function createSanctuaryServer(options) {
|
|
|
12089
12634
|
l1_state_encrypted: true,
|
|
12090
12635
|
l2_execution_isolated: false,
|
|
12091
12636
|
l2_isolation_type: "process-level",
|
|
12092
|
-
l3_proofs_available:
|
|
12637
|
+
l3_proofs_available: true,
|
|
12093
12638
|
l4_reputation_active: true,
|
|
12094
12639
|
overall_level: "mvs",
|
|
12095
12640
|
degradations
|
|
@@ -12112,14 +12657,6 @@ async function createSanctuaryServer(options) {
|
|
|
12112
12657
|
severity: "warning",
|
|
12113
12658
|
mitigation: "TEE support planned for a future release"
|
|
12114
12659
|
});
|
|
12115
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
12116
|
-
degradations.push({
|
|
12117
|
-
layer: "l3",
|
|
12118
|
-
description: "Commitment schemes only (no ZK proofs)",
|
|
12119
|
-
severity: "info",
|
|
12120
|
-
mitigation: "ZK proof support planned for v0.2.0"
|
|
12121
|
-
});
|
|
12122
|
-
}
|
|
12123
12660
|
return toolResult({
|
|
12124
12661
|
status: degradations.some((d) => d.severity === "critical") ? "compromised" : degradations.some((d) => d.severity === "warning") ? "degraded" : "healthy",
|
|
12125
12662
|
storage_bytes: storageSizeBytes,
|
|
@@ -12138,7 +12675,7 @@ async function createSanctuaryServer(options) {
|
|
|
12138
12675
|
last_attestation: (/* @__PURE__ */ new Date()).toISOString()
|
|
12139
12676
|
},
|
|
12140
12677
|
l3: {
|
|
12141
|
-
status:
|
|
12678
|
+
status: "active",
|
|
12142
12679
|
proof_system: config.disclosure.proof_system,
|
|
12143
12680
|
circuits_loaded: 0,
|
|
12144
12681
|
proofs_generated_total: 0
|