@svrnsec/pulse 0.6.0 → 0.8.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.
Files changed (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +883 -622
  3. package/SECURITY.md +86 -86
  4. package/bin/svrnsec-pulse.js +7 -7
  5. package/dist/{pulse.cjs.js → pulse.cjs} +6379 -6420
  6. package/dist/pulse.cjs.map +1 -0
  7. package/dist/pulse.esm.js +6380 -6421
  8. package/dist/pulse.esm.js.map +1 -1
  9. package/index.d.ts +895 -846
  10. package/package.json +185 -165
  11. package/pkg/pulse_core.js +174 -173
  12. package/src/analysis/audio.js +213 -213
  13. package/src/analysis/authenticityAudit.js +408 -390
  14. package/src/analysis/coherence.js +502 -502
  15. package/src/analysis/coordinatedBehavior.js +825 -0
  16. package/src/analysis/heuristic.js +428 -428
  17. package/src/analysis/jitter.js +446 -446
  18. package/src/analysis/llm.js +473 -472
  19. package/src/analysis/populationEntropy.js +404 -403
  20. package/src/analysis/provider.js +248 -248
  21. package/src/analysis/refraction.js +392 -0
  22. package/src/analysis/trustScore.js +356 -356
  23. package/src/cli/args.js +36 -36
  24. package/src/cli/commands/scan.js +192 -192
  25. package/src/cli/runner.js +157 -157
  26. package/src/collector/adaptive.js +200 -200
  27. package/src/collector/bio.js +297 -287
  28. package/src/collector/canvas.js +247 -239
  29. package/src/collector/dram.js +203 -203
  30. package/src/collector/enf.js +311 -311
  31. package/src/collector/entropy.js +195 -195
  32. package/src/collector/gpu.js +248 -245
  33. package/src/collector/idleAttestation.js +480 -480
  34. package/src/collector/sabTimer.js +189 -191
  35. package/src/fingerprint.js +475 -475
  36. package/src/index.js +342 -342
  37. package/src/integrations/react-native.js +462 -459
  38. package/src/integrations/react.js +184 -185
  39. package/src/middleware/express.js +155 -155
  40. package/src/middleware/next.js +174 -175
  41. package/src/proof/challenge.js +249 -249
  42. package/src/proof/engagementToken.js +426 -394
  43. package/src/proof/fingerprint.js +268 -268
  44. package/src/proof/validator.js +83 -143
  45. package/src/registry/serializer.js +349 -349
  46. package/src/terminal.js +263 -263
  47. package/src/update-notifier.js +259 -264
  48. package/dist/pulse.cjs.js.map +0 -1
@@ -1,185 +1,184 @@
1
- /**
2
- * @sovereign/pulse — React Hook
3
- *
4
- * import { usePulse } from '@sovereign/pulse/react';
5
- *
6
- * const {
7
- * run, reset,
8
- * stage, pct, vmConf, hwConf, earlyVerdict,
9
- * proof, result,
10
- * isRunning, isReady, error,
11
- * } = usePulse({ apiKey: 'sk_live_...' });
12
- *
13
- * // Or self-hosted:
14
- * const { run, proof, result } = usePulse({
15
- * challengeUrl: '/api/pulse/challenge',
16
- * verifyUrl: '/api/pulse/verify',
17
- * });
18
- */
19
-
20
- import { useState, useCallback, useRef } from 'react';
21
-
22
- // Lazy import — only loaded in browser, allows tree-shaking in SSR builds
23
- let _pulseModule = null;
24
- async function getPulse() {
25
- if (!_pulseModule) {
26
- _pulseModule = await import('../index.js');
27
- }
28
- return _pulseModule.pulse;
29
- }
30
-
31
- /**
32
- * @param {object} opts
33
- * @param {string} [opts.apiKey] - hosted API key (zero-config)
34
- * @param {string} [opts.apiUrl] - hosted API base URL (default: https://api.sovereign.dev)
35
- * @param {string} [opts.challengeUrl] - self-hosted challenge endpoint
36
- * @param {string} [opts.verifyUrl] - self-hosted verify endpoint
37
- * @param {number} [opts.iterations=200]
38
- * @param {number} [opts.bioWindowMs=3000]
39
- * @param {boolean} [opts.adaptive=true]
40
- * @param {boolean} [opts.autoRun=false] - run immediately on mount
41
- * @param {Function} [opts.onResult] - callback when result is ready
42
- * @param {Function} [opts.onError] - callback on error
43
- */
44
- export function usePulse(opts = {}) {
45
- const {
46
- apiKey,
47
- apiUrl = 'https://api.sovereign.dev',
48
- challengeUrl,
49
- verifyUrl,
50
- iterations = 200,
51
- bioWindowMs = 3000,
52
- adaptive = true,
53
- autoRun = false,
54
- onResult,
55
- onError,
56
- } = opts;
57
-
58
- // ── State ────────────────────────────────────────────────────────────────
59
- const [stage, setStage] = useState(null);
60
- const [pct, setPct] = useState(0);
61
- const [vmConf, setVmConf] = useState(0);
62
- const [hwConf, setHwConf] = useState(0);
63
- const [earlyVerdict, setEarlyVerdict]= useState(null);
64
- const [proof, setProof] = useState(null);
65
- const [result, setResult] = useState(null);
66
- const [isRunning, setIsRunning] = useState(false);
67
- const [error, setError] = useState(null);
68
-
69
- const abortRef = useRef(null);
70
- const hasAutoRun = useRef(false);
71
-
72
- // ── run() ────────────────────────────────────────────────────────────────
73
- const run = useCallback(async () => {
74
- if (isRunning) return;
75
-
76
- // Reset
77
- setStage(null); setPct(0); setVmConf(0); setHwConf(0);
78
- setEarlyVerdict(null); setProof(null); setResult(null);
79
- setError(null); setIsRunning(true);
80
-
81
- try {
82
- // 1. Resolve nonce
83
- let nonce;
84
- if (apiKey) {
85
- const res = await fetch(`${apiUrl}/v1/challenge`, {
86
- headers: { 'Authorization': `Bearer ${apiKey}` },
87
- });
88
- if (!res.ok) throw new Error(`Challenge failed: ${res.status}`);
89
- ({ nonce } = await res.json());
90
- } else if (challengeUrl) {
91
- const res = await fetch(challengeUrl);
92
- if (!res.ok) throw new Error(`Challenge failed: ${res.status}`);
93
- ({ nonce } = await res.json());
94
- } else {
95
- throw new Error(
96
- 'usePulse requires either apiKey or challengeUrl. ' +
97
- 'Pass apiKey for the hosted API, or challengeUrl + verifyUrl for self-hosted.'
98
- );
99
- }
100
-
101
- // 2. Run the probe
102
- const pulse = await getPulse();
103
- const commitment = await pulse({
104
- nonce,
105
- iterations,
106
- bioWindowMs,
107
- adaptive,
108
- onProgress: (s, meta = {}) => {
109
- setStage(s);
110
- if (s === 'entropy_batch' && meta) {
111
- if (meta.pct != null) setPct(meta.pct);
112
- if (meta.vmConf != null) setVmConf(meta.vmConf);
113
- if (meta.hwConf != null) setHwConf(meta.hwConf);
114
- if (meta.earlyVerdict != null) setEarlyVerdict(meta.earlyVerdict);
115
- }
116
- },
117
- });
118
-
119
- setProof(commitment);
120
- setPct(100);
121
-
122
- // 3. Verify (hosted or self-hosted)
123
- if (apiKey || verifyUrl) {
124
- const url = apiKey ? `${apiUrl}/v1/verify` : verifyUrl;
125
- const headers = {
126
- 'Content-Type': 'application/json',
127
- ...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}),
128
- };
129
-
130
- const res = await fetch(url, {
131
- method: 'POST',
132
- headers,
133
- body: JSON.stringify({ payload: commitment.payload, hash: commitment.hash }),
134
- });
135
- const verifyResult = await res.json();
136
- setResult(verifyResult);
137
- onResult?.(verifyResult, commitment);
138
- } else {
139
- onResult?.(null, commitment);
140
- }
141
-
142
- } catch (err) {
143
- setError(err);
144
- onError?.(err);
145
- } finally {
146
- setIsRunning(false);
147
- }
148
- }, [isRunning, apiKey, apiUrl, challengeUrl, verifyUrl, iterations, bioWindowMs, adaptive, onResult, onError]);
149
-
150
- // ── reset() ──────────────────────────────────────────────────────────────
151
- const reset = useCallback(() => {
152
- setStage(null); setPct(0); setVmConf(0); setHwConf(0);
153
- setEarlyVerdict(null); setProof(null); setResult(null);
154
- setIsRunning(false); setError(null);
155
- }, []);
156
-
157
- // ── autoRun on mount ──────────────────────────────────────────────────────
158
- // Note: We use a ref to avoid triggering on every render.
159
- // Consumers should wrap in useEffect if they need SSR safety:
160
- // useEffect(() => { if (autoRun) run(); }, []);
161
- if (autoRun && !hasAutoRun.current && typeof window !== 'undefined') {
162
- hasAutoRun.current = true;
163
- // Defer to next microtask so hook state is initialised
164
- Promise.resolve().then(run);
165
- }
166
-
167
- return {
168
- // Actions
169
- run,
170
- reset,
171
- // Live probe state
172
- stage,
173
- pct,
174
- vmConf,
175
- hwConf,
176
- earlyVerdict,
177
- // Results
178
- proof,
179
- result,
180
- // Status
181
- isRunning,
182
- isReady: !isRunning && (proof != null || error != null),
183
- error,
184
- };
185
- }
1
+ /**
2
+ * @svrnsec/pulse — React Hook
3
+ *
4
+ * import { usePulse } from '@svrnsec/pulse/react';
5
+ *
6
+ * const {
7
+ * run, reset,
8
+ * stage, pct, vmConf, hwConf, earlyVerdict,
9
+ * proof, result,
10
+ * isRunning, isReady, error,
11
+ * } = usePulse({ apiKey: 'sk_live_...' });
12
+ *
13
+ * // Or self-hosted:
14
+ * const { run, proof, result } = usePulse({
15
+ * challengeUrl: '/api/pulse/challenge',
16
+ * verifyUrl: '/api/pulse/verify',
17
+ * });
18
+ */
19
+
20
+ import { useState, useCallback, useRef, useEffect } from 'react';
21
+
22
+ // Lazy import — only loaded in browser, allows tree-shaking in SSR builds
23
+ let _pulseModule = null;
24
+ async function getPulse() {
25
+ if (!_pulseModule) {
26
+ _pulseModule = await import('../index.js');
27
+ }
28
+ return _pulseModule.pulse;
29
+ }
30
+
31
+ /**
32
+ * @param {object} opts
33
+ * @param {string} [opts.apiKey] - hosted API key (zero-config)
34
+ * @param {string} [opts.apiUrl] - hosted API base URL (default: https://api.sovereign.dev)
35
+ * @param {string} [opts.challengeUrl] - self-hosted challenge endpoint
36
+ * @param {string} [opts.verifyUrl] - self-hosted verify endpoint
37
+ * @param {number} [opts.iterations=200]
38
+ * @param {number} [opts.bioWindowMs=3000]
39
+ * @param {boolean} [opts.adaptive=true]
40
+ * @param {boolean} [opts.autoRun=false] - run immediately on mount
41
+ * @param {Function} [opts.onResult] - callback when result is ready
42
+ * @param {Function} [opts.onError] - callback on error
43
+ */
44
+ export function usePulse(opts = {}) {
45
+ const {
46
+ apiKey,
47
+ apiUrl = 'https://api.sovereign.dev',
48
+ challengeUrl,
49
+ verifyUrl,
50
+ iterations = 200,
51
+ bioWindowMs = 3000,
52
+ adaptive = true,
53
+ autoRun = false,
54
+ onResult,
55
+ onError,
56
+ } = opts;
57
+
58
+ // ── State ────────────────────────────────────────────────────────────────
59
+ const [stage, setStage] = useState(null);
60
+ const [pct, setPct] = useState(0);
61
+ const [vmConf, setVmConf] = useState(0);
62
+ const [hwConf, setHwConf] = useState(0);
63
+ const [earlyVerdict, setEarlyVerdict]= useState(null);
64
+ const [proof, setProof] = useState(null);
65
+ const [result, setResult] = useState(null);
66
+ const [isRunning, setIsRunning] = useState(false);
67
+ const [error, setError] = useState(null);
68
+
69
+ const abortRef = useRef(null);
70
+ const hasAutoRun = useRef(false);
71
+
72
+ // ── run() ────────────────────────────────────────────────────────────────
73
+ const run = useCallback(async () => {
74
+ if (isRunning) return;
75
+
76
+ // Reset
77
+ setStage(null); setPct(0); setVmConf(0); setHwConf(0);
78
+ setEarlyVerdict(null); setProof(null); setResult(null);
79
+ setError(null); setIsRunning(true);
80
+
81
+ try {
82
+ // 1. Resolve nonce
83
+ let nonce;
84
+ if (apiKey) {
85
+ const res = await fetch(`${apiUrl}/v1/challenge`, {
86
+ headers: { 'Authorization': `Bearer ${apiKey}` },
87
+ });
88
+ if (!res.ok) throw new Error(`Challenge failed: ${res.status}`);
89
+ ({ nonce } = await res.json());
90
+ } else if (challengeUrl) {
91
+ const res = await fetch(challengeUrl);
92
+ if (!res.ok) throw new Error(`Challenge failed: ${res.status}`);
93
+ ({ nonce } = await res.json());
94
+ } else {
95
+ throw new Error(
96
+ 'usePulse requires either apiKey or challengeUrl. ' +
97
+ 'Pass apiKey for the hosted API, or challengeUrl + verifyUrl for self-hosted.'
98
+ );
99
+ }
100
+
101
+ // 2. Run the probe
102
+ const pulse = await getPulse();
103
+ const commitment = await pulse({
104
+ nonce,
105
+ iterations,
106
+ bioWindowMs,
107
+ adaptive,
108
+ onProgress: (s, meta = {}) => {
109
+ setStage(s);
110
+ if (s === 'entropy_batch' && meta) {
111
+ if (meta.pct != null) setPct(meta.pct);
112
+ if (meta.vmConf != null) setVmConf(meta.vmConf);
113
+ if (meta.hwConf != null) setHwConf(meta.hwConf);
114
+ if (meta.earlyVerdict != null) setEarlyVerdict(meta.earlyVerdict);
115
+ }
116
+ },
117
+ });
118
+
119
+ setProof(commitment);
120
+ setPct(100);
121
+
122
+ // 3. Verify (hosted or self-hosted)
123
+ if (apiKey || verifyUrl) {
124
+ const url = apiKey ? `${apiUrl}/v1/verify` : verifyUrl;
125
+ const headers = {
126
+ 'Content-Type': 'application/json',
127
+ ...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}),
128
+ };
129
+
130
+ const res = await fetch(url, {
131
+ method: 'POST',
132
+ headers,
133
+ body: JSON.stringify({ payload: commitment.payload, hash: commitment.hash }),
134
+ });
135
+ if (!res.ok) throw new Error('Verify failed: ' + res.status);
136
+ const verifyResult = await res.json();
137
+ setResult(verifyResult);
138
+ onResult?.(verifyResult, commitment);
139
+ } else {
140
+ onResult?.(null, commitment);
141
+ }
142
+
143
+ } catch (err) {
144
+ setError(err);
145
+ onError?.(err);
146
+ } finally {
147
+ setIsRunning(false);
148
+ }
149
+ }, [isRunning, apiKey, apiUrl, challengeUrl, verifyUrl, iterations, bioWindowMs, adaptive, onResult, onError]);
150
+
151
+ // ── reset() ──────────────────────────────────────────────────────────────
152
+ const reset = useCallback(() => {
153
+ setStage(null); setPct(0); setVmConf(0); setHwConf(0);
154
+ setEarlyVerdict(null); setProof(null); setResult(null);
155
+ setIsRunning(false); setError(null);
156
+ }, []);
157
+
158
+ // ── autoRun on mount ──────────────────────────────────────────────────────
159
+ useEffect(() => {
160
+ if (autoRun && !hasAutoRun.current) {
161
+ hasAutoRun.current = true;
162
+ run();
163
+ }
164
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
165
+
166
+ return {
167
+ // Actions
168
+ run,
169
+ reset,
170
+ // Live probe state
171
+ stage,
172
+ pct,
173
+ vmConf,
174
+ hwConf,
175
+ earlyVerdict,
176
+ // Results
177
+ proof,
178
+ result,
179
+ // Status
180
+ isRunning,
181
+ isReady: !isRunning && (proof != null || error != null),
182
+ error,
183
+ };
184
+ }