protect-mcp 0.6.0 → 0.6.2
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/README.md +42 -1
- package/dist/{chunk-SPHLVRJ2.mjs → chunk-3YCKR72H.mjs} +223 -4
- package/dist/{chunk-BYYWYSHM.mjs → chunk-PLKRTBDR.mjs} +15 -3
- package/dist/{chunk-GQWJCHQV.mjs → chunk-S4ICHNSP.mjs} +2 -2
- package/dist/{chunk-YTBC72JJ.mjs → chunk-UV53U6D4.mjs} +69 -25
- package/dist/cli.js +305 -31
- package/dist/cli.mjs +4 -4
- package/dist/hook-server.js +283 -28
- package/dist/hook-server.mjs +2 -2
- package/dist/{http-transport-LNBENGXD.mjs → http-transport-MO32ESHZ.mjs} +2 -2
- package/dist/index.d.mts +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +308 -30
- package/dist/index.mjs +10 -4
- package/package.json +3 -2
package/dist/hook-server.js
CHANGED
|
@@ -205,11 +205,40 @@ async function isCedarAvailable() {
|
|
|
205
205
|
var import_node_fs2 = require("fs");
|
|
206
206
|
var signerState = null;
|
|
207
207
|
var artifactsModule = null;
|
|
208
|
+
var signingConfigured = false;
|
|
209
|
+
var signingInitError = null;
|
|
208
210
|
async function initSigning(config) {
|
|
209
211
|
const warnings = [];
|
|
212
|
+
signerState = null;
|
|
213
|
+
artifactsModule = null;
|
|
214
|
+
signingConfigured = Boolean(config && config.enabled !== false);
|
|
215
|
+
signingInitError = null;
|
|
210
216
|
if (!config || config.enabled === false) {
|
|
211
217
|
return warnings;
|
|
212
218
|
}
|
|
219
|
+
if (!config.key_path) {
|
|
220
|
+
signingInitError = "signing enabled but key_path is not configured";
|
|
221
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
222
|
+
return warnings;
|
|
223
|
+
}
|
|
224
|
+
if (!(0, import_node_fs2.existsSync)(config.key_path)) {
|
|
225
|
+
signingInitError = `key file not found at ${config.key_path}`;
|
|
226
|
+
warnings.push(`signing: ${signingInitError} \u2014 run "protect-mcp init" to generate`);
|
|
227
|
+
return warnings;
|
|
228
|
+
}
|
|
229
|
+
let keyData;
|
|
230
|
+
try {
|
|
231
|
+
keyData = JSON.parse((0, import_node_fs2.readFileSync)(config.key_path, "utf-8"));
|
|
232
|
+
if (!keyData.privateKey || !keyData.publicKey) {
|
|
233
|
+
signingInitError = "key file missing privateKey or publicKey fields";
|
|
234
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
235
|
+
return warnings;
|
|
236
|
+
}
|
|
237
|
+
} catch (err) {
|
|
238
|
+
signingInitError = `failed to load key file: ${err instanceof Error ? err.message : err}`;
|
|
239
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
240
|
+
return warnings;
|
|
241
|
+
}
|
|
213
242
|
try {
|
|
214
243
|
const moduleName = "@veritasacta/artifacts";
|
|
215
244
|
artifactsModule = await import(
|
|
@@ -217,37 +246,48 @@ async function initSigning(config) {
|
|
|
217
246
|
moduleName
|
|
218
247
|
);
|
|
219
248
|
} catch {
|
|
220
|
-
|
|
249
|
+
signingInitError = "@veritasacta/artifacts not available";
|
|
250
|
+
warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
|
|
221
251
|
return warnings;
|
|
222
252
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
signerState = {
|
|
235
|
-
privateKey: keyData.privateKey,
|
|
236
|
-
publicKey: keyData.publicKey,
|
|
237
|
-
kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
|
|
238
|
-
issuer: config.issuer || keyData.issuer || "protect-mcp"
|
|
239
|
-
};
|
|
240
|
-
} catch (err) {
|
|
241
|
-
warnings.push(`signing: failed to load key file: ${err instanceof Error ? err.message : err}`);
|
|
242
|
-
}
|
|
253
|
+
try {
|
|
254
|
+
signerState = {
|
|
255
|
+
privateKey: keyData.privateKey,
|
|
256
|
+
publicKey: keyData.publicKey,
|
|
257
|
+
kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
|
|
258
|
+
issuer: config.issuer || keyData.issuer || "protect-mcp"
|
|
259
|
+
};
|
|
260
|
+
} catch (err) {
|
|
261
|
+
signingInitError = `failed to initialize signer: ${err instanceof Error ? err.message : err}`;
|
|
262
|
+
artifactsModule = null;
|
|
263
|
+
warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
|
|
243
264
|
}
|
|
244
265
|
return warnings;
|
|
245
266
|
}
|
|
246
267
|
function signDecision(entry) {
|
|
268
|
+
const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
|
|
269
|
+
if (signingConfigured && signingInitError) {
|
|
270
|
+
return {
|
|
271
|
+
ok: false,
|
|
272
|
+
signed: null,
|
|
273
|
+
artifact_type: artifactType,
|
|
274
|
+
warning: `signing initialization failed: ${signingInitError}`,
|
|
275
|
+
error: signingInitError
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (signingConfigured && (!signerState || !artifactsModule)) {
|
|
279
|
+
const error = "signing was configured but no signer is ready";
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
signed: null,
|
|
283
|
+
artifact_type: artifactType,
|
|
284
|
+
warning: error,
|
|
285
|
+
error
|
|
286
|
+
};
|
|
287
|
+
}
|
|
247
288
|
if (!signerState || !artifactsModule) {
|
|
248
|
-
return { signed: null, artifact_type: "none" };
|
|
289
|
+
return { ok: false, signed: null, artifact_type: "none" };
|
|
249
290
|
}
|
|
250
|
-
const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
|
|
251
291
|
try {
|
|
252
292
|
const payload = {
|
|
253
293
|
tool: entry.tool,
|
|
@@ -288,14 +328,18 @@ function signDecision(entry) {
|
|
|
288
328
|
}
|
|
289
329
|
);
|
|
290
330
|
return {
|
|
331
|
+
ok: true,
|
|
291
332
|
signed: JSON.stringify(result.artifact),
|
|
292
333
|
artifact_type: artifactType
|
|
293
334
|
};
|
|
294
335
|
} catch (err) {
|
|
336
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
295
337
|
return {
|
|
338
|
+
ok: false,
|
|
296
339
|
signed: null,
|
|
297
340
|
artifact_type: artifactType,
|
|
298
|
-
warning: `signing failed: ${
|
|
341
|
+
warning: `signing failed: ${message}`,
|
|
342
|
+
error: message
|
|
299
343
|
};
|
|
300
344
|
}
|
|
301
345
|
}
|
|
@@ -308,7 +352,7 @@ function getSignerInfo() {
|
|
|
308
352
|
};
|
|
309
353
|
}
|
|
310
354
|
function isSigningEnabled() {
|
|
311
|
-
return signerState !== null && artifactsModule !== null;
|
|
355
|
+
return signingConfigured && signingInitError === null && signerState !== null && artifactsModule !== null;
|
|
312
356
|
}
|
|
313
357
|
|
|
314
358
|
// src/policy.ts
|
|
@@ -418,6 +462,154 @@ var ReceiptBuffer = class {
|
|
|
418
462
|
}
|
|
419
463
|
};
|
|
420
464
|
|
|
465
|
+
// src/scopeblind-bridge.ts
|
|
466
|
+
var DEFAULT_BASE = "https://scopeblind.com";
|
|
467
|
+
var FLUSH_INTERVAL_MS = 5e3;
|
|
468
|
+
var BATCH_MAX = 128;
|
|
469
|
+
var BRASS_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
470
|
+
var ScopeBlindBridge = class {
|
|
471
|
+
token;
|
|
472
|
+
base;
|
|
473
|
+
tenantOverride;
|
|
474
|
+
cachedProof = null;
|
|
475
|
+
queue = [];
|
|
476
|
+
flushTimer = null;
|
|
477
|
+
stats;
|
|
478
|
+
shuttingDown = false;
|
|
479
|
+
constructor(env = process.env) {
|
|
480
|
+
this.token = env.SCOPEBLIND_TOKEN || null;
|
|
481
|
+
this.base = (env.SCOPEBLIND_BASE || DEFAULT_BASE).replace(/\/$/, "");
|
|
482
|
+
this.tenantOverride = env.SCOPEBLIND_TENANT || null;
|
|
483
|
+
this.stats = {
|
|
484
|
+
enabled: Boolean(this.token),
|
|
485
|
+
tenant_slug: this.tenantOverride,
|
|
486
|
+
forwarded_total: 0,
|
|
487
|
+
rejected_total: 0,
|
|
488
|
+
last_flush_at: null,
|
|
489
|
+
last_error: null
|
|
490
|
+
};
|
|
491
|
+
if (this.enabled()) {
|
|
492
|
+
this.flushTimer = setInterval(() => {
|
|
493
|
+
void this.flush();
|
|
494
|
+
}, FLUSH_INTERVAL_MS);
|
|
495
|
+
if (typeof this.flushTimer === "object" && this.flushTimer && "unref" in this.flushTimer) {
|
|
496
|
+
this.flushTimer.unref?.();
|
|
497
|
+
}
|
|
498
|
+
process.on("beforeExit", () => {
|
|
499
|
+
void this.shutdown();
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
enabled() {
|
|
504
|
+
return Boolean(this.token);
|
|
505
|
+
}
|
|
506
|
+
/** Push a signed receipt into the queue. Non-blocking. */
|
|
507
|
+
forward(signedReceipt) {
|
|
508
|
+
if (!this.enabled() || this.shuttingDown) return;
|
|
509
|
+
this.queue.push(signedReceipt);
|
|
510
|
+
if (this.queue.length >= BATCH_MAX) void this.flush();
|
|
511
|
+
}
|
|
512
|
+
/** Flush the queue. Safe to call concurrently. */
|
|
513
|
+
async flush() {
|
|
514
|
+
if (!this.enabled() || this.queue.length === 0) return;
|
|
515
|
+
const batch = this.queue.splice(0, BATCH_MAX);
|
|
516
|
+
try {
|
|
517
|
+
const proof = await this.ensureBrassProof();
|
|
518
|
+
const slug = this.tenantOverride || proof?.tenant_id;
|
|
519
|
+
if (!slug) {
|
|
520
|
+
this.queue.unshift(...batch);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
this.stats.tenant_slug = slug;
|
|
524
|
+
const res = await fetch(`${this.base}/fn/console/${slug}/receipts`, {
|
|
525
|
+
method: "POST",
|
|
526
|
+
headers: {
|
|
527
|
+
"content-type": "application/json",
|
|
528
|
+
authorization: `Bearer ${this.token}`,
|
|
529
|
+
"user-agent": "protect-mcp/scopeblind-bridge"
|
|
530
|
+
},
|
|
531
|
+
body: JSON.stringify({ receipts: batch })
|
|
532
|
+
});
|
|
533
|
+
if (!res.ok) {
|
|
534
|
+
const errBody = await res.text().catch(() => "");
|
|
535
|
+
this.stats.last_error = `HTTP ${res.status} ${errBody.slice(0, 160)}`;
|
|
536
|
+
this.stats.rejected_total += batch.length;
|
|
537
|
+
if (res.status >= 500 && res.status !== 503) {
|
|
538
|
+
this.queue.unshift(...batch);
|
|
539
|
+
}
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const body = await res.json().catch(() => ({}));
|
|
543
|
+
this.stats.forwarded_total += body?.accepted ?? batch.length;
|
|
544
|
+
this.stats.rejected_total += body?.rejected ?? 0;
|
|
545
|
+
this.stats.last_flush_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
546
|
+
this.stats.last_error = null;
|
|
547
|
+
} catch (err) {
|
|
548
|
+
this.stats.last_error = String(err?.message || err);
|
|
549
|
+
this.queue.unshift(...batch);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/** Exchange SCOPEBLIND_TOKEN for a BRASS-v2 proof; refresh near expiry. */
|
|
553
|
+
async ensureBrassProof() {
|
|
554
|
+
if (!this.token) return null;
|
|
555
|
+
const now = Date.now();
|
|
556
|
+
if (this.cachedProof && Date.parse(this.cachedProof.expires_at) - now > BRASS_REFRESH_MARGIN_MS) {
|
|
557
|
+
return this.cachedProof;
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
const res = await fetch(`${this.base}/fn/brass/issue`, {
|
|
561
|
+
method: "POST",
|
|
562
|
+
headers: {
|
|
563
|
+
"content-type": "application/json",
|
|
564
|
+
"user-agent": "protect-mcp/scopeblind-bridge"
|
|
565
|
+
},
|
|
566
|
+
body: JSON.stringify({
|
|
567
|
+
token: this.token,
|
|
568
|
+
scope: "protect-mcp-receipt-emit",
|
|
569
|
+
ttl_seconds: 3600
|
|
570
|
+
})
|
|
571
|
+
});
|
|
572
|
+
if (!res.ok) {
|
|
573
|
+
const text = await res.text().catch(() => "");
|
|
574
|
+
this.stats.last_error = `brass-issue: HTTP ${res.status} ${text.slice(0, 160)}`;
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
const body = await res.json();
|
|
578
|
+
if (!body?.auth_proof) {
|
|
579
|
+
this.stats.last_error = "brass-issue: missing auth_proof in response";
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
this.cachedProof = body.auth_proof;
|
|
583
|
+
return this.cachedProof;
|
|
584
|
+
} catch (err) {
|
|
585
|
+
this.stats.last_error = `brass-issue: ${err?.message || err}`;
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Return a snapshot of bridge stats. Useful for `protect-mcp scopeblind status`.
|
|
591
|
+
*/
|
|
592
|
+
getStats() {
|
|
593
|
+
return {
|
|
594
|
+
...this.stats,
|
|
595
|
+
queued: this.queue.length,
|
|
596
|
+
brass_proof_expires_at: this.cachedProof?.expires_at || null
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/** Flush remaining receipts and stop the interval. Called on process exit. */
|
|
600
|
+
async shutdown() {
|
|
601
|
+
if (this.shuttingDown) return;
|
|
602
|
+
this.shuttingDown = true;
|
|
603
|
+
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
604
|
+
if (this.queue.length > 0) await this.flush();
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
var singleton = null;
|
|
608
|
+
function getScopeBlindBridge() {
|
|
609
|
+
if (!singleton) singleton = new ScopeBlindBridge();
|
|
610
|
+
return singleton;
|
|
611
|
+
}
|
|
612
|
+
|
|
421
613
|
// src/hook-server.ts
|
|
422
614
|
var DEFAULT_PORT = 9377;
|
|
423
615
|
var LOG_FILE = ".protect-mcp-log.jsonl";
|
|
@@ -626,7 +818,7 @@ async function handlePreToolUse(input, state) {
|
|
|
626
818
|
const hookLatency = Date.now() - hookStart;
|
|
627
819
|
const denyKey = `${toolName}:${input.sessionId || "default"}`;
|
|
628
820
|
state.denyCounter.delete(denyKey);
|
|
629
|
-
emitDecisionLog(state, {
|
|
821
|
+
const emit = emitDecisionLog(state, {
|
|
630
822
|
tool: toolName,
|
|
631
823
|
decision: "allow",
|
|
632
824
|
reason_code: state.cedarPolicies ? "cedar_allow" : state.jsonPolicy ? "policy_allow" : "observe_mode",
|
|
@@ -638,6 +830,15 @@ async function handlePreToolUse(input, state) {
|
|
|
638
830
|
sandbox_state: detectSandboxState(),
|
|
639
831
|
plan_receipt_id: state.activePlanReceiptId || void 0
|
|
640
832
|
});
|
|
833
|
+
if (state.enforce && emit.signingFailed) {
|
|
834
|
+
return {
|
|
835
|
+
hookSpecificOutput: {
|
|
836
|
+
hookEventName: "PreToolUse",
|
|
837
|
+
permissionDecision: "deny",
|
|
838
|
+
permissionDecisionReason: `[ScopeBlind] "${toolName}" was blocked because its receipt could not be signed. Failing closed: a governed action that cannot be proven is not allowed.`
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
}
|
|
641
842
|
return {};
|
|
642
843
|
}
|
|
643
844
|
async function handlePostToolUse(input, state) {
|
|
@@ -885,11 +1086,35 @@ function emitDecisionLog(state, entry) {
|
|
|
885
1086
|
} catch {
|
|
886
1087
|
}
|
|
887
1088
|
state.receiptBuffer.add(log.request_id, signed.signed);
|
|
888
|
-
|
|
889
|
-
|
|
1089
|
+
try {
|
|
1090
|
+
const bridge = getScopeBlindBridge();
|
|
1091
|
+
if (bridge.enabled()) {
|
|
1092
|
+
const parsed = typeof signed.signed === "string" ? JSON.parse(signed.signed) : signed.signed;
|
|
1093
|
+
bridge.forward(parsed);
|
|
1094
|
+
}
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
process.stderr.write(`[PROTECT_MCP] ScopeBlind forward error: ${err instanceof Error ? err.message : err}
|
|
1097
|
+
`);
|
|
1098
|
+
}
|
|
1099
|
+
} else if (signed.error) {
|
|
1100
|
+
const tombstone = JSON.stringify({
|
|
1101
|
+
type: "scopeblind.signing_failure.v1",
|
|
1102
|
+
request_id: log.request_id,
|
|
1103
|
+
tool: log.tool,
|
|
1104
|
+
decision: log.decision,
|
|
1105
|
+
error: signed.error,
|
|
1106
|
+
at: new Date(log.timestamp).toISOString()
|
|
1107
|
+
});
|
|
1108
|
+
try {
|
|
1109
|
+
(0, import_node_fs5.appendFileSync)(state.receiptFilePath, tombstone + "\n");
|
|
1110
|
+
} catch {
|
|
1111
|
+
}
|
|
1112
|
+
process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
|
|
890
1113
|
`);
|
|
1114
|
+
return { signingFailed: true };
|
|
891
1115
|
}
|
|
892
1116
|
}
|
|
1117
|
+
return { signingFailed: false };
|
|
893
1118
|
}
|
|
894
1119
|
async function routeHookEvent(input, state) {
|
|
895
1120
|
switch (input.hookEventName) {
|
|
@@ -1231,3 +1456,33 @@ function normalizeHookInput(raw) {
|
|
|
1231
1456
|
0 && (module.exports = {
|
|
1232
1457
|
startHookServer
|
|
1233
1458
|
});
|
|
1459
|
+
/**
|
|
1460
|
+
* scopeblind-bridge.ts
|
|
1461
|
+
*
|
|
1462
|
+
* Optional bridge between protect-mcp (local, MIT) and a paid ScopeBlind
|
|
1463
|
+
* tenant. When SCOPEBLIND_TOKEN is set in the environment, every signed
|
|
1464
|
+
* receipt that protect-mcp emits also gets forwarded to the tenant's
|
|
1465
|
+
* dashboard at https://scopeblind.com/console/<slug>.
|
|
1466
|
+
*
|
|
1467
|
+
* Lifecycle:
|
|
1468
|
+
* 1. On first use, exchange SCOPEBLIND_TOKEN for a short-lived BRASS-v2
|
|
1469
|
+
* auth proof from /fn/brass/issue. Cache the proof in memory until
|
|
1470
|
+
* ~5 minutes before expiry, then refresh.
|
|
1471
|
+
* 2. As receipts are emitted by hook-server.ts, push them into an
|
|
1472
|
+
* in-memory batch queue.
|
|
1473
|
+
* 3. Flush the queue every 5s (or when it reaches 128 receipts) by POSTing
|
|
1474
|
+
* to /fn/console/<slug>/receipts with Bearer SCOPEBLIND_TOKEN.
|
|
1475
|
+
*
|
|
1476
|
+
* Failure mode: forward errors NEVER throw upstream. protect-mcp continues
|
|
1477
|
+
* to mint and persist receipts locally regardless of dashboard availability.
|
|
1478
|
+
* The bridge logs failures to stderr (best-effort) and retries on the next
|
|
1479
|
+
* flush.
|
|
1480
|
+
*
|
|
1481
|
+
* Configuration:
|
|
1482
|
+
* SCOPEBLIND_TOKEN Tenant bearer token (from welcome email).
|
|
1483
|
+
* SCOPEBLIND_TENANT Optional slug override. By default we discover
|
|
1484
|
+
* the slug from the BRASS proof's tenant_id.
|
|
1485
|
+
* SCOPEBLIND_BASE Defaults to https://scopeblind.com.
|
|
1486
|
+
*
|
|
1487
|
+
* @license MIT
|
|
1488
|
+
*/
|
package/dist/hook-server.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -803,9 +803,9 @@ declare function validateCredentials(credentials: Record<string, CredentialConfi
|
|
|
803
803
|
* Produces signed v2 artifact receipts for tool call decisions.
|
|
804
804
|
* Uses @veritasacta/artifacts as a required dependency (Sprint 2+).
|
|
805
805
|
*
|
|
806
|
-
* If signing is configured, every decision
|
|
807
|
-
*
|
|
808
|
-
*
|
|
806
|
+
* If signing is configured, every decision must produce a signed artifact.
|
|
807
|
+
* Initialization and signing failures are returned as explicit errors so the
|
|
808
|
+
* enforce path can deny rather than silently proceeding without evidence.
|
|
809
809
|
*/
|
|
810
810
|
|
|
811
811
|
/**
|
|
@@ -827,9 +827,11 @@ declare function initSigning(config: SigningConfig | undefined): Promise<string[
|
|
|
827
827
|
* @standard RFC 8032 (Ed25519), RFC 8785 (JCS)
|
|
828
828
|
*/
|
|
829
829
|
declare function signDecision(entry: DecisionLog): {
|
|
830
|
+
ok: boolean;
|
|
830
831
|
signed: string | null;
|
|
831
832
|
artifact_type: string;
|
|
832
833
|
warning?: string;
|
|
834
|
+
error?: string;
|
|
833
835
|
};
|
|
834
836
|
/**
|
|
835
837
|
* Get the signer's public key info for discovery/verification.
|
|
@@ -2911,4 +2913,73 @@ declare function confidentialInference(_prompt: string, _config: ConfidentialInf
|
|
|
2911
2913
|
receipt: Record<string, unknown>;
|
|
2912
2914
|
}>;
|
|
2913
2915
|
|
|
2914
|
-
|
|
2916
|
+
/**
|
|
2917
|
+
* scopeblind-bridge.ts
|
|
2918
|
+
*
|
|
2919
|
+
* Optional bridge between protect-mcp (local, MIT) and a paid ScopeBlind
|
|
2920
|
+
* tenant. When SCOPEBLIND_TOKEN is set in the environment, every signed
|
|
2921
|
+
* receipt that protect-mcp emits also gets forwarded to the tenant's
|
|
2922
|
+
* dashboard at https://scopeblind.com/console/<slug>.
|
|
2923
|
+
*
|
|
2924
|
+
* Lifecycle:
|
|
2925
|
+
* 1. On first use, exchange SCOPEBLIND_TOKEN for a short-lived BRASS-v2
|
|
2926
|
+
* auth proof from /fn/brass/issue. Cache the proof in memory until
|
|
2927
|
+
* ~5 minutes before expiry, then refresh.
|
|
2928
|
+
* 2. As receipts are emitted by hook-server.ts, push them into an
|
|
2929
|
+
* in-memory batch queue.
|
|
2930
|
+
* 3. Flush the queue every 5s (or when it reaches 128 receipts) by POSTing
|
|
2931
|
+
* to /fn/console/<slug>/receipts with Bearer SCOPEBLIND_TOKEN.
|
|
2932
|
+
*
|
|
2933
|
+
* Failure mode: forward errors NEVER throw upstream. protect-mcp continues
|
|
2934
|
+
* to mint and persist receipts locally regardless of dashboard availability.
|
|
2935
|
+
* The bridge logs failures to stderr (best-effort) and retries on the next
|
|
2936
|
+
* flush.
|
|
2937
|
+
*
|
|
2938
|
+
* Configuration:
|
|
2939
|
+
* SCOPEBLIND_TOKEN Tenant bearer token (from welcome email).
|
|
2940
|
+
* SCOPEBLIND_TENANT Optional slug override. By default we discover
|
|
2941
|
+
* the slug from the BRASS proof's tenant_id.
|
|
2942
|
+
* SCOPEBLIND_BASE Defaults to https://scopeblind.com.
|
|
2943
|
+
*
|
|
2944
|
+
* @license MIT
|
|
2945
|
+
*/
|
|
2946
|
+
interface BridgeStats {
|
|
2947
|
+
enabled: boolean;
|
|
2948
|
+
tenant_slug: string | null;
|
|
2949
|
+
forwarded_total: number;
|
|
2950
|
+
rejected_total: number;
|
|
2951
|
+
last_flush_at: string | null;
|
|
2952
|
+
last_error: string | null;
|
|
2953
|
+
}
|
|
2954
|
+
declare class ScopeBlindBridge {
|
|
2955
|
+
private readonly token;
|
|
2956
|
+
private readonly base;
|
|
2957
|
+
private readonly tenantOverride;
|
|
2958
|
+
private cachedProof;
|
|
2959
|
+
private queue;
|
|
2960
|
+
private flushTimer;
|
|
2961
|
+
private stats;
|
|
2962
|
+
private shuttingDown;
|
|
2963
|
+
constructor(env?: Record<string, string | undefined>);
|
|
2964
|
+
enabled(): boolean;
|
|
2965
|
+
/** Push a signed receipt into the queue. Non-blocking. */
|
|
2966
|
+
forward(signedReceipt: any): void;
|
|
2967
|
+
/** Flush the queue. Safe to call concurrently. */
|
|
2968
|
+
flush(): Promise<void>;
|
|
2969
|
+
/** Exchange SCOPEBLIND_TOKEN for a BRASS-v2 proof; refresh near expiry. */
|
|
2970
|
+
private ensureBrassProof;
|
|
2971
|
+
/**
|
|
2972
|
+
* Return a snapshot of bridge stats. Useful for `protect-mcp scopeblind status`.
|
|
2973
|
+
*/
|
|
2974
|
+
getStats(): BridgeStats & {
|
|
2975
|
+
queued: number;
|
|
2976
|
+
brass_proof_expires_at: string | null;
|
|
2977
|
+
};
|
|
2978
|
+
/** Flush remaining receipts and stop the interval. Called on process exit. */
|
|
2979
|
+
shutdown(): Promise<void>;
|
|
2980
|
+
}
|
|
2981
|
+
declare function getScopeBlindBridge(): ScopeBlindBridge;
|
|
2982
|
+
/** Convenience: forward a signed receipt without instantiating yourself. */
|
|
2983
|
+
declare function forwardReceipt(signedReceipt: any): void;
|
|
2984
|
+
|
|
2985
|
+
export { type ActionReceipt, type AdmissionResult, type AgentId, type AgentManifest, type ApprovalAssertion, type ApprovalChallenge, type ApprovalNotification, type ApprovalResult, type ArenaPayload, type ArenaReceipt, type AttestationDocument, type AttestationPayload, type AttestationProvider, type AttestationReceipt, type AttestationResult, type AuditBundle, type AuditBundleOptions, type BenchmarkPayload, type BenchmarkReceipt, type BuilderId, type C2PAAssertion, type C2PAIngredient, type C2PAManifest, type C2PAOptions, type CCRConnectorConfig, type CCRSessionContext, type CalibrationScore, type CedarEvalRequest, type CedarPolicySet, type CedarSchema, type CedarSchemaResult, type CommittedFieldOpening, type CommittedSignResult, type ComplianceReport, ConfidentialGate, type ConfidentialGateConfig, type ConfidentialInferenceConfig, type CredentialConfig, type DecisionContext, type DecisionLog, type DelegationReceipt, type DisclosureMode, type Ed25519PublicKey, type EvidenceAttestation, type EvidenceAttestationInput, type EvidenceIssuer, type EvidenceReceipt, type EvidenceReceiptBase, type EvidenceSummary, type EvidenceSummaryEntry, type EvidenceType, type ExternalDecision, type ExternalPDPConfig, type HFDatasetMetadata, type HFReceiptRow, type HookEventName, type HookInput, type HookResponse, type IssuerType, type JsonRpcRequest, type JsonRpcResponse, type LeaseCompatibility, type ManifestBuilder, type ManifestCapabilities, type ManifestConfig, type ManifestIdentity, type ManifestPresentation, type ManifestSignature, type ManifestStatus, type McpToolDescription, type MinimalDisclosure, type NotificationConfig, type PassportTokenClaims, type PayloadDigest, type PlanReceipt, type PolicyEngineMode, type PredictionReceipt, type PredictionResolution, type PropagatorConfig, type ProtectConfig, ProtectGateway, type ProtectPolicy, type RateLimit, ReceiptPropagator, type RedactedResult, type RedactionSalt, type RekorAnchor, type RekorVerification, type RestraintPayload, type RestraintReceipt, type SHA256Hash, type SafetyTranscript, type Sandbox, type SandboxConfig, type SandboxReceipt, type SandboxResult, type SandboxToolCall, type SchemaGeneratorConfig, ScopeBlindBridge, type SigningConfig, type SimulationResult, type SimulationSummary, type SwarmContext, type TierOverrides, type TimingMetrics, type ToolPolicy, type TrustTier, type WorkPayload, type WorkReceipt, anchorToRekor, buildDecisionContext, checkRateLimit, collectSignedReceipts, computeCalibration, confidentialInference, createApprovalChallenge, createApprovalReceiptPayload, createAttestationField, createAuditBundle, createC2PAManifest, createDisclosurePackage, createEvidenceAttestation, createLogAnchorField, createReceiptChannel, createSandbox, destroySandbox, discloseField, ed25519ToDIDKey, evaluateCedar, evaluateTier, exportC2PAManifestJSON, exportJSONL, formatReportMarkdown, formatSimulation, forwardReceipt, generateC2PACommand, generateCedarSchema, generateDatasetCard, generateHFMetadata, generateReport, generateSafetyTranscript, generateSchemaStub, getScopeBlindBridge, getSignerInfo, getToolPolicy, hashReceipt, hashResponseBody, initSigning, isAgentId, isCedarAvailable, isDisclosureMode, isEvidenceType, isManifestStatus, isSigningEnabled, listCredentialLabels, loadCedarPolicies, loadPolicy, manifestToVC, meetsMinTier, parseLogFile, parseNotificationConfigFromEnv, parseRateLimit, queryExternalPDP, receiptToVP, receiptsToHFRows, redactFields, resolveCredential, revealField, runInSandbox, sendApprovalNotification, signCommittedDecision, signDecision, simulate, toCredentialRequestOptions, toManifoldFormat, toMetaculusFormat, validateCredentials, validateEvidenceReceipt, validateManifest, verifyActaC2PAAssertions, verifyAllCommitments, verifyApprovalAssertion, verifyCommitment, verifyEvidenceAttestation, verifyRekorAnchor };
|
package/dist/index.d.ts
CHANGED
|
@@ -803,9 +803,9 @@ declare function validateCredentials(credentials: Record<string, CredentialConfi
|
|
|
803
803
|
* Produces signed v2 artifact receipts for tool call decisions.
|
|
804
804
|
* Uses @veritasacta/artifacts as a required dependency (Sprint 2+).
|
|
805
805
|
*
|
|
806
|
-
* If signing is configured, every decision
|
|
807
|
-
*
|
|
808
|
-
*
|
|
806
|
+
* If signing is configured, every decision must produce a signed artifact.
|
|
807
|
+
* Initialization and signing failures are returned as explicit errors so the
|
|
808
|
+
* enforce path can deny rather than silently proceeding without evidence.
|
|
809
809
|
*/
|
|
810
810
|
|
|
811
811
|
/**
|
|
@@ -827,9 +827,11 @@ declare function initSigning(config: SigningConfig | undefined): Promise<string[
|
|
|
827
827
|
* @standard RFC 8032 (Ed25519), RFC 8785 (JCS)
|
|
828
828
|
*/
|
|
829
829
|
declare function signDecision(entry: DecisionLog): {
|
|
830
|
+
ok: boolean;
|
|
830
831
|
signed: string | null;
|
|
831
832
|
artifact_type: string;
|
|
832
833
|
warning?: string;
|
|
834
|
+
error?: string;
|
|
833
835
|
};
|
|
834
836
|
/**
|
|
835
837
|
* Get the signer's public key info for discovery/verification.
|
|
@@ -2911,4 +2913,73 @@ declare function confidentialInference(_prompt: string, _config: ConfidentialInf
|
|
|
2911
2913
|
receipt: Record<string, unknown>;
|
|
2912
2914
|
}>;
|
|
2913
2915
|
|
|
2914
|
-
|
|
2916
|
+
/**
|
|
2917
|
+
* scopeblind-bridge.ts
|
|
2918
|
+
*
|
|
2919
|
+
* Optional bridge between protect-mcp (local, MIT) and a paid ScopeBlind
|
|
2920
|
+
* tenant. When SCOPEBLIND_TOKEN is set in the environment, every signed
|
|
2921
|
+
* receipt that protect-mcp emits also gets forwarded to the tenant's
|
|
2922
|
+
* dashboard at https://scopeblind.com/console/<slug>.
|
|
2923
|
+
*
|
|
2924
|
+
* Lifecycle:
|
|
2925
|
+
* 1. On first use, exchange SCOPEBLIND_TOKEN for a short-lived BRASS-v2
|
|
2926
|
+
* auth proof from /fn/brass/issue. Cache the proof in memory until
|
|
2927
|
+
* ~5 minutes before expiry, then refresh.
|
|
2928
|
+
* 2. As receipts are emitted by hook-server.ts, push them into an
|
|
2929
|
+
* in-memory batch queue.
|
|
2930
|
+
* 3. Flush the queue every 5s (or when it reaches 128 receipts) by POSTing
|
|
2931
|
+
* to /fn/console/<slug>/receipts with Bearer SCOPEBLIND_TOKEN.
|
|
2932
|
+
*
|
|
2933
|
+
* Failure mode: forward errors NEVER throw upstream. protect-mcp continues
|
|
2934
|
+
* to mint and persist receipts locally regardless of dashboard availability.
|
|
2935
|
+
* The bridge logs failures to stderr (best-effort) and retries on the next
|
|
2936
|
+
* flush.
|
|
2937
|
+
*
|
|
2938
|
+
* Configuration:
|
|
2939
|
+
* SCOPEBLIND_TOKEN Tenant bearer token (from welcome email).
|
|
2940
|
+
* SCOPEBLIND_TENANT Optional slug override. By default we discover
|
|
2941
|
+
* the slug from the BRASS proof's tenant_id.
|
|
2942
|
+
* SCOPEBLIND_BASE Defaults to https://scopeblind.com.
|
|
2943
|
+
*
|
|
2944
|
+
* @license MIT
|
|
2945
|
+
*/
|
|
2946
|
+
interface BridgeStats {
|
|
2947
|
+
enabled: boolean;
|
|
2948
|
+
tenant_slug: string | null;
|
|
2949
|
+
forwarded_total: number;
|
|
2950
|
+
rejected_total: number;
|
|
2951
|
+
last_flush_at: string | null;
|
|
2952
|
+
last_error: string | null;
|
|
2953
|
+
}
|
|
2954
|
+
declare class ScopeBlindBridge {
|
|
2955
|
+
private readonly token;
|
|
2956
|
+
private readonly base;
|
|
2957
|
+
private readonly tenantOverride;
|
|
2958
|
+
private cachedProof;
|
|
2959
|
+
private queue;
|
|
2960
|
+
private flushTimer;
|
|
2961
|
+
private stats;
|
|
2962
|
+
private shuttingDown;
|
|
2963
|
+
constructor(env?: Record<string, string | undefined>);
|
|
2964
|
+
enabled(): boolean;
|
|
2965
|
+
/** Push a signed receipt into the queue. Non-blocking. */
|
|
2966
|
+
forward(signedReceipt: any): void;
|
|
2967
|
+
/** Flush the queue. Safe to call concurrently. */
|
|
2968
|
+
flush(): Promise<void>;
|
|
2969
|
+
/** Exchange SCOPEBLIND_TOKEN for a BRASS-v2 proof; refresh near expiry. */
|
|
2970
|
+
private ensureBrassProof;
|
|
2971
|
+
/**
|
|
2972
|
+
* Return a snapshot of bridge stats. Useful for `protect-mcp scopeblind status`.
|
|
2973
|
+
*/
|
|
2974
|
+
getStats(): BridgeStats & {
|
|
2975
|
+
queued: number;
|
|
2976
|
+
brass_proof_expires_at: string | null;
|
|
2977
|
+
};
|
|
2978
|
+
/** Flush remaining receipts and stop the interval. Called on process exit. */
|
|
2979
|
+
shutdown(): Promise<void>;
|
|
2980
|
+
}
|
|
2981
|
+
declare function getScopeBlindBridge(): ScopeBlindBridge;
|
|
2982
|
+
/** Convenience: forward a signed receipt without instantiating yourself. */
|
|
2983
|
+
declare function forwardReceipt(signedReceipt: any): void;
|
|
2984
|
+
|
|
2985
|
+
export { type ActionReceipt, type AdmissionResult, type AgentId, type AgentManifest, type ApprovalAssertion, type ApprovalChallenge, type ApprovalNotification, type ApprovalResult, type ArenaPayload, type ArenaReceipt, type AttestationDocument, type AttestationPayload, type AttestationProvider, type AttestationReceipt, type AttestationResult, type AuditBundle, type AuditBundleOptions, type BenchmarkPayload, type BenchmarkReceipt, type BuilderId, type C2PAAssertion, type C2PAIngredient, type C2PAManifest, type C2PAOptions, type CCRConnectorConfig, type CCRSessionContext, type CalibrationScore, type CedarEvalRequest, type CedarPolicySet, type CedarSchema, type CedarSchemaResult, type CommittedFieldOpening, type CommittedSignResult, type ComplianceReport, ConfidentialGate, type ConfidentialGateConfig, type ConfidentialInferenceConfig, type CredentialConfig, type DecisionContext, type DecisionLog, type DelegationReceipt, type DisclosureMode, type Ed25519PublicKey, type EvidenceAttestation, type EvidenceAttestationInput, type EvidenceIssuer, type EvidenceReceipt, type EvidenceReceiptBase, type EvidenceSummary, type EvidenceSummaryEntry, type EvidenceType, type ExternalDecision, type ExternalPDPConfig, type HFDatasetMetadata, type HFReceiptRow, type HookEventName, type HookInput, type HookResponse, type IssuerType, type JsonRpcRequest, type JsonRpcResponse, type LeaseCompatibility, type ManifestBuilder, type ManifestCapabilities, type ManifestConfig, type ManifestIdentity, type ManifestPresentation, type ManifestSignature, type ManifestStatus, type McpToolDescription, type MinimalDisclosure, type NotificationConfig, type PassportTokenClaims, type PayloadDigest, type PlanReceipt, type PolicyEngineMode, type PredictionReceipt, type PredictionResolution, type PropagatorConfig, type ProtectConfig, ProtectGateway, type ProtectPolicy, type RateLimit, ReceiptPropagator, type RedactedResult, type RedactionSalt, type RekorAnchor, type RekorVerification, type RestraintPayload, type RestraintReceipt, type SHA256Hash, type SafetyTranscript, type Sandbox, type SandboxConfig, type SandboxReceipt, type SandboxResult, type SandboxToolCall, type SchemaGeneratorConfig, ScopeBlindBridge, type SigningConfig, type SimulationResult, type SimulationSummary, type SwarmContext, type TierOverrides, type TimingMetrics, type ToolPolicy, type TrustTier, type WorkPayload, type WorkReceipt, anchorToRekor, buildDecisionContext, checkRateLimit, collectSignedReceipts, computeCalibration, confidentialInference, createApprovalChallenge, createApprovalReceiptPayload, createAttestationField, createAuditBundle, createC2PAManifest, createDisclosurePackage, createEvidenceAttestation, createLogAnchorField, createReceiptChannel, createSandbox, destroySandbox, discloseField, ed25519ToDIDKey, evaluateCedar, evaluateTier, exportC2PAManifestJSON, exportJSONL, formatReportMarkdown, formatSimulation, forwardReceipt, generateC2PACommand, generateCedarSchema, generateDatasetCard, generateHFMetadata, generateReport, generateSafetyTranscript, generateSchemaStub, getScopeBlindBridge, getSignerInfo, getToolPolicy, hashReceipt, hashResponseBody, initSigning, isAgentId, isCedarAvailable, isDisclosureMode, isEvidenceType, isManifestStatus, isSigningEnabled, listCredentialLabels, loadCedarPolicies, loadPolicy, manifestToVC, meetsMinTier, parseLogFile, parseNotificationConfigFromEnv, parseRateLimit, queryExternalPDP, receiptToVP, receiptsToHFRows, redactFields, resolveCredential, revealField, runInSandbox, sendApprovalNotification, signCommittedDecision, signDecision, simulate, toCredentialRequestOptions, toManifoldFormat, toMetaculusFormat, validateCredentials, validateEvidenceReceipt, validateManifest, verifyActaC2PAAssertions, verifyAllCommitments, verifyApprovalAssertion, verifyCommitment, verifyEvidenceAttestation, verifyRekorAnchor };
|