@iam-protocol/pulse-sdk 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @iam-protocol/pulse-sdk
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@iam-protocol/pulse-sdk.svg)](https://www.npmjs.com/package/@iam-protocol/pulse-sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@iam-protocol/pulse-sdk.svg)](https://www.npmjs.com/package/@iam-protocol/pulse-sdk)
5
+
3
6
  Client-side SDK for the IAM Protocol. Captures behavioral biometrics (voice, motion, touch), generates a Temporal-Biometric Hash, produces a Groth16 zero-knowledge proof, and submits it for on-chain verification on Solana.
4
7
 
5
8
  ## Install
package/dist/index.d.mts CHANGED
@@ -51,6 +51,8 @@ interface CaptureOptions {
51
51
  minDurationMs?: number;
52
52
  /** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
53
53
  maxDurationMs?: number;
54
+ /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
55
+ onAudioLevel?: (rms: number) => void;
54
56
  }
55
57
  /** Stage of a capture session */
56
58
  type CaptureStage = "audio" | "motion" | "touch";
@@ -119,7 +121,7 @@ declare class PulseSession {
119
121
  private motionData;
120
122
  private touchData;
121
123
  constructor(config: ResolvedConfig, touchElement?: HTMLElement);
122
- startAudio(): Promise<void>;
124
+ startAudio(onAudioLevel?: (rms: number) => void): Promise<void>;
123
125
  stopAudio(): Promise<AudioCapture | null>;
124
126
  skipAudio(): void;
125
127
  startMotion(): Promise<void>;
@@ -360,6 +362,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
360
362
  * Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
361
363
  */
362
364
  declare function generatePhrase(wordCount?: number): string;
365
+ /**
366
+ * Generate a sequence of phrases for dynamic mid-session switching.
367
+ * Each phrase uses a different syllable subset to prevent pre-computation.
368
+ */
369
+ declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
363
370
 
364
371
  /**
365
372
  * Generate Lissajous curve points for the touch tracing challenge.
@@ -386,5 +393,13 @@ declare function randomLissajousParams(): LissajousParams;
386
393
  * Generate Lissajous curve points normalized to [0, 1] range.
387
394
  */
388
395
  declare function generateLissajousPoints(params: LissajousParams): Point2D[];
396
+ /**
397
+ * Generate a sequence of Lissajous curves for dynamic mid-session switching.
398
+ * Each curve uses different parameters, preventing pre-computation.
399
+ */
400
+ declare function generateLissajousSequence(count?: number): {
401
+ params: LissajousParams;
402
+ points: Point2D[];
403
+ }[];
389
404
 
390
- export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, 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, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
405
+ export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, 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, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
package/dist/index.d.ts CHANGED
@@ -51,6 +51,8 @@ interface CaptureOptions {
51
51
  minDurationMs?: number;
52
52
  /** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
53
53
  maxDurationMs?: number;
54
+ /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
55
+ onAudioLevel?: (rms: number) => void;
54
56
  }
55
57
  /** Stage of a capture session */
56
58
  type CaptureStage = "audio" | "motion" | "touch";
@@ -119,7 +121,7 @@ declare class PulseSession {
119
121
  private motionData;
120
122
  private touchData;
121
123
  constructor(config: ResolvedConfig, touchElement?: HTMLElement);
122
- startAudio(): Promise<void>;
124
+ startAudio(onAudioLevel?: (rms: number) => void): Promise<void>;
123
125
  stopAudio(): Promise<AudioCapture | null>;
124
126
  skipAudio(): void;
125
127
  startMotion(): Promise<void>;
@@ -360,6 +362,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
360
362
  * Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
361
363
  */
362
364
  declare function generatePhrase(wordCount?: number): string;
365
+ /**
366
+ * Generate a sequence of phrases for dynamic mid-session switching.
367
+ * Each phrase uses a different syllable subset to prevent pre-computation.
368
+ */
369
+ declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
363
370
 
364
371
  /**
365
372
  * Generate Lissajous curve points for the touch tracing challenge.
@@ -386,5 +393,13 @@ declare function randomLissajousParams(): LissajousParams;
386
393
  * Generate Lissajous curve points normalized to [0, 1] range.
387
394
  */
388
395
  declare function generateLissajousPoints(params: LissajousParams): Point2D[];
396
+ /**
397
+ * Generate a sequence of Lissajous curves for dynamic mid-session switching.
398
+ * Each curve uses different parameters, preventing pre-computation.
399
+ */
400
+ declare function generateLissajousSequence(count?: number): {
401
+ params: LissajousParams;
402
+ points: Point2D[];
403
+ }[];
389
404
 
390
- export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, 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, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
405
+ export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, 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, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
package/dist/index.js CHANGED
@@ -12994,7 +12994,9 @@ __export(index_exports, {
12994
12994
  fetchIdentityState: () => fetchIdentityState,
12995
12995
  fuseFeatures: () => fuseFeatures,
12996
12996
  generateLissajousPoints: () => generateLissajousPoints,
12997
+ generateLissajousSequence: () => generateLissajousSequence,
12997
12998
  generatePhrase: () => generatePhrase,
12999
+ generatePhraseSequence: () => generatePhraseSequence,
12998
13000
  generateProof: () => generateProof,
12999
13001
  generateSalt: () => generateSalt,
13000
13002
  generateSolanaProof: () => generateSolanaProof,
@@ -13047,7 +13049,8 @@ async function captureAudio(options = {}) {
13047
13049
  const {
13048
13050
  signal,
13049
13051
  minDurationMs = MIN_CAPTURE_MS,
13050
- maxDurationMs = MAX_CAPTURE_MS
13052
+ maxDurationMs = MAX_CAPTURE_MS,
13053
+ onAudioLevel
13051
13054
  } = options;
13052
13055
  const stream = await navigator.mediaDevices.getUserMedia({
13053
13056
  audio: {
@@ -13067,7 +13070,13 @@ async function captureAudio(options = {}) {
13067
13070
  const bufferSize = 4096;
13068
13071
  const processor = ctx.createScriptProcessor(bufferSize, 1, 1);
13069
13072
  processor.onaudioprocess = (e2) => {
13070
- chunks.push(new Float32Array(e2.inputBuffer.getChannelData(0)));
13073
+ const data = e2.inputBuffer.getChannelData(0);
13074
+ chunks.push(new Float32Array(data));
13075
+ if (onAudioLevel) {
13076
+ let sum = 0;
13077
+ for (let i = 0; i < data.length; i++) sum += data[i] * data[i];
13078
+ onAudioLevel(Math.sqrt(sum / data.length));
13079
+ }
13071
13080
  };
13072
13081
  source.connect(processor);
13073
13082
  processor.connect(ctx.destination);
@@ -13937,13 +13946,14 @@ var PulseSession = class {
13937
13946
  this.touchElement = touchElement;
13938
13947
  }
13939
13948
  // --- Audio ---
13940
- async startAudio() {
13949
+ async startAudio(onAudioLevel) {
13941
13950
  if (this.audioStageState !== "idle")
13942
13951
  throw new Error("Audio capture already started");
13943
13952
  this.audioStageState = "capturing";
13944
13953
  this.audioController = new AbortController();
13945
13954
  this.audioPromise = captureAudio({
13946
- signal: this.audioController.signal
13955
+ signal: this.audioController.signal,
13956
+ onAudioLevel
13947
13957
  }).catch(() => null);
13948
13958
  }
13949
13959
  async stopAudio() {
@@ -14191,6 +14201,28 @@ function generatePhrase(wordCount = 5) {
14191
14201
  }
14192
14202
  return words.join(" ");
14193
14203
  }
14204
+ function generatePhraseSequence(count = 3, wordCount = 4) {
14205
+ const subsetSize = Math.floor(SYLLABLES.length / count);
14206
+ const phrases = [];
14207
+ for (let p = 0; p < count; p++) {
14208
+ const start = p * subsetSize % SYLLABLES.length;
14209
+ const subset = [
14210
+ ...SYLLABLES.slice(start, start + subsetSize),
14211
+ ...SYLLABLES.slice(0, Math.max(0, start + subsetSize - SYLLABLES.length))
14212
+ ];
14213
+ const words = [];
14214
+ for (let w = 0; w < wordCount; w++) {
14215
+ const syllableCount = 2 + Math.floor(Math.random() * 2);
14216
+ let word = "";
14217
+ for (let s = 0; s < syllableCount; s++) {
14218
+ word += subset[Math.floor(Math.random() * subset.length)];
14219
+ }
14220
+ words.push(word);
14221
+ }
14222
+ phrases.push(words.join(" "));
14223
+ }
14224
+ return phrases;
14225
+ }
14194
14226
 
14195
14227
  // src/challenge/lissajous.ts
14196
14228
  function randomLissajousParams() {
@@ -14221,6 +14253,33 @@ function generateLissajousPoints(params) {
14221
14253
  }
14222
14254
  return result;
14223
14255
  }
14256
+ function generateLissajousSequence(count = 2) {
14257
+ const allRatios = [
14258
+ [1, 2],
14259
+ [2, 3],
14260
+ [3, 4],
14261
+ [3, 5],
14262
+ [4, 5],
14263
+ [1, 3],
14264
+ [2, 5],
14265
+ [5, 6],
14266
+ [3, 7],
14267
+ [4, 7]
14268
+ ];
14269
+ const shuffled = [...allRatios].sort(() => Math.random() - 0.5);
14270
+ const sequence = [];
14271
+ for (let i = 0; i < count; i++) {
14272
+ const pair = shuffled[i % shuffled.length];
14273
+ const params = {
14274
+ a: pair[0],
14275
+ b: pair[1],
14276
+ delta: Math.PI * (0.1 + Math.random() * 0.8),
14277
+ points: 200
14278
+ };
14279
+ sequence.push({ params, points: generateLissajousPoints(params) });
14280
+ }
14281
+ return sequence;
14282
+ }
14224
14283
  // Annotate the CommonJS export names for ESM import in node:
14225
14284
  0 && (module.exports = {
14226
14285
  DEFAULT_CAPTURE_MS,
@@ -14240,7 +14299,9 @@ function generateLissajousPoints(params) {
14240
14299
  fetchIdentityState,
14241
14300
  fuseFeatures,
14242
14301
  generateLissajousPoints,
14302
+ generateLissajousSequence,
14243
14303
  generatePhrase,
14304
+ generatePhraseSequence,
14244
14305
  generateProof,
14245
14306
  generateSalt,
14246
14307
  generateSolanaProof,