openttt 0.1.3 → 0.2.1

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 (44) hide show
  1. package/README.md +48 -26
  2. package/dist/adaptive_switch.js +1 -1
  3. package/dist/auto_mint.js +2 -2
  4. package/dist/http_client.d.ts +98 -0
  5. package/dist/http_client.js +252 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +2 -0
  8. package/dist/integrity_client.d.ts +39 -0
  9. package/dist/integrity_client.js +114 -0
  10. package/dist/time_synthesis.js +0 -4
  11. package/dist/ttt_client.d.ts +18 -0
  12. package/dist/ttt_client.js +20 -0
  13. package/package.json +6 -2
  14. package/dist/golay.d.ts +0 -6
  15. package/dist/golay.js +0 -166
  16. package/dist/grg_forward.d.ts +0 -11
  17. package/dist/grg_forward.js +0 -74
  18. package/dist/grg_inverse.d.ts +0 -7
  19. package/dist/grg_inverse.js +0 -100
  20. package/dist/grg_pipeline.d.ts +0 -13
  21. package/dist/grg_pipeline.js +0 -64
  22. package/dist/reed_solomon.d.ts +0 -12
  23. package/dist/reed_solomon.js +0 -179
  24. package/vendor/helm-crypto/golay.d.ts +0 -6
  25. package/vendor/helm-crypto/golay.js +0 -167
  26. package/vendor/helm-crypto/golay.js.map +0 -1
  27. package/vendor/helm-crypto/grg_forward.d.ts +0 -22
  28. package/vendor/helm-crypto/grg_forward.js +0 -89
  29. package/vendor/helm-crypto/grg_forward.js.map +0 -1
  30. package/vendor/helm-crypto/grg_inverse.d.ts +0 -16
  31. package/vendor/helm-crypto/grg_inverse.js +0 -118
  32. package/vendor/helm-crypto/grg_inverse.js.map +0 -1
  33. package/vendor/helm-crypto/grg_pipeline.d.ts +0 -13
  34. package/vendor/helm-crypto/grg_pipeline.js +0 -66
  35. package/vendor/helm-crypto/grg_pipeline.js.map +0 -1
  36. package/vendor/helm-crypto/index.d.ts +0 -5
  37. package/vendor/helm-crypto/index.js +0 -17
  38. package/vendor/helm-crypto/index.js.map +0 -1
  39. package/vendor/helm-crypto/logger.d.ts +0 -6
  40. package/vendor/helm-crypto/logger.js +0 -11
  41. package/vendor/helm-crypto/logger.js.map +0 -1
  42. package/vendor/helm-crypto/reed_solomon.d.ts +0 -37
  43. package/vendor/helm-crypto/reed_solomon.js +0 -210
  44. package/vendor/helm-crypto/reed_solomon.js.map +0 -1
