@sanctuary-framework/mcp-server 0.5.6 → 0.5.7

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
@@ -4064,6 +4064,108 @@ var AutoApproveChannel = class {
4064
4064
  }
4065
4065
  };
4066
4066
 
4067
+ // src/shr/types.ts
4068
+ function deepSortKeys(obj) {
4069
+ if (obj === null || typeof obj !== "object") return obj;
4070
+ if (Array.isArray(obj)) return obj.map(deepSortKeys);
4071
+ const sorted = {};
4072
+ for (const key of Object.keys(obj).sort()) {
4073
+ sorted[key] = deepSortKeys(obj[key]);
4074
+ }
4075
+ return sorted;
4076
+ }
4077
+ function canonicalizeForSigning(body) {
4078
+ return JSON.stringify(deepSortKeys(body));
4079
+ }
4080
+
4081
+ // src/shr/generator.ts
4082
+ init_encoding();
4083
+ var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
4084
+ function generateSHR(identityId, opts) {
4085
+ const { config, identityManager, masterKey, validityMs } = opts;
4086
+ const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
4087
+ if (!identity) {
4088
+ return "No identity available for signing. Create an identity first.";
4089
+ }
4090
+ const now = /* @__PURE__ */ new Date();
4091
+ const expiresAt = new Date(now.getTime() + (validityMs ?? DEFAULT_VALIDITY_MS));
4092
+ const degradations = [];
4093
+ if (config.execution.environment === "local-process") {
4094
+ degradations.push({
4095
+ layer: "l2",
4096
+ code: "PROCESS_ISOLATION_ONLY",
4097
+ severity: "warning",
4098
+ description: "Process-level isolation only (no TEE)",
4099
+ mitigation: "TEE support planned for a future release"
4100
+ });
4101
+ degradations.push({
4102
+ layer: "l2",
4103
+ code: "SELF_REPORTED_ATTESTATION",
4104
+ severity: "warning",
4105
+ description: "Attestation is self-reported (no hardware root of trust)",
4106
+ mitigation: "TEE attestation planned for a future release"
4107
+ });
4108
+ }
4109
+ const body = {
4110
+ shr_version: "1.0",
4111
+ implementation: {
4112
+ sanctuary_version: config.version,
4113
+ node_version: process.versions.node,
4114
+ generated_by: "sanctuary-mcp-server"
4115
+ },
4116
+ instance_id: identity.identity_id,
4117
+ generated_at: now.toISOString(),
4118
+ expires_at: expiresAt.toISOString(),
4119
+ layers: {
4120
+ l1: {
4121
+ status: "active",
4122
+ encryption: config.state.encryption,
4123
+ key_custody: "self",
4124
+ integrity: config.state.integrity,
4125
+ identity_type: config.state.identity_provider,
4126
+ state_portable: true
4127
+ },
4128
+ l2: {
4129
+ status: config.execution.environment === "local-process" ? "degraded" : "active",
4130
+ isolation_type: config.execution.environment,
4131
+ attestation_available: config.execution.attestation
4132
+ },
4133
+ l3: {
4134
+ status: "active",
4135
+ proof_system: config.disclosure.proof_system,
4136
+ selective_disclosure: true
4137
+ },
4138
+ l4: {
4139
+ status: "active",
4140
+ reputation_mode: config.reputation.mode,
4141
+ attestation_format: config.reputation.attestation_format,
4142
+ reputation_portable: true
4143
+ }
4144
+ },
4145
+ capabilities: {
4146
+ handshake: true,
4147
+ shr_exchange: true,
4148
+ reputation_verify: true,
4149
+ encrypted_channel: false
4150
+ // Not yet implemented
4151
+ },
4152
+ degradations
4153
+ };
4154
+ const canonical = canonicalizeForSigning(body);
4155
+ const payload = stringToBytes(canonical);
4156
+ const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
4157
+ const signatureBytes = sign(
4158
+ payload,
4159
+ identity.encrypted_private_key,
4160
+ encryptionKey
4161
+ );
4162
+ return {
4163
+ body,
4164
+ signed_by: identity.public_key,
4165
+ signature: toBase64url(signatureBytes)
4166
+ };
4167
+ }
4168
+
4067
4169
  // src/principal-policy/dashboard-html.ts
