@entros/pulse-sdk 3.1.0 → 3.3.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@entros/pulse-sdk.svg)](https://www.npmjs.com/package/@entros/pulse-sdk)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@entros/pulse-sdk.svg)](https://www.npmjs.com/package/@entros/pulse-sdk)
5
5
 
6
- Client-side SDK for the Entros Protocol. Captures behavioral biometrics (voice, motion, touch), extracts a 308-dimensional statistical feature vector (v3 expansion: MFCCs with pre-emphasis (C1-C12, MFCC[0] dropped), LPC coefficients, formant trajectories, voice quality, pitch contour shape, IMU FFT-band tremor, cross-axis covariance, mouse-derived FFT / autocorrelation analogues for desktop, touch curvature, gap distribution, path efficiency — see `docs/master/BLUEPRINT-feature-pipeline-v2.md`), generates a Groth16 zero-knowledge proof, and submits for on-chain verification on Solana. Raw biometric data stays on-device — only derived features and the proof are transmitted.
6
+ Client-side SDK for the Entros Protocol. Captures behavioral biometrics (voice, motion, touch), extracts a 308-dimensional statistical feature vector (v3 expansion: MFCCs with pre-emphasis (C1-C12, MFCC[0] dropped), LPC coefficients, formant trajectories, voice quality, pitch contour shape, IMU FFT-band tremor, cross-axis covariance, mouse-derived FFT / autocorrelation analogues for desktop, touch curvature, gap distribution, path efficiency — see `docs/master/BLUEPRINT-feature-pipeline-v2.md`), generates a Groth16 zero-knowledge proof, and submits for on-chain verification on Solana. Raw biometric data never persists — only derived features and the proof are retained.
7
7
 
8
8
  > **Looking for a drop-in?** Most integrators want [`@entros/verify`](https://github.com/entros-protocol/entros-verify) — a popup-pattern React component that wraps this SDK and ships verification in five lines of JSX. Use this package directly when you need to own the verification UX (custom capture canvas, branded loading states, mobile-native).
9
9
 
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
1
3
  declare const FINGERPRINT_BITS = 256;
2
4
  declare const DEFAULT_THRESHOLD = 96;
3
5
  declare const DEFAULT_MIN_DISTANCE = 3;
@@ -316,23 +318,6 @@ interface FeatureVector {
316
318
  /** Concatenated feature vector for SimHash input */
317
319
  type FusedFeatureVector = number[];
318
320
 
319
- declare function mean(values: number[]): number;
320
- declare function variance(values: number[], mu?: number): number;
321
- declare function skewness(values: number[]): number;
322
- declare function kurtosis(values: number[]): number;
323
- declare function condense(values: number[]): StatsSummary;
324
- /**
325
- * Shannon entropy over histogram bins. Measures information density.
326
- * Real human data has moderate entropy (varied but structured).
327
- * Synthetic data is either too uniform (high entropy) or too structured (low entropy).
328
- */
329
- declare function entropy(values: number[], bins?: number): number;
330
- /**
331
- * Autocorrelation at a given lag. Detects periodic synthetic patterns.
332
- * Real human data has low autocorrelation at most lags (chaotic/noisy).
333
- * Synthetic data often has high autocorrelation (periodic/smooth).
334
- */
335
- declare function autocorrelation(values: number[], lag?: number): number;
336
321
  /**
337
322
  * Concatenate raw features without normalization.
338
323
  * Used for server-side validation where physical units matter.
@@ -528,6 +513,15 @@ declare function submitViaWallet(proof: SolanaProof, commitment: Uint8Array, opt
528
513
  * VerificationResult PDA instead.
529
514
  */
530
515
  signedReceipt?: SignedReceiptDto;
516
+ /**
517
+ * Encrypted baseline blob (master-list #98). When present, the SDK
518
+ * appends a `set_encrypted_baseline` instruction at the end of the
519
+ * atomic transaction so the wallet's on-chain baseline is rewritten
520
+ * to reflect the new fingerprint in the same wallet prompt as the
521
+ * mint / re-verify. Omitted when the wallet adapter lacks
522
+ * `signMessage` (the SDK can't derive the AES key without it).
523
+ */
524
+ encryptedBaselineBlob?: Uint8Array;
531
525
  }): Promise<SubmissionResult>;
532
526
  /**
533
527
  * Submit a baseline reset on-chain via a connected wallet.
@@ -549,6 +543,15 @@ declare function submitResetViaWallet(commitment: Uint8Array, options: {
549
543
  connection: any;
550
544
  relayerUrl?: string;
551
545
  relayerApiKey?: string;
546
+ /**
547
+ * Encrypted baseline blob (master-list #98). When present, the SDK
548
+ * appends a `set_encrypted_baseline` instruction so the wallet's
549
+ * on-chain baseline is rewritten under the NEW post-reset commitment
550
+ * in the same atomic transaction. Without this, the prior blob would
551
+ * be stale on the next recovery attempt (auth-tag mismatch under the
552
+ * new commitment in AAD) and recovery would fall back to fresh capture.
553
+ */
554
+ encryptedBaselineBlob?: Uint8Array;
552
555
  }): Promise<SubmissionResult>;
