@sanctuary-framework/mcp-server 0.5.11 → 0.5.13

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/index.js CHANGED
@@ -1,12 +1,12 @@
1
+ import { randomBytes as randomBytes$1, createHmac } from 'crypto';
2
+ import { gcm } from '@noble/ciphers/aes.js';
1
3
  import { sha256 } from '@noble/hashes/sha256';
2
4
  import { hmac } from '@noble/hashes/hmac';
5
+ import { RistrettoPoint, ed25519 } from '@noble/curves/ed25519';
3
6
  import { readFile, mkdir, writeFile, stat, unlink, readdir, chmod, access } from 'fs/promises';
4
7
  import { join } from 'path';
5
8
  import { platform, homedir } from 'os';
6
9
  import { createRequire } from 'module';
7
- import { randomBytes as randomBytes$1, createHmac } from 'crypto';
8
- import { gcm } from '@noble/ciphers/aes.js';
9
- import { RistrettoPoint, ed25519 } from '@noble/curves/ed25519';
10
10
  import { argon2id } from 'hash-wasm';
11
11
  import { hkdf } from '@noble/hashes/hkdf';
12
12
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
@@ -25,6 +25,26 @@ var __export = (target, all) => {
25
25
  for (var name in all)
26
26
  __defProp(target, name, { get: all[name], enumerable: true });
27
27
  };
28
+ function randomBytes(length) {
29
+ if (length <= 0) {
30
+ throw new RangeError("Length must be positive");
31
+ }
32
+ const buf = randomBytes$1(length);
33
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
34
+ }
35
+ function generateIV() {
36
+ return randomBytes(12);
37
+ }
38
+ function generateSalt() {
39
+ return randomBytes(32);
40
+ }
41
+ function generateRandomKey() {
42
+ return randomBytes(32);
43
+ }
44
+ var init_random = __esm({
45
+ "src/core/random.ts"() {
46
+ }
47
+ });
28
48
 
29
49
  // src/core/encoding.ts
30
50
  var encoding_exports = {};
@@ -76,6 +96,42 @@ var init_encoding = __esm({
76
96
  "src/core/encoding.ts"() {
77
97
  }
78
98
  });
99
+ function encrypt(plaintext, key, aad) {
100
+ if (key.length !== 32) {
101
+ throw new Error("Key must be exactly 32 bytes (256 bits)");
102
+ }
103
+ const iv = generateIV();
104
+ const cipher = gcm(key, iv, aad);
105
+ const ciphertext = cipher.encrypt(plaintext);
106
+ return {
107
+ v: 1,
108
+ alg: "aes-256-gcm",
109
+ iv: toBase64url(iv),
110
+ ct: toBase64url(ciphertext),
111
+ ts: (/* @__PURE__ */ new Date()).toISOString()
112
+ };
113
+ }
114
+ function decrypt(payload, key, aad) {
115
+ if (key.length !== 32) {
116
+ throw new Error("Key must be exactly 32 bytes (256 bits)");
117
+ }
118
+ if (payload.v !== 1) {
119
+ throw new Error(`Unsupported payload version: ${payload.v}`);
120
+ }
121
+ if (payload.alg !== "aes-256-gcm") {
122
+ throw new Error(`Unsupported algorithm: ${payload.alg}`);
123
+ }
124
+ const iv = fromBase64url(payload.iv);
125
+ const ciphertext = fromBase64url(payload.ct);
126
+ const cipher = gcm(key, iv, aad);
127
+ return cipher.decrypt(ciphertext);
128
+ }
129
+ var init_encryption = __esm({
130
+ "src/core/encryption.ts"() {
131
+ init_random();
132
+ init_encoding();
133
+ }
134
+ });
79
135
 
80
136
  // src/core/hashing.ts
81
137
  var hashing_exports = {};
@@ -204,6 +260,123 @@ var init_hashing = __esm({
204
260
  init_encoding();
205
261
  }
206
262
  });
