@sanctuary-framework/mcp-server 1.1.1 → 1.1.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
@@ -11887,6 +11887,20 @@ async function handleRequest(deps, req, res) {
11887
11887
  const url = new URL(req.url ?? "/", `http://${host}`);
11888
11888
  const method = (req.method ?? "GET").toUpperCase();
11889
11889
  const path = url.pathname;
11890
+ if (deps.v11Bindings) {
11891
+ const handled = await dispatchV11Request(
11892
+ {
11893
+ bindings: deps.v11Bindings,
11894
+ ...deps.authToken !== void 0 ? { authToken: deps.authToken } : {},
11895
+ loopbackAutoAuth: deps.loopbackAutoAuth ?? false
11896
+ },
11897
+ req,
11898
+ res,
11899
+ url,
11900
+ method
11901
+ );
11902
+ if (handled) return true;
11903
+ }
11890
11904
  if (!isAuthorized(deps, req, url)) {
11891
11905
  writeJSON(res, 401, { error: "unauthorized" });
11892
11906
  return true;
@@ -12067,6 +12081,7 @@ var init_api = __esm({
12067
12081
  init_registry();
12068
12082
  init_init();
12069
12083
  init_discovery();
12084
+ init_dispatch();
12070
12085
  }
12071
12086
  });
12072
12087
 
@@ -13602,6 +13617,53 @@ var init_v1_1 = __esm({
13602
13617
  init_client();
13603
13618
  }
13604
13619
  });