553
556
 
554
557
  /**
@@ -647,6 +650,125 @@ interface StoredVerificationData {
647
650
  timestamp: number;
648
651
  }
649
652
 
653
+ /**
654
+ * Wallet-keyed encrypted baseline storage (master-list #98).
655
+ *
656
+ * Architecture: the user's previous SimHash + salt (private witnesses for
657
+ * the Hamming-distance ZK proof) are encrypted with AES-256-GCM under a
658
+ * key derived from a deterministic `signMessage` on a domain-separated
659
+ * payload. The 96-byte ciphertext blob is stored on chain in a per-wallet
660
+ * `EncryptedBaseline` PDA, making the baseline wallet-portable across
661
+ * devices and recoverable after a cache wipe.
662
+ *
663
+ * The GCM AAD binds (version, algorithm, wallet, baselinePda, current on-
664
+ * chain commitment). After `reset_identity_state` cycles the commitment,
665
+ * the old blob's auth-tag fails to verify under the new commitment — that
666
+ * mismatch IS the staleness signal. No explicit version tracking needed.
667
+ *
668
+ * Plaintext biometric data never reaches chain at any point. This module
669
+ * is the only place wallet-derived key material flows; it never leaves
670
+ * memory and the key is non-extractable.
671
+ */
672
+
673
+ /** Layout: version(1) + algo(1) + reserved(2) + IV(12) + ct+tag(80) */
674
+ declare const ENCRYPTED_BASELINE_BLOB_BYTES = 96;
675
+ /**
676
+ * Pack a 256-bit fingerprint into 32 bytes. LSB-first within each byte —
677
+ * the convention is internal to the encrypted-baseline blob; the only
678
+ * requirement is that `bytesToFingerprint` is its exact inverse.
679
+ */
680
+ declare function fingerprintToBytes(bits: number[]): Uint8Array;
681
+ /** Inverse of `fingerprintToBytes`. */
682
+ declare function bytesToFingerprint(bytes: Uint8Array): number[];
683
+ /** Big-endian 32-byte → BigInt. Inverse of `bigintToBytes32` in hashing/poseidon. */
684
+ declare function bytes32ToBigint(bytes: Uint8Array): bigint;
685
+ /**
686
+ * The per-wallet `EncryptedBaseline` PDA address. Owned by entros-anchor.
687
+ * Seeds: `[b"encrypted_baseline", walletPubkey.toBuffer()]`.
688
+ */
689
+ declare function deriveEncryptedBaselinePda(walletPubkey: PublicKey): Promise<[PublicKey, number]>;
690
+ /**
691
+ * Wallet adapter shape needed for key derivation.
692
+ * Compatible with `@solana/wallet-adapter-base` `WalletAdapter`.
693
+ */
694
+ interface BaselineWallet {
695
+ publicKey: PublicKey;
696
+ signMessage: (msg: Uint8Array) => Promise<Uint8Array>;
697
+ }
698
+ /**
699
+ * Derive an AES-256 key from the wallet's deterministic signature over
700
+ * the domain-separated payload, via HKDF-SHA256.
701
+ *
702
+ * IKM = Ed25519 signature (64 bytes, pseudorandom under RoM)
703
+ * salt = wallet pubkey bytes (32 bytes — wallet-binding salt)
704
+ * info = "entros-protocol/identity-baseline/v1"
705
+ * L = 32 bytes (AES-256 key length)
706
+ *
707
+ * Determinism follows from RFC 8032 Ed25519 signature determinism. Same
708
+ * wallet + same message → same signature → same key, across any device.
709
+ *
710
+ * The derived `CryptoKey` is non-extractable; only WebCrypto can use it
711
+ * for AES-GCM encrypt/decrypt.
712
+ *
713
+ * Throws if the wallet doesn't implement `signMessage` (e.g., some Ledger
714
+ * firmware versions). Callers should catch and fall back gracefully.
715
+ */
716
+ declare function deriveBaselineKey(wallet: BaselineWallet): Promise<CryptoKey>;
717
+ /**
718
+ * Derive the baseline AES key for `wallet`, reusing the cached derivation
719
+ * when one is in-flight or resolved for this wallet pubkey in the current
720
+ * page lifetime. The key is deterministic per wallet, so caching is purely
721
+ * a UX optimization (avoids re-prompting `signMessage` on every verification
722
+ * within a session). Cache survives a single page load and is dropped on
723
+ * reload or via `clearBaselineKeyCache`.
724
+ *
725
+ * Concurrent calls for the same wallet share one `signMessage` prompt; a
726
+ * failed derivation evicts the cache entry so subsequent calls re-derive.
727
+ */
728
+ declare function getOrDeriveBaselineKey(wallet: BaselineWallet): Promise<CryptoKey>;
729
+ /** Drop all cached baseline keys. Test hook + emergency wipe. */
730
+ declare function clearBaselineKeyCache(): void;
731
+ /**
732
+ * Encrypt (simhash || salt) into a 96-byte versioned envelope.
733
+ *
734
+ * @param simhash 32-byte SimHash fingerprint (packed 256 bits)
735
+ * @param salt 32-byte Poseidon commitment salt
736
+ * @param key AES-256-GCM key from `deriveBaselineKey`
737
+ * @param walletPubkey wallet that owns the EncryptedBaseline PDA
738
+ * @param baselinePda the EncryptedBaseline PDA address (bound in AAD)
739
+ * @param commitment the on-chain `current_commitment` at encryption time
740
+ * (bound in AAD for staleness detection)
741
+ */
742
+ declare function encryptBaselineBlob(simhash: Uint8Array, salt: Uint8Array, key: CryptoKey, walletPubkey: PublicKey, baselinePda: PublicKey, commitment: Uint8Array): Promise<Uint8Array>;
743
+ /**
744
+ * Thrown when the on-chain blob's auth tag fails to verify under the
745
+ * current on-chain commitment. Indicates the blob was sealed against a
746
+ * different commitment (e.g., before a `reset_identity_state`), or the
747
+ * AAD-binding is otherwise broken. The SDK should fall back to a fresh-
748
+ * capture flow rather than attempting to recover.
749
+ */
750
+ declare class StaleEncryptedBaselineError extends Error {
751
+ constructor(message: string);
752
+ }
753
+ /**
754
+ * Decrypt a 96-byte versioned envelope back to (simhash, salt).
755
+ *
756
+ * Throws `StaleEncryptedBaselineError` on auth-tag mismatch — this is the
757
+ * cryptographic staleness signal. Other errors indicate a malformed blob.
758
+ */
759
+ declare function decryptBaselineBlob(blob: Uint8Array, key: CryptoKey, walletPubkey: PublicKey, baselinePda: PublicKey, commitment: Uint8Array): Promise<{
760
+ simhash: Uint8Array;
761
+ salt: Uint8Array;
762
+ }>;
763
+ /**
764
+ * Fetch the user's `EncryptedBaseline` PDA from chain. Returns `null` if
765
+ * the account has never been initialized (user hasn't called
766
+ * `set_encrypted_baseline` yet).
767
+ */
768
+ declare function fetchEncryptedBaseline(walletPubkey: PublicKey, connection: {
769
+ getAccountInfo: (k: PublicKey) => Promise<unknown>;
770
+ }): Promise<Uint8Array | null>;
771
+
650
772
  /**
651
773
  * Fetch identity state from the on-chain IdentityState PDA.
652
774
  *
@@ -681,6 +803,48 @@ declare function storeVerificationData(data: StoredVerificationData): Promise<vo
681
803
  * Decrypts if encrypted, migrates plaintext to encrypted on first load.
682
804
  */
