@stvor/sdk 2.4.0 → 3.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.
Files changed (82) hide show
  1. package/dist/facade/app.cjs +29 -0
  2. package/dist/facade/app.d.ts +83 -76
  3. package/dist/facade/app.js +330 -195
  4. package/dist/facade/crypto-session.cjs +29 -0
  5. package/dist/facade/crypto-session.d.ts +49 -54
  6. package/dist/facade/crypto-session.js +117 -140
  7. package/dist/facade/errors.cjs +29 -0
  8. package/dist/facade/errors.d.ts +29 -12
  9. package/dist/facade/errors.js +49 -8
  10. package/dist/facade/index.cjs +29 -0
  11. package/dist/facade/index.d.ts +27 -8
  12. package/dist/facade/index.js +23 -3
  13. package/dist/facade/local-storage-identity-store.cjs +29 -0
  14. package/dist/facade/local-storage-identity-store.d.ts +50 -0
  15. package/dist/facade/local-storage-identity-store.js +100 -0
  16. package/dist/facade/metrics-attestation.cjs +29 -0
  17. package/dist/facade/metrics-attestation.d.ts +209 -0
  18. package/dist/facade/metrics-attestation.js +333 -0
  19. package/dist/facade/metrics-engine.cjs +29 -0
  20. package/dist/facade/metrics-engine.d.ts +91 -0
  21. package/dist/facade/metrics-engine.js +170 -0
  22. package/dist/facade/redis-replay-cache.cjs +29 -0
  23. package/dist/facade/redis-replay-cache.d.ts +88 -0
  24. package/dist/facade/redis-replay-cache.js +60 -0
  25. package/dist/facade/relay-client.cjs +29 -0
  26. package/dist/facade/relay-client.d.ts +22 -23
  27. package/dist/facade/relay-client.js +107 -128
  28. package/dist/facade/replay-manager.cjs +29 -0
  29. package/dist/facade/replay-manager.d.ts +28 -35
  30. package/dist/facade/replay-manager.js +102 -69
  31. package/dist/facade/sodium-singleton.cjs +29 -0
  32. package/dist/facade/tofu-manager.cjs +29 -0
  33. package/dist/facade/tofu-manager.d.ts +38 -36
  34. package/dist/facade/tofu-manager.js +109 -77
  35. package/dist/facade/types.cjs +29 -0
  36. package/dist/facade/types.d.ts +2 -0
  37. package/dist/index.cjs +29 -0
  38. package/dist/index.d.cts +6 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +7 -0
  41. package/dist/legacy.cjs +29 -0
  42. package/dist/legacy.d.ts +31 -1
  43. package/dist/legacy.js +90 -2
  44. package/dist/ratchet/core-production.cjs +29 -0
  45. package/dist/ratchet/core-production.d.ts +95 -0
  46. package/dist/ratchet/core-production.js +286 -0
  47. package/dist/ratchet/index.cjs +29 -0
  48. package/dist/ratchet/index.d.ts +49 -78
  49. package/dist/ratchet/index.js +313 -288
  50. package/dist/ratchet/key-recovery.cjs +29 -0
  51. package/dist/ratchet/replay-protection.cjs +29 -0
  52. package/dist/ratchet/tofu.cjs +29 -0
  53. package/dist/src/facade/app.cjs +29 -0
  54. package/dist/src/facade/app.d.ts +105 -0
  55. package/dist/src/facade/app.js +245 -0
  56. package/dist/src/facade/crypto.cjs +29 -0
  57. package/dist/src/facade/errors.cjs +29 -0
  58. package/dist/src/facade/errors.d.ts +19 -0
  59. package/dist/src/facade/errors.js +21 -0
  60. package/dist/src/facade/index.cjs +29 -0
  61. package/dist/src/facade/index.d.ts +8 -0
  62. package/dist/src/facade/index.js +5 -0
  63. package/dist/src/facade/relay-client.cjs +29 -0
  64. package/dist/src/facade/relay-client.d.ts +36 -0
  65. package/dist/src/facade/relay-client.js +154 -0
  66. package/dist/src/facade/types.cjs +29 -0
  67. package/dist/src/facade/types.d.ts +50 -0
  68. package/dist/src/facade/types.js +4 -0
  69. package/dist/src/index.cjs +29 -0
  70. package/dist/src/index.d.ts +2 -0
  71. package/dist/src/index.js +2 -0
  72. package/dist/src/legacy.cjs +29 -0
  73. package/dist/src/legacy.d.ts +0 -0
  74. package/dist/src/legacy.js +1 -0
  75. package/dist/src/mock-relay-server.cjs +29 -0
  76. package/dist/src/mock-relay-server.d.ts +30 -0
  77. package/dist/src/mock-relay-server.js +236 -0
  78. package/package.json +37 -11
  79. package/dist/ratchet/tests/ratchet.test.d.ts +0 -1
  80. package/dist/ratchet/tests/ratchet.test.js +0 -160
  81. /package/dist/{facade → src/facade}/crypto.d.ts +0 -0
  82. /package/dist/{facade → src/facade}/crypto.js +0 -0
