protect-mcp 0.5.5 → 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.
@@ -78,11 +78,40 @@ function checkRateLimit(key, limit, store) {
78
78
  import { readFileSync as readFileSync2, existsSync } from "fs";
79
79
  var signerState = null;
80
80
  var artifactsModule = null;
81
+ var signingConfigured = false;
82
+ var signingInitError = null;
81
83
  async function initSigning(config) {
82
84
  const warnings = [];
85
+ signerState = null;
86
+ artifactsModule = null;
87
+ signingConfigured = Boolean(config && config.enabled !== false);
88
+ signingInitError = null;
83
89
  if (!config || config.enabled === false) {
84
90
  return warnings;
85
91
  }
92
+ if (!config.key_path) {
93
+ signingInitError = "signing enabled but key_path is not configured";
94
+ warnings.push(`signing: ${signingInitError}`);
95
+ return warnings;
96
+ }
97
+ if (!existsSync(config.key_path)) {
98
+ signingInitError = `key file not found at ${config.key_path}`;
99
+ warnings.push(`signing: ${signingInitError} \u2014 run "protect-mcp init" to generate`);
100
+ return warnings;
101
+ }
102
+ let keyData;
103
+ try {
104
+ keyData = JSON.parse(readFileSync2(config.key_path, "utf-8"));
105
+ if (!keyData.privateKey || !keyData.publicKey) {
106
+ signingInitError = "key file missing privateKey or publicKey fields";
107
+ warnings.push(`signing: ${signingInitError}`);
108
+ return warnings;
109
+ }
110
+ } catch (err) {
111
+ signingInitError = `failed to load key file: ${err instanceof Error ? err.message : err}`;
112
+ warnings.push(`signing: ${signingInitError}`);
113
+ return warnings;
114
+ }
86
115
  try {
87
116
  const moduleName = "@veritasacta/artifacts";
88
117
  artifactsModule = await import(
@@ -90,37 +119,48 @@ async function initSigning(config) {
90
119
  moduleName
91
120
  );
92
121
  } catch {
93
- warnings.push("signing: @veritasacta/artifacts not available \u2014 receipts will be unsigned");
122
+ signingInitError = "@veritasacta/artifacts not available";
123
+ warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
94
124
  return warnings;
95
125
  }
96
- if (config.key_path) {
97
- if (!existsSync(config.key_path)) {
98
- warnings.push(`signing: key file not found at ${config.key_path} \u2014 run "protect-mcp init" to generate`);
99
- return warnings;
100
- }
101
- try {
102
- const keyData = JSON.parse(readFileSync2(config.key_path, "utf-8"));
103
- if (!keyData.privateKey || !keyData.publicKey) {
104
- warnings.push("signing: key file missing privateKey or publicKey fields");
105
- return warnings;
106
- }
107
- signerState = {
108
- privateKey: keyData.privateKey,
109
- publicKey: keyData.publicKey,
110
- kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
111
- issuer: config.issuer || keyData.issuer || "protect-mcp"
112
- };
113
- } catch (err) {
114
- warnings.push(`signing: failed to load key file: ${err instanceof Error ? err.message : err}`);
115
- }
126
+ try {
127
+ signerState = {
128
+ privateKey: keyData.privateKey,
129
+ publicKey: keyData.publicKey,
130
+ kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
131
+ issuer: config.issuer || keyData.issuer || "protect-mcp"
132
+ };
133
+ } catch (err) {
134
+ signingInitError = `failed to initialize signer: ${err instanceof Error ? err.message : err}`;
135
+ artifactsModule = null;
136
+ warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
116
137
  }
117
138
  return warnings;
118
139
  }
119
140
  function signDecision(entry) {
141
+ const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
142
+ if (signingConfigured && signingInitError) {
143
+ return {
144
+ ok: false,
145
+ signed: null,
146
+ artifact_type: artifactType,
147
+ warning: `signing initialization failed: ${signingInitError}`,
148
+ error: signingInitError
149
+ };
150
+ }
151
+ if (signingConfigured && (!signerState || !artifactsModule)) {
152
+ const error = "signing was configured but no signer is ready";
153
+ return {
154
+ ok: false,
155
+ signed: null,
156
+ artifact_type: artifactType,
157
+ warning: error,
158
+ error
159
+ };
160
+ }
120
161
  if (!signerState || !artifactsModule) {
121
- return { signed: null, artifact_type: "none" };
162
+ return { ok: false, signed: null, artifact_type: "none" };
122
163
  }
123
- const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
124
164
  try {
125
165
  const payload = {
126
166
  tool: entry.tool,
@@ -161,14 +201,18 @@ function signDecision(entry) {
161
201
  }
162
202
  );
163
203
  return {
204
+ ok: true,
164
205
  signed: JSON.stringify(result.artifact),
165
206
  artifact_type: artifactType
166
207
  };
167
208
  } catch (err) {
209
+ const message = err instanceof Error ? err.message : "unknown error";
168
210
  return {
211
+ ok: false,
169
212
  signed: null,
170
213
  artifact_type: artifactType,
171
- warning: `signing failed: ${err instanceof Error ? err.message : "unknown error"}`
214
+ warning: `signing failed: ${message}`,
215
+ error: message
172
216
  };
173
217
  }
174
218
  }
@@ -181,7 +225,7 @@ function getSignerInfo() {
181
225
  };
182
226
  }
183
227
  function isSigningEnabled() {
184
- return signerState !== null && artifactsModule !== null;
228
+ return signingConfigured && signingInitError === null && signerState !== null && artifactsModule !== null;
185
229
  }
186
230
 
187
231
  // src/cedar-evaluator.ts
package/dist/cli.js CHANGED
@@ -361,9 +361,36 @@ var init_credentials = __esm({
361
361
  // src/signing.ts
362
362
  async function initSigning(config) {
363
363
  const warnings = [];
364
+ signerState = null;
365
+ artifactsModule = null;
366
+ signingConfigured = Boolean(config && config.enabled !== false);
367
+ signingInitError = null;
364
368
  if (!config || config.enabled === false) {
365
369
  return warnings;
366
370
  }
371
+ if (!config.key_path) {
372
+ signingInitError = "signing enabled but key_path is not configured";
373
+ warnings.push(`signing: ${signingInitError}`);
374
+ return warnings;
375
+ }
376
+ if (!(0, import_node_fs3.existsSync)(config.key_path)) {
377
+ signingInitError = `key file not found at ${config.key_path}`;
378
+ warnings.push(`signing: ${signingInitError} \u2014 run "protect-mcp init" to generate`);
379
+ return warnings;
380
+ }
381
+ let keyData;
382
+ try {
383
+ keyData = JSON.parse((0, import_node_fs3.readFileSync)(config.key_path, "utf-8"));
384
+ if (!keyData.privateKey || !keyData.publicKey) {
385
+ signingInitError = "key file missing privateKey or publicKey fields";
386
+ warnings.push(`signing: ${signingInitError}`);
387
+ return warnings;
388
+ }
389
+ } catch (err) {
390
+ signingInitError = `failed to load key file: ${err instanceof Error ? err.message : err}`;
391
+ warnings.push(`signing: ${signingInitError}`);
392
+ return warnings;
393
+ }
367
394
  try {
368
395
  const moduleName = "@veritasacta/artifacts";
369
396
  artifactsModule = await import(
@@ -371,37 +398,48 @@ async function initSigning(config) {
371
398
  moduleName
372
399
  );
373
400
  } catch {
374
- warnings.push("signing: @veritasacta/artifacts not available \u2014 receipts will be unsigned");
401
+ signingInitError = "@veritasacta/artifacts not available";
402
+ warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
375
403
  return warnings;
376
404
  }
377
- if (config.key_path) {
378
- if (!(0, import_node_fs3.existsSync)(config.key_path)) {
379
- warnings.push(`signing: key file not found at ${config.key_path} \u2014 run "protect-mcp init" to generate`);
380
- return warnings;
381
- }
382
- try {
383
- const keyData = JSON.parse((0, import_node_fs3.readFileSync)(config.key_path, "utf-8"));
384
- if (!keyData.privateKey || !keyData.publicKey) {
385
- warnings.push("signing: key file missing privateKey or publicKey fields");
386
- return warnings;
387
- }
388
- signerState = {
389
- privateKey: keyData.privateKey,
390
- publicKey: keyData.publicKey,
391
- kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
392
- issuer: config.issuer || keyData.issuer || "protect-mcp"
393
- };
394
- } catch (err) {
395
- warnings.push(`signing: failed to load key file: ${err instanceof Error ? err.message : err}`);
396
- }
405
+ try {
406
+ signerState = {
407
+ privateKey: keyData.privateKey,
408
+ publicKey: keyData.publicKey,
409
+ kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
410
+ issuer: config.issuer || keyData.issuer || "protect-mcp"
411
+ };
412
+ } catch (err) {
413
+ signingInitError = `failed to initialize signer: ${err instanceof Error ? err.message : err}`;
414
+ artifactsModule = null;
415
+ warnings.push(`signing: ${signingInitError} \u2014 enforce mode will fail closed`);
397
416
  }
398
417
  return warnings;
399
418
  }
400
419
  function signDecision(entry) {
420
+ const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
421
+ if (signingConfigured && signingInitError) {
422
+ return {
423
+ ok: false,
424
+ signed: null,
425
+ artifact_type: artifactType,
426
+ warning: `signing initialization failed: ${signingInitError}`,
427
+ error: signingInitError
428
+ };
429
+ }
430
+ if (signingConfigured && (!signerState || !artifactsModule)) {
431
+ const error = "signing was configured but no signer is ready";
432
+ return {
433
+ ok: false,
434
+ signed: null,
435
+ artifact_type: artifactType,
436
+ warning: error,
437
+ error
438
+ };
439
+ }
401
440
  if (!signerState || !artifactsModule) {
402
- return { signed: null, artifact_type: "none" };
441
+ return { ok: false, signed: null, artifact_type: "none" };
403
442
  }
404
- const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
405
443
  try {
406
444
  const payload = {
407
445
  tool: entry.tool,
@@ -442,14 +480,18 @@ function signDecision(entry) {
442
480
  }
443
481
  );
444
482
  return {
483
+ ok: true,
445
484
  signed: JSON.stringify(result.artifact),
446
485
  artifact_type: artifactType
447
486
  };
448
487
  } catch (err) {
488
+ const message = err instanceof Error ? err.message : "unknown error";
449
489
  return {
490
+ ok: false,
450
491
  signed: null,
451
492
  artifact_type: artifactType,
452
- warning: `signing failed: ${err instanceof Error ? err.message : "unknown error"}`
493
+ warning: `signing failed: ${message}`,
494
+ error: message
453
495
  };
454
496
  }
455
497
  }
@@ -462,15 +504,17 @@ function getSignerInfo() {
462
504
  };
463
505
  }
464
506
  function isSigningEnabled() {
465
- return signerState !== null && artifactsModule !== null;
507
+ return signingConfigured && signingInitError === null && signerState !== null && artifactsModule !== null;
466
508
  }
467
- var import_node_fs3, signerState, artifactsModule;
509
+ var import_node_fs3, signerState, artifactsModule, signingConfigured, signingInitError;
468
510
  var init_signing = __esm({
469
511
  "src/signing.ts"() {
470
512
  "use strict";
471
513
  import_node_fs3 = require("fs");
472
514
  signerState = null;
473
515
  artifactsModule = null;
516
+ signingConfigured = false;
517
+ signingInitError = null;
474
518
  }
475
519
  });
476
520
 
@@ -1639,8 +1683,20 @@ var init_gateway = __esm({
1639
1683
  this.evidenceStore.save();
1640
1684
  }
1641
1685
  }
1642
- } else if (signed.warning) {
1643
- process.stderr.write(`[PROTECT_MCP] Warning: ${signed.warning}
1686
+ } else if (signed.error) {
1687
+ const tombstone = JSON.stringify({
1688
+ type: "scopeblind.signing_failure.v1",
1689
+ request_id: log.request_id,
1690
+ tool: log.tool,
1691
+ decision: log.decision,
1692
+ error: signed.error,
1693
+ at: new Date(log.timestamp).toISOString()
1694
+ });
1695
+ try {
1696
+ (0, import_node_fs6.appendFileSync)(this.receiptFilePath, tombstone + "\n");
1697
+ } catch {
1698
+ }
1699
+ process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
1644
1700
  `);
1645
1701
  }
1646
1702
  }
@@ -4768,6 +4824,160 @@ var init_hook_patterns = __esm({
4768
4824
  }
4769
4825
  });
4770
4826
 
4827
+ // src/scopeblind-bridge.ts
4828
+ function getScopeBlindBridge() {
4829
+ if (!singleton) singleton = new ScopeBlindBridge();
4830
+ return singleton;
4831
+ }
4832
+ var DEFAULT_BASE, FLUSH_INTERVAL_MS, BATCH_MAX, BRASS_REFRESH_MARGIN_MS, ScopeBlindBridge, singleton;
4833
+ var init_scopeblind_bridge = __esm({
4834
+ "src/scopeblind-bridge.ts"() {
4835
+ "use strict";
4836
+ DEFAULT_BASE = "https://scopeblind.com";
4837
+ FLUSH_INTERVAL_MS = 5e3;
4838
+ BATCH_MAX = 128;
4839
+ BRASS_REFRESH_MARGIN_MS = 5 * 60 * 1e3;
4840
+ ScopeBlindBridge = class {
4841
+ token;
4842
+ base;
4843
+ tenantOverride;
4844
+ cachedProof = null;
4845
+ queue = [];
4846
+ flushTimer = null;
4847
+ stats;
4848
+ shuttingDown = false;
4849
+ constructor(env = process.env) {
4850
+ this.token = env.SCOPEBLIND_TOKEN || null;
4851
+ this.base = (env.SCOPEBLIND_BASE || DEFAULT_BASE).replace(/\/$/, "");
4852
+ this.tenantOverride = env.SCOPEBLIND_TENANT || null;
4853
+ this.stats = {
4854
+ enabled: Boolean(this.token),
4855
+ tenant_slug: this.tenantOverride,
4856
+ forwarded_total: 0,
4857
+ rejected_total: 0,
4858
+ last_flush_at: null,
4859
+ last_error: null
4860
+ };
4861
+ if (this.enabled()) {
4862
+ this.flushTimer = setInterval(() => {
4863
+ void this.flush();
4864
+ }, FLUSH_INTERVAL_MS);
4865
+ if (typeof this.flushTimer === "object" && this.flushTimer && "unref" in this.flushTimer) {
4866
+ this.flushTimer.unref?.();
4867
+ }
4868
+ process.on("beforeExit", () => {
4869
+ void this.shutdown();
4870
+ });
4871
+ }
4872
+ }
4873
+ enabled() {
4874
+ return Boolean(this.token);
4875
+ }
4876
+ /** Push a signed receipt into the queue. Non-blocking. */
4877
+ forward(signedReceipt) {
4878
+ if (!this.enabled() || this.shuttingDown) return;
4879
+ this.queue.push(signedReceipt);
4880
+ if (this.queue.length >= BATCH_MAX) void this.flush();
4881
+ }
4882
+ /** Flush the queue. Safe to call concurrently. */
4883
+ async flush() {
4884
+ if (!this.enabled() || this.queue.length === 0) return;
4885
+ const batch = this.queue.splice(0, BATCH_MAX);
4886
+ try {
4887
+ const proof = await this.ensureBrassProof();
4888
+ const slug = this.tenantOverride || proof?.tenant_id;
4889
+ if (!slug) {
4890
+ this.queue.unshift(...batch);
4891
+ return;
4892
+ }
4893
+ this.stats.tenant_slug = slug;
4894
+ const res = await fetch(`${this.base}/fn/console/${slug}/receipts`, {
4895
+ method: "POST",
4896
+ headers: {
4897
+ "content-type": "application/json",
4898
+ authorization: `Bearer ${this.token}`,
4899
+ "user-agent": "protect-mcp/scopeblind-bridge"
4900
+ },
4901
+ body: JSON.stringify({ receipts: batch })
4902
+ });
4903
+ if (!res.ok) {
4904
+ const errBody = await res.text().catch(() => "");
4905
+ this.stats.last_error = `HTTP ${res.status} ${errBody.slice(0, 160)}`;
4906
+ this.stats.rejected_total += batch.length;
4907
+ if (res.status >= 500 && res.status !== 503) {
4908
+ this.queue.unshift(...batch);
4909
+ }
4910
+ return;
4911
+ }
4912
+ const body = await res.json().catch(() => ({}));
4913
+ this.stats.forwarded_total += body?.accepted ?? batch.length;
4914
+ this.stats.rejected_total += body?.rejected ?? 0;
4915
+ this.stats.last_flush_at = (/* @__PURE__ */ new Date()).toISOString();
4916
+ this.stats.last_error = null;
4917
+ } catch (err) {
4918
+ this.stats.last_error = String(err?.message || err);
4919
+ this.queue.unshift(...batch);
4920
+ }
4921
+ }
4922
+ /** Exchange SCOPEBLIND_TOKEN for a BRASS-v2 proof; refresh near expiry. */
4923
+ async ensureBrassProof() {
4924
+ if (!this.token) return null;
4925
+ const now = Date.now();
4926
+ if (this.cachedProof && Date.parse(this.cachedProof.expires_at) - now > BRASS_REFRESH_MARGIN_MS) {
4927
+ return this.cachedProof;
4928
+ }
4929
+ try {
4930
+ const res = await fetch(`${this.base}/fn/brass/issue`, {
4931
+ method: "POST",
4932
+ headers: {
4933
+ "content-type": "application/json",
4934
+ "user-agent": "protect-mcp/scopeblind-bridge"
4935
+ },
4936
+ body: JSON.stringify({
4937
+ token: this.token,
4938
+ scope: "protect-mcp-receipt-emit",
4939
+ ttl_seconds: 3600
4940
+ })
4941
+ });
4942
+ if (!res.ok) {
4943
+ const text = await res.text().catch(() => "");
4944
+ this.stats.last_error = `brass-issue: HTTP ${res.status} ${text.slice(0, 160)}`;
4945
+ return null;
4946
+ }
4947
+ const body = await res.json();
4948
+ if (!body?.auth_proof) {
4949
+ this.stats.last_error = "brass-issue: missing auth_proof in response";
4950
+ return null;
4951
+ }
4952
+ this.cachedProof = body.auth_proof;
4953
+ return this.cachedProof;
4954
+ } catch (err) {
4955
+ this.stats.last_error = `brass-issue: ${err?.message || err}`;
4956
+ return null;
4957
+ }
4958
+ }
4959
+ /**
4960
+ * Return a snapshot of bridge stats. Useful for `protect-mcp scopeblind status`.
4961
+ */
4962
+ getStats() {
4963
+ return {
4964
+ ...this.stats,
4965
+ queued: this.queue.length,
4966
+ brass_proof_expires_at: this.cachedProof?.expires_at || null
4967
+ };
4968
+ }
4969
+ /** Flush remaining receipts and stop the interval. Called on process exit. */
4970
+ async shutdown() {
4971
+ if (this.shuttingDown) return;
4972
+ this.shuttingDown = true;
4973
+ if (this.flushTimer) clearInterval(this.flushTimer);
4974
+ if (this.queue.length > 0) await this.flush();
4975
+ }
4976
+ };
4977
+ singleton = null;
4978
+ }
4979
+ });
4980
+
4771
4981
  // src/hook-server.ts
4772
4982
  var hook_server_exports = {};
4773
4983
  __export(hook_server_exports, {
@@ -4976,7 +5186,7 @@ async function handlePreToolUse(input, state) {
4976
5186
  const hookLatency = Date.now() - hookStart;
4977
5187
  const denyKey = `${toolName}:${input.sessionId || "default"}`;
4978
5188
  state.denyCounter.delete(denyKey);
4979
- emitDecisionLog(state, {
5189
+ const emit = emitDecisionLog(state, {
4980
5190
  tool: toolName,
4981
5191
  decision: "allow",
4982
5192
  reason_code: state.cedarPolicies ? "cedar_allow" : state.jsonPolicy ? "policy_allow" : "observe_mode",
@@ -4988,6 +5198,15 @@ async function handlePreToolUse(input, state) {
4988
5198
  sandbox_state: detectSandboxState(),
4989
5199
  plan_receipt_id: state.activePlanReceiptId || void 0
4990
5200
  });
5201
+ if (state.enforce && emit.signingFailed) {
5202
+ return {
5203
+ hookSpecificOutput: {
5204
+ hookEventName: "PreToolUse",
5205
+ permissionDecision: "deny",
5206
+ permissionDecisionReason: `[ScopeBlind] "${toolName}" was blocked because its receipt could not be signed. Failing closed: a governed action that cannot be proven is not allowed.`
5207
+ }
5208
+ };
5209
+ }
4991
5210
  return {};
4992
5211
  }
4993
5212
  async function handlePostToolUse(input, state) {
@@ -5235,11 +5454,35 @@ function emitDecisionLog(state, entry) {
5235
5454
  } catch {
5236
5455
  }
5237
5456
  state.receiptBuffer.add(log.request_id, signed.signed);
5238
- } else if (signed.warning) {
5239
- process.stderr.write(`[PROTECT_MCP] Warning: ${signed.warning}
5457
+ try {
5458
+ const bridge = getScopeBlindBridge();
5459
+ if (bridge.enabled()) {
5460
+ const parsed = typeof signed.signed === "string" ? JSON.parse(signed.signed) : signed.signed;
5461
+ bridge.forward(parsed);
5462
+ }
5463
+ } catch (err) {
5464
+ process.stderr.write(`[PROTECT_MCP] ScopeBlind forward error: ${err instanceof Error ? err.message : err}
5465
+ `);
5466
+ }
5467
+ } else if (signed.error) {
5468
+ const tombstone = JSON.stringify({
5469
+ type: "scopeblind.signing_failure.v1",
5470
+ request_id: log.request_id,
5471
+ tool: log.tool,
5472
+ decision: log.decision,
5473
+ error: signed.error,
5474
+ at: new Date(log.timestamp).toISOString()
5475
+ });
5476
+ try {
5477
+ (0, import_node_fs8.appendFileSync)(state.receiptFilePath, tombstone + "\n");
5478
+ } catch {
5479
+ }
5480
+ process.stderr.write(`[PROTECT_MCP_SIGNING_FAILURE] ${tombstone}
5240
5481
  `);
5482
+ return { signingFailed: true };
5241
5483
  }
5242
5484
  }
5485
+ return { signingFailed: false };
5243
5486
  }
5244
5487
  async function routeHookEvent(input, state) {
5245
5488
  switch (input.hookEventName) {
@@ -5550,6 +5793,7 @@ var init_hook_server = __esm({
5550
5793
  init_signing();
5551
5794
  init_policy();
5552
5795
  init_http_server();
5796
+ init_scopeblind_bridge();
5553
5797
  DEFAULT_PORT = 9377;
5554
5798
  LOG_FILE3 = ".protect-mcp-log.jsonl";
5555
5799
  RECEIPTS_FILE2 = ".protect-mcp-receipts.jsonl";
@@ -7712,6 +7956,36 @@ main().catch((err) => {
7712
7956
  `);
7713
7957
  process.exit(1);
7714
7958
  });
7959
+ /**
7960
+ * scopeblind-bridge.ts
7961
+ *
7962
+ * Optional bridge between protect-mcp (local, MIT) and a paid ScopeBlind
7963
+ * tenant. When SCOPEBLIND_TOKEN is set in the environment, every signed
7964
+ * receipt that protect-mcp emits also gets forwarded to the tenant's
7965
+ * dashboard at https://scopeblind.com/console/<slug>.
7966
+ *
7967
+ * Lifecycle:
7968
+ * 1. On first use, exchange SCOPEBLIND_TOKEN for a short-lived BRASS-v2
7969
+ * auth proof from /fn/brass/issue. Cache the proof in memory until
7970
+ * ~5 minutes before expiry, then refresh.
7971
+ * 2. As receipts are emitted by hook-server.ts, push them into an
7972
+ * in-memory batch queue.
7973
+ * 3. Flush the queue every 5s (or when it reaches 128 receipts) by POSTing
7974
+ * to /fn/console/<slug>/receipts with Bearer SCOPEBLIND_TOKEN.
7975
+ *
7976
+ * Failure mode: forward errors NEVER throw upstream. protect-mcp continues
7977
+ * to mint and persist receipts locally regardless of dashboard availability.
7978
+ * The bridge logs failures to stderr (best-effort) and retries on the next
7979
+ * flush.
7980
+ *
7981
+ * Configuration:
7982
+ * SCOPEBLIND_TOKEN Tenant bearer token (from welcome email).
7983
+ * SCOPEBLIND_TENANT Optional slug override. By default we discover
7984
+ * the slug from the BRASS proof's tenant_id.
7985
+ * SCOPEBLIND_BASE Defaults to https://scopeblind.com.
7986
+ *
7987
+ * @license MIT
7988
+ */
7715
7989
  /*! Bundled license information:
7716
7990
 
7717
7991
  @noble/hashes/esm/utils.js:
package/dist/cli.mjs CHANGED
@@ -3,17 +3,17 @@ import {
3
3
  formatSimulation,
4
4
  parseLogFile,
5
5
  simulate
6
- } from "./chunk-GQWJCHQV.mjs";
6
+ } from "./chunk-S4ICHNSP.mjs";
7
7
  import {
8
8
  ProtectGateway,
9
9
  validateCredentials
10
- } from "./chunk-BYYWYSHM.mjs";
10
+ } from "./chunk-PLKRTBDR.mjs";
11
11
  import {
12
12
  initSigning,
13
13
  isCedarAvailable,
14
14
  loadCedarPolicies,
15
15
  loadPolicy
16
- } from "./chunk-YTBC72JJ.mjs";
16
+ } from "./chunk-UV53U6D4.mjs";
17
17
  import "./chunk-PQJP2ZCI.mjs";
18
18
 
19
19
  // src/cli.ts
@@ -145,7 +145,7 @@ async function handleInit(argv) {
145
145
  let keypair;
146
146
  {
147
147
  const { randomBytes } = await import("crypto");
148
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
148
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
149
149
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
150
150
  const privateKey = randomBytes(32);
151
151
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -765,7 +765,7 @@ ${bold("protect-mcp quickstart")}
765
765
  const { randomBytes } = await import("crypto");
766
766
  let keypair;
767
767
  try {
768
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
768
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
769
769
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
770
770
  const privateKey = randomBytes(32);
771
771
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -1111,7 +1111,7 @@ ${bold("protect-mcp init-hooks")}
1111
1111
  if (!existsSync(keysDir)) mkdirSync(keysDir, { recursive: true });
1112
1112
  const { randomBytes: rb } = await import("crypto");
1113
1113
  try {
1114
- const { ed25519 } = await import("./ed25519-V7HDL2WC.mjs");
1114
+ const { ed25519 } = await import("./ed25519-DZMMNNVE.mjs");
1115
1115
  const { bytesToHex } = await import("./utils-6AYZFE5A.mjs");
1116
1116
  const privateKey = rb(32);
1117
1117
  const publicKey = ed25519.getPublicKey(privateKey);
@@ -1442,7 +1442,7 @@ async function main() {
1442
1442
  if (useHttp) {
1443
1443
  const portIdx = args.indexOf("--port");
1444
1444
  const httpPort = portIdx >= 0 && args[portIdx + 1] ? parseInt(args[portIdx + 1]) : 3e3;
1445
- const { startHttpTransport } = await import("./http-transport-LNBENGXD.mjs");
1445
+ const { startHttpTransport } = await import("./http-transport-MO32ESHZ.mjs");
1446
1446
  startHttpTransport({ port: httpPort, config, serverCommand: childCommand });
1447
1447
  return;
1448
1448
  }
@@ -0,0 +1,38 @@
1
+ import {
2
+ ED25519_TORSION_SUBGROUP,
3
+ RistrettoPoint,
4
+ ed25519,
5
+ ed25519_hasher,
6
+ ed25519ctx,
7
+ ed25519ph,
8
+ edwardsToMontgomery,
9
+ edwardsToMontgomeryPriv,
10
+ edwardsToMontgomeryPub,
11
+ encodeToCurve,
12
+ hashToCurve,
13
+ hashToRistretto255,
14
+ hash_to_ristretto255,
15
+ ristretto255,
16
+ ristretto255_hasher,
17
+ x25519
18
+ } from "./chunk-LYKNULYU.mjs";
19
+ import "./chunk-D733KAPG.mjs";
20
+ import "./chunk-PQJP2ZCI.mjs";
21
+ export {
22
+ ED25519_TORSION_SUBGROUP,
23
+ RistrettoPoint,
24
+ ed25519,
25
+ ed25519_hasher,
26
+ ed25519ctx,
27
+ ed25519ph,
28
+ edwardsToMontgomery,
29
+ edwardsToMontgomeryPriv,
30
+ edwardsToMontgomeryPub,
31
+ encodeToCurve,
32
+ hashToCurve,
33
+ hashToRistretto255,
34
+ hash_to_ristretto255,
35
+ ristretto255,
36
+ ristretto255_hasher,
37
+ x25519
38
+ };