package/README.md CHANGED
@@ -10,7 +10,9 @@ OpenTTT brings cryptographic time verification to blockchain transaction orderin
10
10
  [![License: BSL-1.1](https://img.shields.io/badge/License-BSL--1.1-blue.svg)](LICENSE)
11
11
  [![CI](https://github.com/Helm-Protocol/OpenTTT/actions/workflows/ci.yml/badge.svg)](https://github.com/Helm-Protocol/OpenTTT/actions/workflows/ci.yml)
12
12
  [![codecov](https://codecov.io/gh/Helm-Protocol/OpenTTT/branch/main/graph/badge.svg)](https://codecov.io/gh/Helm-Protocol/OpenTTT)
13
- [![Tests](https://img.shields.io/badge/tests-273%20passing%20%C2%B7%2029%20suites-brightgreen)]()
13
+ [![Tests](https://img.shields.io/badge/tests-386%20passing%20%C2%B7%2032%20suites-brightgreen)]()
14
+
15
+ > If this project is useful to you, please [star it on GitHub](https://github.com/Helm-Protocol/OpenTTT) — it helps others find it.
14
16
 
15
17
  ```
16
18
  npm install openttt
@@ -40,7 +42,22 @@ No governance vote. No slashing committee. Cheating is simply bad business.
40
42
 
41
43
  ## Quick Start
42
44
 
43
- Three lines to start minting TimeTokens:
45
+ ### Try it in 30 seconds — No ETH, No Wallet
46
+
47
+ ```typescript
48
+ import { HttpOnlyClient } from "openttt";
49
+
50
+ const client = new HttpOnlyClient();
51
+ const pot = await client.generatePoT();
52
+ console.log(pot.timestamp, pot.confidence, pot.sources);
53
+
54
+ const valid = client.verifyPoT(pot);
55
+ console.log("Valid:", valid); // true
56
+ ```
57
+
58
+ No blockchain. No wallet. No gas fees. Just verified time from 4 independent HTTPS sources (NIST, Apple, Google, Cloudflare). Start here, upgrade to on-chain when ready.
59
+
60
+ ### On-Chain Mode (Full Power)
44
61
 
45
62
  ```typescript
46
63
  import { TTTClient } from "openttt";
@@ -49,9 +66,7 @@ const ttt = await TTTClient.forBase({ privateKey: process.env.OPERATOR_PK! });
49
66
  ttt.startAutoMint();
50
67
  ```
51
68
 
52
- That is it. The SDK connects to Base Mainnet, synthesizes time from three atomic clock sources, and begins minting Proof-of-Time tokens at your configured tier interval.
53
-
54
- > **Shorthand**: Pass `privateKey` directly as a string instead of the full `signer` config object. The verbose form `{ signer: { type: "privateKey", key: "0x..." } }` still works for when you need other signer types (Turnkey, KMS, Privy).
69
+ Connects to Base, synthesizes time from atomic clock sources, and mints Proof-of-Time tokens on-chain.
55
70
 
56
71
  ---
57
72
 
@@ -246,8 +261,8 @@ const ttt = await TTTClient.create({
246
261
 
247
262
  | Method | Description |
248
263
  |---|---|
249
- | `GrgPipeline.processForward(data)` | Encode: Golomb compression -> Reed-Solomon erasure coding -> Golay verification |
250
- | `GrgPipeline.processInverse(shards, length)` | Decode: Golay verify -> Reed-Solomon reconstruct -> Golomb decompress |
264
+ | `GrgPipeline.processForward(data)` | Encode data through the multi-layer integrity pipeline, producing verifiable shards |
265
+ | `GrgPipeline.processInverse(shards, length)` | Decode shards back to original data with integrity verification |
251
266
 
252
267
  ### AdaptiveSwitch
253
268
 
@@ -269,32 +284,18 @@ TTTClient (entry point)
269
284
  | |-- EVMConnector On-chain mint/burn/events (ethers v6)
270
285
  | '-- ProtocolFee EIP-712 signed fee collection
271
286
  |-- AdaptiveSwitch TURBO/FULL mode state machine
272
- |-- GRG Pipeline Golomb + Reed-Solomon + Golay error correction
287
+ |-- GRG Pipeline Multi-layer data integrity (proprietary)
273
288
  |-- PoolRegistry Multi-pool statistics tracking
274
289
  '-- Signer Abstraction PrivateKey | Turnkey | Privy | KMS
275
290
  ```
276
291
 
277
- ### The GRG Pipeline
292
+ ### Data Integrity: GRG Pipeline
278
293
 
279
- GRG (Golomb-Reed-Golay) is the data integrity backbone of OpenTTT, analogous to how the TLS record protocol protects HTTP payloads.
294
+ GRG is a multi-layer data integrity pipeline that protects PoT payloads — analogous to how the TLS record protocol protects HTTP payloads. It provides compression, erasure coding, and error correction in a single pass.
280
295
 
281
- ```
282
- FORWARD (Encode)
283
- Raw Data --> Golomb-Rice Compression
284
- --> Reed-Solomon Erasure Coding
285
- --> Golay(24,12) Verification Codes
286
- --> Shards
287
-
288
- INVERSE (Decode)
289
- Shards --> Golay(24,12) Error Detection
290
- --> Reed-Solomon Reconstruction
291
- --> Golomb-Rice Decompression
292
- --> Raw Data
293
- ```
296
+ The pipeline produces verifiable shards that can be independently validated and reconstructed, ensuring PoT integrity even under partial data loss.
294
297
 
295
- **Golomb-Rice** compresses timestamp deltas efficiently (small integers compress well).
296
- **Reed-Solomon** adds erasure coding so data survives shard loss.
297
- **Golay(24,12)** detects and corrects up to 3-bit errors per 24-bit codeword.
298
+ > Implementation details are proprietary. See the [IETF Draft](https://datatracker.ietf.org/doc/draft-helmprotocol-tttps/) for the abstract specification.
298
299
 
299
300
  ### Adaptive Mode Switching
300
301
 
@@ -390,4 +391,25 @@ Copyright 2026 Helm Protocol.
390
391
 
391
392
  ---
392
393
 
394
+ ## Learn More
395
+
396
+ - [IETF Draft: draft-helmprotocol-tttps-00](https://datatracker.ietf.org/doc/draft-helmprotocol-tttps/) — TTTPS Protocol Specification
397
+ - [Yellow Paper](https://github.com/Helm-Protocol/OpenTTT/blob/main/YELLOW_PAPER.md) — Technical Deep Dive
398
+ - [MCP Server](https://github.com/Helm-Protocol/OpenTTT/tree/main/mcp) — AI Agent Integration (`@helm-protocol/ttt-mcp`)
399
+ - [Subgraph (The Graph)](https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.2.0) — On-chain PoT Data
400
+ - [Base Sepolia Contracts](https://sepolia.basescan.org/address/0xde357135cA493e59680182CDE9E1c6A4dA400811) — TTT ERC-1155
401
+ - [Helm Protocol](https://github.com/Helm-Protocol) — GitHub Organization
402
+
393
403
  [GitHub](https://github.com/Helm-Protocol/OpenTTT) | Built by [Helm Protocol](https://github.com/Helm-Protocol)
404
+
405
+ ---
406
+
407
+ ## Contributing
408
+
409
+ Contributions are welcome. If you find a bug, have a feature request, or want to improve the documentation, please open an issue or submit a pull request on [GitHub](https://github.com/Helm-Protocol/OpenTTT).
410
+
411
+ - **Bug reports**: Open an issue with a minimal reproduction case.
412
+ - **Feature requests**: Open an issue describing the use case and expected behavior.
413
+ - **Pull requests**: Fork the repo, make your changes, ensure all tests pass (`npm test`), and open a PR against `main`.
414
+
415
+ For significant changes, please open an issue first to discuss the approach.
@@ -43,7 +43,7 @@ class AdaptiveSwitch {
43
43
  let sequenceOk = orderMatch && timeMatch;
44
44
  // B1-1: Do not skip GrgInverse.verify() in TURBO mode
45
45
  // We check integrity regardless of mode
46
- const integrityOk = helm_crypto_1.GrgInverse.verify(block.data, tttRecord.grgPayload, chainId, poolAddress);
46
+ const integrityOk = helm_crypto_1.IntegrityDecoder.verify(block.data, tttRecord.grgPayload, chainId, poolAddress);
47
47
  if (!integrityOk) {
48
48
  logger_1.logger.error(`[AdaptiveSwitch] GRG integrity check FAILED`);
49
49
  sequenceOk = false; // Mark as false if integrity fails
package/dist/auto_mint.js CHANGED
@@ -220,10 +220,10 @@ class AutoMintEngine {
220
220
  const tokenId = ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "address", "uint64", "uint256"], [BigInt(this.config.chainId), this.config.poolAddress, synthesized.timestamp, nonceSuffix]));
221
221
  // 3. Fee calculation
222
222
  const feeCalculation = await this.feeEngine.calculateMintFee(this.config.tier);
223
- // 4. EVM mint call — run full GRG pipeline (Golomb → Reed-Solomon → Golay+HMAC)
223
+ // 4. EVM mint call — run GRG integrity pipeline
224
224
  const grgPayload = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(["bytes32", "bytes32", "uint64", "uint8"], [tokenId, potHash, synthesized.timestamp, pot.sources]);
225
225
  const grgStart = Date.now();
226
- const grgShards = helm_crypto_1.GrgForward.encode(ethers_1.ethers.getBytes(grgPayload), this.config.chainId, this.config.poolAddress);
226
+ const grgShards = helm_crypto_1.IntegrityEncoder.encode(ethers_1.ethers.getBytes(grgPayload), this.config.chainId, this.config.poolAddress);
227
227
  const grgElapsed = Date.now() - grgStart;
228
228
  logger_1.logger.info(`[AutoMint] GRG pipeline completed in ${grgElapsed}ms`);
229
229
  if (grgElapsed > 50 && !this.warnedGrgSlow) {
@@ -0,0 +1,98 @@
1
+ /**
2
+ * TTTClient.httpOnly() — Zero-friction Proof of Time
3
+ * No ETH, no signer, no on-chain. Just verified time.
4
+ *
5
+ * Usage:
6
+ * const client = TTTClient.httpOnly();
7
+ * const pot = await client.generatePoT();
8
+ * console.log(pot.timestamp, pot.confidence);
9
+ *
10
+ * Time sources: NIST, Apple, Google, Cloudflare HTTPS Date headers.
11
+ * HMAC-SHA256 signature — no ethers.js dependency required.
12
+ */
13
+ import { ProofOfTime } from "./types";
14
+ export interface HttpPoT {
15
+ /** Synthesized timestamp in Unix nanoseconds (bigint). */
16
+ timestamp: bigint;
17
+ /** Fraction of configured sources that responded (0.0–1.0). */
18
+ confidence: number;
19
+ /** Lowest NTP stratum observed across sources (2 for HTTPS Date headers). */
20
+ stratum: number;
21
+ /** Number of sources that successfully responded. */
22
+ sources: number;
23
+ /** Per-source readings used to compute the median. */
24
+ sourceReadings: {
25
+ source: string;
26
+ timestamp: bigint;
27
+ uncertainty: number;
28
+ stratum?: number;
29
+ }[];
30
+ /** Replay-protection nonce (hex). */
31
+ nonce: string;
32
+ /** Expiry timestamp in Unix milliseconds (bigint). */
33
+ expiresAt: bigint;
34
+ /** HMAC-SHA256 over canonical fields, hex-encoded. */
35
+ hmac: string;
36
+ }
37
+ export interface HttpPoTVerifyResult {
38
+ valid: boolean;
39
+ reason?: string;
40
+ }
41
+ export interface HttpOnlyClientOptions {
42
+ /**
43
+ * Override HMAC secret for non-sandbox usage.
44
+ * Defaults to a fixed sandbox key — sufficient for local verification only.
45
+ */
46
+ hmacSecret?: string;
47
+ /**
48
+ * Per-source request timeout in ms. Default: 3000.
49
+ */
50
+ timeoutMs?: number;
51
+ /**
52
+ * PoT validity window in seconds. Default: 60.
53
+ */
54
+ expirySeconds?: number;
55
+ /**
56
+ * Maximum divergence allowed between sources in nanoseconds.
57
+ * Default: 2_000_000_000n (2 seconds — lenient for HTTPS Date 1s resolution).
58
+ */
59
+ toleranceNs?: bigint;
60
+ }
61
+ /**
62
+ * HttpOnlyClient — zero-dependency Proof of Time over HTTPS.
63
+ *
64
+ * Fetches time from NIST, Apple, Google, and Cloudflare HTTPS endpoints,
65
+ * computes the median, and returns a PoT with HMAC integrity protection.
66
+ *
67
+ * No ETH, no signer, no on-chain interaction required.
68
+ */
69
+ export declare class HttpOnlyClient {
70
+ private readonly hmacSecret;
71
+ private readonly timeoutMs;
72
+ private readonly expirySeconds;
73
+ private readonly toleranceNs;
74
+ private readonly usedNonces;
75
+ private readonly NONCE_TTL_MS;
76
+ private readonly MAX_NONCE_CACHE;
77
+ constructor(options?: HttpOnlyClientOptions);
78
+ /**
79
+ * Fetch time from all four HTTPS sources, compute median, return PoT.
80
+ * Requires at least 1 source to succeed.
81
+ */
82
+ generatePoT(): Promise<HttpPoT>;
83
+ /**
84
+ * Verify an HttpPoT:
85
+ * - HMAC integrity check
86
+ * - Expiry check
87
+ * - Nonce replay protection
88
+ * - Source divergence tolerance check
89
+ *
90
+ * No on-chain interaction. Pure local verification.
91
+ */
92
+ verifyPoT(pot: HttpPoT): HttpPoTVerifyResult;
93
+ /**
94
+ * Convert HttpPoT to the standard ProofOfTime shape used by the rest of the SDK.
95
+ * The issuerSignature field is omitted (no on-chain signer in httpOnly mode).
96
+ */
97
+ static toProofOfTime(pot: HttpPoT): ProofOfTime;
98
+ }
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ /**
3
+ * TTTClient.httpOnly() — Zero-friction Proof of Time
4
+ * No ETH, no signer, no on-chain. Just verified time.
5
+ *
6
+ * Usage:
7
+ * const client = TTTClient.httpOnly();
8
+ * const pot = await client.generatePoT();
9
+ * console.log(pot.timestamp, pot.confidence);
10
+ *
11
+ * Time sources: NIST, Apple, Google, Cloudflare HTTPS Date headers.
12
+ * HMAC-SHA256 signature — no ethers.js dependency required.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.HttpOnlyClient = void 0;
49
+ const crypto = __importStar(require("crypto"));
50
+ const https = __importStar(require("https"));
51
+ const SOURCES = [
52
+ { name: "nist", url: "https://time.nist.gov/" },
53
+ { name: "apple", url: "https://time.apple.com/" },
54
+ { name: "google", url: "https://time.google.com/" },
55
+ { name: "cloudflare", url: "https://time.cloudflare.com/" },
56
+ ];
57
+ function fetchHttpsDate(name, url, timeoutMs = 3000) {
58
+ return new Promise((resolve, reject) => {
59
+ const t1 = BigInt(Date.now()) * 1000000n;
60
+ const timer = setTimeout(() => {
61
+ req.destroy();
62
+ reject(new Error(`[httpOnly] Timeout: ${name} (${timeoutMs}ms)`));
63
+ }, timeoutMs);
64
+ const req = https.request(url, { method: "HEAD" }, (res) => {
65
+ clearTimeout(timer);
66
+ res.resume(); // drain so socket is freed
67
+ const t4 = BigInt(Date.now()) * 1000000n;
68
+ const dateHeader = res.headers["date"];
69
+ if (!dateHeader) {
70
+ reject(new Error(`[httpOnly] No Date header from ${name}`));
71
+ return;
72
+ }
73
+ const parsed = new Date(dateHeader).getTime();
74
+ if (isNaN(parsed)) {
75
+ reject(new Error(`[httpOnly] Invalid Date header from ${name}: ${dateHeader}`));
76
+ return;
77
+ }
78
+ const serverNs = BigInt(parsed) * 1000000n;
79
+ const rttNs = t4 - t1;
80
+ // Offset-corrected: server time + half RTT
81
+ const corrected = serverNs + rttNs / 2n;
82
+ const rttMs = Number(rttNs) / 1_000_000;
83
+ resolve({
84
+ source: name,
85
+ timestamp: corrected,
86
+ uncertainty: rttMs / 2 + 500, // 500ms base — HTTP Date has 1s resolution
87
+ stratum: 2,
88
+ });
89
+ });
90
+ req.on("error", (err) => {
91
+ clearTimeout(timer);
92
+ reject(new Error(`[httpOnly] Request error for ${name}: ${err.message}`));
93
+ });
94
+ req.end();
95
+ });
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // HMAC helpers — default sandbox key derived from fixed chain+address strings
99
+ // ---------------------------------------------------------------------------
100
+ const SANDBOX_HMAC_SECRET = "openttt-sandbox:chainId=0:address=0x0000000000000000000000000000000000000000";
101
+ function computeHmac(timestamp, nonce, expiresAt, sources, secret = SANDBOX_HMAC_SECRET) {
102
+ // Canonical message: deterministic field concatenation
103
+ const msg = `${timestamp.toString()}:${nonce}:${expiresAt.toString()}:${sources}`;
104
+ return crypto.createHmac("sha256", secret).update(msg).digest("hex");
105
+ }
106
+ /**
107
+ * HttpOnlyClient — zero-dependency Proof of Time over HTTPS.
108
+ *
109
+ * Fetches time from NIST, Apple, Google, and Cloudflare HTTPS endpoints,
110
+ * computes the median, and returns a PoT with HMAC integrity protection.
111
+ *
112
+ * No ETH, no signer, no on-chain interaction required.
113
+ */
114
+ class HttpOnlyClient {
115
+ hmacSecret;
116
+ timeoutMs;
117
+ expirySeconds;
118
+ toleranceNs;
119
+ usedNonces = new Map();
120
+ NONCE_TTL_MS = 300_000; // 5 min
121
+ MAX_NONCE_CACHE = 10_000;
122
+ constructor(options = {}) {
123
+ this.hmacSecret = options.hmacSecret ?? SANDBOX_HMAC_SECRET;
124
+ this.timeoutMs = options.timeoutMs ?? 3000;
125
+ this.expirySeconds = options.expirySeconds ?? 60;
126
+ this.toleranceNs = options.toleranceNs ?? 2000000000n; // 2 s
127
+ }
128
+ /**
129
+ * Fetch time from all four HTTPS sources, compute median, return PoT.
130
+ * Requires at least 1 source to succeed.
131
+ */
132
+ async generatePoT() {
133
+ const results = await Promise.allSettled(SOURCES.map((s) => fetchHttpsDate(s.name, s.url, this.timeoutMs)));
134
+ const readings = [];
135
+ for (const r of results) {
136
+ if (r.status === "fulfilled")
137
+ readings.push(r.value);
138
+ }
139
+ if (readings.length === 0) {
140
+ throw new Error("[httpOnly] All HTTPS time sources failed. Check network connectivity.");
141
+ }
142
+ // Sort by timestamp for median selection
143
+ readings.sort((a, b) => (a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0));
144
+ let finalTs;
145
+ let finalUnc;
146
+ let finalStratum;
147
+ if (readings.length === 1) {
148
+ finalTs = readings[0].timestamp;
149
+ finalUnc = readings[0].uncertainty;
150
+ finalStratum = readings[0].stratum;
151
+ }
152
+ else if (readings.length === 2) {
153
+ finalTs = (readings[0].timestamp + readings[1].timestamp) / 2n;
154
+ finalUnc = (readings[0].uncertainty + readings[1].uncertainty) / 2;
155
+ finalStratum = Math.min(readings[0].stratum, readings[1].stratum);
156
+ }
157
+ else {
158
+ const mid = Math.floor(readings.length / 2);
159
+ finalTs = readings[mid].timestamp;
160
+ finalUnc = readings[mid].uncertainty;
161
+ finalStratum = readings[mid].stratum;
162
+ }
163
+ const nonce = crypto.randomBytes(16).toString("hex");
164
+ const expiresAt = BigInt(Date.now()) + BigInt(this.expirySeconds * 1000);
165
+ const sourceReadings = readings.map((r) => ({
166
+ source: r.source,
167
+ timestamp: r.timestamp,
168
+ uncertainty: r.uncertainty,
169
+ stratum: r.stratum,
170
+ }));
171
+ const hmac = computeHmac(finalTs, nonce, expiresAt, readings.length, this.hmacSecret);
172
+ return {
173
+ timestamp: finalTs,
174
+ confidence: readings.length / SOURCES.length,
175
+ stratum: finalStratum,
176
+ sources: readings.length,
177
+ sourceReadings,
178
+ nonce,
179
+ expiresAt,
180
+ hmac,
181
+ };
182
+ }
183
+ /**
184
+ * Verify an HttpPoT:
185
+ * - HMAC integrity check
186
+ * - Expiry check
187
+ * - Nonce replay protection
188
+ * - Source divergence tolerance check
189
+ *
190
+ * No on-chain interaction. Pure local verification.
191
+ */
192
+ verifyPoT(pot) {
193
+ // 1. Expiry
194
+ if (BigInt(Date.now()) > pot.expiresAt) {
195
+ return { valid: false, reason: "PoT expired" };
196
+ }
197
+ // 2. HMAC integrity
198
+ const expected = computeHmac(pot.timestamp, pot.nonce, pot.expiresAt, pot.sources, this.hmacSecret);
199
+ if (!crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(pot.hmac, "hex"))) {
200
+ return { valid: false, reason: "HMAC mismatch — PoT may have been tampered" };
201
+ }
202
+ // 3. Nonce replay (bounded cache + TTL)
203
+ const now = Date.now();
204
+ if (this.usedNonces.size > this.MAX_NONCE_CACHE / 2) {
205
+ for (const [k, ts] of this.usedNonces) {
206
+ if (now - ts > this.NONCE_TTL_MS)
207
+ this.usedNonces.delete(k);
208
+ }
209
+ }
210
+ if (this.usedNonces.has(pot.nonce)) {
211
+ return { valid: false, reason: "Duplicate nonce — replay detected" };
212
+ }
213
+ if (this.usedNonces.size >= this.MAX_NONCE_CACHE) {
214
+ const oldest = this.usedNonces.keys().next().value;
215
+ if (oldest !== undefined)
216
+ this.usedNonces.delete(oldest);
217
+ }
218
+ this.usedNonces.set(pot.nonce, now);
219
+ // 4. Source divergence
220
+ for (const reading of pot.sourceReadings) {
221
+ const diff = reading.timestamp > pot.timestamp
222
+ ? reading.timestamp - pot.timestamp
223
+ : pot.timestamp - reading.timestamp;
224
+ if (diff > this.toleranceNs) {
225
+ return {
226
+ valid: false,
227
+ reason: `Source ${reading.source} diverges by ${diff}ns (tolerance: ${this.toleranceNs}ns)`,
228
+ };
229
+ }
230
+ }
231
+ return { valid: true };
232
+ }
233
+ /**
234
+ * Convert HttpPoT to the standard ProofOfTime shape used by the rest of the SDK.
235
+ * The issuerSignature field is omitted (no on-chain signer in httpOnly mode).
236
+ */
237
+ static toProofOfTime(pot) {
238
+ return {
239
+ timestamp: pot.timestamp,
240
+ uncertainty: pot.sourceReadings.length > 0
241
+ ? pot.sourceReadings.reduce((sum, r) => sum + r.uncertainty, 0) / pot.sourceReadings.length
242
+ : 500,
243
+ sources: pot.sources,
244
+ stratum: pot.stratum,
245
+ confidence: pot.confidence,
246
+ sourceReadings: pot.sourceReadings,
247
+ nonce: pot.nonce,
248
+ expiresAt: pot.expiresAt,
249
+ };
250
+ }
251
+ }
252
+ exports.HttpOnlyClient = HttpOnlyClient;
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./v4_hook";
9
9
  export * from "./logger";
10
10
  export * from "./types";
11
11
  export * from "./ttt_client";
12
+ export * from "./http_client";
12
13
  export * from "./auto_mint";
13
14
  export * from "./time_synthesis";
14
15
  export * from "./dynamic_fee";
@@ -19,3 +20,4 @@ export * from "./pot_signer";
19
20
  export * from "./ct_log";
20
21
  export * from "./trust_store";
21
22
  export * from "./revenue_tiers";
23
+ export * from "./integrity_client";
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ __exportStar(require("./v4_hook"), exports);
26
26
  __exportStar(require("./logger"), exports);
27
27
  __exportStar(require("./types"), exports);
28
28
  __exportStar(require("./ttt_client"), exports);
29
+ __exportStar(require("./http_client"), exports);
29
30
  __exportStar(require("./auto_mint"), exports);
30
31
  __exportStar(require("./time_synthesis"), exports);
31
32
  __exportStar(require("./dynamic_fee"), exports);
@@ -36,3 +37,4 @@ __exportStar(require("./pot_signer"), exports);
36
37
  __exportStar(require("./ct_log"), exports);
37
38
  __exportStar(require("./trust_store"), exports);
38
39
  __exportStar(require("./revenue_tiers"), exports);
40
+ __exportStar(require("./integrity_client"), exports);
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Integrity Client — replaces local integrity computation with server-side API call.
3
+ * Core pipeline source code stays in Helm private repo. Only API calls go through npm SDK.
4
+ *
5
+ * Drop-in replacement interface for local encode() and verify() operations.
6
+ */
7
+ export interface IntegrityEncodeResult {
8
+ /** Hex-encoded serialized shards (joined as JSON array of hex strings) */
9
+ shards: string[];
10
+ }
11
+ export interface IntegrityVerifyResult {
12
+ valid: boolean;
13
+ }
14
+ export declare class IntegrityClient {
15
+ private baseUrl;
16
+ private timeoutMs;
17
+ constructor(baseUrl?: string, options?: {
18
+ timeoutMs?: number;
19
+ });
20
+ /**
21
+ * Forward pass: encode data through integrity pipeline (server-side).
22
+ *
23
+ * @returns Array of Uint8Array shards
24
+ */
25
+ encode(data: Uint8Array, chainId: number, poolAddress: string): Promise<Uint8Array[]>;
26
+ /**
27
+ * Verify: check data integrity (server-side).
28
+ *
29
+ * @returns boolean — true if data matches the original shards
30
+ */
31
+ verify(data: Uint8Array, originalShards: Uint8Array[], chainId: number, poolAddress: string): Promise<boolean>;
32
+ /**
33
+ * Health check — ping the integrity API server.
34
+ * Returns true if reachable within timeoutMs.
35
+ */
36
+ isReachable(): Promise<boolean>;
37
+ }
38
+ export declare function getDefaultIntegrityClient(): IntegrityClient;
39
+ export declare function setDefaultIntegrityClient(client: IntegrityClient): void;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * Integrity Client — replaces local integrity computation with server-side API call.
4
+ * Core pipeline source code stays in Helm private repo. Only API calls go through npm SDK.
5
+ *
6
+ * Drop-in replacement interface for local encode() and verify() operations.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.IntegrityClient = void 0;
10
+ exports.getDefaultIntegrityClient = getDefaultIntegrityClient;
11
+ exports.setDefaultIntegrityClient = setDefaultIntegrityClient;
12
+ class IntegrityClient {
13
+ baseUrl;
14
+ timeoutMs;
15
+ constructor(baseUrl = "https://integrity.helmprotocol.com/api/v1", options) {
16
+ this.baseUrl = baseUrl.replace(/\/$/, "");
17
+ this.timeoutMs = options?.timeoutMs ?? 5000;
18
+ }
19
+ /**
20
+ * Forward pass: encode data through integrity pipeline (server-side).
21
+ *
22
+ * @returns Array of Uint8Array shards
23
+ */
24
+ async encode(data, chainId, poolAddress) {
25
+ const controller = new AbortController();
26
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
27
+ try {
28
+ const resp = await fetch(`${this.baseUrl}/encode`, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/json" },
31
+ body: JSON.stringify({
32
+ data: Buffer.from(data).toString("hex"),
33
+ chainId: chainId,
34
+ poolAddress,
35
+ }),
36
+ signal: controller.signal,
37
+ });
38
+ if (!resp.ok) {
39
+ throw new Error(`Integrity API error: ${resp.status} ${resp.statusText}`);
40
+ }
41
+ const result = await resp.json();
42
+ return result.shards.map((hex) => Buffer.from(hex, "hex"));
43
+ }
44
+ finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ /**
49
+ * Verify: check data integrity (server-side).
50
+ *
51
+ * @returns boolean — true if data matches the original shards
52
+ */
53
+ async verify(data, originalShards, chainId, poolAddress) {
54
+ const controller = new AbortController();
55
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
56
+ try {
57
+ const resp = await fetch(`${this.baseUrl}/verify`, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({
61
+ data: Buffer.from(data).toString("hex"),
62
+ shards: originalShards.map((s) => Buffer.from(s).toString("hex")),
63
+ chainId: chainId,
64
+ poolAddress,
65
+ }),
66
+ signal: controller.signal,
67
+ });
68
+ if (!resp.ok) {
69
+ throw new Error(`Integrity API error: ${resp.status} ${resp.statusText}`);
70
+ }
71
+ const result = await resp.json();
72
+ return result.valid;
73
+ }
74
+ finally {
75
+ clearTimeout(timer);
76
+ }
77
+ }
78
+ /**
79
+ * Health check — ping the integrity API server.
80
+ * Returns true if reachable within timeoutMs.
81
+ */
82
+ async isReachable() {
83
+ const controller = new AbortController();
84
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
85
+ try {
86
+ const resp = await fetch(`${this.baseUrl}/health`, {
87
+ method: "GET",
88
+ signal: controller.signal,
89
+ });
90
+ return resp.ok;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ finally {
96
+ clearTimeout(timer);
97
+ }
98
+ }
99
+ }
100
+ exports.IntegrityClient = IntegrityClient;
101
+ /**
102
+ * Singleton default client — uses production integrity API endpoint.
103
+ * Can be overridden via setDefaultIntegrityClient() for testing/staging.
104
+ */
105
+ let _defaultClient = null;
106
+ function getDefaultIntegrityClient() {
107
+ if (!_defaultClient) {
108
+ _defaultClient = new IntegrityClient();
109
+ }
110
+ return _defaultClient;
111
+ }
112
+ function setDefaultIntegrityClient(client) {
113
+ _defaultClient = client;
114
+ }