683
805
  declare function loadVerificationData(): Promise<StoredVerificationData | null>;
806
+ /**
807
+ * Outcome of an attempt to recover the local baseline from the on-chain
808
+ * encrypted blob (master-list #98 cache-clear / cross-device path).
809
+ *
810
+ * Reasons distinguish recoverable from terminal failures:
811
+ * - `no-on-chain-identity`: caller should treat as first-verification.
812
+ * - `no-encrypted-baseline`: identity exists but user has never written
813
+ * an encrypted baseline (pre-3.3.0 SDK or pre-#98 deploy). UX should
814
+ * surface the existing baseline-missing copy and offer reset.
815
+ * - `signing-unavailable`: AES key derivation failed because the wallet
816
+ * can't `signMessage` (no method on the adapter, e.g., older Ledger
817
+ * firmware) OR the user cancelled the prompt OR the wallet erred. The
818
+ * `detail` field carries the specific cause when present.
819
+ * - `stale-baseline`: blob predates a `reset_identity_state` cycle.
820
+ * Treat as terminal recovery failure; route to fresh-capture flow.
821
+ * - `unknown-error`: catch-all (RPC failure, malformed blob, etc.).
822
+ */
823
+ type BaselineRecoveryReason = "no-on-chain-identity" | "no-encrypted-baseline" | "signing-unavailable" | "stale-baseline" | "unknown-error";
824
+ interface BaselineRecoveryResult {
825
+ recovered: boolean;
826
+ reason?: BaselineRecoveryReason;
827
+ detail?: string;
828
+ }
829
+ /**
830
+ * Attempt to recover this wallet's local baseline from the on-chain
831
+ * `EncryptedBaseline` PDA. On success, writes the recovered fingerprint /
832
+ * salt / commitment / timestamp into the SDK's normal local storage tier
833
+ * so the next `loadVerificationData()` call returns it transparently.
834
+ *
835
+ * Wallet flow on success: ONE `signMessage` prompt (for the AES key
836
+ * derivation). The wallet's currently-cached key — if `getOrDeriveBaselineKey`
837
+ * has been called earlier in the session — short-circuits the prompt.
838
+ *
839
+ * No-op when:
840
+ * - The on-chain `IdentityState` PDA does not exist (treat as first-verify).
841
+ * - The on-chain `EncryptedBaseline` PDA does not exist (pre-#98 or wallet
842
+ * never had `set_encrypted_baseline` written — UX should offer reset).
843
+ * - The wallet lacks `signMessage` (some Ledger firmware versions).
844
+ * - The blob's auth tag doesn't verify under the current on-chain
845
+ * commitment (stale blob from a prior reset cycle).
846
+ */
847
+ declare function recoverBaselineFromChain(wallet: BaselineWallet, connection: any): Promise<BaselineRecoveryResult>;
684
848
 
