@sanctuary-framework/mcp-server 0.5.12 → 0.5.14

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.cjs CHANGED
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var crypto = require('crypto');
4
+ var aes_js = require('@noble/ciphers/aes.js');
3
5
  var sha256 = require('@noble/hashes/sha256');
4
6
  var hmac = require('@noble/hashes/hmac');
7
+ var ed25519 = require('@noble/curves/ed25519');
5
8
  var promises = require('fs/promises');
6
9
  var path = require('path');
7
10
  var os = require('os');
8
11
  var module$1 = require('module');
9
- var crypto = require('crypto');
10
- var aes_js = require('@noble/ciphers/aes.js');
11
- var ed25519 = require('@noble/curves/ed25519');
12
12
  var hashWasm = require('hash-wasm');
13
13
  var hkdf = require('@noble/hashes/hkdf');
14
14
  var index_js = require('@modelcontextprotocol/sdk/server/index.js');
@@ -28,6 +28,26 @@ var __export = (target, all) => {
28
28
  for (var name in all)
29
29
  __defProp(target, name, { get: all[name], enumerable: true });
30
30
  };
31
+ function randomBytes(length) {
32
+ if (length <= 0) {
33
+ throw new RangeError("Length must be positive");
34
+ }
35
+ const buf = crypto.randomBytes(length);
36
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
37
+ }
38
+ function generateIV() {
39
+ return randomBytes(12);
40
+ }
41
+ function generateSalt() {
42
+ return randomBytes(32);
43
+ }
44
+ function generateRandomKey() {
45
+ return randomBytes(32);
46
+ }
47
+ var init_random = __esm({
48
+ "src/core/random.ts"() {
49
+ }
50
+ });
31
51
 
32
52
  // src/core/encoding.ts
33
53
  var encoding_exports = {};
@@ -79,6 +99,42 @@ var init_encoding = __esm({
79
99
  "src/core/encoding.ts"() {
80
100
  }
81
101
  });
102
+ function encrypt(plaintext, key, aad) {
103
+ if (key.length !== 32) {
104
+ throw new Error("Key must be exactly 32 bytes (256 bits)");
105
+ }
106
+ const iv = generateIV();
107
+ const cipher = aes_js.gcm(key, iv, aad);
108
+ const ciphertext = cipher.encrypt(plaintext);
109
+ return {
110
+ v: 1,
111
+ alg: "aes-256-gcm",
112
+ iv: toBase64url(iv),
113
+ ct: toBase64url(ciphertext),
114
+ ts: (/* @__PURE__ */ new Date()).toISOString()
115
+ };
116
+ }
117
+ function decrypt(payload, key, aad) {
118
+ if (key.length !== 32) {
119
+ throw new Error("Key must be exactly 32 bytes (256 bits)");
120
+ }
121
+ if (payload.v !== 1) {
122
+ throw new Error(`Unsupported payload version: ${payload.v}`);
123
+ }
124
+ if (payload.alg !== "aes-256-gcm") {
125
+ throw new Error(`Unsupported algorithm: ${payload.alg}`);
126
+ }
127
+ const iv = fromBase64url(payload.iv);
128
+ const ciphertext = fromBase64url(payload.ct);
129
+ const cipher = aes_js.gcm(key, iv, aad);
130
+ return cipher.decrypt(ciphertext);
131
+ }
132
+ var init_encryption = __esm({
133
+ "src/core/encryption.ts"() {
134
+ init_random();
135
+ init_encoding();
136
+ }
137
+ });
82
138
 
83
139
  // src/core/hashing.ts
84
140
  var hashing_exports = {};
@@ -207,6 +263,123 @@ var init_hashing = __esm({
207
263
  init_encoding();
208
264
  }
209
265
  });
266
+
267
+ // src/core/identity.ts
268
+ var identity_exports = {};
269
+ __export(identity_exports, {
270
+ createIdentity: () => createIdentity,
271
+ generateIdentityId: () => generateIdentityId,
272
+ generateKeypair: () => generateKeypair,
273
+ publicKeyToDid: () => publicKeyToDid,
274
+ rotateKeys: () => rotateKeys,
275
+ sign: () => sign,
276
+ verify: () => verify
277
+ });
278
+ function generateKeypair() {
279
+ const privateKey = randomBytes(32);
280
+ const publicKey = ed25519.ed25519.getPublicKey(privateKey);
281
+ return { publicKey, privateKey };
282
+ }
283
+ function publicKeyToDid(publicKey) {
284
+ const multicodec = new Uint8Array([237, 1, ...publicKey]);
285
+ return `did:key:z${toBase64url(multicodec)}`;
286
+ }
287
+ function generateIdentityId(publicKey) {
288
+ const keyHash = hash(publicKey);
289
+ return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
290
+ }
291
+ function createIdentity(label, encryptionKey, keyProtection) {
292
+ const { publicKey, privateKey } = generateKeypair();
293
+ const identityId = generateIdentityId(publicKey);
294
+ const did = publicKeyToDid(publicKey);
295
+ const now = (/* @__PURE__ */ new Date()).toISOString();
296
+ const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
297
+ privateKey.fill(0);
298
+ const publicIdentity = {
299
+ identity_id: identityId,
300
+ label,
301
+ public_key: toBase64url(publicKey),
302
+ did,
303
+ created_at: now,
304
+ key_type: "ed25519",
305
+ key_protection: keyProtection
306
+ };
307
+ const storedIdentity = {
308
+ ...publicIdentity,
309
+ encrypted_private_key: encryptedPrivateKey,
310
+ rotation_history: []
311
+ };
312
+ return { publicIdentity, storedIdentity };
313
+ }
314
+ function sign(payload, encryptedPrivateKey, encryptionKey) {
315
+ const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
316
+ try {
317
+ return ed25519.ed25519.sign(payload, privateKey);
318
+ } finally {
319
+ privateKey.fill(0);
320
+ }
321
+ }
322
+ function verify(payload, signature, publicKey) {
323
+ try {
324
+ return ed25519.ed25519.verify(signature, payload, publicKey);
325
+ } catch {
326
+ return false;
327
+ }
328
+ }
329
+ function rotateKeys(storedIdentity, encryptionKey, reason) {
330
+ const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
331
+ const newIdentityDid = publicKeyToDid(newPublicKey);
332
+ const now = (/* @__PURE__ */ new Date()).toISOString();
333
+ const eventData = JSON.stringify({
334
+ old_public_key: storedIdentity.public_key,
335
+ new_public_key: toBase64url(newPublicKey),
336
+ identity_id: storedIdentity.identity_id,
337
+ reason,
338
+ rotated_at: now
339
+ });
340
+ const eventBytes = new TextEncoder().encode(eventData);
341
+ const signature = sign(
342
+ eventBytes,
343
+ storedIdentity.encrypted_private_key,
344
+ encryptionKey
345
+ );
346
+ const rotationEvent = {
347
+ old_public_key: storedIdentity.public_key,
348
+ new_public_key: toBase64url(newPublicKey),
349
+ identity_id: storedIdentity.identity_id,
350
+ reason,
351
+ rotated_at: now,
352
+ signature: toBase64url(signature)
353
+ };
354
+ const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
355
+ newPrivateKey.fill(0);
356
+ const updatedIdentity = {
357
+ ...storedIdentity,
358
+ public_key: toBase64url(newPublicKey),
359
+ did: newIdentityDid,
360
+ encrypted_private_key: encryptedNewPrivateKey,
361
+ rotation_history: [
362
+ ...storedIdentity.rotation_history,
363
+ {
364
+ old_public_key: storedIdentity.public_key,
365
+ new_public_key: toBase64url(newPublicKey),
366
+ rotation_event: toBase64url(
367
+ new TextEncoder().encode(JSON.stringify(rotationEvent))
368
+ ),
369
+ rotated_at: now
370
+ }
371
+ ]
372
+ };
373
+ return { updatedIdentity, rotationEvent };
374
+ }
375
+ var init_identity = __esm({
376
+ "src/core/identity.ts"() {
377
+ init_encoding();
378
+ init_encryption();
379
+ init_hashing();
380
+ init_random();
381
+ }
382
+ });
210
383
  var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
