cwn-eve-trust-gate 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+
4
+ Copyright (c) 2026 Cyber Warrior Network
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
7
+ this file except in compliance with the License. You may obtain a copy of the
8
+ License at:
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software distributed
13
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14
+ CONDITIONS OF ANY KIND, either express or implied.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # eve-trust-gate
2
+
3
+ [eve](https://github.com/vercel/eve) tools for **Trust Gate** post-quantum, tamper-evident receipts on consequential agent actions.
4
+
5
+ Trust Gate receipts are signed Ed25519 + ML-DSA-65 (FIPS 204) by the hosted MCP server (no local signing key). Each receipt is verifiable offline from the certificate alone. The hosted server defaults to PQ-required verify (defends against Ed25519-only downgrade); set `TRUST_GATE_REQUIRE_PQ=false` on your own deployment to allow Ed25519-only receipts.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install cwn-eve-trust-gate
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Each tool is a named export. Drop one into `agent/tools/` per file, re-exported as the
16
+ default — eve derives a tool's runtime name from the filename, not from any `name` field
17
+ on the definition:
18
+
19
+ ```ts
20
+ // agent/tools/mint_action_receipt.ts
21
+ export { mintActionReceipt as default } from "cwn-eve-trust-gate";
22
+ ```
23
+
24
+ ```ts
25
+ // agent/tools/verify_receipt.ts
26
+ export { verifyReceipt as default } from "cwn-eve-trust-gate";
27
+ ```
28
+
29
+ Repeat for `gateDecision`, `checkEgress`, `runExitDrill` — or use the factory to see all
30
+ five at once:
31
+
32
+ ```ts
33
+ import { createTrustGateTools } from "cwn-eve-trust-gate";
34
+
35
+ console.log(Object.keys(createTrustGateTools()));
36
+ // ["mintActionReceipt", "verifyReceipt", "gateDecision", "checkEgress", "runExitDrill"]
37
+ ```
38
+
39
+ Typical flow inside an agent:
40
+
41
+ 1. the agent decides to do something consequential
42
+ 2. it calls `mint_action_receipt(agentId, operation, target)`
43
+ 3. the receipt is stored, attached to the audit log, returned to the user
44
+ 4. later anyone (including this same package) can call `verify_receipt(receipt)` to confirm tamper-free
45
+
46
+ ## Configuration
47
+
48
+ ```bash
49
+ # point at a different deployment (self-hosted)
50
+ export TRUST_GATE_URL="https://trust-gate-mcp.onrender.com"
51
+ ```
52
+
53
+ ## Tools
54
+
55
+ | Tool | Purpose |
56
+ |---|---|
57
+ | `mintActionReceipt` | Mint a post-quantum receipt for any consequential agent action. |
58
+ | `verifyReceipt` | Calls the hosted server to verify a receipt's signature and hash. Defaults to PQ-required. |
59
+ | `gateDecision` | Two-phase (PREVIEW/COMMIT) decision gate with a tamper-evident receipt on commit. |
60
+ | `checkEgress` | Classifies outbound data (PUBLIC/INTERNAL/CONFIDENTIAL/RESTRICTED); the caller acts on a RESTRICTED result. |
61
+ | `runExitDrill` | Vendor exit readiness drill, run server-side — checks the hosted server's signing key, model access, data export. |
62
+
63
+ ## Telemetry
64
+
65
+ Each tool invocation makes one fire-and-forget `GET /x?via=eve&kind=api` against the Trust Gate server. No PII, no cookies, no fingerprinting — just a channel tag so adoption per framework is measurable. Telemetry never blocks or fails the tool.
66
+
67
+ ## Background
68
+
69
+ * **Trust Gate MCP** — the hosted server: <https://trust-gate-mcp.onrender.com>
70
+ * **eve** — the Vercel agent framework this package targets: <https://github.com/vercel/eve>
71
+ * **Sibling adapters** — same protocol, other frameworks: [langchain-trust-gate](https://github.com/CWNApps/langchain-trust-gate), [crewai-trust-gate](https://github.com/CWNApps/crewai-trust-gate), [llama-index-trust-gate](https://github.com/CWNApps/llama-index-trust-gate)
72
+ * **OAO** — the open-source receipt primitive: <https://github.com/CWNApps/openagentontology>
73
+
74
+ ## License
75
+
76
+ Apache-2.0.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Transport layer for the hosted Trust Gate MCP server.
3
+ *
4
+ * Same wire protocol as langchain-trust-gate / crewai-trust-gate / llama-index-trust-gate:
5
+ * one JSON-RPC 2.0 `tools/call` POST to `/mcp`, plus one fire-and-forget telemetry ping to
6
+ * `/x?via=eve`. No PII, no cookies — telemetry is a bare channel-attribution tag and never
7
+ * blocks or fails a tool call.
8
+ */
9
+ export declare const TRUST_GATE_URL: string;
10
+ export declare class TrustGateError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ interface McpCallOptions {
14
+ timeoutMs?: number;
15
+ }
16
+ /** H4 (pol.must_do.150): a Trust Gate `kid` is `sha256(pubkey_bytes)[:32]` hex chars —
17
+ * exactly 128 bits. A `kid` of any other width indicates a malformed or spoofed
18
+ * response and must not be trusted as a same-notary check. */
19
+ export declare function isValidKid(kid: unknown): kid is string;
20
+ /** One JSON-RPC `tools/call` against the hosted Trust Gate MCP server. */
21
+ export declare function mcpCall(method: string, args: Record<string, unknown>, options?: McpCallOptions): Promise<Record<string, unknown>>;
22
+ /** Fire-and-forget channel-attribution ping. Any failure is swallowed — telemetry
23
+ * must never block or fail a tool call. */
24
+ export declare function pingTelemetry(kind?: string): void;
25
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Transport layer for the hosted Trust Gate MCP server.
3
+ *
4
+ * Same wire protocol as langchain-trust-gate / crewai-trust-gate / llama-index-trust-gate:
5
+ * one JSON-RPC 2.0 `tools/call` POST to `/mcp`, plus one fire-and-forget telemetry ping to
6
+ * `/x?via=eve`. No PII, no cookies — telemetry is a bare channel-attribution tag and never
7
+ * blocks or fails a tool call.
8
+ */
9
+ export const TRUST_GATE_URL = process.env.TRUST_GATE_URL ?? "https://trust-gate-mcp.onrender.com";
10
+ const VIA = "eve";
11
+ // R4 (pol.must_do.150): a stalled or malicious server can hold the connection open past
12
+ // headers and drip an unbounded body. 1MB is generous for a receipt/decision JSON payload
13
+ // and small enough to bound memory regardless of how slowly it arrives.
14
+ const MAX_RESPONSE_BYTES = 1_000_000;
15
+ export class TrustGateError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "TrustGateError";
19
+ }
20
+ }
21
+ async function withTimeout(run, timeoutMs) {
22
+ const controller = new AbortController();
23
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
24
+ try {
25
+ return await run(controller.signal);
26
+ }
27
+ finally {
28
+ clearTimeout(timer);
29
+ }
30
+ }
31
+ function abortRejection(signal) {
32
+ return new Promise((_, reject) => {
33
+ const fail = () => reject(new TrustGateError("Trust Gate response read aborted (timeout)"));
34
+ if (signal.aborted) {
35
+ fail();
36
+ return;
37
+ }
38
+ signal.addEventListener("abort", fail, { once: true });
39
+ });
40
+ }
41
+ /**
42
+ * Reads a Response body under `signal` and a byte cap. The timeout/abort deadline must
43
+ * cover this, not just the initial `fetch()` — `fetch()` commonly resolves once headers
44
+ * arrive, before the body has streamed, so a timer that stops at `fetch()` leaves the body
45
+ * read unbounded (R4: a stalled-body or unbounded-body resource-exhaustion path).
46
+ *
47
+ * Each read races against the abort signal directly (not just a pre-read check) so a
48
+ * stalled stream is bounded by the timeout even if the underlying runtime's reader
49
+ * doesn't itself reject on abort.
50
+ */
51
+ async function readJsonCapped(response, signal) {
52
+ const reader = response.body?.getReader();
53
+ if (!reader) {
54
+ // Some runtimes/mocks don't expose a streamable body; fall back to text(), still
55
+ // under the same signal via the caller's overall withTimeout scope.
56
+ const text = await Promise.race([response.text(), abortRejection(signal)]);
57
+ if (text.length > MAX_RESPONSE_BYTES) {
58
+ throw new TrustGateError(`Trust Gate response exceeded ${MAX_RESPONSE_BYTES} byte cap`);
59
+ }
60
+ return JSON.parse(text);
61
+ }
62
+ const chunks = [];
63
+ let total = 0;
64
+ try {
65
+ for (;;) {
66
+ const { done, value } = await Promise.race([reader.read(), abortRejection(signal)]);
67
+ if (done)
68
+ break;
69
+ total += value.byteLength;
70
+ if (total > MAX_RESPONSE_BYTES) {
71
+ await reader.cancel().catch(() => { });
72
+ throw new TrustGateError(`Trust Gate response exceeded ${MAX_RESPONSE_BYTES} byte cap`);
73
+ }
74
+ chunks.push(value);
75
+ }
76
+ }
77
+ catch (err) {
78
+ reader.cancel().catch(() => { });
79
+ if (err instanceof TrustGateError)
80
+ throw err;
81
+ throw new TrustGateError(`Trust Gate response read failed: ${err.message}`);
82
+ }
83
+ const combined = new Uint8Array(total);
84
+ let offset = 0;
85
+ for (const chunk of chunks) {
86
+ combined.set(chunk, offset);
87
+ offset += chunk.byteLength;
88
+ }
89
+ return JSON.parse(new TextDecoder().decode(combined));
90
+ }
91
+ /** H4 (pol.must_do.150): a Trust Gate `kid` is `sha256(pubkey_bytes)[:32]` hex chars —
92
+ * exactly 128 bits. A `kid` of any other width indicates a malformed or spoofed
93
+ * response and must not be trusted as a same-notary check. */
94
+ export function isValidKid(kid) {
95
+ return typeof kid === "string" && /^[0-9a-f]{32}$/i.test(kid);
96
+ }
97
+ /** One JSON-RPC `tools/call` against the hosted Trust Gate MCP server. */
98
+ export async function mcpCall(method, args, options = {}) {
99
+ const timeoutMs = options.timeoutMs ?? 30_000;
100
+ const payload = {
101
+ jsonrpc: "2.0",
102
+ id: 1,
103
+ method: "tools/call",
104
+ params: { name: method, arguments: args },
105
+ };
106
+ let body;
107
+ try {
108
+ body = (await withTimeout(async (signal) => {
109
+ const response = await fetch(`${TRUST_GATE_URL}/mcp`, {
110
+ method: "POST",
111
+ signal,
112
+ headers: {
113
+ "Content-Type": "application/json",
114
+ Accept: "application/json, text/event-stream",
115
+ "MCP-Protocol-Version": "2025-03-26",
116
+ },
117
+ body: JSON.stringify(payload),
118
+ });
119
+ if (!response.ok) {
120
+ const text = await readJsonCapped(response, signal).catch(() => "");
121
+ throw new TrustGateError(`Trust Gate MCP ${response.status}: ${JSON.stringify(text).slice(0, 200)}`);
122
+ }
123
+ // Body read happens INSIDE the same timed/aborted scope as the fetch itself —
124
+ // this is the fix for the R4 finding: headers-only resolution no longer leaves
125
+ // the body read unbounded.
126
+ return readJsonCapped(response, signal);
127
+ }, timeoutMs));
128
+ }
129
+ catch (err) {
130
+ if (err instanceof TrustGateError)
131
+ throw err;
132
+ throw new TrustGateError(`Cannot reach Trust Gate at ${TRUST_GATE_URL}: ${err.message}`);
133
+ }
134
+ if (body.error) {
135
+ throw new TrustGateError(`Trust Gate MCP error: ${JSON.stringify(body.error)}`);
136
+ }
137
+ const result = extractResult(body.result);
138
+ // H4: any kid present in a response must be well-formed, or the offline same-notary
139
+ // check this field exists for cannot be trusted. Fail closed rather than pass a
140
+ // malformed kid through silently.
141
+ //
142
+ // The kid can be one level deeper than `result` itself: confirmed live against the
143
+ // hosted server, mint_action_receipt's structuredContent is itself shaped as
144
+ // `{ result: { atom_id, kid, ... } }` (not flat) — the Python siblings pass this
145
+ // exact shape through unmodified too (langchain/crewai's `_mcp_call` never unwraps
146
+ // it further), so `extractResult` intentionally doesn't either, to keep this
147
+ // package's return shape identical to the established contract. The kid check
148
+ // below just needs to look in both places it can actually appear.
149
+ const kid = findKid(result);
150
+ if (kid !== undefined && !isValidKid(kid)) {
151
+ throw new TrustGateError(`Trust Gate response carried a malformed kid (expected 32 hex chars): ${JSON.stringify(kid)}`);
152
+ }
153
+ return result;
154
+ }
155
+ function findKid(result) {
156
+ if ("kid" in result)
157
+ return result.kid;
158
+ if (result.result && typeof result.result === "object") {
159
+ const nested = result.result;
160
+ if ("kid" in nested)
161
+ return nested.kid;
162
+ }
163
+ return undefined;
164
+ }
165
+ function extractResult(result) {
166
+ if (result && typeof result === "object") {
167
+ const r = result;
168
+ if ("structuredContent" in r) {
169
+ return r.structuredContent;
170
+ }
171
+ if ("content" in r && Array.isArray(r.content) && r.content.length > 0) {
172
+ const first = r.content[0];
173
+ if (typeof first.text === "string") {
174
+ try {
175
+ return JSON.parse(first.text);
176
+ }
177
+ catch {
178
+ return { raw: r.content };
179
+ }
180
+ }
181
+ }
182
+ return r;
183
+ }
184
+ return { raw: result };
185
+ }
186
+ /** Fire-and-forget channel-attribution ping. Any failure is swallowed — telemetry
187
+ * must never block or fail a tool call. */
188
+ export function pingTelemetry(kind = "api") {
189
+ withTimeout((signal) => fetch(`${TRUST_GATE_URL}/x?via=${VIA}&kind=${encodeURIComponent(kind)}`, {
190
+ signal,
191
+ }), 2_000).catch(() => {
192
+ /* best-effort only */
193
+ });
194
+ }
@@ -0,0 +1,37 @@
1
+ export { TRUST_GATE_URL, TrustGateError, mcpCall, pingTelemetry, isValidKid } from "./client.js";
2
+ export { mintActionReceipt } from "./tools/mint-action-receipt.js";
3
+ export { verifyReceipt } from "./tools/verify-receipt.js";
4
+ export { gateDecision } from "./tools/gate-decision.js";
5
+ export { checkEgress } from "./tools/check-egress.js";
6
+ export { runExitDrill } from "./tools/run-exit-drill.js";
7
+ /**
8
+ * All 5 Trust Gate tools, ready to drop into an eve `agent/tools/` directory
9
+ * or spread into a `defineExtension` bundle.
10
+ */
11
+ export declare function createTrustGateTools(): {
12
+ mintActionReceipt: import("eve/tools").ToolDefinition<{
13
+ agentId: string;
14
+ operation: string;
15
+ target: string;
16
+ policy?: string | undefined;
17
+ inputs?: string | undefined;
18
+ decision?: string | undefined;
19
+ }, Record<string, unknown>>;
20
+ verifyReceipt: import("eve/tools").ToolDefinition<{
21
+ receipt: Record<string, unknown>;
22
+ requirePq?: boolean | undefined;
23
+ }, Record<string, unknown>>;
24
+ gateDecision: import("eve/tools").ToolDefinition<{
25
+ action: string;
26
+ resource: string;
27
+ context: Record<string, unknown>;
28
+ phase: "PREVIEW" | "COMMIT";
29
+ previewId?: string | undefined;
30
+ }, Record<string, unknown>>;
31
+ checkEgress: import("eve/tools").ToolDefinition<{
32
+ destination: string;
33
+ dataSample: string;
34
+ provider: string;
35
+ }, Record<string, unknown>>;
36
+ runExitDrill: import("eve/tools").ToolDefinition<Record<string, never>, Record<string, unknown>>;
37
+ };
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ export { TRUST_GATE_URL, TrustGateError, mcpCall, pingTelemetry, isValidKid } from "./client.js";
2
+ export { mintActionReceipt } from "./tools/mint-action-receipt.js";
3
+ export { verifyReceipt } from "./tools/verify-receipt.js";
4
+ export { gateDecision } from "./tools/gate-decision.js";
5
+ export { checkEgress } from "./tools/check-egress.js";
6
+ export { runExitDrill } from "./tools/run-exit-drill.js";
7
+ import { mintActionReceipt } from "./tools/mint-action-receipt.js";
8
+ import { verifyReceipt } from "./tools/verify-receipt.js";
9
+ import { gateDecision } from "./tools/gate-decision.js";
10
+ import { checkEgress } from "./tools/check-egress.js";
11
+ import { runExitDrill } from "./tools/run-exit-drill.js";
12
+ /**
13
+ * All 5 Trust Gate tools, ready to drop into an eve `agent/tools/` directory
14
+ * or spread into a `defineExtension` bundle.
15
+ */
16
+ export function createTrustGateTools() {
17
+ return {
18
+ mintActionReceipt,
19
+ verifyReceipt,
20
+ gateDecision,
21
+ checkEgress,
22
+ runExitDrill,
23
+ };
24
+ }
@@ -0,0 +1,5 @@
1
+ export declare const checkEgress: import("eve/tools").ToolDefinition<{
2
+ destination: string;
3
+ dataSample: string;
4
+ provider: string;
5
+ }, Record<string, unknown>>;
@@ -0,0 +1,23 @@
1
+ import { defineTool } from "eve/tools";
2
+ import { z } from "zod";
3
+ import { mcpCall, pingTelemetry } from "../client.js";
4
+ export const checkEgress = defineTool({
5
+ description: "Egress classification check. The hosted server scans data for sensitivity markers " +
6
+ "and classifies it as PUBLIC / INTERNAL / CONFIDENTIAL / RESTRICTED, returning that " +
7
+ "classification plus a tamper-evident receipt. This tool only reports the " +
8
+ "classification -- it does not itself intercept or block network calls; the caller " +
9
+ "is responsible for acting on a RESTRICTED result.",
10
+ inputSchema: z.object({
11
+ destination: z.string().min(1).describe("Where data is being sent (e.g., 'openai.com')."),
12
+ dataSample: z.string().min(1).describe("Sample of the data being sent (scanned for sensitivity)."),
13
+ provider: z.string().min(1).describe("The provider/service receiving the data."),
14
+ }),
15
+ async execute({ destination, dataSample, provider }) {
16
+ pingTelemetry();
17
+ return mcpCall("check_egress", {
18
+ destination,
19
+ data_sample: dataSample,
20
+ provider,
21
+ });
22
+ },
23
+ });
@@ -0,0 +1,7 @@
1
+ export declare const gateDecision: import("eve/tools").ToolDefinition<{
2
+ action: string;
3
+ resource: string;
4
+ context: Record<string, unknown>;
5
+ phase: "PREVIEW" | "COMMIT";
6
+ previewId?: string | undefined;
7
+ }, Record<string, unknown>>;
@@ -0,0 +1,25 @@
1
+ import { defineTool } from "eve/tools";
2
+ import { z } from "zod";
3
+ import { mcpCall, pingTelemetry } from "../client.js";
4
+ export const gateDecision = defineTool({
5
+ description: "Two-phase decision gate. PREVIEW returns a risk assessment and preview_id " +
6
+ "without acting. COMMIT requires the preview_id, verifies inputs match, " +
7
+ "and mints a tamper-evident receipt. Stateless.",
8
+ inputSchema: z.object({
9
+ action: z.string().min(1).describe("The action to evaluate (e.g., 'deploy', 'send_email')."),
10
+ resource: z.string().min(1).describe("Target resource (e.g., 'prod/api', 'user-db')."),
11
+ context: z.record(z.string(), z.unknown()).describe("Context object for the decision (hashed in the receipt)."),
12
+ phase: z
13
+ .enum(["PREVIEW", "COMMIT"])
14
+ .default("PREVIEW")
15
+ .describe("'PREVIEW' for risk assessment, 'COMMIT' to proceed with receipt."),
16
+ previewId: z.string().optional().describe("Required for COMMIT. Returned by the PREVIEW phase."),
17
+ }),
18
+ async execute({ action, resource, context, phase, previewId }) {
19
+ pingTelemetry();
20
+ const args = { action, resource, context, phase };
21
+ if (previewId !== undefined)
22
+ args.preview_id = previewId;
23
+ return mcpCall("gate_decision", args);
24
+ },
25
+ });
@@ -0,0 +1,8 @@
1
+ export declare const mintActionReceipt: import("eve/tools").ToolDefinition<{
2
+ agentId: string;
3
+ operation: string;
4
+ target: string;
5
+ policy?: string | undefined;
6
+ inputs?: string | undefined;
7
+ decision?: string | undefined;
8
+ }, Record<string, unknown>>;
@@ -0,0 +1,31 @@
1
+ import { defineTool } from "eve/tools";
2
+ import { z } from "zod";
3
+ import { mcpCall, pingTelemetry } from "../client.js";
4
+ export const mintActionReceipt = defineTool({
5
+ description: "Mint a post-quantum, tamper-evident receipt for a consequential agent action. " +
6
+ "Returns a receipt that's verifiable offline from the certificate alone. " +
7
+ "Receipt is signed Ed25519 + ML-DSA-65; carries a 128-bit kid for offline " +
8
+ "same-notary check.",
9
+ inputSchema: z.object({
10
+ agentId: z.string().min(1).describe("Identifier of the agent performing the action."),
11
+ operation: z.string().min(1).describe("Operation name (e.g., 'deploy', 'send_email')."),
12
+ target: z.string().min(1).describe("Target of the action."),
13
+ policy: z.string().optional().describe("Policy label for the action (default: 'agent action evidence')."),
14
+ inputs: z.string().optional().describe("Optional serialized inputs to bind into the receipt."),
15
+ decision: z.string().optional().describe("Decision label (default: 'ACTION_GOVERNED')."),
16
+ }),
17
+ async execute({ agentId, operation, target, policy, inputs, decision }) {
18
+ pingTelemetry();
19
+ const args = {
20
+ agent_id: agentId,
21
+ operation,
22
+ target,
23
+ policy: policy ?? "agent action evidence",
24
+ };
25
+ if (inputs !== undefined)
26
+ args.inputs = inputs;
27
+ if (decision !== undefined)
28
+ args.decision = decision;
29
+ return mcpCall("mint_action_receipt", args);
30
+ },
31
+ });
@@ -0,0 +1 @@
1
+ export declare const runExitDrill: import("eve/tools").ToolDefinition<Record<string, never>, Record<string, unknown>>;
@@ -0,0 +1,13 @@
1
+ import { defineTool } from "eve/tools";
2
+ import { z } from "zod";
3
+ import { mcpCall, pingTelemetry } from "../client.js";
4
+ export const runExitDrill = defineTool({
5
+ description: "Vendor exit readiness drill. Runs on the hosted Trust Gate server (not the caller's " +
6
+ "own machine) -- checks the server's own signing key, model access, and data export " +
7
+ "paths. Returns results + tamper-evident receipt.",
8
+ inputSchema: z.object({}),
9
+ async execute() {
10
+ pingTelemetry();
11
+ return mcpCall("run_exit_drill", {});
12
+ },
13
+ });
@@ -0,0 +1,4 @@
1
+ export declare const verifyReceipt: import("eve/tools").ToolDefinition<{
2
+ receipt: Record<string, unknown>;
3
+ requirePq?: boolean | undefined;
4
+ }, Record<string, unknown>>;
@@ -0,0 +1,30 @@
1
+ import { defineTool } from "eve/tools";
2
+ import { z } from "zod";
3
+ import { mcpCall, pingTelemetry } from "../client.js";
4
+ export const verifyReceipt = defineTool({
5
+ description: "Calls the hosted Trust Gate server to verify a receipt's signature and evidence " +
6
+ "hash (a networked call, not a local/offline check -- the receipt format itself is " +
7
+ "designed to be independently verifiable offline from the certificate alone, but " +
8
+ "this tool takes the convenient hosted-verification path rather than performing " +
9
+ "that check locally). Returns {ok, hash_ok, sig_ok, signed, legs, signature_alg, reason}. " +
10
+ "Defaults to PQ-required mode on the server -- defends against Ed25519-only downgrade " +
11
+ "by requiring at least one verified PQ leg.",
12
+ inputSchema: z.object({
13
+ receipt: z.record(z.string(), z.unknown()).describe("The Trust Gate receipt to verify."),
14
+ // H3 (pol.must_do.150): left undefined by default so the hosted server obeys its own
15
+ // TRUST_GATE_REQUIRE_PQ env (default true). This tool never silently forces it false —
16
+ // only an explicit caller-supplied `false` opts out of PQ-required verification.
17
+ requirePq: z
18
+ .boolean()
19
+ .optional()
20
+ .describe("Undefined = obey the server's TRUST_GATE_REQUIRE_PQ (default true). " +
21
+ "false = allow Ed25519-only receipts (downgrade — opt in explicitly)."),
22
+ }),
23
+ async execute({ receipt, requirePq }) {
24
+ pingTelemetry();
25
+ const args = { receipt };
26
+ if (requirePq !== undefined)
27
+ args.require_pq = requirePq;
28
+ return mcpCall("verify_receipt", args);
29
+ },
30
+ });
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "cwn-eve-trust-gate",
3
+ "version": "0.1.0",
4
+ "description": "eve (Vercel agent framework) tools for Trust Gate post-quantum, tamper-evident receipts.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json",
21
+ "test": "node --import tsx --test tests/adversarial.test.ts",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "eve",
26
+ "vercel",
27
+ "mcp",
28
+ "trust-gate",
29
+ "post-quantum",
30
+ "agent",
31
+ "receipt"
32
+ ],
33
+ "author": "Cyber Warrior Network <apps@cyberwarriornetwork.com>",
34
+ "license": "Apache-2.0",
35
+ "homepage": "https://github.com/CWNApps/eve-trust-gate",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/CWNApps/eve-trust-gate.git"
39
+ },
40
+ "bugs": "https://github.com/CWNApps/eve-trust-gate/issues",
41
+ "engines": {
42
+ "node": ">=20.0.0"
43
+ },
44
+ "peerDependencies": {
45
+ "eve": "^0.24.0"
46
+ },
47
+ "dependencies": {
48
+ "zod": "^4.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "eve": "^0.24.4",
52
+ "tsx": "^4.19.0",
53
+ "typescript": "^5.6.0",
54
+ "@types/node": "^22.10.0"
55
+ }
56
+ }