685
849
  /**
686
850
  * FALLBACK challenge-phrase generator. Used only when the executor's
@@ -787,4 +951,4 @@ declare function fetchChallenge(executorUrl: string, walletAddress: string, apiK
787
951
  */
788
952
  declare function encodeAudioAsBase64(samples: Float32Array): string;
789
953
 
790
- 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_AUDIO_SAMPLES, MIN_CAPTURE_MS, MIN_MOTION_SAMPLES, MIN_TOUCH_SAMPLES, MOTION_FEATURE_COUNT, 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, TOUCH_FEATURE_COUNT, 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 };
954
+ export { type AgentHumanOperator, type AudioCapture, type BaselineRecoveryReason, type BaselineRecoveryResult, type BaselineWallet, type CaptureOptions, type CaptureStage, type ChallengeResponse, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, ENCRYPTED_BASELINE_BLOB_BYTES, type EntrosAttestation, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_AUDIO_SAMPLES, MIN_CAPTURE_MS, MIN_MOTION_SAMPLES, MIN_TOUCH_SAMPLES, MOTION_FEATURE_COUNT, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, SPEAKER_FEATURE_COUNT, type SensorData, type SolanaProof, type StageState, StaleEncryptedBaselineError, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, TOUCH_FEATURE_COUNT, type TemporalFingerprint, type TouchSample, type VerificationResult, attestAgentOperator, bigintToBytes32, bytes32ToBigint, bytesToFingerprint, clearBaselineKeyCache, computeCommitment, decryptBaselineBlob, deriveBaselineKey, deriveEncryptedBaselinePda, encodeAudioAsBase64, encryptBaselineBlob, extractAccelerationMagnitude, extractMotionFeatures, extractMouseDynamics, extractSpeakerFeatures, extractSpeakerFeaturesDetailed, extractTouchFeatures, fetchChallenge, fetchEncryptedBaseline, fetchIdentityState, fingerprintToBytes, fuseFeatures, fuseRawFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, getAgentHumanOperator, getOrDeriveBaselineKey, hammingDistance, loadVerificationData, packBits, prepareCircuitInput, randomLissajousParams, recoverBaselineFromChain, serializeProof, simhash, storeVerificationData, submitResetViaWallet, submitViaRelayer, submitViaWallet, toBigEndian32, verifyEntrosAttestation };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
1
3
  declare const FINGERPRINT_BITS = 256;
