@sanctuary-framework/mcp-server 1.2.2 → 1.2.3

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/cli.js CHANGED
@@ -4421,13 +4421,23 @@ function parseScalar(value) {
4421
4421
  return value.replace(/^["']|["']$/g, "");
4422
4422
  }
4423
4423
  function validatePolicy(raw) {
4424
+ if (!("tier1_always_approve" in raw)) {
4425
+ throw new Error(
4426
+ "Policy file must include 'tier1_always_approve' as an explicit list (use [] for empty). Remove specific entries instead of removing the whole key."
4427
+ );
4428
+ }
4429
+ if (!("approval_channel" in raw)) {
4430
+ throw new Error(
4431
+ "Policy file must include 'approval_channel' as an explicit object (use {} for defaults). Remove specific entries instead of removing the whole key."
4432
+ );
4433
+ }
4424
4434
  const userTier3 = raw.tier3_always_allow ?? [];
4425
4435
  const mergedTier3 = [
4426
4436
  .../* @__PURE__ */ new Set([...userTier3, ...DEFAULT_POLICY.tier3_always_allow])
4427
4437
  ];
4428
4438
  return {
4429
4439
  version: raw.version ?? 1,
4430
- tier1_always_approve: raw.tier1_always_approve ?? DEFAULT_POLICY.tier1_always_approve,
4440
+ tier1_always_approve: raw.tier1_always_approve,
4431
4441
  tier2_anomaly: {
4432
4442
  ...DEFAULT_TIER2,
4433
4443
  ...raw.tier2_anomaly ?? {}
@@ -4448,6 +4458,11 @@ function generateDefaultPolicyYaml() {
4448
4458
  # This file controls what your agent can do without asking.
4449
4459
  # Edit this file directly. Your agent cannot modify it.
4450
4460
  # Changes take effect on server restart.
4461
+ #
4462
+ # Required keys (must be present; use [] or {} for empty):
4463
+ # tier1_always_approve, approval_channel
4464
+ # Optional keys (omit to use defaults; new defaults merge automatically):
4465
+ # tier2_anomaly, tier3_always_allow
4451
4466
 
4452
4467
  version: 1
4453
4468
 
@@ -11567,6 +11582,7 @@ __export(passphrase_exports, {
11567
11582
  getOrCreatePassphrase: () => getOrCreatePassphrase,
11568
11583
  isOsKeyringLocation: () => isOsKeyringLocation,
11569
11584
  keychainServiceFor: () => keychainServiceFor,
11585
+ legacyKeychainServiceFor: () => legacyKeychainServiceFor,
11570
11586
  persistUserProvidedPassphrase: () => persistUserProvidedPassphrase,
11571
11587
  readStoredPassphrase: () => readStoredPassphrase
11572
11588
  });
@@ -11580,16 +11596,29 @@ async function getOrCreatePassphrase(opts = {}) {
11580
11596
  const plat = opts.platformOverride ?? platform();
11581
11597
  const exec2 = opts.exec ?? defaultExec;
11582
11598
  const derive = opts.deriveMachineKey ?? deriveMachineKey;
11599
+ const legacyService = legacyKeychainServiceFor(storagePath, home);
11583
11600
  if (plat === "darwin") {
11584
11601
  const fromKc = await readFromKeychain(exec2, service);
11585
11602
  if (fromKc) {
11586
11603
  return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11587
11604
  }
11605
+ if (legacyService !== service) {
11606
+ const fromLegacy = await readFromKeychain(exec2, legacyService);
11607
+ if (fromLegacy) {
11608
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11609
+ }
11610
+ }
11588
11611
  } else if (plat === "linux") {
11589
11612
  const fromSs = await readFromSecretService(exec2, service);
11590
11613
  if (fromSs) {
11591
11614
  return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11592
11615
  }
11616
+ if (legacyService !== service) {
11617
+ const fromLegacy = await readFromSecretService(exec2, legacyService);
11618
+ if (fromLegacy) {
11619
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11620
+ }
11621
+ }
11593
11622
  }
11594
11623
  const fallback = fallbackFilePath(home, storagePath);
11595
11624
  const fromFile = await readFromFallbackFile(fallback, home, derive);
@@ -11622,6 +11651,7 @@ async function readStoredPassphrase(opts = {}) {
11622
11651
  const home = opts.home ?? homedir();
11623
11652
  const storagePath = opts.storagePath ?? resolveStoragePath(process.env, home);
11624
11653
  const service = keychainServiceFor(storagePath, home);
11654
+ const legacyService = legacyKeychainServiceFor(storagePath, home);
11625
11655
  const plat = opts.platformOverride ?? platform();
11626
11656
  const exec2 = opts.exec ?? defaultExec;
11627
11657
  const derive = opts.deriveMachineKey ?? deriveMachineKey;
@@ -11630,11 +11660,23 @@ async function readStoredPassphrase(opts = {}) {
11630
11660
  if (fromKc) {
11631
11661
  return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11632
11662
  }
11663
+ if (legacyService !== service) {
11664
+ const fromLegacy = await readFromKeychain(exec2, legacyService);
11665
+ if (fromLegacy) {
11666
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11667
+ }
11668
+ }
11633
11669
  } else if (plat === "linux") {
11634
11670
  const fromSs = await readFromSecretService(exec2, service);
11635
11671
  if (fromSs) {
11636
11672
  return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11637
11673
  }
11674
+ if (legacyService !== service) {
11675
+ const fromLegacy = await readFromSecretService(exec2, legacyService);
11676
+ if (fromLegacy) {
11677
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11678
+ }
11679
+ }
11638
11680
  }
11639
11681
  const fallback = fallbackFilePath(home, storagePath);
11640
11682
  const fromFile = await readFromFallbackFile(fallback, home, derive);
@@ -11715,9 +11757,18 @@ async function writeToKeychain(value, exec2, service = KEYCHAIN_SERVICE_DEFAULT)
11715
11757
  }
11716
11758
  }
11717
11759
  function keychainServiceFor(storagePath, home = homedir()) {
11718
- const defaultPath = join(home, DEFAULT_STORAGE_DIR);
11719
- if (storagePath === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11720
- const digest = sha256(Buffer.from(storagePath, "utf-8"));
11760
+ const defaultPath = resolve(join(home, DEFAULT_STORAGE_DIR));
11761
+ const canonicalStorage = resolve(storagePath);
11762
+ if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11763
+ const digest = sha256(Buffer.from(canonicalStorage, "utf-8"));
11764
+ const suffix = Buffer.from(digest).toString("hex").slice(0, 16);
11765
+ return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
11766
+ }
11767
+ function legacyKeychainServiceFor(storagePath, home = homedir()) {
11768
+ const defaultPath = resolve(join(home, DEFAULT_STORAGE_DIR));
11769
+ const canonicalStorage = resolve(storagePath);
11770
+ if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11771
+ const digest = sha256(Buffer.from(canonicalStorage, "utf-8"));
11721
11772
  const suffix = Buffer.from(digest).toString("hex").slice(0, 12);
11722
11773
  return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
11723
11774
  }
@@ -11804,7 +11855,7 @@ function deriveMachineKey(home) {
11804
11855
  return hkdf(sha256, material, void 0, "sanctuary-passphrase-v1", 32);
11805
11856
  }
11806
11857
  async function defaultExec(cmd, args, input) {
11807
- return new Promise((resolve6, reject) => {
11858
+ return new Promise((resolve8, reject) => {
11808
11859
  const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
11809
11860
  let stdout = "";
11810
11861
  let stderr = "";
@@ -11815,7 +11866,7 @@ async function defaultExec(cmd, args, input) {
11815
11866
  stderr += d.toString();
11816
11867
  });
11817
11868
  child.on("error", reject);
11818
- child.on("close", (code) => resolve6({ stdout, stderr, code }));
11869
+ child.on("close", (code) => resolve8({ stdout, stderr, code }));
11819
11870
  if (input !== void 0) {
11820
11871
  child.stdin.write(input);
11821
11872
  }
@@ -12009,7 +12060,7 @@ async function discoverTenants(options = {}) {
12009
12060
  for (const child of children) {
12010
12061
  const childPath = join(root, child);
12011
12062
  if (child.startsWith(".")) continue;
12012
- if (child === "state" || child === "backup" || child === "config") continue;
12063
+ if (child === "state" || child === "backup" || child === "config" || child === "default") continue;
12013
12064
  const s = await stat(childPath).catch(() => null);
12014
12065
  if (!s || !s.isDirectory()) continue;
12015
12066
  const desc = await describeTenant(child, childPath, home);
@@ -12021,6 +12072,17 @@ async function discoverTenants(options = {}) {
12021
12072
  const desc = await describeTenant(basename(extra), extra, home);
12022
12073
  if (desc) tenants.push(desc);
12023
12074
  }
12075
+ const seen = /* @__PURE__ */ new Map();
12076
+ for (const t of tenants) {
12077
+ seen.set(t.name, (seen.get(t.name) ?? 0) + 1);
12078
+ }
12079
+ for (const [name, count] of seen) {
12080
+ if (count > 1) {
12081
+ console.error(
12082
+ `[sanctuary] warning: ${count} tenants share the name "${name}". Use --tenant with a unique name or storage path to disambiguate.`
12083
+ );
12084
+ }
12085
+ }
12024
12086
  tenants.sort((a, b) => {
12025
12087
  if (a.name === "default") return -1;
12026
12088
  if (b.name === "default") return 1;
@@ -16819,6 +16881,8 @@ var init_intelligence_api_router = __esm({
16819
16881
  this.code = code;
16820
16882
  this.name = "IntelligenceRouterError";
16821
16883
  }
16884
+ statusCode;
16885
+ code;
16822
16886
  };
16823
16887
  }
16824
16888
  });
@@ -17095,7 +17159,7 @@ var init_dashboard = __esm({
17095
17159
  server = createServer$2(handler);
17096
17160
  }
17097
17161
  this.httpServer = server;
17098
- return new Promise((resolve6, reject) => {
17162
+ return new Promise((resolve8, reject) => {
17099
17163
  const protocol = this.useTLS ? "https" : "http";
17100
17164
  const baseUrl = `${protocol}://${this.config.host}:${this.config.port}`;
17101
17165
  server.listen(this.config.port, this.config.host, () => {
@@ -17120,7 +17184,7 @@ var init_dashboard = __esm({
17120
17184
  if (shouldAutoOpen) {
17121
17185
  this.openInBrowser(sessionUrl);
17122
17186
  }
17123
- resolve6();
17187
+ resolve8();
17124
17188
  });
17125
17189
  server.on("error", (err) => {
17126
17190
  if (err.code === "EADDRINUSE") {
@@ -17166,8 +17230,8 @@ var init_dashboard = __esm({
17166
17230
  }
17167
17231
  this.rateLimits.clear();
17168
17232
  if (this.httpServer) {
17169
- return new Promise((resolve6) => {
17170
- this.httpServer.close(() => resolve6());
17233
+ return new Promise((resolve8) => {
17234
+ this.httpServer.close(() => resolve8());
17171
17235
  });
17172
17236
  }
17173
17237
  }
@@ -17181,7 +17245,7 @@ var init_dashboard = __esm({
17181
17245
  `[Sanctuary] Approval required: ${request.operation} (Tier ${request.tier}) \u2014 open dashboard to respond
17182
17246
  `
17183
17247
  );
17184
- return new Promise((resolve6) => {
17248
+ return new Promise((resolve8) => {
17185
17249
  const timer = setTimeout(() => {
17186
17250
  this.pending.delete(id);
17187
17251
  const response = {
@@ -17195,12 +17259,12 @@ var init_dashboard = __esm({
17195
17259
  decision: response.decision,
17196
17260
  decided_by: "timeout"
17197
17261
  });
17198
- resolve6(response);
17262
+ resolve8(response);
17199
17263
  }, this.config.timeout_seconds * 1e3);
17200
17264
  const pending = {
17201
17265
  id,
17202
17266
  request,
17203
- resolve: resolve6,
17267
+ resolve: resolve8,
17204
17268
  timer,
17205
17269
  created_at: (/* @__PURE__ */ new Date()).toISOString()
17206
17270
  };
@@ -18066,7 +18130,7 @@ var init_webhook = __esm({
18066
18130
  * Start the callback listener server.
18067
18131
  */
18068
18132
  async start() {
18069
- return new Promise((resolve6, reject) => {
18133
+ return new Promise((resolve8, reject) => {
18070
18134
  this.callbackServer = createServer$2(
18071
18135
  (req, res) => this.handleCallback(req, res)
18072
18136
  );
@@ -18081,7 +18145,7 @@ var init_webhook = __esm({
18081
18145
 
18082
18146
  `
18083
18147
  );
18084
- resolve6();
18148
+ resolve8();
18085
18149
  }
18086
18150
  );
18087
18151
  this.callbackServer.on("error", reject);
@@ -18101,8 +18165,8 @@ var init_webhook = __esm({
18101
18165
  }
18102
18166
  this.pending.clear();
18103
18167
  if (this.callbackServer) {
18104
- return new Promise((resolve6) => {
18105
- this.callbackServer.close(() => resolve6());
18168
+ return new Promise((resolve8) => {
18169
+ this.callbackServer.close(() => resolve8());
18106
18170
  });
18107
18171
  }
18108
18172
  }
@@ -18115,7 +18179,7 @@ var init_webhook = __esm({
18115
18179
  `[Sanctuary] Webhook approval sent: ${request.operation} (Tier ${request.tier}) \u2014 awaiting callback
18116
18180
  `
18117
18181
  );
18118
- return new Promise((resolve6) => {
18182
+ return new Promise((resolve8) => {
18119
18183
  const timer = setTimeout(() => {
18120
18184
  this.pending.delete(id);
18121
18185
  const response = {
@@ -18124,12 +18188,12 @@ var init_webhook = __esm({
18124
18188
  decided_at: (/* @__PURE__ */ new Date()).toISOString(),
18125
18189
  decided_by: "timeout"
18126
18190
  };
18127
- resolve6(response);
18191
+ resolve8(response);
18128
18192
  }, this.config.timeout_seconds * 1e3);
18129
18193
  const pending = {
18130
18194
  id,
18131
18195
  request,
18132
- resolve: resolve6,
18196
+ resolve: resolve8,
18133
18197
  timer,
18134
18198
  created_at: (/* @__PURE__ */ new Date()).toISOString()
18135
18199
  };
@@ -19422,12 +19486,25 @@ var init_injection_detector = __esm({
19422
19486
  }
19423
19487
  });
19424
19488
 
19489
+ // src/principal-policy/deny-vocabulary.ts
19490
+ var AGENT_VISIBLE_DENY_REASONS;
19491
+ var init_deny_vocabulary = __esm({
19492
+ "src/principal-policy/deny-vocabulary.ts"() {
19493
+ AGENT_VISIBLE_DENY_REASONS = {
19494
+ REQUIRES_APPROVAL: "operation requires operator approval",
19495
+ NOT_PERMITTED: "operation not permitted",
19496
+ REQUIRES_OPERATOR: "operation requires operator action"
19497
+ };
19498
+ }
19499
+ });
19500
+
19425
19501
  // src/principal-policy/gate.ts
19426
19502
  var ApprovalGate;
19427
19503
  var init_gate = __esm({
19428
19504
  "src/principal-policy/gate.ts"() {
19429
19505
  init_loader();
19430
19506
  init_injection_detector();
19507
+ init_deny_vocabulary();
19431
19508
  ApprovalGate = class {
19432
19509
  policy;
19433
19510
  baseline;
@@ -19479,10 +19556,16 @@ var init_gate = __esm({
19479
19556
  });
19480
19557
  }
19481
19558
  if (injectionResult.recommendation === "block") {
19559
+ this.auditLog.append("l2", `gate_injection_block:${operation}`, "system", {
19560
+ tier: 1,
19561
+ operation,
19562
+ injection_confidence: injectionResult.confidence,
19563
+ signal_count: injectionResult.signals.length
19564
+ });
19482
19565
  return {
19483
19566
  allowed: false,
19484
19567
  tier: 1,
19485
- reason: `Blocked: prompt injection detected in "${operation}" (confidence: ${(injectionResult.confidence * 100).toFixed(0)}%)`,
19568
+ reason: AGENT_VISIBLE_DENY_REASONS.NOT_PERMITTED,
19486
19569
  approval_required: false
19487
19570
  };
19488
19571
  }
@@ -19559,7 +19642,7 @@ var init_gate = __esm({
19559
19642
  this.auditLog.append("l2", `gate_unclassified:${operation}`, "system", {
19560
19643
  tier: 1,
19561
19644
  operation,
19562
- warning: "Operation is not classified in any policy tier \u2014 defaulting to Tier 1 (require approval)"
19645
+ warning: "Operation is not classified in any policy tier, defaulting to Tier 1 (require approval)"
19563
19646
  });
19564
19647
  return this.requestApproval(
19565
19648
  operation,
@@ -19688,7 +19771,7 @@ var init_gate = __esm({
19688
19771
  return {
19689
19772
  allowed: response.decision === "approve",
19690
19773
  tier,
19691
- reason: response.decision === "approve" ? `Approved by ${response.decided_by}` : `Tier ${tier} operation requires approval`,
19774
+ reason: response.decision === "approve" ? `Approved by ${response.decided_by}` : AGENT_VISIBLE_DENY_REASONS.REQUIRES_APPROVAL,
19692
19775
  approval_required: true,
19693
19776
  approval_response: response
19694
19777
  };
@@ -20277,7 +20360,11 @@ var init_tools5 = __esm({
20277
20360
 
20278
20361
  // src/handshake/protocol.ts
20279
20362
  function generateNonce() {
20280
- return toBase64url(randomBytes(32));
20363
+ const nonce = randomBytes(32);
20364
+ if (!nonce || nonce.length !== 32) {
20365
+ throw new Error("Nonce generation failed: randomBytes returned unexpected length");
20366
+ }
20367
+ return toBase64url(nonce);
20281
20368
  }
20282
20369
  function initiateHandshake(ourSHR) {
20283
20370
  const nonce = generateNonce();
@@ -20388,6 +20475,18 @@ function completeHandshake(response, session, identityManager, masterKey, identi
20388
20475
  return { completion, result };
20389
20476
  }
20390
20477
  function verifyCompletion(completion, session) {
20478
+ if (completion.protocol_version !== "1.0") {
20479
+ return {
20480
+ counterparty_id: "unknown",
20481
+ counterparty_shr: session.our_shr,
20482
+ verified: false,
20483
+ sovereignty_level: "unverified",
20484
+ trust_tier: "unverified",
20485
+ completed_at: completion.completed_at,
20486
+ expires_at: (/* @__PURE__ */ new Date()).toISOString(),
20487
+ errors: [`Unsupported protocol version: ${completion.protocol_version}`]
20488
+ };
20489
+ }
20391
20490
  const errors = [];
20392
20491
  if (!session.their_shr) {
20393
20492
  return {
@@ -22674,6 +22773,9 @@ Inspect the file and either correct the JSON or delete it manually before re-run
22674
22773
  this.cause = cause;
22675
22774
  this.name = "ResetHistoryMalformedError";
22676
22775
  }
22776
+ markerPath;
22777
+ lineNumber;
22778
+ cause;
22677
22779
  };
22678
22780
  }
22679
22781
  });
@@ -24832,7 +24934,7 @@ async function runOpenAIPrivacyFilter(text, config) {
24832
24934
  return parsed;
24833
24935
  }
24834
24936
  function runCommand(command, input, timeoutMs) {
24835
- return new Promise((resolve6, reject) => {
24937
+ return new Promise((resolve8, reject) => {
24836
24938
  const child = spawn(command, [], {
24837
24939
  stdio: ["pipe", "pipe", "pipe"],
24838
24940
  shell: false
@@ -24863,7 +24965,7 @@ function runCommand(command, input, timeoutMs) {
24863
24965
  ));
24864
24966
  return;
24865
24967
  }
24866
- resolve6(stdout);
24968
+ resolve8(stdout);
24867
24969
  });
24868
24970
  child.stdin.end(input);
24869
24971
  });
@@ -26739,13 +26841,13 @@ var init_proxy_router = __esm({
26739
26841
  * Call an upstream tool with a timeout.
26740
26842
  */
26741
26843
  async callWithTimeout(serverName, toolName, args, timeoutMs) {
26742
- return new Promise((resolve6, reject) => {
26844
+ return new Promise((resolve8, reject) => {
26743
26845
  const timer = setTimeout(() => {
26744
26846
  reject(new Error(`Upstream tool call timed out after ${timeoutMs}ms`));
26745
26847
  }, timeoutMs);
26746
26848
  this.clientManager.callTool(serverName, toolName, args).then((result) => {
26747
26849
  clearTimeout(timer);
26748
- resolve6(result);
26850
+ resolve8(result);
26749
26851
  }).catch((err) => {
26750
26852
  clearTimeout(timer);
26751
26853
  reject(err);
@@ -32081,7 +32183,7 @@ function makeEventId(prefix) {
32081
32183
  function hashOf(input) {
32082
32184
  return hashToString(sha256(stringToBytes(input)));
32083
32185
  }
32084
- var DEFAULT_CONCIERGE_MAX_TOKENS, OperatorChatService;
32186
+ var DEFAULT_CONCIERGE_MAX_TOKENS, SANCTUARY_DOMAIN_REFERENCE, OperatorChatService;
32085
32187
  var init_operator_chat_service = __esm({
32086
32188
  "src/chat/operator-chat-service.ts"() {
32087
32189
  init_hashing();
@@ -32089,6 +32191,33 @@ var init_operator_chat_service = __esm({
32089
32191
  init_operator_chat_audit_events();
32090
32192
  init_operator_chat_types();
32091
32193
  DEFAULT_CONCIERGE_MAX_TOKENS = 512;
32194
+ SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
32195
+ 1. Castle Wall: OS-boundary egress filter enforced at the kernel level. Blocks unauthorized outbound calls even from prompt-injected agents.
32196
+ 2. Sentinels: internal observation via process introspection. Surfaces anomalies to the operator; does not enforce.
32197
+ 3. Charter (Cooperative MCP): the sovereignty surface for compliant agents. Policy gates, approval tiers, audit logging, and encrypted state all live here.
32198
+ 4. Heralds: Concordia receipts and Verascore reputation. Cross-fortress accountability after an action completes.
32199
+
32200
+ Five channel templates (canonical names):
32201
+ - request-approve-act: agent proposes an action, operator approves or denies before execution.
32202
+ - read-then-report: agent reads outputs from a data source and reports summaries to the operator.
32203
+ - scheduled-digest: agent runs on a schedule and delivers a periodic digest.
32204
+ - plan-draft-only: agent drafts plans; operator reviews before any execution step.
32205
+ - fortress-relay: agent relays messages between fortresses under operator-scoped policy.
32206
+
32207
+ Four canonical policy slots:
32208
+ - memory: governs what the agent may persist and retrieve from encrypted state.
32209
+ - credentials: governs access to secrets, API keys, and tokens held in the broker.
32210
+ - plans: governs the agent's ability to create, modify, or execute plans.
32211
+ - outputs: governs what the agent may emit to external surfaces (files, APIs, messages).
32212
+
32213
+ Key concepts:
32214
+ - Fortress: the operator-owned sovereignty harness. All state is encrypted at rest under the cocoon.
32215
+ - Cocoon: master-key-wrapped storage derived from the operator's passphrase via Argon2id.
32216
+ - Identity: Ed25519 keypair with a DID, owned by the operator. Private keys never leave the cocoon.
32217
+ - Audit log: append-only encrypted blobs, sequential, recording every gate decision and tool call.
32218
+ - Wrapped agent: any agent runtime that connects to Sanctuary as an MCP client. Tier A (native), Tier B (adapter-wrapped), Tier C (escape hatch).
32219
+
32220
+ Note: this is a static reference block (v1.2.x). Dynamic context injection (live template list, policy schema) ships in v1.3.`;
32092
32221
  OperatorChatService = class {
32093
32222
  store;
32094
32223
  auditLog;
@@ -32233,6 +32362,9 @@ var init_operator_chat_service = __esm({
32233
32362
  * than nested structures. Format:
32234
32363
  *
32235
32364
  * ```
32365
+ * ## Sanctuary reference
32366
+ * <static domain reference block>
32367
+ *
32236
32368
  * ## Recent activity
32237
32369
  * <recentActivity output>
32238
32370
  *
@@ -32244,15 +32376,28 @@ var init_operator_chat_service = __esm({
32244
32376
  * ```
32245
32377
  */
32246
32378
  async assembleConciergeContext() {
32379
+ const ref = `## Sanctuary reference
32380
+ ${SANCTUARY_DOMAIN_REFERENCE}`;
32247
32381
  if (!this.contextProviders) {
32248
- return "## Recent activity\n(no providers wired)\n\n## Wrapped agents\n(no providers wired)\n\n## Open inbox\n(no providers wired)";
32382
+ return `${ref}
32383
+
32384
+ ## Recent activity
32385
+ (no providers wired)
32386
+
32387
+ ## Wrapped agents
32388
+ (no providers wired)
32389
+
32390
+ ## Open inbox
32391
+ (no providers wired)`;
32249
32392
  }
32250
32393
  const [activity, agents, inbox] = await Promise.all([
32251
32394
  this.contextProviders.recentActivity(),
32252
32395
  this.contextProviders.agentInventory(),
32253
32396
  this.contextProviders.openInbox()
32254
32397
  ]);
32255
- return `## Recent activity
32398
+ return `${ref}
32399
+
32400
+ ## Recent activity
32256
32401
  ${activity}
32257
32402
 
32258
32403
  ## Wrapped agents
@@ -35284,6 +35429,9 @@ async function importExitBundle(opts) {
35284
35429
  reputationArtifact?.json ?? null,
35285
35430
  manifest
35286
35431
  );
35432
+ if (!conflicts.public_identity_exists && identityArtifact?.json && opts.identityManager.getPrimaryIdentityId() !== null && opts.identityManager.getPrimaryIdentityId() !== identityArtifact.json.bundle.identity_id) {
35433
+ conflicts.public_identity_exists = true;
35434
+ }
35287
35435
  if (!opts.activate) {
35288
35436
  return {
35289
35437
  verified: true,
@@ -35310,7 +35458,7 @@ async function importExitBundle(opts) {
35310
35458
  if (conflicts.public_identity_exists && !opts.forceRebind) {
35311
35459
  throw new ExitBundleImportError(
35312
35460
  "IDENTITY_OVERWRITE_REFUSED",
35313
- "Importing this bundle would overwrite an existing fortress public identity. Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
35461
+ "Importing this exit bundle would overwrite an existing fortress public identity (either the same identity already imported, or a different identity is currently active). Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
35314
35462
  );
35315
35463
  }
35316
35464
  if (conflicts.public_identity_exists && opts.forceRebind && identityArtifact) {
@@ -35700,6 +35848,26 @@ async function runExitCommand(args) {
35700
35848
  write(err, "Usage: sanctuary exit import <dir> [--activate]\n");
35701
35849
  return 2;
35702
35850
  }
35851
+ const bundleRoot = resolve(dir);
35852
+ try {
35853
+ await access(bundleRoot);
35854
+ } catch {
35855
+ write(err, `Error: bundle directory not found: ${bundleRoot}
35856
+ `);
35857
+ return 1;
35858
+ }
35859
+ const manifestPath = join(bundleRoot, "manifest.json");
35860
+ try {
35861
+ const raw = await readFile(manifestPath, "utf8");
35862
+ JSON.parse(raw);
35863
+ } catch {
35864
+ write(
35865
+ err,
35866
+ `Error: bundle manifest missing or malformed at ${manifestPath}
35867
+ `
35868
+ );
35869
+ return 1;
35870
+ }
35703
35871
  const activate = hasFlag(argv, "--activate");
35704
35872
  const forceRebind = hasFlag(argv, "--force-rebind");
35705
35873
  const acceptUnverifiableAttestations = hasFlag(
@@ -35873,11 +36041,11 @@ async function startDashboardServer(options) {
35873
36041
  }
35874
36042
  }
35875
36043
  });
35876
- await new Promise((resolve6, reject) => {
36044
+ await new Promise((resolve8, reject) => {
35877
36045
  server.once("error", reject);
35878
36046
  server.listen(port, host, () => {
35879
36047
  server.off("error", reject);
35880
- resolve6();
36048
+ resolve8();
35881
36049
  });
35882
36050
  });
35883
36051
  const actualPort = (() => {
@@ -35890,8 +36058,8 @@ async function startDashboardServer(options) {
35890
36058
  url,
35891
36059
  port: actualPort,
35892
36060
  host,
35893
- stop: () => new Promise((resolve6, reject) => {
35894
- server.close((err) => err ? reject(err) : resolve6());
36061
+ stop: () => new Promise((resolve8, reject) => {
36062
+ server.close((err) => err ? reject(err) : resolve8());
35895
36063
  }),
35896
36064
  publish,
35897
36065
  publishActivity: (entry) => publish({ type: "activity", data: entry }),
@@ -36564,7 +36732,7 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
36564
36732
  clientManager.configure(enabledServers).catch((err) => {
36565
36733
  console.error(`[Sanctuary] Failed to configure upstream servers: ${err instanceof Error ? err.message : "unknown error"}`);
36566
36734
  });
36567
- await new Promise((resolve6) => setTimeout(resolve6, 2e3));
36735
+ await new Promise((resolve8) => setTimeout(resolve8, 2e3));
36568
36736
  const proxiedTools = proxyRouter.getProxiedTools();
36569
36737
  if (proxiedTools.length > 0) {
36570
36738
  allTools.push(...proxiedTools);
@@ -37403,8 +37571,8 @@ async function runWrap(options, deps = {}) {
37403
37571
  passphraseValue = process.env.SANCTUARY_PASSPHRASE;
37404
37572
  } else {
37405
37573
  try {
37406
- const resolve6 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
37407
- const resolved = await resolve6();
37574
+ const resolve8 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
37575
+ const resolved = await resolve8();
37408
37576
  passphraseLocation = resolved.location;
37409
37577
  passphraseSource = resolved.source;
37410
37578
  passphraseValue = resolved.value;
@@ -37759,12 +37927,12 @@ async function defaultOpenBrowser(url) {
37759
37927
  cmd = "xdg-open";
37760
37928
  args = [url];
37761
37929
  }
37762
- await new Promise((resolve6) => {
37930
+ await new Promise((resolve8) => {
37763
37931
  const child = spawn(cmd, args, { stdio: "ignore", detached: true });
37764
- child.on("error", () => resolve6());
37932
+ child.on("error", () => resolve8());
37765
37933
  child.on("spawn", () => {
37766
37934
  child.unref();
37767
- resolve6();
37935
+ resolve8();
37768
37936
  });
37769
37937
  });
37770
37938
  }
@@ -38733,32 +38901,32 @@ endstream`;
38733
38901
  const offsets = new Array(totalObjects + 1).fill(0);
38734
38902
  const chunks = [];
38735
38903
  let bytePos = 0;
38736
- const write2 = (s) => {
38904
+ const write3 = (s) => {
38737
38905
  const buf = Buffer.from(s, "latin1");
38738
38906
  chunks.push(buf);
38739
38907
  bytePos += buf.length;
38740
38908
  };
38741
- write2("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
38909
+ write3("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
38742
38910
  for (let i = 1; i <= totalObjects; i++) {
38743
38911
  offsets[i] = bytePos;
38744
- write2(`${i} 0 obj
38912
+ write3(`${i} 0 obj
38745
38913
  ${objectBodies[i]}
38746
38914
  endobj
38747
38915
  `);
38748
38916
  }
38749
38917
  const xrefPos = bytePos;
38750
- write2(`xref
38918
+ write3(`xref
38751
38919
  0 ${totalObjects + 1}
38752
38920
  `);
38753
- write2("0000000000 65535 f \n");
38921
+ write3("0000000000 65535 f \n");
38754
38922
  for (let i = 1; i <= totalObjects; i++) {
38755
- write2(`${offsets[i].toString().padStart(10, "0")} 00000 n
38923
+ write3(`${offsets[i].toString().padStart(10, "0")} 00000 n
38756
38924
  `);
38757
38925
  }
38758
- write2(`trailer
38926
+ write3(`trailer
38759
38927
  << /Size ${totalObjects + 1} /Root 1 0 R >>
38760
38928
  `);
38761
- write2(`startxref
38929
+ write3(`startxref
38762
38930
  ${xrefPos}
38763
38931
  %%EOF
38764
38932
  `);
@@ -39093,7 +39261,7 @@ var init_backend_interface = __esm({
39093
39261
  }
39094
39262
  });
39095
39263
  async function runSecurity(args, input) {
39096
- return new Promise((resolve6, reject) => {
39264
+ return new Promise((resolve8, reject) => {
39097
39265
  const child = spawn(SECURITY_BIN, args, { stdio: ["pipe", "pipe", "pipe"] });
39098
39266
  let stdout = "";
39099
39267
  let stderr = "";
@@ -39115,7 +39283,7 @@ async function runSecurity(args, input) {
39115
39283
  reject(err);
39116
39284
  });
39117
39285
  child.on("close", (code) => {
39118
- resolve6({ stdout, stderr, code: code ?? -1 });
39286
+ resolve8({ stdout, stderr, code: code ?? -1 });
39119
39287
  });
39120
39288
  if (input !== void 0) {
39121
39289
  child.stdin.write(input);
@@ -40191,7 +40359,7 @@ async function readValue(stdin, prompt2) {
40191
40359
  return await readFirstLine(stdin);
40192
40360
  }
40193
40361
  async function readFirstLine(stdin) {
40194
- return new Promise((resolve6, reject) => {
40362
+ return new Promise((resolve8, reject) => {
40195
40363
  const rl = createInterface$1({ input: stdin });
40196
40364
  let resolved = false;
40197
40365
  const finish = (value) => {
@@ -40202,7 +40370,7 @@ async function readFirstLine(stdin) {
40202
40370
  rl.close();
40203
40371
  } catch {
40204
40372
  }
40205
- resolve6(value);
40373
+ resolve8(value);
40206
40374
  };
40207
40375
  const deadline = setTimeout(() => {
40208
40376
  finish("");
@@ -40221,7 +40389,7 @@ async function promptSilently(stdin, prompt2) {
40221
40389
  process.stderr.write(`${prompt2}: `);
40222
40390
  stdin.setRawMode?.(true);
40223
40391
  stdin.resume();
40224
- return await new Promise((resolve6) => {
40392
+ return await new Promise((resolve8) => {
40225
40393
  let buf = "";
40226
40394
  const onData = (chunk) => {
40227
40395
  const s = chunk.toString("utf8");
@@ -40231,7 +40399,7 @@ async function promptSilently(stdin, prompt2) {
40231
40399
  stdin.pause();
40232
40400
  stdin.off("data", onData);
40233
40401
  process.stderr.write("\n");
40234
- resolve6(buf);
40402
+ resolve8(buf);
40235
40403
  return;
40236
40404
  }
40237
40405
  if (ch === "") {
@@ -40498,13 +40666,179 @@ var init_cli4 = __esm({
40498
40666
  init_discovery();
40499
40667
  }
40500
40668
  });
40669
+
40670
+ // src/cli/identity.ts
40671
+ var identity_exports2 = {};
40672
+ __export(identity_exports2, {
40673
+ runIdentityCommand: () => runIdentityCommand
40674
+ });
40675
+ function write2(stream, text) {
40676
+ stream.write(text);
40677
+ }
40678
+ function flagValue2(argv, name) {
40679
+ const index = argv.indexOf(name);
40680
+ if (index === -1) return void 0;
40681
+ return argv[index + 1];
40682
+ }
40683
+ function hasFlag2(argv, name) {
40684
+ return argv.includes(name);
40685
+ }
40686
+ function printUsage3(out) {
40687
+ write2(
40688
+ out,
40689
+ `Usage: sanctuary identity <command> [options]
40690
+
40691
+ Commands:
40692
+ show Print the active identity (DID, identity_id, public key).
40693
+
40694
+ Options:
40695
+ --fortress <path> Override the storage path.
40696
+ --passphrase <val> Passphrase for master-key derivation.
40697
+ --json Output as JSON.
40698
+ --help, -h Show this help.
40699
+
40700
+ Environment variables:
40701
+ SANCTUARY_PASSPHRASE Key derivation passphrase.
40702
+ SANCTUARY_STORAGE_PATH State directory (default: ~/.sanctuary).
40703
+ SANCTUARY_FORTRESS_PATH Operator-friendly alias for STORAGE_PATH.
40704
+ SANCTUARY_RECOVERY_KEY Recovery key (alternative to passphrase).
40705
+
40706
+ Identity data is encrypted at rest. A passphrase or recovery key is
40707
+ required to decrypt and display identity information.
40708
+ `
40709
+ );
40710
+ }
40711
+ async function runIdentityCommand(args) {
40712
+ const argv = args.argv;
40713
+ const out = args.out ?? process.stdout;
40714
+ const err = args.err ?? process.stderr;
40715
+ const env = args.env ?? process.env;
40716
+ if (argv.length === 0 || hasFlag2(argv, "--help") || hasFlag2(argv, "-h")) {
40717
+ printUsage3(out);
40718
+ return 0;
40719
+ }
40720
+ const command = argv[0];
40721
+ if (command === "show") {
40722
+ return await cmdShow(argv.slice(1), out, err, env);
40723
+ }
40724
+ write2(err, `Unknown identity command: ${command}
40725
+ `);
40726
+ write2(err, `Run "sanctuary identity --help" for usage.
40727
+ `);
40728
+ return 2;
40729
+ }
40730
+ async function cmdShow(argv, out, err, env) {
40731
+ const json = hasFlag2(argv, "--json");
40732
+ const fortressFlag = flagValue2(argv, "--fortress");
40733
+ if (fortressFlag) {
40734
+ process.env.SANCTUARY_STORAGE_PATH = fortressFlag;
40735
+ }
40736
+ const passphrase = flagValue2(argv, "--passphrase") ?? env.SANCTUARY_PASSPHRASE;
40737
+ const recoveryKey = env.SANCTUARY_RECOVERY_KEY;
40738
+ if (!passphrase && !recoveryKey) {
40739
+ write2(
40740
+ err,
40741
+ "Error: sanctuary identity show requires SANCTUARY_PASSPHRASE, --passphrase, or SANCTUARY_RECOVERY_KEY.\n"
40742
+ );
40743
+ return 1;
40744
+ }
40745
+ try {
40746
+ const config = await loadConfig();
40747
+ await mkdir(config.storage_path, { recursive: true, mode: 448 });
40748
+ const stateStoragePath = join(config.storage_path, "state");
40749
+ const storage = new FilesystemStorage(stateStoragePath);
40750
+ let masterKey;
40751
+ if (passphrase) {
40752
+ let existingParams;
40753
+ const raw = await storage.read("_meta", "key-params");
40754
+ if (raw)
40755
+ existingParams = JSON.parse(bytesToString(raw));
40756
+ const derived = await deriveMasterKey(passphrase, existingParams);
40757
+ masterKey = derived.key;
40758
+ } else {
40759
+ masterKey = fromBase64url(recoveryKey);
40760
+ if (masterKey.length !== 32) {
40761
+ write2(err, "Error: SANCTUARY_RECOVERY_KEY must decode to 32 bytes.\n");
40762
+ return 1;
40763
+ }
40764
+ }
40765
+ const identityManager = new IdentityManager(storage, masterKey);
40766
+ const loadResult = await identityManager.load();
40767
+ if (loadResult.loaded === 0) {
40768
+ write2(
40769
+ err,
40770
+ loadResult.total > 0 ? "Error: identity files found but none could be decrypted. Wrong passphrase?\n" : "No identities found in this fortress.\n"
40771
+ );
40772
+ return 1;
40773
+ }
40774
+ const primary = identityManager.getDefault();
40775
+ if (!primary) {
40776
+ write2(err, "No primary identity set.\n");
40777
+ return 1;
40778
+ }
40779
+ if (json) {
40780
+ write2(
40781
+ out,
40782
+ JSON.stringify(
40783
+ {
40784
+ identity_id: primary.identity_id,
40785
+ did: primary.did,
40786
+ public_key: primary.public_key,
40787
+ label: primary.label,
40788
+ key_type: primary.key_type,
40789
+ created_at: primary.created_at,
40790
+ storage_path: config.storage_path,
40791
+ total_identities: loadResult.loaded
40792
+ },
40793
+ null,
40794
+ 2
40795
+ ) + "\n"
40796
+ );
40797
+ } else {
40798
+ write2(out, `identity_id: ${primary.identity_id}
40799
+ `);
40800
+ write2(out, `did: ${primary.did}
40801
+ `);
40802
+ write2(out, `public_key: ${primary.public_key}
40803
+ `);
40804
+ write2(out, `label: ${primary.label}
40805
+ `);
40806
+ write2(out, `key_type: ${primary.key_type}
40807
+ `);
40808
+ write2(out, `created_at: ${primary.created_at}
40809
+ `);
40810
+ write2(out, `storage_path: ${config.storage_path}
40811
+ `);
40812
+ write2(out, `total_identities: ${loadResult.loaded}
40813
+ `);
40814
+ }
40815
+ return 0;
40816
+ } catch (error) {
40817
+ write2(
40818
+ err,
40819
+ error instanceof Error ? `Error: ${error.message}
40820
+ ` : `Error: ${String(error)}
40821
+ `
40822
+ );
40823
+ return 1;
40824
+ }
40825
+ }
40826
+ var init_identity2 = __esm({
40827
+ "src/cli/identity.ts"() {
40828
+ init_filesystem();
40829
+ init_tools();
40830
+ init_key_derivation();
40831
+ init_encoding();
40832
+ init_config();
40833
+ }
40834
+ });
40501
40835
  async function probeTenantDashboard(tenant, options = {}) {
40502
40836
  const rt = tenant.runtime;
40503
40837
  if (!rt) {
40504
40838
  return { running: false, status: null, reason: "no runtime.json" };
40505
40839
  }
40506
40840
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
40507
- return await new Promise((resolve6) => {
40841
+ return await new Promise((resolve8) => {
40508
40842
  const req = get$1(
40509
40843
  {
40510
40844
  host: rt.dashboard_host,
@@ -40516,9 +40850,9 @@ async function probeTenantDashboard(tenant, options = {}) {
40516
40850
  res.resume();
40517
40851
  const status = res.statusCode ?? 0;
40518
40852
  if (status > 0 && status < 500) {
40519
- resolve6({ running: true, status, reason: null });
40853
+ resolve8({ running: true, status, reason: null });
40520
40854
  } else {
40521
- resolve6({
40855
+ resolve8({
40522
40856
  running: false,
40523
40857
  status,
40524
40858
  reason: `dashboard returned ${status}`
@@ -40528,10 +40862,10 @@ async function probeTenantDashboard(tenant, options = {}) {
40528
40862
  );
40529
40863
  req.on("timeout", () => {
40530
40864
  req.destroy();
40531
- resolve6({ running: false, status: null, reason: "timeout" });
40865
+ resolve8({ running: false, status: null, reason: "timeout" });
40532
40866
  });
40533
40867
  req.on("error", (err) => {
40534
- resolve6({
40868
+ resolve8({
40535
40869
  running: false,
40536
40870
  status: null,
40537
40871
  reason: err.code ?? err.message
@@ -40563,10 +40897,17 @@ function resolveCtx(args) {
40563
40897
  };
40564
40898
  }
40565
40899
  async function runAgentsCommand(args) {
40900
+ const fortressIdx = args.argv.indexOf("--fortress");
40901
+ if (fortressIdx !== -1 && args.argv[fortressIdx + 1]) {
40902
+ args = { ...args, root: args.argv[fortressIdx + 1] };
40903
+ const filtered = [...args.argv];
40904
+ filtered.splice(fortressIdx, 2);
40905
+ args = { ...args, argv: filtered };
40906
+ }
40566
40907
  const ctx = resolveCtx(args);
40567
40908
  const [sub, ...rest] = args.argv;
40568
40909
  if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
40569
- printUsage3(ctx.out);
40910
+ printUsage4(ctx.out);
40570
40911
  return 0;
40571
40912
  }
40572
40913
  try {
@@ -40574,13 +40915,13 @@ async function runAgentsCommand(args) {
40574
40915
  case "list":
40575
40916
  return await cmdList3(rest, ctx);
40576
40917
  case "show":
40577
- return await cmdShow(rest, ctx);
40918
+ return await cmdShow2(rest, ctx);
40578
40919
  case "status":
40579
40920
  return await cmdStatus(rest, ctx);
40580
40921
  default:
40581
40922
  ctx.err.write(`Unknown subcommand: ${sub}
40582
40923
  `);
40583
- printUsage3(ctx.err);
40924
+ printUsage4(ctx.err);
40584
40925
  return 2;
40585
40926
  }
40586
40927
  } catch (e) {
@@ -40590,13 +40931,17 @@ async function runAgentsCommand(args) {
40590
40931
  return 1;
40591
40932
  }
40592
40933
  }
40593
- function printUsage3(s) {
40934
+ function printUsage4(s) {
40594
40935
  s.write(`Usage: sanctuary agents <command> [flags]
40595
40936
 
40596
40937
  list [--json] List every tenant visible on this host.
40597
40938
  show <tenant> [--json] Show details for one tenant.
40598
40939
  status [--json] One-line-per-tenant running/stopped summary.
40599
40940
 
40941
+ Options:
40942
+ --fortress <path> Scope discovery to a specific storage path
40943
+ instead of scanning ~/.sanctuary.
40944
+
40600
40945
  Tenants are discovered by scanning ~/.sanctuary and any storage paths in
40601
40946
  SANCTUARY_AGENTS_EXTRA_PATHS or ~/.sanctuary/agents-extra.json. Tenant
40602
40947
  creation is done via \`sanctuary wrap\` with SANCTUARY_STORAGE_PATH set.
@@ -40680,7 +41025,7 @@ async function cmdList3(argv, ctx) {
40680
41025
  }
40681
41026
  return 0;
40682
41027
  }
40683
- async function cmdShow(argv, ctx) {
41028
+ async function cmdShow2(argv, ctx) {
40684
41029
  const positional = argv.find((a) => !a.startsWith("--"));
40685
41030
  if (!positional) {
40686
41031
  ctx.err.write("Missing tenant. Usage: sanctuary agents show <tenant>\n");
@@ -40831,10 +41176,10 @@ async function runResetPassphraseCommand(args) {
40831
41176
  const plat = args.platformOverride ?? process.platform;
40832
41177
  const parsed = parseArgs2(args.argv);
40833
41178
  if (parsed.help) {
40834
- printUsage4(out);
41179
+ printUsage5(out);
40835
41180
  return 0;
40836
41181
  }
40837
- const storagePath = parsed.storage ?? args.storagePath ?? resolveStoragePath(process.env, home);
41182
+ const storagePath = parsed.storage ?? parsed.fortress ?? args.storagePath ?? resolveStoragePath(process.env, home);
40838
41183
  out.write(banner(storagePath));
40839
41184
  const runtimeFile = join(storagePath, "runtime.json");
40840
41185
  if (await fileExists4(runtimeFile)) {
@@ -40891,13 +41236,15 @@ function parseArgs2(argv) {
40891
41236
  out.mode = v;
40892
41237
  } else if (a === "--storage" && argv[i + 1]) {
40893
41238
  out.storage = argv[++i];
41239
+ } else if (a === "--fortress" && argv[i + 1]) {
41240
+ out.fortress = argv[++i];
40894
41241
  } else if (a && a.startsWith("--")) {
40895
41242
  throw new Error(`Unknown flag: ${a}`);
40896
41243
  }
40897
41244
  }
40898
41245
  return out;
40899
41246
  }
40900
- function printUsage4(out) {
41247
+ function printUsage5(out) {
40901
41248
  out.write(`
40902
41249
  Usage: sanctuary reset-passphrase [options]
40903
41250
 
@@ -40920,9 +41267,9 @@ Recover a fortress whose passphrase has been lost or corrupted. Three modes:
40920
41267
 
40921
41268
  Options:
40922
41269
  --mode <shares|guardian|nuke> Pick a path non-interactively.
40923
- --storage <path> Override the resolved storage path.
40924
- Defaults to SANCTUARY_STORAGE_PATH or
40925
- ~/.sanctuary.
41270
+ --fortress <path> Override the fortress storage path.
41271
+ Consistent with "sanctuary wrap --fortress".
41272
+ --storage <path> Alias for --fortress.
40926
41273
  --help, -h Show this help.
40927
41274
 
40928
41275
  Without --mode, the command surveys which paths are operationally available
@@ -41193,7 +41540,7 @@ async function prompt(lines, err, question) {
41193
41540
  return await lines.next();
41194
41541
  }
41195
41542
  async function defaultExec2(cmd, args) {
41196
- return await new Promise((resolve6, reject) => {
41543
+ return await new Promise((resolve8, reject) => {
41197
41544
  const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
41198
41545
  let stdout = "";
41199
41546
  let stderr = "";
@@ -41204,7 +41551,7 @@ async function defaultExec2(cmd, args) {
41204
41551
  stderr += d.toString();
41205
41552
  });
41206
41553
  child.on("error", reject);
41207
- child.on("close", (code) => resolve6({ stdout, stderr, code }));
41554
+ child.on("close", (code) => resolve8({ stdout, stderr, code }));
41208
41555
  });
41209
41556
  }
41210
41557
  var LineReader;
@@ -41240,8 +41587,8 @@ var init_reset_passphrase = __esm({
41240
41587
  return Promise.resolve(this.queue.shift());
41241
41588
  }
41242
41589
  if (this.closed) return Promise.resolve("");
41243
- return new Promise((resolve6) => {
41244
- this.waiters.push(resolve6);
41590
+ return new Promise((resolve8) => {
41591
+ this.waiters.push(resolve8);
41245
41592
  });
41246
41593
  }
41247
41594
  close() {
@@ -41640,11 +41987,11 @@ async function startMultiDashboardServer(options = {}) {
41640
41987
  }
41641
41988
  }
41642
41989
  });
41643
- await new Promise((resolve6, reject) => {
41990
+ await new Promise((resolve8, reject) => {
41644
41991
  server.once("error", reject);
41645
41992
  server.listen(port, host, () => {
41646
41993
  server.off("error", reject);
41647
- resolve6();
41994
+ resolve8();
41648
41995
  });
41649
41996
  });
41650
41997
  const addr = server.address();
@@ -41653,8 +42000,8 @@ async function startMultiDashboardServer(options = {}) {
41653
42000
  url: `http://${host}:${actualPort}`,
41654
42001
  port: actualPort,
41655
42002
  host,
41656
- stop: () => new Promise((resolve6, reject) => {
41657
- server.close((err) => err ? reject(err) : resolve6());
42003
+ stop: () => new Promise((resolve8, reject) => {
42004
+ server.close((err) => err ? reject(err) : resolve8());
41658
42005
  })
41659
42006
  };
41660
42007
  }
@@ -42054,7 +42401,7 @@ function formatUpdateMessage(current, latest) {
42054
42401
  return `[Sanctuary] Update available: ${current} \u2192 ${latest}. Run: npx @sanctuary-framework/mcp-server@latest`;
42055
42402
  }
42056
42403
  function fetchLatestVersion(currentVersion) {
42057
- return new Promise((resolve6) => {
42404
+ return new Promise((resolve8) => {
42058
42405
  const req = get(
42059
42406
  REGISTRY_URL,
42060
42407
  {
@@ -42064,7 +42411,7 @@ function fetchLatestVersion(currentVersion) {
42064
42411
  (res) => {
42065
42412
  if (res.statusCode !== 200) {
42066
42413
  res.resume();
42067
- resolve6(null);
42414
+ resolve8(null);
42068
42415
  return;
42069
42416
  }
42070
42417
  let data = "";
@@ -42073,7 +42420,7 @@ function fetchLatestVersion(currentVersion) {
42073
42420
  data += chunk;
42074
42421
  if (data.length > 32768) {
42075
42422
  res.destroy();
42076
- resolve6(null);
42423
+ resolve8(null);
42077
42424
  }
42078
42425
  });
42079
42426
  res.on("end", () => {
@@ -42081,20 +42428,20 @@ function fetchLatestVersion(currentVersion) {
42081
42428
  const json = JSON.parse(data);
42082
42429
  const latest = json.version;
42083
42430
  if (typeof latest === "string" && isNewerVersion(currentVersion, latest)) {
42084
- resolve6(latest);
42431
+ resolve8(latest);
42085
42432
  } else {
42086
- resolve6(null);
42433
+ resolve8(null);
42087
42434
  }
42088
42435
  } catch {
42089
- resolve6(null);
42436
+ resolve8(null);
42090
42437
  }
42091
42438
  });
42092
42439
  }
42093
42440
  );
42094
- req.on("error", () => resolve6(null));
42441
+ req.on("error", () => resolve8(null));
42095
42442
  req.on("timeout", () => {
42096
42443
  req.destroy();
42097
- resolve6(null);
42444
+ resolve8(null);
42098
42445
  });
42099
42446
  });
42100
42447
  }
@@ -42172,6 +42519,11 @@ async function main() {
42172
42519
  const code = await runTemplateCommand2({ argv: args.slice(1) });
42173
42520
  process.exit(code);
42174
42521
  }
42522
+ if (args[0] === "identity") {
42523
+ const { runIdentityCommand: runIdentityCommand2 } = await Promise.resolve().then(() => (init_identity2(), identity_exports2));
42524
+ const code = await runIdentityCommand2({ argv: args.slice(1) });
42525
+ process.exit(code);
42526
+ }
42175
42527
  if (args[0] === "agents") {
42176
42528
  const { runAgentsCommand: runAgentsCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
42177
42529
  const code = await runAgentsCommand2({ argv: args.slice(1) });
@@ -42393,6 +42745,9 @@ Subcommands:
42393
42745
  Use "sanctuary dashboard --help" for options.
42394
42746
  Pass --multi to render the multi-tenant overview.
42395
42747
 
42748
+ identity Inspect the active identity (DID, public key).
42749
+ Use "sanctuary identity --help" for options.
42750
+
42396
42751
  template Manage policy templates (list, init).
42397
42752
  Use "sanctuary template --help" for options.
42398
42753