13620
+
13621
+ // src/dashboard/v1_1/dispatch.ts
13622
+ async function dispatchV11Request(inputs, req, res, url, method) {
13623
+ const { bindings, authToken, loopbackAutoAuth } = inputs;
13624
+ if (method === "GET" && (url.pathname === "/v1.1" || url.pathname === "/v1.1/")) {
13625
+ return handleDashboardV11Route(
13626
+ {
13627
+ identityId: bindings.identityId,
13628
+ fortressId: bindings.fortressId,
13629
+ ...authToken !== void 0 ? { authToken } : {}
13630
+ },
13631
+ req,
13632
+ res
13633
+ );
13634
+ }
13635
+ if (url.pathname.startsWith("/api/hub/")) {
13636
+ const authConfig = {
13637
+ loopbackAutoAuth,
13638
+ ...authToken !== void 0 ? { authToken } : {}
13639
+ };
13640
+ return handleHubRoute(
13641
+ { authConfig, service: bindings.hubService },
13642
+ req,
13643
+ res
13644
+ );
13645
+ }
13646
+ if (method === "GET" && url.pathname === "/api/identities") {
13647
+ const authConfig = {
13648
+ loopbackAutoAuth,
13649
+ ...authToken !== void 0 ? { authToken } : {}
13650
+ };
13651
+ const aliasReq = Object.create(req);
13652
+ aliasReq.url = "/api/hub/agents" + url.search;
13653
+ return handleHubRoute(
13654
+ { authConfig, service: bindings.hubService },
13655
+ aliasReq,
13656
+ res
13657
+ );
13658
+ }
13659
+ return false;
13660
+ }
13661
+ var init_dispatch = __esm({
13662
+ "src/dashboard/v1_1/dispatch.ts"() {
13663
+ init_api_router();
13664
+ init_v1_1();
13665
+ }
13666
+ });
13605
13667
  function isDashboardViewRoute(method, path) {
13606
13668
  if (method !== "GET") return false;
13607
13669
  return path === "/" || path === "/dashboard" || path === "/fortress" || path === "/events";
@@ -13614,8 +13676,7 @@ var init_dashboard = __esm({
13614
13676
  init_dashboard_html();
13615
13677
  init_fortress_view();
13616
13678
  init_system_prompt_generator();
13617
- init_api_router();
13618
- init_v1_1();
13679
+ init_dispatch();
13619
13680
  SESSION_TTL_REMOTE_MS = 5 * 60 * 1e3;
13620
13681
  SESSION_TTL_LOCAL_MS = 24 * 60 * 60 * 1e3;
13621
13682
  MAX_SESSIONS = 1e3;
@@ -13737,45 +13798,17 @@ var init_dashboard = __esm({
13737
13798
  */
13738
13799
  async dispatchV11(req, res, url, method) {
13739
13800
  if (!this.v11Bindings) return false;
13740
- if (method === "GET" && (url.pathname === "/v1.1" || url.pathname === "/v1.1/")) {
13741
- const handled = handleDashboardV11Route(
13742
- {
13743
- identityId: this.v11Bindings.identityId,
13744
- fortressId: this.v11Bindings.fortressId,
13745
- ...this.authToken !== void 0 ? { authToken: this.authToken } : {}
13746
- },
13747
- req,
13748
- res
13749
- );
13750
- return handled;
13751
- }
13752
- if (url.pathname.startsWith("/api/hub/")) {
13753
- const authConfig = {
13754
- loopbackAutoAuth: this._autoAuthLocalhost,
13755
- ...this.authToken !== void 0 ? { authToken: this.authToken } : {}
13756
- };
13757
- const handled = await handleHubRoute(
13758
- { authConfig, service: this.v11Bindings.hubService },
13759
- req,
13760
- res
13761
- );
13762
- return handled;
13763
- }
13764
- if (method === "GET" && url.pathname === "/api/identities") {
13765
- const authConfig = {
13766
- loopbackAutoAuth: this._autoAuthLocalhost,
13767
- ...this.authToken !== void 0 ? { authToken: this.authToken } : {}
13768
- };
13769
- const aliasReq = Object.create(req);
13770
- aliasReq.url = "/api/hub/agents" + url.search;
13771
- const handled = await handleHubRoute(
13772
- { authConfig, service: this.v11Bindings.hubService },
13773
- aliasReq,
13774
- res
13775
- );
13776
- return handled;
13777
- }
13778
- return false;
13801
+ return dispatchV11Request(
13802
+ {
13803
+ bindings: this.v11Bindings,
13804
+ ...this.authToken !== void 0 ? { authToken: this.authToken } : {},
13805
+ loopbackAutoAuth: this._autoAuthLocalhost
13806
+ },
13807
+ req,
13808
+ res,
13809
+ url,
13810
+ method
13811
+ );
13779
13812
  }
13780
13813
  /**
13781
13814
  * v0.10.2: enable (or disable) the loopback auto-auth fast path. See
@@ -27503,14 +27536,14 @@ var init_generator2 = __esm({
27503
27536
  ]);
27504
27537
  }
27505
27538
  });
27506
- function printRecoveryKeyBanner(recoveryKey, filePath, output = process.stderr) {
27539
+ function printSecretBanner(secret, filePath, copy, output = process.stderr) {
27507
27540
  const lines = [
27508
- "SANCTUARY: First Run, Recovery Key Generated",
27541
+ copy.bannerHeader,
27509
27542
  "",
27510
- `Recovery Key: ${recoveryKey}`,
27543
+ `${copy.bannerSecretLabel}: ${secret}`,
27511
27544
  "",
27512
- "SAVE THIS KEY. It will not be shown again.",
27513
- "Without it, your encrypted state is unrecoverable.",
27545
+ copy.bannerSaveLine,
27546
+ copy.bannerLossLine,
27514
27547
  "",
27515
27548
  "Plaintext copy written to:",
27516
27549
  ` ${filePath}`,
@@ -27529,8 +27562,8 @@ ${bottom}
27529
27562
 
27530
27563
  `);
27531
27564
  }
27532
- async function writeRecoveryKeyFile(opts) {
27533
- const filePath = path.join(opts.storagePath, RECOVERY_KEY_FILENAME);
27565
+ async function writeSecretFile(opts) {
27566
+ const filePath = path.join(opts.storagePath, opts.copy.fileName);
27534
27567
  try {
27535
27568
  await promises.access(filePath, promises.constants.F_OK);
27536
27569
  return { filePath, written: false };
@@ -27540,54 +27573,47 @@ async function writeRecoveryKeyFile(opts) {
27540
27573
  const now = (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
27541
27574
  const fortressLine = opts.fortressId ? `Fortress: ${opts.fortressId}
27542
27575
  ` : "";
27543
- const content = `SANCTUARY RECOVERY KEY \u2014 DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.
27576
+ const content = `${opts.copy.fileWarningHeader}
27544
27577
  Generated: ${now}
27545
27578
  ` + fortressLine + `
27546
- Recovery key:
27547
- ${opts.recoveryKey}
27548
-
27549
- This file was created on first init. Sanctuary will NOT regenerate this file on
27550
- subsequent runs and will NOT display the key again. After moving this file off
27551
- the host (encrypted backup, password manager, paper safe), delete it from the
27552
- fortress directory. Do NOT keep it in the fortress; the recovery key bypasses
27553
- the cocoon passphrase by design.
27554
- `;
27579
+ ${opts.copy.fileSecretLabel}
27580
+ ${opts.secret}
27581
+
27582
+ ` + opts.copy.fileBody;
27555
27583
  await promises.writeFile(filePath, content, { mode: 384 });
27556
27584
  return { filePath, written: true };
27557
27585
  }
27558
- async function confirmRecoveryKeySaved(io) {
27586
+ async function confirmSecretSaved(copy, declinedError, nonInteractiveError, io) {
27559
27587
  const input = io?.input ?? process.stdin;
27560
27588
  const output = io?.output ?? process.stderr;
27561
27589
  const realStdin = !io && process.stdin.isTTY !== true;
27562
27590
  if (realStdin) {
27563
- throw new RecoveryKeyConfirmationNonInteractiveError();
27591
+ throw new nonInteractiveError();
27564
27592
  }
27565
27593
  const rl = promises$1.createInterface({ input, output });
27566
27594
  try {
27567
27595
  const answer = await rl.question(
27568
- "Have you saved the recovery key? [y/N] "
27596
+ `Have you saved the ${copy.promptLabel}? [y/N] `
27569
27597
  );
27570
27598
  const normalized = answer.trim().toLowerCase();
27571
27599
  if (normalized !== "y" && normalized !== "yes") {
27572
- throw new RecoveryKeyConfirmationDeclinedError();
27600
+ throw new declinedError();
27573
27601
  }
27574
27602
  } finally {
27575
27603
  rl.close();
27576
27604
  }
27577
27605
  }
27578
- async function discloseRecoveryKey(opts) {
27606
+ async function discloseSecret(opts, copy, declinedError, nonInteractiveError) {
27579
27607
  const mode = opts.mode ?? "interactive";
27580
- const fileResult = await writeRecoveryKeyFile({
27608
+ const writeOpts = {
27581
27609
  storagePath: opts.storagePath,
27582
- recoveryKey: opts.recoveryKey,
27583
- ...opts.fortressId !== void 0 ? { fortressId: opts.fortressId } : {},
27584
- ...opts.now !== void 0 ? { now: opts.now } : {}
27585
- });
27586
- printRecoveryKeyBanner(
27587
- opts.recoveryKey,
27588
- fileResult.filePath,
27589
- opts.io?.output
27590
- );
27610
+ secret: opts.secret,
27611
+ copy
27612
+ };
27613
+ if (opts.fortressId !== void 0) writeOpts.fortressId = opts.fortressId;
27614
+ if (opts.now !== void 0) writeOpts.now = opts.now;
27615
+ const fileResult = await writeSecretFile(writeOpts);
27616
+ printSecretBanner(opts.secret, fileResult.filePath, copy, opts.io?.output);
27591
27617
  if (mode === "no-confirm" || mode === "stdio-server") {
27592
27618
  return {
27593
27619
  filePath: fileResult.filePath,
@@ -27595,17 +27621,72 @@ async function discloseRecoveryKey(opts) {
27595
27621
  confirmed: false
27596
27622
  };
27597
27623
  }
27598
- await confirmRecoveryKeySaved(opts.io);
27624
+ await confirmSecretSaved(copy, declinedError, nonInteractiveError, opts.io);
27599
27625
  return {
27600
27626
  filePath: fileResult.filePath,
27601
27627
  fileWritten: fileResult.written,
27602
27628
  confirmed: true
27603
27629
  };
27604
27630
  }
27605
- var RECOVERY_KEY_FILENAME, RecoveryKeyConfirmationDeclinedError, RecoveryKeyConfirmationNonInteractiveError;
27631
+ async function discloseRecoveryKey(opts) {
27632
+ const internalOpts = {
27633
+ secret: opts.recoveryKey,
27634
+ storagePath: opts.storagePath
27635
+ };
27636
+ if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
27637
+ if (opts.mode !== void 0) internalOpts.mode = opts.mode;
27638
+ if (opts.now !== void 0) internalOpts.now = opts.now;
27639
+ if (opts.io !== void 0) internalOpts.io = opts.io;
27640
+ return discloseSecret(
27641
+ internalOpts,
27642
+ RECOVERY_KEY_COPY,
27643
+ RecoveryKeyConfirmationDeclinedError,
27644
+ RecoveryKeyConfirmationNonInteractiveError
27645
+ );
27646
+ }
27647
+ async function disclosePassphrase(opts) {
27648
+ const internalOpts = {
27649
+ secret: opts.passphrase,
27650
+ storagePath: opts.storagePath
27651
+ };
27652
+ if (opts.fortressId !== void 0) internalOpts.fortressId = opts.fortressId;
27653
+ if (opts.mode !== void 0) internalOpts.mode = opts.mode;
27654
+ if (opts.now !== void 0) internalOpts.now = opts.now;
27655
+ if (opts.io !== void 0) internalOpts.io = opts.io;
27656
+ return discloseSecret(
27657
+ internalOpts,
27658
+ PASSPHRASE_BACKUP_COPY,
27659
+ PassphraseConfirmationDeclinedError,
27660
+ PassphraseConfirmationNonInteractiveError
27661
+ );
27662
+ }
27663
+ var RECOVERY_KEY_FILENAME, PASSPHRASE_BACKUP_FILENAME, RECOVERY_KEY_COPY, PASSPHRASE_BACKUP_COPY, RecoveryKeyConfirmationDeclinedError, RecoveryKeyConfirmationNonInteractiveError, PassphraseConfirmationDeclinedError, PassphraseConfirmationNonInteractiveError;
27606
27664
  var init_recovery_key_disclosure = __esm({
27607
27665
  "src/cocoon/recovery-key-disclosure.ts"() {
27608
27666
  RECOVERY_KEY_FILENAME = "recovery-key.txt";
27667
+ PASSPHRASE_BACKUP_FILENAME = "passphrase-backup.txt";
27668
+ RECOVERY_KEY_COPY = {
27669
+ fileName: RECOVERY_KEY_FILENAME,
27670
+ bannerHeader: "SANCTUARY: First Run, Recovery Key Generated",
27671
+ bannerSecretLabel: "Recovery Key",
27672
+ bannerSaveLine: "SAVE THIS KEY. It will not be shown again.",
27673
+ bannerLossLine: "Without it, your encrypted state is unrecoverable.",
27674
+ fileWarningHeader: "SANCTUARY RECOVERY KEY, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
27675
+ fileSecretLabel: "Recovery key:",
27676
+ fileBody: "This file was created on first init. Sanctuary will NOT regenerate this file on\nsubsequent runs and will NOT display the key again. After moving this file off\nthe host (encrypted backup, password manager, paper safe), delete it from the\nfortress directory. Do NOT keep it in the fortress; the recovery key bypasses\nthe cocoon passphrase by design.\n",
27677
+ promptLabel: "recovery key"
27678
+ };
27679
+ PASSPHRASE_BACKUP_COPY = {
27680
+ fileName: PASSPHRASE_BACKUP_FILENAME,
27681
+ bannerHeader: "SANCTUARY: First Run, Passphrase Generated",
27682
+ bannerSecretLabel: "Passphrase",
27683
+ bannerSaveLine: "SAVE THIS PASSPHRASE. It will not be shown again.",
27684
+ bannerLossLine: "Without it, your encrypted state is unrecoverable.",
27685
+ fileWarningHeader: "SANCTUARY PASSPHRASE, DO NOT COMMIT, DO NOT EMAIL, MOVE OFF-HOST IMMEDIATELY.",
27686
+ fileSecretLabel: "Passphrase:",
27687
+ fileBody: "This file was created on first wrap when Sanctuary generated the passphrase.\nSanctuary will NOT regenerate this file on subsequent runs and will NOT display\nthe passphrase again. After moving this file off the host (encrypted backup,\npassword manager, paper safe), delete it from the fortress directory. Do NOT\nkeep it in the fortress; the keychain copy is recoverable only while the host\nand its OS keyring are intact.\n",
27688
+ promptLabel: "passphrase"
27689
+ };
27609
27690
  RecoveryKeyConfirmationDeclinedError = class extends Error {
27610
27691
  constructor() {
27611
27692
  super(
@@ -27622,6 +27703,22 @@ var init_recovery_key_disclosure = __esm({
27622
27703
  this.name = "RecoveryKeyConfirmationNonInteractiveError";
27623
27704
  }
27624
27705
  };
27706
+ PassphraseConfirmationDeclinedError = class extends Error {
27707
+ constructor() {
27708
+ super(
27709
+ "Passphrase confirmation declined. Save the passphrase (printed above and written to passphrase-backup.txt) before re-running wrap."
27710
+ );
27711
+ this.name = "PassphraseConfirmationDeclinedError";
27712
+ }
27713
+ };
27714
+ PassphraseConfirmationNonInteractiveError = class extends Error {
27715
+ constructor() {
27716
+ super(
27717
+ "Passphrase confirmation requires an interactive terminal. Re-run with --no-open for scripted use, or run from a TTY."
27718
+ );
27719
+ this.name = "PassphraseConfirmationNonInteractiveError";
27720
+ }
27721
+ };
27625
27722
  }
27626
27723
  });
27627
27724
 
@@ -30023,14 +30120,18 @@ async function startDashboardServer(options) {
30023
30120
  }
30024
30121
  }
30025
30122
  };
30026
- const deps = {
30027
- sources: options.sources,
30028
- authToken: options.authToken,
30029
- approvals: options.approvals,
30030
- onEvent
30031
- };
30123
+ let v11Bindings = null;
30124
+ let v11LoopbackAutoAuth = false;
30032
30125
  const server = http.createServer(async (req, res) => {
30033
30126
  try {
30127
+ const deps = {
30128
+ sources: options.sources,
30129
+ authToken: options.authToken,
30130
+ approvals: options.approvals,
30131
+ onEvent,
30132
+ v11Bindings,
30133
+ loopbackAutoAuth: v11LoopbackAutoAuth
30134
+ };
30034
30135
  const served = await handleRequest(deps, req, res);
30035
30136
  if (!served) {
30036
30137
  res.writeHead(404, { "Content-Type": "application/json" });
@@ -30068,7 +30169,13 @@ async function startDashboardServer(options) {
30068
30169
  publishActivity: (entry) => publish({ type: "activity", data: entry }),
30069
30170
  publishApproval: (approval) => publish({ type: "approval", data: approval }),
30070
30171
  publishInbox: (item) => publish({ type: "inbox", data: item }),
30071
- publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot })
30172
+ publishAgentStatus: (snapshot) => publish({ type: "agent_status", data: snapshot }),
30173
+ setV11Bindings: (bindings) => {
30174
+ v11Bindings = bindings;
30175
+ },
30176
+ setV11LoopbackAutoAuth: (enabled) => {
30177
+ v11LoopbackAutoAuth = enabled;
30178
+ }
30072
30179
  };
30073
30180
  }
30074
30181
  var DEFAULT_PORT, DEFAULT_HOST;
@@ -31332,12 +31439,14 @@ async function runWrap(options, deps = {}) {
31332
31439
  const storagePath = resolveStoragePath();
31333
31440
  let passphraseLocation;
31334
31441
  let passphraseSource;
31442
+ let passphraseValue;
31335
31443
  if (options.passphrase) {
31336
31444
  try {
31337
31445
  const persist = deps.persistPassphrase ?? ((value) => persistUserProvidedPassphrase(value, { storagePath }));
31338
31446
  const persisted = await persist(options.passphrase);
31339
31447
  passphraseLocation = persisted.location;
31340
31448
  passphraseSource = persisted.source;
31449
+ passphraseValue = options.passphrase;
31341
31450
  console.error(
31342
31451
  `
31343
31452
  \u{1F510} Persisted user-supplied passphrase (${persisted.location}).`
@@ -31355,12 +31464,14 @@ async function runWrap(options, deps = {}) {
31355
31464
  } else if (process.env.SANCTUARY_PASSPHRASE) {
31356
31465
  passphraseLocation = "SANCTUARY_PASSPHRASE";
31357
31466
  passphraseSource = "env";
31467
+ passphraseValue = process.env.SANCTUARY_PASSPHRASE;
31358
31468
  } else {
31359
31469
  try {
31360
31470
  const resolve5 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
31361
31471
  const resolved = await resolve5();
31362
31472
  passphraseLocation = resolved.location;
31363
31473
  passphraseSource = resolved.source;
31474
+ passphraseValue = resolved.value;
31364
31475
  if (resolved.source === "generated") {
31365
31476
  console.error(
31366
31477
  `
@@ -31396,6 +31507,27 @@ async function runWrap(options, deps = {}) {
31396
31507
  );
31397
31508
  }
31398
31509
  await promises.mkdir(storagePath, { recursive: true, mode: 448 });
31510
+ if (passphraseSource === "generated" && passphraseValue !== void 0) {
31511
+ try {
31512
+ await disclosePassphrase({
31513
+ passphrase: passphraseValue,
31514
+ storagePath,
31515
+ fortressId: fortressIdFromStoragePath(storagePath),
31516
+ // --no-open (CI / scripted) or non-TTY stdin both skip the prompt
31517
+ // the same way init's --no-confirm does. Operator who scripted the
31518
+ // call still gets the banner + the file; they will not see a hang.
31519
+ mode: options.noOpen || process.stdin.isTTY !== true ? "no-confirm" : "interactive"
31520
+ });
31521
+ } catch (err) {
31522
+ if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
31523
+ console.error(`
31524
+ Sanctuary wrap: ${err.message}
31525
+ `);
31526
+ process.exit(2);
31527
+ }
31528
+ throw err;
31529
+ }
31530
+ }
31399
31531
  const profile = createWrapProfile(upstreamServers);
31400
31532
  const profilePath = path.join(storagePath, "cocoon-profile.json");
31401
31533
  await promises.writeFile(profilePath, JSON.stringify(profile, null, 2), {
@@ -31418,6 +31550,13 @@ async function runWrap(options, deps = {}) {
31418
31550
  if (process.env.SANCTUARY_DASHBOARD_ENABLED) {
31419
31551
  sanctuaryEnv.SANCTUARY_DASHBOARD_ENABLED = process.env.SANCTUARY_DASHBOARD_ENABLED;
31420
31552
  }
31553
+ if (options.fortress) {
31554
+ sanctuaryEnv.SANCTUARY_FORTRESS_PATH = path.resolve(options.fortress);
31555
+ } else if (process.env.SANCTUARY_FORTRESS_PATH) {
31556
+ sanctuaryEnv.SANCTUARY_FORTRESS_PATH = path.resolve(
31557
+ process.env.SANCTUARY_FORTRESS_PATH
31558
+ );
31559
+ }
31421
31560
  const rewrite = deps.rewriteConfig ?? rewriteConfigForCocoon;
31422
31561
  await rewrite(
31423
31562
  agentConfig,
@@ -31445,6 +31584,40 @@ async function runWrap(options, deps = {}) {
31445
31584
  authToken,
31446
31585
  readPackageVersion()
31447
31586
  );
31587
+ if (passphraseValue !== void 0) {
31588
+ try {
31589
+ const v11Storage = new FilesystemStorage(`${storagePath}/state`);
31590
+ let existingParams;
31591
+ try {
31592
+ const raw = await v11Storage.read("_meta", "key-params");
31593
+ if (raw) {
31594
+ existingParams = JSON.parse(bytesToString(raw));
31595
+ }
31596
+ } catch {
31597
+ }
31598
+ const derived = await deriveMasterKey(passphraseValue, existingParams);
31599
+ if (!existingParams) {
31600
+ await v11Storage.write(
31601
+ "_meta",
31602
+ "key-params",
31603
+ stringToBytes(JSON.stringify(derived.params))
31604
+ );
31605
+ }
31606
+ const wrapAuditLog = new AuditLog(v11Storage, derived.key);
31607
+ dashboard.setV11Bindings(
31608
+ buildV11Bindings({
31609
+ identityId: `fortress:${storagePath}`,
31610
+ fortressId: fortressIdFromStoragePath(storagePath),
31611
+ auditLog: wrapAuditLog
31612
+ })
31613
+ );
31614
+ dashboard.setV11LoopbackAutoAuth(true);
31615
+ } catch (err) {
31616
+ console.error(
31617
+ ` Note: v1.1 dashboard surfaces unavailable on wrap URL (${err.message}). Run \`sanctuary dashboard\` to reach them.`
31618
+ );
31619
+ }
31620
+ }
31448
31621
  const dashboardUrl = `${dashboard.url}?token=${authToken}`;
31449
31622
  const webhookCallbackPortRaw = process.env.SANCTUARY_WEBHOOK_CALLBACK_PORT;
31450
31623
  const webhookCallbackPort = webhookCallbackPortRaw ? parseInt(webhookCallbackPortRaw, 10) : void 0;
@@ -31807,9 +31980,15 @@ var init_cli2 = __esm({
31807
31980
  init_config_reader();
31808
31981
  init_passphrase();
31809
31982
  init_dashboard2();
31983
+ init_wiring();
31984
+ init_filesystem();
31985
+ init_key_derivation();
31986
+ init_encoding();
31987
+ init_audit_log();
31810
31988
  init_config();
31811
31989
  init_paths();
31812
31990
  init_runtime();
31991
+ init_recovery_key_disclosure();
31813
31992
  COCOON_GOVERNOR_DEFAULTS = {
31814
31993
  volume_limit: 200,
31815
31994
  rate_limit_per_tool: 20,
@@ -35201,10 +35380,10 @@ __export(dashboard_standalone_exports, {
35201
35380
  renderTenantDiscoveryHint: () => renderTenantDiscoveryHint,
35202
35381
  startStandaloneDashboard: () => startStandaloneDashboard
35203
35382
  });
35204
- async function discoverableSubTenants(currentStoragePath) {
35383
+ async function discoverableSubTenants(currentStoragePath, discoveryOptions) {
35205
35384
  let all;
35206
35385
  try {
35207
- all = await discoverTenants();
35386
+ all = await discoverTenants(discoveryOptions);
35208
35387
  } catch {
35209
35388
  return [];
35210
35389
  }
@@ -35607,6 +35786,9 @@ var { version: PKG_VERSION4 } = require4("../package.json");
35607
35786
  async function main() {
35608
35787
  const args = process.argv.slice(2);
35609
35788
  let passphrase = process.env.SANCTUARY_PASSPHRASE;
35789
+ if (process.env.SANCTUARY_FORTRESS_PATH && !process.env.SANCTUARY_STORAGE_PATH) {
35790
+ process.env.SANCTUARY_STORAGE_PATH = process.env.SANCTUARY_FORTRESS_PATH;
35791
+ }
35610
35792
  if (args[0] === "dashboard") {
35611
35793
  await runStandaloneDashboard(args.slice(1));
35612
35794
  return;