2
4
  declare const DEFAULT_THRESHOLD = 96;
3
5
  declare const DEFAULT_MIN_DISTANCE = 3;
@@ -316,23 +318,6 @@ interface FeatureVector {
316
318
  /** Concatenated feature vector for SimHash input */
317
319
  type FusedFeatureVector = number[];
318
320
 
319
- declare function mean(values: number[]): number;
320
- declare function variance(values: number[], mu?: number): number;
321
- declare function skewness(values: number[]): number;
322
- declare function kurtosis(values: number[]): number;
323
- declare function condense(values: number[]): StatsSummary;
324
- /**
325
- * Shannon entropy over histogram bins. Measures information density.
326
- * Real human data has moderate entropy (varied but structured).
327
- * Synthetic data is either too uniform (high entropy) or too structured (low entropy).
328
- */
329
- declare function entropy(values: number[], bins?: number): number;
330
- /**
331
- * Autocorrelation at a given lag. Detects periodic synthetic patterns.
332
- * Real human data has low autocorrelation at most lags (chaotic/noisy).
333
- * Synthetic data often has high autocorrelation (periodic/smooth).
334
- */
335
- declare function autocorrelation(values: number[], lag?: number): number;
336
321
  /**
337
322
  * Concatenate raw features without normalization.
338
323
  * Used for server-side validation where physical units matter.
@@ -528,6 +513,15 @@ declare function submitViaWallet(proof: SolanaProof, commitment: Uint8Array, opt
528
513
  * VerificationResult PDA instead.
529
514
  */
530
515
  signedReceipt?: SignedReceiptDto;
516
+ /**
517
+ * Encrypted baseline blob (master-list #98). When present, the SDK
518
+ * appends a `set_encrypted_baseline` instruction at the end of the
519
+ * atomic transaction so the wallet's on-chain baseline is rewritten
520
+ * to reflect the new fingerprint in the same wallet prompt as the
521
+ * mint / re-verify. Omitted when the wallet adapter lacks
522
+ * `signMessage` (the SDK can't derive the AES key without it).
523
+ */
524
+ encryptedBaselineBlob?: Uint8Array;
531
525
  }): Promise<SubmissionResult>;
532
526
  /**
533
527
  * Submit a baseline reset on-chain via a connected wallet.
@@ -549,6 +543,15 @@ declare function submitResetViaWallet(commitment: Uint8Array, options: {
549
543
  connection: any;
550
544
  relayerUrl?: string;
551
545
  relayerApiKey?: string;
546
+ /**
547
+ * Encrypted baseline blob (master-list #98). When present, the SDK
548
+ * appends a `set_encrypted_baseline` instruction so the wallet's
549
+ * on-chain baseline is rewritten under the NEW post-reset commitment
550
+ * in the same atomic transaction. Without this, the prior blob would
551
+ * be stale on the next recovery attempt (auth-tag mismatch under the
552
+ * new commitment in AAD) and recovery would fall back to fresh capture.
553
+ */
554
+ encryptedBaselineBlob?: Uint8Array;
552
555
  }): Promise<SubmissionResult>;
553
556
 
