@xrpl-utilities/mcp 0.2.194 → 0.2.201
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 +30 -2
- package/dist/dispatch.d.ts +16 -0
- package/dist/dispatch.js +108 -5
- package/dist/dispatch.js.map +1 -1
- package/dist/hSealReceipt.d.ts +35 -6
- package/dist/hSealReceipt.js +184 -17
- package/dist/hSealReceipt.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/server.js +25 -1
- package/dist/server.js.map +1 -1
- package/dist/services/flows.js +1 -1
- package/dist/services/flows.js.map +1 -1
- package/dist/services/pulse.js +27 -1
- package/dist/services/pulse.js.map +1 -1
- package/dist/services/sentinel.js +35 -1
- package/dist/services/sentinel.js.map +1 -1
- package/dist/services/telemetry.js +1 -1
- package/dist/services/telemetry.js.map +1 -1
- package/dist/services/trust.js +33 -1
- package/dist/services/trust.js.map +1 -1
- package/dist/services/vault.js +1 -1
- package/dist/services/vault.js.map +1 -1
- package/dist/transport/http.d.ts +63 -2
- package/dist/transport/http.js +253 -13
- package/dist/transport/http.js.map +1 -1
- package/dist/validate.d.ts +12 -3
- package/dist/validate.js +75 -11
- package/dist/validate.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -40,7 +40,18 @@ sign and retry against whichever rail its wallet supports.
|
|
|
40
40
|
|
|
41
41
|
Operators can set `MCP_BYPASS_KEY` on the server to enable an opt-in
|
|
42
42
|
bypass for friendlies / demos. The caller passes the matching key as
|
|
43
|
-
`_bypass_key` in the tool args.
|
|
43
|
+
`_bypass_key` in the tool args.
|
|
44
|
+
|
|
45
|
+
The hosted endpoint budgets failed `_bypass_key` attempts per caller
|
|
46
|
+
separately from, and far more tightly than, ordinary requests: a 60
|
|
47
|
+
requests-per-minute cap is a fair-use limit, not an access control on a
|
|
48
|
+
secret. The budget is spent per guess, not per HTTP request, so a batched
|
|
49
|
+
JSON-RPC body cannot outrun it, and only requests that actually carry a
|
|
50
|
+
`_bypass_key` are held off once it is exhausted - `tools/list`, free tools
|
|
51
|
+
and paid x402 calls keep working. Batches are capped at 20 messages. Use a key of at least 32 random bytes, and scope or rotate it
|
|
52
|
+
per service rather than sharing one portfolio-wide key. Bucket keying
|
|
53
|
+
depends on `TRUST_PROXY_HOPS` matching the real number of proxies in
|
|
54
|
+
front of the container - see `.env.example`.
|
|
44
55
|
|
|
45
56
|
### H-Seal receipt co-signing (optional)
|
|
46
57
|
|
|
@@ -51,7 +62,16 @@ provider attestation. The attestation rides on the tool result's
|
|
|
51
62
|
`_meta.hSeal`, so a caller can anchor a tamper-evident, independently
|
|
52
63
|
verifiable on-chain receipt of the interaction. When either var is unset the
|
|
53
64
|
feature is inert and responses are unchanged. Never hardcode the key — env
|
|
54
|
-
only.
|
|
65
|
+
only.
|
|
66
|
+
|
|
67
|
+
When a backend co-signs its own output, the MCP also builds a 2-party
|
|
68
|
+
receipt (`_meta.hSealReceipt`). That signs with the operator key, so set
|
|
69
|
+
`HSEAL_ALLOWED_PROVIDERS` to the CAIP-10 identities of the XR-* backends
|
|
70
|
+
you are willing to vouch for. Without it any provider that proves it holds
|
|
71
|
+
the key for the identity it names is accepted. The `responseHash` is always
|
|
72
|
+
recomputed from the delivered body; `requestHash` stays provider-asserted
|
|
73
|
+
(the backends hash a synthetic request object the MCP cannot reproduce) and
|
|
74
|
+
the result says so via `requestHashBasis`. See `src/hSeal.ts` and the ops runbook
|
|
55
75
|
[`docs/hseal-provider.md`](docs/hseal-provider.md) (current identity, the
|
|
56
76
|
ed25519 curve gotcha, and how to rotate/recover the key).
|
|
57
77
|
|
|
@@ -106,11 +126,19 @@ useful as templates in any language.
|
|
|
106
126
|
npm install
|
|
107
127
|
npm run build
|
|
108
128
|
node dist/index.js --transport http --port 8080
|
|
129
|
+
npm test # builds, then runs the node:test suite in tests/
|
|
109
130
|
```
|
|
110
131
|
|
|
111
132
|
Point MCP Inspector at `http://localhost:8080/mcp` to walk through
|
|
112
133
|
tool definitions interactively.
|
|
113
134
|
|
|
135
|
+
Set `STRICT_VALIDATE=1` to treat a manifest that could not be read as
|
|
136
|
+
drift instead of a warning. It is off by default because Railway restarts
|
|
137
|
+
`ON_FAILURE`: failing closed on boot would crash-loop the endpoint, and
|
|
138
|
+
take the other five healthy services' tools down with it, whenever one
|
|
139
|
+
backend is cold. Either way, a service whose manifest was not read logs
|
|
140
|
+
`NOT CHECKED` - "could not check" is never reported as "all clear".
|
|
141
|
+
|
|
114
142
|
## Releases
|
|
115
143
|
|
|
116
144
|
Releases are cut by tag push. The `Release` workflow builds, validates
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -44,6 +44,22 @@ export interface DispatchOptions {
|
|
|
44
44
|
* can distinguish MCP traffic from direct API users.
|
|
45
45
|
*/
|
|
46
46
|
userAgent?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Called when a caller presented a `_bypass_key` that did not match.
|
|
49
|
+
* The transport layer counts these per-IP: a 60/min request limit still
|
|
50
|
+
* permits thousands of guesses a day against the shared bypass key, and
|
|
51
|
+
* the 402-vs-200 divergence is a clean oracle, so failed attempts need
|
|
52
|
+
* their own much tighter budget.
|
|
53
|
+
*/
|
|
54
|
+
onBypassFailure?: () => void;
|
|
55
|
+
/**
|
|
56
|
+
* True while this caller has already burned through the failed-`_bypass_key`
|
|
57
|
+
* budget. Checked here, at the point of the guess, not only at the start of
|
|
58
|
+
* the HTTP request: one batched JSON-RPC POST carries thousands of messages,
|
|
59
|
+
* so a per-request check let a single body spend thousands of guesses before
|
|
60
|
+
* the lockout could apply.
|
|
61
|
+
*/
|
|
62
|
+
isBypassBlocked?: () => boolean;
|
|
47
63
|
}
|
|
48
64
|
/**
|
|
49
65
|
* Run a single tool call. Throws on missing tool / network error /
|
package/dist/dispatch.js
CHANGED
|
@@ -55,6 +55,10 @@ export async function dispatchTool(toolName, args, opts = {}) {
|
|
|
55
55
|
}
|
|
56
56
|
const { service, tool } = owner;
|
|
57
57
|
const baseUrl = opts.baseUrlOverride?.(service.id) ?? service.baseUrl;
|
|
58
|
+
// The low-level MCP Server does NOT check arguments against the advertised
|
|
59
|
+
// inputSchema - only McpServer.registerTool does - so every declared
|
|
60
|
+
// pattern/enum/limit is advisory until we enforce it here.
|
|
61
|
+
args = validateArgs(toolName, tool.inputSchema, args);
|
|
58
62
|
// Substitute path params (e.g. /domain/{domain_id}) from args.
|
|
59
63
|
let path = tool.path;
|
|
60
64
|
const consumedPathArgs = new Set();
|
|
@@ -62,8 +66,17 @@ export async function dispatchTool(toolName, args, opts = {}) {
|
|
|
62
66
|
if (!(key in args)) {
|
|
63
67
|
throw new Error(`tool ${toolName} requires path parameter ${key}, missing from args`);
|
|
64
68
|
}
|
|
69
|
+
// Unconditional, schema-independent. "." / ".." / a slash survive
|
|
70
|
+
// encodeURIComponent's intent because the WHATWG URL parser normalizes the
|
|
71
|
+
// built URL afterwards: /domain/.. collapses to the service root, and the
|
|
72
|
+
// paid drill-down tool then returns the root banner as a success - which,
|
|
73
|
+
// with H-Seal on, gets co-signed as an answer to the tool that was called.
|
|
74
|
+
const raw = String(args[key]);
|
|
75
|
+
if (raw === "" || raw === "." || raw === ".." || /[/\\]/.test(raw)) {
|
|
76
|
+
throw new Error(`tool ${toolName}: invalid value for path parameter ${key}`);
|
|
77
|
+
}
|
|
65
78
|
consumedPathArgs.add(key);
|
|
66
|
-
return encodeURIComponent(
|
|
79
|
+
return encodeURIComponent(raw);
|
|
67
80
|
});
|
|
68
81
|
// Strip args reserved for transport-level concerns (payment_signature,
|
|
69
82
|
// _bypass_key) AND args already consumed by path params. What remains
|
|
@@ -95,10 +108,26 @@ export async function dispatchTool(toolName, args, opts = {}) {
|
|
|
95
108
|
if (callerPaymentSig) {
|
|
96
109
|
headers["PAYMENT-SIGNATURE"] = callerPaymentSig;
|
|
97
110
|
}
|
|
98
|
-
else if (callerBypassKey
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
111
|
+
else if (callerBypassKey) {
|
|
112
|
+
// Check BEFORE comparing. Gating only the failure branch would leave the
|
|
113
|
+
// compare reachable, so a blocked caller who finally guesses right still
|
|
114
|
+
// gets granted - the budget would cap the noise, not the attack.
|
|
115
|
+
// Throwing rather than falling through to the 402 path also stops the
|
|
116
|
+
// upstream fan-out a batched body would otherwise amplify, and it reads
|
|
117
|
+
// the same for every key: the block is per-IP, so it leaks nothing.
|
|
118
|
+
if (opts.isBypassBlocked?.()) {
|
|
119
|
+
throw new Error("bypass key attempts exhausted; retry later");
|
|
120
|
+
}
|
|
121
|
+
if (opts.bypassKey && timingSafeStringEqual(callerBypassKey, opts.bypassKey)) {
|
|
122
|
+
// Operator-issued bypass. Forward as the dev-bypass header that
|
|
123
|
+
// the underlying services accept.
|
|
124
|
+
headers["PAYMENT-SIGNATURE"] = callerBypassKey;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// A wrong key is a guess at the operator secret, not ordinary traffic.
|
|
128
|
+
// Report it so the transport can budget guesses separately from requests.
|
|
129
|
+
opts.onBypassFailure?.();
|
|
130
|
+
}
|
|
102
131
|
}
|
|
103
132
|
else {
|
|
104
133
|
// No auth supplied. Fire the request anyway so the caller gets
|
|
@@ -168,4 +197,78 @@ export async function dispatchTool(toolName, args, opts = {}) {
|
|
|
168
197
|
function stringArg(v) {
|
|
169
198
|
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
170
199
|
}
|
|
200
|
+
// Args the dispatcher consumes itself. They are never part of the API payload
|
|
201
|
+
// and are not declared on every schema, so the unknown-key drop must not eat
|
|
202
|
+
// them or the operator bypass stops working.
|
|
203
|
+
const RESERVED_ARGS = new Set(["payment_signature", "_bypass_key"]);
|
|
204
|
+
/**
|
|
205
|
+
* Enforce the tool's advertised inputSchema. Throws on a violation; returns the
|
|
206
|
+
* args to actually send, with unknown keys dropped when the schema is closed
|
|
207
|
+
* (today every tool sets additionalProperties: false, so an invented key would
|
|
208
|
+
* otherwise be forwarded verbatim into the upstream query string or body).
|
|
209
|
+
*
|
|
210
|
+
* Deliberately tolerant about numbers-as-strings: LLM clients routinely send
|
|
211
|
+
* "10" for an integer arg and the backends coerce it, so rejecting that would
|
|
212
|
+
* break working callers without closing anything.
|
|
213
|
+
*/
|
|
214
|
+
function validateArgs(toolName, schema, args) {
|
|
215
|
+
const props = schema.properties ?? {};
|
|
216
|
+
const closed = schema.additionalProperties === false;
|
|
217
|
+
const out = {};
|
|
218
|
+
// `required` is deliberately NOT enforced here. A caller probing a paid tool
|
|
219
|
+
// with no args to get the real 402 challenge back is a supported discovery
|
|
220
|
+
// move (see the auth block below); turning that into a local error would
|
|
221
|
+
// break it.
|
|
222
|
+
for (const [key, value] of Object.entries(args)) {
|
|
223
|
+
const spec = props[key];
|
|
224
|
+
if (!spec) {
|
|
225
|
+
if (RESERVED_ARGS.has(key))
|
|
226
|
+
out[key] = value;
|
|
227
|
+
else if (!closed)
|
|
228
|
+
out[key] = value;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (value === undefined || value === null) {
|
|
232
|
+
out[key] = value;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const problem = valueProblem(spec, value);
|
|
236
|
+
if (problem) {
|
|
237
|
+
throw new Error(`tool ${toolName}: invalid value for ${key}: ${problem}`);
|
|
238
|
+
}
|
|
239
|
+
out[key] = value;
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
function valueProblem(spec, value) {
|
|
244
|
+
if (spec.enum && !spec.enum.some((e) => e === value || String(e) === String(value))) {
|
|
245
|
+
return `must be one of [${spec.enum.map(String).join(", ")}]`;
|
|
246
|
+
}
|
|
247
|
+
if (spec.type === "number" || spec.type === "integer") {
|
|
248
|
+
const n = typeof value === "number" ? value : Number(String(value));
|
|
249
|
+
if (!Number.isFinite(n))
|
|
250
|
+
return `must be a ${spec.type}`;
|
|
251
|
+
if (spec.type === "integer" && !Number.isInteger(n))
|
|
252
|
+
return "must be an integer";
|
|
253
|
+
if (typeof spec.minimum === "number" && n < spec.minimum)
|
|
254
|
+
return `must be >= ${spec.minimum}`;
|
|
255
|
+
if (typeof spec.maximum === "number" && n > spec.maximum)
|
|
256
|
+
return `must be <= ${spec.maximum}`;
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
if (spec.type === "boolean") {
|
|
260
|
+
if (typeof value === "boolean")
|
|
261
|
+
return null;
|
|
262
|
+
return value === "true" || value === "false" ? null : "must be a boolean";
|
|
263
|
+
}
|
|
264
|
+
if (spec.type === "string") {
|
|
265
|
+
if (typeof value !== "string")
|
|
266
|
+
return "must be a string";
|
|
267
|
+
if (spec.pattern && !new RegExp(spec.pattern).test(value)) {
|
|
268
|
+
return `must match ${spec.pattern}`;
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
171
274
|
//# sourceMappingURL=dispatch.js.map
|
package/dist/dispatch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"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"}
|
package/dist/hSealReceipt.d.ts
CHANGED
|
@@ -17,23 +17,52 @@
|
|
|
17
17
|
* no new secret is required. Inert when unset. NB: the H-Seal API host is
|
|
18
18
|
* h-seal.xr-utilities.ai (the .com host is a browser-only, Cloudflare-challenged
|
|
19
19
|
* surface and returns 403 to server clients).
|
|
20
|
+
*
|
|
21
|
+
* NOTHING in the attestation may be taken on trust. The receipt carries a real
|
|
22
|
+
* signature from the operator's key, so a backend (or anything that can shape a
|
|
23
|
+
* proxied response) that hands us an invented (requestHash, responseHash,
|
|
24
|
+
* providerIdentity) triple would otherwise get the operator to vouch for
|
|
25
|
+
* arbitrary content on behalf of an arbitrary third party. Before signing:
|
|
26
|
+
*
|
|
27
|
+
* 1. responseHash is recomputed from the body we actually delivered. It is
|
|
28
|
+
* the one hash the MCP can corroborate on its own.
|
|
29
|
+
* 2. The provider's own ed25519 signature is verified, AND the public key on
|
|
30
|
+
* the wire must derive the r-address in providerIdentity - otherwise any
|
|
31
|
+
* self-signed blob naming a real service would pass.
|
|
32
|
+
* 3. providerIdentity is pinned to HSEAL_ALLOWED_PROVIDERS when that env var
|
|
33
|
+
* is set (comma-separated CAIP-10 identities).
|
|
34
|
+
*
|
|
35
|
+
* requestHash stays PROVIDER-ASSERTED: backends hash a synthetic
|
|
36
|
+
* {"tool": ...} object the MCP cannot reproduce, so a strict comparison would
|
|
37
|
+
* reject every live receipt. The result says so via `requestHashBasis` rather
|
|
38
|
+
* than implying the caller corroborated it.
|
|
20
39
|
*/
|
|
21
40
|
export declare const hSealReceiptEnabled: boolean;
|
|
22
41
|
export interface HSealReceiptResult {
|
|
23
|
-
|
|
42
|
+
/** The signed receipt. Absent whenever the MCP refused to sign, or H-Seal explicitly rejected. */
|
|
43
|
+
body?: unknown;
|
|
24
44
|
verdict?: unknown;
|
|
25
45
|
verifyError?: string;
|
|
46
|
+
/** Always "provider_asserted" — the MCP cannot reproduce the backend's requestHash preimage. */
|
|
47
|
+
requestHashBasis?: string;
|
|
26
48
|
}
|
|
27
49
|
/**
|
|
28
|
-
* Build + verify a 2-party receipt from a backend provider attestation.
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
50
|
+
* Build + verify a 2-party receipt from a backend provider attestation. Returns
|
|
51
|
+
* the signed `body` (valid + independently verifiable) when signing succeeds;
|
|
52
|
+
* `verdict` is best-effort — if the H-Seal service is unreachable the body
|
|
53
|
+
* still stands and `verifyError` records why, but an explicit H-Seal reject
|
|
54
|
+
* drops the body. A responseHash that does not match what we delivered also
|
|
55
|
+
* drops the body, and says so in `verifyError` rather than vanishing, as does
|
|
56
|
+
* every other refusal: a malformed attestation, a provider signature that does
|
|
57
|
+
* not authenticate, and an identity outside HSEAL_ALLOWED_PROVIDERS all return
|
|
58
|
+
* a body-less result with a reason. Only two cases return undefined - H-Seal
|
|
59
|
+
* not being configured at all, and our own signing throwing. Never throws.
|
|
33
60
|
*/
|
|
34
61
|
export declare function buildReceipt(opts: {
|
|
35
62
|
serviceEndpoint: string;
|
|
36
63
|
attestation: unknown;
|
|
64
|
+
/** The body actually delivered to the caller (result minus the attestation envelope). */
|
|
65
|
+
responseBody: unknown;
|
|
37
66
|
startedAt: number;
|
|
38
67
|
completedAt: number;
|
|
39
68
|
latencyMs: number;
|
package/dist/hSealReceipt.js
CHANGED
|
@@ -17,40 +17,198 @@
|
|
|
17
17
|
* no new secret is required. Inert when unset. NB: the H-Seal API host is
|
|
18
18
|
* h-seal.xr-utilities.ai (the .com host is a browser-only, Cloudflare-challenged
|
|
19
19
|
* surface and returns 403 to server clients).
|
|
20
|
+
*
|
|
21
|
+
* NOTHING in the attestation may be taken on trust. The receipt carries a real
|
|
22
|
+
* signature from the operator's key, so a backend (or anything that can shape a
|
|
23
|
+
* proxied response) that hands us an invented (requestHash, responseHash,
|
|
24
|
+
* providerIdentity) triple would otherwise get the operator to vouch for
|
|
25
|
+
* arbitrary content on behalf of an arbitrary third party. Before signing:
|
|
26
|
+
*
|
|
27
|
+
* 1. responseHash is recomputed from the body we actually delivered. It is
|
|
28
|
+
* the one hash the MCP can corroborate on its own.
|
|
29
|
+
* 2. The provider's own ed25519 signature is verified, AND the public key on
|
|
30
|
+
* the wire must derive the r-address in providerIdentity - otherwise any
|
|
31
|
+
* self-signed blob naming a real service would pass.
|
|
32
|
+
* 3. providerIdentity is pinned to HSEAL_ALLOWED_PROVIDERS when that env var
|
|
33
|
+
* is set (comma-separated CAIP-10 identities).
|
|
34
|
+
*
|
|
35
|
+
* requestHash stays PROVIDER-ASSERTED: backends hash a synthetic
|
|
36
|
+
* {"tool": ...} object the MCP cannot reproduce, so a strict comparison would
|
|
37
|
+
* reject every live receipt. The result says so via `requestHashBasis` rather
|
|
38
|
+
* than implying the caller corroborated it.
|
|
20
39
|
*/
|
|
21
|
-
import { randomUUID } from "node:crypto";
|
|
22
|
-
import { signReceipt, attachAttestation, HSealClient, ed25519Signer, } from "@xr-utilities/h-seal-provider";
|
|
40
|
+
import { createHash, createPublicKey, randomUUID, verify as verifySignature } from "node:crypto";
|
|
41
|
+
import { signReceipt, attachAttestation, hashCanonicalJson, sha256Hex, HSealClient, ed25519Signer, } from "@xr-utilities/h-seal-provider";
|
|
23
42
|
const HSEAL_API = process.env["HSEAL_ENDPOINT"] ?? "https://h-seal.xr-utilities.ai";
|
|
24
43
|
const RECEIPT_TOPIC = process.env["HSEAL_RECEIPT_TOPIC"] ?? "0.0.10500472";
|
|
25
44
|
const callerIdentity = process.env["MCP_IDENTITY"] ?? process.env["PROVIDER_IDENTITY"];
|
|
26
45
|
const callerKeyRaw = process.env["MCP_KEY_RAW"] ?? process.env["PROVIDER_KEY_RAW"];
|
|
46
|
+
const allowedProviders = (process.env["HSEAL_ALLOWED_PROVIDERS"] ?? "")
|
|
47
|
+
.split(",")
|
|
48
|
+
.map((s) => s.trim())
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
/** How the receipt's requestHash was established. See the module header. */
|
|
51
|
+
const REQUEST_HASH_BASIS = "provider_asserted";
|
|
27
52
|
export const hSealReceiptEnabled = Boolean(callerIdentity && callerKeyRaw);
|
|
53
|
+
/**
|
|
54
|
+
* A well-formed attestation carries the provider's own signature. Accepting a
|
|
55
|
+
* three-string blob would let a "2-party" receipt be emitted with no second
|
|
56
|
+
* party at all, so providerSignature / scheme / issuedAt are required here.
|
|
57
|
+
*/
|
|
28
58
|
function looksLikeAttestation(a) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
if (typeof a !== "object" || a === null)
|
|
60
|
+
return false;
|
|
61
|
+
const r = a;
|
|
62
|
+
return (typeof r["requestHash"] === "string" &&
|
|
63
|
+
typeof r["responseHash"] === "string" &&
|
|
64
|
+
typeof r["providerIdentity"] === "string" &&
|
|
65
|
+
typeof r["providerSignature"] === "string" &&
|
|
66
|
+
typeof r["providerSignatureScheme"] === "string" &&
|
|
67
|
+
typeof r["providerIssuedAt"] === "number");
|
|
68
|
+
}
|
|
69
|
+
const XRPL_BASE58 = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
|
|
70
|
+
// SPKI DER header for a raw ed25519 public key: 12 fixed bytes + the 32-byte key.
|
|
71
|
+
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
72
|
+
function base58Xrpl(buf) {
|
|
73
|
+
let n = BigInt("0x" + (buf.toString("hex") || "0"));
|
|
74
|
+
let out = "";
|
|
75
|
+
while (n > 0n) {
|
|
76
|
+
out = XRPL_BASE58[Number(n % 58n)] + out;
|
|
77
|
+
n /= 58n;
|
|
78
|
+
}
|
|
79
|
+
for (const b of buf) {
|
|
80
|
+
if (b !== 0)
|
|
81
|
+
break;
|
|
82
|
+
out = XRPL_BASE58[0] + out;
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
/** Classic r-address for a 33-byte XRPL public key (0xED prefix + raw ed25519). */
|
|
87
|
+
function xrplAddressFromPubKey(pubKey) {
|
|
88
|
+
const accountId = createHash("ripemd160")
|
|
89
|
+
.update(createHash("sha256").update(pubKey).digest())
|
|
90
|
+
.digest();
|
|
91
|
+
const payload = Buffer.concat([Buffer.from([0x00]), accountId]);
|
|
92
|
+
const checksum = createHash("sha256")
|
|
93
|
+
.update(createHash("sha256").update(payload).digest())
|
|
94
|
+
.digest()
|
|
95
|
+
.subarray(0, 4);
|
|
96
|
+
return base58Xrpl(Buffer.concat([payload, checksum]));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Authenticate the provider. Returns null when the attestation really was
|
|
100
|
+
* signed by the account it names, otherwise the reason it wasn't. The preimage
|
|
101
|
+
* mirrors the SDK's signProviderAttestation exactly: the sha256 of the
|
|
102
|
+
* canonical JSON of {kind: "provider_attest", payload}, signed raw for XRPL.
|
|
103
|
+
*/
|
|
104
|
+
function providerSignatureProblem(att) {
|
|
105
|
+
const parts = att.providerIdentity.split(":");
|
|
106
|
+
const namespace = parts[0];
|
|
107
|
+
const address = parts.slice(2).join(":");
|
|
108
|
+
if (namespace !== "xrpl" || !address) {
|
|
109
|
+
return `providerIdentity ${att.providerIdentity} is not an xrpl CAIP-10 identity; cannot authenticate it`;
|
|
110
|
+
}
|
|
111
|
+
if (att.providerSignatureScheme !== "ed25519") {
|
|
112
|
+
return `providerSignatureScheme ${att.providerSignatureScheme} is not ed25519`;
|
|
113
|
+
}
|
|
114
|
+
const wire = /^ed([0-9a-fA-F]{64}):([0-9a-fA-F]{128})$/.exec(att.providerSignature);
|
|
115
|
+
if (!wire) {
|
|
116
|
+
return "providerSignature is not the ed<pubkey_hex>:<sig_hex> wire form";
|
|
117
|
+
}
|
|
118
|
+
const pubKeyRaw = Buffer.from(wire[1], "hex");
|
|
119
|
+
const signature = Buffer.from(wire[2], "hex");
|
|
120
|
+
// Bind the key to the identity. Without this a self-signed blob naming any
|
|
121
|
+
// r-address verifies against its own key and passes.
|
|
122
|
+
const derived = xrplAddressFromPubKey(Buffer.concat([Buffer.from([0xed]), pubKeyRaw]));
|
|
123
|
+
if (derived !== address) {
|
|
124
|
+
return `providerSignature public key derives ${derived}, not the ${address} in providerIdentity`;
|
|
125
|
+
}
|
|
126
|
+
const digest = hashCanonicalJson({
|
|
127
|
+
kind: "provider_attest",
|
|
128
|
+
payload: {
|
|
129
|
+
providerIdentity: att.providerIdentity,
|
|
130
|
+
requestHash: att.requestHash,
|
|
131
|
+
responseHash: att.responseHash,
|
|
132
|
+
providerIssuedAt: att.providerIssuedAt,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const pubKey = createPublicKey({
|
|
136
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, pubKeyRaw]),
|
|
137
|
+
format: "der",
|
|
138
|
+
type: "spki",
|
|
139
|
+
});
|
|
140
|
+
if (!verifySignature(null, digest, pubKey, signature)) {
|
|
141
|
+
return "providerSignature does not verify over the attested payload";
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
33
144
|
}
|
|
145
|
+
// One line per unpinned provider, not one per call: the operator needs to see
|
|
146
|
+
// it once to populate HSEAL_ALLOWED_PROVIDERS, not on every tool call.
|
|
147
|
+
const warnedUnpinned = new Set();
|
|
34
148
|
/**
|
|
35
|
-
* Build + verify a 2-party receipt from a backend provider attestation.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
149
|
+
* Build + verify a 2-party receipt from a backend provider attestation. Returns
|
|
150
|
+
* the signed `body` (valid + independently verifiable) when signing succeeds;
|
|
151
|
+
* `verdict` is best-effort — if the H-Seal service is unreachable the body
|
|
152
|
+
* still stands and `verifyError` records why, but an explicit H-Seal reject
|
|
153
|
+
* drops the body. A responseHash that does not match what we delivered also
|
|
154
|
+
* drops the body, and says so in `verifyError` rather than vanishing, as does
|
|
155
|
+
* every other refusal: a malformed attestation, a provider signature that does
|
|
156
|
+
* not authenticate, and an identity outside HSEAL_ALLOWED_PROVIDERS all return
|
|
157
|
+
* a body-less result with a reason. Only two cases return undefined - H-Seal
|
|
158
|
+
* not being configured at all, and our own signing throwing. Never throws.
|
|
40
159
|
*/
|
|
41
160
|
export async function buildReceipt(opts) {
|
|
161
|
+
// The ONLY branch that may stay silent: H-Seal genuinely is not configured,
|
|
162
|
+
// so the absence of _meta.hSealReceipt is the truthful answer.
|
|
42
163
|
if (!hSealReceiptEnabled)
|
|
43
164
|
return undefined;
|
|
44
|
-
if (!looksLikeAttestation(opts.attestation))
|
|
45
|
-
|
|
165
|
+
if (!looksLikeAttestation(opts.attestation)) {
|
|
166
|
+
// The server only calls us when the backend put SOMETHING in the
|
|
167
|
+
// attestation slot, so a shape we cannot parse is producer drift, not
|
|
168
|
+
// "no attestation". Do not echo the blob back to the caller.
|
|
169
|
+
console.error("[hSealReceipt] attestation is missing required fields (see looksLikeAttestation); refusing to sign");
|
|
170
|
+
return { verifyError: "provider attestation is malformed", requestHashBasis: REQUEST_HASH_BASIS };
|
|
171
|
+
}
|
|
46
172
|
const att = opts.attestation;
|
|
173
|
+
// Corroborate the one hash we can compute ourselves. The backends hash the
|
|
174
|
+
// canonical JSON of the response minus the attestation envelope, which is
|
|
175
|
+
// exactly what the server hands us here. A mismatch means the receipt would
|
|
176
|
+
// bind our signature to bytes we never served.
|
|
177
|
+
if (sha256Hex(opts.responseBody) !== att.responseHash) {
|
|
178
|
+
// Refusing is right, but returning nothing made the refusal invisible: the
|
|
179
|
+
// flagship paid tool would just stop carrying _meta.hSealReceipt with no
|
|
180
|
+
// signal past one log line. Name the provider so an operator can tell which
|
|
181
|
+
// backend drifted (a producer that attests before its response model
|
|
182
|
+
// appends footer fields lands here on EVERY call).
|
|
183
|
+
console.error(`[hSealReceipt] responseHash does not match the delivered body for ${att.providerIdentity}; refusing to sign`);
|
|
184
|
+
return { verifyError: "responseHash does not match the delivered body", requestHashBasis: REQUEST_HASH_BASIS };
|
|
185
|
+
}
|
|
186
|
+
const problem = providerSignatureProblem(att);
|
|
187
|
+
if (problem) {
|
|
188
|
+
console.error(`[hSealReceipt] provider attestation rejected: ${problem}; refusing to sign`);
|
|
189
|
+
// `problem` names an UNAUTHENTICATED identity/key, so it stays in the log.
|
|
190
|
+
// The caller gets the fact of the refusal, which is the part that must not
|
|
191
|
+
// be indistinguishable from the feature being switched off.
|
|
192
|
+
return { verifyError: "provider attestation failed authentication", requestHashBasis: REQUEST_HASH_BASIS };
|
|
193
|
+
}
|
|
194
|
+
if (allowedProviders.length > 0) {
|
|
195
|
+
if (!allowedProviders.includes(att.providerIdentity)) {
|
|
196
|
+
console.error(`[hSealReceipt] providerIdentity ${att.providerIdentity} is not in HSEAL_ALLOWED_PROVIDERS; refusing to sign`);
|
|
197
|
+
return { verifyError: "provider is not in HSEAL_ALLOWED_PROVIDERS", requestHashBasis: REQUEST_HASH_BASIS };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (!warnedUnpinned.has(att.providerIdentity)) {
|
|
201
|
+
warnedUnpinned.add(att.providerIdentity);
|
|
202
|
+
console.error(`[hSealReceipt] HSEAL_ALLOWED_PROVIDERS is unset; co-signing for self-attested provider ${att.providerIdentity}. ` +
|
|
203
|
+
`Set it to the known XR-* identities to pin who this key will vouch for.`);
|
|
204
|
+
}
|
|
47
205
|
try {
|
|
48
206
|
const signed = await signReceipt({
|
|
49
207
|
receipt: {
|
|
50
208
|
taskId: randomUUID(),
|
|
51
209
|
serviceEndpoint: opts.serviceEndpoint,
|
|
52
|
-
requestHash: att.requestHash, //
|
|
53
|
-
responseHash: att.responseHash, //
|
|
210
|
+
requestHash: att.requestHash, // provider-asserted, see REQUEST_HASH_BASIS
|
|
211
|
+
responseHash: att.responseHash, // corroborated against the delivered body above
|
|
54
212
|
resultStatus: "success",
|
|
55
213
|
startedAt: opts.startedAt,
|
|
56
214
|
completedAt: opts.completedAt,
|
|
@@ -65,10 +223,19 @@ export async function buildReceipt(opts) {
|
|
|
65
223
|
const body = attachAttestation(signed.body, att);
|
|
66
224
|
try {
|
|
67
225
|
const verdict = await new HSealClient({ endpoint: HSEAL_API }).verify(body);
|
|
68
|
-
|
|
226
|
+
if (verdict.ok === false) {
|
|
227
|
+
// An explicit reject is the one verdict that must drop the body:
|
|
228
|
+
// handing the caller a receipt H-Seal has already refused presents it
|
|
229
|
+
// as provenance it does not carry. An unreachable H-Seal is different
|
|
230
|
+
// — the body verifies independently, so it still rides out below.
|
|
231
|
+
const reason = verdict.reason ?? "no reason given";
|
|
232
|
+
console.error(`[hSealReceipt] H-Seal rejected the receipt: ${reason}`);
|
|
233
|
+
return { verifyError: `H-Seal rejected the receipt: ${reason}`, requestHashBasis: REQUEST_HASH_BASIS };
|
|
234
|
+
}
|
|
235
|
+
return { body, verdict, requestHashBasis: REQUEST_HASH_BASIS };
|
|
69
236
|
}
|
|
70
237
|
catch (err) {
|
|
71
|
-
return { body, verifyError: err.message };
|
|
238
|
+
return { body, verifyError: err.message, requestHashBasis: REQUEST_HASH_BASIS };
|
|
72
239
|
}
|
|
73
240
|
}
|
|
74
241
|
catch (err) {
|
package/dist/hSealReceipt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hSealReceipt.js","sourceRoot":"","sources":["../src/hSealReceipt.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hSealReceipt.js","sourceRoot":"","sources":["../src/hSealReceipt.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -39,9 +39,17 @@ async function main() {
|
|
|
39
39
|
// on stderr (Claude Desktop captures stdout for the JSON-RPC stream).
|
|
40
40
|
// On HTTP we log normally.
|
|
41
41
|
if (!skipValidate) {
|
|
42
|
-
|
|
42
|
+
// Opt-in, default off: railway.json restarts ON_FAILURE, so failing closed
|
|
43
|
+
// by default would crash-loop the endpoint whenever one backend is cold.
|
|
44
|
+
const strict = process.env["STRICT_VALIDATE"] === "1";
|
|
45
|
+
const results = await validateAllServices({ strict });
|
|
43
46
|
const log = transport === "stdio" ? console.error : console.log;
|
|
44
47
|
for (const r of results) {
|
|
48
|
+
if (!r.checked) {
|
|
49
|
+
// Always say it, strict or not: a boot that verified nothing must be
|
|
50
|
+
// impossible to mistake for a clean one.
|
|
51
|
+
log(`[validate] ${r.service}: NOT CHECKED (no manifest)`);
|
|
52
|
+
}
|
|
45
53
|
if (r.errors.length) {
|
|
46
54
|
log(`[validate] ${r.service}: ERRORS`);
|
|
47
55
|
r.errors.forEach((e) => log(` ${e}`));
|
|
@@ -52,7 +60,8 @@ async function main() {
|
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
const anyError = results.some((r) => r.errors.length);
|
|
55
|
-
|
|
63
|
+
const anyUnchecked = results.some((r) => !r.checked);
|
|
64
|
+
if ((anyError || (strict && anyUnchecked)) && process.env["MCP_FAIL_ON_DRIFT"] === "1") {
|
|
56
65
|
process.exit(2);
|
|
57
66
|
}
|
|
58
67
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,SAAS,SAAS,CAAC,IAAc;IAC/B,IAAI,SAAS,GACX,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC/D,IAAI,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;IACjD,IAAI,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,GAAG,CAAC;IAE5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,KAAK,aAAa,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,MAAM;gBAAE,SAAS,GAAG,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACxC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,CAAC,KAAK,iBAAiB,EAAE,CAAC;YACnC,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3E,sEAAsE;IACtE,sEAAsE;IACtE,2BAA2B;IAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|