211
384
  var { version: PKG_VERSION } = require2("../package.json");
212
385
  var SANCTUARY_VERSION = PKG_VERSION;
@@ -384,24 +557,9 @@ function deepMerge(base, override) {
384
557
  }
385
558
  return result;
386
559
  }
387
- function randomBytes(length) {
388
- if (length <= 0) {
389
- throw new RangeError("Length must be positive");
390
- }
391
- const buf = crypto.randomBytes(length);
392
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
393
- }
394
- function generateIV() {
395
- return randomBytes(12);
396
- }
397
- function generateSalt() {
398
- return randomBytes(32);
399
- }
400
- function generateRandomKey() {
401
- return randomBytes(32);
402
- }
403
560
 
404
561
  // src/storage/filesystem.ts
562
+ init_random();
405
563
  var FilesystemStorage = class {
406
564
  basePath;
407
565
  constructor(basePath) {
@@ -509,141 +667,14 @@ var FilesystemStorage = class {
509
667
  return total;
510
668
  }
511
669
  };
512
- init_encoding();
513
- function encrypt(plaintext, key, aad) {
514
- if (key.length !== 32) {
515
- throw new Error("Key must be exactly 32 bytes (256 bits)");
516
- }
517
- const iv = generateIV();
518
- const cipher = aes_js.gcm(key, iv, aad);
519
- const ciphertext = cipher.encrypt(plaintext);
520
- return {
521
- v: 1,
522
- alg: "aes-256-gcm",
523
- iv: toBase64url(iv),
524
- ct: toBase64url(ciphertext),
525
- ts: (/* @__PURE__ */ new Date()).toISOString()
526
- };
527
- }
528
- function decrypt(payload, key, aad) {
529
- if (key.length !== 32) {
530
- throw new Error("Key must be exactly 32 bytes (256 bits)");
531
- }
532
- if (payload.v !== 1) {
533
- throw new Error(`Unsupported payload version: ${payload.v}`);
534
- }
535
- if (payload.alg !== "aes-256-gcm") {
536
- throw new Error(`Unsupported algorithm: ${payload.alg}`);
537
- }
538
- const iv = fromBase64url(payload.iv);
539
- const ciphertext = fromBase64url(payload.ct);
540
- const cipher = aes_js.gcm(key, iv, aad);
541
- return cipher.decrypt(ciphertext);
542
- }
543
670
 
544
671
  // src/l1-cognitive/state-store.ts
672
+ init_encryption();
545
673
  init_hashing();
674
+ init_identity();
546
675
 
547
- // src/core/identity.ts
548
- init_encoding();
549
- init_hashing();
550
- function generateKeypair() {
551
- const privateKey = randomBytes(32);
552
- const publicKey = ed25519.ed25519.getPublicKey(privateKey);
553
- return { publicKey, privateKey };
554
- }
555
- function publicKeyToDid(publicKey) {
556
- const multicodec = new Uint8Array([237, 1, ...publicKey]);
557
- return `did:key:z${toBase64url(multicodec)}`;
558
- }
559
- function generateIdentityId(publicKey) {
560
- const keyHash = hash(publicKey);
561
- return Array.from(keyHash.slice(0, 16)).map((b) => b.toString(16).padStart(2, "0")).join("");
562
- }
563
- function createIdentity(label, encryptionKey, keyProtection) {
564
- const { publicKey, privateKey } = generateKeypair();
565
- const identityId = generateIdentityId(publicKey);
566
- const did = publicKeyToDid(publicKey);
567
- const now = (/* @__PURE__ */ new Date()).toISOString();
568
- const encryptedPrivateKey = encrypt(privateKey, encryptionKey);
569
- privateKey.fill(0);
570
- const publicIdentity = {
571
- identity_id: identityId,
572
- label,
573
- public_key: toBase64url(publicKey),
574
- did,
575
- created_at: now,
576
- key_type: "ed25519",
577
- key_protection: keyProtection
578
- };
579
- const storedIdentity = {
580
- ...publicIdentity,
581
- encrypted_private_key: encryptedPrivateKey,
582
- rotation_history: []
583
- };
584
- return { publicIdentity, storedIdentity };
585
- }
586
- function sign(payload, encryptedPrivateKey, encryptionKey) {
587
- const privateKey = decrypt(encryptedPrivateKey, encryptionKey);
588
- try {
589
- return ed25519.ed25519.sign(payload, privateKey);
590
- } finally {
591
- privateKey.fill(0);
592
- }
593
- }
594
- function verify(payload, signature, publicKey) {
595
- try {
596
- return ed25519.ed25519.verify(signature, payload, publicKey);
597
- } catch {
598
- return false;
599
- }
600
- }
601
- function rotateKeys(storedIdentity, encryptionKey, reason) {
602
- const { publicKey: newPublicKey, privateKey: newPrivateKey } = generateKeypair();
603
- const newIdentityDid = publicKeyToDid(newPublicKey);
604
- const now = (/* @__PURE__ */ new Date()).toISOString();
605
- const eventData = JSON.stringify({
606
- old_public_key: storedIdentity.public_key,
607
- new_public_key: toBase64url(newPublicKey),
608
- identity_id: storedIdentity.identity_id,
609
- reason,
610
- rotated_at: now
611
- });
612
- const eventBytes = new TextEncoder().encode(eventData);
613
- const signature = sign(
614
- eventBytes,
615
- storedIdentity.encrypted_private_key,
616
- encryptionKey
617
- );
618
- const rotationEvent = {
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
- signature: toBase64url(signature)
625
- };
626
- const encryptedNewPrivateKey = encrypt(newPrivateKey, encryptionKey);
627
- newPrivateKey.fill(0);
628
- const updatedIdentity = {
629
- ...storedIdentity,
630
- public_key: toBase64url(newPublicKey),
631
- did: newIdentityDid,
632
- encrypted_private_key: encryptedNewPrivateKey,
633
- rotation_history: [
634
- ...storedIdentity.rotation_history,
635
- {
636
- old_public_key: storedIdentity.public_key,
637
- new_public_key: toBase64url(newPublicKey),
638
- rotation_event: toBase64url(
639
- new TextEncoder().encode(JSON.stringify(rotationEvent))
640
- ),
641
- rotated_at: now
642
- }
643
- ]
644
- };
645
- return { updatedIdentity, rotationEvent };
646
- }
676
+ // src/core/key-derivation.ts
677
+ init_random();
647
678
  init_encoding();
648
679
  var ARGON2_MEMORY_COST = 65536;
649
680
  var ARGON2_TIME_COST = 3;
@@ -717,7 +748,9 @@ var RESERVED_NAMESPACE_PREFIXES = [
717
748
  "_bridge",
718
749
  "_federation",
719
750
  "_handshake",
720
- "_shr"
751
+ "_shr",
752
+ "_sovereignty_profile",
753
+ "_context_gate_policies"
721
754
  ];
722
755
  var StateStore = class {
723
756
  storage;
@@ -1241,7 +1274,9 @@ function toolResult(data) {
1241
1274
  }
1242
1275
 
1243
1276
  // src/l1-cognitive/tools.ts
1277
+ init_identity();
1244
1278
  init_encoding();
1279
+ init_encryption();
1245
1280
  init_encoding();
1246
1281
  var RESERVED_NAMESPACE_PREFIXES2 = [
1247
1282
  "_identities",
@@ -1256,7 +1291,9 @@ var RESERVED_NAMESPACE_PREFIXES2 = [
1256
1291
  "_bridge",
1257
1292
  "_federation",
1258
1293
  "_handshake",
1259
- "_shr"
1294
+ "_shr",
1295
+ "_sovereignty_profile",
1296
+ "_context_gate_policies"
1260
1297
  ];
1261
1298
  function getReservedNamespaceViolation(namespace) {
1262
1299
  for (const prefix of RESERVED_NAMESPACE_PREFIXES2) {
@@ -1748,6 +1785,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
1748
1785
  }
1749
1786
 
1750
1787
  // src/l2-operational/audit-log.ts
1788
+ init_encryption();
1751
1789
  init_encoding();
1752
1790
  var AuditLog = class {
1753
1791
  storage;
@@ -1845,6 +1883,8 @@ var AuditLog = class {
1845
1883
  // src/l3-disclosure/commitments.ts
1846
1884
  init_hashing();
1847
1885
  init_encoding();
1886
+ init_random();
1887
+ init_encryption();
1848
1888
  init_encoding();
1849
1889
  function createCommitment(value, blindingFactor) {
1850
1890
  const blindingBytes = blindingFactor ? fromBase64url(blindingFactor) : randomBytes(32);
@@ -1925,7 +1965,9 @@ var CommitmentStore = class {
1925
1965
  };
1926
1966
 
1927
1967
  // src/l3-disclosure/policies.ts
1968
+ init_encryption();
1928
1969
  init_encoding();
1970
+ init_random();
1929
1971
  function evaluateDisclosure(policy, context, requestedFields) {
1930
1972
  return requestedFields.map((field) => {
1931
1973
  const exactRule = policy.rules.find((r) => r.context === context);
@@ -2056,6 +2098,9 @@ var PolicyStore = class {
2056
2098
  );
2057
2099
  }
2058
2100
  };
2101
+
2102
+ // src/l3-disclosure/zk-proofs.ts
2103
+ init_random();
2059
2104
  init_encoding();
2060
2105
  var G = ed25519.RistrettoPoint.BASE;
2061
2106
  var H_INPUT = concatBytes(
@@ -2733,7 +2778,10 @@ function createL3Tools(storage, masterKey, auditLog) {
2733
2778
  }
2734
2779
 
2735
2780
  // src/l4-reputation/reputation-store.ts
2781
+ init_encryption();
2736
2782
  init_encoding();
2783
+ init_random();
2784
+ init_identity();
2737
2785
  function computeMedian(values) {
2738
2786
  if (values.length === 0) return 0;
2739
2787
  const sorted = [...values].sort((a, b) => a - b);
@@ -3628,6 +3676,24 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3628
3676
  }
3629
3677
  const publishType = args.type;
3630
3678
  const veracoreUrl = args.verascore_url || "https://verascore.ai";
3679
+ const ALLOWED_VERASCORE_HOSTS = ["verascore.ai", "www.verascore.ai", "api.verascore.ai"];
3680
+ try {
3681
+ const parsed = new URL(veracoreUrl);
3682
+ if (parsed.protocol !== "https:") {
3683
+ return toolResult({
3684
+ error: `verascore_url must use HTTPS. Got: ${parsed.protocol}`
3685
+ });
3686
+ }
3687
+ if (!ALLOWED_VERASCORE_HOSTS.includes(parsed.hostname)) {
3688
+ return toolResult({
3689
+ error: `verascore_url must point to a known Verascore domain (${ALLOWED_VERASCORE_HOSTS.join(", ")}). Got: ${parsed.hostname}`
3690
+ });
3691
+ }
3692
+ } catch {
3693
+ return toolResult({
3694
+ error: `verascore_url is not a valid URL: ${veracoreUrl}`
3695
+ });
3696
+ }
3631
3697
  const agentId = args.verascore_agent_id || identity.did.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
3632
3698
  let publishData;
3633
3699
  if (args.data) {
@@ -3657,24 +3723,21 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3657
3723
  return toolResult({ error: `Unknown publish type: ${publishType}` });
3658
3724
  }
3659
3725
  }
3660
- const { sign: sign2, createPrivateKey } = await import('crypto');
3661
- const payloadBytes = Buffer.from(JSON.stringify(publishData), "utf-8");
3726
+ const { sign: identitySign } = await Promise.resolve().then(() => (init_identity(), identity_exports));
3727
+ const payloadBytes = new TextEncoder().encode(JSON.stringify(publishData));
3662
3728
  let signatureB64;
3663
3729
  try {
3664
- const signingKey = derivePurposeKey(masterKey, "verascore-publish");
3665
- const privateKey = createPrivateKey({
3666
- key: Buffer.concat([
3667
- Buffer.from("302e020100300506032b657004220420", "hex"),
3668
- // Ed25519 DER PKCS8 prefix
3669
- Buffer.from(signingKey.slice(0, 32))
3670
- ]),
3671
- format: "der",
3672
- type: "pkcs8"
3673
- });
3674
- const sig = sign2(null, payloadBytes, privateKey);
3675
- signatureB64 = sig.toString("base64url");
3730
+ const signingBytes = identitySign(
3731
+ payloadBytes,
3732
+ identity.encrypted_private_key,
3733
+ identityEncryptionKey
3734
+ );
3735
+ signatureB64 = toBase64url(signingBytes);
3676
3736
  } catch (signError) {
3677
- signatureB64 = toBase64url(new Uint8Array(64));
3737
+ return toolResult({
3738
+ error: "Failed to sign publish payload. Identity key may be corrupted.",
3739
+ details: signError instanceof Error ? signError.message : String(signError)
3740
+ });
3678
3741
  }
3679
3742
  const requestBody = {
3680
3743
  agentId,
@@ -3753,7 +3816,11 @@ var DEFAULT_POLICY = {
3753
3816
  "reputation_import",
3754
3817
  "reputation_export",
3755
3818
  "bootstrap_provide_guarantee",
3756
- "decommission_certificate"
3819
+ "decommission_certificate",
3820
+ "reputation_publish",
3821
+ // SEC-039: Explicit Tier 1 — sends data to external API
3822
+ "sovereignty_profile_update"
3823
+ // Changes enforcement behavior — always requires approval
3757
3824
  ],
3758
3825
  tier2_anomaly: DEFAULT_TIER2,
3759
3826
  tier3_always_allow: [
@@ -3805,7 +3872,11 @@ var DEFAULT_POLICY = {
3805
3872
  "shr_gateway_export",
3806
3873
  "bridge_commit",
3807
3874
  "bridge_verify",
3808
- "bridge_attest"
3875
+ "bridge_attest",
3876
+ "dashboard_open",
3877
+ // SEC-039: Explicit Tier 3 — only generates a URL
3878
+ "sovereignty_profile_get",
3879
+ "sovereignty_profile_generate_prompt"
3809
3880
  ],
3810
3881
  approval_channel: DEFAULT_CHANNEL
3811
3882
  };
@@ -3913,6 +3984,8 @@ tier1_always_approve:
3913
3984
  - reputation_import
3914
3985
  - reputation_export
3915
3986
  - bootstrap_provide_guarantee
3987
+ - reputation_publish
3988
+ - sovereignty_profile_update
3916
3989
 
3917
3990
  # \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
3918
3991
  # Triggers approval when agent behavior deviates from its baseline.
@@ -3975,6 +4048,9 @@ tier3_always_allow:
3975
4048
  - bridge_commit
3976
4049
  - bridge_verify
3977
4050
  - bridge_attest
4051
+ - dashboard_open
4052
+ - sovereignty_profile_get
4053
+ - sovereignty_profile_generate_prompt
3978
4054
 
3979
4055
  # \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
3980
4056
  # How Sanctuary reaches you when approval is needed.
@@ -4002,6 +4078,7 @@ async function loadPrincipalPolicy(storagePath) {
4002
4078
  }
4003
4079
 
4004
4080
  // src/principal-policy/baseline.ts
4081
+ init_encryption();
4005
4082
  init_encoding();
4006
4083
  var BASELINE_NAMESPACE = "_principal";
4007
4084
  var BASELINE_KEY = "session-baseline";
@@ -4227,6 +4304,7 @@ function canonicalizeForSigning(body) {
4227
4304
  }
4228
4305
 
4229
4306
  // src/shr/generator.ts
4307
+ init_identity();
4230
4308
  init_encoding();
4231
4309
  var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
4232
4310
  function generateSHR(identityId, opts) {
@@ -5179,73 +5257,200 @@ function generateDashboardHTML(options) {
5179
5257
  right: 0;
5180
5258
  }
5181
5259
 
5182
- .pending-header {
5183
- padding: 16px 20px;
5184
- border-bottom: 1px solid var(--border);
5185
- font-weight: 600;
5186
- color: var(--text-primary);
5260
+ .pending-header {
5261
+ padding: 16px 20px;
5262
+ border-bottom: 1px solid var(--border);
5263
+ font-weight: 600;
5264
+ color: var(--text-primary);
5265
+ }
5266
+
5267
+ .pending-items {
5268
+ flex: 1;
5269
+ overflow-y: auto;
5270
+ padding: 16px;
5271
+ }
5272
+
5273
+ .pending-item {
5274
+ background-color: var(--bg);
5275
+ border: 1px solid var(--border);
5276
+ border-radius: 6px;
5277
+ padding: 16px;
5278
+ margin-bottom: 12px;
5279
+ }
5280
+
5281
+ .pending-title {
5282
+ font-weight: 600;
5283
+ color: var(--text-primary);
5284
+ margin-bottom: 8px;
5285
+ word-break: break-word;
5286
+ }
5287
+
5288
+ .pending-countdown {
5289
+ font-size: 12px;
5290
+ color: var(--amber);
5291
+ margin-bottom: 12px;
5292
+ font-weight: 500;
5293
+ }
5294
+
5295
+ .pending-actions {
5296
+ display: flex;
5297
+ gap: 8px;
5298
+ }
5299
+
5300
+ .pending-btn {
5301
+ flex: 1;
5302
+ padding: 8px 12px;
5303
+ border: none;
5304
+ border-radius: 4px;
5305
+ font-size: 12px;
5306
+ font-weight: 600;
5307
+ cursor: pointer;
5308
+ transition: background-color 0.2s;
5309
+ }
5310
+
5311
+ .pending-approve {
5312
+ background-color: var(--green);
5313
+ color: var(--bg);
5314
+ }
5315
+
5316
+ .pending-approve:hover {
5317
+ background-color: #3fa040;
5318
+ }
5319
+
5320
+ .pending-deny {
5321
+ background-color: var(--red);
5322
+ color: var(--bg);
5323
+ }
5324
+
5325
+ .pending-deny:hover {
5326
+ background-color: #e03c3c;
5327
+ }
5328
+
5329
+ /* Sovereignty Profile Panel */
5330
+ .profile-panel {
5331
+ background-color: var(--surface);
5332
+ border: 1px solid var(--border);
5333
+ border-radius: 8px;
5334
+ padding: 20px;
5335
+ }
5336
+
5337
+ .profile-panel .panel-header {
5338
+ display: flex;
5339
+ justify-content: space-between;
5340
+ align-items: center;
5341
+ margin-bottom: 16px;
5342
+ }
5343
+
5344
+ .profile-panel .panel-title {
5345
+ font-size: 14px;
5346
+ font-weight: 600;
5347
+ color: var(--text-primary);
5348
+ }
5349
+
5350
+ .profile-cards {
5351
+ display: grid;
5352
+ grid-template-columns: repeat(5, 1fr);
5353
+ gap: 12px;
5354
+ margin-bottom: 16px;
5355
+ }
5356
+
5357
+ .profile-card {
5358
+ background-color: var(--bg);
5359
+ border: 1px solid var(--border);
5360
+ border-radius: 6px;
5361
+ padding: 14px;
5362
+ display: flex;
5363
+ flex-direction: column;
5364
+ gap: 8px;
5365
+ }
5366
+
5367
+ .profile-card-name {
5368
+ font-size: 12px;
5369
+ font-weight: 600;
5370
+ color: var(--text-primary);
5371
+ }
5372
+
5373
+ .profile-card-desc {
5374
+ font-size: 11px;
5375
+ color: var(--text-secondary);
5376
+ line-height: 1.4;
5377
+ }
5378
+
5379
+ .profile-badge {
5380
+ display: inline-flex;
5381
+ align-items: center;
5382
+ gap: 4px;
5383
+ padding: 2px 8px;
5384
+ border-radius: 4px;
5385
+ font-size: 10px;
5386
+ font-weight: 600;
5387
+ width: fit-content;
5388
+ }
5389
+
5390
+ .profile-badge.enabled {
5391
+ background-color: rgba(63, 185, 80, 0.15);
5392
+ color: var(--green);
5393
+ }
5394
+
5395
+ .profile-badge.disabled {
5396
+ background-color: rgba(139, 148, 158, 0.15);
5397
+ color: var(--text-secondary);
5187
5398
  }
5188
5399
 
5189
- .pending-items {
5190
- flex: 1;
5191
- overflow-y: auto;
5192
- padding: 16px;
5400
+ .prompt-section {
5401
+ margin-top: 12px;
5193
5402
  }
5194
5403
 
5195
- .pending-item {
5404
+ .prompt-textarea {
5405
+ width: 100%;
5406
+ min-height: 120px;
5196
5407
  background-color: var(--bg);
5197
5408
  border: 1px solid var(--border);
5198
5409
  border-radius: 6px;
5199
- padding: 16px;
5200
- margin-bottom: 12px;
5201
- }
5202
-
5203
- .pending-title {
5204
- font-weight: 600;
5205
5410
  color: var(--text-primary);
5206
- margin-bottom: 8px;
5207
- word-break: break-word;
5208
- }
5209
-
5210
- .pending-countdown {
5411
+ font-family: 'JetBrains Mono', monospace;
5211
5412
  font-size: 12px;
5212
- color: var(--amber);
5213
- margin-bottom: 12px;
5214
- font-weight: 500;
5413
+ padding: 12px;
5414
+ resize: vertical;
5415
+ margin-top: 8px;
5215
5416
  }
5216
5417
 
5217
- .pending-actions {
5418
+ .prompt-actions {
5218
5419
  display: flex;
5219
5420
  gap: 8px;
5421
+ margin-top: 8px;
5220
5422
  }
5221
5423
 
5222
- .pending-btn {
5223
- flex: 1;
5224
- padding: 8px 12px;
5225
- border: none;
5424
+ .prompt-btn {
5425
+ padding: 6px 12px;
5426
+ border: 1px solid var(--border);
5226
5427
  border-radius: 4px;
5428
+ background-color: var(--surface);
5429
+ color: var(--text-primary);
5227
5430
  font-size: 12px;
5228
- font-weight: 600;
5229
5431
  cursor: pointer;
5230
- transition: background-color 0.2s;
5231
5432
  }
5232
5433
 
5233
- .pending-approve {
5234
- background-color: var(--green);
5235
- color: var(--bg);
5434
+ .prompt-btn:hover {
5435
+ background-color: var(--muted);
5236
5436
  }
5237
5437
 
5238
- .pending-approve:hover {
5239
- background-color: #3fa040;
5438
+ .prompt-btn.primary {
5439
+ background-color: var(--blue);
5440
+ color: var(--bg);
5441
+ border-color: var(--blue);
5240
5442
  }
5241
5443
 
5242
- .pending-deny {
5243
- background-color: var(--red);
5244
- color: var(--bg);
5444
+ @media (max-width: 900px) {
5445
+ .profile-cards {
5446
+ grid-template-columns: repeat(2, 1fr);
5447
+ }
5245
5448
  }
5246
5449
 
5247
- .pending-deny:hover {
5248
- background-color: #e03c3c;
5450
+ @media (max-width: 500px) {
5451
+ .profile-cards {
5452
+ grid-template-columns: 1fr;
5453
+ }
5249
5454
  }
5250
5455
 
5251
5456
  /* Threat Panel */
@@ -5586,6 +5791,48 @@ function generateDashboardHTML(options) {
5586
5791
  </div>
5587
5792
  </div>
5588
5793
 
5794
+ <!-- Sovereignty Profile Panel -->
5795
+ <div class="profile-panel" id="sovereignty-profile-panel">
5796
+ <div class="panel-header">
5797
+ <div class="panel-title">Sovereignty Profile</div>
5798
+ <span class="card-value" id="profile-updated-at" style="font-size: 11px; color: var(--text-secondary);">\u2014</span>
5799
+ </div>
5800
+ <div class="profile-cards" id="profile-cards">
5801
+ <div class="profile-card" data-feature="audit_logging">
5802
+ <div class="profile-card-name">Audit Logging</div>
5803
+ <div class="profile-badge disabled" id="badge-audit_logging">OFF</div>
5804
+ <div class="profile-card-desc">Encrypted audit trail of all tool calls</div>
5805
+ </div>
5806
+ <div class="profile-card" data-feature="injection_detection">
5807
+ <div class="profile-card-name">Injection Detection</div>
5808
+ <div class="profile-badge disabled" id="badge-injection_detection">OFF</div>
5809
+ <div class="profile-card-desc">Scans tool arguments for prompt injection</div>
5810
+ </div>
5811
+ <div class="profile-card" data-feature="context_gating">
5812
+ <div class="profile-card-name">Context Gating</div>
5813
+ <div class="profile-badge disabled" id="badge-context_gating">OFF</div>
5814
+ <div class="profile-card-desc">Controls context flow to remote providers</div>
5815
+ </div>
5816
+ <div class="profile-card" data-feature="approval_gate">
5817
+ <div class="profile-card-name">Approval Gates</div>
5818
+ <div class="profile-badge disabled" id="badge-approval_gate">OFF</div>
5819
+ <div class="profile-card-desc">Human approval for high-risk operations</div>
5820
+ </div>
5821
+ <div class="profile-card" data-feature="zk_proofs">
5822
+ <div class="profile-card-name">ZK Proofs</div>
5823
+ <div class="profile-badge disabled" id="badge-zk_proofs">OFF</div>
5824
+ <div class="profile-card-desc">Prove claims without revealing data</div>
5825
+ </div>
5826
+ </div>
5827
+ <div class="prompt-section">
5828
+ <div class="prompt-actions">
5829
+ <button class="prompt-btn primary" id="generate-prompt-btn">Generate System Prompt</button>
5830
+ <button class="prompt-btn" id="copy-prompt-btn" style="display:none;">Copy</button>
5831
+ </div>
5832
+ <textarea class="prompt-textarea" id="system-prompt-output" readonly style="display:none;" placeholder="Click 'Generate System Prompt' to create an agent instruction snippet..."></textarea>
5833
+ </div>
5834
+ </div>
5835
+
5589
5836
  <!-- Threat Panel -->
5590
5837
  <div class="threat-panel collapsed">
5591
5838
  <div class="threat-header">
@@ -5607,7 +5854,9 @@ function generateDashboardHTML(options) {
5607
5854
 
5608
5855
  <script>
5609
5856
  // Constants
5610
- const AUTH_TOKEN = '${options.authToken || ""}' || sessionStorage.getItem('authToken') || '';
5857
+ // SEC-038: Do NOT embed the long-lived auth token in page source.
5858
+ // Use only the session token stored in sessionStorage by the login flow.
5859
+ const AUTH_TOKEN = sessionStorage.getItem('authToken') || '';
5611
5860
  const TIMEOUT_SECONDS = ${options.timeoutSeconds};
5612
5861
  const API_BASE = '';
5613
5862
 
@@ -5618,6 +5867,7 @@ function generateDashboardHTML(options) {
5618
5867
  handshakes: [],
5619
5868
  shr: null,
5620
5869
  status: null,
5870
+ systemPrompt: null,
5621
5871
  };
5622
5872
 
5623
5873
  let pendingRequests = new Map();
@@ -6070,6 +6320,10 @@ function generateDashboardHTML(options) {
6070
6320
  removePendingRequest(data.requestId);
6071
6321
  });
6072
6322
 
6323
+ eventSource.addEventListener('sovereignty-profile-update', () => {
6324
+ updateSovereigntyProfile();
6325
+ });
6326
+
6073
6327
  eventSource.onerror = () => {
6074
6328
  console.error('SSE error');
6075
6329
  setTimeout(setupSSE, 5000);
@@ -6226,6 +6480,58 @@ function generateDashboardHTML(options) {
6226
6480
  document.getElementById('pending-overlay').classList.toggle('show');
6227
6481
  });
6228
6482
 
6483
+ // Sovereignty Profile
6484
+ async function updateSovereigntyProfile() {
6485
+ try {
6486
+ const data = await fetchAPI('/api/sovereignty-profile');
6487
+ if (data && data.profile) {
6488
+ const features = data.profile.features;
6489
+ for (const [key, value] of Object.entries(features)) {
6490
+ const badge = document.getElementById('badge-' + key);
6491
+ if (badge) {
6492
+ const enabled = value && value.enabled;
6493
+ badge.textContent = enabled ? 'ON' : 'OFF';
6494
+ badge.className = 'profile-badge ' + (enabled ? 'enabled' : 'disabled');
6495
+ }
6496
+ }
6497
+ const updatedEl = document.getElementById('profile-updated-at');
6498
+ if (updatedEl && data.profile.updated_at) {
6499
+ updatedEl.textContent = 'Updated: ' + new Date(data.profile.updated_at).toLocaleString();
6500
+ }
6501
+ // Cache the prompt
6502
+ if (data.system_prompt) {
6503
+ apiState.systemPrompt = data.system_prompt;
6504
+ }
6505
+ }
6506
+ } catch (e) {
6507
+ // Profile not available
6508
+ }
6509
+ }
6510
+
6511
+ document.getElementById('generate-prompt-btn').addEventListener('click', async () => {
6512
+ const data = await fetchAPI('/api/sovereignty-profile');
6513
+ if (data && data.system_prompt) {
6514
+ const textarea = document.getElementById('system-prompt-output');
6515
+ const copyBtn = document.getElementById('copy-prompt-btn');
6516
+ textarea.value = data.system_prompt;
6517
+ textarea.style.display = 'block';
6518
+ copyBtn.style.display = 'inline-flex';
6519
+ }
6520
+ });
6521
+
6522
+ document.getElementById('copy-prompt-btn').addEventListener('click', async () => {
6523
+ const textarea = document.getElementById('system-prompt-output');
6524
+ try {
6525
+ await navigator.clipboard.writeText(textarea.value);
6526
+ const btn = document.getElementById('copy-prompt-btn');
6527
+ const original = btn.textContent;
6528
+ btn.textContent = 'Copied!';
6529
+ setTimeout(() => { btn.textContent = original; }, 2000);
6530
+ } catch (err) {
6531
+ console.error('Copy failed:', err);
6532
+ }
6533
+ });
6534
+
6229
6535
  // Initialize
6230
6536
  async function initialize() {
6231
6537
  if (!AUTH_TOKEN) {
@@ -6240,6 +6546,7 @@ function generateDashboardHTML(options) {
6240
6546
  updateHandshakes(),
6241
6547
  updateSHR(),
6242
6548
  updateStatus(),
6549
+ updateSovereigntyProfile(),
6243
6550
  ]);
6244
6551
 
6245
6552
  // Setup SSE for real-time updates
@@ -6256,6 +6563,80 @@ function generateDashboardHTML(options) {
6256
6563
  </html>`;
6257
6564
  }
6258
6565
 
6566
+ // src/system-prompt-generator.ts
6567
+ var FEATURE_INFO = {
6568
+ audit_logging: {
6569
+ name: "Audit Logging",
6570
+ activeDescription: "All your tool calls are logged to an encrypted audit trail. No action needed \u2014 this is automatic.",
6571
+ disabledDescription: "audit logging (sanctuary/monitor_audit_log)"
6572
+ },
6573
+ injection_detection: {
6574
+ name: "Injection Detection",
6575
+ activeDescription: "Your tool call arguments are scanned for prompt injection attempts. No action needed \u2014 this is automatic.",
6576
+ disabledDescription: "injection detection"
6577
+ },
6578
+ context_gating: {
6579
+ name: "Context Gating",
6580
+ activeDescription: "Before making outbound calls to remote providers, filter your context through sanctuary/context_gate_filter to ensure minimum-necessary disclosure.",
6581
+ toolNames: ["sanctuary/context_gate_filter", "sanctuary/context_gate_set_policy"],
6582
+ disabledDescription: "context gating (sanctuary/context_gate_filter)"
6583
+ },
6584
+ approval_gate: {
6585
+ name: "Approval Gates",
6586
+ activeDescription: "High-risk operations require human approval before execution. Tier 1 operations always require approval; Tier 2 operations trigger approval on anomaly detection.",
6587
+ disabledDescription: "approval gates"
6588
+ },
6589
+ zk_proofs: {
6590
+ name: "Zero-Knowledge Proofs",
6591
+ activeDescription: "You can prove claims about your data without revealing the underlying values. Use sanctuary/zk_commit to create commitments, sanctuary/zk_prove for proofs of knowledge, and sanctuary/zk_range_prove for range proofs.",
6592
+ toolNames: ["sanctuary/zk_commit", "sanctuary/zk_prove", "sanctuary/zk_range_prove"],
6593
+ disabledDescription: "zero-knowledge proofs (sanctuary/zk_commit, sanctuary/zk_prove)"
6594
+ }
6595
+ };
6596
+ function generateSystemPrompt(profile) {
6597
+ const activeFeatures = [];
6598
+ const inactiveFeatures = [];
6599
+ const featureKeys = [
6600
+ "audit_logging",
6601
+ "injection_detection",
6602
+ "context_gating",
6603
+ "approval_gate",
6604
+ "zk_proofs"
6605
+ ];
6606
+ for (const key of featureKeys) {
6607
+ const featureConfig = profile.features[key];
6608
+ const info = FEATURE_INFO[key];
6609
+ if (featureConfig.enabled) {
6610
+ let desc = `- ${info.name}: ${info.activeDescription}`;
6611
+ if (key === "injection_detection" && "sensitivity" in featureConfig && featureConfig.sensitivity) {
6612
+ desc += ` Sensitivity: ${featureConfig.sensitivity}.`;
6613
+ }
6614
+ if (key === "context_gating" && "policy_id" in featureConfig && featureConfig.policy_id) {
6615
+ desc += ` Active policy: ${featureConfig.policy_id}.`;
6616
+ }
6617
+ activeFeatures.push(desc);
6618
+ } else {
6619
+ inactiveFeatures.push(info.disabledDescription);
6620
+ }
6621
+ }
6622
+ const lines = [
6623
+ "You are protected by Sanctuary sovereignty infrastructure. The following protections are active:",
6624
+ ""
6625
+ ];
6626
+ if (activeFeatures.length > 0) {
6627
+ lines.push(...activeFeatures);
6628
+ } else {
6629
+ lines.push("- No features are currently enabled. Contact your operator to configure protections.");
6630
+ }
6631
+ if (inactiveFeatures.length > 0) {
6632
+ lines.push("");
6633
+ lines.push(
6634
+ `Optional tools available but not currently enabled: ${inactiveFeatures.join(", ")}.`
6635
+ );
6636
+ }
6637
+ return lines.join("\n");
6638
+ }
6639
+
6259
6640
  // src/principal-policy/dashboard.ts
6260
6641
  var SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
6261
6642
  var SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
@@ -6276,6 +6657,7 @@ var DashboardApprovalChannel = class {
6276
6657
  handshakeResults = null;
6277
6658
  shrOpts = null;
6278
6659
  _sanctuaryConfig = null;
6660
+ profileStore = null;
6279
6661
  dashboardHTML;
6280
6662
  loginHTML;
6281
6663
  authToken;
@@ -6315,6 +6697,7 @@ var DashboardApprovalChannel = class {
6315
6697
  if (deps.handshakeResults) this.handshakeResults = deps.handshakeResults;
6316
6698
  if (deps.shrOpts) this.shrOpts = deps.shrOpts;
6317
6699
  if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
6700
+ if (deps.profileStore) this.profileStore = deps.profileStore;
6318
6701
  }
6319
6702
  /**
6320
6703
  * Mark this dashboard as running in standalone mode.
@@ -6662,6 +7045,10 @@ var DashboardApprovalChannel = class {
6662
7045
  this.handleHandshakes(res);
6663
7046
  } else if (method === "GET" && url.pathname === "/api/shr") {
6664
7047
  this.handleSHR(res);
7048
+ } else if (method === "GET" && url.pathname === "/api/sovereignty-profile") {
7049
+ this.handleSovereigntyProfileGet(res);
7050
+ } else if (method === "POST" && url.pathname === "/api/sovereignty-profile") {
7051
+ this.handleSovereigntyProfileUpdate(req, res);
6665
7052
  } else if (method === "POST" && url.pathname.startsWith("/api/approve/")) {
6666
7053
  if (!this.checkRateLimit(req, res, "decisions")) return;
6667
7054
  const id = url.pathname.slice("/api/approve/".length);
@@ -6953,6 +7340,61 @@ data: ${JSON.stringify(initData)}
6953
7340
  res.writeHead(200, { "Content-Type": "application/json" });
6954
7341
  res.end(JSON.stringify(shr));
6955
7342
  }
7343
+ // ── Sovereignty Profile API ─────────────────────────────────────────
7344
+ handleSovereigntyProfileGet(res) {
7345
+ if (!this.profileStore) {
7346
+ res.writeHead(200, { "Content-Type": "application/json" });
7347
+ res.end(JSON.stringify({ error: "Sovereignty Profile not available" }));
7348
+ return;
7349
+ }
7350
+ try {
7351
+ const profile = this.profileStore.get();
7352
+ const prompt = generateSystemPrompt(profile);
7353
+ res.writeHead(200, { "Content-Type": "application/json" });
7354
+ res.end(JSON.stringify({ profile, system_prompt: prompt }));
7355
+ } catch {
7356
+ res.writeHead(500, { "Content-Type": "application/json" });
7357
+ res.end(JSON.stringify({ error: "Failed to read sovereignty profile" }));
7358
+ }
7359
+ }
7360
+ handleSovereigntyProfileUpdate(req, res) {
7361
+ if (!this.profileStore) {
7362
+ res.writeHead(400, { "Content-Type": "application/json" });
7363
+ res.end(JSON.stringify({ error: "Sovereignty Profile not available" }));
7364
+ return;
7365
+ }
7366
+ let body = "";
7367
+ let destroyed = false;
7368
+ req.on("data", (chunk) => {
7369
+ body += chunk.toString();
7370
+ if (body.length > 16384) {
7371
+ destroyed = true;
7372
+ res.writeHead(413, { "Content-Type": "application/json" });
7373
+ res.end(JSON.stringify({ error: "Request body too large" }));
7374
+ req.destroy();
7375
+ }
7376
+ });
7377
+ req.on("end", async () => {
7378
+ if (destroyed) return;
7379
+ try {
7380
+ const updates = JSON.parse(body);
7381
+ const updated = await this.profileStore.update(updates);
7382
+ const prompt = generateSystemPrompt(updated);
7383
+ if (this.auditLog) {
7384
+ this.auditLog.append("l2", "sovereignty_profile_update_dashboard", "dashboard", {
7385
+ changes: updates,
7386
+ features_enabled: Object.entries(updated.features).filter(([, v]) => v.enabled).map(([k]) => k)
7387
+ });
7388
+ }
7389
+ this.broadcastSSE("sovereignty-profile-update", { profile: updated, system_prompt: prompt });
7390
+ res.writeHead(200, { "Content-Type": "application/json" });
7391
+ res.end(JSON.stringify({ profile: updated, system_prompt: prompt }));
7392
+ } catch {
7393
+ res.writeHead(400, { "Content-Type": "application/json" });
7394
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
7395
+ }
7396
+ });
7397
+ }
6956
7398
  // ── SSE Broadcasting ────────────────────────────────────────────────
6957
7399
  broadcastSSE(event, data) {
6958
7400
  const message = `event: ${event}
@@ -8120,6 +8562,7 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
8120
8562
  }
8121
8563
 
8122
8564
  // src/shr/verifier.ts
8565
+ init_identity();
8123
8566
  init_encoding();
8124
8567
  function verifySHR(shr, now) {
8125
8568
  const errors = [];
@@ -8542,7 +8985,9 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
8542
8985
  }
8543
8986
 
8544
8987
  // src/handshake/protocol.ts
8988
+ init_identity();
8545
8989
  init_encoding();
8990
+ init_random();
8546
8991
  function generateNonce() {
8547
8992
  return toBase64url(randomBytes(32));
8548
8993
  }
@@ -8709,7 +9154,9 @@ function deriveTrustTier(level) {
8709
9154
  }
8710
9155
 
8711
9156
  // src/handshake/attestation.ts
9157
+ init_identity();
8712
9158
  init_encoding();
9159
+ init_identity();
8713
9160
  init_encoding();
8714
9161
  var ATTESTATION_VERSION = "1.0";
8715
9162
  function deriveTrustTier2(level) {
@@ -9502,10 +9949,13 @@ function createFederationTools(auditLog, handshakeResults) {
9502
9949
 
9503
9950
  // src/bridge/tools.ts
9504
9951
  init_encoding();
9952
+ init_encryption();
9505
9953
  init_encoding();
9506
9954
 
9507
9955
  // src/bridge/bridge.ts
9956
+ init_identity();
9508
9957
  init_encoding();
9958
+ init_random();
9509
9959
  init_hashing();
9510
9960
  function canonicalize(outcome) {
9511
9961
  return stringToBytes(stableStringify(outcome));
@@ -10656,7 +11106,9 @@ function createAuditTools(config) {
10656
11106
  }
10657
11107
 
10658
11108
  // src/l2-operational/context-gate.ts
11109
+ init_encryption();
10659
11110
  init_encoding();
11111
+ init_random();
10660
11112
  init_hashing();
10661
11113
  var MAX_CONTEXT_FIELDS = 1e3;
10662
11114
  var MAX_POLICY_RULES = 50;
@@ -12625,7 +13077,272 @@ function createL2HardeningTools(storagePath, auditLog) {
12625
13077
  ];
12626
13078
  }
12627
13079
 
13080
+ // src/sovereignty-profile.ts
13081
+ init_encryption();
13082
+ init_encoding();
13083
+ var NAMESPACE = "_sovereignty_profile";
13084
+ var PROFILE_KEY = "active";
13085
+ var HKDF_DOMAIN = "sovereignty-profile";
13086
+ function createDefaultProfile() {
13087
+ return {
13088
+ version: 1,
13089
+ features: {
13090
+ audit_logging: { enabled: true },
13091
+ injection_detection: { enabled: true },
13092
+ context_gating: { enabled: false },
13093
+ approval_gate: { enabled: false },
13094
+ zk_proofs: { enabled: false }
13095
+ },
13096
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
13097
+ };
13098
+ }
13099
+ var SovereigntyProfileStore = class {
13100
+ storage;
13101
+ encryptionKey;
13102
+ profile = null;
13103
+ constructor(storage, masterKey) {
13104
+ this.storage = storage;
13105
+ this.encryptionKey = derivePurposeKey(masterKey, HKDF_DOMAIN);
13106
+ }
13107
+ /**
13108
+ * Load the active sovereignty profile from encrypted storage.
13109
+ * Creates the default profile on first run.
13110
+ */
13111
+ async load() {
13112
+ if (this.profile) return this.profile;
13113
+ const raw = await this.storage.read(NAMESPACE, PROFILE_KEY);
13114
+ if (raw) {
13115
+ try {
13116
+ const encrypted = JSON.parse(bytesToString(raw));
13117
+ const decrypted = decrypt(encrypted, this.encryptionKey);
13118
+ this.profile = JSON.parse(bytesToString(decrypted));
13119
+ return this.profile;
13120
+ } catch {
13121
+ }
13122
+ }
13123
+ this.profile = createDefaultProfile();
13124
+ await this.persist();
13125
+ return this.profile;
13126
+ }
13127
+ /**
13128
+ * Get the current profile. Must call load() first.
13129
+ */
13130
+ get() {
13131
+ if (!this.profile) {
13132
+ throw new Error("SovereigntyProfileStore: call load() before get()");
13133
+ }
13134
+ return this.profile;
13135
+ }
13136
+ /**
13137
+ * Apply a partial update to the profile.
13138
+ * Returns the updated profile.
13139
+ */
13140
+ async update(updates) {
13141
+ if (!this.profile) {
13142
+ await this.load();
13143
+ }
13144
+ const features = this.profile.features;
13145
+ if (updates.audit_logging !== void 0) {
13146
+ if (updates.audit_logging.enabled !== void 0) {
13147
+ if (typeof updates.audit_logging.enabled !== "boolean") {
13148
+ throw new Error("audit_logging.enabled must be a boolean");
13149
+ }
13150
+ features.audit_logging.enabled = updates.audit_logging.enabled;
13151
+ }
13152
+ }
13153
+ if (updates.injection_detection !== void 0) {
13154
+ if (updates.injection_detection.enabled !== void 0) {
13155
+ if (typeof updates.injection_detection.enabled !== "boolean") {
13156
+ throw new Error("injection_detection.enabled must be a boolean");
13157
+ }
13158
+ features.injection_detection.enabled = updates.injection_detection.enabled;
13159
+ }
13160
+ if (updates.injection_detection.sensitivity !== void 0) {
13161
+ const valid = ["low", "medium", "high"];
13162
+ if (!valid.includes(updates.injection_detection.sensitivity)) {
13163
+ throw new Error("injection_detection.sensitivity must be low, medium, or high");
13164
+ }
13165
+ features.injection_detection.sensitivity = updates.injection_detection.sensitivity;
13166
+ }
13167
+ }
13168
+ if (updates.context_gating !== void 0) {
13169
+ if (updates.context_gating.enabled !== void 0) {
13170
+ if (typeof updates.context_gating.enabled !== "boolean") {
13171
+ throw new Error("context_gating.enabled must be a boolean");
13172
+ }
13173
+ features.context_gating.enabled = updates.context_gating.enabled;
13174
+ }
13175
+ if (updates.context_gating.policy_id !== void 0) {
13176
+ if (typeof updates.context_gating.policy_id !== "string" || updates.context_gating.policy_id.length > 256) {
13177
+ throw new Error("context_gating.policy_id must be a string of 256 characters or fewer");
13178
+ }
13179
+ features.context_gating.policy_id = updates.context_gating.policy_id;
13180
+ }
13181
+ }
13182
+ if (updates.approval_gate !== void 0) {
13183
+ if (updates.approval_gate.enabled !== void 0) {
13184
+ if (typeof updates.approval_gate.enabled !== "boolean") {
13185
+ throw new Error("approval_gate.enabled must be a boolean");
13186
+ }
13187
+ features.approval_gate.enabled = updates.approval_gate.enabled;
13188
+ }
13189
+ }
13190
+ if (updates.zk_proofs !== void 0) {
13191
+ if (updates.zk_proofs.enabled !== void 0) {
13192
+ if (typeof updates.zk_proofs.enabled !== "boolean") {
13193
+ throw new Error("zk_proofs.enabled must be a boolean");
13194
+ }
13195
+ features.zk_proofs.enabled = updates.zk_proofs.enabled;
13196
+ }
13197
+ }
13198
+ this.profile.updated_at = (/* @__PURE__ */ new Date()).toISOString();
13199
+ await this.persist();
13200
+ return this.profile;
13201
+ }
13202
+ /**
13203
+ * Persist the current profile to encrypted storage.
13204
+ */
13205
+ async persist() {
13206
+ const serialized = stringToBytes(JSON.stringify(this.profile));
13207
+ const encrypted = encrypt(serialized, this.encryptionKey);
13208
+ await this.storage.write(
13209
+ NAMESPACE,
13210
+ PROFILE_KEY,
13211
+ stringToBytes(JSON.stringify(encrypted))
13212
+ );
13213
+ }
13214
+ };
13215
+
13216
+ // src/sovereignty-profile-tools.ts
13217
+ function createSovereigntyProfileTools(profileStore, auditLog) {
13218
+ const tools = [
13219
+ // ── Get Profile ──────────────────────────────────────────────────
13220
+ {
13221
+ name: "sanctuary/sovereignty_profile_get",
13222
+ description: "Get the current Sovereignty Profile \u2014 shows which Sanctuary features are active (audit logging, injection detection, context gating, approval gates, ZK proofs) and their configuration.",
13223
+ inputSchema: {
13224
+ type: "object",
13225
+ properties: {}
13226
+ },
13227
+ handler: async () => {
13228
+ const profile = profileStore.get();
13229
+ auditLog.append("l2", "sovereignty_profile_get", "system", {
13230
+ features_enabled: Object.entries(profile.features).filter(([, v]) => v.enabled).map(([k]) => k)
13231
+ });
13232
+ return toolResult({
13233
+ profile,
13234
+ message: "Current Sovereignty Profile. Use sovereignty_profile_update to change settings."
13235
+ });
13236
+ }
13237
+ },
13238
+ // ── Update Profile ───────────────────────────────────────────────
13239
+ {
13240
+ name: "sanctuary/sovereignty_profile_update",
13241
+ description: "Update the Sovereignty Profile feature toggles. This changes which Sanctuary protections are active. Requires human approval (Tier 1) because it modifies enforcement behavior. Pass only the features you want to change \u2014 unspecified features remain unchanged.",
13242
+ inputSchema: {
13243
+ type: "object",
13244
+ properties: {
13245
+ audit_logging: {
13246
+ type: "object",
13247
+ properties: {
13248
+ enabled: { type: "boolean" }
13249
+ },
13250
+ description: "Toggle audit logging on/off"
13251
+ },
13252
+ injection_detection: {
13253
+ type: "object",
13254
+ properties: {
13255
+ enabled: { type: "boolean" },
13256
+ sensitivity: {
13257
+ type: "string",
13258
+ enum: ["low", "medium", "high"],
13259
+ description: "Detection sensitivity threshold"
13260
+ }
13261
+ },
13262
+ description: "Toggle injection detection and set sensitivity"
13263
+ },
13264
+ context_gating: {
13265
+ type: "object",
13266
+ properties: {
13267
+ enabled: { type: "boolean" },
13268
+ policy_id: {
13269
+ type: "string",
13270
+ description: "ID of the context-gating policy to use"
13271
+ }
13272
+ },
13273
+ description: "Toggle context gating and set active policy"
13274
+ },
13275
+ approval_gate: {
13276
+ type: "object",
13277
+ properties: {
13278
+ enabled: { type: "boolean" }
13279
+ },
13280
+ description: "Toggle approval gates on/off"
13281
+ },
13282
+ zk_proofs: {
13283
+ type: "object",
13284
+ properties: {
13285
+ enabled: { type: "boolean" }
13286
+ },
13287
+ description: "Toggle zero-knowledge proofs on/off"
13288
+ }
13289
+ }
13290
+ },
13291
+ handler: async (args) => {
13292
+ const updates = {};
13293
+ if (args.audit_logging !== void 0) {
13294
+ updates.audit_logging = args.audit_logging;
13295
+ }
13296
+ if (args.injection_detection !== void 0) {
13297
+ updates.injection_detection = args.injection_detection;
13298
+ }
13299
+ if (args.context_gating !== void 0) {
13300
+ updates.context_gating = args.context_gating;
13301
+ }
13302
+ if (args.approval_gate !== void 0) {
13303
+ updates.approval_gate = args.approval_gate;
13304
+ }
13305
+ if (args.zk_proofs !== void 0) {
13306
+ updates.zk_proofs = args.zk_proofs;
13307
+ }
13308
+ const updated = await profileStore.update(updates);
13309
+ auditLog.append("l2", "sovereignty_profile_update", "system", {
13310
+ changes: updates,
13311
+ features_enabled: Object.entries(updated.features).filter(([, v]) => v.enabled).map(([k]) => k)
13312
+ });
13313
+ return toolResult({
13314
+ profile: updated,
13315
+ message: "Sovereignty Profile updated. Changes take effect immediately."
13316
+ });
13317
+ }
13318
+ },
13319
+ // ── Generate System Prompt ───────────────────────────────────────
13320
+ {
13321
+ name: "sanctuary/sovereignty_profile_generate_prompt",
13322
+ description: "Generate a system prompt snippet based on the active Sovereignty Profile. The snippet instructs an agent on which Sanctuary features are active and how to use them. Copy and paste this into your agent's system configuration.",
13323
+ inputSchema: {
13324
+ type: "object",
13325
+ properties: {}
13326
+ },
13327
+ handler: async () => {
13328
+ const profile = profileStore.get();
13329
+ const prompt = generateSystemPrompt(profile);
13330
+ auditLog.append("l2", "sovereignty_profile_generate_prompt", "system", {
13331
+ features_enabled: Object.entries(profile.features).filter(([, v]) => v.enabled).map(([k]) => k)
13332
+ });
13333
+ return toolResult({
13334
+ system_prompt: prompt,
13335
+ token_estimate: Math.ceil(prompt.length / 4),
13336
+ message: "Copy the system_prompt text above and paste it into your agent's system configuration. It will update dynamically as you toggle features."
13337
+ });
13338
+ }
13339
+ }
13340
+ ];
13341
+ return { tools };
13342
+ }
13343
+
12628
13344
  // src/index.ts
13345
+ init_random();
12629
13346
  init_encoding();
12630
13347
 
12631
13348
  // src/l2-operational/model-provenance.ts
@@ -13132,6 +13849,9 @@ async function createSanctuaryServer(options) {
13132
13849
  const { tools: auditTools } = createAuditTools(config);
13133
13850
  const { tools: contextGateTools, enforcer: contextGateEnforcer } = createContextGateTools(storage, masterKey, auditLog);
13134
13851
  const hardeningTools = createL2HardeningTools(config.storage_path, auditLog);
13852
+ const profileStore = new SovereigntyProfileStore(storage, masterKey);
13853
+ await profileStore.load();
13854
+ const { tools: profileTools } = createSovereigntyProfileTools(profileStore, auditLog);
13135
13855
  const policy = await loadPrincipalPolicy(config.storage_path);
13136
13856
  const baseline = new BaselineTracker(storage, masterKey);
13137
13857
  await baseline.load();
@@ -13159,7 +13879,8 @@ async function createSanctuaryServer(options) {
13159
13879
  identityManager,
13160
13880
  handshakeResults,
13161
13881
  shrOpts: { config, identityManager, masterKey },
13162
- sanctuaryConfig: config
13882
+ sanctuaryConfig: config,
13883
+ profileStore
13163
13884
  });
13164
13885
  await dashboard.start();
13165
13886
  approvalChannel = dashboard;
@@ -13236,6 +13957,7 @@ async function createSanctuaryServer(options) {
13236
13957
  ...auditTools,
13237
13958
  ...contextGateTools,
13238
13959
  ...hardeningTools,
13960
+ ...profileTools,
13239
13961
  ...dashboardTools,
13240
13962
  manifestTool
13241
13963
  ];
@@ -13285,6 +14007,7 @@ exports.MODEL_PRESETS = MODEL_PRESETS;
13285
14007
  exports.MemoryStorage = MemoryStorage;
13286
14008
  exports.PolicyStore = PolicyStore;
13287
14009
  exports.ReputationStore = ReputationStore;
14010
+ exports.SovereigntyProfileStore = SovereigntyProfileStore;
13288
14011
  exports.StateStore = StateStore;
13289
14012
  exports.StderrApprovalChannel = StderrApprovalChannel;
13290
14013
  exports.TIER_WEIGHTS = TIER_WEIGHTS;
@@ -13294,6 +14017,7 @@ exports.classifyField = classifyField;
13294
14017
  exports.completeHandshake = completeHandshake;
13295
14018
  exports.computeWeightedScore = computeWeightedScore;
13296
14019
  exports.createBridgeCommitment = createBridgeCommitment;
14020
+ exports.createDefaultProfile = createDefaultProfile;
13297
14021
  exports.createPedersenCommitment = createPedersenCommitment;
13298
14022
  exports.createProofOfKnowledge = createProofOfKnowledge;
13299
14023
  exports.createRangeProof = createRangeProof;
@@ -13302,6 +14026,7 @@ exports.evaluateField = evaluateField;
13302
14026
  exports.filterContext = filterContext;
13303
14027
  exports.generateAttestation = generateAttestation;
13304
14028
  exports.generateSHR = generateSHR;
14029
+ exports.generateSystemPrompt = generateSystemPrompt;
13305
14030
  exports.getTemplate = getTemplate;
13306
14031
  exports.initiateHandshake = initiateHandshake;
13307
14032
  exports.listTemplateIds = listTemplateIds;