@sanctuary-framework/mcp-server 0.5.6 → 0.5.7
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 +366 -212
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +366 -212
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +231 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -48
- package/dist/index.d.ts +60 -48
- package/dist/index.js +231 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var crypto = require('crypto');
|
|
|
9
9
|
var aes_js = require('@noble/ciphers/aes.js');
|
|
10
10
|
var sha256 = require('@noble/hashes/sha256');
|
|
11
11
|
var hmac = require('@noble/hashes/hmac');
|
|
12
|
+
var ed25519 = require('@noble/curves/ed25519');
|
|
12
13
|
var hashWasm = require('hash-wasm');
|
|
13
14
|
var hkdf = require('@noble/hashes/hkdf');
|
|
14
15
|
var http = require('http');
|
|
@@ -16,7 +17,6 @@ var https = require('https');
|
|
|
16
17
|
var fs = require('fs');
|
|
17
18
|
var child_process = require('child_process');
|
|
18
19
|
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
19
|
-
var ed25519 = require('@noble/curves/ed25519');
|
|
20
20
|
var index_js = require('@modelcontextprotocol/sdk/server/index.js');
|
|
21
21
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
22
22
|
|
|
@@ -560,6 +560,111 @@ var init_hashing = __esm({
|
|
|
560
560
|
init_encoding();
|
|
561
561
|
}
|
|
562
562
|
});
|
|
563
|
+
function generateKeypair() {
|
|
564
|
+
const privateKey = randomBytes(32);
|
|
565
|
+
const publicKey = ed25519.ed25519.getPublicKey(privateKey);
|
|
566
|
+
return { publicKey, privateKey };
|
|
567
|
+
}
|
|
568
|
+
function publicKeyToDid(publicKey) {
|
|
569
|
+
const multicodec = new Uint8Array([237, 1, ...publicKey]);
|
|
570
|
+
return `did:key:z${toBase64url(multicodec)}`;
|
|
571
|
+
}
|
|
572
|
+
function generateIdentityId(publicKey) {
|
|
573
|
+
const keyHash = hash(publicKey);
|
|
574
|
+
return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
575
|
+
}
|
|
576
|
+
function createIdentity(label, encryptionKey, keyProtection) {
|
|
577
|
+
const { publicKey, privateKey } = generateKeypair();
|
|
578
|
+
const identityId = generateIdentityId(publicKey);
|
|
579
|
+
const did = publicKeyToDid(publicKey);
|
|
580
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
581
|
+
const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
|
|
582
|
+
privateKey.fill(0);
|
|
583
|
+
const publicIdentity = {
|
|
584
|
+
identity_id: identityId,
|
|
585
|
+
label,
|
|
586
|
+
public_key: toBase64url(publicKey),
|
|
587
|
+
did,
|
|
588
|
+
created_at: now,
|
|
589
|
+
key_type: "ed25519",
|
|
590
|
+
key_protection: keyProtection
|
|
591
|
+
};
|
|
592
|
+
const storedIdentity = {
|
|
593
|
+
...publicIdentity,
|
|
594
|
+
encrypted_private_key: encryptedPrivateKey,
|
|
595
|
+
rotation_history: []
|
|
596
|
+
};
|
|
597
|
+
return { publicIdentity, storedIdentity };
|
|
598
|
+
}
|
|
599
|
+
function sign(payload, encryptedPrivateKey, encryptionKey) {
|
|
600
|
+
const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
|
|
601
|
+
try {
|
|
602
|
+
return ed25519.ed25519.sign(payload, privateKey);
|
|
603
|
+
} finally {
|
|
604
|
+
privateKey.fill(0);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function verify(payload, signature, publicKey) {
|
|
608
|
+
try {
|
|
609
|
+
return ed25519.ed25519.verify(signature, payload, publicKey);
|
|
610
|
+
} catch {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function rotateKeys(storedIdentity, encryptionKey, reason) {
|
|
615
|
+
const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
|
|
616
|
+
const newIdentityDid = publicKeyToDid(newPublicKey);
|
|
617
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
618
|
+
const eventData = JSON.stringify({
|
|
619
|
+
old_public_key: storedIdentity.public_key,
|
|
620
|
+
new_public_key: toBase64url(newPublicKey),
|
|
621
|
+
identity_id: storedIdentity.identity_id,
|
|
622
|
+
reason,
|
|
623
|
+
rotated_at: now
|
|
624
|
+
});
|
|
625
|
+
const eventBytes = new TextEncoder().encode(eventData);
|
|
626
|
+
const signature = sign(
|
|
627
|
+
eventBytes,
|
|
628
|
+
storedIdentity.encrypted_private_key,
|
|
629
|
+
encryptionKey
|
|
630
|
+
);
|
|
631
|
+
const rotationEvent = {
|
|
632
|
+
old_public_key: storedIdentity.public_key,
|
|
633
|
+
new_public_key: toBase64url(newPublicKey),
|
|
634
|
+
identity_id: storedIdentity.identity_id,
|
|
635
|
+
reason,
|
|
636
|
+
rotated_at: now,
|
|
637
|
+
signature: toBase64url(signature)
|
|
638
|
+
};
|
|
639
|
+
const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
|
|
640
|
+
newPrivateKey.fill(0);
|
|
641
|
+
const updatedIdentity = {
|
|
642
|
+
...storedIdentity,
|
|
643
|
+
public_key: toBase64url(newPublicKey),
|
|
644
|
+
did: newIdentityDid,
|
|
645
|
+
encrypted_private_key: encryptedNewPrivateKey,
|
|
646
|
+
rotation_history: [
|
|
647
|
+
...storedIdentity.rotation_history,
|
|
648
|
+
{
|
|
649
|
+
old_public_key: storedIdentity.public_key,
|
|
650
|
+
new_public_key: toBase64url(newPublicKey),
|
|
651
|
+
rotation_event: toBase64url(
|
|
652
|
+
new TextEncoder().encode(JSON.stringify(rotationEvent))
|
|
653
|
+
),
|
|
654
|
+
rotated_at: now
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
};
|
|
658
|
+
return { updatedIdentity, rotationEvent };
|
|
659
|
+
}
|
|
660
|
+
var init_identity = __esm({
|
|
661
|
+
"src/core/identity.ts"() {
|
|
662
|
+
init_encoding();
|
|
663
|
+
init_encryption();
|
|
664
|
+
init_hashing();
|
|
665
|
+
init_random();
|
|
666
|
+
}
|
|
667
|
+
});
|
|
563
668
|
async function deriveMasterKey(passphrase, existingParams) {
|
|
564
669
|
const salt = existingParams ? fromBase64url(existingParams.salt) : generateSalt();
|
|
565
670
|
const params = existingParams ?? {
|
|
@@ -1164,6 +1269,120 @@ var init_baseline = __esm({
|
|
|
1164
1269
|
}
|
|
1165
1270
|
});
|
|
1166
1271
|
|
|
1272
|
+
// src/shr/types.ts
|
|
1273
|
+
function deepSortKeys(obj) {
|
|
1274
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1275
|
+
if (Array.isArray(obj)) return obj.map(deepSortKeys);
|
|
1276
|
+
const sorted = {};
|
|
1277
|
+
for (const key of Object.keys(obj).sort()) {
|
|
1278
|
+
sorted[key] = deepSortKeys(obj[key]);
|
|
1279
|
+
}
|
|
1280
|
+
return sorted;
|
|
1281
|
+
}
|
|
1282
|
+
function canonicalizeForSigning(body) {
|
|
1283
|
+
return JSON.stringify(deepSortKeys(body));
|
|
1284
|
+
}
|
|
1285
|
+
var init_types = __esm({
|
|
1286
|
+
"src/shr/types.ts"() {
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
// src/shr/generator.ts
|
|
1291
|
+
function generateSHR(identityId, opts) {
|
|
1292
|
+
const { config, identityManager, masterKey, validityMs } = opts;
|
|
1293
|
+
const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
|
|
1294
|
+
if (!identity) {
|
|
1295
|
+
return "No identity available for signing. Create an identity first.";
|
|
1296
|
+
}
|
|
1297
|
+
const now = /* @__PURE__ */ new Date();
|
|
1298
|
+
const expiresAt = new Date(now.getTime() + (validityMs ?? DEFAULT_VALIDITY_MS));
|
|
1299
|
+
const degradations = [];
|
|
1300
|
+
if (config.execution.environment === "local-process") {
|
|
1301
|
+
degradations.push({
|
|
1302
|
+
layer: "l2",
|
|
1303
|
+
code: "PROCESS_ISOLATION_ONLY",
|
|
1304
|
+
severity: "warning",
|
|
1305
|
+
description: "Process-level isolation only (no TEE)",
|
|
1306
|
+
mitigation: "TEE support planned for a future release"
|
|
1307
|
+
});
|
|
1308
|
+
degradations.push({
|
|
1309
|
+
layer: "l2",
|
|
1310
|
+
code: "SELF_REPORTED_ATTESTATION",
|
|
1311
|
+
severity: "warning",
|
|
1312
|
+
description: "Attestation is self-reported (no hardware root of trust)",
|
|
1313
|
+
mitigation: "TEE attestation planned for a future release"
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
const body = {
|
|
1317
|
+
shr_version: "1.0",
|
|
1318
|
+
implementation: {
|
|
1319
|
+
sanctuary_version: config.version,
|
|
1320
|
+
node_version: process.versions.node,
|
|
1321
|
+
generated_by: "sanctuary-mcp-server"
|
|
1322
|
+
},
|
|
1323
|
+
instance_id: identity.identity_id,
|
|
1324
|
+
generated_at: now.toISOString(),
|
|
1325
|
+
expires_at: expiresAt.toISOString(),
|
|
1326
|
+
layers: {
|
|
1327
|
+
l1: {
|
|
1328
|
+
status: "active",
|
|
1329
|
+
encryption: config.state.encryption,
|
|
1330
|
+
key_custody: "self",
|
|
1331
|
+
integrity: config.state.integrity,
|
|
1332
|
+
identity_type: config.state.identity_provider,
|
|
1333
|
+
state_portable: true
|
|
1334
|
+
},
|
|
1335
|
+
l2: {
|
|
1336
|
+
status: config.execution.environment === "local-process" ? "degraded" : "active",
|
|
1337
|
+
isolation_type: config.execution.environment,
|
|
1338
|
+
attestation_available: config.execution.attestation
|
|
1339
|
+
},
|
|
1340
|
+
l3: {
|
|
1341
|
+
status: "active",
|
|
1342
|
+
proof_system: config.disclosure.proof_system,
|
|
1343
|
+
selective_disclosure: true
|
|
1344
|
+
},
|
|
1345
|
+
l4: {
|
|
1346
|
+
status: "active",
|
|
1347
|
+
reputation_mode: config.reputation.mode,
|
|
1348
|
+
attestation_format: config.reputation.attestation_format,
|
|
1349
|
+
reputation_portable: true
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
capabilities: {
|
|
1353
|
+
handshake: true,
|
|
1354
|
+
shr_exchange: true,
|
|
1355
|
+
reputation_verify: true,
|
|
1356
|
+
encrypted_channel: false
|
|
1357
|
+
// Not yet implemented
|
|
1358
|
+
},
|
|
1359
|
+
degradations
|
|
1360
|
+
};
|
|
1361
|
+
const canonical = canonicalizeForSigning(body);
|
|
1362
|
+
const payload = stringToBytes(canonical);
|
|
1363
|
+
const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
|
|
1364
|
+
const signatureBytes = sign(
|
|
1365
|
+
payload,
|
|
1366
|
+
identity.encrypted_private_key,
|
|
1367
|
+
encryptionKey
|
|
1368
|
+
);
|
|
1369
|
+
return {
|
|
1370
|
+
body,
|
|
1371
|
+
signed_by: identity.public_key,
|
|
1372
|
+
signature: toBase64url(signatureBytes)
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
var DEFAULT_VALIDITY_MS;
|
|
1376
|
+
var init_generator = __esm({
|
|
1377
|
+
"src/shr/generator.ts"() {
|
|
1378
|
+
init_types();
|
|
1379
|
+
init_identity();
|
|
1380
|
+
init_encoding();
|
|
1381
|
+
init_key_derivation();
|
|
1382
|
+
DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1167
1386
|
// src/principal-policy/dashboard-html.ts
|
|
1168
1387
|
function generateLoginHTML(options) {
|
|
1169
1388
|
return `<!DOCTYPE html>
|
|
@@ -1171,7 +1390,7 @@ function generateLoginHTML(options) {
|
|
|
1171
1390
|
<head>
|
|
1172
1391
|
<meta charset="UTF-8">
|
|
1173
1392
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1174
|
-
<title>Sanctuary Dashboard</title>
|
|
1393
|
+
<title>Sanctuary \u2014 Principal Dashboard</title>
|
|
1175
1394
|
<style>
|
|
1176
1395
|
:root {
|
|
1177
1396
|
--bg: #0d1117;
|
|
@@ -1342,7 +1561,7 @@ function generateLoginHTML(options) {
|
|
|
1342
1561
|
|
|
1343
1562
|
<form id="login-form">
|
|
1344
1563
|
<div class="form-group">
|
|
1345
|
-
<label for="auth-token">
|
|
1564
|
+
<label for="auth-token">Auth Token</label>
|
|
1346
1565
|
<input
|
|
1347
1566
|
type="text"
|
|
1348
1567
|
id="auth-token"
|
|
@@ -1427,7 +1646,7 @@ function generateDashboardHTML(options) {
|
|
|
1427
1646
|
<head>
|
|
1428
1647
|
<meta charset="UTF-8">
|
|
1429
1648
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1430
|
-
<title>Sanctuary Dashboard</title>
|
|
1649
|
+
<title>Sanctuary \u2014 Principal Dashboard</title>
|
|
1431
1650
|
<style>
|
|
1432
1651
|
:root {
|
|
1433
1652
|
--bg: #0d1117;
|
|
@@ -3078,6 +3297,7 @@ var SESSION_TTL_REMOTE_MS, SESSION_TTL_LOCAL_MS, MAX_SESSIONS, RATE_LIMIT_WINDOW
|
|
|
3078
3297
|
var init_dashboard = __esm({
|
|
3079
3298
|
"src/principal-policy/dashboard.ts"() {
|
|
3080
3299
|
init_config();
|
|
3300
|
+
init_generator();
|
|
3081
3301
|
init_dashboard_html();
|
|
3082
3302
|
SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
|
|
3083
3303
|
SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -3094,6 +3314,10 @@ var init_dashboard = __esm({
|
|
|
3094
3314
|
policy = null;
|
|
3095
3315
|
baseline = null;
|
|
3096
3316
|
auditLog = null;
|
|
3317
|
+
identityManager = null;
|
|
3318
|
+
handshakeResults = null;
|
|
3319
|
+
shrOpts = null;
|
|
3320
|
+
_sanctuaryConfig = null;
|
|
3097
3321
|
dashboardHTML;
|
|
3098
3322
|
loginHTML;
|
|
3099
3323
|
authToken;
|
|
@@ -3127,6 +3351,10 @@ var init_dashboard = __esm({
|
|
|
3127
3351
|
this.policy = deps.policy;
|
|
3128
3352
|
this.baseline = deps.baseline;
|
|
3129
3353
|
this.auditLog = deps.auditLog;
|
|
3354
|
+
if (deps.identityManager) this.identityManager = deps.identityManager;
|
|
3355
|
+
if (deps.handshakeResults) this.handshakeResults = deps.handshakeResults;
|
|
3356
|
+
if (deps.shrOpts) this.shrOpts = deps.shrOpts;
|
|
3357
|
+
if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
|
|
3130
3358
|
}
|
|
3131
3359
|
/**
|
|
3132
3360
|
* Start the HTTP(S) server for the dashboard.
|
|
@@ -3459,6 +3687,14 @@ var init_dashboard = __esm({
|
|
|
3459
3687
|
this.handlePendingList(res);
|
|
3460
3688
|
} else if (method === "GET" && url.pathname === "/api/audit-log") {
|
|
3461
3689
|
this.handleAuditLog(url, res);
|
|
3690
|
+
} else if (method === "GET" && url.pathname === "/api/sovereignty") {
|
|
3691
|
+
this.handleSovereignty(res);
|
|
3692
|
+
} else if (method === "GET" && url.pathname === "/api/identity") {
|
|
3693
|
+
this.handleIdentity(res);
|
|
3694
|
+
} else if (method === "GET" && url.pathname === "/api/handshakes") {
|
|
3695
|
+
this.handleHandshakes(res);
|
|
3696
|
+
} else if (method === "GET" && url.pathname === "/api/shr") {
|
|
3697
|
+
this.handleSHR(res);
|
|
3462
3698
|
} else if (method === "POST" && url.pathname.startsWith("/api/approve/")) {
|
|
3463
3699
|
if (!this.checkRateLimit(req, res, "decisions")) return;
|
|
3464
3700
|
const id = url.pathname.slice("/api/approve/".length);
|
|
@@ -3648,6 +3884,107 @@ data: ${JSON.stringify(initData)}
|
|
|
3648
3884
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3649
3885
|
res.end(JSON.stringify({ success: true, decision }));
|
|
3650
3886
|
}
|
|
3887
|
+
// ── Sovereignty Data Routes ─────────────────────────────────────────
|
|
3888
|
+
handleSovereignty(res) {
|
|
3889
|
+
if (!this.shrOpts) {
|
|
3890
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3891
|
+
res.end(JSON.stringify({ error: "SHR generator not available" }));
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3894
|
+
const shr = generateSHR(void 0, this.shrOpts);
|
|
3895
|
+
if (typeof shr === "string") {
|
|
3896
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3897
|
+
res.end(JSON.stringify({ error: shr }));
|
|
3898
|
+
return;
|
|
3899
|
+
}
|
|
3900
|
+
const layers = shr.body.layers;
|
|
3901
|
+
let score = 0;
|
|
3902
|
+
for (const layer of [layers.l1, layers.l2, layers.l3, layers.l4]) {
|
|
3903
|
+
if (layer.status === "active") score += 25;
|
|
3904
|
+
else if (layer.status === "degraded") score += 15;
|
|
3905
|
+
}
|
|
3906
|
+
const overallLevel = score === 100 ? "full" : score >= 65 ? "degraded" : score >= 25 ? "minimal" : "unverified";
|
|
3907
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3908
|
+
res.end(JSON.stringify({
|
|
3909
|
+
score,
|
|
3910
|
+
overall_level: overallLevel,
|
|
3911
|
+
layers: {
|
|
3912
|
+
l1: { status: layers.l1.status, detail: layers.l1.encryption, key_custody: layers.l1.key_custody },
|
|
3913
|
+
l2: { status: layers.l2.status, detail: layers.l2.isolation_type, attestation: layers.l2.attestation_available },
|
|
3914
|
+
l3: { status: layers.l3.status, detail: layers.l3.proof_system, selective_disclosure: layers.l3.selective_disclosure },
|
|
3915
|
+
l4: { status: layers.l4.status, detail: layers.l4.attestation_format, reputation_portable: layers.l4.reputation_portable }
|
|
3916
|
+
},
|
|
3917
|
+
degradations: shr.body.degradations,
|
|
3918
|
+
capabilities: shr.body.capabilities,
|
|
3919
|
+
config_loaded: this._sanctuaryConfig != null
|
|
3920
|
+
}));
|
|
3921
|
+
}
|
|
3922
|
+
handleIdentity(res) {
|
|
3923
|
+
if (!this.identityManager) {
|
|
3924
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3925
|
+
res.end(JSON.stringify({ identities: [], count: 0 }));
|
|
3926
|
+
return;
|
|
3927
|
+
}
|
|
3928
|
+
const identities = this.identityManager.list().map((id) => ({
|
|
3929
|
+
identity_id: id.identity_id,
|
|
3930
|
+
label: id.label,
|
|
3931
|
+
public_key: id.public_key,
|
|
3932
|
+
did: id.did,
|
|
3933
|
+
created_at: id.created_at,
|
|
3934
|
+
key_type: id.key_type,
|
|
3935
|
+
key_protection: id.key_protection,
|
|
3936
|
+
rotation_count: id.rotation_history?.length ?? 0
|
|
3937
|
+
}));
|
|
3938
|
+
const primary = this.identityManager.getDefault();
|
|
3939
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3940
|
+
res.end(JSON.stringify({
|
|
3941
|
+
identities,
|
|
3942
|
+
count: identities.length,
|
|
3943
|
+
primary_id: primary?.identity_id ?? null
|
|
3944
|
+
}));
|
|
3945
|
+
}
|
|
3946
|
+
handleHandshakes(res) {
|
|
3947
|
+
if (!this.handshakeResults) {
|
|
3948
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3949
|
+
res.end(JSON.stringify({ handshakes: [], count: 0 }));
|
|
3950
|
+
return;
|
|
3951
|
+
}
|
|
3952
|
+
const handshakes = Array.from(this.handshakeResults.values()).map((h) => ({
|
|
3953
|
+
counterparty_id: h.counterparty_id,
|
|
3954
|
+
verified: h.verified,
|
|
3955
|
+
sovereignty_level: h.sovereignty_level,
|
|
3956
|
+
trust_tier: h.trust_tier,
|
|
3957
|
+
completed_at: h.completed_at,
|
|
3958
|
+
expires_at: h.expires_at,
|
|
3959
|
+
errors: h.errors
|
|
3960
|
+
}));
|
|
3961
|
+
handshakes.sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime());
|
|
3962
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3963
|
+
res.end(JSON.stringify({
|
|
3964
|
+
handshakes,
|
|
3965
|
+
count: handshakes.length,
|
|
3966
|
+
tier_distribution: {
|
|
3967
|
+
verified_sovereign: handshakes.filter((h) => h.trust_tier === "verified-sovereign").length,
|
|
3968
|
+
verified_degraded: handshakes.filter((h) => h.trust_tier === "verified-degraded").length,
|
|
3969
|
+
unverified: handshakes.filter((h) => h.trust_tier === "unverified").length
|
|
3970
|
+
}
|
|
3971
|
+
}));
|
|
3972
|
+
}
|
|
3973
|
+
handleSHR(res) {
|
|
3974
|
+
if (!this.shrOpts) {
|
|
3975
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3976
|
+
res.end(JSON.stringify({ error: "SHR generator not available" }));
|
|
3977
|
+
return;
|
|
3978
|
+
}
|
|
3979
|
+
const shr = generateSHR(void 0, this.shrOpts);
|
|
3980
|
+
if (typeof shr === "string") {
|
|
3981
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3982
|
+
res.end(JSON.stringify({ error: shr }));
|
|
3983
|
+
return;
|
|
3984
|
+
}
|
|
3985
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3986
|
+
res.end(JSON.stringify(shr));
|
|
3987
|
+
}
|
|
3651
3988
|
// ── SSE Broadcasting ────────────────────────────────────────────────
|
|
3652
3989
|
broadcastSSE(event, data) {
|
|
3653
3990
|
const message = `event: ${event}
|
|
@@ -3889,111 +4226,7 @@ init_filesystem();
|
|
|
3889
4226
|
// src/l1-cognitive/state-store.ts
|
|
3890
4227
|
init_encryption();
|
|
3891
4228
|
init_hashing();
|
|
3892
|
-
|
|
3893
|
-
// src/core/identity.ts
|
|
3894
|
-
init_encoding();
|
|
3895
|
-
init_encryption();
|
|
3896
|
-
init_hashing();
|
|
3897
|
-
init_random();
|
|
3898
|
-
function generateKeypair() {
|
|
3899
|
-
const privateKey = randomBytes(32);
|
|
3900
|
-
const publicKey = ed25519.ed25519.getPublicKey(privateKey);
|
|
3901
|
-
return { publicKey, privateKey };
|
|
3902
|
-
}
|
|
3903
|
-
function publicKeyToDid(publicKey) {
|
|
3904
|
-
const multicodec = new Uint8Array([237, 1, ...publicKey]);
|
|
3905
|
-
return `did:key:z${toBase64url(multicodec)}`;
|
|
3906
|
-
}
|
|
3907
|
-
function generateIdentityId(publicKey) {
|
|
3908
|
-
const keyHash = hash(publicKey);
|
|
3909
|
-
return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3910
|
-
}
|
|
3911
|
-
function createIdentity(label, encryptionKey, keyProtection) {
|
|
3912
|
-
const { publicKey, privateKey } = generateKeypair();
|
|
3913
|
-
const identityId = generateIdentityId(publicKey);
|
|
3914
|
-
const did = publicKeyToDid(publicKey);
|
|
3915
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3916
|
-
const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
|
|
3917
|
-
privateKey.fill(0);
|
|
3918
|
-
const publicIdentity = {
|
|
3919
|
-
identity_id: identityId,
|
|
3920
|
-
label,
|
|
3921
|
-
public_key: toBase64url(publicKey),
|
|
3922
|
-
did,
|
|
3923
|
-
created_at: now,
|
|
3924
|
-
key_type: "ed25519",
|
|
3925
|
-
key_protection: keyProtection
|
|
3926
|
-
};
|
|
3927
|
-
const storedIdentity = {
|
|
3928
|
-
...publicIdentity,
|
|
3929
|
-
encrypted_private_key: encryptedPrivateKey,
|
|
3930
|
-
rotation_history: []
|
|
3931
|
-
};
|
|
3932
|
-
return { publicIdentity, storedIdentity };
|
|
3933
|
-
}
|
|
3934
|
-
function sign(payload, encryptedPrivateKey, encryptionKey) {
|
|
3935
|
-
const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
|
|
3936
|
-
try {
|
|
3937
|
-
return ed25519.ed25519.sign(payload, privateKey);
|
|
3938
|
-
} finally {
|
|
3939
|
-
privateKey.fill(0);
|
|
3940
|
-
}
|
|
3941
|
-
}
|
|
3942
|
-
function verify(payload, signature, publicKey) {
|
|
3943
|
-
try {
|
|
3944
|
-
return ed25519.ed25519.verify(signature, payload, publicKey);
|
|
3945
|
-
} catch {
|
|
3946
|
-
return false;
|
|
3947
|
-
}
|
|
3948
|
-
}
|
|
3949
|
-
function rotateKeys(storedIdentity, encryptionKey, reason) {
|
|
3950
|
-
const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
|
|
3951
|
-
const newIdentityDid = publicKeyToDid(newPublicKey);
|
|
3952
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3953
|
-
const eventData = JSON.stringify({
|
|
3954
|
-
old_public_key: storedIdentity.public_key,
|
|
3955
|
-
new_public_key: toBase64url(newPublicKey),
|
|
3956
|
-
identity_id: storedIdentity.identity_id,
|
|
3957
|
-
reason,
|
|
3958
|
-
rotated_at: now
|
|
3959
|
-
});
|
|
3960
|
-
const eventBytes = new TextEncoder().encode(eventData);
|
|
3961
|
-
const signature = sign(
|
|
3962
|
-
eventBytes,
|
|
3963
|
-
storedIdentity.encrypted_private_key,
|
|
3964
|
-
encryptionKey
|
|
3965
|
-
);
|
|
3966
|
-
const rotationEvent = {
|
|
3967
|
-
old_public_key: storedIdentity.public_key,
|
|
3968
|
-
new_public_key: toBase64url(newPublicKey),
|
|
3969
|
-
identity_id: storedIdentity.identity_id,
|
|
3970
|
-
reason,
|
|
3971
|
-
rotated_at: now,
|
|
3972
|
-
signature: toBase64url(signature)
|
|
3973
|
-
};
|
|
3974
|
-
const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
|
|
3975
|
-
newPrivateKey.fill(0);
|
|
3976
|
-
const updatedIdentity = {
|
|
3977
|
-
...storedIdentity,
|
|
3978
|
-
public_key: toBase64url(newPublicKey),
|
|
3979
|
-
did: newIdentityDid,
|
|
3980
|
-
encrypted_private_key: encryptedNewPrivateKey,
|
|
3981
|
-
rotation_history: [
|
|
3982
|
-
...storedIdentity.rotation_history,
|
|
3983
|
-
{
|
|
3984
|
-
old_public_key: storedIdentity.public_key,
|
|
3985
|
-
new_public_key: toBase64url(newPublicKey),
|
|
3986
|
-
rotation_event: toBase64url(
|
|
3987
|
-
new TextEncoder().encode(JSON.stringify(rotationEvent))
|
|
3988
|
-
),
|
|
3989
|
-
rotated_at: now
|
|
3990
|
-
}
|
|
3991
|
-
]
|
|
3992
|
-
};
|
|
3993
|
-
return { updatedIdentity, rotationEvent };
|
|
3994
|
-
}
|
|
3995
|
-
|
|
3996
|
-
// src/l1-cognitive/state-store.ts
|
|
4229
|
+
init_identity();
|
|
3997
4230
|
init_key_derivation();
|
|
3998
4231
|
init_encoding();
|
|
3999
4232
|
var RESERVED_NAMESPACE_PREFIXES = [
|
|
@@ -4533,6 +4766,7 @@ function toolResult(data) {
|
|
|
4533
4766
|
}
|
|
4534
4767
|
|
|
4535
4768
|
// src/l1-cognitive/tools.ts
|
|
4769
|
+
init_identity();
|
|
4536
4770
|
init_key_derivation();
|
|
4537
4771
|
init_encoding();
|
|
4538
4772
|
init_encryption();
|
|
@@ -5942,6 +6176,7 @@ init_encryption();
|
|
|
5942
6176
|
init_key_derivation();
|
|
5943
6177
|
init_encoding();
|
|
5944
6178
|
init_random();
|
|
6179
|
+
init_identity();
|
|
5945
6180
|
function computeMedian(values) {
|
|
5946
6181
|
if (values.length === 0) return 0;
|
|
5947
6182
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -7914,110 +8149,12 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
|
7914
8149
|
];
|
|
7915
8150
|
}
|
|
7916
8151
|
|
|
7917
|
-
// src/shr/
|
|
7918
|
-
|
|
7919
|
-
if (obj === null || typeof obj !== "object") return obj;
|
|
7920
|
-
if (Array.isArray(obj)) return obj.map(deepSortKeys);
|
|
7921
|
-
const sorted = {};
|
|
7922
|
-
for (const key of Object.keys(obj).sort()) {
|
|
7923
|
-
sorted[key] = deepSortKeys(obj[key]);
|
|
7924
|
-
}
|
|
7925
|
-
return sorted;
|
|
7926
|
-
}
|
|
7927
|
-
function canonicalizeForSigning(body) {
|
|
7928
|
-
return JSON.stringify(deepSortKeys(body));
|
|
7929
|
-
}
|
|
7930
|
-
|
|
7931
|
-
// src/shr/generator.ts
|
|
7932
|
-
init_encoding();
|
|
7933
|
-
init_key_derivation();
|
|
7934
|
-
var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
|
|
7935
|
-
function generateSHR(identityId, opts) {
|
|
7936
|
-
const { config, identityManager, masterKey, validityMs } = opts;
|
|
7937
|
-
const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
|
|
7938
|
-
if (!identity) {
|
|
7939
|
-
return "No identity available for signing. Create an identity first.";
|
|
7940
|
-
}
|
|
7941
|
-
const now = /* @__PURE__ */ new Date();
|
|
7942
|
-
const expiresAt = new Date(now.getTime() + (validityMs ?? DEFAULT_VALIDITY_MS));
|
|
7943
|
-
const degradations = [];
|
|
7944
|
-
if (config.execution.environment === "local-process") {
|
|
7945
|
-
degradations.push({
|
|
7946
|
-
layer: "l2",
|
|
7947
|
-
code: "PROCESS_ISOLATION_ONLY",
|
|
7948
|
-
severity: "warning",
|
|
7949
|
-
description: "Process-level isolation only (no TEE)",
|
|
7950
|
-
mitigation: "TEE support planned for a future release"
|
|
7951
|
-
});
|
|
7952
|
-
degradations.push({
|
|
7953
|
-
layer: "l2",
|
|
7954
|
-
code: "SELF_REPORTED_ATTESTATION",
|
|
7955
|
-
severity: "warning",
|
|
7956
|
-
description: "Attestation is self-reported (no hardware root of trust)",
|
|
7957
|
-
mitigation: "TEE attestation planned for a future release"
|
|
7958
|
-
});
|
|
7959
|
-
}
|
|
7960
|
-
const body = {
|
|
7961
|
-
shr_version: "1.0",
|
|
7962
|
-
implementation: {
|
|
7963
|
-
sanctuary_version: config.version,
|
|
7964
|
-
node_version: process.versions.node,
|
|
7965
|
-
generated_by: "sanctuary-mcp-server"
|
|
7966
|
-
},
|
|
7967
|
-
instance_id: identity.identity_id,
|
|
7968
|
-
generated_at: now.toISOString(),
|
|
7969
|
-
expires_at: expiresAt.toISOString(),
|
|
7970
|
-
layers: {
|
|
7971
|
-
l1: {
|
|
7972
|
-
status: "active",
|
|
7973
|
-
encryption: config.state.encryption,
|
|
7974
|
-
key_custody: "self",
|
|
7975
|
-
integrity: config.state.integrity,
|
|
7976
|
-
identity_type: config.state.identity_provider,
|
|
7977
|
-
state_portable: true
|
|
7978
|
-
},
|
|
7979
|
-
l2: {
|
|
7980
|
-
status: config.execution.environment === "local-process" ? "degraded" : "active",
|
|
7981
|
-
isolation_type: config.execution.environment,
|
|
7982
|
-
attestation_available: config.execution.attestation
|
|
7983
|
-
},
|
|
7984
|
-
l3: {
|
|
7985
|
-
status: "active",
|
|
7986
|
-
proof_system: config.disclosure.proof_system,
|
|
7987
|
-
selective_disclosure: true
|
|
7988
|
-
},
|
|
7989
|
-
l4: {
|
|
7990
|
-
status: "active",
|
|
7991
|
-
reputation_mode: config.reputation.mode,
|
|
7992
|
-
attestation_format: config.reputation.attestation_format,
|
|
7993
|
-
reputation_portable: true
|
|
7994
|
-
}
|
|
7995
|
-
},
|
|
7996
|
-
capabilities: {
|
|
7997
|
-
handshake: true,
|
|
7998
|
-
shr_exchange: true,
|
|
7999
|
-
reputation_verify: true,
|
|
8000
|
-
encrypted_channel: false
|
|
8001
|
-
// Not yet implemented
|
|
8002
|
-
},
|
|
8003
|
-
degradations
|
|
8004
|
-
};
|
|
8005
|
-
const canonical = canonicalizeForSigning(body);
|
|
8006
|
-
const payload = stringToBytes(canonical);
|
|
8007
|
-
const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
|
|
8008
|
-
const signatureBytes = sign(
|
|
8009
|
-
payload,
|
|
8010
|
-
identity.encrypted_private_key,
|
|
8011
|
-
encryptionKey
|
|
8012
|
-
);
|
|
8013
|
-
return {
|
|
8014
|
-
body,
|
|
8015
|
-
signed_by: identity.public_key,
|
|
8016
|
-
signature: toBase64url(signatureBytes)
|
|
8017
|
-
};
|
|
8018
|
-
}
|
|
8152
|
+
// src/shr/tools.ts
|
|
8153
|
+
init_generator();
|
|
8019
8154
|
|
|
8020
8155
|
// src/shr/verifier.ts
|
|
8156
|
+
init_types();
|
|
8157
|
+
init_identity();
|
|
8021
8158
|
init_encoding();
|
|
8022
8159
|
function verifySHR(shr, now) {
|
|
8023
8160
|
const errors = [];
|
|
@@ -8439,7 +8576,11 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
8439
8576
|
return { tools };
|
|
8440
8577
|
}
|
|
8441
8578
|
|
|
8579
|
+
// src/handshake/tools.ts
|
|
8580
|
+
init_generator();
|
|
8581
|
+
|
|
8442
8582
|
// src/handshake/protocol.ts
|
|
8583
|
+
init_identity();
|
|
8443
8584
|
init_encoding();
|
|
8444
8585
|
init_random();
|
|
8445
8586
|
init_key_derivation();
|
|
@@ -8609,8 +8750,11 @@ function deriveTrustTier(level) {
|
|
|
8609
8750
|
}
|
|
8610
8751
|
|
|
8611
8752
|
// src/handshake/attestation.ts
|
|
8753
|
+
init_types();
|
|
8754
|
+
init_identity();
|
|
8612
8755
|
init_encoding();
|
|
8613
8756
|
init_key_derivation();
|
|
8757
|
+
init_identity();
|
|
8614
8758
|
init_encoding();
|
|
8615
8759
|
var ATTESTATION_VERSION = "1.0";
|
|
8616
8760
|
function deriveTrustTier2(level) {
|
|
@@ -9408,6 +9552,7 @@ init_encryption();
|
|
|
9408
9552
|
init_encoding();
|
|
9409
9553
|
|
|
9410
9554
|
// src/bridge/bridge.ts
|
|
9555
|
+
init_identity();
|
|
9411
9556
|
init_encoding();
|
|
9412
9557
|
init_random();
|
|
9413
9558
|
init_hashing();
|
|
@@ -12496,6 +12641,7 @@ init_filesystem();
|
|
|
12496
12641
|
init_baseline();
|
|
12497
12642
|
init_loader();
|
|
12498
12643
|
init_dashboard();
|
|
12644
|
+
init_generator();
|
|
12499
12645
|
async function createSanctuaryServer(options) {
|
|
12500
12646
|
const config = await loadConfig(options?.configPath);
|
|
12501
12647
|
await promises.mkdir(config.storage_path, { recursive: true, mode: 448 });
|
|
@@ -12849,7 +12995,15 @@ async function createSanctuaryServer(options) {
|
|
|
12849
12995
|
tls: config.dashboard.tls,
|
|
12850
12996
|
auto_open: config.dashboard.auto_open
|
|
12851
12997
|
});
|
|
12852
|
-
dashboard.setDependencies({
|
|
12998
|
+
dashboard.setDependencies({
|
|
12999
|
+
policy,
|
|
13000
|
+
baseline,
|
|
13001
|
+
auditLog,
|
|
13002
|
+
identityManager,
|
|
13003
|
+
handshakeResults,
|
|
13004
|
+
shrOpts: { config, identityManager, masterKey },
|
|
13005
|
+
sanctuaryConfig: config
|
|
13006
|
+
});
|
|
12853
13007
|
await dashboard.start();
|
|
12854
13008
|
approvalChannel = dashboard;
|
|
12855
13009
|
} else if (config.webhook.enabled && config.webhook.url && config.webhook.secret) {
|