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