@entros/pulse-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/index.d.mts +665 -0
- package/dist/index.d.ts +665 -0
- package/dist/index.js +3130 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3041 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
declare const FINGERPRINT_BITS = 256;
|
|
2
|
+
declare const DEFAULT_THRESHOLD = 96;
|
|
3
|
+
declare const DEFAULT_MIN_DISTANCE = 3;
|
|
4
|
+
declare const MIN_CAPTURE_MS = 2000;
|
|
5
|
+
declare const MAX_CAPTURE_MS = 60000;
|
|
6
|
+
declare const DEFAULT_CAPTURE_MS = 7000;
|
|
7
|
+
declare const PROGRAM_IDS: {
|
|
8
|
+
readonly entrosAnchor: "GZYwTp2ozeuRA5Gof9vs4ya961aANcJBdUzB7LN6q4b2";
|
|
9
|
+
readonly entrosVerifier: "4F97jNoxQzT2qRbkWpW3ztC3Nz2TtKj3rnKG8ExgnrfV";
|
|
10
|
+
readonly entrosRegistry: "6VBs3zr9KrfFPGd6j7aGBPQWwZa5tajVfA7HN6MMV9VW";
|
|
11
|
+
};
|
|
12
|
+
interface PulseConfig {
|
|
13
|
+
cluster: "devnet" | "mainnet-beta" | "localnet";
|
|
14
|
+
rpcEndpoint?: string;
|
|
15
|
+
relayerUrl?: string;
|
|
16
|
+
relayerApiKey?: string;
|
|
17
|
+
zkeyUrl?: string;
|
|
18
|
+
wasmUrl?: string;
|
|
19
|
+
threshold?: number;
|
|
20
|
+
/** Enable console logging for diagnostics. Default: false. */
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Raw audio samples captured during the Pulse challenge */
|
|
25
|
+
interface AudioCapture {
|
|
26
|
+
samples: Float32Array;
|
|
27
|
+
sampleRate: number;
|
|
28
|
+
duration: number;
|
|
29
|
+
}
|
|
30
|
+
/** Single IMU reading */
|
|
31
|
+
interface MotionSample {
|
|
32
|
+
timestamp: number;
|
|
33
|
+
ax: number;
|
|
34
|
+
ay: number;
|
|
35
|
+
az: number;
|
|
36
|
+
gx: number;
|
|
37
|
+
gy: number;
|
|
38
|
+
gz: number;
|
|
39
|
+
}
|
|
40
|
+
/** Single touch reading */
|
|
41
|
+
interface TouchSample {
|
|
42
|
+
timestamp: number;
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
pressure: number;
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
/** Options for event-driven sensor capture */
|
|
50
|
+
interface CaptureOptions {
|
|
51
|
+
/** AbortSignal to stop capture. If omitted, captures for maxDurationMs. */
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
/** Minimum capture duration in ms. Capture continues until this even if signal fires early. Default: 2000 */
|
|
54
|
+
minDurationMs?: number;
|
|
55
|
+
/** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
|
|
56
|
+
maxDurationMs?: number;
|
|
57
|
+
/** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
|
|
58
|
+
onAudioLevel?: (rms: number) => void;
|
|
59
|
+
/** Pre-acquired MediaStream. If provided, captureAudio skips getUserMedia. */
|
|
60
|
+
stream?: MediaStream;
|
|
61
|
+
/** If true, captureMotion skips requestMotionPermission (already acquired). */
|
|
62
|
+
permissionGranted?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Stage of a capture session */
|
|
65
|
+
type CaptureStage = "audio" | "motion" | "touch";
|
|
66
|
+
/** State of an individual capture stage */
|
|
67
|
+
type StageState = "idle" | "capturing" | "captured" | "skipped";
|
|
68
|
+
/** Combined sensor data from a Pulse capture session */
|
|
69
|
+
interface SensorData {
|
|
70
|
+
audio: AudioCapture | null;
|
|
71
|
+
motion: MotionSample[];
|
|
72
|
+
touch: TouchSample[];
|
|
73
|
+
modalities: {
|
|
74
|
+
audio: boolean;
|
|
75
|
+
motion: boolean;
|
|
76
|
+
touch: boolean;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Result of a verification submission */
|
|
81
|
+
interface SubmissionResult {
|
|
82
|
+
success: boolean;
|
|
83
|
+
txSignature?: string;
|
|
84
|
+
attestationTx?: string;
|
|
85
|
+
error?: string;
|
|
86
|
+
}
|
|
87
|
+
/** Result of a full Pulse verification */
|
|
88
|
+
interface VerificationResult {
|
|
89
|
+
success: boolean;
|
|
90
|
+
commitment: Uint8Array;
|
|
91
|
+
txSignature?: string;
|
|
92
|
+
attestationTx?: string;
|
|
93
|
+
isFirstVerification: boolean;
|
|
94
|
+
error?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type ResolvedConfig = Required<Pick<PulseConfig, "cluster" | "threshold">> & PulseConfig;
|
|
98
|
+
/**
|
|
99
|
+
* PulseSession — event-driven staged capture session.
|
|
100
|
+
*
|
|
101
|
+
* Gives the caller control over when each sensor stage starts and stops.
|
|
102
|
+
* After all stages complete, call complete() to run the processing pipeline.
|
|
103
|
+
*
|
|
104
|
+
* Usage:
|
|
105
|
+
* const session = pulse.createSession(touchElement);
|
|
106
|
+
* await session.startAudio();
|
|
107
|
+
* // ... user speaks ...
|
|
108
|
+
* await session.stopAudio();
|
|
109
|
+
* await session.startMotion();
|
|
110
|
+
* // ... user holds device ...
|
|
111
|
+
* await session.stopMotion();
|
|
112
|
+
* await session.startTouch();
|
|
113
|
+
* // ... user traces curve ...
|
|
114
|
+
* await session.stopTouch();
|
|
115
|
+
* const result = await session.complete(wallet, connection);
|
|
116
|
+
*/
|
|
117
|
+
declare class PulseSession {
|
|
118
|
+
private config;
|
|
119
|
+
private touchElement;
|
|
120
|
+
private audioStageState;
|
|
121
|
+
private motionStageState;
|
|
122
|
+
private touchStageState;
|
|
123
|
+
private audioController;
|
|
124
|
+
private motionController;
|
|
125
|
+
private touchController;
|
|
126
|
+
private audioPromise;
|
|
127
|
+
private motionPromise;
|
|
128
|
+
private touchPromise;
|
|
129
|
+
private audioData;
|
|
130
|
+
private motionData;
|
|
131
|
+
private touchData;
|
|
132
|
+
constructor(config: ResolvedConfig, touchElement?: HTMLElement);
|
|
133
|
+
startAudio(onAudioLevel?: (rms: number) => void): Promise<void>;
|
|
134
|
+
stopAudio(): Promise<AudioCapture | null>;
|
|
135
|
+
startMotion(): Promise<void>;
|
|
136
|
+
stopMotion(): Promise<MotionSample[]>;
|
|
137
|
+
skipMotion(): void;
|
|
138
|
+
isMotionCapturing(): boolean;
|
|
139
|
+
startTouch(): Promise<void>;
|
|
140
|
+
stopTouch(): Promise<TouchSample[]>;
|
|
141
|
+
skipTouch(): void;
|
|
142
|
+
complete(wallet?: any, connection?: any, onProgress?: (stage: string) => void): Promise<VerificationResult>;
|
|
143
|
+
/**
|
|
144
|
+
* Complete the session as a baseline RESET instead of a normal verify.
|
|
145
|
+
*
|
|
146
|
+
* Use when the wallet has an on-chain IdentityState but the device has
|
|
147
|
+
* no recoverable local baseline (cleared site data, new device, etc).
|
|
148
|
+
* Skips the Hamming ZK proof; submits `reset_identity_state` on chain,
|
|
149
|
+
* which rotates the commitment and zeros verification history.
|
|
150
|
+
*
|
|
151
|
+
* Requires a connected wallet + Solana connection. Rejects if either
|
|
152
|
+
* is missing — reset is a wallet-mode-only operation since it writes
|
|
153
|
+
* to the user's on-chain account.
|
|
154
|
+
*/
|
|
155
|
+
completeReset(wallet: any, connection: any, onProgress?: (stage: string) => void): Promise<VerificationResult>;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* PulseSDK — main entry point for Entros Protocol verification.
|
|
159
|
+
*
|
|
160
|
+
* Two usage modes:
|
|
161
|
+
* 1. Simple (backward-compatible): pulse.verify(touchElement) — captures all sensors
|
|
162
|
+
* for DEFAULT_CAPTURE_MS in parallel, then processes.
|
|
163
|
+
* 2. Staged (event-driven): pulse.createSession(touchElement) — caller controls
|
|
164
|
+
* when each sensor stage starts and stops.
|
|
165
|
+
*/
|
|
166
|
+
declare class PulseSDK {
|
|
167
|
+
private config;
|
|
168
|
+
constructor(config: PulseConfig);
|
|
169
|
+
/**
|
|
170
|
+
* Create a staged capture session for event-driven control.
|
|
171
|
+
*/
|
|
172
|
+
createSession(touchElement?: HTMLElement): PulseSession;
|
|
173
|
+
/**
|
|
174
|
+
* Run a full verification with automatic timed capture (backward-compatible).
|
|
175
|
+
* Captures all sensors in parallel for DEFAULT_CAPTURE_MS, then processes.
|
|
176
|
+
*/
|
|
177
|
+
verify(touchElement?: HTMLElement, wallet?: any, connection?: any): Promise<VerificationResult>;
|
|
178
|
+
/**
|
|
179
|
+
* Reset the wallet's on-chain baseline using a fresh capture.
|
|
180
|
+
*
|
|
181
|
+
* Convenience wrapper that mirrors `verify()` but routes the captured
|
|
182
|
+
* sensor data through `reset_identity_state` instead of `update_anchor`.
|
|
183
|
+
* Use when the wallet has an on-chain IdentityState but the local
|
|
184
|
+
* encrypted baseline is unrecoverable.
|
|
185
|
+
*
|
|
186
|
+
* For fine-grained control, call `createSession()` and `completeReset()`
|
|
187
|
+
* directly — the session API exposes per-stage start/stop hooks that
|
|
188
|
+
* this convenience wrapper trades away for simplicity.
|
|
189
|
+
*/
|
|
190
|
+
resetBaseline(touchElement: HTMLElement | undefined, wallet: any, connection: any, onProgress?: (stage: string) => void): Promise<VerificationResult>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** 256-bit Temporal Fingerprint as an array of 0/1 values */
|
|
194
|
+
type TemporalFingerprint = number[];
|
|
195
|
+
/** Temporal-Biometric Hash: commitment + data needed for re-verification */
|
|
196
|
+
interface TBH {
|
|
197
|
+
fingerprint: TemporalFingerprint;
|
|
198
|
+
salt: bigint;
|
|
199
|
+
commitment: bigint;
|
|
200
|
+
commitmentBytes: Uint8Array;
|
|
201
|
+
}
|
|
202
|
+
/** Packed field elements from bit packing (2 × 128-bit) */
|
|
203
|
+
interface PackedFingerprint {
|
|
204
|
+
lo: bigint;
|
|
205
|
+
hi: bigint;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
declare function simhash(features: number[]): TemporalFingerprint;
|
|
209
|
+
/**
|
|
210
|
+
* Compute Hamming distance between two fingerprints.
|
|
211
|
+
*/
|
|
212
|
+
declare function hammingDistance(a: TemporalFingerprint, b: TemporalFingerprint): number;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Pack 256-bit fingerprint into two 128-bit field elements.
|
|
216
|
+
* Little-endian bit ordering within each chunk (matches circuit's Bits2Num).
|
|
217
|
+
*/
|
|
218
|
+
declare function packBits(fingerprint: TemporalFingerprint): PackedFingerprint;
|
|
219
|
+
/**
|
|
220
|
+
* Compute Poseidon commitment: Poseidon(pack_lo, pack_hi, salt).
|
|
221
|
+
* Matches the circuit's CommitmentCheck template exactly.
|
|
222
|
+
*/
|
|
223
|
+
declare function computeCommitment(fingerprint: TemporalFingerprint, salt: bigint): Promise<bigint>;
|
|
224
|
+
/**
|
|
225
|
+
* Generate a random salt within the BN254 scalar field.
|
|
226
|
+
*/
|
|
227
|
+
declare function generateSalt(): bigint;
|
|
228
|
+
/**
|
|
229
|
+
* Convert a BigInt to a 32-byte big-endian Uint8Array.
|
|
230
|
+
*/
|
|
231
|
+
declare function bigintToBytes32(n: bigint): Uint8Array;
|
|
232
|
+
/**
|
|
233
|
+
* Generate a complete TBH from a fingerprint.
|
|
234
|
+
*/
|
|
235
|
+
declare function generateTBH(fingerprint: TemporalFingerprint, salt?: bigint): Promise<TBH>;
|
|
236
|
+
|
|
237
|
+
/** Statistical summary of a time series */
|
|
238
|
+
interface StatsSummary {
|
|
239
|
+
mean: number;
|
|
240
|
+
variance: number;
|
|
241
|
+
skewness: number;
|
|
242
|
+
kurtosis: number;
|
|
243
|
+
}
|
|
244
|
+
/** Feature vector from all sensor modalities */
|
|
245
|
+
interface FeatureVector {
|
|
246
|
+
audio: number[];
|
|
247
|
+
motion: number[];
|
|
248
|
+
touch: number[];
|
|
249
|
+
}
|
|
250
|
+
/** Concatenated feature vector for SimHash input */
|
|
251
|
+
type FusedFeatureVector = number[];
|
|
252
|
+
|
|
253
|
+
declare function mean(values: number[]): number;
|
|
254
|
+
declare function variance(values: number[], mu?: number): number;
|
|
255
|
+
declare function skewness(values: number[]): number;
|
|
256
|
+
declare function kurtosis(values: number[]): number;
|
|
257
|
+
declare function condense(values: number[]): StatsSummary;
|
|
258
|
+
/**
|
|
259
|
+
* Shannon entropy over histogram bins. Measures information density.
|
|
260
|
+
* Real human data has moderate entropy (varied but structured).
|
|
261
|
+
* Synthetic data is either too uniform (high entropy) or too structured (low entropy).
|
|
262
|
+
*/
|
|
263
|
+
declare function entropy(values: number[], bins?: number): number;
|
|
264
|
+
/**
|
|
265
|
+
* Autocorrelation at a given lag. Detects periodic synthetic patterns.
|
|
266
|
+
* Real human data has low autocorrelation at most lags (chaotic/noisy).
|
|
267
|
+
* Synthetic data often has high autocorrelation (periodic/smooth).
|
|
268
|
+
*/
|
|
269
|
+
declare function autocorrelation(values: number[], lag?: number): number;
|
|
270
|
+
/**
|
|
271
|
+
* Concatenate raw features without normalization.
|
|
272
|
+
* Used for server-side validation where physical units matter.
|
|
273
|
+
*/
|
|
274
|
+
declare function fuseRawFeatures(audio: number[], motion: number[], touch: number[]): number[];
|
|
275
|
+
/**
|
|
276
|
+
* Normalize and concatenate features for SimHash computation.
|
|
277
|
+
*/
|
|
278
|
+
declare function fuseFeatures(audio: number[], motion: number[], touch: number[]): number[];
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Speaker-dependent audio feature extraction.
|
|
282
|
+
*
|
|
283
|
+
* Extracts features that characterize HOW someone speaks (prosody, vocal physiology)
|
|
284
|
+
* rather than WHAT they say (phonetic content). These features are stable across
|
|
285
|
+
* different utterances from the same speaker.
|
|
286
|
+
*
|
|
287
|
+
* Output: 44 values
|
|
288
|
+
* F0 statistics (5) + F0 delta (4) + jitter (4) + shimmer (4) +
|
|
289
|
+
* HNR statistics (5) + formant ratios (8) + LTAS (8) + voicing ratio (1) +
|
|
290
|
+
* amplitude statistics (5)
|
|
291
|
+
*/
|
|
292
|
+
|
|
293
|
+
declare const SPEAKER_FEATURE_COUNT = 44;
|
|
294
|
+
/**
|
|
295
|
+
* Extract speaker-dependent audio features.
|
|
296
|
+
*
|
|
297
|
+
* Captures physiological vocal characteristics (F0, jitter, shimmer, HNR, formant
|
|
298
|
+
* ratios) that are stable across different utterances from the same speaker.
|
|
299
|
+
* Content-independent by design — different phrases produce similar feature values.
|
|
300
|
+
*
|
|
301
|
+
* Returns 44 values.
|
|
302
|
+
*/
|
|
303
|
+
/**
|
|
304
|
+
* Extracts 44 speaker features AND the raw F0 contour.
|
|
305
|
+
* The F0 contour is surfaced so Tier 2 cross-modal temporal analysis can be
|
|
306
|
+
* performed server-side against the motion time-series. Feature vector shape
|
|
307
|
+
* and semantics are unchanged.
|
|
308
|
+
*/
|
|
309
|
+
declare function extractSpeakerFeaturesDetailed(audio: AudioCapture): Promise<{
|
|
310
|
+
features: number[];
|
|
311
|
+
f0Contour: number[];
|
|
312
|
+
}>;
|
|
313
|
+
/**
|
|
314
|
+
* Extracts 44 speaker features. Backward-compatible wrapper that discards
|
|
315
|
+
* the F0 contour; use `extractSpeakerFeaturesDetailed` when the contour is
|
|
316
|
+
* needed (e.g. for Tier 2 server-side cross-modal analysis).
|
|
317
|
+
*/
|
|
318
|
+
declare function extractSpeakerFeatures(audio: AudioCapture): Promise<number[]>;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Compute per-sample acceleration magnitude |a| = √(ax² + ay² + az²) and
|
|
322
|
+
* linearly resample to a target frame count. Used for Tier 2 cross-modal
|
|
323
|
+
* temporal analysis against the F0 contour; the two time-series must share
|
|
324
|
+
* the same frame count for direct correlation.
|
|
325
|
+
*
|
|
326
|
+
* Returns an empty array if motion data is absent or too short.
|
|
327
|
+
*/
|
|
328
|
+
declare function extractAccelerationMagnitude(samples: MotionSample[], targetFrameCount: number): number[];
|
|
329
|
+
/**
|
|
330
|
+
* Extract kinematic features from motion (IMU) data.
|
|
331
|
+
* Computes jerk (3rd derivative) and jounce (4th derivative) of acceleration,
|
|
332
|
+
* then condenses each axis into statistics.
|
|
333
|
+
*
|
|
334
|
+
* Returns: ~54 values (6 axes × 2 derivatives × 4 stats + 6 jitter variance values)
|
|
335
|
+
*/
|
|
336
|
+
declare function extractMotionFeatures(samples: MotionSample[]): number[];
|
|
337
|
+
/**
|
|
338
|
+
* Extract kinematic features from touch data.
|
|
339
|
+
* Computes velocity and acceleration of touch coordinates,
|
|
340
|
+
* plus pressure and area statistics.
|
|
341
|
+
*
|
|
342
|
+
* Returns: ~36 values (32 base + 4 jitter variance for x, y, pressure, area)
|
|
343
|
+
*/
|
|
344
|
+
declare function extractTouchFeatures(samples: TouchSample[]): number[];
|
|
345
|
+
/**
|
|
346
|
+
* Extract mouse dynamics features as a desktop replacement for motion sensor data.
|
|
347
|
+
* Captures behavioral patterns from mouse/pointer movement that are user-specific:
|
|
348
|
+
* path curvature, speed patterns, micro-corrections, pause behavior.
|
|
349
|
+
*
|
|
350
|
+
* Returns: 54 values (matches motion feature dimension for consistent SimHash input)
|
|
351
|
+
*/
|
|
352
|
+
declare function extractMouseDynamics(samples: TouchSample[]): number[];
|
|
353
|
+
|
|
354
|
+
/** Serialized proof ready for on-chain submission */
|
|
355
|
+
interface SolanaProof {
|
|
356
|
+
proofBytes: Uint8Array;
|
|
357
|
+
publicInputs: Uint8Array[];
|
|
358
|
+
}
|
|
359
|
+
/** Raw snarkjs proof output */
|
|
360
|
+
interface RawProof {
|
|
361
|
+
pi_a: string[];
|
|
362
|
+
pi_b: string[][];
|
|
363
|
+
pi_c: string[];
|
|
364
|
+
protocol: string;
|
|
365
|
+
curve: string;
|
|
366
|
+
}
|
|
367
|
+
/** Circuit input for proof generation */
|
|
368
|
+
interface CircuitInput {
|
|
369
|
+
ft_new: number[];
|
|
370
|
+
ft_prev: number[];
|
|
371
|
+
salt_new: string;
|
|
372
|
+
salt_prev: string;
|
|
373
|
+
commitment_new: string;
|
|
374
|
+
commitment_prev: string;
|
|
375
|
+
threshold: string;
|
|
376
|
+
min_distance: string;
|
|
377
|
+
}
|
|
378
|
+
/** Proof generation result */
|
|
379
|
+
interface ProofResult {
|
|
380
|
+
proof: RawProof;
|
|
381
|
+
publicSignals: string[];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Convert a decimal string to a 32-byte big-endian Uint8Array.
|
|
386
|
+
*/
|
|
387
|
+
declare function toBigEndian32(decStr: string): Uint8Array;
|
|
388
|
+
/**
|
|
389
|
+
* Serialize an snarkjs proof into the 256-byte format groth16-solana expects.
|
|
390
|
+
*
|
|
391
|
+
* proof_a: 64 bytes (x + negated y)
|
|
392
|
+
* proof_b: 128 bytes (G2 with reversed coordinate ordering: c1 before c0)
|
|
393
|
+
* proof_c: 64 bytes (x + y)
|
|
394
|
+
*/
|
|
395
|
+
declare function serializeProof(proof: RawProof, publicSignals: string[]): SolanaProof;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Prepare circuit input from current and previous TBH data.
|
|
399
|
+
*/
|
|
400
|
+
declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number, minDistance?: number): CircuitInput;
|
|
401
|
+
/**
|
|
402
|
+
* Generate a Groth16 proof for the Hamming distance circuit.
|
|
403
|
+
*
|
|
404
|
+
* @param input - Circuit input (fingerprints, salts, commitments, threshold)
|
|
405
|
+
* @param wasmPath - Path or URL to iam_hamming.wasm
|
|
406
|
+
* @param zkeyPath - Path or URL to iam_hamming_final.zkey
|
|
407
|
+
*/
|
|
408
|
+
declare function generateProof(input: CircuitInput, wasmPath: string, zkeyPath: string): Promise<ProofResult>;
|
|
409
|
+
/**
|
|
410
|
+
* Generate a proof and serialize it for Solana submission.
|
|
411
|
+
*/
|
|
412
|
+
declare function generateSolanaProof(current: TBH, previous: TBH, wasmPath: string, zkeyPath: string, threshold?: number): Promise<SolanaProof>;
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Submit a proof on-chain via a connected wallet (wallet-connected mode).
|
|
416
|
+
* Uses Anchor SDK to construct and send the transaction.
|
|
417
|
+
*
|
|
418
|
+
* Flow for re-verification: single batched transaction containing
|
|
419
|
+
* ComputeBudget → create_challenge → verify_proof → update_anchor
|
|
420
|
+
* Flow for first verification: mint_anchor (already 1 transaction)
|
|
421
|
+
*/
|
|
422
|
+
declare function submitViaWallet(proof: SolanaProof, commitment: Uint8Array, options: {
|
|
423
|
+
wallet: any;
|
|
424
|
+
connection: any;
|
|
425
|
+
isFirstVerification: boolean;
|
|
426
|
+
relayerUrl?: string;
|
|
427
|
+
relayerApiKey?: string;
|
|
428
|
+
}): Promise<SubmissionResult>;
|
|
429
|
+
/**
|
|
430
|
+
* Submit a baseline reset on-chain via a connected wallet.
|
|
431
|
+
*
|
|
432
|
+
* Fires when the on-chain IdentityState exists for the wallet but the
|
|
433
|
+
* device's local encrypted fingerprint envelope is unrecoverable. The
|
|
434
|
+
* ZK Hamming proof used by `update_anchor` needs the previous
|
|
435
|
+
* fingerprint's bits as a private witness; without them, re-verification
|
|
436
|
+
* is blocked. `reset_identity_state` rotates `current_commitment`
|
|
437
|
+
* in place, zeroes verification_count / trust_score / recent_timestamps,
|
|
438
|
+
* and sets a 7-day cooldown before the next reset.
|
|
439
|
+
*
|
|
440
|
+
* Transaction shape: single instruction (no challenge / verify_proof /
|
|
441
|
+
* ZK proof required). Humanness evidence comes from the Tier 1
|
|
442
|
+
* validation pipeline invoked at the /attest step (same as mint and
|
|
443
|
+
* update).
|
|
444
|
+
*/
|
|
445
|
+
declare function submitResetViaWallet(commitment: Uint8Array, options: {
|
|
446
|
+
wallet: any;
|
|
447
|
+
connection: any;
|
|
448
|
+
relayerUrl?: string;
|
|
449
|
+
relayerApiKey?: string;
|
|
450
|
+
}): Promise<SubmissionResult>;
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Submit a proof via the Entros relayer API (walletless mode).
|
|
454
|
+
* The relayer submits the on-chain transaction using the integrator's funded account.
|
|
455
|
+
* The user needs no wallet, no SOL, no crypto knowledge.
|
|
456
|
+
*/
|
|
457
|
+
declare function submitViaRelayer(proof: SolanaProof, commitment: Uint8Array, options: {
|
|
458
|
+
relayerUrl: string;
|
|
459
|
+
apiKey?: string;
|
|
460
|
+
isFirstVerification: boolean;
|
|
461
|
+
}): Promise<SubmissionResult>;
|
|
462
|
+
|
|
463
|
+
/** Decoded Entros attestation from the Solana Attestation Service */
|
|
464
|
+
interface EntrosAttestation {
|
|
465
|
+
isHuman: boolean;
|
|
466
|
+
trustScore: number;
|
|
467
|
+
verifiedAt: number;
|
|
468
|
+
mode: string;
|
|
469
|
+
expired: boolean;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Check if a wallet has a valid Entros attestation via SAS.
|
|
473
|
+
*
|
|
474
|
+
* Derives the attestation PDA, fetches the account, deserializes
|
|
475
|
+
* the attestation data, and checks expiry.
|
|
476
|
+
*
|
|
477
|
+
* @param walletAddress - Base58 Solana wallet address
|
|
478
|
+
* @param connection - Solana web3.js Connection instance
|
|
479
|
+
* @returns Decoded attestation or null if none exists
|
|
480
|
+
*/
|
|
481
|
+
declare function verifyEntrosAttestation(walletAddress: string, connection: any): Promise<EntrosAttestation | null>;
|
|
482
|
+
|
|
483
|
+
/** Metadata written to an AI agent linking it to a verified human operator */
|
|
484
|
+
interface AgentHumanOperator {
|
|
485
|
+
anchorPda: string;
|
|
486
|
+
trustScore: number;
|
|
487
|
+
verifiedAt: number;
|
|
488
|
+
wallet: string;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Attest that a verified Entros human operates an AI agent on the Solana Agent Registry.
|
|
492
|
+
*
|
|
493
|
+
* Reads the user's on-chain IdentityState PDA, builds metadata JSON, and writes
|
|
494
|
+
* it to the agent's metadata via a manually constructed set_metadata_pda instruction.
|
|
495
|
+
* The metadata is immutable once set, permanently linking the agent to its human operator.
|
|
496
|
+
*
|
|
497
|
+
* The wallet must own both the Entros Anchor and the agent's Metaplex Core NFT.
|
|
498
|
+
*
|
|
499
|
+
* @param agentAsset - Base58 pubkey of the agent's Metaplex Core NFT
|
|
500
|
+
* @param options - Wallet adapter and Solana connection
|
|
501
|
+
* @returns Transaction signature on success
|
|
502
|
+
*/
|
|
503
|
+
declare function attestAgentOperator(agentAsset: string, options: {
|
|
504
|
+
wallet: any;
|
|
505
|
+
connection: any;
|
|
506
|
+
cluster?: PulseConfig["cluster"];
|
|
507
|
+
}): Promise<{
|
|
508
|
+
success: boolean;
|
|
509
|
+
signature?: string;
|
|
510
|
+
error?: string;
|
|
511
|
+
}>;
|
|
512
|
+
/**
|
|
513
|
+
* Query whether an AI agent has a verified human operator via Entros.
|
|
514
|
+
*
|
|
515
|
+
* Reads the "iam:human-operator" metadata from the agent's on-chain record
|
|
516
|
+
* and returns the operator's Entros Anchor details.
|
|
517
|
+
*
|
|
518
|
+
* @param agentAsset - Base58 pubkey of the agent's Metaplex Core NFT
|
|
519
|
+
* @param connection - Solana connection (optional, defaults to devnet)
|
|
520
|
+
* @returns Operator metadata or null if no Entros attestation exists
|
|
521
|
+
*/
|
|
522
|
+
declare function getAgentHumanOperator(agentAsset: string, connection?: any, cluster?: PulseConfig["cluster"]): Promise<AgentHumanOperator | null>;
|
|
523
|
+
|
|
524
|
+
/** On-chain identity state (mirrors the Anchor program's IdentityState) */
|
|
525
|
+
interface IdentityState {
|
|
526
|
+
owner: string;
|
|
527
|
+
creationTimestamp: number;
|
|
528
|
+
lastVerificationTimestamp: number;
|
|
529
|
+
verificationCount: number;
|
|
530
|
+
trustScore: number;
|
|
531
|
+
currentCommitment: Uint8Array;
|
|
532
|
+
mint: string;
|
|
533
|
+
/**
|
|
534
|
+
* Unix timestamp of the most recent `reset_identity_state` call.
|
|
535
|
+
* Zero for accounts that have never been reset (freshly minted or
|
|
536
|
+
* minted before the reset feature was deployed).
|
|
537
|
+
*/
|
|
538
|
+
lastResetTimestamp: number;
|
|
539
|
+
}
|
|
540
|
+
/** Local storage of previous verification data (needed for re-verification) */
|
|
541
|
+
interface StoredVerificationData {
|
|
542
|
+
fingerprint: number[];
|
|
543
|
+
salt: string;
|
|
544
|
+
commitment: string;
|
|
545
|
+
timestamp: number;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Fetch identity state from the on-chain IdentityState PDA.
|
|
550
|
+
*/
|
|
551
|
+
declare function fetchIdentityState(walletPubkey: string, connection: any): Promise<IdentityState | null>;
|
|
552
|
+
/**
|
|
553
|
+
* Store verification data locally for re-verification.
|
|
554
|
+
* Encrypts with AES-256-GCM when Web Crypto is available.
|
|
555
|
+
* Falls back to plaintext with a warning otherwise.
|
|
556
|
+
*/
|
|
557
|
+
declare function storeVerificationData(data: StoredVerificationData): Promise<void>;
|
|
558
|
+
/**
|
|
559
|
+
* Load previously stored verification data.
|
|
560
|
+
* Decrypts if encrypted, migrates plaintext to encrypted on first load.
|
|
561
|
+
*/
|
|
562
|
+
declare function loadVerificationData(): Promise<StoredVerificationData | null>;
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Generate a random phonetically-balanced phrase for the voice challenge.
|
|
566
|
+
* Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
|
|
567
|
+
* Uses crypto.getRandomValues for unpredictable challenge generation.
|
|
568
|
+
*/
|
|
569
|
+
declare function generatePhrase(wordCount?: number): string;
|
|
570
|
+
/**
|
|
571
|
+
* Generate a sequence of phrases for dynamic mid-session switching.
|
|
572
|
+
* Each phrase uses a different syllable subset to prevent pre-computation.
|
|
573
|
+
*/
|
|
574
|
+
declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Generate Lissajous curve points for the touch tracing challenge.
|
|
578
|
+
* The user traces this shape on screen while speaking the phrase.
|
|
579
|
+
*
|
|
580
|
+
* x(t) = A * sin(a*t + delta)
|
|
581
|
+
* y(t) = B * sin(b*t)
|
|
582
|
+
*/
|
|
583
|
+
interface LissajousParams {
|
|
584
|
+
a: number;
|
|
585
|
+
b: number;
|
|
586
|
+
delta: number;
|
|
587
|
+
points: number;
|
|
588
|
+
}
|
|
589
|
+
interface Point2D {
|
|
590
|
+
x: number;
|
|
591
|
+
y: number;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Generate random Lissajous parameters for a challenge.
|
|
595
|
+
*/
|
|
596
|
+
declare function randomLissajousParams(): LissajousParams;
|
|
597
|
+
/**
|
|
598
|
+
* Generate Lissajous curve points normalized to [0, 1] range.
|
|
599
|
+
*/
|
|
600
|
+
declare function generateLissajousPoints(params: LissajousParams): Point2D[];
|
|
601
|
+
/**
|
|
602
|
+
* Generate a sequence of Lissajous curves for dynamic mid-session switching.
|
|
603
|
+
* Each curve uses different parameters, preventing pre-computation.
|
|
604
|
+
*/
|
|
605
|
+
declare function generateLissajousSequence(count?: number): {
|
|
606
|
+
params: LissajousParams;
|
|
607
|
+
points: Point2D[];
|
|
608
|
+
}[];
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Fetch the server-issued challenge from the executor.
|
|
612
|
+
*
|
|
613
|
+
* The executor's `/challenge` endpoint returns a fresh nonce + nonsense phrase
|
|
614
|
+
* bound to the wallet for a short TTL (default 60s). The phrase is shown to
|
|
615
|
+
* the user as the voice challenge and is looked up server-side at
|
|
616
|
+
* `/validate-features` to run phoneme-distance matching against the submitted
|
|
617
|
+
* audio (master-list #89, phrase content binding via STT).
|
|
618
|
+
*
|
|
619
|
+
* Server-issued phrases are the only safe design for content binding: if the
|
|
620
|
+
* client generated the phrase and sent it to the server alongside the audio,
|
|
621
|
+
* an attacker would submit their own phrase matching whatever content they
|
|
622
|
+
* captured. With server issuance, the phrase is bound to the nonce and the
|
|
623
|
+
* client cannot substitute it.
|
|
624
|
+
*/
|
|
625
|
+
/**
|
|
626
|
+
* Server-issued challenge artifacts. Returned by `fetchChallenge`.
|
|
627
|
+
*/
|
|
628
|
+
interface ChallengeResponse {
|
|
629
|
+
/** 32-byte nonce used for on-chain `create_challenge` and the `/attest` handshake. */
|
|
630
|
+
nonce: Uint8Array;
|
|
631
|
+
/** Nonsense phrase (5 space-separated words of 2-3 syllables each) the user must speak aloud. */
|
|
632
|
+
phrase: string;
|
|
633
|
+
/** Nonce TTL in seconds (default 60). */
|
|
634
|
+
expiresIn: number;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Fetch a fresh nonce + phrase from the executor. Throws on network error or
|
|
638
|
+
* non-2xx response so the caller can surface a retry UX.
|
|
639
|
+
*
|
|
640
|
+
* @param executorUrl - Base URL of the executor (e.g. `https://executor.entros.io`).
|
|
641
|
+
* @param walletAddress - Base58-encoded wallet public key.
|
|
642
|
+
* @param apiKey - Optional executor API key (`X-API-Key` header).
|
|
643
|
+
*/
|
|
644
|
+
declare function fetchChallenge(executorUrl: string, walletAddress: string, apiKey?: string): Promise<ChallengeResponse>;
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Encode captured Float32 audio samples as base64 int16 PCM for transmission
|
|
648
|
+
* to the validation service (master-list #89 phrase content binding).
|
|
649
|
+
*
|
|
650
|
+
* Audio is captured as `Float32Array` with values in `[-1.0, 1.0]` by the
|
|
651
|
+
* Pulse SDK (`sensor/audio.ts`). The validation service's phrase-binding
|
|
652
|
+
* module decodes base64 → Vec<i16> → Vec<f32> before feeding Whisper-tiny.
|
|
653
|
+
* int16 is the standard compact representation: 2 bytes per sample vs 4 for
|
|
654
|
+
* f32, halving wire size without perceptible quality loss for 16kHz speech.
|
|
655
|
+
*
|
|
656
|
+
* Byte layout: little-endian int16 samples, contiguous, no header.
|
|
657
|
+
*/
|
|
658
|
+
/**
|
|
659
|
+
* Convert Float32 PCM samples to base64-encoded 16-bit little-endian PCM.
|
|
660
|
+
* Samples are clamped to [-1, 1] and scaled. Uses `btoa`, a DOM global
|
|
661
|
+
* available in browser runtimes and in Node 16+.
|
|
662
|
+
*/
|
|
663
|
+
declare function encodeAudioAsBase64(samples: Float32Array): string;
|
|
664
|
+
|
|
665
|
+
export { type AgentHumanOperator, type AudioCapture, type CaptureOptions, type CaptureStage, type ChallengeResponse, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, type EntrosAttestation, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, SPEAKER_FEATURE_COUNT, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, attestAgentOperator, autocorrelation, bigintToBytes32, computeCommitment, condense, encodeAudioAsBase64, entropy, extractAccelerationMagnitude, extractMotionFeatures, extractMouseDynamics, extractSpeakerFeatures, extractSpeakerFeaturesDetailed, extractTouchFeatures, fetchChallenge, fetchIdentityState, fuseFeatures, fuseRawFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, getAgentHumanOperator, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitResetViaWallet, submitViaRelayer, submitViaWallet, toBigEndian32, variance, verifyEntrosAttestation };
|