@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 +444 -89
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +444 -89
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +161 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +162 -37
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
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
|
|
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
|
-
|
|
11727
|
-
|
|
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((
|
|
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) =>
|
|
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((
|
|
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
|
-
|
|
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((
|
|
17177
|
-
this.httpServer.close(() =>
|
|
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((
|
|
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
|
-
|
|
17269
|
+
resolve8(response);
|
|
17206
17270
|
}, this.config.timeout_seconds * 1e3);
|
|
17207
17271
|
const pending = {
|
|
17208
17272
|
id,
|
|
17209
17273
|
request,
|
|
17210
|
-
resolve:
|
|
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((
|
|
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
|
-
|
|
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((
|
|
18112
|
-
this.callbackServer.close(() =>
|
|
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((
|
|
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
|
-
|
|
18198
|
+
resolve8(response);
|
|
18135
18199
|
}, this.config.timeout_seconds * 1e3);
|
|
18136
18200
|
const pending = {
|
|
18137
18201
|
id,
|
|
18138
18202
|
request,
|
|
18139
|
-
resolve:
|
|
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:
|
|
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
|
|
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}` :
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
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((
|
|
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
|
-
|
|
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((
|
|
35901
|
-
server.close((err) => err ? reject(err) :
|
|
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((
|
|
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
|
|
37414
|
-
const resolved = await
|
|
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((
|
|
37937
|
+
await new Promise((resolve8) => {
|
|
37770
37938
|
const child = child_process.spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
37771
|
-
child.on("error", () =>
|
|
37939
|
+
child.on("error", () => resolve8());
|
|
37772
37940
|
child.on("spawn", () => {
|
|
37773
37941
|
child.unref();
|
|
37774
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
38919
|
+
write3(`${i} 0 obj
|
|
38752
38920
|
${objectBodies[i]}
|
|
38753
38921
|
endobj
|
|
38754
38922
|
`);
|
|
38755
38923
|
}
|
|
38756
38924
|
const xrefPos = bytePos;
|
|
38757
|
-
|
|
38925
|
+
write3(`xref
|
|
38758
38926
|
0 ${totalObjects + 1}
|
|
38759
38927
|
`);
|
|
38760
|
-
|
|
38928
|
+
write3("0000000000 65535 f \n");
|
|
38761
38929
|
for (let i = 1; i <= totalObjects; i++) {
|
|
38762
|
-
|
|
38930
|
+
write3(`${offsets[i].toString().padStart(10, "0")} 00000 n
|
|
38763
38931
|
`);
|
|
38764
38932
|
}
|
|
38765
|
-
|
|
38933
|
+
write3(`trailer
|
|
38766
38934
|
<< /Size ${totalObjects + 1} /Root 1 0 R >>
|
|
38767
38935
|
`);
|
|
38768
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
40860
|
+
resolve8({ running: true, status, reason: null });
|
|
40527
40861
|
} else {
|
|
40528
|
-
|
|
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
|
-
|
|
40872
|
+
resolve8({ running: false, status: null, reason: "timeout" });
|
|
40539
40873
|
});
|
|
40540
40874
|
req.on("error", (err) => {
|
|
40541
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
--
|
|
40931
|
-
|
|
40932
|
-
|
|
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((
|
|
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) =>
|
|
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((
|
|
41251
|
-
this.waiters.push(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
41664
|
-
server.close((err) => err ? reject(err) :
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42438
|
+
resolve8(latest);
|
|
42092
42439
|
} else {
|
|
42093
|
-
|
|
42440
|
+
resolve8(null);
|
|
42094
42441
|
}
|
|
42095
42442
|
} catch {
|
|
42096
|
-
|
|
42443
|
+
resolve8(null);
|
|
42097
42444
|
}
|
|
42098
42445
|
});
|
|
42099
42446
|
}
|
|
42100
42447
|
);
|
|
42101
|
-
req.on("error", () =>
|
|
42448
|
+
req.on("error", () => resolve8(null));
|
|
42102
42449
|
req.on("timeout", () => {
|
|
42103
42450
|
req.destroy();
|
|
42104
|
-
|
|
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
|
|