263
+
264
+ // src/core/identity.ts
265
+ var identity_exports = {};
266
+ __export(identity_exports, {
267
+ createIdentity: () => createIdentity,
268
+ generateIdentityId: () => generateIdentityId,
269
+ generateKeypair: () => generateKeypair,
270
+ publicKeyToDid: () => publicKeyToDid,
271
+ rotateKeys: () => rotateKeys,
272
+ sign: () => sign,
273
+ verify: () => verify
274
+ });
275
+ function generateKeypair() {
276
+ const privateKey = randomBytes(32);
277
+ const publicKey = ed25519.getPublicKey(privateKey);
278
+ return { publicKey, privateKey };
279
+ }
280
+ function publicKeyToDid(publicKey) {
281
+ const multicodec = new Uint8Array([237, 1, ...publicKey]);
282
+ return `did:key:z${toBase64url(multicodec)}`;
283
+ }
284
+ function generateIdentityId(publicKey) {
285
+ const keyHash = hash(publicKey);
286
+ return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
287
+ }
288
+ function createIdentity(label, encryptionKey, keyProtection) {
289
+ const { publicKey, privateKey } = generateKeypair();
290
+ const identityId = generateIdentityId(publicKey);
291
+ const did = publicKeyToDid(publicKey);
292
+ const now = (/* @__PURE__ */ new Date()).toISOString();
293
+ const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
294
+ privateKey.fill(0);
295
+ const publicIdentity = {
296
+ identity_id: identityId,
297
+ label,
298
+ public_key: toBase64url(publicKey),
299
+ did,
300
+ created_at: now,
301
+ key_type: "ed25519",
302
+ key_protection: keyProtection
303
+ };
304
+ const storedIdentity = {
305
+ ...publicIdentity,
306
+ encrypted_private_key: encryptedPrivateKey,
307
+ rotation_history: []
308
+ };
309
+ return { publicIdentity, storedIdentity };
310
+ }
311
+ function sign(payload, encryptedPrivateKey, encryptionKey) {
312
+ const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
313
+ try {
314
+ return ed25519.sign(payload, privateKey);
315
+ } finally {
316
+ privateKey.fill(0);
317
+ }
318
+ }
319
+ function verify(payload, signature, publicKey) {
320
+ try {
321
+ return ed25519.verify(signature, payload, publicKey);
322
+ } catch {
323
+ return false;
324
+ }
325
+ }
326
+ function rotateKeys(storedIdentity, encryptionKey, reason) {
327
+ const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
328
+ const newIdentityDid = publicKeyToDid(newPublicKey);
329
+ const now = (/* @__PURE__ */ new Date()).toISOString();
330
+ const eventData = JSON.stringify({
331
+ old_public_key: storedIdentity.public_key,
332
+ new_public_key: toBase64url(newPublicKey),
333
+ identity_id: storedIdentity.identity_id,
334
+ reason,
335
+ rotated_at: now
336
+ });
337
+ const eventBytes = new TextEncoder().encode(eventData);
338
+ const signature = sign(
339
+ eventBytes,
340
+ storedIdentity.encrypted_private_key,
341
+ encryptionKey
342
+ );
343
+ const rotationEvent = {
344
+ old_public_key: storedIdentity.public_key,
345
+ new_public_key: toBase64url(newPublicKey),
346
+ identity_id: storedIdentity.identity_id,
347
+ reason,
348
+ rotated_at: now,
349
+ signature: toBase64url(signature)
350
+ };
351
+ const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
352
+ newPrivateKey.fill(0);
353
+ const updatedIdentity = {
354
+ ...storedIdentity,
355
+ public_key: toBase64url(newPublicKey),
356
+ did: newIdentityDid,
357
+ encrypted_private_key: encryptedNewPrivateKey,
358
+ rotation_history: [
359
+ ...storedIdentity.rotation_history,
360
+ {
361
+ old_public_key: storedIdentity.public_key,
362
+ new_public_key: toBase64url(newPublicKey),
363
+ rotation_event: toBase64url(
364
+ new TextEncoder().encode(JSON.stringify(rotationEvent))
365
+ ),
366
+ rotated_at: now
367
+ }
368
+ ]
369
+ };
370
+ return { updatedIdentity, rotationEvent };
371
+ }
372
+ var init_identity = __esm({
373
+ "src/core/identity.ts"() {
374
+ init_encoding();
375
+ init_encryption();
376
+ init_hashing();
377
+ init_random();
378
+ }
379
+ });
207
380
  var require2 = createRequire(import.meta.url);
