protect-mcp 0.4.6 → 0.5.0

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.
@@ -1,81 +1,16 @@
1
- // src/policy.ts
2
- import { createHash } from "crypto";
3
- import { readFileSync } from "fs";
4
- function loadPolicy(path) {
5
- const raw = readFileSync(path, "utf-8");
6
- const parsed = JSON.parse(raw);
7
- if (!parsed.tools || typeof parsed.tools !== "object") {
8
- throw new Error(`Invalid policy file: missing "tools" object in ${path}`);
9
- }
10
- const policy = {
11
- tools: parsed.tools,
12
- default_tier: parsed.default_tier || "unknown",
13
- policy_engine: parsed.policy_engine || "built-in",
14
- ...parsed.external ? { external: parsed.external } : {}
15
- };
16
- const digest = computePolicyDigest(policy);
17
- return {
18
- policy,
19
- digest,
20
- credentials: parsed.credentials,
21
- signing: parsed.signing
22
- };
23
- }
24
- function computePolicyDigest(policy) {
25
- const canonical = JSON.stringify(sortKeysDeep(policy));
26
- return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
27
- }
28
- function sortKeysDeep(obj) {
29
- if (obj === null || typeof obj !== "object") return obj;
30
- if (Array.isArray(obj)) return obj.map(sortKeysDeep);
31
- const sorted = {};
32
- for (const key of Object.keys(obj).sort()) {
33
- sorted[key] = sortKeysDeep(obj[key]);
34
- }
35
- return sorted;
36
- }
37
- function getToolPolicy(toolName, policy) {
38
- if (!policy) {
39
- return { require: "any" };
40
- }
41
- if (policy.tools[toolName]) {
42
- return policy.tools[toolName];
43
- }
44
- if (policy.tools["*"]) {
45
- return policy.tools["*"];
46
- }
47
- return { require: "any" };
48
- }
49
- function parseRateLimit(spec) {
50
- const match = spec.match(/^(\d+)\/(second|minute|hour|day)$/);
51
- if (!match) {
52
- throw new Error(`Invalid rate limit format: "${spec}". Expected "N/unit" (e.g. "5/hour")`);
53
- }
54
- const count = parseInt(match[1], 10);
55
- const unit = match[2];
56
- const windowMs = {
57
- second: 1e3,
58
- minute: 6e4,
59
- hour: 36e5,
60
- day: 864e5
61
- };
62
- return { count, windowMs: windowMs[unit] };
63
- }
64
- function checkRateLimit(key, limit, store) {
65
- const now = Date.now();
66
- const windowStart = now - limit.windowMs;
67
- const timestamps = (store.get(key) || []).filter((t) => t > windowStart);
68
- if (timestamps.length >= limit.count) {
69
- store.set(key, timestamps);
70
- return { allowed: false, remaining: 0 };
71
- }
72
- timestamps.push(now);
73
- store.set(key, timestamps);
74
- return { allowed: true, remaining: limit.count - timestamps.length };
75
- }
1
+ import {
2
+ ReceiptBuffer,
3
+ checkRateLimit,
4
+ evaluateCedar,
5
+ getToolPolicy,
6
+ isSigningEnabled,
7
+ parseRateLimit,
8
+ signDecision,
9
+ startStatusServer
10
+ } from "./chunk-V52W3XIN.mjs";
76
11
 
77
12
  // src/evidence-store.ts
78
- import { readFileSync as readFileSync2, writeFileSync, existsSync } from "fs";
13
+ import { readFileSync, writeFileSync, existsSync } from "fs";
79
14
  import { join } from "path";
