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