4068
4170
  function generateLoginHTML(options) {
4069
4171
  return `<!DOCTYPE html>
@@ -4071,7 +4173,7 @@ function generateLoginHTML(options) {
4071
4173
  <head>
4072
4174
  <meta charset="UTF-8">
4073
4175
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
4074
- <title>Sanctuary Dashboard</title>
4176
+ <title>Sanctuary \u2014 Principal Dashboard</title>
4075
4177
  <style>
4076
4178
  :root {
4077
4179
  --bg: #0d1117;
@@ -4242,7 +4344,7 @@ function generateLoginHTML(options) {
4242
4344
 
4243
4345
  <form id="login-form">
4244
4346
  <div class="form-group">
4245
- <label for="auth-token">Bearer Token</label>
4347
+ <label for="auth-token">Auth Token</label>
4246
4348
  <input
4247
4349
  type="text"
4248
4350
  id="auth-token"
@@ -4327,7 +4429,7 @@ function generateDashboardHTML(options) {
4327
4429
  <head>
4328
4430
  <meta charset="UTF-8">
4329
4431
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
4330
- <title>Sanctuary Dashboard</title>
4432
+ <title>Sanctuary \u2014 Principal Dashboard</title>
4331
4433
  <style>
4332
4434
  :root {
4333
4435
  --bg: #0d1117;
@@ -5987,6 +6089,10 @@ var DashboardApprovalChannel = class {
5987
6089
  policy = null;
5988
6090
  baseline = null;
5989
6091
  auditLog = null;
6092
+ identityManager = null;
6093
+ handshakeResults = null;
6094
+ shrOpts = null;
6095
+ _sanctuaryConfig = null;
5990
6096
  dashboardHTML;
5991
6097
  loginHTML;
5992
6098
  authToken;
@@ -6020,6 +6126,10 @@ var DashboardApprovalChannel = class {
6020
6126
  this.policy = deps.policy;
6021
6127
  this.baseline = deps.baseline;
6022
6128
  this.auditLog = deps.auditLog;
6129
+ if (deps.identityManager) this.identityManager = deps.identityManager;
6130
+ if (deps.handshakeResults) this.handshakeResults = deps.handshakeResults;
6131
+ if (deps.shrOpts) this.shrOpts = deps.shrOpts;
6132
+ if (deps.sanctuaryConfig) this._sanctuaryConfig = deps.sanctuaryConfig;
6023
6133
  }
6024
6134
  /**
6025
6135
  * Start the HTTP(S) server for the dashboard.
@@ -6352,6 +6462,14 @@ var DashboardApprovalChannel = class {
6352
6462
  this.handlePendingList(res);
6353
6463
  } else if (method === "GET" && url.pathname === "/api/audit-log") {
6354
6464
  this.handleAuditLog(url, res);
6465
+ } else if (method === "GET" && url.pathname === "/api/sovereignty") {
6466
+ this.handleSovereignty(res);
6467
+ } else if (method === "GET" && url.pathname === "/api/identity") {
6468
+ this.handleIdentity(res);
6469
+ } else if (method === "GET" && url.pathname === "/api/handshakes") {
6470
+ this.handleHandshakes(res);
6471
+ } else if (method === "GET" && url.pathname === "/api/shr") {
6472
+ this.handleSHR(res);
6355
6473
  } else if (method === "POST" && url.pathname.startsWith("/api/approve/")) {
6356
6474
  if (!this.checkRateLimit(req, res, "decisions")) return;
6357
6475
  const id = url.pathname.slice("/api/approve/".length);
@@ -6541,6 +6659,107 @@ data: ${JSON.stringify(initData)}
6541
6659
  res.writeHead(200, { "Content-Type": "application/json" });
6542
6660
  res.end(JSON.stringify({ success: true, decision }));
6543
6661
  }
6662
+ // ── Sovereignty Data Routes ─────────────────────────────────────────
6663
+ handleSovereignty(res) {
6664
+ if (!this.shrOpts) {
6665
+ res.writeHead(200, { "Content-Type": "application/json" });
6666
+ res.end(JSON.stringify({ error: "SHR generator not available" }));
6667
+ return;
6668
+ }
6669
+ const shr = generateSHR(void 0, this.shrOpts);
6670
+ if (typeof shr === "string") {
6671
+ res.writeHead(200, { "Content-Type": "application/json" });
6672
+ res.end(JSON.stringify({ error: shr }));
6673
+ return;
6674
+ }
6675
+ const layers = shr.body.layers;
6676
+ let score = 0;
6677
+ for (const layer of [layers.l1, layers.l2, layers.l3, layers.l4]) {
6678
+ if (layer.status === "active") score += 25;
6679
+ else if (layer.status === "degraded") score += 15;
6680
+ }
6681
+ const overallLevel = score === 100 ? "full" : score >= 65 ? "degraded" : score >= 25 ? "minimal" : "unverified";
6682
+ res.writeHead(200, { "Content-Type": "application/json" });
6683
+ res.end(JSON.stringify({
6684
+ score,
6685
+ overall_level: overallLevel,
6686
+ layers: {
6687
+ l1: { status: layers.l1.status, detail: layers.l1.encryption, key_custody: layers.l1.key_custody },
6688
+ l2: { status: layers.l2.status, detail: layers.l2.isolation_type, attestation: layers.l2.attestation_available },
6689
+ l3: { status: layers.l3.status, detail: layers.l3.proof_system, selective_disclosure: layers.l3.selective_disclosure },
6690
+ l4: { status: layers.l4.status, detail: layers.l4.attestation_format, reputation_portable: layers.l4.reputation_portable }
6691
+ },
6692
+ degradations: shr.body.degradations,
6693
+ capabilities: shr.body.capabilities,
6694
+ config_loaded: this._sanctuaryConfig != null
6695
+ }));
6696
+ }
6697
+ handleIdentity(res) {
6698
+ if (!this.identityManager) {
6699
+ res.writeHead(200, { "Content-Type": "application/json" });
6700
+ res.end(JSON.stringify({ identities: [], count: 0 }));
6701
+ return;
6702
+ }
6703
+ const identities = this.identityManager.list().map((id) => ({
6704
+ identity_id: id.identity_id,
6705
+ label: id.label,
6706
+ public_key: id.public_key,
6707
+ did: id.did,
6708
+ created_at: id.created_at,
6709
+ key_type: id.key_type,
6710
+ key_protection: id.key_protection,
6711
+ rotation_count: id.rotation_history?.length ?? 0
6712
+ }));
6713
+ const primary = this.identityManager.getDefault();
6714
+ res.writeHead(200, { "Content-Type": "application/json" });
6715
+ res.end(JSON.stringify({
6716
+ identities,
6717
+ count: identities.length,
6718
+ primary_id: primary?.identity_id ?? null
6719
+ }));
6720
+ }
6721
+ handleHandshakes(res) {
6722
+ if (!this.handshakeResults) {
6723
+ res.writeHead(200, { "Content-Type": "application/json" });
6724
+ res.end(JSON.stringify({ handshakes: [], count: 0 }));
6725
+ return;
6726
+ }
6727
+ const handshakes = Array.from(this.handshakeResults.values()).map((h) => ({
6728
+ counterparty_id: h.counterparty_id,
6729
+ verified: h.verified,
6730
+ sovereignty_level: h.sovereignty_level,
6731
+ trust_tier: h.trust_tier,
6732
+ completed_at: h.completed_at,
6733
+ expires_at: h.expires_at,
6734
+ errors: h.errors
6735
+ }));
6736
+ handshakes.sort((a, b) => new Date(b.completed_at).getTime() - new Date(a.completed_at).getTime());
6737
+ res.writeHead(200, { "Content-Type": "application/json" });
6738
+ res.end(JSON.stringify({
6739
+ handshakes,
6740
+ count: handshakes.length,
6741
+ tier_distribution: {
6742
+ verified_sovereign: handshakes.filter((h) => h.trust_tier === "verified-sovereign").length,
6743
+ verified_degraded: handshakes.filter((h) => h.trust_tier === "verified-degraded").length,
6744
+ unverified: handshakes.filter((h) => h.trust_tier === "unverified").length
6745
+ }
6746
+ }));
6747
+ }
6748
+ handleSHR(res) {
6749
+ if (!this.shrOpts) {
6750
+ res.writeHead(200, { "Content-Type": "application/json" });
6751
+ res.end(JSON.stringify({ error: "SHR generator not available" }));
6752
+ return;
6753
+ }
6754
+ const shr = generateSHR(void 0, this.shrOpts);
6755
+ if (typeof shr === "string") {
6756
+ res.writeHead(200, { "Content-Type": "application/json" });
6757
+ res.end(JSON.stringify({ error: shr }));
6758
+ return;
6759
+ }
6760
+ res.writeHead(200, { "Content-Type": "application/json" });
6761
+ res.end(JSON.stringify(shr));
6762
+ }
6544
6763
  // ── SSE Broadcasting ────────────────────────────────────────────────
6545
6764
  broadcastSSE(event, data) {
6546
6765
  const message = `event: ${event}
@@ -7707,108 +7926,6 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
7707
7926
  ];
