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/index.js
CHANGED
|
@@ -35022,6 +35022,7 @@ __export(index_exports, {
|
|
|
35022
35022
|
ConfidentialGate: () => ConfidentialGate,
|
|
35023
35023
|
ProtectGateway: () => ProtectGateway,
|
|
35024
35024
|
ReceiptPropagator: () => ReceiptPropagator,
|
|
35025
|
+
ScopeBlindBridge: () => ScopeBlindBridge,
|
|
35025
35026
|
anchorToRekor: () => anchorToRekor,
|
|
35026
35027
|
buildDecisionContext: () => buildDecisionContext,
|
|
35027
35028
|
checkRateLimit: () => checkRateLimit,
|
|
@@ -35048,6 +35049,7 @@ __export(index_exports, {
|
|
|
35048
35049
|
exportJSONL: () => exportJSONL,
|
|
35049
35050
|
formatReportMarkdown: () => formatReportMarkdown,
|
|
35050
35051
|
formatSimulation: () => formatSimulation,
|
|
35052
|
+
forwardReceipt: () => forwardReceipt,
|
|
35051
35053
|
generateC2PACommand: () => generateC2PACommand,
|
|
35052
35054
|
generateCedarSchema: () => generateCedarSchema,
|
|
35053
35055
|
generateDatasetCard: () => generateDatasetCard,
|
|
@@ -35058,6 +35060,7 @@ __export(index_exports, {
|
|
|
35058
35060
|
generateSampleCedarPolicy: () => generateSampleCedarPolicy,
|
|
35059
35061
|
generateSchemaStub: () => generateSchemaStub,
|
|
35060
35062
|
generateVerifyReceiptSkill: () => generateVerifyReceiptSkill,
|
|
35063
|
+
getScopeBlindBridge: () => getScopeBlindBridge,
|
|
35061
35064
|
getSignerInfo: () => getSignerInfo,
|
|
35062
35065
|
getToolPolicy: () => getToolPolicy,
|
|
35063
35066
|
hashReceipt: () => hashReceipt,
|
|
@@ -35424,11 +35427,40 @@ function validateCredentials(credentials) {
|
|
|
35424
35427
|
var import_node_fs3 = require("fs");
|
|
35425
35428
|
var signerState = null;
|
|
35426
35429
|
var artifactsModule = null;
|
|
35430
|
+
var signingConfigured = false;
|
|
35431
|
+
var signingInitError = null;
|
|
35427
35432
|
async function initSigning(config) {
|
|
35428
35433
|
const warnings = [];
|
|
35434
|
+
signerState = null;
|
|
35435
|
+
artifactsModule = null;
|
|
35436
|
+
signingConfigured = Boolean(config && config.enabled !== false);
|
|
35437
|
+
signingInitError = null;
|
|
35429
35438
|
if (!config || config.enabled === false) {
|
|
35430
35439
|
return warnings;
|
|
35431
35440
|
}
|
|
35441
|
+
if (!config.key_path) {
|
|
35442
|
+
signingInitError = "signing enabled but key_path is not configured";
|
|
35443
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
35444
|
+
return warnings;
|
|
35445
|
+
}
|
|
35446
|
+
if (!(0, import_node_fs3.existsSync)(config.key_path)) {
|
|
35447
|
+
signingInitError = `key file not found at ${config.key_path}`;
|
|
35448
|
+
warnings.push(`signing: ${signingInitError} \u2014 run "protect-mcp init" to generate`);
|
|
35449
|
+
return warnings;
|
|
35450
|
+
}
|
|
35451
|
+
let keyData;
|
|
35452
|
+
try {
|
|
35453
|
+
keyData = JSON.parse((0, import_node_fs3.readFileSync)(config.key_path, "utf-8"));
|
|
35454
|
+
if (!keyData.privateKey || !keyData.publicKey) {
|
|
35455
|
+
signingInitError = "key file missing privateKey or publicKey fields";
|
|
35456
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
35457
|
+
return warnings;
|
|
35458
|
+
}
|
|
35459
|
+
} catch (err) {
|
|
35460
|
+
signingInitError = `failed to load key file: ${err instanceof Error ? err.message : err}`;
|
|
35461
|
+
warnings.push(`signing: ${signingInitError}`);
|
|
35462
|
+
return warnings;
|
|
35463
|
+
}
|
|
35432
35464
|
try {
|
|
35433
35465
|
const moduleName = "@veritasacta/artifacts";
|
|
35434
35466
|
artifactsModule = await import(
|
|
@@ -35436,37 +35468,48 @@ async function initSigning(config) {
|
|
|
35436
35468
|
moduleName
|
|
35437
35469
|
);
|
|
35438
35470
|
} catch {
|
|
35439
|
-
|
|
35471
|
+
signingInitError = "@veritasacta/artifacts not available";
|
|
35472
|
+
warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
|
|
35440
35473
|
return warnings;
|
|
35441
35474
|
}
|
|
35442
|
-
|
|
35443
|
-
|
|
35444
|
-
|
|
35445
|
-
|
|
35446
|
-
|
|
35447
|
-
|
|
35448
|
-
|
|
35449
|
-
|
|
35450
|
-
|
|
35451
|
-
|
|
35452
|
-
|
|
35453
|
-
signerState = {
|
|
35454
|
-
privateKey: keyData.privateKey,
|
|
35455
|
-
publicKey: keyData.publicKey,
|
|
35456
|
-
kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
|
|
35457
|
-
issuer: config.issuer || keyData.issuer || "protect-mcp"
|
|
35458
|
-
};
|
|
35459
|
-
} catch (err) {
|
|
35460
|
-
warnings.push(`signing: failed to load key file: ${err instanceof Error ? err.message : err}`);
|
|
35461
|
-
}
|
|
35475
|
+
try {
|
|
35476
|
+
signerState = {
|
|
35477
|
+
privateKey: keyData.privateKey,
|
|
35478
|
+
publicKey: keyData.publicKey,
|
|
35479
|
+
kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
|
|
35480
|
+
issuer: config.issuer || keyData.issuer || "protect-mcp"
|
|
35481
|
+
};
|
|
35482
|
+
} catch (err) {
|
|
35483
|
+
signingInitError = `failed to initialize signer: ${err instanceof Error ? err.message : err}`;
|
|
35484
|
+
artifactsModule = null;
|
|
35485
|
+
warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
|
|
35462
35486
|
}
|
|
35463
35487
|
return warnings;
|
|
35464
35488
|
}
|
|
35465
35489
|
function signDecision(entry) {
|
|
35490
|
+
const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
|
|
35491
|
+
if (signingConfigured && signingInitError) {
|
|
35492
|
+
return {
|
|
35493
|
+
ok: false,
|
|
35494
|
+
signed: null,
|
|
35495
|
+
artifact_type: artifactType,
|
|
35496
|
+
warning: `signing initialization failed: ${signingInitError}`,
|
|
35497
|
+
error: signingInitError
|
|
35498
|
+
};
|
|
35499
|
+
}
|
|
35500
|
+
if (signingConfigured && (!signerState || !artifactsModule)) {
|
|
35501
|
+
const error = "signing was configured but no signer is ready";
|
|
35502
|
+
return {
|
|
35503
|
+
ok: false,
|
|
35504
|
+
signed: null,
|
|
35505
|
+
artifact_type: artifactType,
|
|
35506
|
+
warning: error,
|
|
35507
|
+
error
|
|
35508
|
+
};
|
|
35509
|
+
}
|
|
35466
35510
|
if (!signerState || !artifactsModule) {
|
|
35467
|
-
return { signed: null, artifact_type: "none" };
|
|
35511
|
+
return { ok: false, signed: null, artifact_type: "none" };
|
|
35468
35512
|
}
|
|
35469
|
-
const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
|
|
35470
35513
|
try {
|
|
35471
35514
|
const payload = {
|
|
35472
35515
|
tool: entry.tool,
|
|
@@ -35507,14 +35550,18 @@ function signDecision(entry) {
|
|
|
35507
35550
|
}
|
|
35508
35551
|
);
|
|
35509
35552
|
return {
|
|
35553
|
+
ok: true,
|
|
35510
35554
|
signed: JSON.stringify(result.artifact),
|
|
35511
35555
|
artifact_type: artifactType
|
|
35512
35556
|
};
|
|
35513
35557
|
} catch (err) {
|
|
35558
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
35514
35559
|
return {
|
|
35560
|
+
ok: false,
|
|
35515
35561
|
signed: null,
|
|
35516
35562
|
artifact_type: artifactType,
|
|
35517
|
-
warning: `signing failed: ${
|
|
35563
|
+
warning: `signing failed: ${message}`,
|
|
35564
|
+
error: message
|
|
35518
35565
|
};
|
|
35519
35566
|
}
|
|
35520
35567
|
}
|
|
@@ -35527,7 +35574,7 @@ function getSignerInfo() {
|
|
|
35527
35574
|
};
|
|
35528
35575
|
}
|
|
35529
35576
|
function isSigningEnabled() {
|
|
35530
|
-
return signerState !== null && artifactsModule !== null;
|
|
35577
|
+
return signingConfigured && signingInitError === null && signerState !== null && artifactsModule !== null;
|
|
35531
35578
|
}
|
|
35532
35579
|
|
|
35533
35580
|
// src/external-pdp.ts
|
|
@@ -36655,8 +36702,20 @@ var ProtectGateway = class {
|
|
|
36655
36702
|
this.evidenceStore.save();
|
|
36656
36703
|
}
|
|
36657
36704
|
}
|
|
36658
|
-
} else if (signed.
|
|
36659
|
-
|
|
36705
|
+
} else if (signed.error) {
|
|
36706
|
+
const tombstone = JSON.stringify({
|
|
36707
|
+
type: "scopeblind.signing_failure.v1",
|
|
36708
|
+
request_id: log.request_id,
|
|
36709
|
+
tool: log.tool,
|
|
36710
|
+
decision: log.decision,
|
|
36711
|
+
error: signed.error,
|
|
36712
|
+
at: new Date(log.timestamp).toISOString()
|
|
36713
|
+
});
|
|
36714
|
+
try {
|
|
36715
|
+
(0, import_node_fs6.appendFileSync)(this.receiptFilePath, tombstone + "\n");
|
|
36716
|
+
} catch {
|
|
36717
|
+
}
|
|
36718
|
+
process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
|
|
36660
36719
|
`);
|
|
36661
36720
|
}
|
|
36662
36721
|
}
|
|
@@ -39629,6 +39688,159 @@ var import_node_http2 = require("http");
|
|
|
39629
39688
|
var import_node_crypto4 = require("crypto");
|
|
39630
39689
|
var import_node_fs9 = require("fs");
|
|
39631
39690
|
var import_node_path5 = require("path");
|
|
39691
|
+
|
|
39692
|
+
// src/scopeblind-bridge.ts
|
|
39693
|
+
var DEFAULT_BASE = "https://scopeblind.com";
|
|
39694
|
+
var FLUSH_INTERVAL_MS = 5e3;
|
|
39695
|
+
var BATCH_MAX = 128;
|
|
39696
|
+
var BRASS_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
|
|
39697
|
+
var ScopeBlindBridge = class {
|
|
39698
|
+
token;
|
|
39699
|
+
base;
|
|
39700
|
+
tenantOverride;
|
|
39701
|
+
cachedProof = null;
|
|
39702
|
+
queue = [];
|
|
39703
|
+
flushTimer = null;
|
|
39704
|
+
stats;
|
|
39705
|
+
shuttingDown = false;
|
|
39706
|
+
constructor(env = process.env) {
|
|
39707
|
+
this.token = env.SCOPEBLIND_TOKEN || null;
|
|
39708
|
+
this.base = (env.SCOPEBLIND_BASE || DEFAULT_BASE).replace(/\/$/, "");
|
|
39709
|
+
this.tenantOverride = env.SCOPEBLIND_TENANT || null;
|
|
39710
|
+
this.stats = {
|
|
39711
|
+
enabled: Boolean(this.token),
|
|
39712
|
+
tenant_slug: this.tenantOverride,
|
|
39713
|
+
forwarded_total: 0,
|
|
39714
|
+
rejected_total: 0,
|
|
39715
|
+
last_flush_at: null,
|
|
39716
|
+
last_error: null
|
|
39717
|
+
};
|
|
39718
|
+
if (this.enabled()) {
|
|
39719
|
+
this.flushTimer = setInterval(() => {
|
|
39720
|
+
void this.flush();
|
|
39721
|
+
}, FLUSH_INTERVAL_MS);
|
|
39722
|
+
if (typeof this.flushTimer === "object" && this.flushTimer && "unref" in this.flushTimer) {
|
|
39723
|
+
this.flushTimer.unref?.();
|
|
39724
|
+
}
|
|
39725
|
+
process.on("beforeExit", () => {
|
|
39726
|
+
void this.shutdown();
|
|
39727
|
+
});
|
|
39728
|
+
}
|
|
39729
|
+
}
|
|
39730
|
+
enabled() {
|
|
39731
|
+
return Boolean(this.token);
|
|
39732
|
+
}
|
|
39733
|
+
/** Push a signed receipt into the queue. Non-blocking. */
|
|
39734
|
+
forward(signedReceipt) {
|
|
39735
|
+
if (!this.enabled() || this.shuttingDown) return;
|
|
39736
|
+
this.queue.push(signedReceipt);
|
|
39737
|
+
if (this.queue.length >= BATCH_MAX) void this.flush();
|
|
39738
|
+
}
|
|
39739
|
+
/** Flush the queue. Safe to call concurrently. */
|
|
39740
|
+
async flush() {
|
|
39741
|
+
if (!this.enabled() || this.queue.length === 0) return;
|
|
39742
|
+
const batch = this.queue.splice(0, BATCH_MAX);
|
|
39743
|
+
try {
|
|
39744
|
+
const proof = await this.ensureBrassProof();
|
|
39745
|
+
const slug = this.tenantOverride || proof?.tenant_id;
|
|
39746
|
+
if (!slug) {
|
|
39747
|
+
this.queue.unshift(...batch);
|
|
39748
|
+
return;
|
|
39749
|
+
}
|
|
39750
|
+
this.stats.tenant_slug = slug;
|
|
39751
|
+
const res = await fetch(`${this.base}/fn/console/${slug}/receipts`, {
|
|
39752
|
+
method: "POST",
|
|
39753
|
+
headers: {
|
|
39754
|
+
"content-type": "application/json",
|
|
39755
|
+
authorization: `Bearer ${this.token}`,
|
|
39756
|
+
"user-agent": "protect-mcp/scopeblind-bridge"
|
|
39757
|
+
},
|
|
39758
|
+
body: JSON.stringify({ receipts: batch })
|
|
39759
|
+
});
|
|
39760
|
+
if (!res.ok) {
|
|
39761
|
+
const errBody = await res.text().catch(() => "");
|
|
39762
|
+
this.stats.last_error = `HTTP ${res.status} ${errBody.slice(0, 160)}`;
|
|
39763
|
+
this.stats.rejected_total += batch.length;
|
|
39764
|
+
if (res.status >= 500 && res.status !== 503) {
|
|
39765
|
+
this.queue.unshift(...batch);
|
|
39766
|
+
}
|
|
39767
|
+
return;
|
|
39768
|
+
}
|
|
39769
|
+
const body = await res.json().catch(() => ({}));
|
|
39770
|
+
this.stats.forwarded_total += body?.accepted ?? batch.length;
|
|
39771
|
+
this.stats.rejected_total += body?.rejected ?? 0;
|
|
39772
|
+
this.stats.last_flush_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
39773
|
+
this.stats.last_error = null;
|
|
39774
|
+
} catch (err) {
|
|
39775
|
+
this.stats.last_error = String(err?.message || err);
|
|
39776
|
+
this.queue.unshift(...batch);
|
|
39777
|
+
}
|
|
39778
|
+
}
|
|
39779
|
+
/** Exchange SCOPEBLIND_TOKEN for a BRASS-v2 proof; refresh near expiry. */
|
|
39780
|
+
async ensureBrassProof() {
|
|
39781
|
+
if (!this.token) return null;
|
|
39782
|
+
const now = Date.now();
|
|
39783
|
+
if (this.cachedProof && Date.parse(this.cachedProof.expires_at) - now > BRASS_REFRESH_MARGIN_MS) {
|
|
39784
|
+
return this.cachedProof;
|
|
39785
|
+
}
|
|
39786
|
+
try {
|
|
39787
|
+
const res = await fetch(`${this.base}/fn/brass/issue`, {
|
|
39788
|
+
method: "POST",
|
|
39789
|
+
headers: {
|
|
39790
|
+
"content-type": "application/json",
|
|
39791
|
+
"user-agent": "protect-mcp/scopeblind-bridge"
|
|
39792
|
+
},
|
|
39793
|
+
body: JSON.stringify({
|
|
39794
|
+
token: this.token,
|
|
39795
|
+
scope: "protect-mcp-receipt-emit",
|
|
39796
|
+
ttl_seconds: 3600
|
|
39797
|
+
})
|
|
39798
|
+
});
|
|
39799
|
+
if (!res.ok) {
|
|
39800
|
+
const text = await res.text().catch(() => "");
|
|
39801
|
+
this.stats.last_error = `brass-issue: HTTP ${res.status} ${text.slice(0, 160)}`;
|
|
39802
|
+
return null;
|
|
39803
|
+
}
|
|
39804
|
+
const body = await res.json();
|
|
39805
|
+
if (!body?.auth_proof) {
|
|
39806
|
+
this.stats.last_error = "brass-issue: missing auth_proof in response";
|
|
39807
|
+
return null;
|
|
39808
|
+
}
|
|
39809
|
+
this.cachedProof = body.auth_proof;
|
|
39810
|
+
return this.cachedProof;
|
|
39811
|
+
} catch (err) {
|
|
39812
|
+
this.stats.last_error = `brass-issue: ${err?.message || err}`;
|
|
39813
|
+
return null;
|
|
39814
|
+
}
|
|
39815
|
+
}
|
|
39816
|
+
/**
|
|
39817
|
+
* Return a snapshot of bridge stats. Useful for `protect-mcp scopeblind status`.
|
|
39818
|
+
*/
|
|
39819
|
+
getStats() {
|
|
39820
|
+
return {
|
|
39821
|
+
...this.stats,
|
|
39822
|
+
queued: this.queue.length,
|
|
39823
|
+
brass_proof_expires_at: this.cachedProof?.expires_at || null
|
|
39824
|
+
};
|
|
39825
|
+
}
|
|
39826
|
+
/** Flush remaining receipts and stop the interval. Called on process exit. */
|
|
39827
|
+
async shutdown() {
|
|
39828
|
+
if (this.shuttingDown) return;
|
|
39829
|
+
this.shuttingDown = true;
|
|
39830
|
+
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
39831
|
+
if (this.queue.length > 0) await this.flush();
|
|
39832
|
+
}
|
|
39833
|
+
};
|
|
39834
|
+
var singleton = null;
|
|
39835
|
+
function getScopeBlindBridge() {
|
|
39836
|
+
if (!singleton) singleton = new ScopeBlindBridge();
|
|
39837
|
+
return singleton;
|
|
39838
|
+
}
|
|
39839
|
+
function forwardReceipt(signedReceipt) {
|
|
39840
|
+
getScopeBlindBridge().forward(signedReceipt);
|
|
39841
|
+
}
|
|
39842
|
+
|
|
39843
|
+
// src/hook-server.ts
|
|
39632
39844
|
var DEFAULT_PORT = 9377;
|
|
39633
39845
|
var LOG_FILE3 = ".protect-mcp-log.jsonl";
|
|
39634
39846
|
var RECEIPTS_FILE2 = ".protect-mcp-receipts.jsonl";
|
|
@@ -39836,7 +40048,7 @@ async function handlePreToolUse(input, state) {
|
|
|
39836
40048
|
const hookLatency = Date.now() - hookStart;
|
|
39837
40049
|
const denyKey = `${toolName}:${input.sessionId || "default"}`;
|
|
39838
40050
|
state.denyCounter.delete(denyKey);
|
|
39839
|
-
emitDecisionLog(state, {
|
|
40051
|
+
const emit = emitDecisionLog(state, {
|
|
39840
40052
|
tool: toolName,
|
|
39841
40053
|
decision: "allow",
|
|
39842
40054
|
reason_code: state.cedarPolicies ? "cedar_allow" : state.jsonPolicy ? "policy_allow" : "observe_mode",
|
|
@@ -39848,6 +40060,15 @@ async function handlePreToolUse(input, state) {
|
|
|
39848
40060
|
sandbox_state: detectSandboxState(),
|
|
39849
40061
|
plan_receipt_id: state.activePlanReceiptId || void 0
|
|
39850
40062
|
});
|
|
40063
|
+
if (state.enforce && emit.signingFailed) {
|
|
40064
|
+
return {
|
|
40065
|
+
hookSpecificOutput: {
|
|
40066
|
+
hookEventName: "PreToolUse",
|
|
40067
|
+
permissionDecision: "deny",
|
|
40068
|
+
permissionDecisionReason: `[ScopeBlind] "${toolName}" was blocked because its receipt could not be signed. Failing closed: a governed action that cannot be proven is not allowed.`
|
|
40069
|
+
}
|
|
40070
|
+
};
|
|
40071
|
+
}
|
|
39851
40072
|
return {};
|
|
39852
40073
|
}
|
|
39853
40074
|
async function handlePostToolUse(input, state) {
|
|
@@ -40095,11 +40316,35 @@ function emitDecisionLog(state, entry) {
|
|
|
40095
40316
|
} catch {
|
|
40096
40317
|
}
|
|
40097
40318
|
state.receiptBuffer.add(log.request_id, signed.signed);
|
|
40098
|
-
|
|
40099
|
-
|
|
40319
|
+
try {
|
|
40320
|
+
const bridge = getScopeBlindBridge();
|
|
40321
|
+
if (bridge.enabled()) {
|
|
40322
|
+
const parsed = typeof signed.signed === "string" ? JSON.parse(signed.signed) : signed.signed;
|
|
40323
|
+
bridge.forward(parsed);
|
|
40324
|
+
}
|
|
40325
|
+
} catch (err) {
|
|
40326
|
+
process.stderr.write(`[PROTECT_MCP] ScopeBlind forward error: ${err instanceof Error ? err.message : err}
|
|
40327
|
+
`);
|
|
40328
|
+
}
|
|
40329
|
+
} else if (signed.error) {
|
|
40330
|
+
const tombstone = JSON.stringify({
|
|
40331
|
+
type: "scopeblind.signing_failure.v1",
|
|
40332
|
+
request_id: log.request_id,
|
|
40333
|
+
tool: log.tool,
|
|
40334
|
+
decision: log.decision,
|
|
40335
|
+
error: signed.error,
|
|
40336
|
+
at: new Date(log.timestamp).toISOString()
|
|
40337
|
+
});
|
|
40338
|
+
try {
|
|
40339
|
+
(0, import_node_fs9.appendFileSync)(state.receiptFilePath, tombstone + "\n");
|
|
40340
|
+
} catch {
|
|
40341
|
+
}
|
|
40342
|
+
process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
|
|
40100
40343
|
`);
|
|
40344
|
+
return { signingFailed: true };
|
|
40101
40345
|
}
|
|
40102
40346
|
}
|
|
40347
|
+
return { signingFailed: false };
|
|
40103
40348
|
}
|
|
40104
40349
|
async function routeHookEvent(input, state) {
|
|
40105
40350
|
switch (input.hookEventName) {
|
|
@@ -42429,6 +42674,7 @@ function createSandboxServer() {
|
|
|
42429
42674
|
ConfidentialGate,
|
|
42430
42675
|
ProtectGateway,
|
|
42431
42676
|
ReceiptPropagator,
|
|
42677
|
+
ScopeBlindBridge,
|
|
42432
42678
|
anchorToRekor,
|
|
42433
42679
|
buildDecisionContext,
|
|
42434
42680
|
checkRateLimit,
|
|
@@ -42455,6 +42701,7 @@ function createSandboxServer() {
|
|
|
42455
42701
|
exportJSONL,
|
|
42456
42702
|
formatReportMarkdown,
|
|
42457
42703
|
formatSimulation,
|
|
42704
|
+
forwardReceipt,
|
|
42458
42705
|
generateC2PACommand,
|
|
42459
42706
|
generateCedarSchema,
|
|
42460
42707
|
generateDatasetCard,
|
|
@@ -42465,6 +42712,7 @@ function createSandboxServer() {
|
|
|
42465
42712
|
generateSampleCedarPolicy,
|
|
42466
42713
|
generateSchemaStub,
|
|
42467
42714
|
generateVerifyReceiptSkill,
|
|
42715
|
+
getScopeBlindBridge,
|
|
42468
42716
|
getSignerInfo,
|
|
42469
42717
|
getToolPolicy,
|
|
42470
42718
|
hashReceipt,
|
|
@@ -42509,6 +42757,36 @@ function createSandboxServer() {
|
|
|
42509
42757
|
verifyEvidenceAttestation,
|
|
42510
42758
|
verifyRekorAnchor
|
|
42511
42759
|
});
|
|
42760
|
+
/**
|
|
42761
|
+
* scopeblind-bridge.ts
|
|
42762
|
+
*
|
|
42763
|
+
* Optional bridge between protect-mcp (local, MIT) and a paid ScopeBlind
|
|
42764
|
+
* tenant. When SCOPEBLIND_TOKEN is set in the environment, every signed
|
|
42765
|
+
* receipt that protect-mcp emits also gets forwarded to the tenant's
|
|
42766
|
+
* dashboard at https://scopeblind.com/console/<slug>.
|
|
42767
|
+
*
|
|
42768
|
+
* Lifecycle:
|
|
42769
|
+
* 1. On first use, exchange SCOPEBLIND_TOKEN for a short-lived BRASS-v2
|
|
42770
|
+
* auth proof from /fn/brass/issue. Cache the proof in memory until
|
|
42771
|
+
* ~5 minutes before expiry, then refresh.
|
|
42772
|
+
* 2. As receipts are emitted by hook-server.ts, push them into an
|
|
42773
|
+
* in-memory batch queue.
|
|
42774
|
+
* 3. Flush the queue every 5s (or when it reaches 128 receipts) by POSTing
|
|
42775
|
+
* to /fn/console/<slug>/receipts with Bearer SCOPEBLIND_TOKEN.
|
|
42776
|
+
*
|
|
42777
|
+
* Failure mode: forward errors NEVER throw upstream. protect-mcp continues
|
|
42778
|
+
* to mint and persist receipts locally regardless of dashboard availability.
|
|
42779
|
+
* The bridge logs failures to stderr (best-effort) and retries on the next
|
|
42780
|
+
* flush.
|
|
42781
|
+
*
|
|
42782
|
+
* Configuration:
|
|
42783
|
+
* SCOPEBLIND_TOKEN Tenant bearer token (from welcome email).
|
|
42784
|
+
* SCOPEBLIND_TENANT Optional slug override. By default we discover
|
|
42785
|
+
* the slug from the BRASS proof's tenant_id.
|
|
42786
|
+
* SCOPEBLIND_BASE Defaults to https://scopeblind.com.
|
|
42787
|
+
*
|
|
42788
|
+
* @license MIT
|
|
42789
|
+
*/
|
|
42512
42790
|
/*! Bundled license information:
|
|
42513
42791
|
|
|
42514
42792
|
@noble/hashes/esm/utils.js:
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
formatSimulation,
|
|
7
7
|
parseLogFile,
|
|
8
8
|
simulate
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-S4ICHNSP.mjs";
|
|
10
10
|
import {
|
|
11
11
|
ProtectGateway,
|
|
12
12
|
buildDecisionContext,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
resolveCredential,
|
|
19
19
|
sendApprovalNotification,
|
|
20
20
|
validateCredentials
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-PLKRTBDR.mjs";
|
|
22
22
|
import {
|
|
23
23
|
createSandboxServer
|
|
24
24
|
} from "./chunk-J6L4XCTE.mjs";
|
|
@@ -29,8 +29,11 @@ import {
|
|
|
29
29
|
generateVerifyReceiptSkill
|
|
30
30
|
} from "./chunk-NMZPXXL3.mjs";
|
|
31
31
|
import {
|
|
32
|
+
ScopeBlindBridge,
|
|
33
|
+
forwardReceipt,
|
|
34
|
+
getScopeBlindBridge,
|
|
32
35
|
startHookServer
|
|
33
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-3YCKR72H.mjs";
|
|
34
37
|
import {
|
|
35
38
|
checkRateLimit,
|
|
36
39
|
evaluateCedar,
|
|
@@ -43,7 +46,7 @@ import {
|
|
|
43
46
|
loadPolicy,
|
|
44
47
|
parseRateLimit,
|
|
45
48
|
signDecision
|
|
46
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-UV53U6D4.mjs";
|
|
47
50
|
import {
|
|
48
51
|
ed25519,
|
|
49
52
|
sha256
|
|
@@ -1934,6 +1937,7 @@ export {
|
|
|
1934
1937
|
ConfidentialGate,
|
|
1935
1938
|
ProtectGateway,
|
|
1936
1939
|
ReceiptPropagator,
|
|
1940
|
+
ScopeBlindBridge,
|
|
1937
1941
|
anchorToRekor,
|
|
1938
1942
|
buildDecisionContext,
|
|
1939
1943
|
checkRateLimit,
|
|
@@ -1960,6 +1964,7 @@ export {
|
|
|
1960
1964
|
exportJSONL,
|
|
1961
1965
|
formatReportMarkdown,
|
|
1962
1966
|
formatSimulation,
|
|
1967
|
+
forwardReceipt,
|
|
1963
1968
|
generateC2PACommand,
|
|
1964
1969
|
generateCedarSchema,
|
|
1965
1970
|
generateDatasetCard,
|
|
@@ -1970,6 +1975,7 @@ export {
|
|
|
1970
1975
|
generateSampleCedarPolicy,
|
|
1971
1976
|
generateSchemaStub,
|
|
1972
1977
|
generateVerifyReceiptSkill,
|
|
1978
|
+
getScopeBlindBridge,
|
|
1973
1979
|
getSignerInfo,
|
|
1974
1980
|
getToolPolicy,
|
|
1975
1981
|
hashReceipt,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "protect-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"mcpName": "com.scopeblind/protect-mcp",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Cedar policy + signed receipts for AI agent decisions. Same primitive shipping in ScopeBlind cold-chain hardware. scopeblind.com/cold-chain",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"module": "dist/index.mjs",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"url": "https://github.com/scopeblind/scopeblind-gateway/issues"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"@veritasacta/artifacts": "^0.2.2",
|
|
65
66
|
"@veritasacta/protocol": "^0.1.0"
|
|
66
67
|
},
|
|
67
68
|
"optionalDependencies": {
|