@@ -1,22 +1,15 @@
1
1
  /**
2
2
  * STVOR Crypto Session Manager
3
- * Integrates X3DH + Double Ratchet from ratchet module
3
+ * Uses ONLY Node.js built-in crypto module zero external dependencies
4
4
  *
5
- * CRITICAL: Identity keys generated ONCE per userId
6
- * Currently in-memory only - keys lost on restart
7
- *
8
- * TODO: Add persistent storage (IndexedDB/Keychain)
5
+ * Manages identity keys (IK + SPK), ECDSA signatures,
6
+ * X3DH session establishment, and Double Ratchet encrypt/decrypt.
9
7
  */
8
+ import { KeyPair } from '../ratchet/index.js';
10
9
  export interface IdentityKeys {
11
- identityKeyPair: {
12
- publicKey: Uint8Array;
13
- privateKey: Uint8Array;
14
- };
15
- signedPreKeyPair: {
16
- publicKey: Uint8Array;
17
- privateKey: Uint8Array;
18
- };
19
- oneTimePreKeys: Uint8Array[];
10
+ identityKeyPair: KeyPair;
11
+ signedPreKeyPair: KeyPair;
12
+ signedPreKeySignature: Buffer;
20
13
  }
21
14
  export interface SerializedPublicKeys {
22
15
  identityKey: string;
@@ -24,53 +17,55 @@ export interface SerializedPublicKeys {
24
17
  signedPreKeySignature: string;
25
18
  oneTimePreKey: string;
26
19
  }
27
- /**
28
- * Manages cryptographic sessions for all peers
29
- */
20
+ export interface IIdentityStore {
21
+ saveIdentityKeys(userId: string, keys: {
22
+ identityKeyPair: {
23
+ publicKey: string;
24
+ privateKey: string;
25
+ };
26
+ signedPreKeyPair: {
27
+ publicKey: string;
28
+ privateKey: string;
29
+ };
30
+ signedPreKeySignature: string;
31
+ }): Promise<void>;
32
+ loadIdentityKeys(userId: string): Promise<{
33
+ identityKeyPair: {
34
+ publicKey: string;
35
+ privateKey: string;
36
+ };
37
+ signedPreKeyPair: {
38
+ publicKey: string;
39
+ privateKey: string;
40
+ };
41
+ signedPreKeySignature: string;
42
+ } | null>;
43
+ }
44
+ export interface ISessionStore {
45
+ saveSession(userId: string, peerId: string, sessionData: Buffer): Promise<void>;
46
+ loadSession(userId: string, peerId: string): Promise<Buffer | null>;
47
+ deleteSession(userId: string, peerId: string): Promise<void>;
48
+ listSessions(userId: string): Promise<string[]>;
49
+ }
30
50
  export declare class CryptoSessionManager {
31
51
  private userId;
32
52
  private identityKeys;
33
53
  private sessions;
34
54
  private initialized;
35
55
  private initPromise;
36
- constructor(userId: string);
37
- /**
38
- * Initialize libsodium and generate identity keys
39
- * RACE CONDITION SAFE: Returns same promise if called concurrently
40
- */
56
+ private identityStore;
57
+ private sessionStore;
58
+ constructor(userId: string, identityStore?: IIdentityStore, sessionStore?: ISessionStore);
41
59
  initialize(): Promise<void>;
42
- private _doInitialize;
43
- /**
44
- * Get serialized public keys for relay registration
45
- */
60
+ private _doInit;
46
61
  getPublicKeys(): SerializedPublicKeys;
47
- /**
48
- * Establish session with peer (X3DH handshake)
49
- */
50
- establishSessionWithPeer(peerId: string, peerPublicKeys: SerializedPublicKeys): Promise<void>;
51
- /**
52
- * Encrypt message for peer using Double Ratchet
53
- */
54
- encryptForPeer(peerId: string, plaintext: string): Promise<{
55
- ciphertext: Uint8Array;
56
- header: {
57
- publicKey: Uint8Array;
58
- nonce: Uint8Array;
59
- };
60
- }>;
61
- /**
62
- * Decrypt message from peer using Double Ratchet
63
- */
64
- decryptFromPeer(peerId: string, ciphertext: Uint8Array, header: {
65
- publicKey: Uint8Array;
66
- nonce: Uint8Array;
67
- }): Promise<string>;
68
- /**
69
- * Check if session exists with peer
70
- */
62
+ establishSession(peerId: string, peerPublicKeys: SerializedPublicKeys): Promise<void>;
63
+ establishSessionWithPeer(peerId: string, pk: SerializedPublicKeys): Promise<void>;
71
64
  hasSession(peerId: string): boolean;
72
- /**
73
- * Destroy all sessions (cleanup)
74
- */
75
- destroy(): void;
65
+ encryptForPeer(peerId: string, plaintext: string): {
66
+ ciphertext: string;
67
+ header: string;
68
+ };
69
+ decryptFromPeer(peerId: string, ciphertext: string, header: string): string;
70
+ forceRatchet(peerId: string): Promise<void>;
76
71
  }
@@ -1,175 +1,152 @@
1
1
  /**
2
2
  * STVOR Crypto Session Manager
3
- * Integrates X3DH + Double Ratchet from ratchet module
3
+ * Uses ONLY Node.js built-in crypto module zero external dependencies
4
4
  *
5
- * CRITICAL: Identity keys generated ONCE per userId
6
- * Currently in-memory only - keys lost on restart
7
- *
8
- * TODO: Add persistent storage (IndexedDB/Keychain)
9
- */
10
- import sodium from 'libsodium-wrappers';
11
- import { ensureSodiumReady } from './sodium-singleton.js';
12
- import { encryptMessage as ratchetEncrypt, decryptMessage as ratchetDecrypt, incrementMessageCounter, } from '../ratchet/index.js';
13
- /**
14
- * Manages cryptographic sessions for all peers
5
+ * Manages identity keys (IK + SPK), ECDSA signatures,
6
+ * X3DH session establishment, and Double Ratchet encrypt/decrypt.
15
7
  */
8
+ import { generateKeyPair, encryptMessage as ratchetEncrypt, decryptMessage as ratchetDecrypt, establishSession as ratchetEstablishSession, serializeSession, initializeCrypto, ecSign, ecVerify, } from '../ratchet/index.js';
9
+ /* ================================================================
10
+ * Helpers
11
+ * ================================================================ */
12
+ function toB64(buf) { return buf.toString('base64url'); }
13
+ function fromB64(s) { return Buffer.from(s, 'base64url'); }
14
+ /* ================================================================
15
+ * CryptoSessionManager
16
+ * ================================================================ */
16
17
  export class CryptoSessionManager {
17
- constructor(userId) {
18
+ constructor(userId, identityStore, sessionStore) {
18
19
  this.identityKeys = null;
19
20
  this.sessions = new Map();
20
21
  this.initialized = false;
21
22
  this.initPromise = null;
23
+ this.identityStore = null;
24
+ this.sessionStore = null;
22
25
  this.userId = userId;
26
+ this.identityStore = identityStore || null;
27
+ this.sessionStore = sessionStore || null;
23
28
  }
24
- /**
25
- * Initialize libsodium and generate identity keys
26
- * RACE CONDITION SAFE: Returns same promise if called concurrently
27
- */
29
+ /* ---- Initialisation ---- */
28
30
  async initialize() {
29
- // Already initialized
30
- if (this.initialized && this.identityKeys) {
31
+ if (this.initialized && this.identityKeys)
31
32
  return;
32
- }
33
- // Initialization in progress - return existing promise
34
- if (this.initPromise) {
33
+ if (this.initPromise)
35
34
  return this.initPromise;
36
- }
37
- // Start initialization
38
- this.initPromise = this._doInitialize();
35
+ this.initPromise = this._doInit();
39
36
  return this.initPromise;
40
37
  }
41
- async _doInitialize() {
42
- // Ensure libsodium ready (singleton - safe to call multiple times)
43
- await ensureSodiumReady();
44
- // CRITICAL: Check again after await (another call might have completed)
45
- if (this.initialized && this.identityKeys) {
46
- return;
47
- }
48
- // Generate long-term identity key pair (Ed25519 for signing)
49
- const identityKeyPair = sodium.crypto_sign_keypair();
50
- // Generate semi-ephemeral signed pre-key (X25519 for DH)
51
- const signedPreKeyPair = sodium.crypto_kx_keypair();
52
- // Generate pool of one-time pre-keys
53
- const oneTimePreKeys = [];
54
- for (let i = 0; i < 10; i++) {
55
- const keypair = sodium.crypto_kx_keypair();
56
- oneTimePreKeys.push(keypair.publicKey);
38
+ async _doInit() {
39
+ await initializeCrypto();
40
+ if (this.identityStore) {
41
+ try {
42
+ const stored = await this.identityStore.loadIdentityKeys(this.userId);
43
+ if (stored) {
44
+ this.identityKeys = {
45
+ identityKeyPair: {
46
+ publicKey: fromB64(stored.identityKeyPair.publicKey),
47
+ privateKey: fromB64(stored.identityKeyPair.privateKey),
48
+ },
49
+ signedPreKeyPair: {
50
+ publicKey: fromB64(stored.signedPreKeyPair.publicKey),
51
+ privateKey: fromB64(stored.signedPreKeyPair.privateKey),
52
+ },
53
+ signedPreKeySignature: fromB64(stored.signedPreKeySignature),
54
+ };
55
+ this.initialized = true;
56
+ return;
57
+ }
58
+ }
59
+ catch (e) {
60
+ console.warn('Failed to load identity keys:', e);
61
+ }
57
62
  }
63
+ const ik = generateKeyPair();
64
+ const spk = generateKeyPair();
65
+ const sig = ecSign(spk.publicKey, ik);
58
66
  this.identityKeys = {
59
- identityKeyPair: {
60
- publicKey: identityKeyPair.publicKey,
61
- privateKey: identityKeyPair.privateKey,
62
- },
63
- signedPreKeyPair: {
64
- publicKey: signedPreKeyPair.publicKey,
65
- privateKey: signedPreKeyPair.privateKey,
66
- },
67
- oneTimePreKeys,
67
+ identityKeyPair: ik,
68
+ signedPreKeyPair: spk,
69
+ signedPreKeySignature: sig,
68
70
  };
71
+ if (this.identityStore) {
72
+ try {
73
+ await this.identityStore.saveIdentityKeys(this.userId, {
74
+ identityKeyPair: { publicKey: toB64(ik.publicKey), privateKey: toB64(ik.privateKey) },
75
+ signedPreKeyPair: { publicKey: toB64(spk.publicKey), privateKey: toB64(spk.privateKey) },
76
+ signedPreKeySignature: toB64(sig),
77
+ });
78
+ }
79
+ catch (e) {
80
+ console.warn('Failed to save identity keys:', e);
81
+ }
82
+ }
69
83
  this.initialized = true;
70
- this.initPromise = null;
71
- console.log(`[Crypto] Identity keys generated for ${this.userId}`);
72
84
  }
73
- /**
74
- * Get serialized public keys for relay registration
75
- */
85
+ /* ---- Public keys ---- */
76
86
  getPublicKeys() {
77
- if (!this.identityKeys) {
78
- throw new Error('CryptoSessionManager not initialized');
79
- }
80
- // Sign the pre-key
81
- const signedPreKeySignature = sodium.crypto_sign_detached(this.identityKeys.signedPreKeyPair.publicKey, this.identityKeys.identityKeyPair.privateKey);
87
+ if (!this.identityKeys)
88
+ throw new Error('Not initialized');
82
89
  return {
83
- identityKey: sodium.to_base64(this.identityKeys.identityKeyPair.publicKey),
84
- signedPreKey: sodium.to_base64(this.identityKeys.signedPreKeyPair.publicKey),
85
- signedPreKeySignature: sodium.to_base64(signedPreKeySignature),
86
- oneTimePreKey: sodium.to_base64(this.identityKeys.oneTimePreKeys[0] || new Uint8Array(32)),
90
+ identityKey: toB64(this.identityKeys.identityKeyPair.publicKey),
91
+ signedPreKey: toB64(this.identityKeys.signedPreKeyPair.publicKey),
92
+ signedPreKeySignature: toB64(this.identityKeys.signedPreKeySignature),
93
+ oneTimePreKey: '',
87
94
  };
88
95
  }
89
- /**
90
- * Establish session with peer (X3DH handshake)
91
- */
92
- async establishSessionWithPeer(peerId, peerPublicKeys) {
93
- if (!this.identityKeys) {
94
- throw new Error('CryptoSessionManager not initialized');
95
- }
96
- // Skip if session already exists
97
- if (this.sessions.has(peerId)) {
98
- return;
99
- }
100
- // Deserialize peer's public keys
101
- const recipientIdentityKey = sodium.from_base64(peerPublicKeys.identityKey);
102
- const recipientSignedPreKey = sodium.from_base64(peerPublicKeys.signedPreKey);
103
- const recipientSPKSignature = sodium.from_base64(peerPublicKeys.signedPreKeySignature);
104
- const recipientOneTimePreKey = sodium.from_base64(peerPublicKeys.oneTimePreKey);
105
- // Verify SPK signature
106
- const isValid = sodium.crypto_sign_verify_detached(recipientSPKSignature, recipientSignedPreKey, recipientIdentityKey);
107
- if (!isValid) {
108
- throw new Error(`Invalid SPK signature for peer ${peerId}`);
96
+ /* ---- Session establishment ---- */
97
+ async establishSession(peerId, peerPublicKeys) {
98
+ if (!this.identityKeys)
99
+ throw new Error('Not initialized');
100
+ const peerIK = fromB64(peerPublicKeys.identityKey);
101
+ const peerSPK = fromB64(peerPublicKeys.signedPreKey);
102
+ const peerSig = peerPublicKeys.signedPreKeySignature
103
+ ? fromB64(peerPublicKeys.signedPreKeySignature)
104
+ : Buffer.alloc(0);
105
+ if (peerSig.length > 0 && !ecVerify(peerSPK, peerSig, peerIK)) {
106
+ throw new Error('Invalid signed pre-key signature — possible MITM attack');
109
107
  }
110
- // Perform X3DH to derive shared secret
111
- const dh1 = sodium.crypto_scalarmult(this.identityKeys.signedPreKeyPair.privateKey, recipientSignedPreKey);
112
- const dh2 = sodium.crypto_scalarmult(this.identityKeys.identityKeyPair.privateKey, recipientOneTimePreKey);
113
- const dh3 = sodium.crypto_scalarmult(this.identityKeys.signedPreKeyPair.privateKey, recipientOneTimePreKey);
114
- // Combine DH outputs
115
- const sharedSecret = sodium.crypto_generichash(32, new Uint8Array([...dh1, ...dh2, ...dh3]));
116
- // Derive root key
117
- const rootKey = sodium.crypto_generichash(32, new Uint8Array([
118
- ...sharedSecret,
119
- ...sodium.from_string('x3dh-root-key-v1'),
120
- ]));
121
- // Create initial session state
122
- const session = {
123
- identityKey: this.identityKeys.identityKeyPair.publicKey,
124
- signedPreKey: this.identityKeys.signedPreKeyPair.publicKey,
125
- oneTimePreKey: this.identityKeys.oneTimePreKeys[0] || new Uint8Array(32),
126
- rootKey,
127
- sendingChainKey: rootKey,
128
- receivingChainKey: rootKey,
129
- skippedMessageKeys: new Map(),
130
- isPostCompromise: false,
131
- };
108
+ const session = ratchetEstablishSession(this.identityKeys.identityKeyPair, this.identityKeys.signedPreKeyPair, peerIK, peerSPK);
132
109
  this.sessions.set(peerId, session);
133
- }
134
- /**
135
- * Encrypt message for peer using Double Ratchet
136
- */
137
- async encryptForPeer(peerId, plaintext) {
138
- const session = this.sessions.get(peerId);
139
- if (!session) {
140
- throw new Error(`No session with peer ${peerId}`);
110
+ if (this.sessionStore) {
111
+ try {
112
+ await this.sessionStore.saveSession(this.userId, peerId, serializeSession(session));
113
+ }
114
+ catch (e) {
115
+ console.warn('Failed to save session:', e);
116
+ }
141
117
  }
142
- // Encrypt using Double Ratchet
143
- const result = ratchetEncrypt(plaintext, session);
144
- // Enforce DH ratchet policy (Forward Secrecy + PCS)
145
- const recipientKey = session.identityKey;
146
- incrementMessageCounter(session, recipientKey);
147
- return result;
148
118
  }
149
- /**
150
- * Decrypt message from peer using Double Ratchet
151
- */
152
- async decryptFromPeer(peerId, ciphertext, header) {
153
- const session = this.sessions.get(peerId);
154
- if (!session) {
155
- throw new Error(`No session with peer ${peerId}`);
156
- }
157
- // Decrypt using Double Ratchet
158
- const plaintext = ratchetDecrypt(ciphertext, header, session);
159
- return plaintext;
119
+ async establishSessionWithPeer(peerId, pk) {
120
+ return this.establishSession(peerId, pk);
160
121
  }
161
- /**
162
- * Check if session exists with peer
163
- */
164
122
  hasSession(peerId) {
165
123
  return this.sessions.has(peerId);
166
124
  }
167
- /**
168
- * Destroy all sessions (cleanup)
169
- */
170
- destroy() {
171
- this.sessions.clear();
172
- this.identityKeys = null;
173
- this.initialized = false;
125
+ /* ---- Encrypt ---- */
126
+ encryptForPeer(peerId, plaintext) {
127
+ const session = this.sessions.get(peerId);
128
+ if (!session)
129
+ throw new Error('No session with peer');
130
+ const { ciphertext, header } = ratchetEncrypt(session, Buffer.from(plaintext, 'utf-8'));
131
+ return { ciphertext: toB64(ciphertext), header: toB64(header) };
132
+ }
133
+ /* ---- Decrypt ---- */
134
+ decryptFromPeer(peerId, ciphertext, header) {
135
+ const session = this.sessions.get(peerId);
136
+ if (!session)
137
+ throw new Error('No session with peer');
138
+ const pt = ratchetDecrypt(session, fromB64(ciphertext), fromB64(header));
139
+ return pt.toString('utf-8');
140
+ }
141
+ /* ---- Post-compromise ---- */
142
+ async forceRatchet(peerId) {
143
+ const session = this.sessions.get(peerId);
144
+ if (session) {
145
+ session.myRatchetKeyPair = generateKeyPair();
146
+ session.sendCount = 0;
147
+ session.recvCount = 0;
148
+ session.prevSendCount = 0;
149
+ session.isPostCompromise = true;
150
+ }
174
151
  }
175
152
  }
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ // Auto-generated CommonJS wrapper for facade/errors.js
4
+ // This allows `require('@stvor/sdk')` to work alongside ESM `import`.
5
+
6
+ const mod = require('module');
7
+ const url = require('url');
8
+
9
+ // Use dynamic import to load the ESM module
10
+ let _cached;
11
+ async function _load() {
12
+ if (!_cached) {
13
+ _cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
14
+ }
15
+ return _cached;
16
+ }
17
+
18
+ // For simple CJS usage, expose a promise-based loader
19
+ module.exports = new Proxy({ load: _load }, {
20
+ get(target, prop) {
21
+ if (prop === '__esModule') return true;
22
+ if (prop === 'then') return undefined; // prevent treating as thenable
23
+ if (prop === 'load') return _load;
24
+ if (prop === 'default') {
25
+ return _load().then(m => m.default);
26
+ }
27
+ return _load().then(m => m[prop]);
28
+ }
29
+ });
@@ -1,19 +1,36 @@
1
- export declare const Errors: {
1
+ /**
2
+ * STVOR DX Facade - Error Handling
3
+ */
4
+ export declare const ErrorCode: {
5
+ readonly AUTH_FAILED: "AUTH_FAILED";
2
6
  readonly INVALID_APP_TOKEN: "INVALID_APP_TOKEN";
3
- readonly INVALID_API_KEY: "INVALID_API_KEY";
4
7
  readonly RELAY_UNAVAILABLE: "RELAY_UNAVAILABLE";
5
- readonly DELIVERY_FAILED: "DELIVERY_FAILED";
6
8
  readonly RECIPIENT_NOT_FOUND: "RECIPIENT_NOT_FOUND";
7
9
  readonly RECIPIENT_TIMEOUT: "RECIPIENT_TIMEOUT";
8
- readonly MESSAGE_INTEGRITY_FAILED: "MESSAGE_INTEGRITY_FAILED";
9
- readonly RECEIVE_TIMEOUT: "RECEIVE_TIMEOUT";
10
- readonly RECEIVE_IN_PROGRESS: "RECEIVE_IN_PROGRESS";
11
- readonly NOT_CONNECTED: "NOT_CONNECTED";
10
+ readonly CLIENT_NOT_READY: "CLIENT_NOT_READY";
11
+ readonly DELIVERY_FAILED: "DELIVERY_FAILED";
12
+ readonly QUOTA_EXCEEDED: "QUOTA_EXCEEDED";
13
+ readonly RATE_LIMITED: "RATE_LIMITED";
12
14
  };
13
- export type ErrorCode = (typeof Errors)[keyof typeof Errors];
15
+ export type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];
14
16
  export declare class StvorError extends Error {
15
- code: ErrorCode;
16
- action?: string | undefined;
17
- retryable?: boolean | undefined;
18
- constructor(code: ErrorCode, message: string, action?: string | undefined, retryable?: boolean | undefined);
17
+ code: string;
18
+ action?: string;
19
+ retryable?: boolean;
20
+ constructor(code: string, message: string, action?: string, retryable?: boolean);
19
21
  }
22
+ export declare const Errors: {
23
+ authFailed(): StvorError;
24
+ invalidAppToken(): StvorError;
25
+ relayUnavailable(): StvorError;
26
+ recipientNotFound(userId: string): StvorError;
27
+ messageIntegrityFailed(): StvorError;
28
+ keystoreCorrupted(): StvorError;
29
+ deviceCompromised(): StvorError;
30
+ protocolMismatch(): StvorError;
31
+ recipientTimeout(userId: string, timeoutMs: number): StvorError;
32
+ clientNotReady(): StvorError;
33
+ deliveryFailed(recipientId: string): StvorError;
34
+ quotaExceeded: () => StvorError;
35
+ rateLimited: () => StvorError;
36
+ };
@@ -1,21 +1,62 @@
1
- export const Errors = {
1
+ /**
2
+ * STVOR DX Facade - Error Handling
3
+ */
4
+ export const ErrorCode = {
5
+ AUTH_FAILED: 'AUTH_FAILED',
2
6
  INVALID_APP_TOKEN: 'INVALID_APP_TOKEN',
3
- INVALID_API_KEY: 'INVALID_API_KEY',
4
7
  RELAY_UNAVAILABLE: 'RELAY_UNAVAILABLE',
5
- DELIVERY_FAILED: 'DELIVERY_FAILED',
6
8
  RECIPIENT_NOT_FOUND: 'RECIPIENT_NOT_FOUND',
7
9
  RECIPIENT_TIMEOUT: 'RECIPIENT_TIMEOUT',
8
- MESSAGE_INTEGRITY_FAILED: 'MESSAGE_INTEGRITY_FAILED',
9
- RECEIVE_TIMEOUT: 'RECEIVE_TIMEOUT',
10
- RECEIVE_IN_PROGRESS: 'RECEIVE_IN_PROGRESS',
11
- NOT_CONNECTED: 'NOT_CONNECTED',
10
+ CLIENT_NOT_READY: 'CLIENT_NOT_READY',
11
+ DELIVERY_FAILED: 'DELIVERY_FAILED',
12
+ QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',
13
+ RATE_LIMITED: 'RATE_LIMITED',
12
14
  };
13
15
  export class StvorError extends Error {
14
16
  constructor(code, message, action, retryable) {
15
17
  super(message);
18
+ this.name = 'StvorError';
16
19
  this.code = code;
17
20
  this.action = action;
18
21
  this.retryable = retryable;
19
- this.name = 'StvorError';
20
22
  }
21
23
  }
24
+ export const Errors = {
25
+ authFailed() {
26
+ return new StvorError(ErrorCode.AUTH_FAILED, 'The AppToken is invalid or has been revoked.', 'Check your dashboard and regenerate a new AppToken.', false);
27
+ },
28
+ invalidAppToken() {
29
+ return new StvorError(ErrorCode.INVALID_APP_TOKEN, 'Invalid AppToken format. AppToken must start with "stvor_".', 'Get your AppToken from the developer dashboard.', false);
30
+ },
31
+ relayUnavailable() {
32
+ return new StvorError(ErrorCode.RELAY_UNAVAILABLE, 'Cannot connect to STVOR relay server.', 'Check your internet connection.', true);
33
+ },
34
+ recipientNotFound(userId) {
35
+ return new StvorError(ErrorCode.RECIPIENT_NOT_FOUND, `User "${userId}" not found. They may not have registered with STVOR.`, 'Ask the recipient to initialize STVOR first, or verify the userId is correct.', false);
36
+ },
37
+ messageIntegrityFailed() {
38
+ return new StvorError(ErrorCode.DELIVERY_FAILED, 'Message integrity check failed or decryption failed.', 'Request the message again from the sender.', false);
39
+ },
40
+ keystoreCorrupted() {
41
+ return new StvorError(ErrorCode.DELIVERY_FAILED, 'Local keystore error (not supported in v0.1).', 'Investigate local storage configuration.', false);
42
+ },
43
+ deviceCompromised() {
44
+ return new StvorError(ErrorCode.DELIVERY_FAILED, 'Device compromise detected (placeholder).', 'Investigate and revoke credentials.', false);
45
+ },
46
+ protocolMismatch() {
47
+ return new StvorError(ErrorCode.DELIVERY_FAILED, 'Protocol version mismatch.', 'Update the SDK to the latest version.', false);
48
+ },
49
+ recipientTimeout(userId, timeoutMs) {
50
+ return new StvorError(ErrorCode.RECIPIENT_TIMEOUT, `Timed out waiting for user "${userId}" after ${timeoutMs}ms. ` +
51
+ `The user may not have registered with STVOR yet.`, 'Ensure the recipient has called connect() and is online, or increase the timeout.', true);
52
+ },
53
+ clientNotReady() {
54
+ return new StvorError(ErrorCode.CLIENT_NOT_READY, 'Client is not ready. Call connect() first and await it.', 'Make sure to await app.connect() before sending messages.', false);
55
+ },
56
+ deliveryFailed(recipientId) {
57
+ return new StvorError(ErrorCode.DELIVERY_FAILED, `Failed to deliver message to ${recipientId}.`, 'Check that the recipient exists and try again.', true);
58
+ },
59
+ quotaExceeded: () => new StvorError(ErrorCode.QUOTA_EXCEEDED, 'Message quota exceeded for this AppToken.', 'UPGRADE_PLAN', false),
60
+ rateLimited: () => new StvorError(ErrorCode.RATE_LIMITED, 'Rate limit exceeded. Please try again later.', 'WAIT', true),
61
+ // receive()/timeout APIs are not part of SDK v0.1 facade; use onMessage().
62
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ // Auto-generated CommonJS wrapper for facade/index.js
4
+ // This allows `require('@stvor/sdk')` to work alongside ESM `import`.
5
+
6
+ const mod = require('module');
7
+ const url = require('url');
8
+
9
+ // Use dynamic import to load the ESM module
10
+ let _cached;
11
+ async function _load() {
12
+ if (!_cached) {
13
+ _cached = await import(url.pathToFileURL(__filename.replace(/\.cjs$/, '.js')).href);
14
+ }
15
+ return _cached;
16
+ }
17
+
18
+ // For simple CJS usage, expose a promise-based loader
19
+ module.exports = new Proxy({ load: _load }, {
20
+ get(target, prop) {
21
+ if (prop === '__esModule') return true;
22
+ if (prop === 'then') return undefined; // prevent treating as thenable
23
+ if (prop === 'load') return _load;
24
+ if (prop === 'default') {
25
+ return _load().then(m => m.default);
26
+ }
27
+ return _load().then(m => m[prop]);
28
+ }
29
+ });
@@ -1,8 +1,27 @@
1
- export * from './app.js';
2
- export * from './errors.js';
3
- export * from './types.js';
4
- export type { DecryptedMessage, SealedPayload } from './types.js';
5
- export type { StvorAppConfig, AppToken, UserId, MessageContent } from './types.js';
6
- export { StvorError } from './errors.js';
7
- export { StvorApp, StvorFacadeClient, Stvor, init, createApp } from './app.js';
8
- export { ErrorCode as STVOR_ERRORS } from './errors.js';
1
+ /**
2
+ * STVOR DX Facade SDK
3
+ * High-level developer experience layer for STVOR E2E encryption
4
+ *
5
+ * Design goals:
6
+ * - Minimal API surface
7
+ * - Zero crypto knowledge required
8
+ * - Secure by default
9
+ * - Opinionated (no configuration)
10
+ */
11
+ export type { DecryptedMessage, SealedPayload } from './app';
12
+ export type { StvorAppConfig, AppToken, UserId, MessageContent } from './types';
13
+ export type { ErrorCode } from './errors';
14
+ export type { Metrics, SignedMetrics } from './metrics-engine';
15
+ export { StvorError } from './errors';
16
+ export { StvorApp, StvorFacadeClient, Stvor, init, createApp } from './app';
17
+ export { ErrorCode as STVOR_ERRORS } from './errors';
18
+ export { verifyMetricsSignature, MetricsEngine } from './metrics-engine';
19
+ export { CryptoSessionManager } from './crypto-session';
20
+ export type { IdentityKeys, SerializedPublicKeys, IIdentityStore, ISessionStore } from './crypto-session';
21
+ export { LocalStorageIdentityStore, LocalStorageSessionStore } from './local-storage-identity-store';
22
+ export { isReplay, validateMessage, validateMessageWithNonce, getCacheStats, cleanupExpiredNonces, initializeReplayProtection, startAutoCleanup, stopAutoCleanup, } from './replay-manager';
23
+ export type { IReplayCache } from './replay-manager';
24
+ export { RedisReplayCache } from './redis-replay-cache';
25
+ export type { RedisClient, RedisReplayCacheConfig } from './redis-replay-cache';
26
+ export { generateFingerprint, storeFingerprint, verifyFingerprint, isFingerprintTrusted, getFingerprint, revokeTrust, trustNewFingerprint, listTrustedFingerprints, getFingerprintInfo, initializeTofu, } from './tofu-manager';
27
+ export type { ITofuStore } from './tofu-manager';