554
557
  /**
@@ -647,6 +650,125 @@ interface StoredVerificationData {
647
650
  timestamp: number;
648
651
  }
649
652
 
653
+ /**
654
+ * Wallet-keyed encrypted baseline storage (master-list #98).
655
+ *
656
+ * Architecture: the user's previous SimHash + salt (private witnesses for
657
+ * the Hamming-distance ZK proof) are encrypted with AES-256-GCM under a
658
+ * key derived from a deterministic `signMessage` on a domain-separated
659
+ * payload. The 96-byte ciphertext blob is stored on chain in a per-wallet
660
+ * `EncryptedBaseline` PDA, making the baseline wallet-portable across
661
+ * devices and recoverable after a cache wipe.
662
+ *
663
+ * The GCM AAD binds (version, algorithm, wallet, baselinePda, current on-
664
+ * chain commitment). After `reset_identity_state` cycles the commitment,
665
+ * the old blob's auth-tag fails to verify under the new commitment — that
666
+ * mismatch IS the staleness signal. No explicit version tracking needed.
667
+ *
668
+ * Plaintext biometric data never reaches chain at any point. This module
669
+ * is the only place wallet-derived key material flows; it never leaves
670
+ * memory and the key is non-extractable.
671
+ */
672
+
673
+ /** Layout: version(1) + algo(1) + reserved(2) + IV(12) + ct+tag(80) */
674
+ declare const ENCRYPTED_BASELINE_BLOB_BYTES = 96;
675
+ /**
676
+ * Pack a 256-bit fingerprint into 32 bytes. LSB-first within each byte —
677
+ * the convention is internal to the encrypted-baseline blob; the only
678
+ * requirement is that `bytesToFingerprint` is its exact inverse.
679
+ */
680
+ declare function fingerprintToBytes(bits: number[]): Uint8Array;
681
+ /** Inverse of `fingerprintToBytes`. */
682
+ declare function bytesToFingerprint(bytes: Uint8Array): number[];
683
+ /** Big-endian 32-byte → BigInt. Inverse of `bigintToBytes32` in hashing/poseidon. */
684
+ declare function bytes32ToBigint(bytes: Uint8Array): bigint;
685
+ /**
686
+ * The per-wallet `EncryptedBaseline` PDA address. Owned by entros-anchor.
687
+ * Seeds: `[b"encrypted_baseline", walletPubkey.toBuffer()]`.
688
+ */
689
+ declare function deriveEncryptedBaselinePda(walletPubkey: PublicKey): Promise<[PublicKey, number]>;
690
+ /**
691
+ * Wallet adapter shape needed for key derivation.
692
+ * Compatible with `@solana/wallet-adapter-base` `WalletAdapter`.
693
+ */
694
+ interface BaselineWallet {
695
+ publicKey: PublicKey;
696
+ signMessage: (msg: Uint8Array) => Promise<Uint8Array>;
697
+ }
698
+ /**
699
+ * Derive an AES-256 key from the wallet's deterministic signature over
700
+ * the domain-separated payload, via HKDF-SHA256.
701
+ *
702
+ * IKM = Ed25519 signature (64 bytes, pseudorandom under RoM)
703
+ * salt = wallet pubkey bytes (32 bytes — wallet-binding salt)
704
+ * info = "entros-protocol/identity-baseline/v1"
705
+ * L = 32 bytes (AES-256 key length)
706
+ *
707
+ * Determinism follows from RFC 8032 Ed25519 signature determinism. Same
708
+ * wallet + same message → same signature → same key, across any device.
709
+ *
710
+ * The derived `CryptoKey` is non-extractable; only WebCrypto can use it
711
+ * for AES-GCM encrypt/decrypt.
712
+ *
713
+ * Throws if the wallet doesn't implement `signMessage` (e.g., some Ledger
714
+ * firmware versions). Callers should catch and fall back gracefully.
715
+ */
716
+ declare function deriveBaselineKey(wallet: BaselineWallet): Promise<CryptoKey>;
717
+ /**
718
+ * Derive the baseline AES key for `wallet`, reusing the cached derivation
719
+ * when one is in-flight or resolved for this wallet pubkey in the current
720
+ * page lifetime. The key is deterministic per wallet, so caching is purely
721
+ * a UX optimization (avoids re-prompting `signMessage` on every verification
722
+ * within a session). Cache survives a single page load and is dropped on
723
+ * reload or via `clearBaselineKeyCache`.
724
+ *
725
+ * Concurrent calls for the same wallet share one `signMessage` prompt; a
726
+ * failed derivation evicts the cache entry so subsequent calls re-derive.
727
+ */
728
+ declare function getOrDeriveBaselineKey(wallet: BaselineWallet): Promise<CryptoKey>;
729
+ /** Drop all cached baseline keys. Test hook + emergency wipe. */
730
+ declare function clearBaselineKeyCache(): void;
731
+ /**
732
+ * Encrypt (simhash || salt) into a 96-byte versioned envelope.
733
+ *
734
+ * @param simhash 32-byte SimHash fingerprint (packed 256 bits)
735
+ * @param salt 32-byte Poseidon commitment salt
736
+ * @param key AES-256-GCM key from `deriveBaselineKey`
737
+ * @param walletPubkey wallet that owns the EncryptedBaseline PDA
738
+ * @param baselinePda the EncryptedBaseline PDA address (bound in AAD)
739
+ * @param commitment the on-chain `current_commitment` at encryption time
740
+ * (bound in AAD for staleness detection)
741
+ */
742
+ declare function encryptBaselineBlob(simhash: Uint8Array, salt: Uint8Array, key: CryptoKey, walletPubkey: PublicKey, baselinePda: PublicKey, commitment: Uint8Array): Promise<Uint8Array>;
743
+ /**
744
+ * Thrown when the on-chain blob's auth tag fails to verify under the
745
+ * current on-chain commitment. Indicates the blob was sealed against a
746
+ * different commitment (e.g., before a `reset_identity_state`), or the
747
+ * AAD-binding is otherwise broken. The SDK should fall back to a fresh-
748
+ * capture flow rather than attempting to recover.
749
+ */
750
+ declare class StaleEncryptedBaselineError extends Error {
751
+ constructor(message: string);
752
+ }
753
+ /**
754
+ * Decrypt a 96-byte versioned envelope back to (simhash, salt).
755
+ *
756
+ * Throws `StaleEncryptedBaselineError` on auth-tag mismatch — this is the
757
+ * cryptographic staleness signal. Other errors indicate a malformed blob.
758
+ */
759
+ declare function decryptBaselineBlob(blob: Uint8Array, key: CryptoKey, walletPubkey: PublicKey, baselinePda: PublicKey, commitment: Uint8Array): Promise<{
760
+ simhash: Uint8Array;
761
+ salt: Uint8Array;
762
+ }>;
763
+ /**
764
+ * Fetch the user's `EncryptedBaseline` PDA from chain. Returns `null` if
765
+ * the account has never been initialized (user hasn't called
766
+ * `set_encrypted_baseline` yet).
767
+ */
768
+ declare function fetchEncryptedBaseline(walletPubkey: PublicKey, connection: {
769
+ getAccountInfo: (k: PublicKey) => Promise<unknown>;
770
+ }): Promise<Uint8Array | null>;
771
+
650
772
  /**
651
773
  * Fetch identity state from the on-chain IdentityState PDA.
652
774
  *
@@ -681,6 +803,48 @@ declare function storeVerificationData(data: StoredVerificationData): Promise<vo
681
803
  * Decrypts if encrypted, migrates plaintext to encrypted on first load.
682
804
  */
