@t-0/provider-sdk 1.1.29 → 1.1.30

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.
Files changed (116) hide show
  1. package/README.md +22 -5
  2. package/lib/cjs/client/client.d.ts +4 -14
  3. package/lib/cjs/client/client.js +2 -38
  4. package/lib/cjs/client/signer.d.ts +1 -3
  5. package/lib/cjs/client/signer.js +7 -34
  6. package/lib/cjs/common/client/client.d.ts +14 -0
  7. package/lib/cjs/common/client/client.js +42 -0
  8. package/lib/cjs/common/client/signer.d.ts +3 -0
  9. package/lib/cjs/common/client/signer.js +36 -0
  10. package/lib/cjs/common/crypto/hash.d.ts +2 -0
  11. package/lib/cjs/common/crypto/hash.js +17 -0
  12. package/lib/cjs/common/crypto/index.d.ts +5 -0
  13. package/lib/cjs/common/crypto/index.js +14 -0
  14. package/lib/cjs/common/crypto/keys.d.ts +2 -0
  15. package/lib/cjs/common/crypto/keys.js +23 -0
  16. package/lib/cjs/common/crypto/request.d.ts +20 -0
  17. package/lib/cjs/common/crypto/request.js +51 -0
  18. package/lib/cjs/common/crypto/verify.d.ts +1 -0
  19. package/lib/cjs/common/crypto/verify.js +19 -0
  20. package/lib/cjs/common/health.d.ts +21 -0
  21. package/lib/cjs/common/health.js +45 -0
  22. package/lib/cjs/common/health_pb.d.ts +157 -0
  23. package/lib/cjs/common/health_pb.js +90 -0
  24. package/lib/cjs/common/node.d.ts +3 -0
  25. package/lib/cjs/common/node.js +15 -0
  26. package/lib/cjs/common/service.d.ts +37 -0
  27. package/lib/cjs/common/service.js +85 -0
  28. package/lib/cjs/common/validate.d.ts +25 -0
  29. package/lib/cjs/common/validate.js +42 -0
  30. package/lib/cjs/common/validation.d.ts +26 -0
  31. package/lib/cjs/common/validation.js +57 -0
  32. package/lib/cjs/crypto/hash.d.ts +1 -2
  33. package/lib/cjs/crypto/hash.js +15 -15
  34. package/lib/cjs/crypto/index.d.ts +1 -5
  35. package/lib/cjs/crypto/index.js +15 -12
  36. package/lib/cjs/crypto/keys.d.ts +1 -2
  37. package/lib/cjs/crypto/keys.js +15 -17
  38. package/lib/cjs/crypto/request.d.ts +1 -20
  39. package/lib/cjs/crypto/request.js +15 -47
  40. package/lib/cjs/crypto/verify.d.ts +1 -1
  41. package/lib/cjs/crypto/verify.js +15 -17
  42. package/lib/cjs/index.d.ts +1 -0
  43. package/lib/cjs/index.js +1 -0
  44. package/lib/cjs/service/health.d.ts +4 -21
  45. package/lib/cjs/service/health.js +5 -41
  46. package/lib/cjs/service/health_pb.d.ts +1 -157
  47. package/lib/cjs/service/health_pb.js +15 -88
  48. package/lib/cjs/service/node.d.ts +1 -3
  49. package/lib/cjs/service/node.js +15 -13
  50. package/lib/cjs/service/service.d.ts +6 -20
  51. package/lib/cjs/service/service.js +5 -84
  52. package/lib/cjs/service/validate.d.ts +1 -25
  53. package/lib/cjs/service/validate.js +15 -40
  54. package/lib/cjs/service/validate_response.d.ts +9 -18
  55. package/lib/cjs/service/validate_response.js +19 -57
  56. package/lib/cjs/version.d.ts +1 -1
  57. package/lib/cjs/version.js +1 -1
  58. package/lib/esm/client/client.d.ts +3 -13
  59. package/lib/esm/client/client.js +2 -35
  60. package/lib/esm/client/signer.d.ts +1 -3
  61. package/lib/esm/client/signer.js +1 -32
  62. package/lib/esm/common/client/client.d.ts +14 -0
  63. package/lib/esm/common/client/client.js +36 -0
  64. package/lib/esm/common/client/signer.d.ts +3 -0
  65. package/lib/esm/common/client/signer.js +32 -0
  66. package/lib/esm/common/crypto/hash.d.ts +2 -0
  67. package/lib/esm/common/crypto/hash.js +13 -0
  68. package/lib/esm/common/crypto/index.d.ts +5 -0
  69. package/lib/esm/common/crypto/index.js +4 -0
  70. package/lib/esm/common/crypto/keys.d.ts +2 -0
  71. package/lib/esm/common/crypto/keys.js +19 -0
  72. package/lib/esm/common/crypto/request.d.ts +20 -0
  73. package/lib/esm/common/crypto/request.js +47 -0
  74. package/lib/esm/common/crypto/verify.d.ts +1 -0
  75. package/lib/esm/common/crypto/verify.js +16 -0
  76. package/lib/esm/common/health.d.ts +21 -0
  77. package/lib/esm/common/health.js +41 -0
  78. package/lib/esm/common/health_pb.d.ts +157 -0
  79. package/lib/esm/common/health_pb.js +87 -0
  80. package/lib/esm/common/node.d.ts +3 -0
  81. package/lib/esm/common/node.js +11 -0
  82. package/lib/esm/common/service.d.ts +37 -0
  83. package/lib/esm/common/service.js +78 -0
  84. package/lib/esm/common/validate.d.ts +25 -0
  85. package/lib/esm/common/validate.js +38 -0
  86. package/lib/esm/common/validation.d.ts +26 -0
  87. package/lib/esm/common/validation.js +54 -0
  88. package/lib/esm/crypto/hash.d.ts +1 -2
  89. package/lib/esm/crypto/hash.js +1 -13
  90. package/lib/esm/crypto/index.d.ts +1 -5
  91. package/lib/esm/crypto/index.js +1 -4
  92. package/lib/esm/crypto/keys.d.ts +1 -2
  93. package/lib/esm/crypto/keys.js +1 -15
  94. package/lib/esm/crypto/request.d.ts +1 -20
  95. package/lib/esm/crypto/request.js +1 -45
  96. package/lib/esm/crypto/verify.d.ts +1 -1
  97. package/lib/esm/crypto/verify.js +1 -16
  98. package/lib/esm/index.d.ts +1 -0
  99. package/lib/esm/index.js +1 -0
  100. package/lib/esm/service/health.d.ts +4 -21
  101. package/lib/esm/service/health.js +3 -41
  102. package/lib/esm/service/health_pb.d.ts +1 -157
  103. package/lib/esm/service/health_pb.js +1 -87
  104. package/lib/esm/service/node.d.ts +1 -3
  105. package/lib/esm/service/node.js +1 -11
  106. package/lib/esm/service/service.d.ts +6 -20
  107. package/lib/esm/service/service.js +4 -81
  108. package/lib/esm/service/validate.d.ts +1 -25
  109. package/lib/esm/service/validate.js +1 -38
  110. package/lib/esm/service/validate_response.d.ts +9 -18
  111. package/lib/esm/service/validate_response.js +15 -54
  112. package/lib/esm/version.d.ts +1 -1
  113. package/lib/esm/version.js +1 -1
  114. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  115. package/lib/tsconfig.esm.tsbuildinfo +1 -1
  116. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # T-0 Provider SDK -- TypeScript
