@sanctuary-framework/mcp-server 0.5.13 → 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
@@ -748,7 +748,9 @@ var RESERVED_NAMESPACE_PREFIXES = [
748
748
  "_bridge",
749
749
  "_federation",
750
750
  "_handshake",
751
- "_shr"
751
+ "_shr",
752
+ "_sovereignty_profile",
753
+ "_context_gate_policies"
752
754
  ];
753
755
  var StateStore = class {
754
756
  storage;
@@ -1289,7 +1291,9 @@ var RESERVED_NAMESPACE_PREFIXES2 = [
1289
1291
  "_bridge",
1290
1292
  "_federation",
1291
1293
  "_handshake",
1292
- "_shr"
1294
+ "_shr",
1295
+ "_sovereignty_profile",
1296
+ "_context_gate_policies"
1293
1297
  ];
1294
1298
  function getReservedNamespaceViolation(namespace) {
1295
1299
  for (const prefix of RESERVED_NAMESPACE_PREFIXES2) {
@@ -3813,8 +3817,10 @@ var DEFAULT_POLICY = {
3813
3817
  "reputation_export",
3814
3818
  "bootstrap_provide_guarantee",
3815
3819
  "decommission_certificate",
3816
- "reputation_publish"
3820
+ "reputation_publish",
3817
3821
  // SEC-039: Explicit Tier 1 — sends data to external API
3822
+ "sovereignty_profile_update"
3823
+ // Changes enforcement behavior — always requires approval
3818
3824
  ],
3819
3825
  tier2_anomaly: DEFAULT_TIER2,
3820
3826
  tier3_always_allow: [
@@ -3867,8 +3873,10 @@ var DEFAULT_POLICY = {
3867
3873
  "bridge_commit",
3868
3874
  "bridge_verify",
3869
3875
  "bridge_attest",
3870
- "dashboard_open"
3876
+ "dashboard_open",
3871
3877
  // SEC-039: Explicit Tier 3 — only generates a URL
3878
+ "sovereignty_profile_get",
3879
+ "sovereignty_profile_generate_prompt"
3872
3880
  ],
3873
3881
  approval_channel: DEFAULT_CHANNEL
3874
3882
  };
@@ -3977,6 +3985,7 @@ tier1_always_approve:
3977
3985
  - reputation_export
3978
3986
  - bootstrap_provide_guarantee
3979
3987
  - reputation_publish
3988
+ - sovereignty_profile_update
3980
3989
 
3981
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
3982
3991
  # Triggers approval when agent behavior deviates from its baseline.
@@ -4040,6 +4049,8 @@ tier3_always_allow:
4040
4049
  - bridge_verify
4041
4050
  - bridge_attest
4042
4051
  - dashboard_open
4052
+ - sovereignty_profile_get
4053
+ - sovereignty_profile_generate_prompt
4043
4054
 
4044
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
4045
4056
  # How Sanctuary reaches you when approval is needed.
@@ -5315,6 +5326,133 @@ function generateDashboardHTML(options) {
5315
5326
  background-color: #e03c3c;
5316
5327
  }
5317
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);
5398
+ }
5399
+
5400
+ .prompt-section {
5401
+ margin-top: 12px;
5402
+ }
5403
+
5404
+ .prompt-textarea {
5405
+ width: 100%;
5406
+ min-height: 120px;
5407
+ background-color: var(--bg);
5408
+ border: 1px solid var(--border);
5409
+ border-radius: 6px;
5410
+ color: var(--text-primary);
5411
+ font-family: 'JetBrains Mono', monospace;
5412
+ font-size: 12px;
5413
+ padding: 12px;
5414
+ resize: vertical;
5415
+ margin-top: 8px;
5416
+ }
5417
+
5418
+ .prompt-actions {
5419
+ display: flex;
5420
+ gap: 8px;
5421
+ margin-top: 8px;
5422
+ }
5423
+
5424
+ .prompt-btn {
5425
+ padding: 6px 12px;
5426
+ border: 1px solid var(--border);
5427
+ border-radius: 4px;
5428
+ background-color: var(--surface);
5429
+ color: var(--text-primary);
5430
+ font-size: 12px;
5431
+ cursor: pointer;
5432
+ }
5433
+
5434
+ .prompt-btn:hover {
5435
+ background-color: var(--muted);
5436
+ }
5437
+
5438
+ .prompt-btn.primary {
5439
+ background-color: var(--blue);
5440
+ color: var(--bg);
5441
+ border-color: var(--blue);
5442
+ }
5443
+
5444
+ @media (max-width: 900px) {
5445
+ .profile-cards {
5446
+ grid-template-columns: repeat(2, 1fr);
5447
+ }
5448
+ }
5449
+
5450
+ @media (max-width: 500px) {
5451
+ .profile-cards {
5452
+ grid-template-columns: 1fr;
5453
+ }
5454
+ }
5455
+
5318
5456
  /* Threat Panel */
5319
5457
  .threat-panel {
5320
5458
  background-color: var(--surface);
@@ -5653,6 +5791,48 @@ function generateDashboardHTML(options) {
5653
5791
  </div>
5654
5792
  </div>
5655
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
+
5656
5836
  <!-- Threat Panel -->
5657
5837
  <div class="threat-panel collapsed">
5658
5838
  <div class="threat-header">
@@ -5687,6 +5867,7 @@ function generateDashboardHTML(options) {
5687
5867
  handshakes: [],
5688
5868
  shr: null,
5689
5869
  status: null,
5870
+ systemPrompt: null,
5690
5871
  };
5691
5872
 
5692
5873
  let pendingRequests = new Map();
@@ -6139,6 +6320,10 @@ function generateDashboardHTML(options) {
6139
6320
  removePendingRequest(data.requestId);
6140
6321
  });
6141
6322
 
6323
+ eventSource.addEventListener('sovereignty-profile-update', () => {
6324
+ updateSovereigntyProfile();
6325
+ });
6326
+
6142
6327
  eventSource.onerror = () => {
6143
6328
  console.error('SSE error');
6144
6329
  setTimeout(setupSSE, 5000);
@@ -6295,6 +6480,58 @@ function generateDashboardHTML(options) {
6295
6480
  document.getElementById('pending-overlay').classList.toggle('show');
6296
6481
  });
6297
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
+
6298
6535
  // Initialize
6299
6536
  async function initialize() {
6300
6537
  if (!AUTH_TOKEN) {
@@ -6309,6 +6546,7 @@ function generateDashboardHTML(options) {
6309
6546
  updateHandshakes(),
6310
6547
  updateSHR(),
6311
6548
  updateStatus(),
6549
+ updateSovereigntyProfile(),
6312
6550
  ]);
6313
6551
 
6314
6552
  // Setup SSE for real-time updates
@@ -6325,6 +6563,80 @@ function generateDashboardHTML(options) {
6325
6563
  </html>`;
6326
6564
  }
6327
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
+
6328
6640
  // src/principal-policy/dashboard.ts
6329
6641
  var SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
6330
6642
  var SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
@@ -6345,6 +6657,7 @@ var DashboardApprovalChannel = class {
6345
6657
  handshakeResults = null;
6346
6658
  shrOpts = null;
6347
6659
  _sanctuaryConfig = null;
6660
+ profileStore = null;
6348
6661
  dashboardHTML;
6349
6662
  loginHTML;
6350
6663
  authToken;
@@ -6384,6 +6697,7 @@ var DashboardApprovalChannel = class {
6384
6697
  if (deps.handshakeResults) this.handshakeResults = deps.handshakeResults;
6385
6698
  if (deps.shrOpts) this.shrOpts = deps.shrOpts;
6386
6699
  if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
6700
+ if (deps.profileStore) this.profileStore = deps.profileStore;
6387
6701
  }
6388
6702
  /**
6389
6703
  * Mark this dashboard as running in standalone mode.
@@ -6731,6 +7045,10 @@ var DashboardApprovalChannel = class {
6731
7045
  this.handleHandshakes(res);
6732
7046
  } else if (method === "GET" && url.pathname === "/api/shr") {
6733
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);
6734
7052
  } else if (method === "POST" && url.pathname.startsWith("/api/approve/")) {
6735
7053
  if (!this.checkRateLimit(req, res, "decisions")) return;
6736
7054
  const id = url.pathname.slice("/api/approve/".length);
@@ -7022,6 +7340,61 @@ data: ${JSON.stringify(initData)}
7022
7340
  res.writeHead(200, { "Content-Type": "application/json" });
7023
7341
  res.end(JSON.stringify(shr));
7024
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
+ }
7025
7398
  // ── SSE Broadcasting ────────────────────────────────────────────────
7026
7399
  broadcastSSE(event, data) {
7027
7400
  const message = `event: ${event}
@@ -12704,6 +13077,270 @@ function createL2HardeningTools(storagePath, auditLog) {
12704
13077
  ];
12705
13078
  }
12706
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
+
12707
13344
  // src/index.ts
12708
13345
  init_random();
12709
13346
  init_encoding();
@@ -13212,6 +13849,9 @@ async function createSanctuaryServer(options) {
13212
13849
  const { tools: auditTools } = createAuditTools(config);
13213
13850
  const { tools: contextGateTools, enforcer: contextGateEnforcer } = createContextGateTools(storage, masterKey, auditLog);
13214
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);
13215
13855
  const policy = await loadPrincipalPolicy(config.storage_path);
13216
13856
  const baseline = new BaselineTracker(storage, masterKey);
13217
13857
  await baseline.load();
@@ -13239,7 +13879,8 @@ async function createSanctuaryServer(options) {
13239
13879
  identityManager,
13240
13880
  handshakeResults,
13241
13881
  shrOpts: { config, identityManager, masterKey },
13242
- sanctuaryConfig: config
13882
+ sanctuaryConfig: config,
13883
+ profileStore
13243
13884
  });
13244
13885
  await dashboard.start();
13245
13886
  approvalChannel = dashboard;
@@ -13316,6 +13957,7 @@ async function createSanctuaryServer(options) {
13316
13957
  ...auditTools,
13317
13958
  ...contextGateTools,
13318
13959
  ...hardeningTools,
13960
+ ...profileTools,
13319
13961
  ...dashboardTools,
13320
13962
  manifestTool
13321
13963
  ];
@@ -13365,6 +14007,7 @@ exports.MODEL_PRESETS = MODEL_PRESETS;
13365
14007
  exports.MemoryStorage = MemoryStorage;
13366
14008
  exports.PolicyStore = PolicyStore;
13367
14009
  exports.ReputationStore = ReputationStore;
14010
+ exports.SovereigntyProfileStore = SovereigntyProfileStore;
13368
14011
  exports.StateStore = StateStore;
13369
14012
  exports.StderrApprovalChannel = StderrApprovalChannel;
13370
14013
  exports.TIER_WEIGHTS = TIER_WEIGHTS;
@@ -13374,6 +14017,7 @@ exports.classifyField = classifyField;
13374
14017
  exports.completeHandshake = completeHandshake;
13375
14018
  exports.computeWeightedScore = computeWeightedScore;
13376
14019
  exports.createBridgeCommitment = createBridgeCommitment;
14020
+ exports.createDefaultProfile = createDefaultProfile;
13377
14021
  exports.createPedersenCommitment = createPedersenCommitment;
13378
14022
  exports.createProofOfKnowledge = createProofOfKnowledge;
13379
14023
  exports.createRangeProof = createRangeProof;
@@ -13382,6 +14026,7 @@ exports.evaluateField = evaluateField;
13382
14026
  exports.filterContext = filterContext;
13383
14027
  exports.generateAttestation = generateAttestation;
13384
14028
  exports.generateSHR = generateSHR;
14029
+ exports.generateSystemPrompt = generateSystemPrompt;
13385
14030
  exports.getTemplate = getTemplate;
13386
14031
  exports.initiateHandshake = initiateHandshake;
13387
14032
  exports.listTemplateIds = listTemplateIds;