683
805
  declare function loadVerificationData(): Promise<StoredVerificationData | null>;
806
+ /**
807
+ * Outcome of an attempt to recover the local baseline from the on-chain
808
+ * encrypted blob (master-list #98 cache-clear / cross-device path).
809
+ *
810
+ * Reasons distinguish recoverable from terminal failures:
811
+ * - `no-on-chain-identity`: caller should treat as first-verification.
812
+ * - `no-encrypted-baseline`: identity exists but user has never written
813
+ * an encrypted baseline (pre-3.3.0 SDK or pre-#98 deploy). UX should
814
+ * surface the existing baseline-missing copy and offer reset.
815
+ * - `signing-unavailable`: AES key derivation failed because the wallet
816
+ * can't `signMessage` (no method on the adapter, e.g., older Ledger
817
+ * firmware) OR the user cancelled the prompt OR the wallet erred. The
818
+ * `detail` field carries the specific cause when present.
819
+ * - `stale-baseline`: blob predates a `reset_identity_state` cycle.
820
+ * Treat as terminal recovery failure; route to fresh-capture flow.
821
+ * - `unknown-error`: catch-all (RPC failure, malformed blob, etc.).
822
+ */
823
+ type BaselineRecoveryReason = "no-on-chain-identity" | "no-encrypted-baseline" | "signing-unavailable" | "stale-baseline" | "unknown-error";
824
+ interface BaselineRecoveryResult {
825
+ recovered: boolean;
826
+ reason?: BaselineRecoveryReason;
827
+ detail?: string;
828
+ }
829
+ /**
830
+ * Attempt to recover this wallet's local baseline from the on-chain
831
+ * `EncryptedBaseline` PDA. On success, writes the recovered fingerprint /
832
+ * salt / commitment / timestamp into the SDK's normal local storage tier
833
+ * so the next `loadVerificationData()` call returns it transparently.
834
+ *
835
+ * Wallet flow on success: ONE `signMessage` prompt (for the AES key
836
+ * derivation). The wallet's currently-cached key — if `getOrDeriveBaselineKey`
837
+ * has been called earlier in the session — short-circuits the prompt.
838
+ *
839
+ * No-op when:
840
+ * - The on-chain `IdentityState` PDA does not exist (treat as first-verify).
841
+ * - The on-chain `EncryptedBaseline` PDA does not exist (pre-#98 or wallet
842
+ * never had `set_encrypted_baseline` written — UX should offer reset).
843
+ * - The wallet lacks `signMessage` (some Ledger firmware versions).
844
+ * - The blob's auth tag doesn't verify under the current on-chain
845
+ * commitment (stale blob from a prior reset cycle).
846
+ */
847
+ declare function recoverBaselineFromChain(wallet: BaselineWallet, connection: any): Promise<BaselineRecoveryResult>;
684
848
 