2
2
 
3
- TypeScript SDK for building provider integrations with the T-0 Network. Handles secp256k1 cryptographic signing, signature verification, and provides typed ConnectRPC clients for all T-0 Network APIs.
3
+ TypeScript SDK for building provider integrations with the T-0 Network. All communication is Protobuf-encoded and secp256k1-signed. Provides signature verification for inbound requests and typed clients for all T-0 Network APIs.
4
4
 
5
5
  ## Quick Start
6
6
 
@@ -62,14 +62,15 @@ const server = http.createServer(
62
62
  server.listen(3000);
63
63
  ```
64
64
 
65
- The middleware chain: `signatureValidation` captures raw request bytes for hashing, `nodeAdapter` bridges ConnectRPC to Node.js HTTP, and `createService` registers your handlers with signature verification.
65
+ The middleware chain: `signatureValidation` captures raw request bytes for hashing, `nodeAdapter` bridges the RPC transport to Node.js HTTP, and `createService` registers your handlers with signature verification.
66
66
 
67
67
  ### Standalone Signature Verification
68
68
 
69
69
  For frameworks that don't use Node's `http.createServer` (Effect, Koa, Fastify, etc.), use `createRequestVerifier` to verify inbound requests with just the raw body bytes and headers:
70
70
 
71
71
  ```ts
72
- import { createRequestVerifier, parsePublicKey } from "@t-0/provider-sdk";
72
+ import { fromBinary, toBinary } from "@bufbuild/protobuf";
73
+ import { createRequestVerifier, PayoutRequestSchema, PayoutResponseSchema } from "@t-0/provider-sdk";
73
74
 
74
75
  const verify = createRequestVerifier({
75
76
  networkPublicKey: process.env.NETWORK_PUBLIC_KEY!,
@@ -91,11 +92,27 @@ function handleRequest(rawBody: Uint8Array, headers: Record<string, string>) {
91
92
  return errorResponse(result.reason);
92
93
  }
93
94
 
94
- // Request is authenticated parse the protobuf body and handle it
95
+ // Deserialize the Protobuf request (same raw bytes you verified)
96
+ const request = fromBinary(PayoutRequestSchema, rawBody);
97
+ const response = handlePayout(request);
98
+
99
+ // Serialize the Protobuf response
100
+ return successResponse(toBinary(PayoutResponseSchema, response), {
101
+ "content-type": "application/proto",
102
+ });
95
103
  }
96
104
  ```
97
105
 
98
- The lower-level primitives are also exported individually: `verifySignature`, `computeDigest`, `keccak256`, `parsePublicKey`, `publicKeysEqual`.
106
+ The lower-level primitives are also exported individually: `verifySignature`, `computeDigest`, `keccak256`, `parsePublicKey`, `publicKeysEqual`. You can also import just the crypto module via the `./crypto` subpath: `import { createRequestVerifier } from "@t-0/provider-sdk/crypto"`.
107
+
108
+ **Important constraints for standalone integrations:**
109
+
110
+ - **Raw body bytes only.** Pass the exact wire bytes to the verifier — no body parsers, no auto-decompression, never re-serialized protobuf. Protobuf encoding is not canonical; re-encoding produces different bytes and breaks verification.
111
+ - **Pass `Uint8Array`, not `ArrayBuffer`.** If your framework gives you an `ArrayBuffer` (e.g. `request.arrayBuffer()`), wrap it: `new Uint8Array(buf)`.
112
+ - **Header case.** `NetworkHeaders` enum values are title-case (`X-Signature`), but Node lowercases incoming headers. Look up headers by lowercase name: `headers["x-signature"]`.
113
+ - **Wire format is binary protobuf.** Requests and successful responses use `Content-Type: application/proto`. Deserialize with `fromBinary()`, serialize with `toBinary()` from `@bufbuild/protobuf`. For errors, return a bare HTTP status code with no body (`401` for auth failures, `403` for permission errors). Success responses **must** include `Content-Type: application/proto`.
114
+ - **Health endpoint.** The T-0 Network probes `/grpc.health.v1.Health/Check` on every endpoint. The probe is signed. Standalone integrations must route this path and return a valid health response. See [`docs/HEALTH_SERVICE.md`](../../docs/HEALTH_SERVICE.md) for the wire contract.
115
+ - **`VerifyRequestFailure` is an open union.** New reason values may be added without a major version bump. Handle unknown reasons as generic failures.
99
116
 
100
117
  ### Network Client
101
118
 
@@ -1,16 +1,6 @@
1
- import { DescService } from "@bufbuild/protobuf";
1
+ import { type Signature } from "../common/client/client.js";
2
+ import type { DescService } from "@bufbuild/protobuf";
2
3
  export declare const DEFAULT_ENDPOINT = "https://api.t-0.network";
3
- export declare function createClient<T extends DescService>(signer: string | Buffer | ((data: Buffer) => Promise<Signature>) | Buffer<ArrayBufferLike>, endpoint: string | undefined, svc: T): import("@connectrpc/connect").Client<T>;
4
- /**
5
- * Signature with metadata for particular request
6
- */
7
- export interface Signature {
8
- signature: Buffer;
9
- publicKey: Buffer;
10
- }
11
- /**
12
- * Signature function for signing requests to T-0 API. Accepts any data in string format and return signature
13
- * with metadata
14
- */
15
- export type SignerFunction = (data: Buffer) => Promise<Signature>;
4
+ export declare function createClient<T extends DescService>(signer: string | Buffer | ((data: Buffer) => Promise<Signature>) | Buffer<ArrayBufferLike>, endpoint: string | undefined, svc: T): import("../index.js").Client<T>;
5
+ export type { Signature, SignerFunction } from "../common/client/client.js";
16
6
  export default createClient;
@@ -1,46 +1,10 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.DEFAULT_ENDPOINT = void 0;
7
4
  exports.createClient = createClient;
8
- const connect_1 = require("@connectrpc/connect");
9
- const connect_web_1 = require("@connectrpc/connect-web");
10
- const sha3_js_1 = require("@noble/hashes/sha3.js");
11
- const signer_js_1 = __importDefault(require("./signer.js"));
12
- const headers_js_1 = __importDefault(require("../common/headers.js"));
5
+ const client_js_1 = require("../common/client/client.js");
13
6
  exports.DEFAULT_ENDPOINT = "https://api.t-0.network";
14
7
  function createClient(signer, endpoint, svc) {
15
- let customFetch;
16
- endpoint = endpoint || exports.DEFAULT_ENDPOINT;
17
- if (typeof signer === "string" || Buffer.isBuffer(signer)) {
18
- signer = (0, signer_js_1.default)(signer);
19
- }
20
- customFetch = async (r, init) => {
21
- if (!init?.body || !((init.body) instanceof Uint8Array)) {
22
- throw "unsupported body type";
23
- }
24
- const ts = Date.now();
25
- // 64‑bit little‑endian timestamp
26
- const tsBuf = Buffer.alloc(8);
27
- tsBuf.writeBigUInt64LE(BigInt(ts));
28
- const hash = sha3_js_1.keccak_256.create()
29
- .update(init.body)
30
- .update(tsBuf);
31
- const hashHex = Buffer.from(hash.digest());
32
- const sig = await signer(hashHex);
33
- const headers = new Headers(init?.headers);
34
- headers.append(headers_js_1.default.Signature, "0x" + sig.signature.toString('hex'));
35
- headers.append(headers_js_1.default.PublicKey, "0x" + sig.publicKey.toString('hex'));
36
- headers.append(headers_js_1.default.SignatureTimestamp, ts.toString());
37
- const modifiedInit = { ...init, headers };
38
- return fetch(r, modifiedInit);
39
- };
40
- const transport = (0, connect_web_1.createConnectTransport)({
41
- baseUrl: endpoint || exports.DEFAULT_ENDPOINT,
42
- fetch: customFetch,
43
- });
44
- return (0, connect_1.createClient)(svc, transport);
8
+ return (0, client_js_1.createClient)(signer, endpoint || exports.DEFAULT_ENDPOINT, svc);
45
9
  }
46
10
  exports.default = createClient;
@@ -1,3 +1 @@
1
- import { Signature } from "./client.js";
2
- export declare const CreateSigner: (privateKey: string | Buffer) => (data: Buffer) => Promise<Signature>;
3
- export default CreateSigner;
1
+ export { CreateSigner, default } from "../common/client/signer.js";
@@ -1,36 +1,9 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CreateSigner = void 0;
4
- const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
5
- const CreateSigner = (privateKey) => {
6
- privateKey = parsePrivateKey(privateKey);
7
- const publicKey = Buffer.from(secp256k1_js_1.secp256k1.getPublicKey(privateKey, false));
8
- return async (data) => {
9
- // Ensure hash is 32 bytes
10
- if (data.length !== 32) {
11
- throw new Error('Message hash must be 32 bytes');
12
- }
13
- // Sign the hash
14
- const signature = secp256k1_js_1.secp256k1.sign(data, privateKey, { prehash: false });
15
- return {
16
- signature: Buffer.from(signature),
17
- publicKey: publicKey,
18
- };
19
- };
20
- };
21
- exports.CreateSigner = CreateSigner;
22
- const parsePrivateKey = (privateKey) => {
23
- if (typeof privateKey == 'string') {
24
- privateKey = privateKey.replace(/^0x/, '');
25
- if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) {
26
- throw new Error('Private key must be 64 hex characters');
27
- }
28
- privateKey = Buffer.from(privateKey, 'hex');
29
- }
30
- // Validate private key
31
- if (!secp256k1_js_1.secp256k1.utils.isValidSecretKey(privateKey)) {
32
- throw new Error('Invalid private key');
33
- }
34
- return privateKey;
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
35
4
  };
36
- exports.default = exports.CreateSigner;
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = exports.CreateSigner = void 0;
7
+ var signer_js_1 = require("../common/client/signer.js");
8
+ Object.defineProperty(exports, "CreateSigner", { enumerable: true, get: function () { return signer_js_1.CreateSigner; } });
9
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(signer_js_1).default; } });
@@ -0,0 +1,14 @@
1
+ import { DescService } from "@bufbuild/protobuf";
2
+ export declare function createClient<T extends DescService>(signer: string | Buffer | ((data: Buffer) => Promise<Signature>) | Buffer<ArrayBufferLike>, endpoint: string, svc: T): import("@connectrpc/connect").Client<T>;
3
+ /**
4
+ * Signature with metadata for particular request
5
+ */
6
+ export interface Signature {
7
+ signature: Buffer;
8
+ publicKey: Buffer;
9
+ }
10
+ /**
11
+ * Signature function for signing requests to T-0 API. Accepts any data in string format and return signature
12
+ * with metadata
13
+ */
14
+ export type SignerFunction = (data: Buffer) => Promise<Signature>;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createClient = createClient;
7
+ const connect_1 = require("@connectrpc/connect");
8
+ const connect_web_1 = require("@connectrpc/connect-web");
9
+ const sha3_js_1 = require("@noble/hashes/sha3.js");
10
+ const signer_js_1 = __importDefault(require("./signer.js"));
11
+ const headers_js_1 = __importDefault(require("../headers.js"));
12
+ function createClient(signer, endpoint, svc) {
13
+ let customFetch;
14
+ if (typeof signer === "string" || Buffer.isBuffer(signer)) {
15
+ signer = (0, signer_js_1.default)(signer);
16
+ }
17
+ customFetch = async (r, init) => {
18
+ if (!init?.body || !((init.body) instanceof Uint8Array)) {
19
+ throw "unsupported body type";
20
+ }
21
+ const ts = Date.now();
22
+ // 64‑bit little‑endian timestamp
23
+ const tsBuf = Buffer.alloc(8);
24
+ tsBuf.writeBigUInt64LE(BigInt(ts));
25
+ const hash = sha3_js_1.keccak_256.create()
26
+ .update(init.body)
27
+ .update(tsBuf);
28
+ const hashHex = Buffer.from(hash.digest());
29
+ const sig = await signer(hashHex);
30
+ const headers = new Headers(init?.headers);
31
+ headers.append(headers_js_1.default.Signature, "0x" + sig.signature.toString('hex'));
32
+ headers.append(headers_js_1.default.PublicKey, "0x" + sig.publicKey.toString('hex'));
33
+ headers.append(headers_js_1.default.SignatureTimestamp, ts.toString());
34
+ const modifiedInit = { ...init, headers };
35
+ return fetch(r, modifiedInit);
36
+ };
37
+ const transport = (0, connect_web_1.createConnectTransport)({
38
+ baseUrl: endpoint,
39
+ fetch: customFetch,
40
+ });
41
+ return (0, connect_1.createClient)(svc, transport);
42
+ }
@@ -0,0 +1,3 @@
1
+ import type { Signature } from "./client.js";
2
+ export declare const CreateSigner: (privateKey: string | Buffer) => (data: Buffer) => Promise<Signature>;
3
+ export default CreateSigner;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CreateSigner = void 0;
4
+ const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
5
+ const CreateSigner = (privateKey) => {
6
+ privateKey = parsePrivateKey(privateKey);
7
+ const publicKey = Buffer.from(secp256k1_js_1.secp256k1.getPublicKey(privateKey, false));
8
+ return async (data) => {
9
+ // Ensure hash is 32 bytes
10
+ if (data.length !== 32) {
11
+ throw new Error('Message hash must be 32 bytes');
12
+ }
13
+ // Sign the hash
14
+ const signature = secp256k1_js_1.secp256k1.sign(data, privateKey, { prehash: false });
15
+ return {
16
+ signature: Buffer.from(signature),
17
+ publicKey: publicKey,
18
+ };
19
+ };
20
+ };
21
+ exports.CreateSigner = CreateSigner;
22
+ const parsePrivateKey = (privateKey) => {
23
+ if (typeof privateKey == 'string') {
24
+ privateKey = privateKey.replace(/^0x/, '');
25
+ if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) {
26
+ throw new Error('Private key must be 64 hex characters');
27
+ }
28
+ privateKey = Buffer.from(privateKey, 'hex');
29
+ }
30
+ // Validate private key
31
+ if (!secp256k1_js_1.secp256k1.utils.isValidSecretKey(privateKey)) {
32
+ throw new Error('Invalid private key');
33
+ }
34
+ return privateKey;
35
+ };
36
+ exports.default = exports.CreateSigner;
@@ -0,0 +1,2 @@
1
+ export declare function keccak256(...inputs: Uint8Array[]): Buffer;
2
+ export declare function computeDigest(body: Uint8Array, timestampMs: number): Buffer;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.keccak256 = keccak256;
4
+ exports.computeDigest = computeDigest;
5
+ const sha3_js_1 = require("@noble/hashes/sha3.js");
6
+ function keccak256(...inputs) {
7
+ const h = sha3_js_1.keccak_256.create();
8
+ for (const input of inputs) {
9
+ h.update(input);
10
+ }
11
+ return Buffer.from(h.digest());
12
+ }
13
+ function computeDigest(body, timestampMs) {
14
+ const tsBuf = Buffer.alloc(8);
15
+ tsBuf.writeBigUInt64LE(BigInt(timestampMs));
16
+ return keccak256(body, tsBuf);
17
+ }
@@ -0,0 +1,5 @@
1
+ export { verifySignature } from './verify.js';
2
+ export { keccak256, computeDigest } from './hash.js';
3
+ export { parsePublicKey, publicKeysEqual } from './keys.js';
4
+ export { createRequestVerifier, DEFAULT_TOLERANCE_MS } from './request.js';
5
+ export type { CreateVerifierOptions, VerifyRequest, VerifyRequestResult, VerifyRequestFailure, RequestVerifier } from './request.js';
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TOLERANCE_MS = exports.createRequestVerifier = exports.publicKeysEqual = exports.parsePublicKey = exports.computeDigest = exports.keccak256 = exports.verifySignature = void 0;
4
+ var verify_js_1 = require("./verify.js");
5
+ Object.defineProperty(exports, "verifySignature", { enumerable: true, get: function () { return verify_js_1.verifySignature; } });
6
+ var hash_js_1 = require("./hash.js");
7
+ Object.defineProperty(exports, "keccak256", { enumerable: true, get: function () { return hash_js_1.keccak256; } });
8
+ Object.defineProperty(exports, "computeDigest", { enumerable: true, get: function () { return hash_js_1.computeDigest; } });
9
+ var keys_js_1 = require("./keys.js");
10
+ Object.defineProperty(exports, "parsePublicKey", { enumerable: true, get: function () { return keys_js_1.parsePublicKey; } });
11
+ Object.defineProperty(exports, "publicKeysEqual", { enumerable: true, get: function () { return keys_js_1.publicKeysEqual; } });
12
+ var request_js_1 = require("./request.js");
13
+ Object.defineProperty(exports, "createRequestVerifier", { enumerable: true, get: function () { return request_js_1.createRequestVerifier; } });
14
+ Object.defineProperty(exports, "DEFAULT_TOLERANCE_MS", { enumerable: true, get: function () { return request_js_1.DEFAULT_TOLERANCE_MS; } });
@@ -0,0 +1,2 @@
1
+ export declare function parsePublicKey(key: string | Buffer): Buffer;
2
+ export declare function publicKeysEqual(a: Uint8Array, b: Uint8Array): boolean;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePublicKey = parsePublicKey;
4
+ exports.publicKeysEqual = publicKeysEqual;
5
+ function parsePublicKey(key) {
6
+ if (typeof key === 'string') {
7
+ const hex = key.startsWith('0x') ? key.slice(2) : key;
8
+ if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {
9
+ throw new Error('Public key contains invalid hex characters');
10
+ }
11
+ key = Buffer.from(hex, 'hex');
12
+ }
13
+ if (key.length !== 65 || key[0] !== 0x04) {
14
+ throw new Error('Public key must be 65 bytes in uncompressed format (0x04 prefix)');
15
+ }
16
+ return Buffer.from(key);
17
+ }
18
+ function publicKeysEqual(a, b) {
19
+ if (a.length !== b.length) {
20
+ return false;
21
+ }
22
+ return Buffer.from(a).compare(Buffer.from(b)) === 0;
23
+ }
@@ -0,0 +1,20 @@
1
+ export declare const DEFAULT_TOLERANCE_MS = 60000;
2
+ export interface CreateVerifierOptions {
3
+ networkPublicKey: string | Buffer;
4
+ toleranceMs?: number;
5
+ }
6
+ export interface VerifyRequest {
7
+ body: Uint8Array | ArrayBufferView | ArrayBufferLike;
8
+ signatureHeader: string;
9
+ publicKeyHeader: string;
10
+ timestampHeader: string;
11
+ }
12
+ export type VerifyRequestFailure = 'invalid_timestamp' | 'timestamp_out_of_range' | 'invalid_public_key' | 'unknown_public_key' | 'invalid_signature_format' | 'signature_failed';
13
+ export type VerifyRequestResult = {
14
+ valid: true;
15
+ } | {
16
+ valid: false;
17
+ reason: VerifyRequestFailure;
18
+ };
19
+ export type RequestVerifier = (req: VerifyRequest) => VerifyRequestResult;
20
+ export declare function createRequestVerifier(opts: CreateVerifierOptions): RequestVerifier;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TOLERANCE_MS = void 0;
4
+ exports.createRequestVerifier = createRequestVerifier;
5
+ const verify_js_1 = require("./verify.js");
6
+ const hash_js_1 = require("./hash.js");
7
+ const keys_js_1 = require("./keys.js");
8
+ exports.DEFAULT_TOLERANCE_MS = 60000;
9
+ function createRequestVerifier(opts) {
10
+ const networkKey = (0, keys_js_1.parsePublicKey)(opts.networkPublicKey);
11
+ const tolerance = opts.toleranceMs ?? exports.DEFAULT_TOLERANCE_MS;
12
+ return (req) => {
13
+ const ts = parseInt(req.timestampHeader, 10);
14
+ if (!Number.isFinite(ts) || ts < 0) {
15
+ return { valid: false, reason: 'invalid_timestamp' };
16
+ }
17
+ if (Math.abs(Date.now() - ts) > tolerance) {
18
+ return { valid: false, reason: 'timestamp_out_of_range' };
19
+ }
20
+ let publicKey;
21
+ try {
22
+ publicKey = (0, keys_js_1.parsePublicKey)(req.publicKeyHeader);
23
+ }
24
+ catch {
25
+ return { valid: false, reason: 'invalid_public_key' };
26
+ }
27
+ if (!(0, keys_js_1.publicKeysEqual)(publicKey, networkKey)) {
28
+ return { valid: false, reason: 'unknown_public_key' };
29
+ }
30
+ const sigHex = req.signatureHeader.startsWith('0x')
31
+ ? req.signatureHeader.slice(2)
32
+ : req.signatureHeader;
33
+ if (!/^[0-9a-fA-F]*$/.test(sigHex) || sigHex.length % 2 !== 0) {
34
+ return { valid: false, reason: 'invalid_signature_format' };
35
+ }
36
+ const signature = Buffer.from(sigHex, 'hex');
37
+ if (signature.length !== 64 && signature.length !== 65) {
38
+ return { valid: false, reason: 'invalid_signature_format' };
39
+ }
40
+ const body = req.body instanceof Uint8Array
41
+ ? req.body
42
+ : ArrayBuffer.isView(req.body)
43
+ ? new Uint8Array(req.body.buffer, req.body.byteOffset, req.body.byteLength)
44
+ : new Uint8Array(req.body);
45
+ const digest = (0, hash_js_1.computeDigest)(body, ts);
46
+ if (!(0, verify_js_1.verifySignature)(publicKey, digest, signature)) {
47
+ return { valid: false, reason: 'signature_failed' };
48
+ }
49
+ return { valid: true };
50
+ };
51
+ }
@@ -0,0 +1 @@
1
+ export declare function verifySignature(publicKey: Uint8Array, digest: Uint8Array, signature: Uint8Array): boolean;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifySignature = verifySignature;
4
+ const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
5
+ function verifySignature(publicKey, digest, signature) {
6
+ if (digest.length !== 32) {
7
+ return false;
8
+ }
9
+ if (signature.length !== 64 && signature.length !== 65) {
10
+ return false;
11
+ }
12
+ const sig64 = signature.length === 65 ? signature.subarray(0, 64) : signature;
13
+ try {
14
+ return secp256k1_js_1.secp256k1.verify(sig64, digest, publicKey, { prehash: false });
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
@@ -0,0 +1,21 @@
1
+ import { type ServiceImpl } from "@connectrpc/connect";
2
+ import { Health } from "./health_pb.js";
3
+ /**
4
+ * Headers carrying the identity of the SDK answering the probe. They ride on
5
+ * the health response and nowhere else: `HealthCheckResponse` has a single
6
+ * status field and `check` names its service in the request, so the contract
7
+ * itself has no room for this — and taxing every callback with headers only the
8
+ * probe reads would be worse.
9
+ */
10
+ export declare const SDK_ECOSYSTEM_HEADER = "T0-Sdk-Ecosystem";
11
+ export declare const SDK_VERSION_HEADER = "T0-Sdk-Version";
12
+ /**
13
+ * Reports SERVING for the services registered on this server and NOT_FOUND for
14
+ * anything else. The set is frozen at registration; nothing is computed per
15
+ * request.
16
+ *
17
+ * `watch` is absent on purpose: it is server-streaming, and the body-hash
18
+ * signature scheme these servers run behind has no story for streams. Omitting
19
+ * it from a partial `ServiceImpl` makes connect-es answer UNIMPLEMENTED.
20
+ */
21
+ export declare const createHealthServiceImpl: (services: string[], version?: string) => Partial<ServiceImpl<typeof Health>>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createHealthServiceImpl = exports.SDK_VERSION_HEADER = exports.SDK_ECOSYSTEM_HEADER = void 0;
4
+ const protobuf_1 = require("@bufbuild/protobuf");
5
+ const connect_1 = require("@connectrpc/connect");
6
+ const health_pb_js_1 = require("./health_pb.js");
7
+ /**
8
+ * Headers carrying the identity of the SDK answering the probe. They ride on
9
+ * the health response and nowhere else: `HealthCheckResponse` has a single
10
+ * status field and `check` names its service in the request, so the contract
11
+ * itself has no room for this — and taxing every callback with headers only the
12
+ * probe reads would be worse.
13
+ */
14
+ exports.SDK_ECOSYSTEM_HEADER = "T0-Sdk-Ecosystem";
15
+ exports.SDK_VERSION_HEADER = "T0-Sdk-Version";
16
+ const SDK_ECOSYSTEM = "node";
17
+ /**
18
+ * Reports SERVING for the services registered on this server and NOT_FOUND for
19
+ * anything else. The set is frozen at registration; nothing is computed per
20
+ * request.
21
+ *
22
+ * `watch` is absent on purpose: it is server-streaming, and the body-hash
23
+ * signature scheme these servers run behind has no story for streams. Omitting
24
+ * it from a partial `ServiceImpl` makes connect-es answer UNIMPLEMENTED.
25
+ */
26
+ const createHealthServiceImpl = (services, version) => {
27
+ const registered = new Set(services);
28
+ const serving = (0, protobuf_1.create)(health_pb_js_1.HealthCheckResponseSchema, {
29
+ status: health_pb_js_1.HealthCheckResponse_ServingStatus.SERVING,
30
+ });
31
+ return {
32
+ check(req, ctx) {
33
+ ctx.responseHeader.set(exports.SDK_ECOSYSTEM_HEADER, SDK_ECOSYSTEM);
34
+ if (version) {
35
+ ctx.responseHeader.set(exports.SDK_VERSION_HEADER, version);
36
+ }
37
+ const { service } = req;
38
+ if (service !== "" && !registered.has(service)) {
39
+ throw new connect_1.ConnectError(`unknown service '${service}'`, connect_1.Code.NotFound);
40
+ }
41
+ return serving;
42
+ },
43
+ };
44
+ };
45
+ exports.createHealthServiceImpl = createHealthServiceImpl;