7708
7927
  }
7709
7928
 
7710
- // src/shr/types.ts
7711
- function deepSortKeys(obj) {
7712
- if (obj === null || typeof obj !== "object") return obj;
7713
- if (Array.isArray(obj)) return obj.map(deepSortKeys);
7714
- const sorted = {};
7715
- for (const key of Object.keys(obj).sort()) {
7716
- sorted[key] = deepSortKeys(obj[key]);
7717
- }
7718
- return sorted;
7719
- }
7720
- function canonicalizeForSigning(body) {
7721
- return JSON.stringify(deepSortKeys(body));
7722
- }
7723
-
7724
- // src/shr/generator.ts
7725
- init_encoding();
7726
- var DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
7727
- function generateSHR(identityId, opts) {
7728
- const { config, identityManager, masterKey, validityMs } = opts;
7729
- const identity = identityId ? identityManager.get(identityId) : identityManager.getDefault();
7730
- if (!identity) {
7731
- return "No identity available for signing. Create an identity first.";
7732
- }
7733
- const now = /* @__PURE__ */ new Date();
7734
- const expiresAt = new Date(now.getTime() + (validityMs ?? DEFAULT_VALIDITY_MS));
7735
- const degradations = [];
7736
- if (config.execution.environment === "local-process") {
7737
- degradations.push({
7738
- layer: "l2",
7739
- code: "PROCESS_ISOLATION_ONLY",
7740
- severity: "warning",
7741
- description: "Process-level isolation only (no TEE)",
7742
- mitigation: "TEE support planned for a future release"
7743
- });
7744
- degradations.push({
7745
- layer: "l2",
7746
- code: "SELF_REPORTED_ATTESTATION",
7747
- severity: "warning",
7748
- description: "Attestation is self-reported (no hardware root of trust)",
7749
- mitigation: "TEE attestation planned for a future release"
7750
- });
7751
- }
7752
- const body = {
7753
- shr_version: "1.0",
7754
- implementation: {
7755
- sanctuary_version: config.version,
7756
- node_version: process.versions.node,
7757
- generated_by: "sanctuary-mcp-server"
7758
- },
7759
- instance_id: identity.identity_id,
7760
- generated_at: now.toISOString(),
7761
- expires_at: expiresAt.toISOString(),
7762
- layers: {
7763
- l1: {
7764
- status: "active",
7765
- encryption: config.state.encryption,
7766
- key_custody: "self",
7767
- integrity: config.state.integrity,
7768
- identity_type: config.state.identity_provider,
7769
- state_portable: true
7770
- },
7771
- l2: {
7772
- status: config.execution.environment === "local-process" ? "degraded" : "active",
7773
- isolation_type: config.execution.environment,
7774
- attestation_available: config.execution.attestation
7775
- },
7776
- l3: {
7777
- status: "active",
7778
- proof_system: config.disclosure.proof_system,
7779
- selective_disclosure: true
7780
- },
7781
- l4: {
7782
- status: "active",
7783
- reputation_mode: config.reputation.mode,
7784
- attestation_format: config.reputation.attestation_format,
7785
- reputation_portable: true
7786
- }
7787
- },
7788
- capabilities: {
7789
- handshake: true,
7790
- shr_exchange: true,
7791
- reputation_verify: true,
7792
- encrypted_channel: false
7793
- // Not yet implemented
7794
- },
7795
- degradations
7796
- };
7797
- const canonical = canonicalizeForSigning(body);
7798
- const payload = stringToBytes(canonical);
7799
- const encryptionKey = derivePurposeKey(masterKey, "identity-encryption");
7800
- const signatureBytes = sign(
7801
- payload,
7802
- identity.encrypted_private_key,
7803
- encryptionKey
7804
- );
7805
- return {
7806
- body,
7807
- signed_by: identity.public_key,
7808
- signature: toBase64url(signatureBytes)
7809
- };
7810
- }
7811
-
7812
7929
  // src/shr/verifier.ts
7813
7930
  init_encoding();
7814
7931
  function verifySHR(shr, now) {
@@ -12679,7 +12796,15 @@ async function createSanctuaryServer(options) {
12679
12796
  tls: config.dashboard.tls,
12680
12797
  auto_open: config.dashboard.auto_open
12681
12798
  });
12682
- dashboard.setDependencies({ policy, baseline, auditLog });
12799
+ dashboard.setDependencies({
12800
+ policy,
12801
+ baseline,
12802
+ auditLog,
12803
+ identityManager,
12804
+ handshakeResults,
12805
+ shrOpts: { config, identityManager, masterKey },
12806
+ sanctuaryConfig: config
12807
+ });
12683
12808
  await dashboard.start();
12684
12809
  approvalChannel = dashboard;
12685
12810
  } else if (config.webhook.enabled && config.webhook.url && config.webhook.secret) {