685
849
  /**
686
850
  * FALLBACK challenge-phrase generator. Used only when the executor's
@@ -787,4 +951,4 @@ declare function fetchChallenge(executorUrl: string, walletAddress: string, apiK
787
951
  */
788
952
  declare function encodeAudioAsBase64(samples: Float32Array): string;
789
953
 
790
- 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_AUDIO_SAMPLES, MIN_CAPTURE_MS, MIN_MOTION_SAMPLES, MIN_TOUCH_SAMPLES, MOTION_FEATURE_COUNT, 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, TOUCH_FEATURE_COUNT, 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 };
954
+ export { type AgentHumanOperator, type AudioCapture, type BaselineRecoveryReason, type BaselineRecoveryResult, type BaselineWallet, type CaptureOptions, type CaptureStage, type ChallengeResponse, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, ENCRYPTED_BASELINE_BLOB_BYTES, type EntrosAttestation, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_AUDIO_SAMPLES, MIN_CAPTURE_MS, MIN_MOTION_SAMPLES, MIN_TOUCH_SAMPLES, MOTION_FEATURE_COUNT, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, SPEAKER_FEATURE_COUNT, type SensorData, type SolanaProof, type StageState, StaleEncryptedBaselineError, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, TOUCH_FEATURE_COUNT, type TemporalFingerprint, type TouchSample, type VerificationResult, attestAgentOperator, bigintToBytes32, bytes32ToBigint, bytesToFingerprint, clearBaselineKeyCache, computeCommitment, decryptBaselineBlob, deriveBaselineKey, deriveEncryptedBaselinePda, encodeAudioAsBase64, encryptBaselineBlob, extractAccelerationMagnitude, extractMotionFeatures, extractMouseDynamics, extractSpeakerFeatures, extractSpeakerFeaturesDetailed, extractTouchFeatures, fetchChallenge, fetchEncryptedBaseline, fetchIdentityState, fingerprintToBytes, fuseFeatures, fuseRawFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, getAgentHumanOperator, getOrDeriveBaselineKey, hammingDistance, loadVerificationData, packBits, prepareCircuitInput, randomLissajousParams, recoverBaselineFromChain, serializeProof, simhash, storeVerificationData, submitResetViaWallet, submitViaRelayer, submitViaWallet, toBigEndian32, verifyEntrosAttestation };