@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.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;
@@ -714,7 +745,9 @@ var RESERVED_NAMESPACE_PREFIXES = [
714
745
  "_bridge",
715
746
  "_federation",
716
747
  "_handshake",
717
- "_shr"
748
+ "_shr",
749
+ "_sovereignty_profile",
750
+ "_context_gate_policies"
718
751
  ];
719
752
  var StateStore = class {
720
753
  storage;
@@ -1238,7 +1271,9 @@ function toolResult(data) {
1238
1271
  }
1239
1272
 
1240
1273
  // src/l1-cognitive/tools.ts
1274
+ init_identity();
1241
1275
  init_encoding();
1276
+ init_encryption();
1242
1277
  init_encoding();
1243
1278
  var RESERVED_NAMESPACE_PREFIXES2 = [
1244
1279
  "_identities",
@@ -1253,7 +1288,9 @@ var RESERVED_NAMESPACE_PREFIXES2 = [
1253
1288
  "_bridge",
1254
1289
  "_federation",
1255
1290
  "_handshake",
1256
- "_shr"
1291
+ "_shr",
1292
+ "_sovereignty_profile",
1293
+ "_context_gate_policies"
1257
1294
  ];
1258
1295
  function getReservedNamespaceViolation(namespace) {
1259
1296
  for (const prefix of RESERVED_NAMESPACE_PREFIXES2) {
@@ -1745,6 +1782,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
1745
1782
  }
1746
1783
 
1747
1784
  // src/l2-operational/audit-log.ts
1785
+ init_encryption();
1748
1786
  init_encoding();
1749
1787
  var AuditLog = class {
1750
1788
  storage;
@@ -1842,6 +1880,8 @@ var AuditLog = class {
1842
1880
  // src/l3-disclosure/commitments.ts
1843
1881
  init_hashing();
1844
1882
  init_encoding();
1883
+ init_random();
1884
+ init_encryption();
1845
1885
  init_encoding();
1846
1886
  function createCommitment(value, blindingFactor) {
1847
1887
  const blindingBytes = blindingFactor ? fromBase64url(blindingFactor) : randomBytes(32);
@@ -1922,7 +1962,9 @@ var CommitmentStore = class {
1922
1962
  };
1923
1963
 
1924
1964
  // src/l3-disclosure/policies.ts
1965
+ init_encryption();
1925
1966
  init_encoding();
1967
+ init_random();
1926
1968
  function evaluateDisclosure(policy, context, requestedFields) {
1927
1969
  return requestedFields.map((field) => {
1928
1970
  const exactRule = policy.rules.find((r) => r.context === context);
@@ -2053,6 +2095,9 @@ var PolicyStore = class {
2053
2095
  );
2054
2096
  }
2055
2097
  };
2098
+
2099
+ // src/l3-disclosure/zk-proofs.ts
2100
+ init_random();
2056
2101
  init_encoding();
2057
2102
  var G = RistrettoPoint.BASE;
2058
2103
  var H_INPUT = concatBytes(
@@ -2730,7 +2775,10 @@ function createL3Tools(storage, masterKey, auditLog) {
2730
2775
  }
2731
2776
 
2732
2777
  // src/l4-reputation/reputation-store.ts
2778
+ init_encryption();
2733
2779
  init_encoding();
2780
+ init_random();
2781
+ init_identity();
2734
2782
  function computeMedian(values) {
2735
2783
  if (values.length === 0) return 0;
2736
2784
  const sorted = [...values].sort((a, b) => a - b);
@@ -3625,6 +3673,24 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3625
3673
  }
3626
3674
  const publishType = args.type;
3627
3675
  const veracoreUrl = args.verascore_url || "https://verascore.ai";
3676
+ const ALLOWED_VERASCORE_HOSTS = ["verascore.ai", "www.verascore.ai", "api.verascore.ai"];
3677
+ try {
3678
+ const parsed = new URL(veracoreUrl);
3679
+ if (parsed.protocol !== "https:") {
3680
+ return toolResult({
3681
+ error: `verascore_url must use HTTPS. Got: ${parsed.protocol}`
3682
+ });
3683
+ }
3684
+ if (!ALLOWED_VERASCORE_HOSTS.includes(parsed.hostname)) {
3685
+ return toolResult({
3686
+ error: `verascore_url must point to a known Verascore domain (${ALLOWED_VERASCORE_HOSTS.join(", ")}). Got: ${parsed.hostname}`
3687
+ });
3688
+ }
3689
+ } catch {
3690
+ return toolResult({
3691
+ error: `verascore_url is not a valid URL: ${veracoreUrl}`
3692
+ });
3693
+ }
3628
3694
  const agentId = args.verascore_agent_id || identity.did.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
3629
3695
  let publishData;
3630
3696
  if (args.data) {
@@ -3654,24 +3720,21 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
3654
3720
  return toolResult({ error: `Unknown publish type: ${publishType}` });
3655
3721
  }
3656
3722
  }
3657
- const { sign: sign2, createPrivateKey } = await import('crypto');
3658
- const payloadBytes = Buffer.from(JSON.stringify(publishData), "utf-8");
3723
+ const { sign: identitySign } = await Promise.resolve().then(() => (init_identity(), identity_exports));
3724
+ const payloadBytes = new TextEncoder().encode(JSON.stringify(publishData));
3659
3725
  let signatureB64;
3660
3726
  try {
3661
- const signingKey = derivePurposeKey(masterKey, "verascore-publish");
3662
- const privateKey = createPrivateKey({
3663
- key: Buffer.concat([
3664
- Buffer.from("302e020100300506032b657004220420", "hex"),
3665
- // Ed25519 DER PKCS8 prefix
3666
- Buffer.from(signingKey.slice(0, 32))
3667
- ]),
3668
- format: "der",
3669
- type: "pkcs8"
3670
- });
3671
- const sig = sign2(null, payloadBytes, privateKey);
3672
- signatureB64 = sig.toString("base64url");
3727
+ const signingBytes = identitySign(
3728
+ payloadBytes,
3729
+ identity.encrypted_private_key,
3730
+ identityEncryptionKey
3731
+ );
3732
+ signatureB64 = toBase64url(signingBytes);
3673
3733
  } catch (signError) {
3674
- signatureB64 = toBase64url(new Uint8Array(64));
3734
+ return toolResult({
3735
+ error: "Failed to sign publish payload. Identity key may be corrupted.",
3736
+ details: signError instanceof Error ? signError.message : String(signError)
3737
+ });
3675
3738
  }
3676
3739
  const requestBody = {
3677
3740
  agentId,
@@ -3750,7 +3813,11 @@ var DEFAULT_POLICY = {
3750
3813
  "reputation_import",
3751
3814
  "reputation_export",
3752
3815
  "bootstrap_provide_guarantee",
3753
- "decommission_certificate"
3816
+ "decommission_certificate",
3817
+ "reputation_publish",
3818
+ // SEC-039: Explicit Tier 1 — sends data to external API
3819
+ "sovereignty_profile_update"
3820
+ // Changes enforcement behavior — always requires approval
3754
3821
  ],
3755
3822
  tier2_anomaly: DEFAULT_TIER2,
3756
3823
  tier3_always_allow: [
@@ -3802,7 +3869,11 @@ var DEFAULT_POLICY = {
3802
3869
  "shr_gateway_export",
3803
3870
  "bridge_commit",
3804
3871
  "bridge_verify",
3805
- "bridge_attest"
3872
+ "bridge_attest",
3873
+ "dashboard_open",
3874
+ // SEC-039: Explicit Tier 3 — only generates a URL
3875
+ "sovereignty_profile_get",
3876
+ "sovereignty_profile_generate_prompt"
3806
3877
  ],
3807
3878
  approval_channel: DEFAULT_CHANNEL
3808
3879
  };
@@ -3910,6 +3981,8 @@ tier1_always_approve:
3910
3981
  - reputation_import
3911
3982
  - reputation_export
3912
3983
  - bootstrap_provide_guarantee
3984
+ - reputation_publish
3985
+ - sovereignty_profile_update
3913
3986
 
3914
3987
  # \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
3915
3988
  # Triggers approval when agent behavior deviates from its baseline.
@@ -3972,6 +4045,9 @@ tier3_always_allow:
3972
4045
  - bridge_commit
3973
4046
  - bridge_verify
3974
4047
  - bridge_attest
4048
+ - dashboard_open
4049
+ - sovereignty_profile_get
4050
+ - sovereignty_profile_generate_prompt
3975
4051
 
3976
4052
  # \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
3977
4053
  # How Sanctuary reaches you when approval is needed.
@@ -3999,6 +4075,7 @@ async function loadPrincipalPolicy(storagePath) {
3999
4075
  }
4000
4076
 
4001
4077
  // src/principal-policy/baseline.ts
4078
+ init_encryption();
4002
4079
  init_encoding();
4003
4080
  var BASELINE_NAMESPACE = "_principal";
4004
4081
  var BASELINE_KEY = "session-baseline";
@@ -4224,6 +4301,7 @@ function canonicalizeForSigning(body) {
4224
4301
  }
4225
4302
 
4226
4303
  // src/shr/generator.ts
4304
+ init_identity();
4227
4305
  init_encoding();
4228
4306
  var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
4229
4307
  function generateSHR(identityId, opts) {
@@ -5176,73 +5254,200 @@ function generateDashboardHTML(options) {
5176
5254
  right: 0;
5177
5255
  }
5178
5256
 
5179
- .pending-header {
5180
- padding: 16px 20px;
5181
- border-bottom: 1px solid var(--border);
5182
- font-weight: 600;
5183
- color: var(--text-primary);
5257
+ .pending-header {
5258
+ padding: 16px 20px;
5259
+ border-bottom: 1px solid var(--border);
5260
+ font-weight: 600;
5261
+ color: var(--text-primary);
5262
+ }
5263
+
5264
+ .pending-items {
5265
+ flex: 1;
5266
+ overflow-y: auto;
5267
+ padding: 16px;
5268
+ }
5269
+
5270
+ .pending-item {
5271
+ background-color: var(--bg);
5272
+ border: 1px solid var(--border);
5273
+ border-radius: 6px;
5274
+ padding: 16px;
5275
+ margin-bottom: 12px;
5276
+ }
5277
+
5278
+ .pending-title {
5279
+ font-weight: 600;
5280
+ color: var(--text-primary);
5281
+ margin-bottom: 8px;
5282
+ word-break: break-word;
5283
+ }
5284
+
5285
+ .pending-countdown {
5286
+ font-size: 12px;
5287
+ color: var(--amber);
5288
+ margin-bottom: 12px;
5289
+ font-weight: 500;
5290
+ }
5291
+
5292
+ .pending-actions {
5293
+ display: flex;
5294
+ gap: 8px;
5295
+ }
5296
+
5297
+ .pending-btn {
5298
+ flex: 1;
5299
+ padding: 8px 12px;
5300
+ border: none;
5301
+ border-radius: 4px;
5302
+ font-size: 12px;
5303
+ font-weight: 600;
5304
+ cursor: pointer;
5305
+ transition: background-color 0.2s;
5306
+ }
5307
+
5308
+ .pending-approve {
5309
+ background-color: var(--green);
5310
+ color: var(--bg);
5311
+ }
5312
+
5313
+ .pending-approve:hover {
5314
+ background-color: #3fa040;
5315
+ }
5316
+
5317
+ .pending-deny {
5318
+ background-color: var(--red);
5319
+ color: var(--bg);
5320
+ }
5321
+
5322
+ .pending-deny:hover {
5323
+ background-color: #e03c3c;
5324
+ }
5325
+
5326
+ /* Sovereignty Profile Panel */
5327
+ .profile-panel {
5328
+ background-color: var(--surface);
5329
+ border: 1px solid var(--border);
5330
+ border-radius: 8px;
5331
+ padding: 20px;
5332
+ }
5333
+
5334
+ .profile-panel .panel-header {
5335
+ display: flex;
5336
+ justify-content: space-between;
5337
+ align-items: center;
5338
+ margin-bottom: 16px;
5339
+ }
5340
+
5341
+ .profile-panel .panel-title {
5342
+ font-size: 14px;
5343
+ font-weight: 600;
5344
+ color: var(--text-primary);
5345
+ }
5346
+
5347
+ .profile-cards {
5348
+ display: grid;
5349
+ grid-template-columns: repeat(5, 1fr);
5350
+ gap: 12px;
5351
+ margin-bottom: 16px;
5352
+ }
5353
+
5354
+ .profile-card {
5355
+ background-color: var(--bg);
5356
+ border: 1px solid var(--border);
5357
+ border-radius: 6px;
5358
+ padding: 14px;
5359
+ display: flex;
5360
+ flex-direction: column;
5361
+ gap: 8px;
5362
+ }
5363
+
5364
+ .profile-card-name {
5365
+ font-size: 12px;
5366
+ font-weight: 600;
5367
+ color: var(--text-primary);
5368
+ }
5369
+
5370
+ .profile-card-desc {
5371
+ font-size: 11px;
5372
+ color: var(--text-secondary);
5373
+ line-height: 1.4;
5374
+ }
5375
+
5376
+ .profile-badge {
5377
+ display: inline-flex;
5378
+ align-items: center;
5379
+ gap: 4px;
5380
+ padding: 2px 8px;
5381
+ border-radius: 4px;
5382
+ font-size: 10px;
5383
+ font-weight: 600;
5384
+ width: fit-content;
5385
+ }
5386
+
5387
+ .profile-badge.enabled {
5388
+ background-color: rgba(63, 185, 80, 0.15);
5389
+ color: var(--green);
5390
+ }
5391
+
5392
+ .profile-badge.disabled {
5393
+ background-color: rgba(139, 148, 158, 0.15);
5394
+ color: var(--text-secondary);
5184
5395
  }
5185
5396
 
5186
- .pending-items {
5187
- flex: 1;
5188
- overflow-y: auto;
5189
- padding: 16px;
5397
+ .prompt-section {
5398
+ margin-top: 12px;
5190
5399
  }
5191
5400
 
5192
- .pending-item {
5401
+ .prompt-textarea {
5402
+ width: 100%;
5403
+ min-height: 120px;
5193
5404
  background-color: var(--bg);
5194
5405
  border: 1px solid var(--border);
5195
5406
  border-radius: 6px;
5196
- padding: 16px;
5197
- margin-bottom: 12px;
5198
- }
5199
-
5200
- .pending-title {
5201
- font-weight: 600;
5202
5407
  color: var(--text-primary);
5203
- margin-bottom: 8px;
5204
- word-break: break-word;
5205
- }
5206
-
5207
- .pending-countdown {
5408
+ font-family: 'JetBrains Mono', monospace;
5208
5409
  font-size: 12px;
5209
- color: var(--amber);
5210
- margin-bottom: 12px;
5211
- font-weight: 500;
5410
+ padding: 12px;
5411
+ resize: vertical;
5412
+ margin-top: 8px;
5212
5413
  }
5213
5414
 
5214
- .pending-actions {
5415
+ .prompt-actions {
5215
5416
  display: flex;
5216
5417
  gap: 8px;
5418
+ margin-top: 8px;
5217
5419
  }
5218
5420
 
5219
- .pending-btn {
5220
- flex: 1;
5221
- padding: 8px 12px;
5222
- border: none;
5421
+ .prompt-btn {
5422
+ padding: 6px 12px;
5423
+ border: 1px solid var(--border);
5223
5424
  border-radius: 4px;
5425
+ background-color: var(--surface);
5426
+ color: var(--text-primary);
5224
5427
  font-size: 12px;
5225
- font-weight: 600;
5226
5428
  cursor: pointer;
5227
- transition: background-color 0.2s;
5228
5429
  }
5229
5430
 
5230
- .pending-approve {
5231
- background-color: var(--green);
5232
- color: var(--bg);
5431
+ .prompt-btn:hover {
5432
+ background-color: var(--muted);
5233
5433
  }
5234
5434
 
5235
- .pending-approve:hover {
5236
- background-color: #3fa040;
5435
+ .prompt-btn.primary {
5436
+ background-color: var(--blue);
5437
+ color: var(--bg);
5438
+ border-color: var(--blue);
5237
5439
  }
5238
5440
 
5239
- .pending-deny {
5240
- background-color: var(--red);
5241
- color: var(--bg);
5441
+ @media (max-width: 900px) {
5442
+ .profile-cards {
5443
+ grid-template-columns: repeat(2, 1fr);
5444
+ }
5242
5445
  }
5243
5446
 
5244
- .pending-deny:hover {
5245
- background-color: #e03c3c;
5447
+ @media (max-width: 500px) {
5448
+ .profile-cards {
5449
+ grid-template-columns: 1fr;
5450
+ }
5246
5451
  }
5247
5452
 
5248
5453
  /* Threat Panel */
@@ -5583,6 +5788,48 @@ function generateDashboardHTML(options) {
5583
5788
  </div>
5584
5789
  </div>
5585
5790
 
5791
+ <!-- Sovereignty Profile Panel -->
5792
+ <div class="profile-panel" id="sovereignty-profile-panel">
5793
+ <div class="panel-header">
5794
+ <div class="panel-title">Sovereignty Profile</div>
5795
+ <span class="card-value" id="profile-updated-at" style="font-size: 11px; color: var(--text-secondary);">\u2014</span>
5796
+ </div>
5797
+ <div class="profile-cards" id="profile-cards">
5798
+ <div class="profile-card" data-feature="audit_logging">
5799
+ <div class="profile-card-name">Audit Logging</div>
5800
+ <div class="profile-badge disabled" id="badge-audit_logging">OFF</div>
5801
+ <div class="profile-card-desc">Encrypted audit trail of all tool calls</div>
5802
+ </div>
5803
+ <div class="profile-card" data-feature="injection_detection">
5804
+ <div class="profile-card-name">Injection Detection</div>
5805
+ <div class="profile-badge disabled" id="badge-injection_detection">OFF</div>
5806
+ <div class="profile-card-desc">Scans tool arguments for prompt injection</div>
5807
+ </div>
5808
+ <div class="profile-card" data-feature="context_gating">
5809
+ <div class="profile-card-name">Context Gating</div>
5810
+ <div class="profile-badge disabled" id="badge-context_gating">OFF</div>
5811
+ <div class="profile-card-desc">Controls context flow to remote providers</div>
5812
+ </div>
5813
+ <div class="profile-card" data-feature="approval_gate">
5814
+ <div class="profile-card-name">Approval Gates</div>
5815
+ <div class="profile-badge disabled" id="badge-approval_gate">OFF</div>
5816
+ <div class="profile-card-desc">Human approval for high-risk operations</div>
5817
+ </div>
5818
+ <div class="profile-card" data-feature="zk_proofs">
5819
+ <div class="profile-card-name">ZK Proofs</div>
5820
+ <div class="profile-badge disabled" id="badge-zk_proofs">OFF</div>
5821
+ <div class="profile-card-desc">Prove claims without revealing data</div>
5822
+ </div>
5823
+ </div>
5824
+ <div class="prompt-section">
5825
+ <div class="prompt-actions">
5826
+ <button class="prompt-btn primary" id="generate-prompt-btn">Generate System Prompt</button>
5827
+ <button class="prompt-btn" id="copy-prompt-btn" style="display:none;">Copy</button>
5828
+ </div>
5829
+ <textarea class="prompt-textarea" id="system-prompt-output" readonly style="display:none;" placeholder="Click 'Generate System Prompt' to create an agent instruction snippet..."></textarea>
5830
+ </div>
5831
+ </div>
5832
+
5586
5833
  <!-- Threat Panel -->
5587
5834
  <div class="threat-panel collapsed">
5588
5835
  <div class="threat-header">
@@ -5604,7 +5851,9 @@ function generateDashboardHTML(options) {
5604
5851
 
5605
5852
  <script>
5606
5853
  // Constants
5607
- const AUTH_TOKEN = '${options.authToken || ""}' || sessionStorage.getItem('authToken') || '';
5854
+ // SEC-038: Do NOT embed the long-lived auth token in page source.
5855
+ // Use only the session token stored in sessionStorage by the login flow.
5856
+ const AUTH_TOKEN = sessionStorage.getItem('authToken') || '';
5608
5857
  const TIMEOUT_SECONDS = ${options.timeoutSeconds};
5609
5858
  const API_BASE = '';
5610
5859
 
@@ -5615,6 +5864,7 @@ function generateDashboardHTML(options) {
5615
5864
  handshakes: [],
5616
5865
  shr: null,
5617
5866
  status: null,
5867
+ systemPrompt: null,
5618
5868
  };
5619
5869
 
5620
5870
  let pendingRequests = new Map();
@@ -6067,6 +6317,10 @@ function generateDashboardHTML(options) {
6067
6317
  removePendingRequest(data.requestId);
6068
6318
  });
6069
6319
 
6320
+ eventSource.addEventListener('sovereignty-profile-update', () => {
6321
+ updateSovereigntyProfile();
6322
+ });
6323
+
6070
6324
  eventSource.onerror = () => {
6071
6325
  console.error('SSE error');
6072
6326
  setTimeout(setupSSE, 5000);
@@ -6223,6 +6477,58 @@ function generateDashboardHTML(options) {
6223
6477
  document.getElementById('pending-overlay').classList.toggle('show');
6224
6478
  });
6225
6479
 
6480
+ // Sovereignty Profile
6481
+ async function updateSovereigntyProfile() {
6482
+ try {
6483
+ const data = await fetchAPI('/api/sovereignty-profile');
6484
+ if (data && data.profile) {
6485
+ const features = data.profile.features;
6486
+ for (const [key, value] of Object.entries(features)) {
6487
+ const badge = document.getElementById('badge-' + key);
6488
+ if (badge) {
6489
+ const enabled = value && value.enabled;
6490
+ badge.textContent = enabled ? 'ON' : 'OFF';
6491
+ badge.className = 'profile-badge ' + (enabled ? 'enabled' : 'disabled');
6492
+ }
6493
+ }
6494
+ const updatedEl = document.getElementById('profile-updated-at');
6495
+ if (updatedEl && data.profile.updated_at) {
6496
+ updatedEl.textContent = 'Updated: ' + new Date(data.profile.updated_at).toLocaleString();
6497
+ }
6498
+ // Cache the prompt
6499
+ if (data.system_prompt) {
6500
+ apiState.systemPrompt = data.system_prompt;
6501
+ }
6502
+ }
6503
+ } catch (e) {
6504
+ // Profile not available
6505
+ }
6506
+ }
6507
+
6508
+ document.getElementById('generate-prompt-btn').addEventListener('click', async () => {
6509
+ const data = await fetchAPI('/api/sovereignty-profile');
6510
+ if (data && data.system_prompt) {
6511
+ const textarea = document.getElementById('system-prompt-output');
6512
+ const copyBtn = document.getElementById('copy-prompt-btn');
6513
+ textarea.value = data.system_prompt;
6514
+ textarea.style.display = 'block';
6515
+ copyBtn.style.display = 'inline-flex';
6516
+ }
6517
+ });
6518
+
6519
+ document.getElementById('copy-prompt-btn').addEventListener('click', async () => {
6520
+ const textarea = document.getElementById('system-prompt-output');
6521
+ try {
6522
+ await navigator.clipboard.writeText(textarea.value);
6523
+ const btn = document.getElementById('copy-prompt-btn');
6524
+ const original = btn.textContent;
6525
+ btn.textContent = 'Copied!';
6526
+ setTimeout(() => { btn.textContent = original; }, 2000);
6527
+ } catch (err) {
6528
+ console.error('Copy failed:', err);
6529
+ }
6530
+ });
6531
+
6226
6532
  // Initialize
6227
6533
  async function initialize() {
6228
6534
  if (!AUTH_TOKEN) {
@@ -6237,6 +6543,7 @@ function generateDashboardHTML(options) {
6237
6543
  updateHandshakes(),
6238
6544
  updateSHR(),
6239
6545
  updateStatus(),
6546
+ updateSovereigntyProfile(),
6240
6547
  ]);
6241
6548
 
6242
6549
  // Setup SSE for real-time updates
@@ -6253,6 +6560,80 @@ function generateDashboardHTML(options) {
6253
6560
  </html>`;
6254
6561
  }
6255
6562
 
6563
+ // src/system-prompt-generator.ts
6564
+ var FEATURE_INFO = {
6565
+ audit_logging: {
6566
+ name: "Audit Logging",
6567
+ activeDescription: "All your tool calls are logged to an encrypted audit trail. No action needed \u2014 this is automatic.",
6568
+ disabledDescription: "audit logging (sanctuary/monitor_audit_log)"
6569
+ },
6570
+ injection_detection: {
6571
+ name: "Injection Detection",
6572
+ activeDescription: "Your tool call arguments are scanned for prompt injection attempts. No action needed \u2014 this is automatic.",
6573
+ disabledDescription: "injection detection"
6574
+ },
6575
+ context_gating: {
6576
+ name: "Context Gating",
6577
+ activeDescription: "Before making outbound calls to remote providers, filter your context through sanctuary/context_gate_filter to ensure minimum-necessary disclosure.",
6578
+ toolNames: ["sanctuary/context_gate_filter", "sanctuary/context_gate_set_policy"],
6579
+ disabledDescription: "context gating (sanctuary/context_gate_filter)"
6580
+ },
6581
+ approval_gate: {
6582
+ name: "Approval Gates",
6583
+ activeDescription: "High-risk operations require human approval before execution. Tier 1 operations always require approval; Tier 2 operations trigger approval on anomaly detection.",
6584
+ disabledDescription: "approval gates"
6585
+ },
6586
+ zk_proofs: {
6587
+ name: "Zero-Knowledge Proofs",
6588
+ 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.",
6589
+ toolNames: ["sanctuary/zk_commit", "sanctuary/zk_prove", "sanctuary/zk_range_prove"],
6590
+ disabledDescription: "zero-knowledge proofs (sanctuary/zk_commit, sanctuary/zk_prove)"
6591
+ }
6592
+ };
6593
+ function generateSystemPrompt(profile) {
6594
+ const activeFeatures = [];
6595
+ const inactiveFeatures = [];
6596
+ const featureKeys = [
6597
+ "audit_logging",
6598
+ "injection_detection",
6599
+ "context_gating",
6600
+ "approval_gate",
6601
+ "zk_proofs"
6602
+ ];
6603
+ for (const key of featureKeys) {
6604
+ const featureConfig = profile.features[key];
6605
+ const info = FEATURE_INFO[key];
6606
+ if (featureConfig.enabled) {
6607
+ let desc = `- ${info.name}: ${info.activeDescription}`;
6608
+ if (key === "injection_detection" && "sensitivity" in featureConfig && featureConfig.sensitivity) {
6609
+ desc += ` Sensitivity: ${featureConfig.sensitivity}.`;
6610
+ }
6611
+ if (key === "context_gating" && "policy_id" in featureConfig && featureConfig.policy_id) {
6612
+ desc += ` Active policy: ${featureConfig.policy_id}.`;
6613
+ }
6614
+ activeFeatures.push(desc);
6615
+ } else {
6616
+ inactiveFeatures.push(info.disabledDescription);
6617
+ }
6618
+ }
6619
+ const lines = [
6620
+ "You are protected by Sanctuary sovereignty infrastructure. The following protections are active:",
6621
+ ""
6622
+ ];
6623
+ if (activeFeatures.length > 0) {
6624
+ lines.push(...activeFeatures);
6625
+ } else {
6626
+ lines.push("- No features are currently enabled. Contact your operator to configure protections.");
6627
+ }
6628
+ if (inactiveFeatures.length > 0) {
6629
+ lines.push("");
6630
+ lines.push(
6631
+ `Optional tools available but not currently enabled: ${inactiveFeatures.join(", ")}.`
6632
+ );
6633
+ }
6634
+ return lines.join("\n");
6635
+ }
6636
+
6256
6637
  // src/principal-policy/dashboard.ts
6257
6638
  var SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
6258
6639
  var SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
@@ -6273,6 +6654,7 @@ var DashboardApprovalChannel = class {
6273
6654
  handshakeResults = null;
6274
6655
  shrOpts = null;
6275
6656
  _sanctuaryConfig = null;
6657
+ profileStore = null;
6276
6658
  dashboardHTML;
6277
6659
  loginHTML;
6278
6660
  authToken;
@@ -6312,6 +6694,7 @@ var DashboardApprovalChannel = class {
6312
6694
  if (deps.handshakeResults) this.handshakeResults = deps.handshakeResults;
6313
6695
  if (deps.shrOpts) this.shrOpts = deps.shrOpts;
6314
6696
  if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
6697
+ if (deps.profileStore) this.profileStore = deps.profileStore;
6315
6698
  }
6316
6699
  /**
6317
6700
  * Mark this dashboard as running in standalone mode.
@@ -6659,6 +7042,10 @@ var DashboardApprovalChannel = class {
6659
7042
  this.handleHandshakes(res);
6660
7043
  } else if (method === "GET" && url.pathname === "/api/shr") {
6661
7044
  this.handleSHR(res);
7045
+ } else if (method === "GET" && url.pathname === "/api/sovereignty-profile") {
7046
+ this.handleSovereigntyProfileGet(res);
7047
+ } else if (method === "POST" && url.pathname === "/api/sovereignty-profile") {
7048
+ this.handleSovereigntyProfileUpdate(req, res);
6662
7049
  } else if (method === "POST" && url.pathname.startsWith("/api/approve/")) {
6663
7050
  if (!this.checkRateLimit(req, res, "decisions")) return;
6664
7051
  const id = url.pathname.slice("/api/approve/".length);
@@ -6950,6 +7337,61 @@ data: ${JSON.stringify(initData)}
6950
7337
  res.writeHead(200, { "Content-Type": "application/json" });
6951
7338
  res.end(JSON.stringify(shr));
6952
7339
  }
7340
+ // ── Sovereignty Profile API ─────────────────────────────────────────
7341
+ handleSovereigntyProfileGet(res) {
7342
+ if (!this.profileStore) {
7343
+ res.writeHead(200, { "Content-Type": "application/json" });
7344
+ res.end(JSON.stringify({ error: "Sovereignty Profile not available" }));
7345
+ return;
7346
+ }
7347
+ try {
7348
+ const profile = this.profileStore.get();
7349
+ const prompt = generateSystemPrompt(profile);
7350
+ res.writeHead(200, { "Content-Type": "application/json" });
7351
+ res.end(JSON.stringify({ profile, system_prompt: prompt }));
7352
+ } catch {
7353
+ res.writeHead(500, { "Content-Type": "application/json" });
7354
+ res.end(JSON.stringify({ error: "Failed to read sovereignty profile" }));
7355
+ }
7356
+ }
7357
+ handleSovereigntyProfileUpdate(req, res) {
7358
+ if (!this.profileStore) {
7359
+ res.writeHead(400, { "Content-Type": "application/json" });
7360
+ res.end(JSON.stringify({ error: "Sovereignty Profile not available" }));
7361
+ return;
7362
+ }
7363
+ let body = "";
7364
+ let destroyed = false;
7365
+ req.on("data", (chunk) => {
7366
+ body += chunk.toString();
7367
+ if (body.length > 16384) {
7368
+ destroyed = true;
7369
+ res.writeHead(413, { "Content-Type": "application/json" });
7370
+ res.end(JSON.stringify({ error: "Request body too large" }));
7371
+ req.destroy();
7372
+ }
7373
+ });
7374
+ req.on("end", async () => {
7375
+ if (destroyed) return;
7376
+ try {
7377
+ const updates = JSON.parse(body);
7378
+ const updated = await this.profileStore.update(updates);
7379
+ const prompt = generateSystemPrompt(updated);
7380
+ if (this.auditLog) {
7381
+ this.auditLog.append("l2", "sovereignty_profile_update_dashboard", "dashboard", {
7382
+ changes: updates,
7383
+ features_enabled: Object.entries(updated.features).filter(([, v]) => v.enabled).map(([k]) => k)
7384
+ });
7385
+ }
7386
+ this.broadcastSSE("sovereignty-profile-update", { profile: updated, system_prompt: prompt });
7387
+ res.writeHead(200, { "Content-Type": "application/json" });
7388
+ res.end(JSON.stringify({ profile: updated, system_prompt: prompt }));
7389
+ } catch {
7390
+ res.writeHead(400, { "Content-Type": "application/json" });
7391
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
7392
+ }
7393
+ });
7394
+ }
6953
7395
  // ── SSE Broadcasting ────────────────────────────────────────────────
6954
7396
  broadcastSSE(event, data) {
6955
7397
  const message = `event: ${event}
@@ -8117,6 +8559,7 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
8117
8559
  }
8118
8560
 
8119
8561
  // src/shr/verifier.ts
8562
+ init_identity();
8120
8563
  init_encoding();
8121
8564
  function verifySHR(shr, now) {
8122
8565
  const errors = [];
@@ -8539,7 +8982,9 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
8539
8982
  }
8540
8983
 
8541
8984
  // src/handshake/protocol.ts
8985
+ init_identity();
8542
8986
  init_encoding();
8987
+ init_random();
8543
8988
  function generateNonce() {
8544
8989
  return toBase64url(randomBytes(32));
8545
8990
  }
@@ -8706,7 +9151,9 @@ function deriveTrustTier(level) {
8706
9151
  }
8707
9152
 
8708
9153
  // src/handshake/attestation.ts
9154
+ init_identity();
8709
9155
  init_encoding();
9156
+ init_identity();
8710
9157
  init_encoding();
8711
9158
  var ATTESTATION_VERSION = "1.0";
8712
9159
  function deriveTrustTier2(level) {
@@ -9499,10 +9946,13 @@ function createFederationTools(auditLog, handshakeResults) {
9499
9946
 
9500
9947
  // src/bridge/tools.ts
9501
9948
  init_encoding();
9949
+ init_encryption();
9502
9950
  init_encoding();
9503
9951
 
9504
9952
  // src/bridge/bridge.ts
9953
+ init_identity();
9505
9954
  init_encoding();
9955
+ init_random();
9506
9956
  init_hashing();
9507
9957
  function canonicalize(outcome) {
9508
9958
  return stringToBytes(stableStringify(outcome));
@@ -10653,7 +11103,9 @@ function createAuditTools(config) {
10653
11103
  }
10654
11104
 
10655
11105
  // src/l2-operational/context-gate.ts
11106
+ init_encryption();
10656
11107
  init_encoding();
11108
+ init_random();
10657
11109
  init_hashing();
10658
11110
  var MAX_CONTEXT_FIELDS = 1e3;
10659
11111
  var MAX_POLICY_RULES = 50;
@@ -12622,7 +13074,272 @@ function createL2HardeningTools(storagePath, auditLog) {
12622
13074
  ];
12623
13075
  }
12624
13076
 
13077
+ // src/sovereignty-profile.ts
13078
+ init_encryption();
13079
+ init_encoding();
13080
+ var NAMESPACE = "_sovereignty_profile";
13081
+ var PROFILE_KEY = "active";
13082
+ var HKDF_DOMAIN = "sovereignty-profile";
13083
+ function createDefaultProfile() {
13084
+ return {
13085
+ version: 1,
13086
+ features: {
13087
+ audit_logging: { enabled: true },
13088
+ injection_detection: { enabled: true },
13089
+ context_gating: { enabled: false },
13090
+ approval_gate: { enabled: false },
13091
+ zk_proofs: { enabled: false }
13092
+ },
13093
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
13094
+ };
13095
+ }
13096
+ var SovereigntyProfileStore = class {
13097
+ storage;
13098
+ encryptionKey;
13099
+ profile = null;
13100
+ constructor(storage, masterKey) {
13101
+ this.storage = storage;
13102
+ this.encryptionKey = derivePurposeKey(masterKey, HKDF_DOMAIN);
13103
+ }
13104
+ /**
13105
+ * Load the active sovereignty profile from encrypted storage.
13106
+ * Creates the default profile on first run.
13107
+ */
13108
+ async load() {
13109
+ if (this.profile) return this.profile;
13110
+ const raw = await this.storage.read(NAMESPACE, PROFILE_KEY);
13111
+ if (raw) {
13112
+ try {
13113
+ const encrypted = JSON.parse(bytesToString(raw));
13114
+ const decrypted = decrypt(encrypted, this.encryptionKey);
13115
+ this.profile = JSON.parse(bytesToString(decrypted));
13116
+ return this.profile;
13117
+ } catch {
13118
+ }
13119
+ }
13120
+ this.profile = createDefaultProfile();
13121
+ await this.persist();
13122
+ return this.profile;
13123
+ }
13124
+ /**
13125
+ * Get the current profile. Must call load() first.
13126
+ */
13127
+ get() {
13128
+ if (!this.profile) {
13129
+ throw new Error("SovereigntyProfileStore: call load() before get()");
13130
+ }
13131
+ return this.profile;
13132
+ }
13133
+ /**
13134
+ * Apply a partial update to the profile.
13135
+ * Returns the updated profile.
13136
+ */
13137
+ async update(updates) {
13138
+ if (!this.profile) {
13139
+ await this.load();
13140
+ }
13141
+ const features = this.profile.features;
13142
+ if (updates.audit_logging !== void 0) {
13143
+ if (updates.audit_logging.enabled !== void 0) {
13144
+ if (typeof updates.audit_logging.enabled !== "boolean") {
13145
+ throw new Error("audit_logging.enabled must be a boolean");
13146
+ }
13147
+ features.audit_logging.enabled = updates.audit_logging.enabled;
13148
+ }
13149
+ }
13150
+ if (updates.injection_detection !== void 0) {
13151
+ if (updates.injection_detection.enabled !== void 0) {
13152
+ if (typeof updates.injection_detection.enabled !== "boolean") {
13153
+ throw new Error("injection_detection.enabled must be a boolean");
13154
+ }
13155
+ features.injection_detection.enabled = updates.injection_detection.enabled;
13156
+ }
13157
+ if (updates.injection_detection.sensitivity !== void 0) {
13158
+ const valid = ["low", "medium", "high"];
13159
+ if (!valid.includes(updates.injection_detection.sensitivity)) {
13160
+ throw new Error("injection_detection.sensitivity must be low, medium, or high");
13161
+ }
13162
+ features.injection_detection.sensitivity = updates.injection_detection.sensitivity;
13163
+ }
13164
+ }
13165
+ if (updates.context_gating !== void 0) {
13166
+ if (updates.context_gating.enabled !== void 0) {
13167
+ if (typeof updates.context_gating.enabled !== "boolean") {
13168
+ throw new Error("context_gating.enabled must be a boolean");
13169
+ }
13170
+ features.context_gating.enabled = updates.context_gating.enabled;
13171
+ }
13172
+ if (updates.context_gating.policy_id !== void 0) {
13173
+ if (typeof updates.context_gating.policy_id !== "string" || updates.context_gating.policy_id.length > 256) {
13174
+ throw new Error("context_gating.policy_id must be a string of 256 characters or fewer");
13175
+ }
13176
+ features.context_gating.policy_id = updates.context_gating.policy_id;
13177
+ }
13178
+ }
13179
+ if (updates.approval_gate !== void 0) {
13180
+ if (updates.approval_gate.enabled !== void 0) {
13181
+ if (typeof updates.approval_gate.enabled !== "boolean") {
13182
+ throw new Error("approval_gate.enabled must be a boolean");
13183
+ }
13184
+ features.approval_gate.enabled = updates.approval_gate.enabled;
13185
+ }
13186
+ }
13187
+ if (updates.zk_proofs !== void 0) {
13188
+ if (updates.zk_proofs.enabled !== void 0) {
13189
+ if (typeof updates.zk_proofs.enabled !== "boolean") {
13190
+ throw new Error("zk_proofs.enabled must be a boolean");
13191
+ }
13192
+ features.zk_proofs.enabled = updates.zk_proofs.enabled;
13193
+ }
13194
+ }
13195
+ this.profile.updated_at = (/* @__PURE__ */ new Date()).toISOString();
13196
+ await this.persist();
13197
+ return this.profile;
13198
+ }
13199
+ /**
13200
+ * Persist the current profile to encrypted storage.
13201
+ */
13202
+ async persist() {
13203
+ const serialized = stringToBytes(JSON.stringify(this.profile));
13204
+ const encrypted = encrypt(serialized, this.encryptionKey);
13205
+ await this.storage.write(
13206
+ NAMESPACE,
13207
+ PROFILE_KEY,
13208
+ stringToBytes(JSON.stringify(encrypted))
13209
+ );
13210
+ }
13211
+ };
13212
+
13213
+ // src/sovereignty-profile-tools.ts
13214
+ function createSovereigntyProfileTools(profileStore, auditLog) {
13215
+ const tools = [
13216
+ // ── Get Profile ──────────────────────────────────────────────────
13217
+ {
13218
+ name: "sanctuary/sovereignty_profile_get",
13219
+ 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.",
13220
+ inputSchema: {
13221
+ type: "object",
13222
+ properties: {}
13223
+ },
13224
+ handler: async () => {
13225
+ const profile = profileStore.get();
13226
+ auditLog.append("l2", "sovereignty_profile_get", "system", {
13227
+ features_enabled: Object.entries(profile.features).filter(([, v]) => v.enabled).map(([k]) => k)
13228
+ });
13229
+ return toolResult({
13230
+ profile,
13231
+ message: "Current Sovereignty Profile. Use sovereignty_profile_update to change settings."
13232
+ });
13233
+ }
13234
+ },
13235
+ // ── Update Profile ───────────────────────────────────────────────
13236
+ {
13237
+ name: "sanctuary/sovereignty_profile_update",
13238
+ 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.",
13239
+ inputSchema: {
13240
+ type: "object",
13241
+ properties: {
13242
+ audit_logging: {
13243
+ type: "object",
13244
+ properties: {
13245
+ enabled: { type: "boolean" }
13246
+ },
13247
+ description: "Toggle audit logging on/off"
13248
+ },
13249
+ injection_detection: {
13250
+ type: "object",
13251
+ properties: {
13252
+ enabled: { type: "boolean" },
13253
+ sensitivity: {
13254
+ type: "string",
13255
+ enum: ["low", "medium", "high"],
13256
+ description: "Detection sensitivity threshold"
13257
+ }
13258
+ },
13259
+ description: "Toggle injection detection and set sensitivity"
13260
+ },
13261
+ context_gating: {
13262
+ type: "object",
13263
+ properties: {
13264
+ enabled: { type: "boolean" },
13265
+ policy_id: {
13266
+ type: "string",
13267
+ description: "ID of the context-gating policy to use"
13268
+ }
13269
+ },
13270
+ description: "Toggle context gating and set active policy"
13271
+ },
13272
+ approval_gate: {
13273
+ type: "object",
13274
+ properties: {
13275
+ enabled: { type: "boolean" }
13276
+ },
13277
+ description: "Toggle approval gates on/off"
13278
+ },
13279
+ zk_proofs: {
13280
+ type: "object",
13281
+ properties: {
13282
+ enabled: { type: "boolean" }
13283
+ },
13284
+ description: "Toggle zero-knowledge proofs on/off"
13285
+ }
13286
+ }
13287
+ },
13288
+ handler: async (args) => {
13289
+ const updates = {};
13290
+ if (args.audit_logging !== void 0) {
13291
+ updates.audit_logging = args.audit_logging;
13292
+ }
13293
+ if (args.injection_detection !== void 0) {
13294
+ updates.injection_detection = args.injection_detection;
13295
+ }
13296
+ if (args.context_gating !== void 0) {
13297
+ updates.context_gating = args.context_gating;
13298
+ }
13299
+ if (args.approval_gate !== void 0) {
13300
+ updates.approval_gate = args.approval_gate;
13301
+ }
13302
+ if (args.zk_proofs !== void 0) {
13303
+ updates.zk_proofs = args.zk_proofs;
13304
+ }
13305
+ const updated = await profileStore.update(updates);
13306
+ auditLog.append("l2", "sovereignty_profile_update", "system", {
13307
+ changes: updates,
13308
+ features_enabled: Object.entries(updated.features).filter(([, v]) => v.enabled).map(([k]) => k)
13309
+ });
13310
+ return toolResult({
13311
+ profile: updated,
13312
+ message: "Sovereignty Profile updated. Changes take effect immediately."
13313
+ });
13314
+ }
13315
+ },
13316
+ // ── Generate System Prompt ───────────────────────────────────────
13317
+ {
13318
+ name: "sanctuary/sovereignty_profile_generate_prompt",
13319
+ 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.",
13320
+ inputSchema: {
13321
+ type: "object",
13322
+ properties: {}
13323
+ },
13324
+ handler: async () => {
13325
+ const profile = profileStore.get();
13326
+ const prompt = generateSystemPrompt(profile);
13327
+ auditLog.append("l2", "sovereignty_profile_generate_prompt", "system", {
13328
+ features_enabled: Object.entries(profile.features).filter(([, v]) => v.enabled).map(([k]) => k)
13329
+ });
13330
+ return toolResult({
13331
+ system_prompt: prompt,
13332
+ token_estimate: Math.ceil(prompt.length / 4),
13333
+ message: "Copy the system_prompt text above and paste it into your agent's system configuration. It will update dynamically as you toggle features."
13334
+ });
13335
+ }
13336
+ }
13337
+ ];
13338
+ return { tools };
13339
+ }
13340
+
12625
13341
  // src/index.ts
13342
+ init_random();
12626
13343
  init_encoding();
12627
13344
 
12628
13345
  // src/l2-operational/model-provenance.ts
@@ -13129,6 +13846,9 @@ async function createSanctuaryServer(options) {
13129
13846
  const { tools: auditTools } = createAuditTools(config);
13130
13847
  const { tools: contextGateTools, enforcer: contextGateEnforcer } = createContextGateTools(storage, masterKey, auditLog);
13131
13848
  const hardeningTools = createL2HardeningTools(config.storage_path, auditLog);
13849
+ const profileStore = new SovereigntyProfileStore(storage, masterKey);
13850
+ await profileStore.load();
13851
+ const { tools: profileTools } = createSovereigntyProfileTools(profileStore, auditLog);
13132
13852
  const policy = await loadPrincipalPolicy(config.storage_path);
13133
13853
  const baseline = new BaselineTracker(storage, masterKey);
13134
13854
  await baseline.load();
@@ -13156,7 +13876,8 @@ async function createSanctuaryServer(options) {
13156
13876
  identityManager,
13157
13877
  handshakeResults,
13158
13878
  shrOpts: { config, identityManager, masterKey },
13159
- sanctuaryConfig: config
13879
+ sanctuaryConfig: config,
13880
+ profileStore
13160
13881
  });
13161
13882
  await dashboard.start();
13162
13883
  approvalChannel = dashboard;
@@ -13233,6 +13954,7 @@ async function createSanctuaryServer(options) {
13233
13954
  ...auditTools,
13234
13955
  ...contextGateTools,
13235
13956
  ...hardeningTools,
13957
+ ...profileTools,
13236
13958
  ...dashboardTools,
13237
13959
  manifestTool
13238
13960
  ];
@@ -13263,6 +13985,6 @@ async function createSanctuaryServer(options) {
13263
13985
  return { server, config };
13264
13986
  }
13265
13987
 
13266
- export { ATTESTATION_VERSION, ApprovalGate, AuditLog, AutoApproveChannel, BaselineTracker, TEMPLATES as CONTEXT_GATE_TEMPLATES, CallbackApprovalChannel, CommitmentStore, ContextGateEnforcer, ContextGatePolicyStore, DashboardApprovalChannel, FederationRegistry, FilesystemStorage, InMemoryModelProvenanceStore, InjectionDetector, MODEL_PRESETS, MemoryStorage, PolicyStore, ReputationStore, StateStore, StderrApprovalChannel, TIER_WEIGHTS, WebhookApprovalChannel, canonicalize, classifyField, completeHandshake, computeWeightedScore, createBridgeCommitment, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, evaluateField, filterContext, generateAttestation, generateSHR, getTemplate, initiateHandshake, listTemplateIds, loadConfig, loadPrincipalPolicy, recommendPolicy, resolveTier, respondToHandshake, signPayload, tierDistribution, verifyAttestation, verifyBridgeCommitment, verifyCompletion, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };
13988
+ export { ATTESTATION_VERSION, ApprovalGate, AuditLog, AutoApproveChannel, BaselineTracker, TEMPLATES as CONTEXT_GATE_TEMPLATES, CallbackApprovalChannel, CommitmentStore, ContextGateEnforcer, ContextGatePolicyStore, DashboardApprovalChannel, FederationRegistry, FilesystemStorage, InMemoryModelProvenanceStore, InjectionDetector, MODEL_PRESETS, MemoryStorage, PolicyStore, ReputationStore, SovereigntyProfileStore, StateStore, StderrApprovalChannel, TIER_WEIGHTS, WebhookApprovalChannel, canonicalize, classifyField, completeHandshake, computeWeightedScore, createBridgeCommitment, createDefaultProfile, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, evaluateField, filterContext, generateAttestation, generateSHR, generateSystemPrompt, getTemplate, initiateHandshake, listTemplateIds, loadConfig, loadPrincipalPolicy, recommendPolicy, resolveTier, respondToHandshake, signPayload, tierDistribution, verifyAttestation, verifyBridgeCommitment, verifyCompletion, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };
13267
13989
  //# sourceMappingURL=index.js.map
13268
13990
  //# sourceMappingURL=index.js.map