80
15
  var DEFAULT_THRESHOLDS = {
81
16
  min_receipts: 10,
@@ -162,7 +97,7 @@ var EvidenceStore = class {
162
97
  load() {
163
98
  if (!existsSync(this.filePath)) return;
164
99
  try {
165
- const raw = readFileSync2(this.filePath, "utf-8");
100
+ const raw = readFileSync(this.filePath, "utf-8");
166
101
  const parsed = JSON.parse(raw);
167
102
  if (parsed.agents && typeof parsed.agents === "object") {
168
103
  for (const [id, record] of Object.entries(parsed.agents)) {
@@ -307,103 +242,6 @@ function validateCredentials(credentials) {
307
242
  return warnings;
308
243
  }
309
244
 
310
- // src/signing.ts
311
- import { readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
312
- var signerState = null;
313
- var artifactsModule = null;
314
- async function initSigning(config) {
315
- const warnings = [];
316
- if (!config || config.enabled === false) {
317
- return warnings;
318
- }
319
- try {
320
- const moduleName = "@veritasacta/artifacts";
321
- artifactsModule = await import(
322
- /* @vite-ignore */
323
- moduleName
324
- );
325
- } catch {
326
- warnings.push("signing: @veritasacta/artifacts not available \u2014 receipts will be unsigned");
327
- return warnings;
328
- }
329
- if (config.key_path) {
330
- if (!existsSync2(config.key_path)) {
331
- warnings.push(`signing: key file not found at ${config.key_path} \u2014 run "protect-mcp init" to generate`);
332
- return warnings;
333
- }
334
- try {
335
- const keyData = JSON.parse(readFileSync3(config.key_path, "utf-8"));
336
- if (!keyData.privateKey || !keyData.publicKey) {
337
- warnings.push("signing: key file missing privateKey or publicKey fields");
338
- return warnings;
339
- }
340
- signerState = {
341
- privateKey: keyData.privateKey,
342
- publicKey: keyData.publicKey,
343
- kid: keyData.kid || artifactsModule.computeKid(keyData.publicKey),
344
- issuer: config.issuer || keyData.issuer || "protect-mcp"
345
- };
346
- } catch (err) {
347
- warnings.push(`signing: failed to load key file: ${err instanceof Error ? err.message : err}`);
348
- }
349
- }
350
- return warnings;
351
- }
352
- function signDecision(entry) {
353
- if (!signerState || !artifactsModule) {
354
- return { signed: null, artifact_type: "none" };
355
- }
356
- const artifactType = entry.decision === "deny" ? "gateway_restraint" : "decision_receipt";
357
- try {
358
- const payload = {
359
- tool: entry.tool,
360
- decision: entry.decision,
361
- reason_code: entry.reason_code,
362
- policy_digest: entry.policy_digest,
363
- scope: entry.request_id,
364
- // request scope
365
- mode: entry.mode,
366
- request_id: entry.request_id
367
- };
368
- if (entry.tier) payload.tier = entry.tier;
369
- if (entry.credential_ref) payload.credential_ref = entry.credential_ref;
370
- if (entry.rate_limit_remaining !== void 0) {
371
- payload.rate_limit_remaining = entry.rate_limit_remaining;
372
- }
373
- if (entry.policy_engine) payload.policy_engine = entry.policy_engine;
374
- const result = artifactsModule.createSignedArtifact(
375
- artifactType,
376
- payload,
377
- signerState.privateKey,
378
- {
379
- kid: signerState.kid,
380
- issuer: signerState.issuer
381
- }
382
- );
383
- return {
384
- signed: JSON.stringify(result.artifact),
385
- artifact_type: artifactType
386
- };
387
- } catch (err) {
388
- return {
389
- signed: null,
390
- artifact_type: artifactType,
391
- warning: `signing failed: ${err instanceof Error ? err.message : "unknown error"}`
392
- };
393
- }
394
- }
395
- function getSignerInfo() {
396
- if (!signerState) return null;
397
- return {
398
- publicKey: signerState.publicKey,
399
- kid: signerState.kid,
400
- issuer: signerState.issuer
401
- };
402
- }
403
- function isSigningEnabled() {
404
- return signerState !== null && artifactsModule !== null;
405
- }
406
-
407
245
  // src/external-pdp.ts
408
246
  async function queryExternalPDP(context, config) {
409
247
  const timeout = config.timeout_ms || 500;
@@ -564,175 +402,6 @@ function buildDecisionContext(toolName, tier, opts) {
564
402
  };
565
403
  }
566
404
 
567
- // src/cedar-evaluator.ts
568
- import { createHash as createHash2 } from "crypto";
569
- import { readFileSync as readFileSync4, readdirSync, existsSync as existsSync3 } from "fs";
570
- import { join as join2, extname } from "path";
571
- var cedarWasm = null;
572
- var loadAttempted = false;
573
- async function ensureCedarWasm() {
574
- if (cedarWasm) return true;
575
- if (loadAttempted) return false;
576
- loadAttempted = true;
577
- try {
578
- const moduleName = "@cedar-policy/cedar-wasm";
579
- cedarWasm = await import(
580
- /* @vite-ignore */
581
- moduleName
582
- );
583
- return true;
584
- } catch {
585
- return false;
586
- }
587
- }
588
- function loadCedarPolicies(dirPath) {
589
- if (!existsSync3(dirPath)) {
590
- throw new Error(`Cedar policy directory not found: ${dirPath}`);
591
- }
592
- const entries = readdirSync(dirPath).filter((f) => extname(f) === ".cedar").sort();
593
- if (entries.length === 0) {
594
- throw new Error(`No .cedar files found in: ${dirPath}`);
595
- }
596
- const sources = [];
597
- for (const file of entries) {
598
- const content = readFileSync4(join2(dirPath, file), "utf-8");
599
- sources.push(content);
600
- }
601
- const concatenated = sources.join("\n\n");
602
- const digest = createHash2("sha256").update(concatenated).digest("hex").slice(0, 16);
603
- return {
604
- source: concatenated,
605
- digest,
606
- fileCount: entries.length,
607
- files: entries
608
- };
609
- }
610
- function buildEntities(req) {
611
- const agentId = req.agentId || req.tier;
612
- return [
613
- {
614
- uid: { type: "Agent", id: agentId },
615
- attrs: {
616
- tier: req.tier,
617
- ...req.agentId ? { agent_id: req.agentId } : {}
618
- },
619
- parents: []
620
- },
621
- {
622
- uid: { type: "Tool", id: req.tool },
623
- attrs: {},
624
- parents: []
625
- }
626
- ];
627
- }
628
- async function evaluateCedar(policySet, req) {
629
- const available = await ensureCedarWasm();
630
- if (!available) {
631
- return {
632
- allowed: true,
633
- reason: "cedar_wasm_not_available",
634
- metadata: { fallback: true }
635
- };
636
- }
637
- try {
638
- const agentId = req.agentId || req.tier;
639
- const authRequest = {
640
- principal: { type: "Agent", id: agentId },
641
- action: { type: "Action", id: "MCP::Tool::call" },
642
- resource: { type: "Tool", id: req.tool },
643
- context: {
644
- tier: req.tier,
645
- ...req.context || {}
646
- }
647
- };
648
- const entities = buildEntities(req);
649
- let result;
650
- if (typeof cedarWasm.isAuthorized === "function") {
651
- result = cedarWasm.isAuthorized({
652
- policies: policySet.source,
653
- entities,
654
- principal: authRequest.principal,
655
- action: authRequest.action,
656
- resource: authRequest.resource,
657
- context: authRequest.context,
658
- schema: null
659
- // No schema enforcement — Cedar still evaluates correctly
660
- });
661
- } else if (typeof cedarWasm.checkAuthorization === "function") {
662
- result = cedarWasm.checkAuthorization(
663
- policySet.source,
664
- JSON.stringify(entities),
665
- JSON.stringify(authRequest)
666
- );
667
- } else {
668
- const cedarEngine = cedarWasm.default || cedarWasm;
669
- if (typeof cedarEngine.isAuthorized === "function") {
670
- result = cedarEngine.isAuthorized({
671
- policies: policySet.source,
672
- entities,
673
- principal: authRequest.principal,
674
- action: authRequest.action,
675
- resource: authRequest.resource,
676
- context: authRequest.context,
677
- schema: null
678
- });
679
- } else {
680
- return {
681
- allowed: true,
682
- reason: "cedar_wasm_api_unsupported",
683
- metadata: { fallback: true, exports: Object.keys(cedarWasm) }
684
- };
685
- }
686
- }
687
- const decision = parseWasmResult(result);
688
- return {
689
- allowed: decision.allowed,
690
- reason: decision.allowed ? void 0 : `cedar_deny${decision.diagnostics ? ": " + decision.diagnostics : ""}`,
691
- metadata: {
692
- policy_digest: policySet.digest,
693
- ...decision.matchedPolicies ? { matched_policies: decision.matchedPolicies } : {}
694
- }
695
- };
696
- } catch (err) {
697
- return {
698
- allowed: true,
699
- reason: `cedar_eval_error: ${err instanceof Error ? err.message : "unknown"}`,
700
- metadata: { fallback: true, error: true }
701
- };
702
- }
703
- }
704
- function parseWasmResult(result) {
705
- if (!result) {
706
- return { allowed: true, diagnostics: "null result from Cedar WASM" };
707
- }
708
- if (result.type === "allow" || result.type === "Allow") {
709
- return { allowed: true };
710
- }
711
- if (result.type === "deny" || result.type === "Deny") {
712
- return {
713
- allowed: false,
714
- diagnostics: result.diagnostics ? JSON.stringify(result.diagnostics) : void 0,
715
- matchedPolicies: result.diagnostics?.reasons
716
- };
717
- }
718
- if (result.decision === "Allow") {
719
- return { allowed: true };
720
- }
721
- if (result.decision === "Deny") {
722
- return {
723
- allowed: false,
724
- diagnostics: result.diagnostics ? JSON.stringify(result.diagnostics) : void 0
725
- };
726
- }
727
- if (typeof result === "boolean") {
728
- return { allowed: result };
729
- }
730
- return { allowed: true, diagnostics: `unknown result format: ${JSON.stringify(result)}` };
731
- }
732
- async function isCedarAvailable() {
733
- return ensureCedarWasm();
734
- }
735
-
736
405
  // src/notifications.ts
737
406
  async function sendApprovalNotification(config, notification) {
738
407
  const promises = [];
@@ -918,235 +587,8 @@ import { spawn } from "child_process";
918
587
  import { randomUUID, randomBytes } from "crypto";
919
588
  import { createInterface } from "readline";
920
589
  import { appendFileSync } from "fs";
921
- import { join as join4 } from "path";
922
-
923
- // src/http-server.ts
924
- import { createServer } from "http";
925
- import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
926
- import { join as join3 } from "path";
590
+ import { join as join2 } from "path";
927
591
  var LOG_FILE = ".protect-mcp-log.jsonl";
928
- var MAX_RECEIPTS = 100;
929
- var ReceiptBuffer = class {
930
- receipts = [];
931
- add(requestId, receipt) {
932
- this.receipts.push({
933
- request_id: requestId,
934
- receipt,
935
- timestamp: Date.now()
936
- });
937
- if (this.receipts.length > MAX_RECEIPTS) {
938
- this.receipts = this.receipts.slice(-MAX_RECEIPTS);
939
- }
940
- }
941
- getAll() {
942
- return [...this.receipts].reverse();
943
- }
944
- getById(requestId) {
945
- return this.receipts.find((r) => r.request_id === requestId);
946
- }
947
- count() {
948
- return this.receipts.length;
949
- }
950
- getLatest() {
951
- return this.receipts.length > 0 ? this.receipts[this.receipts.length - 1] : void 0;
952
- }
953
- };
954
- function startStatusServer(config, receiptBuffer, approvalStore, approvalNonce) {
955
- const startTime = Date.now();
956
- const logDir = process.cwd();
957
- const server = createServer((req, res) => {
958
- res.setHeader("Access-Control-Allow-Origin", "*");
959
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
960
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
961
- res.setHeader("Content-Type", "application/json");
962
- if (req.method === "OPTIONS") {
963
- res.writeHead(204);
964
- res.end();
965
- return;
966
- }
967
- const url = new URL(req.url || "/", `http://localhost:${config.port}`);
968
- const path = url.pathname;
969
- try {
970
- if (path === "/health") {
971
- handleHealth(res, startTime, config);
972
- } else if (path === "/status") {
973
- handleStatus(res, logDir);
974
- } else if (path === "/receipts") {
975
- handleReceipts(res, receiptBuffer, url);
976
- } else if (path === "/receipts/latest") {
977
- handleReceiptLatest(res, receiptBuffer);
978
- } else if (path.startsWith("/receipts/")) {
979
- const id = path.slice("/receipts/".length);
980
- handleReceiptById(res, receiptBuffer, id);
981
- } else if (path === "/approve" && req.method === "POST") {
982
- handleApprove(req, res, approvalStore, approvalNonce);
983
- } else if (path === "/approvals" && req.method === "GET") {
984
- handleListApprovals(res, approvalStore);
985
- } else {
986
- res.writeHead(404);
987
- res.end(JSON.stringify({ error: "not_found", endpoints: ["/health", "/status", "/receipts", "/receipts/latest", "/receipts/:id", "/approve", "/approvals"] }));
988
- }
989
- } catch (err) {
990
- res.writeHead(500);
991
- res.end(JSON.stringify({ error: "internal_error" }));
992
- }
993
- });
994
- server.on("error", (err) => {
995
- if (config.verbose) {
996
- process.stderr.write(`[PROTECT_MCP] HTTP status server error: ${err.message}
997
- `);
998
- }
999
- });
1000
- server.listen(config.port, "127.0.0.1", () => {
1001
- if (config.verbose) {
1002
- process.stderr.write(`[PROTECT_MCP] HTTP status server listening on http://127.0.0.1:${config.port}
1003
- `);
1004
- }
1005
- });
1006
- server.unref();
1007
- return server;
1008
- }
1009
- function handleHealth(res, startTime, config) {
1010
- res.writeHead(200);
1011
- res.end(JSON.stringify({
1012
- status: "ok",
1013
- uptime_ms: Date.now() - startTime,
1014
- mode: config.mode,
1015
- version: "0.3.1"
1016
- }));
1017
- }
1018
- function handleStatus(res, logDir) {
1019
- const logPath = join3(logDir, LOG_FILE);
1020
- if (!existsSync4(logPath)) {
1021
- res.writeHead(200);
1022
- res.end(JSON.stringify({ entries: 0, message: "no log file yet" }));
1023
- return;
1024
- }
1025
- const raw = readFileSync5(logPath, "utf-8");
1026
- const lines = raw.trim().split("\n").filter(Boolean);
1027
- const entries = [];
1028
- for (const line of lines) {
1029
- try {
1030
- entries.push(JSON.parse(line));
1031
- } catch {
1032
- }
1033
- }
1034
- const toolCounts = {};
1035
- let allowCount = 0, denyCount = 0;
1036
- const tierCounts = {};
1037
- for (const e of entries) {
1038
- toolCounts[e.tool] = (toolCounts[e.tool] || 0) + 1;
1039
- if (e.decision === "allow") allowCount++;
1040
- else denyCount++;
1041
- if (e.tier) tierCounts[e.tier] = (tierCounts[e.tier] || 0) + 1;
1042
- }
1043
- res.writeHead(200);
1044
- res.end(JSON.stringify({
1045
- entries: entries.length,
1046
- allow: allowCount,
1047
- deny: denyCount,
1048
- tools: toolCounts,
1049
- tiers: tierCounts,
1050
- first_timestamp: entries.length > 0 ? entries[0].timestamp : null,
1051
- last_timestamp: entries.length > 0 ? entries[entries.length - 1].timestamp : null
1052
- }));
1053
- }
1054
- function handleReceipts(res, buffer, url) {
1055
- const limit = parseInt(url.searchParams.get("limit") || "20", 10);
1056
- const receipts = buffer.getAll().slice(0, Math.min(limit, MAX_RECEIPTS));
1057
- res.writeHead(200);
1058
- res.end(JSON.stringify({
1059
- count: receipts.length,
1060
- total: buffer.count(),
1061
- receipts
1062
- }));
1063
- }
1064
- function handleReceiptLatest(res, buffer) {
1065
- const latest = buffer.getLatest();
1066
- if (!latest) {
1067
- res.writeHead(404);
1068
- res.end(JSON.stringify({ error: "no_receipts", message: "No receipts yet. Make a tool call through protect-mcp first." }));
1069
- return;
1070
- }
1071
- res.writeHead(200);
1072
- res.end(JSON.stringify(latest));
1073
- }
1074
- function handleReceiptById(res, buffer, id) {
1075
- const receipt = buffer.getById(id);
1076
- if (!receipt) {
1077
- res.writeHead(404);
1078
- res.end(JSON.stringify({ error: "receipt_not_found", request_id: id }));
1079
- return;
1080
- }
1081
- res.writeHead(200);
1082
- res.end(JSON.stringify(receipt));
1083
- }
1084
- function handleApprove(req, res, approvalStore, expectedNonce) {
1085
- if (!approvalStore) {
1086
- res.writeHead(503);
1087
- res.end(JSON.stringify({ error: "approval_store_not_available" }));
1088
- return;
1089
- }
1090
- let body = "";
1091
- req.on("data", (chunk) => {
1092
- body += chunk.toString();
1093
- });
1094
- req.on("end", () => {
1095
- try {
1096
- const { request_id, tool, mode, nonce } = JSON.parse(body);
1097
- if (expectedNonce && nonce !== expectedNonce) {
1098
- res.writeHead(403);
1099
- res.end(JSON.stringify({ error: "invalid_nonce", message: "Approval nonce does not match. Check stderr output for the correct nonce." }));
1100
- return;
1101
- }
1102
- if (!tool || typeof tool !== "string") {
1103
- res.writeHead(400);
1104
- res.end(JSON.stringify({ error: "missing_tool", usage: '{"request_id":"abc123","tool":"send_email","mode":"once|always","nonce":"..."}' }));
1105
- return;
1106
- }
1107
- const grantMode = mode === "always" ? "always" : "once";
1108
- const ttlMs = grantMode === "once" ? 5 * 60 * 1e3 : 24 * 60 * 60 * 1e3;
1109
- const grantEntry = { tool, mode: grantMode, expires_at: Date.now() + ttlMs };
1110
- if (grantMode === "always") {
1111
- approvalStore.set(`always:${tool}`, grantEntry);
1112
- } else if (request_id) {
1113
- approvalStore.set(request_id, grantEntry);
1114
- } else {
1115
- approvalStore.set(tool, grantEntry);
1116
- }
1117
- res.writeHead(200);
1118
- res.end(JSON.stringify({
1119
- approved: true,
1120
- request_id: request_id || null,
1121
- tool,
1122
- mode: grantMode,
1123
- expires_in_seconds: ttlMs / 1e3
1124
- }));
1125
- } catch {
1126
- res.writeHead(400);
1127
- res.end(JSON.stringify({ error: "invalid_json", usage: '{"request_id":"abc123","tool":"send_email","mode":"once","nonce":"..."}' }));
1128
- }
1129
- });
1130
- }
1131
- function handleListApprovals(res, approvalStore) {
1132
- if (!approvalStore) {
1133
- res.writeHead(200);
1134
- res.end(JSON.stringify({ grants: [] }));
1135
- return;
1136
- }
1137
- const now = Date.now();
1138
- const grants = [];
1139
- for (const [key, grant] of approvalStore) {
1140
- if (now < grant.expires_at) {
1141
- grants.push({ key, tool: grant.tool, mode: grant.mode, expires_in_seconds: Math.round((grant.expires_at - now) / 1e3) });
1142
- }
1143
- }
1144
- res.writeHead(200);
1145
- res.end(JSON.stringify({ grants }));
1146
- }
1147
-
1148
- // src/gateway.ts
1149
- var LOG_FILE2 = ".protect-mcp-log.jsonl";
1150
592
  var RECEIPTS_FILE = ".protect-mcp-receipts.jsonl";
1151
593
  var ProtectGateway = class {
1152
594
  child = null;
@@ -1172,8 +614,8 @@ var ProtectGateway = class {
1172
614
  cedarPolicySet = null;
1173
615
  constructor(config) {
1174
616
  this.config = config;
1175
- this.logFilePath = join4(process.cwd(), LOG_FILE2);
1176
- this.receiptFilePath = join4(process.cwd(), RECEIPTS_FILE);
617
+ this.logFilePath = join2(process.cwd(), LOG_FILE);
618
+ this.receiptFilePath = join2(process.cwd(), RECEIPTS_FILE);
1177
619
  this.evidenceStore = new EvidenceStore();
1178
620
  this.receiptBuffer = new ReceiptBuffer();
1179
621
  this.notificationConfig = parseNotificationConfigFromEnv();
@@ -1663,23 +1105,13 @@ var ProtectGateway = class {
1663
1105
  };
1664
1106
 
1665
1107
  export {
1666
- loadPolicy,
1667
- getToolPolicy,
1668
- parseRateLimit,
1669
- checkRateLimit,
1670
1108
  evaluateTier,
1671
1109
  meetsMinTier,
1672
1110
  resolveCredential,
1673
1111
  listCredentialLabels,
1674
1112
  validateCredentials,
1675
- initSigning,
1676
- signDecision,
1677
- getSignerInfo,
1678
- isSigningEnabled,
1679
1113
  queryExternalPDP,
1680
1114
  buildDecisionContext,
1681
- loadCedarPolicies,
1682
- isCedarAvailable,
1683
1115
  sendApprovalNotification,
1684
1116
  parseNotificationConfigFromEnv,
1685
1117
  ProtectGateway