@stvor/sdk 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/index.mdx +649 -0
- package/example.ts +64 -0
- package/facade/app.ts +331 -0
- package/facade/errors.ts +149 -0
- package/facade/index.ts +21 -0
- package/facade/relay-client.ts +173 -0
- package/facade/types.ts +65 -0
- package/index.ts +9 -0
- package/legacy.ts +158 -0
- package/package.json +34 -0
package/facade/types.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR DX Facade - Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AppToken from developer dashboard
|
|
7
|
+
* Replaces "API key" terminology for better DX
|
|
8
|
+
*/
|
|
9
|
+
export type AppToken = string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for SDK initialization
|
|
13
|
+
*/
|
|
14
|
+
export interface StvorAppConfig {
|
|
15
|
+
/** AppToken from STVOR developer dashboard */
|
|
16
|
+
appToken: string;
|
|
17
|
+
|
|
18
|
+
/** Relay server URL (optional, defaults to stvor.io) */
|
|
19
|
+
relayUrl?: string;
|
|
20
|
+
|
|
21
|
+
/** Connection timeout in ms (optional, default 10000) */
|
|
22
|
+
timeout?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* User identifier in your application
|
|
27
|
+
* Can be email, username, or UUID
|
|
28
|
+
*/
|
|
29
|
+
export type UserId = string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Message content - text or binary data
|
|
33
|
+
*/
|
|
34
|
+
export type MessageContent = string | Uint8Array;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Result of receiving a decrypted message
|
|
38
|
+
*/
|
|
39
|
+
export interface DecryptedMessage {
|
|
40
|
+
/** Unique message identifier */
|
|
41
|
+
id: string;
|
|
42
|
+
|
|
43
|
+
/** Sender's user ID */
|
|
44
|
+
senderId: UserId;
|
|
45
|
+
|
|
46
|
+
/** Decrypted content */
|
|
47
|
+
content: MessageContent;
|
|
48
|
+
|
|
49
|
+
/** Timestamp when message was sent */
|
|
50
|
+
timestamp: Date;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sealed payload for encrypting files/binary data
|
|
55
|
+
*/
|
|
56
|
+
export interface SealedPayload {
|
|
57
|
+
/** Encrypted ciphertext */
|
|
58
|
+
ciphertext: Uint8Array;
|
|
59
|
+
|
|
60
|
+
/** Nonce used for encryption */
|
|
61
|
+
nonce: Uint8Array;
|
|
62
|
+
|
|
63
|
+
/** Recipient user ID this was sealed for */
|
|
64
|
+
recipientId: UserId;
|
|
65
|
+
}
|
package/index.ts
ADDED
package/legacy.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STVOR SDK - Legacy Core API
|
|
3
|
+
* Kept for backwards compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface StvorConfig {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
serverUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Peer {
|
|
12
|
+
id: string;
|
|
13
|
+
publicKey: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface EncryptedMessage {
|
|
17
|
+
ciphertext: string;
|
|
18
|
+
nonce: string;
|
|
19
|
+
from: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class StvorClient {
|
|
23
|
+
private config: StvorConfig;
|
|
24
|
+
private myKeyPair: CryptoKeyPair | null = null;
|
|
25
|
+
private myId: string = '';
|
|
26
|
+
private peers: Map<string, CryptoKey> = new Map();
|
|
27
|
+
|
|
28
|
+
constructor(config: StvorConfig) {
|
|
29
|
+
this.config = {
|
|
30
|
+
serverUrl: 'http://localhost:3001',
|
|
31
|
+
...config,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async ready(): Promise<void> {
|
|
36
|
+
if (!this.config.apiKey) {
|
|
37
|
+
throw new Error('API key is required');
|
|
38
|
+
}
|
|
39
|
+
this.myKeyPair = await crypto.subtle.generateKey(
|
|
40
|
+
{ name: 'ECDH', namedCurve: 'P-256' },
|
|
41
|
+
true,
|
|
42
|
+
['deriveKey', 'deriveBits']
|
|
43
|
+
);
|
|
44
|
+
this.myId = this.config.apiKey.substring(0, 8);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async createPeer(name: string): Promise<Peer> {
|
|
48
|
+
if (!this.myKeyPair) throw new Error('Call ready() first');
|
|
49
|
+
|
|
50
|
+
const publicKey = await crypto.subtle.exportKey('jwk', this.myKeyPair.publicKey);
|
|
51
|
+
|
|
52
|
+
const res = await fetch(`${this.config.serverUrl}/register`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({ user_id: name, publicKey }),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const err = await res.json();
|
|
60
|
+
throw new Error(`Registration failed: ${JSON.stringify(err)}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { id: name, publicKey };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async send({ to, message }: { to: string; message: string }): Promise<EncryptedMessage> {
|
|
67
|
+
if (!this.myKeyPair) throw new Error('Call ready() first');
|
|
68
|
+
|
|
69
|
+
let recipientKey = this.peers.get(to);
|
|
70
|
+
if (!recipientKey) {
|
|
71
|
+
const res = await fetch(`${this.config.serverUrl}/public-key/${to}`);
|
|
72
|
+
if (!res.ok) throw new Error(`Peer ${to} not found`);
|
|
73
|
+
const { publicKey } = await res.json();
|
|
74
|
+
recipientKey = await crypto.subtle.importKey(
|
|
75
|
+
'jwk',
|
|
76
|
+
publicKey,
|
|
77
|
+
{ name: 'ECDH', namedCurve: 'P-256' },
|
|
78
|
+
false,
|
|
79
|
+
[]
|
|
80
|
+
);
|
|
81
|
+
this.peers.set(to, recipientKey);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sharedKey = await crypto.subtle.deriveKey(
|
|
85
|
+
{ name: 'ECDH', public: recipientKey },
|
|
86
|
+
this.myKeyPair.privateKey,
|
|
87
|
+
{ name: 'AES-GCM', length: 256 },
|
|
88
|
+
false,
|
|
89
|
+
['encrypt', 'decrypt']
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
93
|
+
const encoder = new TextEncoder();
|
|
94
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
95
|
+
{ name: 'AES-GCM', iv },
|
|
96
|
+
sharedKey,
|
|
97
|
+
encoder.encode(message)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const sendRes = await fetch(`${this.config.serverUrl}/message`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'Content-Type': 'application/json' },
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
from: this.myId,
|
|
105
|
+
to,
|
|
106
|
+
ciphertext: Buffer.from(encrypted).toString('base64'),
|
|
107
|
+
nonce: Buffer.from(iv).toString('base64'),
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!sendRes.ok) {
|
|
112
|
+
throw new Error('Failed to send message');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
ciphertext: Buffer.from(encrypted).toString('base64'),
|
|
117
|
+
nonce: Buffer.from(iv).toString('base64'),
|
|
118
|
+
from: this.myId
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async receive(encrypted: EncryptedMessage): Promise<string> {
|
|
123
|
+
if (!this.myKeyPair) throw new Error('Call ready() first');
|
|
124
|
+
|
|
125
|
+
let senderKey = this.peers.get(encrypted.from);
|
|
126
|
+
if (!senderKey) {
|
|
127
|
+
const res = await fetch(`${this.config.serverUrl}/public-key/${encrypted.from}`);
|
|
128
|
+
if (!res.ok) throw new Error(`Sender ${encrypted.from} not found`);
|
|
129
|
+
const { publicKey } = await res.json();
|
|
130
|
+
senderKey = await crypto.subtle.importKey(
|
|
131
|
+
'jwk',
|
|
132
|
+
publicKey,
|
|
133
|
+
{ name: 'ECDH', namedCurve: 'P-256' },
|
|
134
|
+
false,
|
|
135
|
+
[]
|
|
136
|
+
);
|
|
137
|
+
this.peers.set(encrypted.from, senderKey);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const sharedKey = await crypto.subtle.deriveKey(
|
|
141
|
+
{ name: 'ECDH', public: senderKey },
|
|
142
|
+
this.myKeyPair.privateKey,
|
|
143
|
+
{ name: 'AES-GCM', length: 256 },
|
|
144
|
+
false,
|
|
145
|
+
['encrypt', 'decrypt']
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const iv = Buffer.from(encrypted.nonce, 'base64');
|
|
149
|
+
const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');
|
|
150
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
151
|
+
{ name: 'AES-GCM', iv },
|
|
152
|
+
sharedKey,
|
|
153
|
+
ciphertext
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return new TextDecoder().decode(decrypted);
|
|
157
|
+
}
|
|
158
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stvor/sdk",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Stvor - is a security infrastructure for the quantum era.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"types": "index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.ts",
|
|
11
|
+
"import": "./index.ts"
|
|
12
|
+
},
|
|
13
|
+
"./facade": {
|
|
14
|
+
"types": "./facade/index.ts",
|
|
15
|
+
"import": "./facade/index.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"e2ee",
|
|
20
|
+
"encryption",
|
|
21
|
+
"security",
|
|
22
|
+
"cryptography",
|
|
23
|
+
"end-to-end"
|
|
24
|
+
],
|
|
25
|
+
"author": "Stvor",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18.0.0"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/stvor/sdk/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://stvor.xyz"
|
|
34
|
+
}
|