@sanctuary-framework/mcp-server 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1936 -1399
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1936 -1399
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1937 -1398
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -2
- package/dist/index.d.ts +97 -2
- package/dist/index.js +1935 -1399
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -231,7 +231,7 @@ function defaultConfig() {
|
|
|
231
231
|
}
|
|
232
232
|
},
|
|
233
233
|
disclosure: {
|
|
234
|
-
proof_system: "
|
|
234
|
+
proof_system: "schnorr-pedersen",
|
|
235
235
|
default_policy: "minimum-necessary"
|
|
236
236
|
},
|
|
237
237
|
reputation: {
|
|
@@ -345,7 +345,7 @@ function validateConfig(config) {
|
|
|
345
345
|
`Unimplemented config value: execution.environment = "${config.execution.environment}". Only ${[...implementedEnvironment].map((v) => `"${v}"`).join(", ")} are currently implemented. Using an unimplemented environment would silently degrade security.`
|
|
346
346
|
);
|
|
347
347
|
}
|
|
348
|
-
const implementedProofSystem = /* @__PURE__ */ new Set(["commitment-only"]);
|
|
348
|
+
const implementedProofSystem = /* @__PURE__ */ new Set(["schnorr-pedersen", "commitment-only"]);
|
|
349
349
|
if (!implementedProofSystem.has(config.disclosure.proof_system)) {
|
|
350
350
|
errors.push(
|
|
351
351
|
`Unimplemented config value: disclosure.proof_system = "${config.disclosure.proof_system}". Only ${[...implementedProofSystem].map((v) => `"${v}"`).join(", ")} is currently implemented. Using an unimplemented proof system would silently degrade security.`
|
|
@@ -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
|
-
|
|
4999
|
-
<div class="sovereignty-badge">
|
|
5000
|
-
<div class="sovereignty-score" id="sovereigntyScore">85</div>
|
|
5001
|
-
<span>Sovereignty Health</span>
|
|
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>
|
|
5144
|
+
</div>
|
|
5002
5145
|
</div>
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5146
|
+
|
|
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>
|
|
5007
5153
|
</div>
|
|
5008
|
-
|
|
5009
|
-
|
|
5154
|
+
|
|
5155
|
+
<div class="status-bar-right">
|
|
5156
|
+
<div class="status-item">
|
|
5157
|
+
<strong id="protections-count">\u2014</strong>
|
|
5158
|
+
<span>Protections</span>
|
|
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>
|
|
5010
5169
|
</div>
|
|
5011
|
-
<div class="status-dot" id="statusDot"></div>
|
|
5012
|
-
<div class="pending-badge hidden" id="pendingBadge">0</div>
|
|
5013
5170
|
</div>
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
<div class="
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
<div class="
|
|
5027
|
-
|
|
5171
|
+
|
|
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>
|
|
5201
|
+
</div>
|
|
5202
|
+
|
|
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>
|
|
5276
|
+
</div>
|
|
5277
|
+
|
|
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>
|
|
5302
|
+
</div>
|
|
5303
|
+
|
|
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>
|
|
5317
|
+
</div>
|
|
5318
|
+
|
|
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>
|
|
5028
5328
|
</div>
|
|
5029
5329
|
</div>
|
|
5030
5330
|
</div>
|
|
5031
5331
|
|
|
5032
|
-
<!--
|
|
5033
|
-
<div class="
|
|
5034
|
-
<div class="
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
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>
|
|
5336
|
+
</div>
|
|
5337
|
+
|
|
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
|
+
};
|
|
5352
|
+
|
|
5353
|
+
let pendingRequests = new Map();
|
|
5354
|
+
let activityLog = [];
|
|
5355
|
+
const maxActivityItems = 50;
|
|
5356
|
+
|
|
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
|
+
}
|
|
5375
|
+
|
|
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
|
+
}
|
|
5381
|
+
|
|
5382
|
+
function calculateSovereigntyScore(shr) {
|
|
5383
|
+
if (!shr || !shr.layers) return 0;
|
|
5384
|
+
const layers = shr.layers;
|
|
5385
|
+
let score = 100;
|
|
5386
|
+
|
|
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;
|
|
5395
|
+
|
|
5396
|
+
return Math.max(0, Math.min(100, score));
|
|
5397
|
+
}
|
|
5398
|
+
|
|
5399
|
+
async function fetchAPI(endpoint) {
|
|
5400
|
+
try {
|
|
5401
|
+
const response = await fetch(API_BASE + endpoint, {
|
|
5402
|
+
headers: {
|
|
5403
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
5404
|
+
},
|
|
5405
|
+
});
|
|
5406
|
+
|
|
5407
|
+
if (response.status === 401) {
|
|
5408
|
+
redirectToLogin();
|
|
5409
|
+
return null;
|
|
5410
|
+
}
|
|
5411
|
+
|
|
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
|
+
}
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
function redirectToLogin() {
|
|
5425
|
+
sessionStorage.removeItem('authToken');
|
|
5426
|
+
window.location.href = '/';
|
|
5427
|
+
}
|
|
5428
|
+
|
|
5429
|
+
// API Updates
|
|
5430
|
+
async function updateSovereignty() {
|
|
5431
|
+
const data = await fetchAPI('/api/sovereignty');
|
|
5432
|
+
if (!data) return;
|
|
5044
5433
|
|
|
5045
|
-
|
|
5046
|
-
<div class="protection-card-icon">\u2713</div>
|
|
5047
|
-
<div class="protection-card-label">Approval Gate</div>
|
|
5048
|
-
<div class="protection-card-status active" id="approvalStatus">\u2713 Active</div>
|
|
5049
|
-
<div class="protection-card-stat" id="approvalStat">T1: 2 | T2: 3</div>
|
|
5050
|
-
</div>
|
|
5434
|
+
apiState.sovereignty = data;
|
|
5051
5435
|
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
<div class="protection-card-status active" id="contextStatus">\u2713 Active</div>
|
|
5056
|
-
<div class="protection-card-stat" id="contextStat">12 filtered</div>
|
|
5057
|
-
</div>
|
|
5436
|
+
const score = calculateSovereigntyScore(data.shr);
|
|
5437
|
+
const badge = document.getElementById('sovereignty-badge');
|
|
5438
|
+
const scoreEl = document.getElementById('sovereignty-score');
|
|
5058
5439
|
|
|
5059
|
-
|
|
5060
|
-
<div class="protection-card-icon">\u26A0</div>
|
|
5061
|
-
<div class="protection-card-label">Injection Detection</div>
|
|
5062
|
-
<div class="protection-card-status active" id="injectionStatus">\u2713 Active</div>
|
|
5063
|
-
<div class="protection-card-stat" id="injectionStat">3 flags today</div>
|
|
5064
|
-
</div>
|
|
5440
|
+
scoreEl.textContent = score;
|
|
5065
5441
|
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
<div class="protection-card-status active" id="baselineStatus">\u2713 Active</div>
|
|
5070
|
-
<div class="protection-card-stat" id="baselineStat">0 anomalies</div>
|
|
5071
|
-
</div>
|
|
5442
|
+
badge.classList.remove('degraded', 'inactive');
|
|
5443
|
+
if (score < 70) badge.classList.add('degraded');
|
|
5444
|
+
if (score < 40) badge.classList.add('inactive');
|
|
5072
5445
|
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
<div class="protection-card-label">Audit Trail</div>
|
|
5076
|
-
<div class="protection-card-status active" id="auditStatus">\u2713 Active</div>
|
|
5077
|
-
<div class="protection-card-stat" id="auditStat">284 entries</div>
|
|
5078
|
-
</div>
|
|
5079
|
-
</div>
|
|
5080
|
-
</div>
|
|
5081
|
-
</div>
|
|
5446
|
+
updateLayerCards(data.shr);
|
|
5447
|
+
}
|
|
5082
5448
|
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
<div class="pending-overlay-header">
|
|
5086
|
-
<div class="pending-overlay-title">Pending Approvals</div>
|
|
5087
|
-
<button class="pending-overlay-close" onclick="closePendingOverlay()">\xD7</button>
|
|
5088
|
-
</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>
|
|
5449
|
+
function updateLayerCards(shr) {
|
|
5450
|
+
if (!shr || !shr.layers) return;
|
|
5103
5451
|
|
|
5104
|
-
|
|
5105
|
-
(function() {
|
|
5106
|
-
'use strict';
|
|
5452
|
+
const layers = shr.layers;
|
|
5107
5453
|
|
|
5108
|
-
|
|
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
|
+
}
|
|
5109
5459
|
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
const EMBEDDED_TOKEN = ${options.authToken ? JSON.stringify(options.authToken) : "null"};
|
|
5113
|
-
const AUTH_TOKEN = EMBEDDED_TOKEN || (function() { try { return sessionStorage.getItem('sanctuary_token'); } catch(_) { return null; } })();
|
|
5114
|
-
const MAX_ACTIVITY_ITEMS = 100;
|
|
5115
|
-
const MAX_THREAT_ITEMS = 20;
|
|
5460
|
+
function updateLayerCard(layer, layerData, detail) {
|
|
5461
|
+
if (!layerData) return;
|
|
5116
5462
|
|
|
5117
|
-
|
|
5463
|
+
const card = document.querySelector(\`[data-layer="\${layer}"]\`);
|
|
5464
|
+
if (!card) return;
|
|
5118
5465
|
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
let startTime = Date.now();
|
|
5122
|
-
let activityCount = 0;
|
|
5123
|
-
let threatCount = 0;
|
|
5124
|
-
const pendingRequests = new Map();
|
|
5125
|
-
const activityItems = [];
|
|
5126
|
-
const threatItems = [];
|
|
5127
|
-
let sovereigntyScore = 85;
|
|
5128
|
-
let sessionRenewalTimer = null;
|
|
5466
|
+
const status = layerData.status || 'inactive';
|
|
5467
|
+
card.classList.remove('degraded', 'inactive');
|
|
5129
5468
|
|
|
5130
|
-
|
|
5469
|
+
if (status === 'degraded') {
|
|
5470
|
+
card.classList.add('degraded');
|
|
5471
|
+
} else if (status === 'inactive') {
|
|
5472
|
+
card.classList.add('inactive');
|
|
5473
|
+
}
|
|
5131
5474
|
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
return h;
|
|
5136
|
-
}
|
|
5475
|
+
document.getElementById(\`\${layer}-status\`).textContent = status.toUpperCase();
|
|
5476
|
+
document.getElementById(\`\${layer}-detail\`).textContent = detail;
|
|
5477
|
+
}
|
|
5137
5478
|
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
return url + sep + 'session=' + SESSION_ID;
|
|
5142
|
-
}
|
|
5479
|
+
async function updateIdentity() {
|
|
5480
|
+
const data = await fetchAPI('/api/identity');
|
|
5481
|
+
if (!data) return;
|
|
5143
5482
|
|
|
5144
|
-
|
|
5145
|
-
document.cookie = 'sanctuary_session=' + sessionId +
|
|
5146
|
-
'; path=/; SameSite=Strict; max-age=' + maxAge;
|
|
5147
|
-
}
|
|
5483
|
+
apiState.identity = data;
|
|
5148
5484
|
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
// Update cookie with new session
|
|
5158
|
-
setCookie(SESSION_ID, ttl);
|
|
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');
|
|
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';
|
|
5226
5493
|
}
|
|
5227
|
-
}
|
|
5228
5494
|
|
|
5229
|
-
|
|
5495
|
+
async function updateHandshakes() {
|
|
5496
|
+
const data = await fetchAPI('/api/handshakes');
|
|
5497
|
+
if (!data) return;
|
|
5230
5498
|
|
|
5231
|
-
|
|
5232
|
-
const {
|
|
5233
|
-
timestamp,
|
|
5234
|
-
tier,
|
|
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
|
-
};
|
|
5499
|
+
apiState.handshakes = data.handshakes || [];
|
|
5252
5500
|
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5501
|
+
document.getElementById('handshake-count').textContent = data.handshakes?.length || '0';
|
|
5502
|
+
|
|
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
|
+
}
|
|
5514
|
+
|
|
5515
|
+
updateHandshakeTable(data.handshakes || []);
|
|
5256
5516
|
}
|
|
5257
5517
|
|
|
5258
|
-
|
|
5259
|
-
|
|
5518
|
+
function updateHandshakeTable(handshakes) {
|
|
5519
|
+
const table = document.getElementById('handshake-table');
|
|
5260
5520
|
|
|
5261
|
-
|
|
5262
|
-
|
|
5521
|
+
if (!handshakes || handshakes.length === 0) {
|
|
5522
|
+
table.innerHTML = '<div class="table-empty">No handshakes completed yet</div>';
|
|
5523
|
+
return;
|
|
5524
|
+
}
|
|
5263
5525
|
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
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('');
|
|
5267
5540
|
}
|
|
5268
5541
|
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
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
|
-
});
|
|
5542
|
+
async function updateSHR() {
|
|
5543
|
+
const data = await fetchAPI('/api/shr');
|
|
5544
|
+
if (!data) return;
|
|
5303
5545
|
|
|
5304
|
-
|
|
5546
|
+
apiState.shr = data;
|
|
5547
|
+
renderSHRViewer(data);
|
|
5305
5548
|
}
|
|
5306
|
-
}
|
|
5307
5549
|
|
|
5308
|
-
|
|
5550
|
+
function renderSHRViewer(shr) {
|
|
5551
|
+
const viewer = document.getElementById('shr-viewer');
|
|
5309
5552
|
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
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
|
-
};
|
|
5553
|
+
if (!shr) {
|
|
5554
|
+
viewer.innerHTML = '<div class="empty-state">No SHR available</div>';
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5329
5557
|
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
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
|
+
}
|
|
5333
5639
|
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5640
|
+
html += \`
|
|
5641
|
+
</div>
|
|
5642
|
+
</div>
|
|
5643
|
+
\`;
|
|
5644
|
+
}
|
|
5338
5645
|
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
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
|
+
}
|
|
5342
5672
|
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
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
|
+
});
|
|
5350
5707
|
}
|
|
5351
5708
|
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
function renderPendingList() {
|
|
5356
|
-
const list = document.getElementById('pendingList');
|
|
5357
|
-
list.innerHTML = '';
|
|
5709
|
+
async function updateStatus() {
|
|
5710
|
+
const data = await fetchAPI('/api/status');
|
|
5711
|
+
if (!data) return;
|
|
5358
5712
|
|
|
5359
|
-
|
|
5360
|
-
const item = document.createElement('div');
|
|
5361
|
-
item.className = 'pending-item';
|
|
5713
|
+
apiState.status = data;
|
|
5362
5714
|
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
const pct = Math.max(0, Math.min(100, (req.remaining / TIMEOUT_SECONDS) * 100));
|
|
5366
|
-
const isUrgent = req.remaining <= 30;
|
|
5715
|
+
document.getElementById('protections-count').textContent = data.protectionsCount || '0';
|
|
5716
|
+
document.getElementById('uptime-value').textContent = formatUptime(data.uptime);
|
|
5367
5717
|
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
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
|
-
'';
|
|
5718
|
+
const connectionStatus = document.getElementById('connection-status');
|
|
5719
|
+
connectionStatus.classList.toggle('disconnected', !data.connected);
|
|
5720
|
+
}
|
|
5385
5721
|
|
|
5386
|
-
|
|
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\`;
|
|
5387
5728
|
}
|
|
5388
|
-
}
|
|
5389
5729
|
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5730
|
+
// SSE Setup
|
|
5731
|
+
function setupSSE() {
|
|
5732
|
+
const eventSource = new EventSource(API_BASE + '/api/events', {
|
|
5733
|
+
headers: {
|
|
5734
|
+
'Authorization': 'Bearer ' + AUTH_TOKEN,
|
|
5735
|
+
},
|
|
5736
|
+
});
|
|
5395
5737
|
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
}).catch(() => {});
|
|
5400
|
-
};
|
|
5738
|
+
eventSource.addEventListener('init', (e) => {
|
|
5739
|
+
console.log('Connected to SSE');
|
|
5740
|
+
});
|
|
5401
5741
|
|
|
5402
|
-
|
|
5742
|
+
eventSource.addEventListener('sovereignty-update', () => {
|
|
5743
|
+
updateSovereignty();
|
|
5744
|
+
});
|
|
5403
5745
|
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
severity,
|
|
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
|
-
};
|
|
5746
|
+
eventSource.addEventListener('handshake-update', () => {
|
|
5747
|
+
updateHandshakes();
|
|
5748
|
+
});
|
|
5419
5749
|
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
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
|
+
});
|
|
5758
|
+
});
|
|
5424
5759
|
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
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
|
+
});
|
|
5768
|
+
});
|
|
5428
5769
|
|
|
5429
|
-
|
|
5430
|
-
|
|
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);
|
|
5779
|
+
});
|
|
5780
|
+
|
|
5781
|
+
eventSource.addEventListener('pending-request', (e) => {
|
|
5782
|
+
const data = JSON.parse(e.data);
|
|
5783
|
+
addPendingRequest(data);
|
|
5784
|
+
});
|
|
5431
5785
|
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5786
|
+
eventSource.addEventListener('request-resolved', (e) => {
|
|
5787
|
+
const data = JSON.parse(e.data);
|
|
5788
|
+
removePendingRequest(data.requestId);
|
|
5789
|
+
});
|
|
5435
5790
|
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5791
|
+
eventSource.onerror = () => {
|
|
5792
|
+
console.error('SSE error');
|
|
5793
|
+
setTimeout(setupSSE, 5000);
|
|
5794
|
+
};
|
|
5440
5795
|
}
|
|
5441
5796
|
|
|
5442
|
-
|
|
5443
|
-
|
|
5797
|
+
// Activity Feed
|
|
5798
|
+
function addActivityItem(item) {
|
|
5799
|
+
activityLog.unshift(item);
|
|
5800
|
+
if (activityLog.length > maxActivityItems) {
|
|
5801
|
+
activityLog.pop();
|
|
5802
|
+
}
|
|
5444
5803
|
|
|
5445
|
-
|
|
5446
|
-
const
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
'</div>' +
|
|
5454
|
-
'<div>' + esc(threat.details) + '</div>' +
|
|
5455
|
-
'';
|
|
5456
|
-
content.appendChild(div);
|
|
5457
|
-
}
|
|
5458
|
-
}
|
|
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
|
+
\`;
|
|
5459
5812
|
|
|
5460
|
-
|
|
5813
|
+
if (feed.querySelector('.empty-state')) {
|
|
5814
|
+
feed.innerHTML = '';
|
|
5815
|
+
}
|
|
5461
5816
|
|
|
5462
|
-
|
|
5463
|
-
if (evtSource) evtSource.close();
|
|
5464
|
-
connect();
|
|
5465
|
-
}
|
|
5817
|
+
feed.insertAdjacentHTML('afterbegin', html);
|
|
5466
5818
|
|
|
5467
|
-
|
|
5468
|
-
|
|
5819
|
+
if (feed.children.length > maxActivityItems) {
|
|
5820
|
+
feed.lastChild.remove();
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5469
5823
|
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
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
|
+
});
|
|
5473
5832
|
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
};
|
|
5833
|
+
updatePendingDisplay();
|
|
5834
|
+
}
|
|
5477
5835
|
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5836
|
+
function removePendingRequest(requestId) {
|
|
5837
|
+
pendingRequests.delete(requestId);
|
|
5838
|
+
updatePendingDisplay();
|
|
5839
|
+
}
|
|
5840
|
+
|
|
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';
|
|
5485
5850
|
}
|
|
5486
|
-
|
|
5487
|
-
|
|
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;
|
|
5488
5859
|
}
|
|
5489
|
-
});
|
|
5490
5860
|
|
|
5491
|
-
|
|
5492
|
-
const
|
|
5493
|
-
|
|
5494
|
-
|
|
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
|
+
}
|
|
5495
5875
|
|
|
5496
|
-
|
|
5497
|
-
const data = JSON.parse(e.data);
|
|
5498
|
-
removePendingRequest(data.request_id);
|
|
5499
|
-
});
|
|
5876
|
+
items.innerHTML = html;
|
|
5500
5877
|
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
tool: data.tool || 'unknown',
|
|
5507
|
-
outcome: data.outcome || 'executed',
|
|
5508
|
-
detail: data.detail || ''
|
|
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
|
+
});
|
|
5509
5883
|
});
|
|
5510
|
-
});
|
|
5511
5884
|
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
tool: data.tool || 'unknown',
|
|
5518
|
-
outcome: data.outcome || 'gated',
|
|
5519
|
-
detail: data.fields_filtered ? 'Filtered ' + data.fields_filtered + ' fields' : data.reason || '',
|
|
5520
|
-
isContextGated: true
|
|
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
|
+
});
|
|
5521
5890
|
});
|
|
5522
|
-
}
|
|
5891
|
+
}
|
|
5523
5892
|
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
tier: data.tier || 2,
|
|
5529
|
-
tool: data.tool || 'unknown',
|
|
5530
|
-
outcome: data.allowed ? 'allowed' : 'denied',
|
|
5531
|
-
detail: data.signal || 'Injection detected',
|
|
5532
|
-
hasInjection: true
|
|
5533
|
-
});
|
|
5534
|
-
addThreat({
|
|
5535
|
-
timestamp: data.timestamp,
|
|
5536
|
-
severity: data.severity || 'medium',
|
|
5537
|
-
type: 'Injection Alert',
|
|
5538
|
-
details: data.signal || 'Suspicious pattern detected'
|
|
5539
|
-
});
|
|
5540
|
-
});
|
|
5893
|
+
// Threat Panel
|
|
5894
|
+
function addThreatAlert(alert) {
|
|
5895
|
+
const panel = document.querySelector('.threat-panel');
|
|
5896
|
+
const content = document.getElementById('threat-alerts');
|
|
5541
5897
|
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
});
|
|
5898
|
+
if (content.querySelector('.empty-state')) {
|
|
5899
|
+
content.innerHTML = '';
|
|
5900
|
+
}
|
|
5546
5901
|
|
|
5547
|
-
|
|
5548
|
-
const data = JSON.parse(e.data);
|
|
5549
|
-
// Audit entries don't show in activity by default, but we could add them
|
|
5550
|
-
});
|
|
5902
|
+
panel.classList.remove('collapsed');
|
|
5551
5903
|
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
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
|
+
\`;
|
|
5557
5910
|
|
|
5558
|
-
|
|
5559
|
-
if (!baseline) return;
|
|
5560
|
-
// Update baseline-derived stats if needed
|
|
5561
|
-
}
|
|
5911
|
+
content.insertAdjacentHTML('afterbegin', html);
|
|
5562
5912
|
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
// Policy info updated
|
|
5913
|
+
const alerts = content.querySelectorAll('.threat-alert');
|
|
5914
|
+
if (alerts.length > 10) {
|
|
5915
|
+
alerts[alerts.length - 1].remove();
|
|
5916
|
+
}
|
|
5568
5917
|
}
|
|
5569
|
-
}
|
|
5570
5918
|
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
}
|
|
5575
|
-
if (status.active_protections !== undefined) {
|
|
5576
|
-
document.getElementById('activeProtections').textContent = status.active_protections;
|
|
5577
|
-
}
|
|
5578
|
-
// Update individual protection cards
|
|
5579
|
-
if (status.encryption !== undefined) {
|
|
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';
|
|
5598
|
-
}
|
|
5599
|
-
if (status.baseline !== undefined) {
|
|
5600
|
-
const el = document.getElementById('baselineStatus');
|
|
5601
|
-
el.className = 'protection-card-status ' + (status.baseline ? 'active' : 'inactive');
|
|
5602
|
-
el.textContent = status.baseline ? '\u2713 Active' : '\u2717 Inactive';
|
|
5603
|
-
}
|
|
5604
|
-
if (status.audit_trail !== undefined) {
|
|
5605
|
-
const el = document.getElementById('auditStatus');
|
|
5606
|
-
el.className = 'protection-card-status ' + (status.audit_trail ? 'active' : 'inactive');
|
|
5607
|
-
el.textContent = status.audit_trail ? '\u2713 Active' : '\u2717 Inactive';
|
|
5608
|
-
}
|
|
5609
|
-
}
|
|
5919
|
+
// Threat Panel Toggle
|
|
5920
|
+
document.querySelector('.threat-header').addEventListener('click', () => {
|
|
5921
|
+
document.querySelector('.threat-panel').classList.toggle('collapsed');
|
|
5922
|
+
});
|
|
5610
5923
|
|
|
5611
|
-
|
|
5924
|
+
// SHR Copy Button
|
|
5925
|
+
document.getElementById('copy-shr-btn').addEventListener('click', async () => {
|
|
5926
|
+
if (!apiState.shr) return;
|
|
5612
5927
|
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
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
|
+
});
|
|
5620
5941
|
|
|
5621
|
-
//
|
|
5622
|
-
|
|
5623
|
-
|
|
5942
|
+
// Pending Overlay Toggle
|
|
5943
|
+
document.getElementById('pending-item-badge').addEventListener('click', () => {
|
|
5944
|
+
document.getElementById('pending-overlay').classList.toggle('show');
|
|
5945
|
+
});
|
|
5624
5946
|
|
|
5625
|
-
//
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
if (el) {
|
|
5631
|
-
el.textContent = req.remaining + 's';
|
|
5632
|
-
}
|
|
5947
|
+
// Initialize
|
|
5948
|
+
async function initialize() {
|
|
5949
|
+
if (!AUTH_TOKEN) {
|
|
5950
|
+
redirectToLogin();
|
|
5951
|
+
return;
|
|
5633
5952
|
}
|
|
5634
|
-
}, 1000);
|
|
5635
5953
|
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
} catch (e) {
|
|
5645
|
-
// Ignore
|
|
5646
|
-
}
|
|
5647
|
-
})();
|
|
5954
|
+
// Initial data fetch
|
|
5955
|
+
await Promise.all([
|
|
5956
|
+
updateSovereignty(),
|
|
5957
|
+
updateIdentity(),
|
|
5958
|
+
updateHandshakes(),
|
|
5959
|
+
updateSHR(),
|
|
5960
|
+
updateStatus(),
|
|
5961
|
+
]);
|
|
5648
5962
|
|
|
5649
|
-
|
|
5650
|
-
|
|
5963
|
+
// Setup SSE for real-time updates
|
|
5964
|
+
setupSSE();
|
|
5651
5965
|
|
|
5966
|
+
// Refresh status periodically
|
|
5967
|
+
setInterval(updateStatus, 30000);
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
// Start
|
|
5971
|
+
initialize();
|
|
5972
|
+
</script>
|
|
5652
5973
|
</body>
|
|
5653
5974
|
</html>`;
|
|
5654
5975
|
}
|
|
@@ -7431,15 +7752,6 @@ function generateSHR(identityId, opts) {
|
|
|
7431
7752
|
mitigation: "TEE attestation planned for a future release"
|
|
7432
7753
|
});
|
|
7433
7754
|
}
|
|
7434
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
7435
|
-
degradations.push({
|
|
7436
|
-
layer: "l3",
|
|
7437
|
-
code: "COMMITMENT_ONLY",
|
|
7438
|
-
severity: "info",
|
|
7439
|
-
description: "Commitment schemes only (no ZK proofs)",
|
|
7440
|
-
mitigation: "ZK proof support planned for future release"
|
|
7441
|
-
});
|
|
7442
|
-
}
|
|
7443
7755
|
const body = {
|
|
7444
7756
|
shr_version: "1.0",
|
|
7445
7757
|
implementation: {
|
|
@@ -7465,9 +7777,9 @@ function generateSHR(identityId, opts) {
|
|
|
7465
7777
|
attestation_available: config.execution.attestation
|
|
7466
7778
|
},
|
|
7467
7779
|
l3: {
|
|
7468
|
-
status:
|
|
7780
|
+
status: "active",
|
|
7469
7781
|
proof_system: config.disclosure.proof_system,
|
|
7470
|
-
selective_disclosure:
|
|
7782
|
+
selective_disclosure: true
|
|
7471
7783
|
},
|
|
7472
7784
|
l4: {
|
|
7473
7785
|
status: "active",
|
|
@@ -7680,7 +7992,7 @@ function extractAuthorizationSignals(body) {
|
|
|
7680
7992
|
behavioral_baseline_active: false,
|
|
7681
7993
|
// Would need explicit field in SHR v1.1
|
|
7682
7994
|
identity_verified: l1.identity_type === "ed25519" || l1.identity_type !== "none",
|
|
7683
|
-
zero_knowledge_capable: l3.status === "active"
|
|
7995
|
+
zero_knowledge_capable: l3.status === "active",
|
|
7684
7996
|
selective_disclosure_active: l3.selective_disclosure,
|
|
7685
7997
|
reputation_portable: l4.reputation_portable,
|
|
7686
7998
|
handshake_capable: body.capabilities.handshake
|
|
@@ -7758,14 +8070,6 @@ function generateAuthorizationConstraints(body, _degradations) {
|
|
|
7758
8070
|
priority: "high"
|
|
7759
8071
|
});
|
|
7760
8072
|
}
|
|
7761
|
-
if (layers.l3.proof_system === "commitment-only") {
|
|
7762
|
-
constraints.push({
|
|
7763
|
-
type: "restricted_scope",
|
|
7764
|
-
description: "No zero-knowledge proofs available \u2014 entire state context may be visible",
|
|
7765
|
-
rationale: "Proof system is commitment-only (no ZK)",
|
|
7766
|
-
priority: "medium"
|
|
7767
|
-
});
|
|
7768
|
-
}
|
|
7769
8073
|
if (layers.l4.status === "degraded") {
|
|
7770
8074
|
constraints.push({
|
|
7771
8075
|
type: "known_agents_only",
|
|
@@ -8097,6 +8401,154 @@ function deriveTrustTier(level) {
|
|
|
8097
8401
|
}
|
|
8098
8402
|
}
|
|
8099
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
|
+
|
|
8100
8552
|
// src/handshake/tools.ts
|
|
8101
8553
|
function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
8102
8554
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -8282,6 +8734,103 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog) {
|
|
|
8282
8734
|
result: session.result ?? null
|
|
8283
8735
|
});
|
|
8284
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
|
+
}
|
|
8285
8834
|
}
|
|
8286
8835
|
];
|
|
8287
8836
|
return { tools, handshakeResults };
|
|
@@ -11893,11 +12442,6 @@ async function createSanctuaryServer(options) {
|
|
|
11893
12442
|
degradations.push(
|
|
11894
12443
|
"L2 isolation is process-level only; no TEE available"
|
|
11895
12444
|
);
|
|
11896
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
11897
|
-
degradations.push(
|
|
11898
|
-
"L3 proofs are commitment-based only; ZK proofs not yet available"
|
|
11899
|
-
);
|
|
11900
|
-
}
|
|
11901
12445
|
return toolResult({
|
|
11902
12446
|
attestation: {
|
|
11903
12447
|
environment_type: config.execution.environment,
|
|
@@ -11923,7 +12467,7 @@ async function createSanctuaryServer(options) {
|
|
|
11923
12467
|
l1_state_encrypted: true,
|
|
11924
12468
|
l2_execution_isolated: false,
|
|
11925
12469
|
l2_isolation_type: "process-level",
|
|
11926
|
-
l3_proofs_available:
|
|
12470
|
+
l3_proofs_available: true,
|
|
11927
12471
|
l4_reputation_active: true,
|
|
11928
12472
|
overall_level: "mvs",
|
|
11929
12473
|
degradations
|
|
@@ -11946,14 +12490,6 @@ async function createSanctuaryServer(options) {
|
|
|
11946
12490
|
severity: "warning",
|
|
11947
12491
|
mitigation: "TEE support planned for a future release"
|
|
11948
12492
|
});
|
|
11949
|
-
if (config.disclosure.proof_system === "commitment-only") {
|
|
11950
|
-
degradations.push({
|
|
11951
|
-
layer: "l3",
|
|
11952
|
-
description: "Commitment schemes only (no ZK proofs)",
|
|
11953
|
-
severity: "info",
|
|
11954
|
-
mitigation: "ZK proof support planned for v0.2.0"
|
|
11955
|
-
});
|
|
11956
|
-
}
|
|
11957
12493
|
return toolResult({
|
|
11958
12494
|
status: degradations.some((d) => d.severity === "critical") ? "compromised" : degradations.some((d) => d.severity === "warning") ? "degraded" : "healthy",
|
|
11959
12495
|
storage_bytes: storageSizeBytes,
|
|
@@ -11972,7 +12508,7 @@ async function createSanctuaryServer(options) {
|
|
|
11972
12508
|
last_attestation: (/* @__PURE__ */ new Date()).toISOString()
|
|
11973
12509
|
},
|
|
11974
12510
|
l3: {
|
|
11975
|
-
status:
|
|
12511
|
+
status: "active",
|
|
11976
12512
|
proof_system: config.disclosure.proof_system,
|
|
11977
12513
|
circuits_loaded: 0,
|
|
11978
12514
|
proofs_generated_total: 0
|
|
@@ -12252,6 +12788,7 @@ async function createSanctuaryServer(options) {
|
|
|
12252
12788
|
return { server, config };
|
|
12253
12789
|
}
|
|
12254
12790
|
|
|
12791
|
+
exports.ATTESTATION_VERSION = ATTESTATION_VERSION;
|
|
12255
12792
|
exports.ApprovalGate = ApprovalGate;
|
|
12256
12793
|
exports.AuditLog = AuditLog;
|
|
12257
12794
|
exports.AutoApproveChannel = AutoApproveChannel;
|
|
@@ -12283,6 +12820,7 @@ exports.createRangeProof = createRangeProof;
|
|
|
12283
12820
|
exports.createSanctuaryServer = createSanctuaryServer;
|
|
12284
12821
|
exports.evaluateField = evaluateField;
|
|
12285
12822
|
exports.filterContext = filterContext;
|
|
12823
|
+
exports.generateAttestation = generateAttestation;
|
|
12286
12824
|
exports.generateSHR = generateSHR;
|
|
12287
12825
|
exports.getTemplate = getTemplate;
|
|
12288
12826
|
exports.initiateHandshake = initiateHandshake;
|
|
@@ -12294,6 +12832,7 @@ exports.resolveTier = resolveTier;
|
|
|
12294
12832
|
exports.respondToHandshake = respondToHandshake;
|
|
12295
12833
|
exports.signPayload = signPayload;
|
|
12296
12834
|
exports.tierDistribution = tierDistribution;
|
|
12835
|
+
exports.verifyAttestation = verifyAttestation;
|
|
12297
12836
|
exports.verifyBridgeCommitment = verifyBridgeCommitment;
|
|
12298
12837
|
exports.verifyCompletion = verifyCompletion;
|
|
12299
12838
|
exports.verifyPedersenCommitment = verifyPedersenCommitment;
|