@sanctuary-framework/mcp-server 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1919 -1352
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1919 -1352
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1920 -1351
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +1918 -1352
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3638,6 +3638,8 @@ var DEFAULT_POLICY = {
|
|
|
3638
3638
|
"handshake_respond",
|
|
3639
3639
|
"handshake_complete",
|
|
3640
3640
|
"handshake_status",
|
|
3641
|
+
"handshake_exchange",
|
|
3642
|
+
"handshake_verify_attestation",
|
|
3641
3643
|
"reputation_query_weighted",
|
|
3642
3644
|
"federation_peers",
|
|
3643
3645
|
"federation_trust_evaluate",
|
|
@@ -3807,6 +3809,8 @@ tier3_always_allow:
|
|
|
3807
3809
|
- handshake_respond
|
|
3808
3810
|
- handshake_complete
|
|
3809
3811
|
- handshake_status
|
|
3812
|
+
- handshake_exchange
|
|
3813
|
+
- handshake_verify_attestation
|
|
3810
3814
|
- reputation_query_weighted
|
|
3811
3815
|
- federation_peers
|
|
3812
3816
|
- federation_trust_evaluate
|
|
@@ -4068,174 +4072,255 @@ function generateLoginHTML(options) {
|
|
|
4068
4072
|
return `<!DOCTYPE html>
|
|
4069
4073
|
<html lang="en">
|
|
4070
4074
|
<head>
|
|
4071
|
-
<meta charset="
|
|
4072
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
4073
|
-
<title>Sanctuary
|
|
4074
|
-
<
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4075
|
+
<meta charset="UTF-8">
|
|
4076
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4077
|
+
<title>Sanctuary Dashboard</title>
|
|
4078
|
+
<style>
|
|
4079
|
+
:root {
|
|
4080
|
+
--bg: #0d1117;
|
|
4081
|
+
--surface: #161b22;
|
|
4082
|
+
--border: #30363d;
|
|
4083
|
+
--text-primary: #e6edf3;
|
|
4084
|
+
--text-secondary: #8b949e;
|
|
4085
|
+
--green: #3fb950;
|
|
4086
|
+
--amber: #d29922;
|
|
4087
|
+
--red: #f85149;
|
|
4088
|
+
--blue: #58a6ff;
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
* {
|
|
4092
|
+
margin: 0;
|
|
4093
|
+
padding: 0;
|
|
4094
|
+
box-sizing: border-box;
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
body {
|
|
4098
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
4099
|
+
background-color: var(--bg);
|
|
4100
|
+
color: var(--text-primary);
|
|
4101
|
+
min-height: 100vh;
|
|
4102
|
+
display: flex;
|
|
4103
|
+
align-items: center;
|
|
4104
|
+
justify-content: center;
|
|
4105
|
+
}
|
|
4106
|
+
|
|
4107
|
+
.login-container {
|
|
4108
|
+
width: 100%;
|
|
4109
|
+
max-width: 400px;
|
|
4110
|
+
padding: 20px;
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
.login-card {
|
|
4114
|
+
background-color: var(--surface);
|
|
4115
|
+
border: 1px solid var(--border);
|
|
4116
|
+
border-radius: 8px;
|
|
4117
|
+
padding: 40px 32px;
|
|
4118
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
.login-header {
|
|
4122
|
+
display: flex;
|
|
4123
|
+
align-items: center;
|
|
4124
|
+
gap: 12px;
|
|
4125
|
+
margin-bottom: 32px;
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4128
|
+
.logo {
|
|
4129
|
+
font-size: 24px;
|
|
4130
|
+
font-weight: 700;
|
|
4131
|
+
color: var(--blue);
|
|
4132
|
+
}
|
|
4133
|
+
|
|
4134
|
+
.logo-text {
|
|
4135
|
+
display: flex;
|
|
4136
|
+
flex-direction: column;
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
.logo-text .title {
|
|
4140
|
+
font-size: 18px;
|
|
4141
|
+
font-weight: 600;
|
|
4142
|
+
letter-spacing: -0.5px;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
.logo-text .version {
|
|
4146
|
+
font-size: 12px;
|
|
4147
|
+
color: var(--text-secondary);
|
|
4148
|
+
margin-top: 2px;
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4151
|
+
.form-group {
|
|
4152
|
+
margin-bottom: 24px;
|
|
4153
|
+
}
|
|
4154
|
+
|
|
4155
|
+
label {
|
|
4156
|
+
display: block;
|
|
4157
|
+
font-size: 14px;
|
|
4158
|
+
font-weight: 500;
|
|
4159
|
+
margin-bottom: 8px;
|
|
4160
|
+
color: var(--text-primary);
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4163
|
+
input[type="text"],
|
|
4164
|
+
input[type="password"] {
|
|
4165
|
+
width: 100%;
|
|
4166
|
+
padding: 10px 12px;
|
|
4167
|
+
background-color: var(--bg);
|
|
4168
|
+
border: 1px solid var(--border);
|
|
4169
|
+
border-radius: 6px;
|
|
4170
|
+
color: var(--text-primary);
|
|
4171
|
+
font-size: 14px;
|
|
4172
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4173
|
+
transition: border-color 0.2s;
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
input[type="text"]:focus,
|
|
4177
|
+
input[type="password"]:focus {
|
|
4178
|
+
outline: none;
|
|
4179
|
+
border-color: var(--blue);
|
|
4180
|
+
box-shadow: 0 0 0 2px rgba(88, 166, 255, 0.1);
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
.error-message {
|
|
4184
|
+
display: none;
|
|
4185
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
4186
|
+
border: 1px solid var(--red);
|
|
4187
|
+
color: #ff9999;
|
|
4188
|
+
padding: 12px;
|
|
4189
|
+
border-radius: 6px;
|
|
4190
|
+
font-size: 13px;
|
|
4191
|
+
margin-bottom: 20px;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
.error-message.show {
|
|
4195
|
+
display: block;
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
button {
|
|
4199
|
+
width: 100%;
|
|
4200
|
+
padding: 10px 16px;
|
|
4201
|
+
background-color: var(--blue);
|
|
4202
|
+
color: var(--bg);
|
|
4203
|
+
border: none;
|
|
4204
|
+
border-radius: 6px;
|
|
4205
|
+
font-size: 14px;
|
|
4206
|
+
font-weight: 600;
|
|
4207
|
+
cursor: pointer;
|
|
4208
|
+
transition: background-color 0.2s;
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
button:hover {
|
|
4212
|
+
background-color: #79c0ff;
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
button:active {
|
|
4216
|
+
background-color: #4184e4;
|
|
4217
|
+
}
|
|
4218
|
+
|
|
4219
|
+
button:disabled {
|
|
4220
|
+
background-color: var(--text-secondary);
|
|
4221
|
+
cursor: not-allowed;
|
|
4222
|
+
opacity: 0.5;
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
.info-text {
|
|
4226
|
+
font-size: 12px;
|
|
4227
|
+
color: var(--text-secondary);
|
|
4228
|
+
margin-top: 16px;
|
|
4229
|
+
text-align: center;
|
|
4230
|
+
}
|
|
4231
|
+
</style>
|
|
4185
4232
|
</head>
|
|
4186
4233
|
<body>
|
|
4187
|
-
<div class="login-container">
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4234
|
+
<div class="login-container">
|
|
4235
|
+
<div class="login-card">
|
|
4236
|
+
<div class="login-header">
|
|
4237
|
+
<div class="logo">\u25C6</div>
|
|
4238
|
+
<div class="logo-text">
|
|
4239
|
+
<div class="title">SANCTUARY</div>
|
|
4240
|
+
<div class="version">v${options.serverVersion}</div>
|
|
4241
|
+
</div>
|
|
4242
|
+
</div>
|
|
4243
|
+
|
|
4244
|
+
<div id="error-message" class="error-message"></div>
|
|
4245
|
+
|
|
4246
|
+
<form id="login-form">
|
|
4247
|
+
<div class="form-group">
|
|
4248
|
+
<label for="auth-token">Bearer Token</label>
|
|
4249
|
+
<input
|
|
4250
|
+
type="text"
|
|
4251
|
+
id="auth-token"
|
|
4252
|
+
name="token"
|
|
4253
|
+
placeholder="Paste your session token..."
|
|
4254
|
+
autocomplete="off"
|
|
4255
|
+
spellcheck="false"
|
|
4256
|
+
required
|
|
4257
|
+
/>
|
|
4258
|
+
</div>
|
|
4259
|
+
|
|
4260
|
+
<button type="submit" id="login-button">Open Dashboard</button>
|
|
4261
|
+
</form>
|
|
4262
|
+
|
|
4263
|
+
<div class="info-text">
|
|
4264
|
+
Session tokens expire after 1 hour of inactivity
|
|
4265
|
+
</div>
|
|
4266
|
+
</div>
|
|
4200
4267
|
</div>
|
|
4201
|
-
|
|
4202
|
-
<script>
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4268
|
+
|
|
4269
|
+
<script>
|
|
4270
|
+
const loginForm = document.getElementById('login-form');
|
|
4271
|
+
const authTokenInput = document.getElementById('auth-token');
|
|
4272
|
+
const errorMessage = document.getElementById('error-message');
|
|
4273
|
+
const loginButton = document.getElementById('login-button');
|
|
4274
|
+
|
|
4275
|
+
loginForm.addEventListener('submit', async (e) => {
|
|
4276
|
+
e.preventDefault();
|
|
4277
|
+
const token = authTokenInput.value.trim();
|
|
4278
|
+
|
|
4279
|
+
if (!token) {
|
|
4280
|
+
showError('Token is required');
|
|
4281
|
+
return;
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
loginButton.disabled = true;
|
|
4285
|
+
loginButton.textContent = 'Verifying...';
|
|
4286
|
+
errorMessage.classList.remove('show');
|
|
4287
|
+
|
|
4288
|
+
try {
|
|
4289
|
+
const response = await fetch('/auth/session', {
|
|
4290
|
+
method: 'POST',
|
|
4291
|
+
headers: {
|
|
4292
|
+
'Content-Type': 'application/json',
|
|
4293
|
+
'Authorization': 'Bearer ' + token,
|
|
4294
|
+
},
|
|
4295
|
+
body: JSON.stringify({ token }),
|
|
4296
|
+
});
|
|
4297
|
+
|
|
4298
|
+
if (response.ok) {
|
|
4299
|
+
const data = await response.json();
|
|
4300
|
+
sessionStorage.setItem('authToken', token);
|
|
4301
|
+
window.location.href = '/dashboard';
|
|
4302
|
+
} else if (response.status === 401) {
|
|
4303
|
+
showError('Invalid token. Please check and try again.');
|
|
4304
|
+
} else {
|
|
4305
|
+
showError('Authentication failed. Please try again.');
|
|
4306
|
+
}
|
|
4307
|
+
} catch (err) {
|
|
4308
|
+
showError('Connection error. Please check your network.');
|
|
4309
|
+
} finally {
|
|
4310
|
+
loginButton.disabled = false;
|
|
4311
|
+
loginButton.textContent = 'Open Dashboard';
|
|
4312
|
+
}
|
|
4216
4313
|
});
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
'; path=/; SameSite=Strict; max-age=' + maxAge;
|
|
4228
|
-
// Reload to enter the dashboard
|
|
4229
|
-
window.location.reload();
|
|
4230
|
-
} catch (err) {
|
|
4231
|
-
errEl.textContent = err.message || 'Authentication failed. Check your token.';
|
|
4232
|
-
errEl.style.display = 'block';
|
|
4233
|
-
btn.disabled = false;
|
|
4234
|
-
btn.textContent = 'Open Dashboard';
|
|
4235
|
-
}
|
|
4236
|
-
return false;
|
|
4237
|
-
}
|
|
4238
|
-
</script>
|
|
4314
|
+
|
|
4315
|
+
function showError(message) {
|
|
4316
|
+
errorMessage.textContent = message;
|
|
4317
|
+
errorMessage.classList.add('show');
|
|
4318
|
+
}
|
|
4319
|
+
|
|
4320
|
+
authTokenInput.addEventListener('input', () => {
|
|
4321
|
+
errorMessage.classList.remove('show');
|
|
4322
|
+
});
|
|
4323
|
+
</script>
|
|
4239
4324
|
</body>
|
|
4240
4325
|
</html>`;
|
|
4241
4326
|
}
|
|
@@ -4243,1412 +4328,1648 @@ function generateDashboardHTML(options) {
|
|
|
4243
4328
|
return `<!DOCTYPE html>
|
|
4244
4329
|
<html lang="en">
|
|
4245
4330
|
<head>
|
|
4246
|
-
<meta charset="
|
|
4247
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
4248
|
-
<title>Sanctuary
|
|
4249
|
-
<
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4331
|
+
<meta charset="UTF-8">
|
|
4332
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4333
|
+
<title>Sanctuary Dashboard</title>
|
|
4334
|
+
<style>
|
|
4335
|
+
:root {
|
|
4336
|
+
--bg: #0d1117;
|
|
4337
|
+
--surface: #161b22;
|
|
4338
|
+
--border: #30363d;
|
|
4339
|
+
--text-primary: #e6edf3;
|
|
4340
|
+
--text-secondary: #8b949e;
|
|
4341
|
+
--green: #3fb950;
|
|
4342
|
+
--amber: #d29922;
|
|
4343
|
+
--red: #f85149;
|
|
4344
|
+
--blue: #58a6ff;
|
|
4345
|
+
--success: #3fb950;
|
|
4346
|
+
--warning: #d29922;
|
|
4347
|
+
--error: #f85149;
|
|
4348
|
+
--muted: #21262d;
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
* {
|
|
4352
|
+
margin: 0;
|
|
4353
|
+
padding: 0;
|
|
4354
|
+
box-sizing: border-box;
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
html, body {
|
|
4358
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
4359
|
+
background-color: var(--bg);
|
|
4360
|
+
color: var(--text-primary);
|
|
4361
|
+
height: 100%;
|
|
4362
|
+
overflow: hidden;
|
|
4363
|
+
}
|
|
4364
|
+
|
|
4365
|
+
body {
|
|
4366
|
+
display: flex;
|
|
4367
|
+
flex-direction: column;
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4370
|
+
/* Status Bar */
|
|
4371
|
+
.status-bar {
|
|
4372
|
+
position: fixed;
|
|
4373
|
+
top: 0;
|
|
4374
|
+
left: 0;
|
|
4375
|
+
right: 0;
|
|
4376
|
+
height: 56px;
|
|
4377
|
+
background-color: var(--surface);
|
|
4378
|
+
border-bottom: 1px solid var(--border);
|
|
4379
|
+
display: flex;
|
|
4380
|
+
align-items: center;
|
|
4381
|
+
padding: 0 24px;
|
|
4382
|
+
gap: 24px;
|
|
4383
|
+
z-index: 100;
|
|
4384
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
.status-bar-left {
|
|
4388
|
+
display: flex;
|
|
4389
|
+
align-items: center;
|
|
4390
|
+
gap: 12px;
|
|
4391
|
+
flex: 0 0 auto;
|
|
4392
|
+
}
|
|
4271
4393
|
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
body {
|
|
4279
|
-
font-family: var(--sans);
|
|
4280
|
-
background: var(--bg);
|
|
4281
|
-
color: var(--text-primary);
|
|
4282
|
-
display: flex;
|
|
4283
|
-
flex-direction: column;
|
|
4284
|
-
}
|
|
4285
|
-
|
|
4286
|
-
/* \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 */
|
|
4394
|
+
.logo-icon {
|
|
4395
|
+
font-size: 20px;
|
|
4396
|
+
color: var(--blue);
|
|
4397
|
+
font-weight: 700;
|
|
4398
|
+
}
|
|
4287
4399
|
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
right: 0;
|
|
4293
|
-
height: 56px;
|
|
4294
|
-
background: var(--surface);
|
|
4295
|
-
border-bottom: 1px solid var(--border);
|
|
4296
|
-
display: flex;
|
|
4297
|
-
align-items: center;
|
|
4298
|
-
padding: 0 20px;
|
|
4299
|
-
gap: 24px;
|
|
4300
|
-
z-index: 1000;
|
|
4301
|
-
}
|
|
4400
|
+
.logo-info {
|
|
4401
|
+
display: flex;
|
|
4402
|
+
flex-direction: column;
|
|
4403
|
+
}
|
|
4302
4404
|
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4405
|
+
.logo-title {
|
|
4406
|
+
font-size: 13px;
|
|
4407
|
+
font-weight: 600;
|
|
4408
|
+
line-height: 1;
|
|
4409
|
+
color: var(--text-primary);
|
|
4410
|
+
}
|
|
4309
4411
|
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
}
|
|
4412
|
+
.logo-version {
|
|
4413
|
+
font-size: 11px;
|
|
4414
|
+
color: var(--text-secondary);
|
|
4415
|
+
margin-top: 2px;
|
|
4416
|
+
}
|
|
4316
4417
|
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4418
|
+
.status-bar-center {
|
|
4419
|
+
flex: 1;
|
|
4420
|
+
display: flex;
|
|
4421
|
+
justify-content: center;
|
|
4422
|
+
}
|
|
4320
4423
|
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4424
|
+
.sovereignty-badge {
|
|
4425
|
+
display: flex;
|
|
4426
|
+
align-items: center;
|
|
4427
|
+
gap: 8px;
|
|
4428
|
+
padding: 8px 16px;
|
|
4429
|
+
background-color: rgba(63, 185, 80, 0.1);
|
|
4430
|
+
border: 1px solid rgba(63, 185, 80, 0.3);
|
|
4431
|
+
border-radius: 6px;
|
|
4432
|
+
font-size: 13px;
|
|
4433
|
+
font-weight: 500;
|
|
4434
|
+
}
|
|
4326
4435
|
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
justify-content: center;
|
|
4332
|
-
}
|
|
4333
|
-
|
|
4334
|
-
.sovereignty-badge {
|
|
4335
|
-
display: flex;
|
|
4336
|
-
align-items: center;
|
|
4337
|
-
gap: 8px;
|
|
4338
|
-
padding: 6px 12px;
|
|
4339
|
-
background: rgba(88, 166, 255, 0.1);
|
|
4340
|
-
border: 1px solid var(--blue);
|
|
4341
|
-
border-radius: 20px;
|
|
4342
|
-
font-size: 13px;
|
|
4343
|
-
font-weight: 600;
|
|
4344
|
-
}
|
|
4436
|
+
.sovereignty-badge.degraded {
|
|
4437
|
+
background-color: rgba(210, 153, 34, 0.1);
|
|
4438
|
+
border-color: rgba(210, 153, 34, 0.3);
|
|
4439
|
+
}
|
|
4345
4440
|
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
width: 28px;
|
|
4351
|
-
height: 28px;
|
|
4352
|
-
border-radius: 50%;
|
|
4353
|
-
font-family: var(--mono);
|
|
4354
|
-
font-weight: 700;
|
|
4355
|
-
font-size: 12px;
|
|
4356
|
-
background: var(--blue);
|
|
4357
|
-
color: var(--bg);
|
|
4358
|
-
}
|
|
4441
|
+
.sovereignty-badge.inactive {
|
|
4442
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
4443
|
+
border-color: rgba(248, 81, 73, 0.3);
|
|
4444
|
+
}
|
|
4359
4445
|
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4446
|
+
.sovereignty-score {
|
|
4447
|
+
font-weight: 700;
|
|
4448
|
+
color: var(--green);
|
|
4449
|
+
}
|
|
4363
4450
|
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4451
|
+
.sovereignty-badge.degraded .sovereignty-score {
|
|
4452
|
+
color: var(--amber);
|
|
4453
|
+
}
|
|
4367
4454
|
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4455
|
+
.sovereignty-badge.inactive .sovereignty-score {
|
|
4456
|
+
color: var(--red);
|
|
4457
|
+
}
|
|
4371
4458
|
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4459
|
+
.status-bar-right {
|
|
4460
|
+
display: flex;
|
|
4461
|
+
align-items: center;
|
|
4462
|
+
gap: 16px;
|
|
4463
|
+
flex: 0 0 auto;
|
|
4464
|
+
}
|
|
4378
4465
|
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
}
|
|
4466
|
+
.status-item {
|
|
4467
|
+
display: flex;
|
|
4468
|
+
align-items: center;
|
|
4469
|
+
gap: 6px;
|
|
4470
|
+
font-size: 12px;
|
|
4471
|
+
color: var(--text-secondary);
|
|
4472
|
+
}
|
|
4387
4473
|
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4474
|
+
.status-item strong {
|
|
4475
|
+
color: var(--text-primary);
|
|
4476
|
+
font-weight: 500;
|
|
4477
|
+
}
|
|
4392
4478
|
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
font-family: var(--mono);
|
|
4400
|
-
}
|
|
4479
|
+
.status-dot {
|
|
4480
|
+
width: 8px;
|
|
4481
|
+
height: 8px;
|
|
4482
|
+
border-radius: 50%;
|
|
4483
|
+
background-color: var(--green);
|
|
4484
|
+
}
|
|
4401
4485
|
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
border-radius: 50%;
|
|
4406
|
-
background: var(--green);
|
|
4407
|
-
animation: pulse 2s ease-in-out infinite;
|
|
4408
|
-
}
|
|
4486
|
+
.status-dot.disconnected {
|
|
4487
|
+
background-color: var(--red);
|
|
4488
|
+
}
|
|
4409
4489
|
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4490
|
+
.pending-badge {
|
|
4491
|
+
display: flex;
|
|
4492
|
+
align-items: center;
|
|
4493
|
+
gap: 6px;
|
|
4494
|
+
padding: 4px 8px;
|
|
4495
|
+
background-color: var(--blue);
|
|
4496
|
+
color: var(--bg);
|
|
4497
|
+
border-radius: 4px;
|
|
4498
|
+
font-size: 11px;
|
|
4499
|
+
font-weight: 600;
|
|
4500
|
+
cursor: pointer;
|
|
4501
|
+
}
|
|
4414
4502
|
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4503
|
+
/* Main Content */
|
|
4504
|
+
.main-content {
|
|
4505
|
+
flex: 1;
|
|
4506
|
+
margin-top: 56px;
|
|
4507
|
+
overflow-y: auto;
|
|
4508
|
+
padding: 24px;
|
|
4509
|
+
}
|
|
4419
4510
|
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
min-width: 24px;
|
|
4425
|
-
height: 24px;
|
|
4426
|
-
padding: 0 6px;
|
|
4427
|
-
background: var(--red);
|
|
4428
|
-
color: white;
|
|
4429
|
-
border-radius: 12px;
|
|
4430
|
-
font-size: 11px;
|
|
4431
|
-
font-weight: 700;
|
|
4432
|
-
animation: pulse 1s ease-in-out infinite;
|
|
4433
|
-
}
|
|
4511
|
+
.grid {
|
|
4512
|
+
display: grid;
|
|
4513
|
+
gap: 20px;
|
|
4514
|
+
}
|
|
4434
4515
|
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4516
|
+
/* Row 1: Sovereignty Layers */
|
|
4517
|
+
.sovereignty-layers {
|
|
4518
|
+
display: grid;
|
|
4519
|
+
grid-template-columns: repeat(4, 1fr);
|
|
4520
|
+
gap: 16px;
|
|
4521
|
+
}
|
|
4438
4522
|
|
|
4439
|
-
|
|
4523
|
+
.layer-card {
|
|
4524
|
+
background-color: var(--surface);
|
|
4525
|
+
border: 1px solid var(--border);
|
|
4526
|
+
border-radius: 8px;
|
|
4527
|
+
padding: 20px;
|
|
4528
|
+
display: flex;
|
|
4529
|
+
flex-direction: column;
|
|
4530
|
+
gap: 12px;
|
|
4531
|
+
}
|
|
4440
4532
|
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
overflow: hidden;
|
|
4446
|
-
}
|
|
4533
|
+
.layer-card.degraded {
|
|
4534
|
+
border-color: var(--amber);
|
|
4535
|
+
background-color: rgba(210, 153, 34, 0.05);
|
|
4536
|
+
}
|
|
4447
4537
|
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
border-right: 1px solid var(--border);
|
|
4453
|
-
overflow: hidden;
|
|
4454
|
-
}
|
|
4538
|
+
.layer-card.inactive {
|
|
4539
|
+
border-color: var(--red);
|
|
4540
|
+
background-color: rgba(248, 81, 73, 0.05);
|
|
4541
|
+
}
|
|
4455
4542
|
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
font-weight: 600;
|
|
4464
|
-
text-transform: uppercase;
|
|
4465
|
-
letter-spacing: 0.5px;
|
|
4466
|
-
color: var(--text-secondary);
|
|
4467
|
-
}
|
|
4543
|
+
.layer-name {
|
|
4544
|
+
font-size: 12px;
|
|
4545
|
+
font-weight: 600;
|
|
4546
|
+
color: var(--text-secondary);
|
|
4547
|
+
text-transform: uppercase;
|
|
4548
|
+
letter-spacing: 0.5px;
|
|
4549
|
+
}
|
|
4468
4550
|
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
}
|
|
4551
|
+
.layer-title {
|
|
4552
|
+
font-size: 14px;
|
|
4553
|
+
font-weight: 600;
|
|
4554
|
+
color: var(--text-primary);
|
|
4555
|
+
}
|
|
4475
4556
|
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4557
|
+
.layer-status {
|
|
4558
|
+
display: inline-flex;
|
|
4559
|
+
align-items: center;
|
|
4560
|
+
gap: 6px;
|
|
4561
|
+
padding: 4px 8px;
|
|
4562
|
+
background-color: rgba(63, 185, 80, 0.15);
|
|
4563
|
+
color: var(--green);
|
|
4564
|
+
border-radius: 4px;
|
|
4565
|
+
font-size: 11px;
|
|
4566
|
+
font-weight: 600;
|
|
4567
|
+
width: fit-content;
|
|
4568
|
+
}
|
|
4481
4569
|
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
font-family: var(--mono);
|
|
4487
|
-
cursor: pointer;
|
|
4488
|
-
transition: background 0.15s;
|
|
4489
|
-
display: flex;
|
|
4490
|
-
align-items: flex-start;
|
|
4491
|
-
gap: 10px;
|
|
4492
|
-
}
|
|
4570
|
+
.layer-card.degraded .layer-status {
|
|
4571
|
+
background-color: rgba(210, 153, 34, 0.15);
|
|
4572
|
+
color: var(--amber);
|
|
4573
|
+
}
|
|
4493
4574
|
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4575
|
+
.layer-card.inactive .layer-status {
|
|
4576
|
+
background-color: rgba(248, 81, 73, 0.15);
|
|
4577
|
+
color: var(--red);
|
|
4578
|
+
}
|
|
4497
4579
|
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4580
|
+
.layer-detail {
|
|
4581
|
+
font-size: 12px;
|
|
4582
|
+
color: var(--text-secondary);
|
|
4583
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4584
|
+
padding: 8px;
|
|
4585
|
+
background-color: var(--bg);
|
|
4586
|
+
border-radius: 4px;
|
|
4587
|
+
border-left: 2px solid var(--blue);
|
|
4588
|
+
}
|
|
4506
4589
|
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4590
|
+
/* Row 2: Info Cards */
|
|
4591
|
+
.info-cards {
|
|
4592
|
+
display: grid;
|
|
4593
|
+
grid-template-columns: repeat(3, 1fr);
|
|
4594
|
+
gap: 16px;
|
|
4595
|
+
}
|
|
4511
4596
|
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4597
|
+
.info-card {
|
|
4598
|
+
background-color: var(--surface);
|
|
4599
|
+
border: 1px solid var(--border);
|
|
4600
|
+
border-radius: 8px;
|
|
4601
|
+
padding: 20px;
|
|
4602
|
+
}
|
|
4517
4603
|
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4604
|
+
.card-header {
|
|
4605
|
+
font-size: 12px;
|
|
4606
|
+
font-weight: 600;
|
|
4607
|
+
color: var(--text-secondary);
|
|
4608
|
+
text-transform: uppercase;
|
|
4609
|
+
letter-spacing: 0.5px;
|
|
4610
|
+
margin-bottom: 16px;
|
|
4611
|
+
}
|
|
4524
4612
|
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
font-weight: 700;
|
|
4533
|
-
border-radius: 3px;
|
|
4534
|
-
text-transform: uppercase;
|
|
4535
|
-
flex: 0 0 auto;
|
|
4536
|
-
}
|
|
4613
|
+
.card-row {
|
|
4614
|
+
display: flex;
|
|
4615
|
+
justify-content: space-between;
|
|
4616
|
+
align-items: center;
|
|
4617
|
+
margin-bottom: 12px;
|
|
4618
|
+
font-size: 13px;
|
|
4619
|
+
}
|
|
4537
4620
|
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
}
|
|
4621
|
+
.card-row:last-child {
|
|
4622
|
+
margin-bottom: 0;
|
|
4623
|
+
}
|
|
4542
4624
|
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
}
|
|
4625
|
+
.card-label {
|
|
4626
|
+
color: var(--text-secondary);
|
|
4627
|
+
}
|
|
4547
4628
|
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4629
|
+
.card-value {
|
|
4630
|
+
color: var(--text-primary);
|
|
4631
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4632
|
+
font-weight: 500;
|
|
4633
|
+
}
|
|
4552
4634
|
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4635
|
+
.identity-badge {
|
|
4636
|
+
display: inline-flex;
|
|
4637
|
+
align-items: center;
|
|
4638
|
+
gap: 4px;
|
|
4639
|
+
padding: 2px 6px;
|
|
4640
|
+
background-color: rgba(88, 166, 255, 0.15);
|
|
4641
|
+
color: var(--blue);
|
|
4642
|
+
border-radius: 3px;
|
|
4643
|
+
font-size: 10px;
|
|
4644
|
+
font-weight: 600;
|
|
4645
|
+
text-transform: uppercase;
|
|
4646
|
+
}
|
|
4557
4647
|
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4648
|
+
.trust-tier-badge {
|
|
4649
|
+
display: inline-flex;
|
|
4650
|
+
align-items: center;
|
|
4651
|
+
gap: 4px;
|
|
4652
|
+
padding: 2px 6px;
|
|
4653
|
+
background-color: rgba(63, 185, 80, 0.15);
|
|
4654
|
+
color: var(--green);
|
|
4655
|
+
border-radius: 3px;
|
|
4656
|
+
font-size: 10px;
|
|
4657
|
+
font-weight: 600;
|
|
4658
|
+
}
|
|
4561
4659
|
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4660
|
+
.truncated {
|
|
4661
|
+
max-width: 200px;
|
|
4662
|
+
overflow: hidden;
|
|
4663
|
+
text-overflow: ellipsis;
|
|
4664
|
+
white-space: nowrap;
|
|
4665
|
+
}
|
|
4565
4666
|
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4667
|
+
/* Row 3: SHR & Activity */
|
|
4668
|
+
.main-panels {
|
|
4669
|
+
display: grid;
|
|
4670
|
+
grid-template-columns: 1fr 1fr;
|
|
4671
|
+
gap: 16px;
|
|
4672
|
+
min-height: 400px;
|
|
4673
|
+
}
|
|
4571
4674
|
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4675
|
+
.panel {
|
|
4676
|
+
background-color: var(--surface);
|
|
4677
|
+
border: 1px solid var(--border);
|
|
4678
|
+
border-radius: 8px;
|
|
4679
|
+
display: flex;
|
|
4680
|
+
flex-direction: column;
|
|
4681
|
+
overflow: hidden;
|
|
4682
|
+
}
|
|
4580
4683
|
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
}
|
|
4684
|
+
.panel-header {
|
|
4685
|
+
padding: 16px 20px;
|
|
4686
|
+
border-bottom: 1px solid var(--border);
|
|
4687
|
+
display: flex;
|
|
4688
|
+
justify-content: space-between;
|
|
4689
|
+
align-items: center;
|
|
4690
|
+
}
|
|
4589
4691
|
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4692
|
+
.panel-title {
|
|
4693
|
+
font-size: 14px;
|
|
4694
|
+
font-weight: 600;
|
|
4695
|
+
color: var(--text-primary);
|
|
4696
|
+
}
|
|
4594
4697
|
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4698
|
+
.panel-action {
|
|
4699
|
+
background: none;
|
|
4700
|
+
border: none;
|
|
4701
|
+
color: var(--blue);
|
|
4702
|
+
cursor: pointer;
|
|
4703
|
+
font-size: 12px;
|
|
4704
|
+
padding: 0;
|
|
4705
|
+
font-weight: 500;
|
|
4706
|
+
transition: color 0.2s;
|
|
4707
|
+
}
|
|
4598
4708
|
|
|
4599
|
-
|
|
4709
|
+
.panel-action:hover {
|
|
4710
|
+
color: #79c0ff;
|
|
4711
|
+
}
|
|
4600
4712
|
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
overflow: hidden;
|
|
4607
|
-
}
|
|
4713
|
+
.panel-content {
|
|
4714
|
+
flex: 1;
|
|
4715
|
+
overflow-y: auto;
|
|
4716
|
+
padding: 20px;
|
|
4717
|
+
}
|
|
4608
4718
|
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
color: var(--text-secondary);
|
|
4617
|
-
display: flex;
|
|
4618
|
-
align-items: center;
|
|
4619
|
-
gap: 8px;
|
|
4620
|
-
}
|
|
4719
|
+
/* SHR Viewer */
|
|
4720
|
+
.shr-json {
|
|
4721
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4722
|
+
font-size: 12px;
|
|
4723
|
+
line-height: 1.6;
|
|
4724
|
+
color: var(--text-secondary);
|
|
4725
|
+
}
|
|
4621
4726
|
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
padding: 16px 16px;
|
|
4626
|
-
display: grid;
|
|
4627
|
-
grid-template-columns: 1fr 1fr;
|
|
4628
|
-
gap: 12px;
|
|
4629
|
-
}
|
|
4727
|
+
.shr-section {
|
|
4728
|
+
margin-bottom: 12px;
|
|
4729
|
+
}
|
|
4630
4730
|
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4731
|
+
.shr-section-header {
|
|
4732
|
+
display: flex;
|
|
4733
|
+
align-items: center;
|
|
4734
|
+
gap: 8px;
|
|
4735
|
+
cursor: pointer;
|
|
4736
|
+
font-weight: 600;
|
|
4737
|
+
color: var(--text-primary);
|
|
4738
|
+
padding: 8px;
|
|
4739
|
+
background-color: var(--bg);
|
|
4740
|
+
border-radius: 4px;
|
|
4741
|
+
user-select: none;
|
|
4742
|
+
}
|
|
4640
4743
|
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4744
|
+
.shr-section-header:hover {
|
|
4745
|
+
background-color: var(--muted);
|
|
4746
|
+
}
|
|
4644
4747
|
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4748
|
+
.shr-toggle {
|
|
4749
|
+
width: 16px;
|
|
4750
|
+
height: 16px;
|
|
4751
|
+
display: flex;
|
|
4752
|
+
align-items: center;
|
|
4753
|
+
justify-content: center;
|
|
4754
|
+
font-size: 10px;
|
|
4755
|
+
transition: transform 0.2s;
|
|
4756
|
+
}
|
|
4652
4757
|
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
gap: 6px;
|
|
4657
|
-
font-size: 12px;
|
|
4658
|
-
font-weight: 600;
|
|
4659
|
-
}
|
|
4758
|
+
.shr-section.collapsed .shr-toggle {
|
|
4759
|
+
transform: rotate(-90deg);
|
|
4760
|
+
}
|
|
4660
4761
|
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4762
|
+
.shr-section-content {
|
|
4763
|
+
padding: 8px 16px;
|
|
4764
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
4765
|
+
border-radius: 4px;
|
|
4766
|
+
margin-top: 4px;
|
|
4767
|
+
}
|
|
4664
4768
|
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4769
|
+
.shr-section.collapsed .shr-section-content {
|
|
4770
|
+
display: none;
|
|
4771
|
+
}
|
|
4668
4772
|
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
margin-top: 4px;
|
|
4674
|
-
}
|
|
4773
|
+
.shr-item {
|
|
4774
|
+
display: flex;
|
|
4775
|
+
margin-bottom: 4px;
|
|
4776
|
+
}
|
|
4675
4777
|
|
|
4676
|
-
|
|
4778
|
+
.shr-key {
|
|
4779
|
+
color: var(--blue);
|
|
4780
|
+
margin-right: 8px;
|
|
4781
|
+
min-width: 120px;
|
|
4782
|
+
}
|
|
4677
4783
|
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
bottom: 0;
|
|
4683
|
-
width: 0;
|
|
4684
|
-
background: var(--surface);
|
|
4685
|
-
border-left: 1px solid var(--border);
|
|
4686
|
-
z-index: 999;
|
|
4687
|
-
overflow-y: auto;
|
|
4688
|
-
transition: width 0.3s ease-out;
|
|
4689
|
-
display: flex;
|
|
4690
|
-
flex-direction: column;
|
|
4691
|
-
}
|
|
4784
|
+
.shr-value {
|
|
4785
|
+
color: var(--green);
|
|
4786
|
+
word-break: break-all;
|
|
4787
|
+
}
|
|
4692
4788
|
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4789
|
+
/* Activity Feed */
|
|
4790
|
+
.activity-feed {
|
|
4791
|
+
display: flex;
|
|
4792
|
+
flex-direction: column;
|
|
4793
|
+
gap: 12px;
|
|
4794
|
+
}
|
|
4696
4795
|
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4796
|
+
.activity-item {
|
|
4797
|
+
padding: 12px;
|
|
4798
|
+
background-color: var(--bg);
|
|
4799
|
+
border-left: 2px solid var(--border);
|
|
4800
|
+
border-radius: 4px;
|
|
4801
|
+
font-size: 12px;
|
|
4702
4802
|
}
|
|
4703
|
-
}
|
|
4704
4803
|
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
display: flex;
|
|
4709
|
-
align-items: center;
|
|
4710
|
-
justify-content: space-between;
|
|
4711
|
-
flex: 0 0 auto;
|
|
4712
|
-
}
|
|
4804
|
+
.activity-item.tool-call {
|
|
4805
|
+
border-left-color: var(--blue);
|
|
4806
|
+
}
|
|
4713
4807
|
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
text-transform: uppercase;
|
|
4718
|
-
letter-spacing: 0.5px;
|
|
4719
|
-
color: var(--text-primary);
|
|
4720
|
-
}
|
|
4808
|
+
.activity-item.context-gate {
|
|
4809
|
+
border-left-color: var(--amber);
|
|
4810
|
+
}
|
|
4721
4811
|
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
color: var(--text-secondary);
|
|
4726
|
-
cursor: pointer;
|
|
4727
|
-
font-size: 18px;
|
|
4728
|
-
padding: 0;
|
|
4729
|
-
display: flex;
|
|
4730
|
-
align-items: center;
|
|
4731
|
-
justify-content: center;
|
|
4732
|
-
}
|
|
4812
|
+
.activity-item.injection {
|
|
4813
|
+
border-left-color: var(--red);
|
|
4814
|
+
}
|
|
4733
4815
|
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4816
|
+
.activity-item.protection {
|
|
4817
|
+
border-left-color: var(--green);
|
|
4818
|
+
}
|
|
4737
4819
|
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4820
|
+
.activity-type {
|
|
4821
|
+
font-weight: 600;
|
|
4822
|
+
color: var(--text-primary);
|
|
4823
|
+
margin-bottom: 4px;
|
|
4824
|
+
text-transform: uppercase;
|
|
4825
|
+
font-size: 11px;
|
|
4826
|
+
letter-spacing: 0.5px;
|
|
4827
|
+
}
|
|
4742
4828
|
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
}
|
|
4829
|
+
.activity-content {
|
|
4830
|
+
color: var(--text-secondary);
|
|
4831
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4832
|
+
margin-bottom: 4px;
|
|
4833
|
+
word-break: break-all;
|
|
4834
|
+
}
|
|
4750
4835
|
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
}
|
|
4836
|
+
.activity-time {
|
|
4837
|
+
font-size: 11px;
|
|
4838
|
+
color: var(--text-secondary);
|
|
4839
|
+
}
|
|
4756
4840
|
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4841
|
+
.empty-state {
|
|
4842
|
+
display: flex;
|
|
4843
|
+
align-items: center;
|
|
4844
|
+
justify-content: center;
|
|
4845
|
+
height: 100%;
|
|
4846
|
+
color: var(--text-secondary);
|
|
4847
|
+
font-size: 13px;
|
|
4848
|
+
}
|
|
4764
4849
|
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
font-weight: 700;
|
|
4773
|
-
border-radius: 3px;
|
|
4774
|
-
text-transform: uppercase;
|
|
4775
|
-
color: white;
|
|
4776
|
-
}
|
|
4850
|
+
/* Row 4: Handshake History */
|
|
4851
|
+
.handshake-table {
|
|
4852
|
+
background-color: var(--surface);
|
|
4853
|
+
border: 1px solid var(--border);
|
|
4854
|
+
border-radius: 8px;
|
|
4855
|
+
overflow: hidden;
|
|
4856
|
+
}
|
|
4777
4857
|
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4858
|
+
.table-header {
|
|
4859
|
+
display: grid;
|
|
4860
|
+
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr 1.5fr;
|
|
4861
|
+
gap: 16px;
|
|
4862
|
+
padding: 16px 20px;
|
|
4863
|
+
border-bottom: 1px solid var(--border);
|
|
4864
|
+
background-color: var(--bg);
|
|
4865
|
+
font-size: 12px;
|
|
4866
|
+
font-weight: 600;
|
|
4867
|
+
color: var(--text-secondary);
|
|
4868
|
+
text-transform: uppercase;
|
|
4869
|
+
letter-spacing: 0.5px;
|
|
4870
|
+
}
|
|
4781
4871
|
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4872
|
+
.table-rows {
|
|
4873
|
+
max-height: 300px;
|
|
4874
|
+
overflow-y: auto;
|
|
4875
|
+
}
|
|
4785
4876
|
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4877
|
+
.table-row {
|
|
4878
|
+
display: grid;
|
|
4879
|
+
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr 1.5fr;
|
|
4880
|
+
gap: 16px;
|
|
4881
|
+
padding: 14px 20px;
|
|
4882
|
+
border-bottom: 1px solid var(--border);
|
|
4883
|
+
align-items: center;
|
|
4884
|
+
font-size: 12px;
|
|
4885
|
+
cursor: pointer;
|
|
4886
|
+
transition: background-color 0.2s;
|
|
4887
|
+
}
|
|
4790
4888
|
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
gap: 6px;
|
|
4795
|
-
font-size: 11px;
|
|
4796
|
-
font-family: var(--mono);
|
|
4797
|
-
color: var(--text-secondary);
|
|
4798
|
-
}
|
|
4889
|
+
.table-row:hover {
|
|
4890
|
+
background-color: var(--bg);
|
|
4891
|
+
}
|
|
4799
4892
|
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
background: rgba(48, 54, 61, 0.8);
|
|
4804
|
-
border-radius: 2px;
|
|
4805
|
-
overflow: hidden;
|
|
4806
|
-
}
|
|
4893
|
+
.table-row:last-child {
|
|
4894
|
+
border-bottom: none;
|
|
4895
|
+
}
|
|
4807
4896
|
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
}
|
|
4897
|
+
.table-cell {
|
|
4898
|
+
color: var(--text-secondary);
|
|
4899
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4900
|
+
}
|
|
4813
4901
|
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4902
|
+
.table-cell.strong {
|
|
4903
|
+
color: var(--text-primary);
|
|
4904
|
+
font-weight: 500;
|
|
4905
|
+
}
|
|
4817
4906
|
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4907
|
+
.table-empty {
|
|
4908
|
+
padding: 40px 20px;
|
|
4909
|
+
text-align: center;
|
|
4910
|
+
color: var(--text-secondary);
|
|
4911
|
+
font-size: 13px;
|
|
4912
|
+
}
|
|
4822
4913
|
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4914
|
+
/* Pending Overlay */
|
|
4915
|
+
.pending-overlay {
|
|
4916
|
+
position: fixed;
|
|
4917
|
+
top: 0;
|
|
4918
|
+
right: -400px;
|
|
4919
|
+
width: 400px;
|
|
4920
|
+
height: 100vh;
|
|
4921
|
+
background-color: var(--surface);
|
|
4922
|
+
border-left: 1px solid var(--border);
|
|
4923
|
+
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
|
|
4924
|
+
z-index: 200;
|
|
4925
|
+
transition: right 0.3s ease;
|
|
4926
|
+
display: flex;
|
|
4927
|
+
flex-direction: column;
|
|
4928
|
+
overflow-y: auto;
|
|
4929
|
+
}
|
|
4834
4930
|
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
}
|
|
4931
|
+
.pending-overlay.show {
|
|
4932
|
+
right: 0;
|
|
4933
|
+
}
|
|
4839
4934
|
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4935
|
+
.pending-header {
|
|
4936
|
+
padding: 16px 20px;
|
|
4937
|
+
border-bottom: 1px solid var(--border);
|
|
4938
|
+
font-weight: 600;
|
|
4939
|
+
color: var(--text-primary);
|
|
4940
|
+
}
|
|
4843
4941
|
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4942
|
+
.pending-items {
|
|
4943
|
+
flex: 1;
|
|
4944
|
+
overflow-y: auto;
|
|
4945
|
+
padding: 16px;
|
|
4946
|
+
}
|
|
4848
4947
|
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4948
|
+
.pending-item {
|
|
4949
|
+
background-color: var(--bg);
|
|
4950
|
+
border: 1px solid var(--border);
|
|
4951
|
+
border-radius: 6px;
|
|
4952
|
+
padding: 16px;
|
|
4953
|
+
margin-bottom: 12px;
|
|
4954
|
+
}
|
|
4852
4955
|
|
|
4853
|
-
|
|
4956
|
+
.pending-title {
|
|
4957
|
+
font-weight: 600;
|
|
4958
|
+
color: var(--text-primary);
|
|
4959
|
+
margin-bottom: 8px;
|
|
4960
|
+
word-break: break-word;
|
|
4961
|
+
}
|
|
4854
4962
|
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
border-top: 1px solid var(--border);
|
|
4862
|
-
max-height: 240px;
|
|
4863
|
-
z-index: 500;
|
|
4864
|
-
display: flex;
|
|
4865
|
-
flex-direction: column;
|
|
4866
|
-
transition: max-height 0.3s ease-out;
|
|
4867
|
-
}
|
|
4963
|
+
.pending-countdown {
|
|
4964
|
+
font-size: 12px;
|
|
4965
|
+
color: var(--amber);
|
|
4966
|
+
margin-bottom: 12px;
|
|
4967
|
+
font-weight: 500;
|
|
4968
|
+
}
|
|
4868
4969
|
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4970
|
+
.pending-actions {
|
|
4971
|
+
display: flex;
|
|
4972
|
+
gap: 8px;
|
|
4973
|
+
}
|
|
4872
4974
|
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
color: var(--text-secondary);
|
|
4884
|
-
flex: 0 0 auto;
|
|
4885
|
-
}
|
|
4975
|
+
.pending-btn {
|
|
4976
|
+
flex: 1;
|
|
4977
|
+
padding: 8px 12px;
|
|
4978
|
+
border: none;
|
|
4979
|
+
border-radius: 4px;
|
|
4980
|
+
font-size: 12px;
|
|
4981
|
+
font-weight: 600;
|
|
4982
|
+
cursor: pointer;
|
|
4983
|
+
transition: background-color 0.2s;
|
|
4984
|
+
}
|
|
4886
4985
|
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4986
|
+
.pending-approve {
|
|
4987
|
+
background-color: var(--green);
|
|
4988
|
+
color: var(--bg);
|
|
4989
|
+
}
|
|
4890
4990
|
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4991
|
+
.pending-approve:hover {
|
|
4992
|
+
background-color: #3fa040;
|
|
4993
|
+
}
|
|
4894
4994
|
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
display: flex;
|
|
4900
|
-
flex-direction: column;
|
|
4901
|
-
gap: 10px;
|
|
4902
|
-
}
|
|
4995
|
+
.pending-deny {
|
|
4996
|
+
background-color: var(--red);
|
|
4997
|
+
color: var(--bg);
|
|
4998
|
+
}
|
|
4903
4999
|
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
border-left: 2px solid var(--red);
|
|
4908
|
-
border-radius: 4px;
|
|
4909
|
-
font-size: 11px;
|
|
4910
|
-
color: var(--text-secondary);
|
|
4911
|
-
}
|
|
5000
|
+
.pending-deny:hover {
|
|
5001
|
+
background-color: #e03c3c;
|
|
5002
|
+
}
|
|
4912
5003
|
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
5004
|
+
/* Threat Panel */
|
|
5005
|
+
.threat-panel {
|
|
5006
|
+
background-color: var(--surface);
|
|
5007
|
+
border: 1px solid var(--border);
|
|
5008
|
+
border-radius: 8px;
|
|
5009
|
+
margin-top: 20px;
|
|
5010
|
+
overflow: hidden;
|
|
5011
|
+
}
|
|
4918
5012
|
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
5013
|
+
.threat-header {
|
|
5014
|
+
padding: 16px 20px;
|
|
5015
|
+
border-bottom: 1px solid var(--border);
|
|
5016
|
+
display: flex;
|
|
5017
|
+
justify-content: space-between;
|
|
5018
|
+
align-items: center;
|
|
5019
|
+
cursor: pointer;
|
|
5020
|
+
user-select: none;
|
|
5021
|
+
}
|
|
4925
5022
|
|
|
4926
|
-
|
|
5023
|
+
.threat-title {
|
|
5024
|
+
font-weight: 600;
|
|
5025
|
+
color: var(--text-primary);
|
|
5026
|
+
}
|
|
4927
5027
|
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
5028
|
+
.threat-toggle {
|
|
5029
|
+
font-size: 10px;
|
|
5030
|
+
color: var(--text-secondary);
|
|
5031
|
+
transition: transform 0.2s;
|
|
5032
|
+
}
|
|
4931
5033
|
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
5034
|
+
.threat-panel.collapsed .threat-toggle {
|
|
5035
|
+
transform: rotate(-90deg);
|
|
5036
|
+
}
|
|
4935
5037
|
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
5038
|
+
.threat-content {
|
|
5039
|
+
padding: 16px 20px;
|
|
5040
|
+
max-height: 300px;
|
|
5041
|
+
overflow-y: auto;
|
|
5042
|
+
}
|
|
4940
5043
|
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
5044
|
+
.threat-panel.collapsed .threat-content {
|
|
5045
|
+
display: none;
|
|
5046
|
+
}
|
|
4944
5047
|
|
|
4945
|
-
|
|
5048
|
+
.threat-alert {
|
|
5049
|
+
background-color: rgba(248, 81, 73, 0.1);
|
|
5050
|
+
border: 1px solid var(--red);
|
|
5051
|
+
border-radius: 4px;
|
|
5052
|
+
padding: 12px;
|
|
5053
|
+
margin-bottom: 8px;
|
|
5054
|
+
font-size: 12px;
|
|
5055
|
+
}
|
|
4946
5056
|
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
display: none;
|
|
5057
|
+
.threat-alert:last-child {
|
|
5058
|
+
margin-bottom: 0;
|
|
4950
5059
|
}
|
|
4951
5060
|
|
|
4952
|
-
.
|
|
4953
|
-
|
|
5061
|
+
.threat-type {
|
|
5062
|
+
font-weight: 600;
|
|
5063
|
+
color: var(--red);
|
|
5064
|
+
margin-bottom: 4px;
|
|
5065
|
+
text-transform: uppercase;
|
|
5066
|
+
font-size: 10px;
|
|
5067
|
+
letter-spacing: 0.5px;
|
|
4954
5068
|
}
|
|
4955
|
-
}
|
|
4956
5069
|
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
padding: 0 12px;
|
|
4960
|
-
gap: 12px;
|
|
4961
|
-
height: 48px;
|
|
5070
|
+
.threat-message {
|
|
5071
|
+
color: var(--text-secondary);
|
|
4962
5072
|
}
|
|
4963
5073
|
|
|
4964
|
-
|
|
4965
|
-
|
|
5074
|
+
/* Scrollbar */
|
|
5075
|
+
::-webkit-scrollbar {
|
|
5076
|
+
width: 8px;
|
|
4966
5077
|
}
|
|
4967
5078
|
|
|
4968
|
-
|
|
4969
|
-
|
|
5079
|
+
::-webkit-scrollbar-track {
|
|
5080
|
+
background-color: transparent;
|
|
4970
5081
|
}
|
|
4971
5082
|
|
|
4972
|
-
|
|
4973
|
-
|
|
5083
|
+
::-webkit-scrollbar-thumb {
|
|
5084
|
+
background-color: var(--border);
|
|
5085
|
+
border-radius: 4px;
|
|
4974
5086
|
}
|
|
4975
5087
|
|
|
4976
|
-
|
|
4977
|
-
|
|
5088
|
+
::-webkit-scrollbar-thumb:hover {
|
|
5089
|
+
background-color: var(--text-secondary);
|
|
4978
5090
|
}
|
|
4979
5091
|
|
|
4980
|
-
|
|
4981
|
-
|
|
5092
|
+
/* Responsive */
|
|
5093
|
+
@media (max-width: 1400px) {
|
|
5094
|
+
.sovereignty-layers {
|
|
5095
|
+
grid-template-columns: repeat(2, 1fr);
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5098
|
+
.main-panels {
|
|
5099
|
+
grid-template-columns: 1fr;
|
|
5100
|
+
}
|
|
5101
|
+
|
|
5102
|
+
.pending-overlay {
|
|
5103
|
+
width: 100%;
|
|
5104
|
+
right: -100%;
|
|
5105
|
+
}
|
|
4982
5106
|
}
|
|
4983
5107
|
|
|
4984
|
-
|
|
4985
|
-
|
|
5108
|
+
@media (max-width: 768px) {
|
|
5109
|
+
.status-bar {
|
|
5110
|
+
flex-wrap: wrap;
|
|
5111
|
+
height: auto;
|
|
5112
|
+
padding: 12px;
|
|
5113
|
+
gap: 12px;
|
|
5114
|
+
}
|
|
5115
|
+
|
|
5116
|
+
.status-bar-center {
|
|
5117
|
+
order: 3;
|
|
5118
|
+
flex-basis: 100%;
|
|
5119
|
+
}
|
|
5120
|
+
|
|
5121
|
+
.main-content {
|
|
5122
|
+
margin-top: auto;
|
|
5123
|
+
}
|
|
5124
|
+
|
|
5125
|
+
.info-cards {
|
|
5126
|
+
grid-template-columns: 1fr;
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
.table-header,
|
|
5130
|
+
.table-row {
|
|
5131
|
+
grid-template-columns: 1fr;
|
|
5132
|
+
}
|
|
4986
5133
|
}
|
|
4987
|
-
|
|
4988
|
-
</style>
|
|
5134
|
+
</style>
|
|
4989
5135
|
</head>
|
|
4990
5136
|
<body>
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
<div class="status-bar">
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
<div class="status-bar-center">
|
|
4999
|
-
<div class="sovereignty-badge">
|
|
5000
|
-
<div class="sovereignty-score" id="sovereigntyScore">85</div>
|
|
5001
|
-
<span>Sovereignty Health</span>
|
|
5002
|
-
</div>
|
|
5003
|
-
</div>
|
|
5004
|
-
<div class="status-bar-right">
|
|
5005
|
-
<div class="protections-indicator">
|
|
5006
|
-
<span class="count" id="activeProtections">6</span>/6 protections
|
|
5007
|
-
</div>
|
|
5008
|
-
<div class="uptime">
|
|
5009
|
-
<span id="uptimeText">\u2014</span>
|
|
5010
|
-
</div>
|
|
5011
|
-
<div class="status-dot" id="statusDot"></div>
|
|
5012
|
-
<div class="pending-badge hidden" id="pendingBadge">0</div>
|
|
5013
|
-
</div>
|
|
5014
|
-
</div>
|
|
5015
|
-
|
|
5016
|
-
<!-- Main Layout -->
|
|
5017
|
-
<div class="main-container">
|
|
5018
|
-
<!-- Activity Feed -->
|
|
5019
|
-
<div class="activity-feed">
|
|
5020
|
-
<div class="feed-header">
|
|
5021
|
-
<div class="feed-header-dot"></div>
|
|
5022
|
-
Live Activity
|
|
5023
|
-
</div>
|
|
5024
|
-
<div class="activity-list" id="activityList">
|
|
5025
|
-
<div class="activity-empty">
|
|
5026
|
-
<div class="activity-empty-icon">\u2192</div>
|
|
5027
|
-
<div class="activity-empty-text">Waiting for activity...</div>
|
|
5137
|
+
<!-- Status Bar -->
|
|
5138
|
+
<div class="status-bar">
|
|
5139
|
+
<div class="status-bar-left">
|
|
5140
|
+
<div class="logo-icon">\u25C6</div>
|
|
5141
|
+
<div class="logo-info">
|
|
5142
|
+
<div class="logo-title">SANCTUARY</div>
|
|
5143
|
+
<div class="logo-version">v${options.serverVersion}</div>
|
|
5028
5144
|
</div>
|
|
5029
5145
|
</div>
|
|
5030
|
-
</div>
|
|
5031
5146
|
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5147
|
+
<div class="status-bar-center">
|
|
5148
|
+
<div id="sovereignty-badge" class="sovereignty-badge">
|
|
5149
|
+
<span>Sovereignty Health:</span>
|
|
5150
|
+
<span class="sovereignty-score" id="sovereignty-score">\u2014</span>
|
|
5151
|
+
<span>/ 100</span>
|
|
5152
|
+
</div>
|
|
5036
5153
|
</div>
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
<
|
|
5041
|
-
<
|
|
5042
|
-
<div class="protection-card-stat" id="encryptionStat">Ed25519</div>
|
|
5154
|
+
|
|
5155
|
+
<div class="status-bar-right">
|
|
5156
|
+
<div class="status-item">
|
|
5157
|
+
<strong id="protections-count">\u2014</strong>
|
|
5158
|
+
<span>Protections</span>
|
|
5043
5159
|
</div>
|
|
5160
|
+
<div class="status-item">
|
|
5161
|
+
<strong id="uptime-value">\u2014</strong>
|
|
5162
|
+
<span>Uptime</span>
|
|
5163
|
+
</div>
|
|
5164
|
+
<div class="status-dot" id="connection-status"></div>
|
|
5165
|
+
<div id="pending-item-badge" class="pending-badge" style="display: none;">
|
|
5166
|
+
<span>\u23F3</span>
|
|
5167
|
+
<span id="pending-count">0</span>
|
|
5168
|
+
</div>
|
|
5169
|
+
</div>
|
|
5170
|
+
</div>
|
|
5044
5171
|
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5172
|
+
<!-- Main Content -->
|
|
5173
|
+
<div class="main-content">
|
|
5174
|
+
<div class="grid">
|
|
5175
|
+
<!-- Row 1: Sovereignty Layers -->
|
|
5176
|
+
<div class="sovereignty-layers" id="sovereignty-layers">
|
|
5177
|
+
<div class="layer-card" data-layer="l1">
|
|
5178
|
+
<div class="layer-name">Layer 1</div>
|
|
5179
|
+
<div class="layer-title">Cognitive Sovereignty</div>
|
|
5180
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l1-status">\u2014</span></div>
|
|
5181
|
+
<div class="layer-detail" id="l1-detail">Loading...</div>
|
|
5182
|
+
</div>
|
|
5183
|
+
<div class="layer-card" data-layer="l2">
|
|
5184
|
+
<div class="layer-name">Layer 2</div>
|
|
5185
|
+
<div class="layer-title">Operational Isolation</div>
|
|
5186
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l2-status">\u2014</span></div>
|
|
5187
|
+
<div class="layer-detail" id="l2-detail">Loading...</div>
|
|
5188
|
+
</div>
|
|
5189
|
+
<div class="layer-card" data-layer="l3">
|
|
5190
|
+
<div class="layer-name">Layer 3</div>
|
|
5191
|
+
<div class="layer-title">Selective Disclosure</div>
|
|
5192
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l3-status">\u2014</span></div>
|
|
5193
|
+
<div class="layer-detail" id="l3-detail">Loading...</div>
|
|
5194
|
+
</div>
|
|
5195
|
+
<div class="layer-card" data-layer="l4">
|
|
5196
|
+
<div class="layer-name">Layer 4</div>
|
|
5197
|
+
<div class="layer-title">Verifiable Reputation</div>
|
|
5198
|
+
<div class="layer-status"><span>\u25CF</span> <span id="l4-status">\u2014</span></div>
|
|
5199
|
+
<div class="layer-detail" id="l4-detail">Loading...</div>
|
|
5200
|
+
</div>
|
|
5050
5201
|
</div>
|
|
5051
5202
|
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
<div class="
|
|
5055
|
-
|
|
5056
|
-
|
|
5203
|
+
<!-- Row 2: Info Cards -->
|
|
5204
|
+
<div class="info-cards">
|
|
5205
|
+
<div class="info-card">
|
|
5206
|
+
<div class="card-header">Identity</div>
|
|
5207
|
+
<div class="card-row">
|
|
5208
|
+
<span class="card-label">Primary</span>
|
|
5209
|
+
<span class="card-value" id="identity-label">\u2014</span>
|
|
5210
|
+
</div>
|
|
5211
|
+
<div class="card-row">
|
|
5212
|
+
<span class="card-label">DID</span>
|
|
5213
|
+
<span class="card-value truncated" id="identity-did" title="">\u2014</span>
|
|
5214
|
+
</div>
|
|
5215
|
+
<div class="card-row">
|
|
5216
|
+
<span class="card-label">Public Key</span>
|
|
5217
|
+
<span class="card-value truncated" id="identity-pubkey" title="">\u2014</span>
|
|
5218
|
+
</div>
|
|
5219
|
+
<div class="card-row">
|
|
5220
|
+
<span class="card-label">Type</span>
|
|
5221
|
+
<span class="identity-badge">Ed25519</span>
|
|
5222
|
+
</div>
|
|
5223
|
+
<div class="card-row">
|
|
5224
|
+
<span class="card-label">Created</span>
|
|
5225
|
+
<span class="card-value" id="identity-created">\u2014</span>
|
|
5226
|
+
</div>
|
|
5227
|
+
<div class="card-row">
|
|
5228
|
+
<span class="card-label">Identities</span>
|
|
5229
|
+
<span class="card-value" id="identity-count">\u2014</span>
|
|
5230
|
+
</div>
|
|
5231
|
+
</div>
|
|
5232
|
+
|
|
5233
|
+
<div class="info-card">
|
|
5234
|
+
<div class="card-header">Handshakes</div>
|
|
5235
|
+
<div class="card-row">
|
|
5236
|
+
<span class="card-label">Total</span>
|
|
5237
|
+
<span class="card-value" id="handshake-count">\u2014</span>
|
|
5238
|
+
</div>
|
|
5239
|
+
<div class="card-row">
|
|
5240
|
+
<span class="card-label">Latest Peer</span>
|
|
5241
|
+
<span class="card-value truncated" id="handshake-latest">\u2014</span>
|
|
5242
|
+
</div>
|
|
5243
|
+
<div class="card-row">
|
|
5244
|
+
<span class="card-label">Trust Tier</span>
|
|
5245
|
+
<span class="trust-tier-badge" id="handshake-tier">Unverified</span>
|
|
5246
|
+
</div>
|
|
5247
|
+
<div class="card-row">
|
|
5248
|
+
<span class="card-label">Timestamp</span>
|
|
5249
|
+
<span class="card-value" id="handshake-time">\u2014</span>
|
|
5250
|
+
</div>
|
|
5251
|
+
</div>
|
|
5252
|
+
|
|
5253
|
+
<div class="info-card">
|
|
5254
|
+
<div class="card-header">Reputation</div>
|
|
5255
|
+
<div class="card-row">
|
|
5256
|
+
<span class="card-label">Weighted Score</span>
|
|
5257
|
+
<span class="card-value" id="reputation-score">\u2014</span>
|
|
5258
|
+
</div>
|
|
5259
|
+
<div class="card-row">
|
|
5260
|
+
<span class="card-label">Attestations</span>
|
|
5261
|
+
<span class="card-value" id="reputation-attestations">\u2014</span>
|
|
5262
|
+
</div>
|
|
5263
|
+
<div class="card-row">
|
|
5264
|
+
<span class="card-label">Verified Sovereign</span>
|
|
5265
|
+
<span class="card-value" id="reputation-verified">\u2014</span>
|
|
5266
|
+
</div>
|
|
5267
|
+
<div class="card-row">
|
|
5268
|
+
<span class="card-label">Verified Degraded</span>
|
|
5269
|
+
<span class="card-value" id="reputation-degraded">\u2014</span>
|
|
5270
|
+
</div>
|
|
5271
|
+
<div class="card-row">
|
|
5272
|
+
<span class="card-label">Unverified</span>
|
|
5273
|
+
<span class="card-value" id="reputation-unverified">\u2014</span>
|
|
5274
|
+
</div>
|
|
5275
|
+
</div>
|
|
5057
5276
|
</div>
|
|
5058
5277
|
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
<div class="
|
|
5062
|
-
|
|
5063
|
-
|
|
5278
|
+
<!-- Row 3: SHR & Activity -->
|
|
5279
|
+
<div class="main-panels">
|
|
5280
|
+
<div class="panel">
|
|
5281
|
+
<div class="panel-header">
|
|
5282
|
+
<div class="panel-title">Sovereignty Health Report</div>
|
|
5283
|
+
<button class="panel-action" id="copy-shr-btn">Copy JSON</button>
|
|
5284
|
+
</div>
|
|
5285
|
+
<div class="panel-content">
|
|
5286
|
+
<div class="shr-json" id="shr-viewer">
|
|
5287
|
+
<div class="empty-state">Loading SHR...</div>
|
|
5288
|
+
</div>
|
|
5289
|
+
</div>
|
|
5290
|
+
</div>
|
|
5291
|
+
|
|
5292
|
+
<div class="panel">
|
|
5293
|
+
<div class="panel-header">
|
|
5294
|
+
<div class="panel-title">Activity Feed</div>
|
|
5295
|
+
</div>
|
|
5296
|
+
<div class="panel-content">
|
|
5297
|
+
<div id="activity-feed" class="activity-feed">
|
|
5298
|
+
<div class="empty-state">Waiting for activity...</div>
|
|
5299
|
+
</div>
|
|
5300
|
+
</div>
|
|
5301
|
+
</div>
|
|
5064
5302
|
</div>
|
|
5065
5303
|
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
<div class="
|
|
5069
|
-
|
|
5070
|
-
|
|
5304
|
+
<!-- Row 4: Handshake History -->
|
|
5305
|
+
<div class="handshake-table">
|
|
5306
|
+
<div class="table-header">
|
|
5307
|
+
<div>Counterparty</div>
|
|
5308
|
+
<div>Trust Tier</div>
|
|
5309
|
+
<div>Sovereignty</div>
|
|
5310
|
+
<div>Verified</div>
|
|
5311
|
+
<div>Completed</div>
|
|
5312
|
+
<div>Expires</div>
|
|
5313
|
+
</div>
|
|
5314
|
+
<div class="table-rows" id="handshake-table">
|
|
5315
|
+
<div class="table-empty">No handshakes completed yet</div>
|
|
5316
|
+
</div>
|
|
5071
5317
|
</div>
|
|
5072
5318
|
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
<div class="
|
|
5076
|
-
|
|
5077
|
-
|
|
5319
|
+
<!-- Threat Panel -->
|
|
5320
|
+
<div class="threat-panel collapsed">
|
|
5321
|
+
<div class="threat-header">
|
|
5322
|
+
<div class="threat-title">Security Threats</div>
|
|
5323
|
+
<div class="threat-toggle">\u25B6</div>
|
|
5324
|
+
</div>
|
|
5325
|
+
<div class="threat-content" id="threat-alerts">
|
|
5326
|
+
<div class="empty-state">No threats detected</div>
|
|
5327
|
+
</div>
|
|
5078
5328
|
</div>
|
|
5079
5329
|
</div>
|
|
5080
5330
|
</div>
|
|
5081
|
-
</div>
|
|
5082
5331
|
|
|
5083
|
-
<!-- Pending
|
|
5084
|
-
<div class="pending-overlay" id="
|
|
5085
|
-
|
|
5086
|
-
<div class="pending-
|
|
5087
|
-
<button class="pending-overlay-close" onclick="closePendingOverlay()">\xD7</button>
|
|
5332
|
+
<!-- Pending Overlay -->
|
|
5333
|
+
<div class="pending-overlay" id="pending-overlay">
|
|
5334
|
+
<div class="pending-header">Pending Approvals</div>
|
|
5335
|
+
<div class="pending-items" id="pending-items"></div>
|
|
5088
5336
|
</div>
|
|
5089
|
-
<div class="pending-list" id="pendingList"></div>
|
|
5090
|
-
</div>
|
|
5091
|
-
|
|
5092
|
-
<!-- Threat Panel (collapsible footer) -->
|
|
5093
|
-
<div class="threat-panel collapsed" id="threatPanel">
|
|
5094
|
-
<div class="threat-header" onclick="toggleThreatPanel()">
|
|
5095
|
-
<span class="threat-icon">\u26A0</span>
|
|
5096
|
-
Recent Threats
|
|
5097
|
-
<span id="threatCount" style="margin-left: auto; color: var(--red); font-weight: 700;">0</span>
|
|
5098
|
-
</div>
|
|
5099
|
-
<div class="threat-content" id="threatContent">
|
|
5100
|
-
<div class="threat-empty">No threats detected</div>
|
|
5101
|
-
</div>
|
|
5102
|
-
</div>
|
|
5103
5337
|
|
|
5104
|
-
<script>
|
|
5105
|
-
|
|
5106
|
-
|
|
5338
|
+
<script>
|
|
5339
|
+
// Constants
|
|
5340
|
+
const AUTH_TOKEN = '${options.authToken || ""}' || sessionStorage.getItem('authToken') || '';
|
|
5341
|
+
const TIMEOUT_SECONDS = ${options.timeoutSeconds};
|
|
5342
|
+
const API_BASE = '';
|
|
5343
|
+
|
|
5344
|
+
// State
|
|
5345
|
+
let apiState = {
|
|
5346
|
+
sovereignty: null,
|
|
5347
|
+
identity: null,
|
|
5348
|
+
handshakes: [],
|
|
5349
|
+
shr: null,
|
|
5350
|
+
status: null,
|
|
5351
|
+
};
|
|
5107
5352
|
|
|
5108
|
-
|
|
5353
|
+
let pendingRequests = new Map();
|
|
5354
|
+
let activityLog = [];
|
|
5355
|
+
const maxActivityItems = 50;
|
|
5109
5356
|
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5357
|
+
// Helpers
|
|
5358
|
+
function esc(text) {
|
|
5359
|
+
if (!text) return '';
|
|
5360
|
+
const div = document.createElement('div');
|
|
5361
|
+
div.textContent = text;
|
|
5362
|
+
return div.innerHTML;
|
|
5363
|
+
}
|
|
5364
|
+
|
|
5365
|
+
function formatTime(isoString) {
|
|
5366
|
+
if (!isoString) return '\u2014';
|
|
5367
|
+
const date = new Date(isoString);
|
|
5368
|
+
return date.toLocaleString('en-US', {
|
|
5369
|
+
month: 'short',
|
|
5370
|
+
day: 'numeric',
|
|
5371
|
+
hour: '2-digit',
|
|
5372
|
+
minute: '2-digit',
|
|
5373
|
+
});
|
|
5374
|
+
}
|
|
5116
5375
|
|
|
5117
|
-
|
|
5376
|
+
function truncate(str, len = 16) {
|
|
5377
|
+
if (!str) return '\u2014';
|
|
5378
|
+
if (str.length <= len) return str;
|
|
5379
|
+
return str.slice(0, len) + '...';
|
|
5380
|
+
}
|
|
5118
5381
|
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
let threatCount = 0;
|
|
5124
|
-
const pendingRequests = new Map();
|
|
5125
|
-
const activityItems = [];
|
|
5126
|
-
const threatItems = [];
|
|
5127
|
-
let sovereigntyScore = 85;
|
|
5128
|
-
let sessionRenewalTimer = null;
|
|
5382
|
+
function calculateSovereigntyScore(shr) {
|
|
5383
|
+
if (!shr || !shr.layers) return 0;
|
|
5384
|
+
const layers = shr.layers;
|
|
5385
|
+
let score = 100;
|
|
5129
5386
|
|
|
5130
|
-
|
|
5387
|
+
if (layers.l1?.status === 'degraded') score -= 20;
|
|
5388
|
+
if (layers.l1?.status === 'inactive') score -= 35;
|
|
5389
|
+
if (layers.l2?.status === 'degraded') score -= 15;
|
|
5390
|
+
if (layers.l2?.status === 'inactive') score -= 25;
|
|
5391
|
+
if (layers.l3?.status === 'degraded') score -= 15;
|
|
5392
|
+
if (layers.l3?.status === 'inactive') score -= 25;
|
|
5393
|
+
if (layers.l4?.status === 'degraded') score -= 10;
|
|
5394
|
+
if (layers.l4?.status === 'inactive') score -= 20;
|
|
5131
5395
|
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
if (AUTH_TOKEN) h['Authorization'] = 'Bearer ' + AUTH_TOKEN;
|
|
5135
|
-
return h;
|
|
5136
|
-
}
|
|
5396
|
+
return Math.max(0, Math.min(100, score));
|
|
5397
|
+
}
|
|
5137
5398
|
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5399
|
+
async function fetchAPI(endpoint) {
|
|
5400
|
+
try {
|
|
5401
|
+
const response = await fetch(API_BASE + endpoint, {
|
|
5402
|
+
headers: {
|
|
5403
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
5404
|
+
},
|
|
5405
|
+
});
|
|
5143
5406
|
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5407
|
+
if (response.status === 401) {
|
|
5408
|
+
redirectToLogin();
|
|
5409
|
+
return null;
|
|
5410
|
+
}
|
|
5148
5411
|
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
// Schedule renewal at 80% of TTL
|
|
5160
|
-
if (sessionRenewalTimer) clearTimeout(sessionRenewalTimer);
|
|
5161
|
-
sessionRenewalTimer = setTimeout(function() {
|
|
5162
|
-
exchangeSession().then(function() { reconnectSSE(); });
|
|
5163
|
-
}, ttl * 800);
|
|
5164
|
-
} else if (resp.status === 401) {
|
|
5165
|
-
// Token invalid or expired \u2014 show non-destructive re-login overlay
|
|
5166
|
-
showSessionExpired();
|
|
5167
|
-
}
|
|
5168
|
-
} catch (e) {
|
|
5169
|
-
// Network error \u2014 retry in 30s
|
|
5170
|
-
if (sessionRenewalTimer) clearTimeout(sessionRenewalTimer);
|
|
5171
|
-
sessionRenewalTimer = setTimeout(function() {
|
|
5172
|
-
exchangeSession().then(function() { reconnectSSE(); });
|
|
5173
|
-
}, 30000);
|
|
5174
|
-
}
|
|
5175
|
-
}
|
|
5176
|
-
|
|
5177
|
-
function showSessionExpired() {
|
|
5178
|
-
// Clear stored token
|
|
5179
|
-
try { sessionStorage.removeItem('sanctuary_token'); } catch(_) {}
|
|
5180
|
-
// Redirect to login page
|
|
5181
|
-
document.cookie = 'sanctuary_session=; path=/; max-age=0';
|
|
5182
|
-
window.location.reload();
|
|
5183
|
-
}
|
|
5184
|
-
|
|
5185
|
-
// \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
|
|
5186
|
-
|
|
5187
|
-
function esc(s) {
|
|
5188
|
-
const d = document.createElement('div');
|
|
5189
|
-
d.textContent = String(s || '');
|
|
5190
|
-
return d.innerHTML;
|
|
5191
|
-
}
|
|
5192
|
-
|
|
5193
|
-
function closePendingOverlay() {
|
|
5194
|
-
document.getElementById('pendingOverlay').classList.remove('active');
|
|
5195
|
-
}
|
|
5196
|
-
|
|
5197
|
-
function toggleThreatPanel() {
|
|
5198
|
-
document.getElementById('threatPanel').classList.toggle('collapsed');
|
|
5199
|
-
}
|
|
5200
|
-
|
|
5201
|
-
function updateUptime() {
|
|
5202
|
-
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
5203
|
-
const hours = Math.floor(elapsed / 3600);
|
|
5204
|
-
const mins = Math.floor((elapsed % 3600) / 60);
|
|
5205
|
-
const secs = elapsed % 60;
|
|
5206
|
-
let uptimeStr = '';
|
|
5207
|
-
if (hours > 0) uptimeStr += hours + 'h ';
|
|
5208
|
-
if (mins > 0) uptimeStr += mins + 'm ';
|
|
5209
|
-
uptimeStr += secs + 's';
|
|
5210
|
-
document.getElementById('uptimeText').textContent = uptimeStr;
|
|
5211
|
-
}
|
|
5212
|
-
|
|
5213
|
-
// \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
|
|
5214
|
-
|
|
5215
|
-
function updateSovereigntyScore(score) {
|
|
5216
|
-
sovereigntyScore = Math.min(100, Math.max(0, score || 85));
|
|
5217
|
-
const badge = document.getElementById('sovereigntyScore');
|
|
5218
|
-
badge.textContent = sovereigntyScore;
|
|
5219
|
-
badge.className = 'sovereignty-score';
|
|
5220
|
-
if (sovereigntyScore >= 80) {
|
|
5221
|
-
badge.classList.add('high');
|
|
5222
|
-
} else if (sovereigntyScore >= 50) {
|
|
5223
|
-
badge.classList.add('medium');
|
|
5224
|
-
} else {
|
|
5225
|
-
badge.classList.add('low');
|
|
5412
|
+
if (!response.ok) {
|
|
5413
|
+
console.error('API Error:', response.status);
|
|
5414
|
+
return null;
|
|
5415
|
+
}
|
|
5416
|
+
|
|
5417
|
+
return await response.json();
|
|
5418
|
+
} catch (err) {
|
|
5419
|
+
console.error('Fetch error:', err);
|
|
5420
|
+
return null;
|
|
5421
|
+
}
|
|
5226
5422
|
}
|
|
5227
|
-
}
|
|
5228
5423
|
|
|
5229
|
-
|
|
5424
|
+
function redirectToLogin() {
|
|
5425
|
+
sessionStorage.removeItem('authToken');
|
|
5426
|
+
window.location.href = '/';
|
|
5427
|
+
}
|
|
5230
5428
|
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
tool,
|
|
5236
|
-
outcome,
|
|
5237
|
-
detail,
|
|
5238
|
-
hasInjection,
|
|
5239
|
-
isContextGated
|
|
5240
|
-
} = data;
|
|
5241
|
-
|
|
5242
|
-
const item = {
|
|
5243
|
-
id: 'activity-' + activityCount++,
|
|
5244
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
5245
|
-
tier: tier || 1,
|
|
5246
|
-
tool: tool || 'unknown_tool',
|
|
5247
|
-
outcome: outcome || 'executed',
|
|
5248
|
-
detail: detail || '',
|
|
5249
|
-
hasInjection: !!hasInjection,
|
|
5250
|
-
isContextGated: !!isContextGated
|
|
5251
|
-
};
|
|
5429
|
+
// API Updates
|
|
5430
|
+
async function updateSovereignty() {
|
|
5431
|
+
const data = await fetchAPI('/api/sovereignty');
|
|
5432
|
+
if (!data) return;
|
|
5252
5433
|
|
|
5253
|
-
|
|
5254
|
-
if (activityItems.length > MAX_ACTIVITY_ITEMS) {
|
|
5255
|
-
activityItems.pop();
|
|
5256
|
-
}
|
|
5434
|
+
apiState.sovereignty = data;
|
|
5257
5435
|
|
|
5258
|
-
|
|
5259
|
-
|
|
5436
|
+
const score = calculateSovereigntyScore(data.shr);
|
|
5437
|
+
const badge = document.getElementById('sovereignty-badge');
|
|
5438
|
+
const scoreEl = document.getElementById('sovereignty-score');
|
|
5260
5439
|
|
|
5261
|
-
|
|
5262
|
-
const list = document.getElementById('activityList');
|
|
5440
|
+
scoreEl.textContent = score;
|
|
5263
5441
|
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5442
|
+
badge.classList.remove('degraded', 'inactive');
|
|
5443
|
+
if (score < 70) badge.classList.add('degraded');
|
|
5444
|
+
if (score < 40) badge.classList.add('inactive');
|
|
5445
|
+
|
|
5446
|
+
updateLayerCards(data.shr);
|
|
5267
5447
|
}
|
|
5268
5448
|
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
const tr = document.createElement('div');
|
|
5272
|
-
tr.className = 'activity-item';
|
|
5273
|
-
tr.id = item.id;
|
|
5274
|
-
|
|
5275
|
-
const time = new Date(item.timestamp);
|
|
5276
|
-
const timeStr = time.toLocaleTimeString();
|
|
5277
|
-
|
|
5278
|
-
const tierClass = 't' + item.tier;
|
|
5279
|
-
const outcomeClass = item.outcome === 'denied' ? 'outcome denied' : 'outcome';
|
|
5280
|
-
|
|
5281
|
-
let icon = '\u25CF';
|
|
5282
|
-
if (item.isContextGated) icon = '\u{1F3AF}';
|
|
5283
|
-
else if (item.hasInjection) icon = '\u26A0';
|
|
5284
|
-
else if (item.outcome === 'denied') icon = '\u2717';
|
|
5285
|
-
else icon = '\u2713';
|
|
5286
|
-
|
|
5287
|
-
tr.innerHTML =
|
|
5288
|
-
'<div class="activity-item-icon">' + esc(icon) + '</div>' +
|
|
5289
|
-
'<div class="activity-item-content">' +
|
|
5290
|
-
'<div class="activity-time">' + esc(timeStr) + '</div>' +
|
|
5291
|
-
'<div class="activity-main">' +
|
|
5292
|
-
'<span class="activity-tier ' + tierClass + '">T' + item.tier + '</span>' +
|
|
5293
|
-
'<span class="activity-tool">' + esc(item.tool) + '</span>' +
|
|
5294
|
-
'<span class="activity-outcome ' + (outcomeClass === 'outcome denied' ? 'denied' : '') + '">' + (item.outcome === 'denied' ? '\u2717 denied' : '\u2713 allowed') + '</span>' +
|
|
5295
|
-
'</div>' +
|
|
5296
|
-
'<div class="activity-detail">' + esc(item.detail) + '</div>' +
|
|
5297
|
-
'</div>' +
|
|
5298
|
-
'';
|
|
5299
|
-
|
|
5300
|
-
tr.addEventListener('click', () => {
|
|
5301
|
-
tr.classList.toggle('expanded');
|
|
5302
|
-
});
|
|
5449
|
+
function updateLayerCards(shr) {
|
|
5450
|
+
if (!shr || !shr.layers) return;
|
|
5303
5451
|
|
|
5304
|
-
|
|
5305
|
-
}
|
|
5306
|
-
}
|
|
5452
|
+
const layers = shr.layers;
|
|
5307
5453
|
|
|
5308
|
-
|
|
5454
|
+
updateLayerCard('l1', layers.l1, layers.l1?.encryption || 'AES-256-GCM');
|
|
5455
|
+
updateLayerCard('l2', layers.l2, layers.l2?.isolation_type || 'Process-level');
|
|
5456
|
+
updateLayerCard('l3', layers.l3, layers.l3?.proof_system || 'Schnorr-Pedersen');
|
|
5457
|
+
updateLayerCard('l4', layers.l4, layers.l4?.reputation_mode || 'Weighted');
|
|
5458
|
+
}
|
|
5309
5459
|
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
request_id,
|
|
5313
|
-
operation,
|
|
5314
|
-
tier,
|
|
5315
|
-
reason,
|
|
5316
|
-
context,
|
|
5317
|
-
timestamp
|
|
5318
|
-
} = data;
|
|
5319
|
-
|
|
5320
|
-
const pending = {
|
|
5321
|
-
id: request_id,
|
|
5322
|
-
operation: operation || 'unknown',
|
|
5323
|
-
tier: tier || 1,
|
|
5324
|
-
reason: reason || '',
|
|
5325
|
-
context: context || {},
|
|
5326
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
5327
|
-
remaining: TIMEOUT_SECONDS
|
|
5328
|
-
};
|
|
5460
|
+
function updateLayerCard(layer, layerData, detail) {
|
|
5461
|
+
if (!layerData) return;
|
|
5329
5462
|
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
}
|
|
5463
|
+
const card = document.querySelector(\`[data-layer="\${layer}"]\`);
|
|
5464
|
+
if (!card) return;
|
|
5333
5465
|
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
updatePendingUI();
|
|
5337
|
-
}
|
|
5466
|
+
const status = layerData.status || 'inactive';
|
|
5467
|
+
card.classList.remove('degraded', 'inactive');
|
|
5338
5468
|
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5469
|
+
if (status === 'degraded') {
|
|
5470
|
+
card.classList.add('degraded');
|
|
5471
|
+
} else if (status === 'inactive') {
|
|
5472
|
+
card.classList.add('inactive');
|
|
5473
|
+
}
|
|
5342
5474
|
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
badge.textContent = count;
|
|
5346
|
-
document.getElementById('pendingOverlay').classList.add('active');
|
|
5347
|
-
} else {
|
|
5348
|
-
badge.classList.add('hidden');
|
|
5349
|
-
document.getElementById('pendingOverlay').classList.remove('active');
|
|
5475
|
+
document.getElementById(\`\${layer}-status\`).textContent = status.toUpperCase();
|
|
5476
|
+
document.getElementById(\`\${layer}-detail\`).textContent = detail;
|
|
5350
5477
|
}
|
|
5351
5478
|
|
|
5352
|
-
|
|
5353
|
-
|
|
5479
|
+
async function updateIdentity() {
|
|
5480
|
+
const data = await fetchAPI('/api/identity');
|
|
5481
|
+
if (!data) return;
|
|
5354
5482
|
|
|
5355
|
-
|
|
5356
|
-
const list = document.getElementById('pendingList');
|
|
5357
|
-
list.innerHTML = '';
|
|
5483
|
+
apiState.identity = data;
|
|
5358
5484
|
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5485
|
+
const primary = data.primary || {};
|
|
5486
|
+
document.getElementById('identity-label').textContent = primary.label || '\u2014';
|
|
5487
|
+
document.getElementById('identity-did').textContent = truncate(primary.did, 24);
|
|
5488
|
+
document.getElementById('identity-did').title = primary.did || '';
|
|
5489
|
+
document.getElementById('identity-pubkey').textContent = truncate(primary.publicKey, 24);
|
|
5490
|
+
document.getElementById('identity-pubkey').title = primary.publicKey || '';
|
|
5491
|
+
document.getElementById('identity-created').textContent = formatTime(primary.createdAt);
|
|
5492
|
+
document.getElementById('identity-count').textContent = data.identities?.length || '\u2014';
|
|
5493
|
+
}
|
|
5362
5494
|
|
|
5363
|
-
|
|
5364
|
-
const
|
|
5365
|
-
|
|
5366
|
-
const isUrgent = req.remaining <= 30;
|
|
5495
|
+
async function updateHandshakes() {
|
|
5496
|
+
const data = await fetchAPI('/api/handshakes');
|
|
5497
|
+
if (!data) return;
|
|
5367
5498
|
|
|
5368
|
-
|
|
5369
|
-
'<div class="pending-item-header">' +
|
|
5370
|
-
'<div class="pending-item-op">' + esc(req.operation) + '</div>' +
|
|
5371
|
-
'<div class="pending-item-tier ' + tierClass + '">T' + tier + '</div>' +
|
|
5372
|
-
'</div>' +
|
|
5373
|
-
'<div class="pending-item-reason">' + esc(req.reason) + '</div>' +
|
|
5374
|
-
'<div class="pending-item-timer ' + (isUrgent ? 'urgent' : '') + '">' +
|
|
5375
|
-
'<div class="pending-item-timer-bar">' +
|
|
5376
|
-
'<div class="pending-item-timer-fill" style="width: ' + pct + '%"></div>' +
|
|
5377
|
-
'</div>' +
|
|
5378
|
-
'<span id="timer-' + id + '">' + req.remaining + 's</span>' +
|
|
5379
|
-
'</div>' +
|
|
5380
|
-
'<div class="pending-item-actions">' +
|
|
5381
|
-
'<button class="btn btn-approve" onclick="handleApprove('' + id + '')">Approve</button>' +
|
|
5382
|
-
'<button class="btn btn-deny" onclick="handleDeny('' + id + '')">Deny</button>' +
|
|
5383
|
-
'</div>' +
|
|
5384
|
-
'';
|
|
5499
|
+
apiState.handshakes = data.handshakes || [];
|
|
5385
5500
|
|
|
5386
|
-
|
|
5387
|
-
}
|
|
5388
|
-
}
|
|
5501
|
+
document.getElementById('handshake-count').textContent = data.handshakes?.length || '0';
|
|
5389
5502
|
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5503
|
+
if (data.handshakes && data.handshakes.length > 0) {
|
|
5504
|
+
const latest = data.handshakes[0];
|
|
5505
|
+
document.getElementById('handshake-latest').textContent = truncate(latest.counterpartyId, 20);
|
|
5506
|
+
document.getElementById('handshake-latest').title = latest.counterpartyId || '';
|
|
5507
|
+
document.getElementById('handshake-tier').textContent = (latest.trustTier || 'Unverified').toUpperCase();
|
|
5508
|
+
document.getElementById('handshake-time').textContent = formatTime(latest.completedAt);
|
|
5509
|
+
} else {
|
|
5510
|
+
document.getElementById('handshake-latest').textContent = '\u2014';
|
|
5511
|
+
document.getElementById('handshake-tier').textContent = 'Unverified';
|
|
5512
|
+
document.getElementById('handshake-time').textContent = '\u2014';
|
|
5513
|
+
}
|
|
5395
5514
|
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
removePendingRequest(id);
|
|
5399
|
-
}).catch(() => {});
|
|
5400
|
-
};
|
|
5515
|
+
updateHandshakeTable(data.handshakes || []);
|
|
5516
|
+
}
|
|
5401
5517
|
|
|
5402
|
-
|
|
5518
|
+
function updateHandshakeTable(handshakes) {
|
|
5519
|
+
const table = document.getElementById('handshake-table');
|
|
5403
5520
|
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
type,
|
|
5409
|
-
details
|
|
5410
|
-
} = data;
|
|
5411
|
-
|
|
5412
|
-
const threat = {
|
|
5413
|
-
id: 'threat-' + threatCount++,
|
|
5414
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
5415
|
-
severity: severity || 'medium',
|
|
5416
|
-
type: type || 'unknown',
|
|
5417
|
-
details: details || ''
|
|
5418
|
-
};
|
|
5521
|
+
if (!handshakes || handshakes.length === 0) {
|
|
5522
|
+
table.innerHTML = '<div class="table-empty">No handshakes completed yet</div>';
|
|
5523
|
+
return;
|
|
5524
|
+
}
|
|
5419
5525
|
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5526
|
+
table.innerHTML = handshakes
|
|
5527
|
+
.map(
|
|
5528
|
+
(hs) => \`
|
|
5529
|
+
<div class="table-row">
|
|
5530
|
+
<div class="table-cell strong">\${esc(truncate(hs.counterpartyId, 24))}</div>
|
|
5531
|
+
<div class="table-cell">\${esc(hs.trustTier || 'Unverified')}</div>
|
|
5532
|
+
<div class="table-cell">\${esc(hs.sovereigntyLevel || '\u2014')}</div>
|
|
5533
|
+
<div class="table-cell">\${hs.verified ? 'Yes' : 'No'}</div>
|
|
5534
|
+
<div class="table-cell">\${formatTime(hs.completedAt)}</div>
|
|
5535
|
+
<div class="table-cell">\${formatTime(hs.expiresAt)}</div>
|
|
5536
|
+
</div>
|
|
5537
|
+
\`
|
|
5538
|
+
)
|
|
5539
|
+
.join('');
|
|
5423
5540
|
}
|
|
5424
5541
|
|
|
5425
|
-
|
|
5426
|
-
|
|
5542
|
+
async function updateSHR() {
|
|
5543
|
+
const data = await fetchAPI('/api/shr');
|
|
5544
|
+
if (!data) return;
|
|
5545
|
+
|
|
5546
|
+
apiState.shr = data;
|
|
5547
|
+
renderSHRViewer(data);
|
|
5427
5548
|
}
|
|
5428
5549
|
|
|
5429
|
-
|
|
5430
|
-
|
|
5550
|
+
function renderSHRViewer(shr) {
|
|
5551
|
+
const viewer = document.getElementById('shr-viewer');
|
|
5431
5552
|
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5553
|
+
if (!shr) {
|
|
5554
|
+
viewer.innerHTML = '<div class="empty-state">No SHR available</div>';
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5435
5557
|
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5558
|
+
let html = '';
|
|
5559
|
+
|
|
5560
|
+
// Implementation
|
|
5561
|
+
html += \`
|
|
5562
|
+
<div class="shr-section">
|
|
5563
|
+
<div class="shr-section-header">
|
|
5564
|
+
<div class="shr-toggle">\u25BC</div>
|
|
5565
|
+
<div>Implementation</div>
|
|
5566
|
+
</div>
|
|
5567
|
+
<div class="shr-section-content">
|
|
5568
|
+
<div class="shr-item">
|
|
5569
|
+
<div class="shr-key">sanctuary_version:</div>
|
|
5570
|
+
<div class="shr-value">\${esc(shr.implementation?.sanctuary_version || '\u2014')}</div>
|
|
5571
|
+
</div>
|
|
5572
|
+
<div class="shr-item">
|
|
5573
|
+
<div class="shr-key">node_version:</div>
|
|
5574
|
+
<div class="shr-value">\${esc(shr.implementation?.node_version || '\u2014')}</div>
|
|
5575
|
+
</div>
|
|
5576
|
+
<div class="shr-item">
|
|
5577
|
+
<div class="shr-key">generated_by:</div>
|
|
5578
|
+
<div class="shr-value">\${esc(shr.implementation?.generated_by || '\u2014')}</div>
|
|
5579
|
+
</div>
|
|
5580
|
+
</div>
|
|
5581
|
+
</div>
|
|
5582
|
+
\`;
|
|
5583
|
+
|
|
5584
|
+
// Metadata
|
|
5585
|
+
html += \`
|
|
5586
|
+
<div class="shr-section">
|
|
5587
|
+
<div class="shr-section-header">
|
|
5588
|
+
<div class="shr-toggle">\u25BC</div>
|
|
5589
|
+
<div>Metadata</div>
|
|
5590
|
+
</div>
|
|
5591
|
+
<div class="shr-section-content">
|
|
5592
|
+
<div class="shr-item">
|
|
5593
|
+
<div class="shr-key">instance_id:</div>
|
|
5594
|
+
<div class="shr-value">\${esc(truncate(shr.instance_id, 20))}</div>
|
|
5595
|
+
</div>
|
|
5596
|
+
<div class="shr-item">
|
|
5597
|
+
<div class="shr-key">generated_at:</div>
|
|
5598
|
+
<div class="shr-value">\${formatTime(shr.generated_at)}</div>
|
|
5599
|
+
</div>
|
|
5600
|
+
<div class="shr-item">
|
|
5601
|
+
<div class="shr-key">expires_at:</div>
|
|
5602
|
+
<div class="shr-value">\${formatTime(shr.expires_at)}</div>
|
|
5603
|
+
</div>
|
|
5604
|
+
</div>
|
|
5605
|
+
</div>
|
|
5606
|
+
\`;
|
|
5607
|
+
|
|
5608
|
+
// Layers
|
|
5609
|
+
if (shr.layers) {
|
|
5610
|
+
html += \`<div class="shr-section">
|
|
5611
|
+
<div class="shr-section-header">
|
|
5612
|
+
<div class="shr-toggle">\u25BC</div>
|
|
5613
|
+
<div>Layers</div>
|
|
5614
|
+
</div>
|
|
5615
|
+
<div class="shr-section-content">
|
|
5616
|
+
\`;
|
|
5617
|
+
|
|
5618
|
+
for (const [key, layer] of Object.entries(shr.layers)) {
|
|
5619
|
+
html += \`
|
|
5620
|
+
<div style="margin-bottom: 12px;">
|
|
5621
|
+
<div style="color: var(--blue); font-weight: 600; margin-bottom: 4px;">\${esc(key)}</div>
|
|
5622
|
+
<div style="padding-left: 12px;">
|
|
5623
|
+
\`;
|
|
5624
|
+
|
|
5625
|
+
for (const [lkey, lvalue] of Object.entries(layer || {})) {
|
|
5626
|
+
const displayValue =
|
|
5627
|
+
typeof lvalue === 'boolean'
|
|
5628
|
+
? lvalue
|
|
5629
|
+
? 'true'
|
|
5630
|
+
: 'false'
|
|
5631
|
+
: esc(String(lvalue));
|
|
5632
|
+
html += \`
|
|
5633
|
+
<div class="shr-item">
|
|
5634
|
+
<div class="shr-key">\${esc(lkey)}:</div>
|
|
5635
|
+
<div class="shr-value">\${displayValue}</div>
|
|
5636
|
+
</div>
|
|
5637
|
+
\`;
|
|
5638
|
+
}
|
|
5441
5639
|
|
|
5442
|
-
|
|
5443
|
-
|
|
5640
|
+
html += \`
|
|
5641
|
+
</div>
|
|
5642
|
+
</div>
|
|
5643
|
+
\`;
|
|
5644
|
+
}
|
|
5444
5645
|
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5646
|
+
html += \`
|
|
5647
|
+
</div>
|
|
5648
|
+
</div>
|
|
5649
|
+
\`;
|
|
5650
|
+
}
|
|
5651
|
+
|
|
5652
|
+
// Capabilities
|
|
5653
|
+
if (shr.capabilities) {
|
|
5654
|
+
html += \`
|
|
5655
|
+
<div class="shr-section">
|
|
5656
|
+
<div class="shr-section-header">
|
|
5657
|
+
<div class="shr-toggle">\u25BC</div>
|
|
5658
|
+
<div>Capabilities</div>
|
|
5659
|
+
</div>
|
|
5660
|
+
<div class="shr-section-content">
|
|
5661
|
+
\`;
|
|
5662
|
+
|
|
5663
|
+
for (const [key, value] of Object.entries(shr.capabilities)) {
|
|
5664
|
+
const displayValue = value ? 'true' : 'false';
|
|
5665
|
+
html += \`
|
|
5666
|
+
<div class="shr-item">
|
|
5667
|
+
<div class="shr-key">\${esc(key)}:</div>
|
|
5668
|
+
<div class="shr-value">\${displayValue}</div>
|
|
5669
|
+
</div>
|
|
5670
|
+
\`;
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
html += \`
|
|
5674
|
+
</div>
|
|
5675
|
+
</div>
|
|
5676
|
+
\`;
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// Signature
|
|
5680
|
+
html += \`
|
|
5681
|
+
<div class="shr-section">
|
|
5682
|
+
<div class="shr-section-header">
|
|
5683
|
+
<div class="shr-toggle">\u25BC</div>
|
|
5684
|
+
<div>Signature</div>
|
|
5685
|
+
</div>
|
|
5686
|
+
<div class="shr-section-content">
|
|
5687
|
+
<div class="shr-item">
|
|
5688
|
+
<div class="shr-key">signed_by:</div>
|
|
5689
|
+
<div class="shr-value">\${esc(truncate(shr.signed_by, 20))}</div>
|
|
5690
|
+
</div>
|
|
5691
|
+
<div class="shr-item">
|
|
5692
|
+
<div class="shr-key">signature:</div>
|
|
5693
|
+
<div class="shr-value">\${esc(truncate(shr.signature, 32))}</div>
|
|
5694
|
+
</div>
|
|
5695
|
+
</div>
|
|
5696
|
+
</div>
|
|
5697
|
+
\`;
|
|
5698
|
+
|
|
5699
|
+
viewer.innerHTML = html;
|
|
5700
|
+
|
|
5701
|
+
// Add collapse functionality
|
|
5702
|
+
document.querySelectorAll('.shr-section-header').forEach((header) => {
|
|
5703
|
+
header.addEventListener('click', () => {
|
|
5704
|
+
header.closest('.shr-section').classList.toggle('collapsed');
|
|
5705
|
+
});
|
|
5706
|
+
});
|
|
5457
5707
|
}
|
|
5458
|
-
}
|
|
5459
5708
|
|
|
5460
|
-
|
|
5709
|
+
async function updateStatus() {
|
|
5710
|
+
const data = await fetchAPI('/api/status');
|
|
5711
|
+
if (!data) return;
|
|
5461
5712
|
|
|
5462
|
-
|
|
5463
|
-
if (evtSource) evtSource.close();
|
|
5464
|
-
connect();
|
|
5465
|
-
}
|
|
5713
|
+
apiState.status = data;
|
|
5466
5714
|
|
|
5467
|
-
|
|
5468
|
-
|
|
5715
|
+
document.getElementById('protections-count').textContent = data.protectionsCount || '0';
|
|
5716
|
+
document.getElementById('uptime-value').textContent = formatUptime(data.uptime);
|
|
5469
5717
|
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
}
|
|
5718
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
5719
|
+
connectionStatus.classList.toggle('disconnected', !data.connected);
|
|
5720
|
+
}
|
|
5473
5721
|
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5722
|
+
function formatUptime(seconds) {
|
|
5723
|
+
if (!seconds) return '\u2014';
|
|
5724
|
+
const hours = Math.floor(seconds / 3600);
|
|
5725
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
5726
|
+
if (hours > 0) return \`\${hours}h \${minutes}m\`;
|
|
5727
|
+
return \`\${minutes}m\`;
|
|
5728
|
+
}
|
|
5477
5729
|
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
}
|
|
5486
|
-
if (data.pending) {
|
|
5487
|
-
data.pending.forEach(addPendingRequest);
|
|
5488
|
-
}
|
|
5489
|
-
});
|
|
5730
|
+
// SSE Setup
|
|
5731
|
+
function setupSSE() {
|
|
5732
|
+
const eventSource = new EventSource(API_BASE + '/api/events', {
|
|
5733
|
+
headers: {
|
|
5734
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
5735
|
+
},
|
|
5736
|
+
});
|
|
5490
5737
|
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
});
|
|
5738
|
+
eventSource.addEventListener('init', (e) => {
|
|
5739
|
+
console.log('Connected to SSE');
|
|
5740
|
+
});
|
|
5495
5741
|
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
});
|
|
5742
|
+
eventSource.addEventListener('sovereignty-update', () => {
|
|
5743
|
+
updateSovereignty();
|
|
5744
|
+
});
|
|
5500
5745
|
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
addActivityItem({
|
|
5504
|
-
timestamp: data.timestamp,
|
|
5505
|
-
tier: data.tier || 1,
|
|
5506
|
-
tool: data.tool || 'unknown',
|
|
5507
|
-
outcome: data.outcome || 'executed',
|
|
5508
|
-
detail: data.detail || ''
|
|
5746
|
+
eventSource.addEventListener('handshake-update', () => {
|
|
5747
|
+
updateHandshakes();
|
|
5509
5748
|
});
|
|
5510
|
-
});
|
|
5511
5749
|
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
isContextGated: true
|
|
5750
|
+
eventSource.addEventListener('tool-call', (e) => {
|
|
5751
|
+
const data = JSON.parse(e.data);
|
|
5752
|
+
addActivityItem({
|
|
5753
|
+
type: 'tool-call',
|
|
5754
|
+
title: 'Tool Call',
|
|
5755
|
+
content: data.toolName,
|
|
5756
|
+
timestamp: new Date().toISOString(),
|
|
5757
|
+
});
|
|
5521
5758
|
});
|
|
5522
|
-
});
|
|
5523
5759
|
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
hasInjection: true
|
|
5760
|
+
eventSource.addEventListener('context-gate-decision', (e) => {
|
|
5761
|
+
const data = JSON.parse(e.data);
|
|
5762
|
+
addActivityItem({
|
|
5763
|
+
type: 'context-gate',
|
|
5764
|
+
title: 'Context Gate',
|
|
5765
|
+
content: data.decision,
|
|
5766
|
+
timestamp: new Date().toISOString(),
|
|
5767
|
+
});
|
|
5533
5768
|
});
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5769
|
+
|
|
5770
|
+
eventSource.addEventListener('injection-alert', (e) => {
|
|
5771
|
+
const data = JSON.parse(e.data);
|
|
5772
|
+
addActivityItem({
|
|
5773
|
+
type: 'injection',
|
|
5774
|
+
title: 'Injection Alert',
|
|
5775
|
+
content: data.pattern,
|
|
5776
|
+
timestamp: new Date().toISOString(),
|
|
5777
|
+
});
|
|
5778
|
+
addThreatAlert(data);
|
|
5539
5779
|
});
|
|
5540
|
-
});
|
|
5541
5780
|
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5781
|
+
eventSource.addEventListener('pending-request', (e) => {
|
|
5782
|
+
const data = JSON.parse(e.data);
|
|
5783
|
+
addPendingRequest(data);
|
|
5784
|
+
});
|
|
5546
5785
|
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5786
|
+
eventSource.addEventListener('request-resolved', (e) => {
|
|
5787
|
+
const data = JSON.parse(e.data);
|
|
5788
|
+
removePendingRequest(data.requestId);
|
|
5789
|
+
});
|
|
5551
5790
|
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5791
|
+
eventSource.onerror = () => {
|
|
5792
|
+
console.error('SSE error');
|
|
5793
|
+
setTimeout(setupSSE, 5000);
|
|
5794
|
+
};
|
|
5795
|
+
}
|
|
5557
5796
|
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5797
|
+
// Activity Feed
|
|
5798
|
+
function addActivityItem(item) {
|
|
5799
|
+
activityLog.unshift(item);
|
|
5800
|
+
if (activityLog.length > maxActivityItems) {
|
|
5801
|
+
activityLog.pop();
|
|
5802
|
+
}
|
|
5562
5803
|
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5804
|
+
const feed = document.getElementById('activity-feed');
|
|
5805
|
+
const html = \`
|
|
5806
|
+
<div class="activity-item \${item.type}">
|
|
5807
|
+
<div class="activity-type">\${esc(item.title)}</div>
|
|
5808
|
+
<div class="activity-content">\${esc(item.content)}</div>
|
|
5809
|
+
<div class="activity-time">\${formatTime(item.timestamp)}</div>
|
|
5810
|
+
</div>
|
|
5811
|
+
\`;
|
|
5570
5812
|
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
const el = document.getElementById('encryptionStatus');
|
|
5581
|
-
el.className = 'protection-card-status ' + (status.encryption ? 'active' : 'inactive');
|
|
5582
|
-
el.textContent = status.encryption ? '\u2713 Active' : '\u2717 Inactive';
|
|
5583
|
-
}
|
|
5584
|
-
if (status.approval_gate !== undefined) {
|
|
5585
|
-
const el = document.getElementById('approvalStatus');
|
|
5586
|
-
el.className = 'protection-card-status ' + (status.approval_gate ? 'active' : 'inactive');
|
|
5587
|
-
el.textContent = status.approval_gate ? '\u2713 Active' : '\u2717 Inactive';
|
|
5588
|
-
}
|
|
5589
|
-
if (status.context_gating !== undefined) {
|
|
5590
|
-
const el = document.getElementById('contextStatus');
|
|
5591
|
-
el.className = 'protection-card-status ' + (status.context_gating ? 'active' : 'inactive');
|
|
5592
|
-
el.textContent = status.context_gating ? '\u2713 Active' : '\u2717 Inactive';
|
|
5593
|
-
}
|
|
5594
|
-
if (status.injection_detection !== undefined) {
|
|
5595
|
-
const el = document.getElementById('injectionStatus');
|
|
5596
|
-
el.className = 'protection-card-status ' + (status.injection_detection ? 'active' : 'inactive');
|
|
5597
|
-
el.textContent = status.injection_detection ? '\u2713 Active' : '\u2717 Inactive';
|
|
5813
|
+
if (feed.querySelector('.empty-state')) {
|
|
5814
|
+
feed.innerHTML = '';
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
feed.insertAdjacentHTML('afterbegin', html);
|
|
5818
|
+
|
|
5819
|
+
if (feed.children.length > maxActivityItems) {
|
|
5820
|
+
feed.lastChild.remove();
|
|
5821
|
+
}
|
|
5598
5822
|
}
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5823
|
+
|
|
5824
|
+
// Pending Requests
|
|
5825
|
+
function addPendingRequest(request) {
|
|
5826
|
+
pendingRequests.set(request.requestId, {
|
|
5827
|
+
id: request.requestId,
|
|
5828
|
+
title: request.title,
|
|
5829
|
+
details: request.details,
|
|
5830
|
+
expiresAt: new Date(Date.now() + TIMEOUT_SECONDS * 1000),
|
|
5831
|
+
});
|
|
5832
|
+
|
|
5833
|
+
updatePendingDisplay();
|
|
5603
5834
|
}
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5835
|
+
|
|
5836
|
+
function removePendingRequest(requestId) {
|
|
5837
|
+
pendingRequests.delete(requestId);
|
|
5838
|
+
updatePendingDisplay();
|
|
5608
5839
|
}
|
|
5609
|
-
}
|
|
5610
5840
|
|
|
5611
|
-
|
|
5841
|
+
function updatePendingDisplay() {
|
|
5842
|
+
const badge = document.getElementById('pending-item-badge');
|
|
5843
|
+
const count = pendingRequests.size;
|
|
5844
|
+
|
|
5845
|
+
if (count > 0) {
|
|
5846
|
+
document.getElementById('pending-count').textContent = count;
|
|
5847
|
+
badge.style.display = 'flex';
|
|
5848
|
+
} else {
|
|
5849
|
+
badge.style.display = 'none';
|
|
5850
|
+
}
|
|
5851
|
+
|
|
5852
|
+
const overlay = document.getElementById('pending-overlay');
|
|
5853
|
+
const items = document.getElementById('pending-items');
|
|
5854
|
+
|
|
5855
|
+
if (count === 0) {
|
|
5856
|
+
items.innerHTML = '';
|
|
5857
|
+
overlay.classList.remove('show');
|
|
5858
|
+
return;
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5861
|
+
let html = '';
|
|
5862
|
+
for (const req of pendingRequests.values()) {
|
|
5863
|
+
const remaining = Math.max(0, Math.floor((req.expiresAt - Date.now()) / 1000));
|
|
5864
|
+
html += \`
|
|
5865
|
+
<div class="pending-item">
|
|
5866
|
+
<div class="pending-title">\${esc(req.title)}</div>
|
|
5867
|
+
<div class="pending-countdown">Expires in \${remaining}s</div>
|
|
5868
|
+
<div class="pending-actions">
|
|
5869
|
+
<button class="pending-btn pending-approve" data-id="\${req.id}">Approve</button>
|
|
5870
|
+
<button class="pending-btn pending-deny" data-id="\${req.id}">Deny</button>
|
|
5871
|
+
</div>
|
|
5872
|
+
</div>
|
|
5873
|
+
\`;
|
|
5874
|
+
}
|
|
5875
|
+
|
|
5876
|
+
items.innerHTML = html;
|
|
5877
|
+
|
|
5878
|
+
document.querySelectorAll('.pending-approve').forEach((btn) => {
|
|
5879
|
+
btn.addEventListener('click', async () => {
|
|
5880
|
+
const id = btn.getAttribute('data-id');
|
|
5881
|
+
await fetchAPI(\`/api/approve/\${id}\`);
|
|
5882
|
+
});
|
|
5883
|
+
});
|
|
5612
5884
|
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5885
|
+
document.querySelectorAll('.pending-deny').forEach((btn) => {
|
|
5886
|
+
btn.addEventListener('click', async () => {
|
|
5887
|
+
const id = btn.getAttribute('data-id');
|
|
5888
|
+
await fetchAPI(\`/api/deny/\${id}\`);
|
|
5889
|
+
});
|
|
5890
|
+
});
|
|
5618
5891
|
}
|
|
5619
|
-
connect();
|
|
5620
5892
|
|
|
5621
|
-
//
|
|
5622
|
-
|
|
5623
|
-
|
|
5893
|
+
// Threat Panel
|
|
5894
|
+
function addThreatAlert(alert) {
|
|
5895
|
+
const panel = document.querySelector('.threat-panel');
|
|
5896
|
+
const content = document.getElementById('threat-alerts');
|
|
5624
5897
|
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
for (const [id, req] of pendingRequests) {
|
|
5628
|
-
req.remaining = Math.max(0, req.remaining - 1);
|
|
5629
|
-
const el = document.getElementById('timer-' + id);
|
|
5630
|
-
if (el) {
|
|
5631
|
-
el.textContent = req.remaining + 's';
|
|
5632
|
-
}
|
|
5898
|
+
if (content.querySelector('.empty-state')) {
|
|
5899
|
+
content.innerHTML = '';
|
|
5633
5900
|
}
|
|
5634
|
-
}, 1000);
|
|
5635
5901
|
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
const
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5902
|
+
panel.classList.remove('collapsed');
|
|
5903
|
+
|
|
5904
|
+
const html = \`
|
|
5905
|
+
<div class="threat-alert">
|
|
5906
|
+
<div class="threat-type">\${esc(alert.type || 'Injection Alert')}</div>
|
|
5907
|
+
<div class="threat-message">\${esc(alert.message || alert.pattern || '\u2014')}</div>
|
|
5908
|
+
</div>
|
|
5909
|
+
\`;
|
|
5910
|
+
|
|
5911
|
+
content.insertAdjacentHTML('afterbegin', html);
|
|
5912
|
+
|
|
5913
|
+
const alerts = content.querySelectorAll('.threat-alert');
|
|
5914
|
+
if (alerts.length > 10) {
|
|
5915
|
+
alerts[alerts.length - 1].remove();
|
|
5643
5916
|
}
|
|
5644
|
-
} catch (e) {
|
|
5645
|
-
// Ignore
|
|
5646
5917
|
}
|
|
5647
|
-
})();
|
|
5648
5918
|
|
|
5649
|
-
|
|
5650
|
-
|
|
5919
|
+
// Threat Panel Toggle
|
|
5920
|
+
document.querySelector('.threat-header').addEventListener('click', () => {
|
|
5921
|
+
document.querySelector('.threat-panel').classList.toggle('collapsed');
|
|
5922
|
+
});
|
|
5923
|
+
|
|
5924
|
+
// SHR Copy Button
|
|
5925
|
+
document.getElementById('copy-shr-btn').addEventListener('click', async () => {
|
|
5926
|
+
if (!apiState.shr) return;
|
|
5927
|
+
|
|
5928
|
+
const json = JSON.stringify(apiState.shr, null, 2);
|
|
5929
|
+
try {
|
|
5930
|
+
await navigator.clipboard.writeText(json);
|
|
5931
|
+
const btn = document.getElementById('copy-shr-btn');
|
|
5932
|
+
const original = btn.textContent;
|
|
5933
|
+
btn.textContent = 'Copied!';
|
|
5934
|
+
setTimeout(() => {
|
|
5935
|
+
btn.textContent = original;
|
|
5936
|
+
}, 2000);
|
|
5937
|
+
} catch (err) {
|
|
5938
|
+
console.error('Copy failed:', err);
|
|
5939
|
+
}
|
|
5940
|
+
});
|
|
5941
|
+
|
|
5942
|
+
// Pending Overlay Toggle
|
|
5943
|
+
document.getElementById('pending-item-badge').addEventListener('click', () => {
|
|
5944
|
+
document.getElementById('pending-overlay').classList.toggle('show');
|
|
5945
|
+
});
|
|
5946
|
+
|
|
5947
|
+
// Initialize
|
|
5948
|
+
async function initialize() {
|
|
5949
|
+
if (!AUTH_TOKEN) {
|
|
5950
|
+
redirectToLogin();
|
|
5951
|
+
return;
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5954
|
+
// Initial data fetch
|
|
5955
|
+
await Promise.all([
|
|
5956
|
+
updateSovereignty(),
|
|
5957
|
+
updateIdentity(),
|
|
5958
|
+
updateHandshakes(),
|
|
5959
|
+
updateSHR(),
|
|
5960
|
+
updateStatus(),
|
|
5961
|
+
]);
|
|
5962
|
+
|
|
5963
|
+
// Setup SSE for real-time updates
|
|
5964
|
+
setupSSE();
|
|
5965
|
+
|
|
5966
|
+
// Refresh status periodically
|
|
5967
|
+
setInterval(updateStatus, 30000);
|
|
5968
|
+
}
|
|
5651
5969
|
|
|
5970
|
+
// Start
|
|
5971
|
+
initialize();
|
|
5972
|
+
</script>
|
|
5652
5973
|
</body>
|
|
5653
5974
|
</html>`;
|
|
5654
5975
|
}
|
|
@@ -8080,6 +8401,154 @@ function deriveTrustTier(level) {
|
|
|
8080
8401
|
}
|
|
8081
8402
|
}
|
|
8082
8403
|
|
|
8404
|
+
// src/handshake/attestation.ts
|
|
8405
|
+
init_encoding();
|
|
8406
|
+
init_encoding();
|
|
8407
|
+
var ATTESTATION_VERSION = "1.0";
|
|
8408
|
+
function deriveTrustTier2(level) {
|
|
8409
|
+
switch (level) {
|
|
8410
|
+
case "full":
|
|
8411
|
+
return "verified-sovereign";
|
|
8412
|
+
case "degraded":
|
|
8413
|
+
return "verified-degraded";
|
|
8414
|
+
default:
|
|
8415
|
+
return "unverified";
|
|
8416
|
+
}
|
|
8417
|
+
}
|
|
8418
|
+
function generateAttestation(opts) {
|
|
8419
|
+
const {
|
|
8420
|
+
attesterSHR,
|
|
8421
|
+
subjectSHR,
|
|
8422
|
+
verificationResult,
|
|
8423
|
+
mutual = false,
|
|
8424
|
+
identityManager,
|
|
8425
|
+
masterKey,
|
|
8426
|
+
identityId
|
|
8427
|
+
} = opts;
|
|
8428
|
+
const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
|
|
8429
|
+
if (!identity) {
|
|
8430
|
+
return { error: "No identity available for signing attestation" };
|
|
8431
|
+
}
|
|
8432
|
+
const now = /* @__PURE__ */ new Date();
|
|
8433
|
+
const attesterExpiry = new Date(attesterSHR.body.expires_at);
|
|
8434
|
+
const subjectExpiry = new Date(subjectSHR.body.expires_at);
|
|
8435
|
+
const earliestExpiry = attesterExpiry < subjectExpiry ? attesterExpiry : subjectExpiry;
|
|
8436
|
+
const sovereigntyLevel = verificationResult.valid ? verificationResult.sovereignty_level : "unverified";
|
|
8437
|
+
const body = {
|
|
8438
|
+
attestation_version: ATTESTATION_VERSION,
|
|
8439
|
+
attester_id: attesterSHR.body.instance_id,
|
|
8440
|
+
subject_id: subjectSHR.body.instance_id,
|
|
8441
|
+
attester_shr: attesterSHR,
|
|
8442
|
+
subject_shr: subjectSHR,
|
|
8443
|
+
verification: {
|
|
8444
|
+
subject_shr_valid: verificationResult.valid,
|
|
8445
|
+
subject_sovereignty_level: sovereigntyLevel,
|
|
8446
|
+
subject_trust_tier: deriveTrustTier2(sovereigntyLevel),
|
|
8447
|
+
mutual,
|
|
8448
|
+
errors: verificationResult.errors,
|
|
8449
|
+
warnings: verificationResult.warnings
|
|
8450
|
+
},
|
|
8451
|
+
attested_at: now.toISOString(),
|
|
8452
|
+
expires_at: earliestExpiry.toISOString()
|
|
8453
|
+
};
|
|
8454
|
+
const canonical = JSON.stringify(deepSortKeys(body));
|
|
8455
|
+
const payload = stringToBytes(canonical);
|
|
8456
|
+
const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
|
|
8457
|
+
const signatureBytes = sign(
|
|
8458
|
+
payload,
|
|
8459
|
+
identity.encrypted_private_key,
|
|
8460
|
+
encryptionKey
|
|
8461
|
+
);
|
|
8462
|
+
const summary = generateSummary(body);
|
|
8463
|
+
return {
|
|
8464
|
+
body,
|
|
8465
|
+
signed_by: identity.public_key,
|
|
8466
|
+
signature: toBase64url(signatureBytes),
|
|
8467
|
+
summary
|
|
8468
|
+
};
|
|
8469
|
+
}
|
|
8470
|
+
function layerLine(label, status) {
|
|
8471
|
+
const icon = status === "active" ? "\u2713" : status === "degraded" ? "~" : "x";
|
|
8472
|
+
return ` ${icon} ${label}: ${status}`;
|
|
8473
|
+
}
|
|
8474
|
+
function generateSummary(body) {
|
|
8475
|
+
const v = body.verification;
|
|
8476
|
+
const sLayers = body.subject_shr.body.layers;
|
|
8477
|
+
const aLayers = body.attester_shr.body.layers;
|
|
8478
|
+
const tierLabel = v.subject_trust_tier === "verified-sovereign" ? "Verified Sovereign" : v.subject_trust_tier === "verified-degraded" ? "Verified (Degraded)" : "Unverified";
|
|
8479
|
+
const lines = [
|
|
8480
|
+
`--- Sovereignty Attestation ---`,
|
|
8481
|
+
``,
|
|
8482
|
+
`Attester: ${body.attester_id.slice(0, 16)}...`,
|
|
8483
|
+
`Subject: ${body.subject_id.slice(0, 16)}...`,
|
|
8484
|
+
`Result: ${tierLabel}`,
|
|
8485
|
+
``,
|
|
8486
|
+
`Subject Sovereignty Posture:`,
|
|
8487
|
+
layerLine("L1 Cognitive Sovereignty", sLayers.l1.status),
|
|
8488
|
+
layerLine("L2 Operational Isolation", sLayers.l2.status),
|
|
8489
|
+
layerLine("L3 Selective Disclosure", sLayers.l3.status),
|
|
8490
|
+
layerLine("L4 Verifiable Reputation", sLayers.l4.status),
|
|
8491
|
+
``,
|
|
8492
|
+
`Attester Sovereignty Posture:`,
|
|
8493
|
+
layerLine("L1 Cognitive Sovereignty", aLayers.l1.status),
|
|
8494
|
+
layerLine("L2 Operational Isolation", aLayers.l2.status),
|
|
8495
|
+
layerLine("L3 Selective Disclosure", aLayers.l3.status),
|
|
8496
|
+
layerLine("L4 Verifiable Reputation", aLayers.l4.status),
|
|
8497
|
+
``,
|
|
8498
|
+
`Mutual: ${v.mutual ? "Yes" : "One-sided"}`,
|
|
8499
|
+
`Attested: ${body.attested_at}`,
|
|
8500
|
+
`Expires: ${body.expires_at}`,
|
|
8501
|
+
`Signature: ${body.attestation_version} / Ed25519`
|
|
8502
|
+
];
|
|
8503
|
+
if (v.warnings.length > 0) {
|
|
8504
|
+
lines.push(``, `Warnings: ${v.warnings.join("; ")}`);
|
|
8505
|
+
}
|
|
8506
|
+
if (v.errors.length > 0) {
|
|
8507
|
+
lines.push(``, `Errors: ${v.errors.join("; ")}`);
|
|
8508
|
+
}
|
|
8509
|
+
lines.push(``, `--- Verify: compare signed_by against attester's known public key ---`);
|
|
8510
|
+
return lines.join("\n");
|
|
8511
|
+
}
|
|
8512
|
+
function verifyAttestation(attestation, now) {
|
|
8513
|
+
const errors = [];
|
|
8514
|
+
const currentTime = now ?? /* @__PURE__ */ new Date();
|
|
8515
|
+
if (attestation.body.attestation_version !== ATTESTATION_VERSION) {
|
|
8516
|
+
errors.push(
|
|
8517
|
+
`Unsupported attestation version: ${attestation.body.attestation_version}`
|
|
8518
|
+
);
|
|
8519
|
+
}
|
|
8520
|
+
if (!attestation.body.attester_id || !attestation.body.subject_id) {
|
|
8521
|
+
errors.push("Missing attester_id or subject_id");
|
|
8522
|
+
}
|
|
8523
|
+
if (!attestation.body.attester_shr || !attestation.body.subject_shr) {
|
|
8524
|
+
errors.push("Missing attester or subject SHR");
|
|
8525
|
+
}
|
|
8526
|
+
const expired = new Date(attestation.body.expires_at) <= currentTime;
|
|
8527
|
+
if (expired) {
|
|
8528
|
+
errors.push("Attestation has expired");
|
|
8529
|
+
}
|
|
8530
|
+
try {
|
|
8531
|
+
const publicKey = fromBase64url(attestation.signed_by);
|
|
8532
|
+
const canonical = JSON.stringify(deepSortKeys(attestation.body));
|
|
8533
|
+
const payload = stringToBytes(canonical);
|
|
8534
|
+
const signatureBytes = fromBase64url(attestation.signature);
|
|
8535
|
+
const signatureValid = verify(payload, signatureBytes, publicKey);
|
|
8536
|
+
if (!signatureValid) {
|
|
8537
|
+
errors.push("Attestation signature is invalid");
|
|
8538
|
+
}
|
|
8539
|
+
} catch (e) {
|
|
8540
|
+
errors.push(`Signature verification error: ${e.message}`);
|
|
8541
|
+
}
|
|
8542
|
+
return {
|
|
8543
|
+
valid: errors.length === 0,
|
|
8544
|
+
errors,
|
|
8545
|
+
attester_id: attestation.body.attester_id ?? "unknown",
|
|
8546
|
+
subject_id: attestation.body.subject_id ?? "unknown",
|
|
8547
|
+
trust_tier: errors.length === 0 ? attestation.body.verification.subject_trust_tier : "unverified",
|
|
8548
|
+
expired
|
|
8549
|
+
};
|
|
8550
|
+
}
|
|
8551
|
+
|
|
8083
8552
|
// src/handshake/tools.ts
|
|
8084
8553
|
function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
8085
8554
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -8265,6 +8734,103 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
|
8265
8734
|
result: session.result ?? null
|
|
8266
8735
|
});
|
|
8267
8736
|
}
|
|
8737
|
+
},
|
|
8738
|
+
// ─── Streamlined Exchange ─────────────────────────────────────────
|
|
8739
|
+
{
|
|
8740
|
+
name: "sanctuary/handshake_exchange",
|
|
8741
|
+
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).",
|
|
8742
|
+
inputSchema: {
|
|
8743
|
+
type: "object",
|
|
8744
|
+
properties: {
|
|
8745
|
+
counterparty_shr: {
|
|
8746
|
+
type: "object",
|
|
8747
|
+
description: "The counterparty's signed SHR (SignedSHR object with body, signed_by, signature)."
|
|
8748
|
+
},
|
|
8749
|
+
identity_id: {
|
|
8750
|
+
type: "string",
|
|
8751
|
+
description: "Identity to use for the exchange. Defaults to primary identity."
|
|
8752
|
+
}
|
|
8753
|
+
},
|
|
8754
|
+
required: ["counterparty_shr"]
|
|
8755
|
+
},
|
|
8756
|
+
handler: async (args) => {
|
|
8757
|
+
const counterpartySHR = args.counterparty_shr;
|
|
8758
|
+
const ourSHR = generateSHR(args.identity_id, shrOpts);
|
|
8759
|
+
if (typeof ourSHR === "string") {
|
|
8760
|
+
return toolResult({ error: ourSHR });
|
|
8761
|
+
}
|
|
8762
|
+
const verificationResult = verifySHR(counterpartySHR);
|
|
8763
|
+
const attestation = generateAttestation({
|
|
8764
|
+
attesterSHR: ourSHR,
|
|
8765
|
+
subjectSHR: counterpartySHR,
|
|
8766
|
+
verificationResult,
|
|
8767
|
+
mutual: false,
|
|
8768
|
+
identityManager,
|
|
8769
|
+
masterKey,
|
|
8770
|
+
identityId: args.identity_id
|
|
8771
|
+
});
|
|
8772
|
+
if ("error" in attestation) {
|
|
8773
|
+
auditLog.append("l4", "handshake_exchange", ourSHR.body.instance_id, void 0, "failure");
|
|
8774
|
+
return toolResult({ error: attestation.error });
|
|
8775
|
+
}
|
|
8776
|
+
if (verificationResult.valid) {
|
|
8777
|
+
const sovereigntyLevel = verificationResult.sovereignty_level;
|
|
8778
|
+
const trustTier = sovereigntyLevel === "full" ? "verified-sovereign" : sovereigntyLevel === "degraded" ? "verified-degraded" : "unverified";
|
|
8779
|
+
handshakeResults.set(verificationResult.counterparty_id, {
|
|
8780
|
+
counterparty_id: verificationResult.counterparty_id,
|
|
8781
|
+
counterparty_shr: counterpartySHR,
|
|
8782
|
+
verified: true,
|
|
8783
|
+
sovereignty_level: sovereigntyLevel,
|
|
8784
|
+
trust_tier: trustTier,
|
|
8785
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8786
|
+
expires_at: verificationResult.expires_at,
|
|
8787
|
+
errors: []
|
|
8788
|
+
});
|
|
8789
|
+
}
|
|
8790
|
+
auditLog.append("l4", "handshake_exchange", ourSHR.body.instance_id);
|
|
8791
|
+
return toolResult({
|
|
8792
|
+
attestation,
|
|
8793
|
+
our_shr: ourSHR,
|
|
8794
|
+
verification: {
|
|
8795
|
+
counterparty_valid: verificationResult.valid,
|
|
8796
|
+
counterparty_sovereignty: verificationResult.sovereignty_level,
|
|
8797
|
+
counterparty_id: verificationResult.counterparty_id,
|
|
8798
|
+
errors: verificationResult.errors,
|
|
8799
|
+
warnings: verificationResult.warnings
|
|
8800
|
+
},
|
|
8801
|
+
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.",
|
|
8802
|
+
_content_trust: "external"
|
|
8803
|
+
});
|
|
8804
|
+
}
|
|
8805
|
+
},
|
|
8806
|
+
{
|
|
8807
|
+
name: "sanctuary/handshake_verify_attestation",
|
|
8808
|
+
description: "Verify a signed attestation artifact from another agent. Checks the Ed25519 signature, temporal validity, and structural integrity.",
|
|
8809
|
+
inputSchema: {
|
|
8810
|
+
type: "object",
|
|
8811
|
+
properties: {
|
|
8812
|
+
attestation: {
|
|
8813
|
+
type: "object",
|
|
8814
|
+
description: "The SignedAttestation object to verify (body, signed_by, signature, summary)."
|
|
8815
|
+
}
|
|
8816
|
+
},
|
|
8817
|
+
required: ["attestation"]
|
|
8818
|
+
},
|
|
8819
|
+
handler: async (args) => {
|
|
8820
|
+
const attestation = args.attestation;
|
|
8821
|
+
const result = verifyAttestation(attestation);
|
|
8822
|
+
auditLog.append(
|
|
8823
|
+
"l4",
|
|
8824
|
+
"handshake_verify_attestation",
|
|
8825
|
+
result.attester_id,
|
|
8826
|
+
void 0,
|
|
8827
|
+
result.valid ? "success" : "failure"
|
|
8828
|
+
);
|
|
8829
|
+
return toolResult({
|
|
8830
|
+
...result,
|
|
8831
|
+
_content_trust: "external"
|
|
8832
|
+
});
|
|
8833
|
+
}
|
|
8268
8834
|
}
|
|
8269
8835
|
];
|
|
8270
8836
|
return { tools, handshakeResults };
|
|
@@ -12222,6 +12788,7 @@ async function createSanctuaryServer(options) {
|
|
|
12222
12788
|
return { server, config };
|
|
12223
12789
|
}
|
|
12224
12790
|
|
|
12791
|
+
exports.ATTESTATION_VERSION = ATTESTATION_VERSION;
|
|
12225
12792
|
exports.ApprovalGate = ApprovalGate;
|
|
12226
12793
|
exports.AuditLog = AuditLog;
|
|
12227
12794
|
exports.AutoApproveChannel = AutoApproveChannel;
|
|
@@ -12253,6 +12820,7 @@ exports.createRangeProof = createRangeProof;
|
|
|
12253
12820
|
exports.createSanctuaryServer = createSanctuaryServer;
|
|
12254
12821
|
exports.evaluateField = evaluateField;
|
|
12255
12822
|
exports.filterContext = filterContext;
|
|
12823
|
+
exports.generateAttestation = generateAttestation;
|
|
12256
12824
|
exports.generateSHR = generateSHR;
|
|
12257
12825
|
exports.getTemplate = getTemplate;
|
|
12258
12826
|
exports.initiateHandshake = initiateHandshake;
|
|
@@ -12264,6 +12832,7 @@ exports.resolveTier = resolveTier;
|
|
|
12264
12832
|
exports.respondToHandshake = respondToHandshake;
|
|
12265
12833
|
exports.signPayload = signPayload;
|
|
12266
12834
|
exports.tierDistribution = tierDistribution;
|
|
12835
|
+
exports.verifyAttestation = verifyAttestation;
|
|
12267
12836
|
exports.verifyBridgeCommitment = verifyBridgeCommitment;
|
|
12268
12837
|
exports.verifyCompletion = verifyCompletion;
|
|
12269
12838
|
exports.verifyPedersenCommitment = verifyPedersenCommitment;
|