208
381
  var { version: PKG_VERSION } = require2("../package.json");
209
382
  var SANCTUARY_VERSION = PKG_VERSION;
@@ -381,24 +554,9 @@ function deepMerge(base, override) {
381
554
  }
382
555
  return result;
383
556
  }
384
- function randomBytes(length) {
385
- if (length <= 0) {
386
- throw new RangeError("Length must be positive");
387
- }
388
- const buf = randomBytes$1(length);
389
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
390
- }
391
- function generateIV() {
392
- return randomBytes(12);
393
- }
394
- function generateSalt() {
395
- return randomBytes(32);
396
- }
397
- function generateRandomKey() {
398
- return randomBytes(32);
399
- }
400
557
 
401
558
  // src/storage/filesystem.ts
559
+ init_random();
402
560
  var FilesystemStorage = class {
403
561
  basePath;
404
562
  constructor(basePath) {
@@ -506,141 +664,14 @@ var FilesystemStorage = class {
506
664
  return total;
507
665
  }
508
666
  };
509
- init_encoding();
510
- function encrypt(plaintext, key, aad) {
511
- if (key.length !== 32) {
512
- throw new Error("Key must be exactly 32 bytes (256 bits)");
513
- }
514
- const iv = generateIV();
515
- const cipher = gcm(key, iv, aad);
516
- const ciphertext = cipher.encrypt(plaintext);
517
- return {
518
- v: 1,
519
- alg: "aes-256-gcm",
520
- iv: toBase64url(iv),
521
- ct: toBase64url(ciphertext),
522
- ts: (/* @__PURE__ */ new Date()).toISOString()
523
- };
524
- }
525
- function decrypt(payload, key, aad) {
526
- if (key.length !== 32) {
527
- throw new Error("Key must be exactly 32 bytes (256 bits)");
528
- }
529
- if (payload.v !== 1) {
530
- throw new Error(`Unsupported payload version: ${payload.v}`);
531
- }
532
- if (payload.alg !== "aes-256-gcm") {
533
- throw new Error(`Unsupported algorithm: ${payload.alg}`);
534
- }
535
- const iv = fromBase64url(payload.iv);
536
- const ciphertext = fromBase64url(payload.ct);
537
- const cipher = gcm(key, iv, aad);
538
- return cipher.decrypt(ciphertext);
539
- }
540
667
 
541
668
  // src/l1-cognitive/state-store.ts
669
+ init_encryption();
542
670
  init_hashing();
671
+ init_identity();
543
672
 
544
- // src/core/identity.ts
545
- init_encoding();
546
- init_hashing();
547
- function generateKeypair() {
548
- const privateKey = randomBytes(32);
549
- const publicKey = ed25519.getPublicKey(privateKey);
550
- return { publicKey, privateKey };
551
- }
552
- function publicKeyToDid(publicKey) {
553
- const multicodec = new Uint8Array([237, 1, ...publicKey]);
554
- return `did:key:z${toBase64url(multicodec)}`;
555
- }
556
- function generateIdentityId(publicKey) {
557
- const keyHash = hash(publicKey);
558
- return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
559
- }
560
- function createIdentity(label, encryptionKey, keyProtection) {
561
- const { publicKey, privateKey } = generateKeypair();
562
- const identityId = generateIdentityId(publicKey);
563
- const did = publicKeyToDid(publicKey);
564
- const now = (/* @__PURE__ */ new Date()).toISOString();
565
- const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
566
- privateKey.fill(0);
567
- const publicIdentity = {
568
- identity_id: identityId,
569
- label,
570
- public_key: toBase64url(publicKey),
571
- did,
572
- created_at: now,
573
- key_type: "ed25519",
574
- key_protection: keyProtection
575
- };
576
- const storedIdentity = {
577
- ...publicIdentity,
578
- encrypted_private_key: encryptedPrivateKey,
579
- rotation_history: []
580
- };
581
- return { publicIdentity, storedIdentity };
582
- }
583
- function sign(payload, encryptedPrivateKey, encryptionKey) {
584
- const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
585
- try {
586
- return ed25519.sign(payload, privateKey);
587
- } finally {
588
- privateKey.fill(0);
589
- }
590
- }
591
- function verify(payload, signature, publicKey) {
592
- try {
593
- return ed25519.verify(signature, payload, publicKey);
594
- } catch {
595
- return false;
596
- }
597
- }
598
- function rotateKeys(storedIdentity, encryptionKey, reason) {
599
- const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
600
- const newIdentityDid = publicKeyToDid(newPublicKey);
601
- const now = (/* @__PURE__ */ new Date()).toISOString();
602
- const eventData = JSON.stringify({
603
- old_public_key: storedIdentity.public_key,
604
- new_public_key: toBase64url(newPublicKey),
605
- identity_id: storedIdentity.identity_id,
606
- reason,
607
- rotated_at: now
608
- });
609
- const eventBytes = new TextEncoder().encode(eventData);
610
- const signature = sign(
611
- eventBytes,
612
- storedIdentity.encrypted_private_key,
613
- encryptionKey
614
- );
615
- const rotationEvent = {
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
- signature: toBase64url(signature)
622
- };
623
- const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
624
- newPrivateKey.fill(0);
625
- const updatedIdentity = {
626
- ...storedIdentity,
627
- public_key: toBase64url(newPublicKey),
628
- did: newIdentityDid,
629
- encrypted_private_key: encryptedNewPrivateKey,
630
- rotation_history: [
631
- ...storedIdentity.rotation_history,
632
- {
633
- old_public_key: storedIdentity.public_key,
634
- new_public_key: toBase64url(newPublicKey),
635
- rotation_event: toBase64url(
636
- new TextEncoder().encode(JSON.stringify(rotationEvent))
637
- ),
638
- rotated_at: now
639
- }
640
- ]
641
- };
642
- return { updatedIdentity, rotationEvent };
643
- }
673
+ // src/core/key-derivation.ts
674
+ init_random();
644
675
  init_encoding();
645
676
  var ARGON2_MEMORY_COST = 65536;
646
677
  var ARGON2_TIME_COST = 3;
@@ -1238,7 +1269,9 @@ function toolResult(data) {
1238
1269
  }
1239
1270
 
1240
1271
  // src/l1-cognitive/tools.ts
1272
+ init_identity();
1241
1273
  init_encoding();
1274
+ init_encryption();
1242
1275
  init_encoding();
1243
1276
  var RESERVED_NAMESPACE_PREFIXES2 = [
1244
1277
  "_identities",
@@ -1275,9 +1308,13 @@ var IdentityManager = class {
1275
1308
  get encryptionKey() {
1276
1309
  return derivePurposeKey(this.masterKey, "identity-encryption");
1277
1310
  }
1278
- /** Load identities from storage on startup */
1311
+ /** Load identities from storage on startup.
1312
+ * Returns { total: number of encrypted files found, loaded: number successfully decrypted }.
1313
+ * A mismatch (total > 0, loaded === 0) indicates a wrong master key / missing passphrase.
1314
+ */
1279
1315
  async load() {
1280
1316
  const entries = await this.storage.list("_identities");
1317
+ let failed = 0;
1281
1318
  for (const entry of entries) {
1282
1319
  const raw = await this.storage.read("_identities", entry.key);
1283
1320
  if (!raw) continue;
@@ -1290,8 +1327,10 @@ var IdentityManager = class {
1290
1327
  this.primaryIdentityId = identity.identity_id;
1291
1328
  }
1292
1329
  } catch {
1330
+ failed++;
1293
1331
  }
1294
1332
  }
1333
+ return { total: entries.length, loaded: this.identities.size, failed };
1295
1334
  }
1296
1335
  /** Save an identity to storage */
1297
1336
  async save(identity) {
@@ -1739,6 +1778,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
1739
1778
  }
1740
1779
 
1741
1780
  // src/l2-operational/audit-log.ts
1781
+ init_encryption();
1742
1782
  init_encoding();
1743
1783
  var AuditLog = class {
1744
1784
  storage;
@@ -1836,6 +1876,8 @@ var AuditLog = class {
1836
1876
  // src/l3-disclosure/commitments.ts
1837
1877
  init_hashing();
1838
1878
  init_encoding();
1879
+ init_random();
1880
+ init_encryption();
1839
1881
  init_encoding();
1840
1882
  function createCommitment(value, blindingFactor) {
1841
1883
  const blindingBytes = blindingFactor ? fromBase64url(blindingFactor) : randomBytes(32);
@@ -1916,7 +1958,9 @@ var CommitmentStore = class {
1916
1958
  };
1917
1959
 
1918
1960
  // src/l3-disclosure/policies.ts
1961
+ init_encryption();
1919
1962
  init_encoding();
1963
+ init_random();
1920
1964
  function evaluateDisclosure(policy, context, requestedFields) {
1921
1965
  return requestedFields.map((field) => {
1922
1966
  const exactRule = policy.rules.find((r) => r.context === context);
@@ -2047,6 +2091,9 @@ var PolicyStore = class {
2047
2091
  );
2048
2092
  }
2049
2093
  };
2094
+
2095
+ // src/l3-disclosure/zk-proofs.ts
2096
+ init_random();
2050
2097
  init_encoding();
2051
2098
  var G = RistrettoPoint.BASE;
2052
2099
  var H_INPUT = concatBytes(
@@ -2724,7 +2771,10 @@ function createL3Tools(storage, masterKey, auditLog) {
2724
2771
  }
2725
2772
 
2726
2773
  // src/l4-reputation/reputation-store.ts
2774
+ init_encryption();
2727
2775
  init_encoding();
2776
+ init_random();
2777
+ init_identity();
2728
2778
  function computeMedian(values) {
2729
2779
  if (values.length === 0) return 0;
2730
2780
  const sorted = [...values].sort((a, b) => a - b);
@@ -3619,6 +3669,24 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3619
3669
  }
3620
3670
  const publishType = args.type;
3621
3671
  const veracoreUrl = args.verascore_url || "https://verascore.ai";
3672
+ const ALLOWED_VERASCORE_HOSTS = ["verascore.ai", "www.verascore.ai", "api.verascore.ai"];
3673
+ try {
3674
+ const parsed = new URL(veracoreUrl);
3675
+ if (parsed.protocol !== "https:") {
3676
+ return toolResult({
3677
+ error: `verascore_url must use HTTPS. Got: ${parsed.protocol}`
3678
+ });
3679
+ }
3680
+ if (!ALLOWED_VERASCORE_HOSTS.includes(parsed.hostname)) {
3681
+ return toolResult({
3682
+ error: `verascore_url must point to a known Verascore domain (${ALLOWED_VERASCORE_HOSTS.join(", ")}). Got: ${parsed.hostname}`
3683
+ });
3684
+ }
3685
+ } catch {
3686
+ return toolResult({
3687
+ error: `verascore_url is not a valid URL: ${veracoreUrl}`
3688
+ });
3689
+ }
3622
3690
  const agentId = args.verascore_agent_id || identity.did.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
3623
3691
  let publishData;
3624
3692
  if (args.data) {
@@ -3648,24 +3716,21 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3648
3716
  return toolResult({ error: `Unknown publish type: ${publishType}` });
3649
3717
  }
3650
3718
  }
3651
- const { sign: sign2, createPrivateKey } = await import('crypto');
3652
- const payloadBytes = Buffer.from(JSON.stringify(publishData), "utf-8");
3719
+ const { sign: identitySign } = await Promise.resolve().then(() => (init_identity(), identity_exports));
3720
+ const payloadBytes = new TextEncoder().encode(JSON.stringify(publishData));
3653
3721
  let signatureB64;
3654
3722
  try {
3655
- const signingKey = derivePurposeKey(masterKey, "verascore-publish");
3656
- const privateKey = createPrivateKey({
3657
- key: Buffer.concat([
3658
- Buffer.from("302e020100300506032b657004220420", "hex"),
3659
- // Ed25519 DER PKCS8 prefix
3660
- Buffer.from(signingKey.slice(0, 32))
3661
- ]),
3662
- format: "der",
3663
- type: "pkcs8"
3664
- });
3665
- const sig = sign2(null, payloadBytes, privateKey);
3666
- signatureB64 = sig.toString("base64url");
3723
+ const signingBytes = identitySign(
3724
+ payloadBytes,
3725
+ identity.encrypted_private_key,
3726
+ identityEncryptionKey
3727
+ );
3728
+ signatureB64 = toBase64url(signingBytes);
3667
3729
  } catch (signError) {
3668
- signatureB64 = toBase64url(new Uint8Array(64));
3730
+ return toolResult({
3731
+ error: "Failed to sign publish payload. Identity key may be corrupted.",
3732
+ details: signError instanceof Error ? signError.message : String(signError)
3733
+ });
3669
3734
  }
3670
3735
  const requestBody = {
3671
3736
  agentId,
@@ -3744,7 +3809,9 @@ var DEFAULT_POLICY = {
3744
3809
  "reputation_import",
3745
3810
  "reputation_export",
3746
3811
  "bootstrap_provide_guarantee",
3747
- "decommission_certificate"
3812
+ "decommission_certificate",
3813
+ "reputation_publish"
3814
+ // SEC-039: Explicit Tier 1 — sends data to external API
3748
3815
  ],
3749
3816
  tier2_anomaly: DEFAULT_TIER2,
3750
3817
  tier3_always_allow: [
@@ -3796,7 +3863,9 @@ var DEFAULT_POLICY = {
3796
3863
  "shr_gateway_export",
3797
3864
  "bridge_commit",
3798
3865
  "bridge_verify",
3799
- "bridge_attest"
3866
+ "bridge_attest",
3867
+ "dashboard_open"
3868
+ // SEC-039: Explicit Tier 3 — only generates a URL
3800
3869
  ],
3801
3870
  approval_channel: DEFAULT_CHANNEL
3802
3871
  };
@@ -3904,6 +3973,7 @@ tier1_always_approve:
3904
3973
  - reputation_import
3905
3974
  - reputation_export
3906
3975
  - bootstrap_provide_guarantee
3976
+ - reputation_publish
3907
3977
 
3908
3978
  # \u2500\u2500\u2500 Tier 2: Behavioral Anomaly Detection \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
3909
3979
  # Triggers approval when agent behavior deviates from its baseline.
@@ -3966,6 +4036,7 @@ tier3_always_allow:
3966
4036
  - bridge_commit
3967
4037
  - bridge_verify
3968
4038
  - bridge_attest
4039
+ - dashboard_open
3969
4040
 
3970
4041
  # \u2500\u2500\u2500 Approval Channel \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\u2500\u2500\u2500
3971
4042
  # How Sanctuary reaches you when approval is needed.
@@ -3993,6 +4064,7 @@ async function loadPrincipalPolicy(storagePath) {
3993
4064
  }
3994
4065
 
3995
4066
  // src/principal-policy/baseline.ts
4067
+ init_encryption();
3996
4068
  init_encoding();
3997
4069
  var BASELINE_NAMESPACE = "_principal";
3998
4070
  var BASELINE_KEY = "session-baseline";
@@ -4218,6 +4290,7 @@ function canonicalizeForSigning(body) {
4218
4290
  }
4219
4291
 
4220
4292
  // src/shr/generator.ts
4293
+ init_identity();
4221
4294
  init_encoding();
4222
4295
  var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
4223
4296
  function generateSHR(identityId, opts) {
@@ -5598,7 +5671,9 @@ function generateDashboardHTML(options) {
5598
5671
 
5599
5672
  <script>
5600
5673
  // Constants
5601
- const AUTH_TOKEN = '${options.authToken || ""}' || sessionStorage.getItem('authToken') || '';
5674
+ // SEC-038: Do NOT embed the long-lived auth token in page source.
5675
+ // Use only the session token stored in sessionStorage by the login flow.
5676
+ const AUTH_TOKEN = sessionStorage.getItem('authToken') || '';
5602
5677
  const TIMEOUT_SECONDS = ${options.timeoutSeconds};
5603
5678
  const API_BASE = '';
5604
5679
 
@@ -8111,6 +8186,7 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
8111
8186
  }
8112
8187
 
8113
8188
  // src/shr/verifier.ts
8189
+ init_identity();
8114
8190
  init_encoding();
8115
8191
  function verifySHR(shr, now) {
8116
8192
  const errors = [];
@@ -8533,7 +8609,9 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
8533
8609
  }
8534
8610
 
8535
8611
  // src/handshake/protocol.ts
8612
+ init_identity();
8536
8613
  init_encoding();
8614
+ init_random();
8537
8615
  function generateNonce() {
8538
8616
  return toBase64url(randomBytes(32));
8539
8617
  }
@@ -8700,7 +8778,9 @@ function deriveTrustTier(level) {
8700
8778
  }
8701
8779
 
8702
8780
  // src/handshake/attestation.ts
8781
+ init_identity();
8703
8782
  init_encoding();
8783
+ init_identity();
8704
8784
  init_encoding();
8705
8785
  var ATTESTATION_VERSION = "1.0";
8706
8786
  function deriveTrustTier2(level) {
@@ -9493,10 +9573,13 @@ function createFederationTools(auditLog, handshakeResults) {
9493
9573
 
9494
9574
  // src/bridge/tools.ts
9495
9575
  init_encoding();
9576
+ init_encryption();
9496
9577
  init_encoding();
9497
9578
 
9498
9579
  // src/bridge/bridge.ts
9580
+ init_identity();
9499
9581
  init_encoding();
9582
+ init_random();
9500
9583
  init_hashing();
9501
9584
  function canonicalize(outcome) {
9502
9585
  return stringToBytes(stableStringify(outcome));
@@ -10647,7 +10730,9 @@ function createAuditTools(config) {
10647
10730
  }
10648
10731
 
10649
10732
  // src/l2-operational/context-gate.ts
10733
+ init_encryption();
10650
10734
  init_encoding();
10735
+ init_random();
10651
10736
  init_hashing();
10652
10737
  var MAX_CONTEXT_FIELDS = 1e3;
10653
10738
  var MAX_POLICY_RULES = 50;
@@ -12617,6 +12702,7 @@ function createL2HardeningTools(storagePath, auditLog) {
12617
12702
  }
12618
12703
 
12619
12704
  // src/index.ts
12705
+ init_random();
12620
12706
  init_encoding();
12621
12707
 
12622
12708
  // src/l2-operational/model-provenance.ts
@@ -12863,7 +12949,29 @@ async function createSanctuaryServer(options) {
12863
12949
  keyProtection,
12864
12950
  auditLog
12865
12951
  );
12866
- await identityManager.load();
12952
+ const loadResult = await identityManager.load();
12953
+ if (loadResult.total > 0 && loadResult.loaded === 0) {
12954
+ console.error(
12955
+ `
12956
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
12957
+ \u2551 \u26A0 WARNING: Encrypted identities found but NONE loaded \u2551
12958
+ \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
12959
+ \u2551 ${loadResult.total} encrypted identity file(s) found on disk \u2551
12960
+ \u2551 0 could be decrypted with the current master key \u2551
12961
+ \u2551 \u2551
12962
+ \u2551 This usually means SANCTUARY_PASSPHRASE is missing or \u2551
12963
+ \u2551 incorrect. The server will start but with NO identity data. \u2551
12964
+ \u2551 \u2551
12965
+ \u2551 To fix: set SANCTUARY_PASSPHRASE to the passphrase used \u2551
12966
+ \u2551 when this Sanctuary instance was first configured. \u2551
12967
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
12968
+ `
12969
+ );
12970
+ } else if (loadResult.failed > 0) {
12971
+ console.error(
12972
+ `Warning: ${loadResult.failed} of ${loadResult.total} identity files could not be decrypted (possibly corrupted).`
12973
+ );
12974
+ }
12867
12975
  const l2Tools = [
12868
12976
  {
12869
12977
  name: "sanctuary/exec_attest",