@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.cjs CHANGED
@@ -4428,13 +4428,23 @@ function parseScalar(value) {
4428
4428
  return value.replace(/^["']|["']$/g, "");
4429
4429
  }
4430
4430
  function validatePolicy(raw) {
4431
+ if (!("tier1_always_approve" in raw)) {
4432
+ throw new Error(
4433
+ "Policy file must include 'tier1_always_approve' as an explicit list (use [] for empty). Remove specific entries instead of removing the whole key."
4434
+ );
4435
+ }
4436
+ if (!("approval_channel" in raw)) {
4437
+ throw new Error(
4438
+ "Policy file must include 'approval_channel' as an explicit object (use {} for defaults). Remove specific entries instead of removing the whole key."
4439
+ );
4440
+ }
4431
4441
  const userTier3 = raw.tier3_always_allow ?? [];
4432
4442
  const mergedTier3 = [
4433
4443
  .../* @__PURE__ */ new Set([...userTier3, ...DEFAULT_POLICY.tier3_always_allow])
4434
4444
  ];
4435
4445
  return {
4436
4446
  version: raw.version ?? 1,
4437
- tier1_always_approve: raw.tier1_always_approve ?? DEFAULT_POLICY.tier1_always_approve,
4447
+ tier1_always_approve: raw.tier1_always_approve,
4438
4448
  tier2_anomaly: {
4439
4449
  ...DEFAULT_TIER2,
4440
4450
  ...raw.tier2_anomaly ?? {}
@@ -4455,6 +4465,11 @@ function generateDefaultPolicyYaml() {
4455
4465
  # This file controls what your agent can do without asking.
4456
4466
  # Edit this file directly. Your agent cannot modify it.
4457
4467
  # Changes take effect on server restart.
4468
+ #
4469
+ # Required keys (must be present; use [] or {} for empty):
4470
+ # tier1_always_approve, approval_channel
4471
+ # Optional keys (omit to use defaults; new defaults merge automatically):
4472
+ # tier2_anomaly, tier3_always_allow
4458
4473
 
4459
4474
  version: 1
4460
4475
 
@@ -11574,6 +11589,7 @@ __export(passphrase_exports, {
11574
11589
  getOrCreatePassphrase: () => getOrCreatePassphrase,
11575
11590
  isOsKeyringLocation: () => isOsKeyringLocation,
11576
11591
  keychainServiceFor: () => keychainServiceFor,
11592
+ legacyKeychainServiceFor: () => legacyKeychainServiceFor,
11577
11593
  persistUserProvidedPassphrase: () => persistUserProvidedPassphrase,
11578
11594
  readStoredPassphrase: () => readStoredPassphrase
11579
11595
  });
@@ -11587,16 +11603,29 @@ async function getOrCreatePassphrase(opts = {}) {
11587
11603
  const plat = opts.platformOverride ?? os.platform();
11588
11604
  const exec2 = opts.exec ?? defaultExec;
11589
11605
  const derive = opts.deriveMachineKey ?? deriveMachineKey;
11606
+ const legacyService = legacyKeychainServiceFor(storagePath, home);
11590
11607
  if (plat === "darwin") {
11591
11608
  const fromKc = await readFromKeychain(exec2, service);
11592
11609
  if (fromKc) {
11593
11610
  return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11594
11611
  }
11612
+ if (legacyService !== service) {
11613
+ const fromLegacy = await readFromKeychain(exec2, legacyService);
11614
+ if (fromLegacy) {
11615
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11616
+ }
11617
+ }
11595
11618
  } else if (plat === "linux") {
11596
11619
  const fromSs = await readFromSecretService(exec2, service);
11597
11620
  if (fromSs) {
11598
11621
  return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11599
11622
  }
11623
+ if (legacyService !== service) {
11624
+ const fromLegacy = await readFromSecretService(exec2, legacyService);
11625
+ if (fromLegacy) {
11626
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11627
+ }
11628
+ }
11600
11629
  }
11601
11630
  const fallback = fallbackFilePath(home, storagePath);
11602
11631
  const fromFile = await readFromFallbackFile(fallback, home, derive);
@@ -11629,6 +11658,7 @@ async function readStoredPassphrase(opts = {}) {
11629
11658
  const home = opts.home ?? os.homedir();
11630
11659
  const storagePath = opts.storagePath ?? resolveStoragePath(process.env, home);
11631
11660
  const service = keychainServiceFor(storagePath, home);
11661
+ const legacyService = legacyKeychainServiceFor(storagePath, home);
11632
11662
  const plat = opts.platformOverride ?? os.platform();
11633
11663
  const exec2 = opts.exec ?? defaultExec;
11634
11664
  const derive = opts.deriveMachineKey ?? deriveMachineKey;
@@ -11637,11 +11667,23 @@ async function readStoredPassphrase(opts = {}) {
11637
11667
  if (fromKc) {
11638
11668
  return { value: fromKc, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11639
11669
  }
11670
+ if (legacyService !== service) {
11671
+ const fromLegacy = await readFromKeychain(exec2, legacyService);
11672
+ if (fromLegacy) {
11673
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_MACOS };
11674
+ }
11675
+ }
11640
11676
  } else if (plat === "linux") {
11641
11677
  const fromSs = await readFromSecretService(exec2, service);
11642
11678
  if (fromSs) {
11643
11679
  return { value: fromSs, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11644
11680
  }
11681
+ if (legacyService !== service) {
11682
+ const fromLegacy = await readFromSecretService(exec2, legacyService);
11683
+ if (fromLegacy) {
11684
+ return { value: fromLegacy, source: "keychain", location: OS_KEYRING_LOCATION_LINUX };
11685
+ }
11686
+ }
11645
11687
  }
11646
11688
  const fallback = fallbackFilePath(home, storagePath);
11647
11689
  const fromFile = await readFromFallbackFile(fallback, home, derive);
@@ -11722,9 +11764,18 @@ async function writeToKeychain(value, exec2, service = KEYCHAIN_SERVICE_DEFAULT)
11722
11764
  }
11723
11765
  }
11724
11766
  function keychainServiceFor(storagePath, home = os.homedir()) {
11725
- const defaultPath = path.join(home, DEFAULT_STORAGE_DIR);
11726
- if (storagePath === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11727
- const digest = sha256.sha256(Buffer.from(storagePath, "utf-8"));
11767
+ const defaultPath = path.resolve(path.join(home, DEFAULT_STORAGE_DIR));
11768
+ const canonicalStorage = path.resolve(storagePath);
11769
+ if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11770
+ const digest = sha256.sha256(Buffer.from(canonicalStorage, "utf-8"));
11771
+ const suffix = Buffer.from(digest).toString("hex").slice(0, 16);
11772
+ return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
11773
+ }
11774
+ function legacyKeychainServiceFor(storagePath, home = os.homedir()) {
11775
+ const defaultPath = path.resolve(path.join(home, DEFAULT_STORAGE_DIR));
11776
+ const canonicalStorage = path.resolve(storagePath);
11777
+ if (canonicalStorage === defaultPath) return KEYCHAIN_SERVICE_DEFAULT;
11778
+ const digest = sha256.sha256(Buffer.from(canonicalStorage, "utf-8"));
11728
11779
  const suffix = Buffer.from(digest).toString("hex").slice(0, 12);
11729
11780
  return `${KEYCHAIN_SERVICE_DEFAULT}-${suffix}`;
11730
11781
  }
@@ -11811,7 +11862,7 @@ function deriveMachineKey(home) {
11811
11862
  return hkdf.hkdf(sha256.sha256, material, void 0, "sanctuary-passphrase-v1", 32);
11812
11863
  }
11813
11864
  async function defaultExec(cmd, args, input) {
11814
- return new Promise((resolve6, reject) => {
11865
+ return new Promise((resolve8, reject) => {
11815
11866
  const child = child_process.spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
11816
11867
  let stdout = "";
11817
11868
  let stderr = "";
@@ -11822,7 +11873,7 @@ async function defaultExec(cmd, args, input) {
11822
11873
  stderr += d.toString();
11823
11874
  });
11824
11875
  child.on("error", reject);
11825
- child.on("close", (code) => resolve6({ stdout, stderr, code }));
11876
+ child.on("close", (code) => resolve8({ stdout, stderr, code }));
11826
11877
  if (input !== void 0) {
11827
11878
  child.stdin.write(input);
11828
11879
  }
@@ -12016,7 +12067,7 @@ async function discoverTenants(options = {}) {
12016
12067
  for (const child of children) {
12017
12068
  const childPath = path.join(root, child);
12018
12069
  if (child.startsWith(".")) continue;
12019
- if (child === "state" || child === "backup" || child === "config") continue;
12070
+ if (child === "state" || child === "backup" || child === "config" || child === "default") continue;
12020
12071
  const s = await promises.stat(childPath).catch(() => null);
12021
12072
  if (!s || !s.isDirectory()) continue;
12022
12073
  const desc = await describeTenant(child, childPath, home);
@@ -12028,6 +12079,17 @@ async function discoverTenants(options = {}) {
12028
12079
  const desc = await describeTenant(path.basename(extra), extra, home);
12029
12080
  if (desc) tenants.push(desc);
12030
12081
  }
12082
+ const seen = /* @__PURE__ */ new Map();
12083
+ for (const t of tenants) {
12084
+ seen.set(t.name, (seen.get(t.name) ?? 0) + 1);
12085
+ }
12086
+ for (const [name, count] of seen) {
12087
+ if (count > 1) {
12088
+ console.error(
12089
+ `[sanctuary] warning: ${count} tenants share the name "${name}". Use --tenant with a unique name or storage path to disambiguate.`
12090
+ );
12091
+ }
12092
+ }
12031
12093
  tenants.sort((a, b) => {
12032
12094
  if (a.name === "default") return -1;
12033
12095
  if (b.name === "default") return 1;
@@ -16826,6 +16888,8 @@ var init_intelligence_api_router = __esm({
16826
16888
  this.code = code;
16827
16889
  this.name = "IntelligenceRouterError";
16828
16890
  }
16891
+ statusCode;
16892
+ code;
16829
16893
  };
16830
16894
  }
16831
16895
  });
@@ -17102,7 +17166,7 @@ var init_dashboard = __esm({
17102
17166
  server = http.createServer(handler);
17103
17167
  }
17104
17168
  this.httpServer = server;
17105
- return new Promise((resolve6, reject) => {
17169
+ return new Promise((resolve8, reject) => {
17106
17170
  const protocol = this.useTLS ? "https" : "http";
17107
17171
  const baseUrl = `${protocol}://${this.config.host}:${this.config.port}`;
17108
17172
  server.listen(this.config.port, this.config.host, () => {
@@ -17127,7 +17191,7 @@ var init_dashboard = __esm({
17127
17191
  if (shouldAutoOpen) {
17128
17192
  this.openInBrowser(sessionUrl);
17129
17193
  }
17130
- resolve6();
17194
+ resolve8();
17131
17195
  });
17132
17196
  server.on("error", (err) => {
17133
17197
  if (err.code === "EADDRINUSE") {
@@ -17173,8 +17237,8 @@ var init_dashboard = __esm({
17173
17237
  }
17174
17238
  this.rateLimits.clear();
17175
17239
  if (this.httpServer) {
17176
- return new Promise((resolve6) => {
17177
- this.httpServer.close(() => resolve6());
17240
+ return new Promise((resolve8) => {
17241
+ this.httpServer.close(() => resolve8());
17178
17242
  });
17179
17243
  }
17180
17244
  }
@@ -17188,7 +17252,7 @@ var init_dashboard = __esm({
17188
17252
  `[Sanctuary] Approval required: ${request.operation} (Tier ${request.tier}) \u2014 open dashboard to respond
17189
17253
  `
17190
17254
  );
17191
- return new Promise((resolve6) => {
17255
+ return new Promise((resolve8) => {
17192
17256
  const timer = setTimeout(() => {
17193
17257
  this.pending.delete(id);
17194
17258
  const response = {
@@ -17202,12 +17266,12 @@ var init_dashboard = __esm({
17202
17266
  decision: response.decision,
17203
17267
  decided_by: "timeout"
17204
17268
  });
17205
- resolve6(response);
17269
+ resolve8(response);
17206
17270
  }, this.config.timeout_seconds * 1e3);
17207
17271
  const pending = {
17208
17272
  id,
17209
17273
  request,
17210
- resolve: resolve6,
17274
+ resolve: resolve8,
17211
17275
  timer,
17212
17276
  created_at: (/* @__PURE__ */ new Date()).toISOString()
17213
17277
  };
@@ -18073,7 +18137,7 @@ var init_webhook = __esm({
18073
18137
  * Start the callback listener server.
18074
18138
  */
18075
18139
  async start() {
18076
- return new Promise((resolve6, reject) => {
18140
+ return new Promise((resolve8, reject) => {
18077
18141
  this.callbackServer = http.createServer(
18078
18142
  (req, res) => this.handleCallback(req, res)
18079
18143
  );
@@ -18088,7 +18152,7 @@ var init_webhook = __esm({
18088
18152
 
18089
18153
  `
18090
18154
  );
18091
- resolve6();
18155
+ resolve8();
18092
18156
  }
18093
18157
  );
18094
18158
  this.callbackServer.on("error", reject);
@@ -18108,8 +18172,8 @@ var init_webhook = __esm({
18108
18172
  }
18109
18173
  this.pending.clear();
18110
18174
  if (this.callbackServer) {
18111
- return new Promise((resolve6) => {
18112
- this.callbackServer.close(() => resolve6());
18175
+ return new Promise((resolve8) => {
18176
+ this.callbackServer.close(() => resolve8());
18113
18177
  });
18114
18178
  }
18115
18179
  }
@@ -18122,7 +18186,7 @@ var init_webhook = __esm({
18122
18186
  `[Sanctuary] Webhook approval sent: ${request.operation} (Tier ${request.tier}) \u2014 awaiting callback
18123
18187
  `
18124
18188
  );
18125
- return new Promise((resolve6) => {
18189
+ return new Promise((resolve8) => {
18126
18190
  const timer = setTimeout(() => {
18127
18191
  this.pending.delete(id);
18128
18192
  const response = {
@@ -18131,12 +18195,12 @@ var init_webhook = __esm({
18131
18195
  decided_at: (/* @__PURE__ */ new Date()).toISOString(),
18132
18196
  decided_by: "timeout"
18133
18197
  };
18134
- resolve6(response);
18198
+ resolve8(response);
18135
18199
  }, this.config.timeout_seconds * 1e3);
18136
18200
  const pending = {
18137
18201
  id,
18138
18202
  request,
18139
- resolve: resolve6,
18203
+ resolve: resolve8,
18140
18204
  timer,
18141
18205
  created_at: (/* @__PURE__ */ new Date()).toISOString()
18142
18206
  };
@@ -19429,12 +19493,25 @@ var init_injection_detector = __esm({
19429
19493
  }
19430
19494
  });
19431
19495
 
19496
+ // src/principal-policy/deny-vocabulary.ts
19497
+ var AGENT_VISIBLE_DENY_REASONS;
19498
+ var init_deny_vocabulary = __esm({
19499
+ "src/principal-policy/deny-vocabulary.ts"() {
19500
+ AGENT_VISIBLE_DENY_REASONS = {
19501
+ REQUIRES_APPROVAL: "operation requires operator approval",
19502
+ NOT_PERMITTED: "operation not permitted",
19503
+ REQUIRES_OPERATOR: "operation requires operator action"
19504
+ };
19505
+ }
19506
+ });
19507
+
19432
19508
  // src/principal-policy/gate.ts
19433
19509
  var ApprovalGate;
19434
19510
  var init_gate = __esm({
19435
19511
  "src/principal-policy/gate.ts"() {
19436
19512
  init_loader();
19437
19513
  init_injection_detector();
19514
+ init_deny_vocabulary();
19438
19515
  ApprovalGate = class {
19439
19516
  policy;
19440
19517
  baseline;
@@ -19486,10 +19563,16 @@ var init_gate = __esm({
19486
19563
  });
19487
19564
  }
19488
19565
  if (injectionResult.recommendation === "block") {
19566
+ this.auditLog.append("l2", `gate_injection_block:${operation}`, "system", {
19567
+ tier: 1,
19568
+ operation,
19569
+ injection_confidence: injectionResult.confidence,
19570
+ signal_count: injectionResult.signals.length
19571
+ });
19489
19572
  return {
19490
19573
  allowed: false,
19491
19574
  tier: 1,
19492
- reason: `Blocked: prompt injection detected in "${operation}" (confidence: ${(injectionResult.confidence * 100).toFixed(0)}%)`,
19575
+ reason: AGENT_VISIBLE_DENY_REASONS.NOT_PERMITTED,
19493
19576
  approval_required: false
19494
19577
  };
19495
19578
  }
@@ -19566,7 +19649,7 @@ var init_gate = __esm({
19566
19649
  this.auditLog.append("l2", `gate_unclassified:${operation}`, "system", {
19567
19650
  tier: 1,
19568
19651
  operation,
19569
- warning: "Operation is not classified in any policy tier \u2014 defaulting to Tier 1 (require approval)"
19652
+ warning: "Operation is not classified in any policy tier, defaulting to Tier 1 (require approval)"
19570
19653
  });
19571
19654
  return this.requestApproval(
19572
19655
  operation,
@@ -19695,7 +19778,7 @@ var init_gate = __esm({
19695
19778
  return {
19696
19779
  allowed: response.decision === "approve",
19697
19780
  tier,
19698
- reason: response.decision === "approve" ? `Approved by ${response.decided_by}` : `Tier ${tier} operation requires approval`,
19781
+ reason: response.decision === "approve" ? `Approved by ${response.decided_by}` : AGENT_VISIBLE_DENY_REASONS.REQUIRES_APPROVAL,
19699
19782
  approval_required: true,
19700
19783
  approval_response: response
19701
19784
  };
@@ -20284,7 +20367,11 @@ var init_tools5 = __esm({
20284
20367
 
20285
20368
  // src/handshake/protocol.ts
20286
20369
  function generateNonce() {
20287
- return toBase64url(randomBytes(32));
20370
+ const nonce = randomBytes(32);
20371
+ if (!nonce || nonce.length !== 32) {
20372
+ throw new Error("Nonce generation failed: randomBytes returned unexpected length");
20373
+ }
20374
+ return toBase64url(nonce);
20288
20375
  }
20289
20376
  function initiateHandshake(ourSHR) {
20290
20377
  const nonce = generateNonce();
@@ -20395,6 +20482,18 @@ function completeHandshake(response, session, identityManager, masterKey, identi
20395
20482
  return { completion, result };
20396
20483
  }
20397
20484
  function verifyCompletion(completion, session) {
20485
+ if (completion.protocol_version !== "1.0") {
20486
+ return {
20487
+ counterparty_id: "unknown",
20488
+ counterparty_shr: session.our_shr,
20489
+ verified: false,
20490
+ sovereignty_level: "unverified",
20491
+ trust_tier: "unverified",
20492
+ completed_at: completion.completed_at,
20493
+ expires_at: (/* @__PURE__ */ new Date()).toISOString(),
20494
+ errors: [`Unsupported protocol version: ${completion.protocol_version}`]
20495
+ };
20496
+ }
20398
20497
  const errors = [];
20399
20498
  if (!session.their_shr) {
20400
20499
  return {
@@ -22681,6 +22780,9 @@ Inspect the file and either correct the JSON or delete it manually before re-run
22681
22780
  this.cause = cause;
22682
22781
  this.name = "ResetHistoryMalformedError";
22683
22782
  }
22783
+ markerPath;
22784
+ lineNumber;
22785
+ cause;
22684
22786
  };
22685
22787
  }
22686
22788
  });
@@ -24839,7 +24941,7 @@ async function runOpenAIPrivacyFilter(text, config) {
24839
24941
  return parsed;
24840
24942
  }
24841
24943
  function runCommand(command, input, timeoutMs) {
24842
- return new Promise((resolve6, reject) => {
24944
+ return new Promise((resolve8, reject) => {
24843
24945
  const child = child_process.spawn(command, [], {
24844
24946
  stdio: ["pipe", "pipe", "pipe"],
24845
24947
  shell: false
@@ -24870,7 +24972,7 @@ function runCommand(command, input, timeoutMs) {
24870
24972
  ));
24871
24973
  return;
24872
24974
  }
24873
- resolve6(stdout);
24975
+ resolve8(stdout);
24874
24976
  });
24875
24977
  child.stdin.end(input);
24876
24978
  });
@@ -26746,13 +26848,13 @@ var init_proxy_router = __esm({
26746
26848
  * Call an upstream tool with a timeout.
26747
26849
  */
26748
26850
  async callWithTimeout(serverName, toolName, args, timeoutMs) {
26749
- return new Promise((resolve6, reject) => {
26851
+ return new Promise((resolve8, reject) => {
26750
26852
  const timer = setTimeout(() => {
26751
26853
  reject(new Error(`Upstream tool call timed out after ${timeoutMs}ms`));
26752
26854
  }, timeoutMs);
26753
26855
  this.clientManager.callTool(serverName, toolName, args).then((result) => {
26754
26856
  clearTimeout(timer);
26755
- resolve6(result);
26857
+ resolve8(result);
26756
26858
  }).catch((err) => {
26757
26859
  clearTimeout(timer);
26758
26860
  reject(err);
@@ -32088,7 +32190,7 @@ function makeEventId(prefix) {
32088
32190
  function hashOf(input) {
32089
32191
  return hashToString(sha256.sha256(stringToBytes(input)));
32090
32192
  }
32091
- var DEFAULT_CONCIERGE_MAX_TOKENS, OperatorChatService;
32193
+ var DEFAULT_CONCIERGE_MAX_TOKENS, SANCTUARY_DOMAIN_REFERENCE, OperatorChatService;
32092
32194
  var init_operator_chat_service = __esm({
32093
32195
  "src/chat/operator-chat-service.ts"() {
32094
32196
  init_hashing();
@@ -32096,6 +32198,33 @@ var init_operator_chat_service = __esm({
32096
32198
  init_operator_chat_audit_events();
32097
32199
  init_operator_chat_types();
32098
32200
  DEFAULT_CONCIERGE_MAX_TOKENS = 512;
32201
+ SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
32202
+ 1. Castle Wall: OS-boundary egress filter enforced at the kernel level. Blocks unauthorized outbound calls even from prompt-injected agents.
32203
+ 2. Sentinels: internal observation via process introspection. Surfaces anomalies to the operator; does not enforce.
32204
+ 3. Charter (Cooperative MCP): the sovereignty surface for compliant agents. Policy gates, approval tiers, audit logging, and encrypted state all live here.
32205
+ 4. Heralds: Concordia receipts and Verascore reputation. Cross-fortress accountability after an action completes.
32206
+
32207
+ Five channel templates (canonical names):
32208
+ - request-approve-act: agent proposes an action, operator approves or denies before execution.
32209
+ - read-then-report: agent reads outputs from a data source and reports summaries to the operator.
32210
+ - scheduled-digest: agent runs on a schedule and delivers a periodic digest.
32211
+ - plan-draft-only: agent drafts plans; operator reviews before any execution step.
32212
+ - fortress-relay: agent relays messages between fortresses under operator-scoped policy.
32213
+
32214
+ Four canonical policy slots:
32215
+ - memory: governs what the agent may persist and retrieve from encrypted state.
32216
+ - credentials: governs access to secrets, API keys, and tokens held in the broker.
32217
+ - plans: governs the agent's ability to create, modify, or execute plans.
32218
+ - outputs: governs what the agent may emit to external surfaces (files, APIs, messages).
32219
+
32220
+ Key concepts:
32221
+ - Fortress: the operator-owned sovereignty harness. All state is encrypted at rest under the cocoon.
32222
+ - Cocoon: master-key-wrapped storage derived from the operator's passphrase via Argon2id.
32223
+ - Identity: Ed25519 keypair with a DID, owned by the operator. Private keys never leave the cocoon.
32224
+ - Audit log: append-only encrypted blobs, sequential, recording every gate decision and tool call.
32225
+ - Wrapped agent: any agent runtime that connects to Sanctuary as an MCP client. Tier A (native), Tier B (adapter-wrapped), Tier C (escape hatch).
32226
+
32227
+ Note: this is a static reference block (v1.2.x). Dynamic context injection (live template list, policy schema) ships in v1.3.`;
32099
32228
  OperatorChatService = class {
32100
32229
  store;
32101
32230
  auditLog;
@@ -32240,6 +32369,9 @@ var init_operator_chat_service = __esm({
32240
32369
  * than nested structures. Format:
32241
32370
  *
32242
32371
  * ```
32372
+ * ## Sanctuary reference
32373
+ * <static domain reference block>
32374
+ *
32243
32375
  * ## Recent activity
32244
32376
  * <recentActivity output>
32245
32377
  *
@@ -32251,15 +32383,28 @@ var init_operator_chat_service = __esm({
32251
32383
  * ```
32252
32384
  */
32253
32385
  async assembleConciergeContext() {
32386
+ const ref = `## Sanctuary reference
32387
+ ${SANCTUARY_DOMAIN_REFERENCE}`;
32254
32388
  if (!this.contextProviders) {
32255
- return "## Recent activity\n(no providers wired)\n\n## Wrapped agents\n(no providers wired)\n\n## Open inbox\n(no providers wired)";
32389
+ return `${ref}
32390
+
32391
+ ## Recent activity
32392
+ (no providers wired)
32393
+
32394
+ ## Wrapped agents
32395
+ (no providers wired)
32396
+
32397
+ ## Open inbox
32398
+ (no providers wired)`;
32256
32399
  }
32257
32400
  const [activity, agents, inbox] = await Promise.all([
32258
32401
  this.contextProviders.recentActivity(),
32259
32402
  this.contextProviders.agentInventory(),
32260
32403
  this.contextProviders.openInbox()
32261
32404
  ]);
32262
- return `## Recent activity
32405
+ return `${ref}
32406
+
32407
+ ## Recent activity
32263
32408
  ${activity}
32264
32409
 
32265
32410
  ## Wrapped agents
@@ -35291,6 +35436,9 @@ async function importExitBundle(opts) {
35291
35436
  reputationArtifact?.json ?? null,
35292
35437
  manifest
35293
35438
  );
35439
+ if (!conflicts.public_identity_exists && identityArtifact?.json && opts.identityManager.getPrimaryIdentityId() !== null && opts.identityManager.getPrimaryIdentityId() !== identityArtifact.json.bundle.identity_id) {
35440
+ conflicts.public_identity_exists = true;
35441
+ }
35294
35442
  if (!opts.activate) {
35295
35443
  return {
35296
35444
  verified: true,
@@ -35317,7 +35465,7 @@ async function importExitBundle(opts) {
35317
35465
  if (conflicts.public_identity_exists && !opts.forceRebind) {
35318
35466
  throw new ExitBundleImportError(
35319
35467
  "IDENTITY_OVERWRITE_REFUSED",
35320
- "Importing this bundle would overwrite an existing fortress public identity. Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
35468
+ "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."
35321
35469
  );
35322
35470
  }
35323
35471
  if (conflicts.public_identity_exists && opts.forceRebind && identityArtifact) {
@@ -35707,6 +35855,26 @@ async function runExitCommand(args) {
35707
35855
  write(err, "Usage: sanctuary exit import <dir> [--activate]\n");
35708
35856
  return 2;
35709
35857
  }
35858
+ const bundleRoot = path.resolve(dir);
35859
+ try {
35860
+ await promises.access(bundleRoot);
35861
+ } catch {
35862
+ write(err, `Error: bundle directory not found: ${bundleRoot}
35863
+ `);
35864
+ return 1;
35865
+ }
35866
+ const manifestPath = path.join(bundleRoot, "manifest.json");
35867
+ try {
35868
+ const raw = await promises.readFile(manifestPath, "utf8");
35869
+ JSON.parse(raw);
35870
+ } catch {
35871
+ write(
35872
+ err,
35873
+ `Error: bundle manifest missing or malformed at ${manifestPath}
35874
+ `
35875
+ );
35876
+ return 1;
35877
+ }
35710
35878
  const activate = hasFlag(argv, "--activate");
35711
35879
  const forceRebind = hasFlag(argv, "--force-rebind");
35712
35880
  const acceptUnverifiableAttestations = hasFlag(
@@ -35880,11 +36048,11 @@ async function startDashboardServer(options) {
35880
36048
  }
35881
36049
  }
35882
36050
  });
35883
- await new Promise((resolve6, reject) => {
36051
+ await new Promise((resolve8, reject) => {
35884
36052
  server.once("error", reject);
35885
36053
  server.listen(port, host, () => {
35886
36054
  server.off("error", reject);
35887
- resolve6();
36055
+ resolve8();
35888
36056
  });
35889
36057
  });
35890
36058
  const actualPort = (() => {
@@ -35897,8 +36065,8 @@ async function startDashboardServer(options) {
35897
36065
  url,
35898
36066
  port: actualPort,
35899
36067
  host,
35900
- stop: () => new Promise((resolve6, reject) => {
35901
- server.close((err) => err ? reject(err) : resolve6());
36068
+ stop: () => new Promise((resolve8, reject) => {
36069
+ server.close((err) => err ? reject(err) : resolve8());
35902
36070
  }),
35903
36071
  publish,
35904
36072
  publishActivity: (entry) => publish({ type: "activity", data: entry }),
@@ -36571,7 +36739,7 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
36571
36739
  clientManager.configure(enabledServers).catch((err) => {
36572
36740
  console.error(`[Sanctuary] Failed to configure upstream servers: ${err instanceof Error ? err.message : "unknown error"}`);
36573
36741
  });
36574
- await new Promise((resolve6) => setTimeout(resolve6, 2e3));
36742
+ await new Promise((resolve8) => setTimeout(resolve8, 2e3));
36575
36743
  const proxiedTools = proxyRouter.getProxiedTools();
36576
36744
  if (proxiedTools.length > 0) {
36577
36745
  allTools.push(...proxiedTools);
@@ -37410,8 +37578,8 @@ async function runWrap(options, deps = {}) {
37410
37578
  passphraseValue = process.env.SANCTUARY_PASSPHRASE;
37411
37579
  } else {
37412
37580
  try {
37413
- const resolve6 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
37414
- const resolved = await resolve6();
37581
+ const resolve8 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
37582
+ const resolved = await resolve8();
37415
37583
  passphraseLocation = resolved.location;
37416
37584
  passphraseSource = resolved.source;
37417
37585
  passphraseValue = resolved.value;
@@ -37766,12 +37934,12 @@ async function defaultOpenBrowser(url) {
37766
37934
  cmd = "xdg-open";
37767
37935
  args = [url];
37768
37936
  }
37769
- await new Promise((resolve6) => {
37937
+ await new Promise((resolve8) => {
37770
37938
  const child = child_process.spawn(cmd, args, { stdio: "ignore", detached: true });
37771
- child.on("error", () => resolve6());
37939
+ child.on("error", () => resolve8());
37772
37940
  child.on("spawn", () => {
37773
37941
  child.unref();
37774
- resolve6();
37942
+ resolve8();
37775
37943
  });
37776
37944
  });
37777
37945
  }
@@ -38740,32 +38908,32 @@ endstream`;
38740
38908
  const offsets = new Array(totalObjects + 1).fill(0);
38741
38909
  const chunks = [];
38742
38910
  let bytePos = 0;
38743
- const write2 = (s) => {
38911
+ const write3 = (s) => {
38744
38912
  const buf = Buffer.from(s, "latin1");
38745
38913
  chunks.push(buf);
38746
38914
  bytePos += buf.length;
38747
38915
  };
38748
- write2("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
38916
+ write3("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
38749
38917
  for (let i = 1; i <= totalObjects; i++) {
38750
38918
  offsets[i] = bytePos;
38751
- write2(`${i} 0 obj
38919
+ write3(`${i} 0 obj
38752
38920
  ${objectBodies[i]}
38753
38921
  endobj
38754
38922
  `);
38755
38923
  }
38756
38924
  const xrefPos = bytePos;
38757
- write2(`xref
38925
+ write3(`xref
38758
38926
  0 ${totalObjects + 1}
38759
38927
  `);
38760
- write2("0000000000 65535 f \n");
38928
+ write3("0000000000 65535 f \n");
38761
38929
  for (let i = 1; i <= totalObjects; i++) {
38762
- write2(`${offsets[i].toString().padStart(10, "0")} 00000 n
38930
+ write3(`${offsets[i].toString().padStart(10, "0")} 00000 n
38763
38931
  `);
38764
38932
  }
38765
- write2(`trailer
38933
+ write3(`trailer
38766
38934
  << /Size ${totalObjects + 1} /Root 1 0 R >>
38767
38935
  `);
38768
- write2(`startxref
38936
+ write3(`startxref
38769
38937
  ${xrefPos}
38770
38938
  %%EOF
38771
38939
  `);
@@ -39100,7 +39268,7 @@ var init_backend_interface = __esm({
39100
39268
  }
39101
39269
  });
39102
39270
  async function runSecurity(args, input) {
39103
- return new Promise((resolve6, reject) => {
39271
+ return new Promise((resolve8, reject) => {
39104
39272
  const child = child_process.spawn(SECURITY_BIN, args, { stdio: ["pipe", "pipe", "pipe"] });
39105
39273
  let stdout = "";
39106
39274
  let stderr = "";
@@ -39122,7 +39290,7 @@ async function runSecurity(args, input) {
39122
39290
  reject(err);
39123
39291
  });
39124
39292
  child.on("close", (code) => {
39125
- resolve6({ stdout, stderr, code: code ?? -1 });
39293
+ resolve8({ stdout, stderr, code: code ?? -1 });
39126
39294
  });
39127
39295
  if (input !== void 0) {
39128
39296
  child.stdin.write(input);
@@ -40198,7 +40366,7 @@ async function readValue(stdin, prompt2) {
40198
40366
  return await readFirstLine(stdin);
40199
40367
  }
40200
40368
  async function readFirstLine(stdin) {
40201
- return new Promise((resolve6, reject) => {
40369
+ return new Promise((resolve8, reject) => {
40202
40370
  const rl = readline.createInterface({ input: stdin });
40203
40371
  let resolved = false;
40204
40372
  const finish = (value) => {
@@ -40209,7 +40377,7 @@ async function readFirstLine(stdin) {
40209
40377
  rl.close();
40210
40378
  } catch {
40211
40379
  }
40212
- resolve6(value);
40380
+ resolve8(value);
40213
40381
  };
40214
40382
  const deadline = setTimeout(() => {
40215
40383
  finish("");
@@ -40228,7 +40396,7 @@ async function promptSilently(stdin, prompt2) {
40228
40396
  process.stderr.write(`${prompt2}: `);
40229
40397
  stdin.setRawMode?.(true);
40230
40398
  stdin.resume();
40231
- return await new Promise((resolve6) => {
40399
+ return await new Promise((resolve8) => {
40232
40400
  let buf = "";
40233
40401
  const onData = (chunk) => {
40234
40402
  const s = chunk.toString("utf8");
@@ -40238,7 +40406,7 @@ async function promptSilently(stdin, prompt2) {
40238
40406
  stdin.pause();
40239
40407
  stdin.off("data", onData);
40240
40408
  process.stderr.write("\n");
40241
- resolve6(buf);
40409
+ resolve8(buf);
40242
40410
  return;
40243
40411
  }
40244
40412
  if (ch === "") {
@@ -40505,13 +40673,179 @@ var init_cli4 = __esm({
40505
40673
  init_discovery();
40506
40674
  }
40507
40675
  });
40676
+
40677
+ // src/cli/identity.ts
40678
+ var identity_exports2 = {};
40679
+ __export(identity_exports2, {
40680
+ runIdentityCommand: () => runIdentityCommand
40681
+ });
40682
+ function write2(stream, text) {
40683
+ stream.write(text);
40684
+ }
40685
+ function flagValue2(argv, name) {
40686
+ const index = argv.indexOf(name);
40687
+ if (index === -1) return void 0;
40688
+ return argv[index + 1];
40689
+ }
40690
+ function hasFlag2(argv, name) {
40691
+ return argv.includes(name);
40692
+ }
40693
+ function printUsage3(out) {
40694
+ write2(
40695
+ out,
40696
+ `Usage: sanctuary identity <command> [options]
40697
+
40698
+ Commands:
40699
+ show Print the active identity (DID, identity_id, public key).
40700
+
40701
+ Options:
40702
+ --fortress <path> Override the storage path.
40703
+ --passphrase <val> Passphrase for master-key derivation.
40704
+ --json Output as JSON.
40705
+ --help, -h Show this help.
40706
+
40707
+ Environment variables:
40708
+ SANCTUARY_PASSPHRASE Key derivation passphrase.
40709
+ SANCTUARY_STORAGE_PATH State directory (default: ~/.sanctuary).
40710
+ SANCTUARY_FORTRESS_PATH Operator-friendly alias for STORAGE_PATH.
40711
+ SANCTUARY_RECOVERY_KEY Recovery key (alternative to passphrase).
40712
+
40713
+ Identity data is encrypted at rest. A passphrase or recovery key is
40714
+ required to decrypt and display identity information.
40715
+ `
40716
+ );
40717
+ }
40718
+ async function runIdentityCommand(args) {
40719
+ const argv = args.argv;
40720
+ const out = args.out ?? process.stdout;
40721
+ const err = args.err ?? process.stderr;
40722
+ const env = args.env ?? process.env;
40723
+ if (argv.length === 0 || hasFlag2(argv, "--help") || hasFlag2(argv, "-h")) {
40724
+ printUsage3(out);
40725
+ return 0;
40726
+ }
40727
+ const command = argv[0];
40728
+ if (command === "show") {
40729
+ return await cmdShow(argv.slice(1), out, err, env);
40730
+ }
40731
+ write2(err, `Unknown identity command: ${command}
40732
+ `);
40733
+ write2(err, `Run "sanctuary identity --help" for usage.
40734
+ `);
40735
+ return 2;
40736
+ }
40737
+ async function cmdShow(argv, out, err, env) {
40738
+ const json = hasFlag2(argv, "--json");
40739
+ const fortressFlag = flagValue2(argv, "--fortress");
40740
+ if (fortressFlag) {
40741
+ process.env.SANCTUARY_STORAGE_PATH = fortressFlag;
40742
+ }
40743
+ const passphrase = flagValue2(argv, "--passphrase") ?? env.SANCTUARY_PASSPHRASE;
40744
+ const recoveryKey = env.SANCTUARY_RECOVERY_KEY;
40745
+ if (!passphrase && !recoveryKey) {
40746
+ write2(
40747
+ err,
40748
+ "Error: sanctuary identity show requires SANCTUARY_PASSPHRASE, --passphrase, or SANCTUARY_RECOVERY_KEY.\n"
40749
+ );
40750
+ return 1;
40751
+ }
40752
+ try {
40753
+ const config = await loadConfig();
40754
+ await promises.mkdir(config.storage_path, { recursive: true, mode: 448 });
40755
+ const stateStoragePath = path.join(config.storage_path, "state");
40756
+ const storage = new FilesystemStorage(stateStoragePath);
40757
+ let masterKey;
40758
+ if (passphrase) {
40759
+ let existingParams;
40760
+ const raw = await storage.read("_meta", "key-params");
40761
+ if (raw)
40762
+ existingParams = JSON.parse(bytesToString(raw));
40763
+ const derived = await deriveMasterKey(passphrase, existingParams);
40764
+ masterKey = derived.key;
40765
+ } else {
40766
+ masterKey = fromBase64url(recoveryKey);
40767
+ if (masterKey.length !== 32) {
40768
+ write2(err, "Error: SANCTUARY_RECOVERY_KEY must decode to 32 bytes.\n");
40769
+ return 1;
40770
+ }
40771
+ }
40772
+ const identityManager = new IdentityManager(storage, masterKey);
40773
+ const loadResult = await identityManager.load();
40774
+ if (loadResult.loaded === 0) {
40775
+ write2(
40776
+ err,
40777
+ loadResult.total > 0 ? "Error: identity files found but none could be decrypted. Wrong passphrase?\n" : "No identities found in this fortress.\n"
40778
+ );
40779
+ return 1;
40780
+ }
40781
+ const primary = identityManager.getDefault();
40782
+ if (!primary) {
40783
+ write2(err, "No primary identity set.\n");
40784
+ return 1;
40785
+ }
40786
+ if (json) {
40787
+ write2(
40788
+ out,
40789
+ JSON.stringify(
40790
+ {
40791
+ identity_id: primary.identity_id,
40792
+ did: primary.did,
40793
+ public_key: primary.public_key,
40794
+ label: primary.label,
40795
+ key_type: primary.key_type,
40796
+ created_at: primary.created_at,
40797
+ storage_path: config.storage_path,
40798
+ total_identities: loadResult.loaded
40799
+ },
40800
+ null,
40801
+ 2
40802
+ ) + "\n"
40803
+ );
40804
+ } else {
40805
+ write2(out, `identity_id: ${primary.identity_id}
40806
+ `);
40807
+ write2(out, `did: ${primary.did}
40808
+ `);
40809
+ write2(out, `public_key: ${primary.public_key}
40810
+ `);
40811
+ write2(out, `label: ${primary.label}
40812
+ `);
40813
+ write2(out, `key_type: ${primary.key_type}
40814
+ `);
40815
+ write2(out, `created_at: ${primary.created_at}
40816
+ `);
40817
+ write2(out, `storage_path: ${config.storage_path}
40818
+ `);
40819
+ write2(out, `total_identities: ${loadResult.loaded}
40820
+ `);
40821
+ }
40822
+ return 0;
40823
+ } catch (error) {
40824
+ write2(
40825
+ err,
40826
+ error instanceof Error ? `Error: ${error.message}
40827
+ ` : `Error: ${String(error)}
40828
+ `
40829
+ );
40830
+ return 1;
40831
+ }
40832
+ }
40833
+ var init_identity2 = __esm({
40834
+ "src/cli/identity.ts"() {
40835
+ init_filesystem();
40836
+ init_tools();
40837
+ init_key_derivation();
40838
+ init_encoding();
40839
+ init_config();
40840
+ }
40841
+ });
40508
40842
  async function probeTenantDashboard(tenant, options = {}) {
40509
40843
  const rt = tenant.runtime;
40510
40844
  if (!rt) {
40511
40845
  return { running: false, status: null, reason: "no runtime.json" };
40512
40846
  }
40513
40847
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
40514
- return await new Promise((resolve6) => {
40848
+ return await new Promise((resolve8) => {
40515
40849
  const req = http.get(
40516
40850
  {
40517
40851
  host: rt.dashboard_host,
@@ -40523,9 +40857,9 @@ async function probeTenantDashboard(tenant, options = {}) {
40523
40857
  res.resume();
40524
40858
  const status = res.statusCode ?? 0;
40525
40859
  if (status > 0 && status < 500) {
40526
- resolve6({ running: true, status, reason: null });
40860
+ resolve8({ running: true, status, reason: null });
40527
40861
  } else {
40528
- resolve6({
40862
+ resolve8({
40529
40863
  running: false,
40530
40864
  status,
40531
40865
  reason: `dashboard returned ${status}`
@@ -40535,10 +40869,10 @@ async function probeTenantDashboard(tenant, options = {}) {
40535
40869
  );
40536
40870
  req.on("timeout", () => {
40537
40871
  req.destroy();
40538
- resolve6({ running: false, status: null, reason: "timeout" });
40872
+ resolve8({ running: false, status: null, reason: "timeout" });
40539
40873
  });
40540
40874
  req.on("error", (err) => {
40541
- resolve6({
40875
+ resolve8({
40542
40876
  running: false,
40543
40877
  status: null,
40544
40878
  reason: err.code ?? err.message
@@ -40570,10 +40904,17 @@ function resolveCtx(args) {
40570
40904
  };
40571
40905
  }
40572
40906
  async function runAgentsCommand(args) {
40907
+ const fortressIdx = args.argv.indexOf("--fortress");
40908
+ if (fortressIdx !== -1 && args.argv[fortressIdx + 1]) {
40909
+ args = { ...args, root: args.argv[fortressIdx + 1] };
40910
+ const filtered = [...args.argv];
40911
+ filtered.splice(fortressIdx, 2);
40912
+ args = { ...args, argv: filtered };
40913
+ }
40573
40914
  const ctx = resolveCtx(args);
40574
40915
  const [sub, ...rest] = args.argv;
40575
40916
  if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
40576
- printUsage3(ctx.out);
40917
+ printUsage4(ctx.out);
40577
40918
  return 0;
40578
40919
  }
40579
40920
  try {
@@ -40581,13 +40922,13 @@ async function runAgentsCommand(args) {
40581
40922
  case "list":
40582
40923
  return await cmdList3(rest, ctx);
40583
40924
  case "show":
40584
- return await cmdShow(rest, ctx);
40925
+ return await cmdShow2(rest, ctx);
40585
40926
  case "status":
40586
40927
  return await cmdStatus(rest, ctx);
40587
40928
  default:
40588
40929
  ctx.err.write(`Unknown subcommand: ${sub}
40589
40930
  `);
40590
- printUsage3(ctx.err);
40931
+ printUsage4(ctx.err);
40591
40932
  return 2;
40592
40933
  }
40593
40934
  } catch (e) {
@@ -40597,13 +40938,17 @@ async function runAgentsCommand(args) {
40597
40938
  return 1;
40598
40939
  }
40599
40940
  }
40600
- function printUsage3(s) {
40941
+ function printUsage4(s) {
40601
40942
  s.write(`Usage: sanctuary agents <command> [flags]
40602
40943
 
40603
40944
  list [--json] List every tenant visible on this host.
40604
40945
  show <tenant> [--json] Show details for one tenant.
40605
40946
  status [--json] One-line-per-tenant running/stopped summary.
40606
40947
 
40948
+ Options:
40949
+ --fortress <path> Scope discovery to a specific storage path
40950
+ instead of scanning ~/.sanctuary.
40951
+
40607
40952
  Tenants are discovered by scanning ~/.sanctuary and any storage paths in
40608
40953
  SANCTUARY_AGENTS_EXTRA_PATHS or ~/.sanctuary/agents-extra.json. Tenant
40609
40954
  creation is done via \`sanctuary wrap\` with SANCTUARY_STORAGE_PATH set.
@@ -40687,7 +41032,7 @@ async function cmdList3(argv, ctx) {
40687
41032
  }
40688
41033
  return 0;
40689
41034
  }
40690
- async function cmdShow(argv, ctx) {
41035
+ async function cmdShow2(argv, ctx) {
40691
41036
  const positional = argv.find((a) => !a.startsWith("--"));
40692
41037
  if (!positional) {
40693
41038
  ctx.err.write("Missing tenant. Usage: sanctuary agents show <tenant>\n");
@@ -40838,10 +41183,10 @@ async function runResetPassphraseCommand(args) {
40838
41183
  const plat = args.platformOverride ?? process.platform;
40839
41184
  const parsed = parseArgs2(args.argv);
40840
41185
  if (parsed.help) {
40841
- printUsage4(out);
41186
+ printUsage5(out);
40842
41187
  return 0;
40843
41188
  }
40844
- const storagePath = parsed.storage ?? args.storagePath ?? resolveStoragePath(process.env, home);
41189
+ const storagePath = parsed.storage ?? parsed.fortress ?? args.storagePath ?? resolveStoragePath(process.env, home);
40845
41190
  out.write(banner(storagePath));
40846
41191
  const runtimeFile = path.join(storagePath, "runtime.json");
40847
41192
  if (await fileExists4(runtimeFile)) {
@@ -40898,13 +41243,15 @@ function parseArgs2(argv) {
40898
41243
  out.mode = v;
40899
41244
  } else if (a === "--storage" && argv[i + 1]) {
40900
41245
  out.storage = argv[++i];
41246
+ } else if (a === "--fortress" && argv[i + 1]) {
41247
+ out.fortress = argv[++i];
40901
41248
  } else if (a && a.startsWith("--")) {
40902
41249
  throw new Error(`Unknown flag: ${a}`);
40903
41250
  }
40904
41251
  }
40905
41252
  return out;
40906
41253
  }
40907
- function printUsage4(out) {
41254
+ function printUsage5(out) {
40908
41255
  out.write(`
40909
41256
  Usage: sanctuary reset-passphrase [options]
40910
41257
 
@@ -40927,9 +41274,9 @@ Recover a fortress whose passphrase has been lost or corrupted. Three modes:
40927
41274
 
40928
41275
  Options:
40929
41276
  --mode <shares|guardian|nuke> Pick a path non-interactively.
40930
- --storage <path> Override the resolved storage path.
40931
- Defaults to SANCTUARY_STORAGE_PATH or
40932
- ~/.sanctuary.
41277
+ --fortress <path> Override the fortress storage path.
41278
+ Consistent with "sanctuary wrap --fortress".
41279
+ --storage <path> Alias for --fortress.
40933
41280
  --help, -h Show this help.
40934
41281
 
40935
41282
  Without --mode, the command surveys which paths are operationally available
@@ -41200,7 +41547,7 @@ async function prompt(lines, err, question) {
41200
41547
  return await lines.next();
41201
41548
  }
41202
41549
  async function defaultExec2(cmd, args) {
41203
- return await new Promise((resolve6, reject) => {
41550
+ return await new Promise((resolve8, reject) => {
41204
41551
  const child = child_process.spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
41205
41552
  let stdout = "";
41206
41553
  let stderr = "";
@@ -41211,7 +41558,7 @@ async function defaultExec2(cmd, args) {
41211
41558
  stderr += d.toString();
41212
41559
  });
41213
41560
  child.on("error", reject);
41214
- child.on("close", (code) => resolve6({ stdout, stderr, code }));
41561
+ child.on("close", (code) => resolve8({ stdout, stderr, code }));
41215
41562
  });
41216
41563
  }
41217
41564
  var LineReader;
@@ -41247,8 +41594,8 @@ var init_reset_passphrase = __esm({
41247
41594
  return Promise.resolve(this.queue.shift());
41248
41595
  }
41249
41596
  if (this.closed) return Promise.resolve("");
41250
- return new Promise((resolve6) => {
41251
- this.waiters.push(resolve6);
41597
+ return new Promise((resolve8) => {
41598
+ this.waiters.push(resolve8);
41252
41599
  });
41253
41600
  }
41254
41601
  close() {
@@ -41647,11 +41994,11 @@ async function startMultiDashboardServer(options = {}) {
41647
41994
  }
41648
41995
  }
41649
41996
  });
41650
- await new Promise((resolve6, reject) => {
41997
+ await new Promise((resolve8, reject) => {
41651
41998
  server.once("error", reject);
41652
41999
  server.listen(port, host, () => {
41653
42000
  server.off("error", reject);
41654
- resolve6();
42001
+ resolve8();
41655
42002
  });
41656
42003
  });
41657
42004
  const addr = server.address();
@@ -41660,8 +42007,8 @@ async function startMultiDashboardServer(options = {}) {
41660
42007
  url: `http://${host}:${actualPort}`,
41661
42008
  port: actualPort,
41662
42009
  host,
41663
- stop: () => new Promise((resolve6, reject) => {
41664
- server.close((err) => err ? reject(err) : resolve6());
42010
+ stop: () => new Promise((resolve8, reject) => {
42011
+ server.close((err) => err ? reject(err) : resolve8());
41665
42012
  })
41666
42013
  };
41667
42014
  }
@@ -42061,7 +42408,7 @@ function formatUpdateMessage(current, latest) {
42061
42408
  return `[Sanctuary] Update available: ${current} \u2192 ${latest}. Run: npx @sanctuary-framework/mcp-server@latest`;
42062
42409
  }
42063
42410
  function fetchLatestVersion(currentVersion) {
42064
- return new Promise((resolve6) => {
42411
+ return new Promise((resolve8) => {
42065
42412
  const req = https.get(
42066
42413
  REGISTRY_URL,
42067
42414
  {
@@ -42071,7 +42418,7 @@ function fetchLatestVersion(currentVersion) {
42071
42418
  (res) => {
42072
42419
  if (res.statusCode !== 200) {
42073
42420
  res.resume();
42074
- resolve6(null);
42421
+ resolve8(null);
42075
42422
  return;
42076
42423
  }
42077
42424
  let data = "";
@@ -42080,7 +42427,7 @@ function fetchLatestVersion(currentVersion) {
42080
42427
  data += chunk;
42081
42428
  if (data.length > 32768) {
42082
42429
  res.destroy();
42083
- resolve6(null);
42430
+ resolve8(null);
42084
42431
  }
42085
42432
  });
42086
42433
  res.on("end", () => {
@@ -42088,20 +42435,20 @@ function fetchLatestVersion(currentVersion) {
42088
42435
  const json = JSON.parse(data);
42089
42436
  const latest = json.version;
42090
42437
  if (typeof latest === "string" && isNewerVersion(currentVersion, latest)) {
42091
- resolve6(latest);
42438
+ resolve8(latest);
42092
42439
  } else {
42093
- resolve6(null);
42440
+ resolve8(null);
42094
42441
  }
42095
42442
  } catch {
42096
- resolve6(null);
42443
+ resolve8(null);
42097
42444
  }
42098
42445
  });
42099
42446
  }
42100
42447
  );
42101
- req.on("error", () => resolve6(null));
42448
+ req.on("error", () => resolve8(null));
42102
42449
  req.on("timeout", () => {
42103
42450
  req.destroy();
42104
- resolve6(null);
42451
+ resolve8(null);
42105
42452
  });
42106
42453
  });
42107
42454
  }
@@ -42179,6 +42526,11 @@ async function main() {
42179
42526
  const code = await runTemplateCommand2({ argv: args.slice(1) });
42180
42527
  process.exit(code);
42181
42528
  }
42529
+ if (args[0] === "identity") {
42530
+ const { runIdentityCommand: runIdentityCommand2 } = await Promise.resolve().then(() => (init_identity2(), identity_exports2));
42531
+ const code = await runIdentityCommand2({ argv: args.slice(1) });
42532
+ process.exit(code);
42533
+ }
42182
42534
  if (args[0] === "agents") {
42183
42535
  const { runAgentsCommand: runAgentsCommand2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
42184
42536
  const code = await runAgentsCommand2({ argv: args.slice(1) });
@@ -42400,6 +42752,9 @@ Subcommands:
42400
42752
  Use "sanctuary dashboard --help" for options.
42401
42753
  Pass --multi to render the multi-tenant overview.
42402
42754
 
42755
+ identity Inspect the active identity (DID, public key).
42756
+ Use "sanctuary identity --help" for options.
42757
+
42403
42758
  template Manage policy templates (list, init).
42404
42759
  Use "sanctuary template --help" for options.
42405
42760