chatly-sdk 1.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/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # chatly-sdk
2
+
3
+ Production-ready end-to-end encrypted chat SDK with WhatsApp-style features.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **End-to-End Encryption**
8
+ - ECDH key exchange (P-256 curve)
9
+ - AES-GCM message encryption
10
+ - Per-user identity keys
11
+ - Per-session ephemeral keys
12
+ - Group shared keys
13
+
14
+ - 💬 **1:1 Messaging**
15
+ - Secure key exchange
16
+ - Encrypt/decrypt functions
17
+ - Message payload schemas
18
+
19
+ - 👥 **Group Messaging**
20
+ - Create groups
21
+ - Add/remove members
22
+ - Per-group shared key
23
+ - Group message encryption
24
+ - Message ordering & timestamps
25
+
26
+ - 🗄️ **Database Integration**
27
+ - Adapter pattern for flexible storage
28
+ - In-memory implementations included
29
+ - UserStoreAdapter, MessageStoreAdapter, GroupStoreAdapter
30
+
31
+ - 🌐 **Networking Layer**
32
+ - Transport adapter interface
33
+ - In-memory transport for testing
34
+ - Easy integration with your own WebSocket server
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install chatly-sdk
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ### Basic Setup
45
+
46
+ ```typescript
47
+ import { ChatSDK, InMemoryUserStore, InMemoryMessageStore, InMemoryGroupStore } from 'chatly-sdk';
48
+
49
+ const sdk = new ChatSDK({
50
+ userStore: new InMemoryUserStore(),
51
+ messageStore: new InMemoryMessageStore(),
52
+ groupStore: new InMemoryGroupStore(),
53
+ });
54
+
55
+ // Create a user
56
+ const user = await sdk.createUser('alice');
57
+ sdk.setCurrentUser(user);
58
+ ```
59
+
60
+ ### 1:1 Chat Example
61
+
62
+ ```typescript
63
+ import { ChatSDK, InMemoryUserStore, InMemoryMessageStore, InMemoryGroupStore } from 'chatly-sdk';
64
+
65
+ const sdk = new ChatSDK({
66
+ userStore: new InMemoryUserStore(),
67
+ messageStore: new InMemoryMessageStore(),
68
+ groupStore: new InMemoryGroupStore(),
69
+ });
70
+
71
+ // Create two users
72
+ const alice = await sdk.createUser('alice');
73
+ const bob = await sdk.createUser('bob');
74
+
75
+ // Start a chat session
76
+ sdk.setCurrentUser(alice);
77
+ const session = await sdk.startSession(alice, bob);
78
+
79
+ // Send a message
80
+ const message = await sdk.sendMessage(session, 'Hello Bob!');
81
+
82
+ // Bob receives and decrypts
83
+ sdk.setCurrentUser(bob);
84
+ const messages = await sdk.getMessagesForUser(bob.id);
85
+ for (const msg of messages) {
86
+ const decrypted = await sdk.decryptMessage(msg, bob);
87
+ console.log(decrypted); // "Hello Bob!"
88
+ }
89
+ ```
90
+
91
+ ### Group Chat Example
92
+
93
+ ```typescript
94
+ // Create users
95
+ const alice = await sdk.createUser('alice');
96
+ const bob = await sdk.createUser('bob');
97
+ const charlie = await sdk.createUser('charlie');
98
+
99
+ // Create a group
100
+ const group = await sdk.createGroup('Project Team', [alice, bob, charlie]);
101
+
102
+ // Send a group message
103
+ sdk.setCurrentUser(alice);
104
+ const message = await sdk.sendMessage(group, 'Hello team!');
105
+
106
+ // Members can read the message
107
+ sdk.setCurrentUser(bob);
108
+ const groupMessages = await sdk.getMessagesForGroup(group.group.id);
109
+ for (const msg of groupMessages) {
110
+ const decrypted = await sdk.decryptMessage(msg, bob);
111
+ console.log(decrypted);
112
+ }
113
+ ```
114
+
115
+ ### Save and Load User
116
+
117
+ ```typescript
118
+ // Create and save user
119
+ const user = await sdk.createUser('john_doe');
120
+ const storedUser = {
121
+ ...user,
122
+ createdAt: Date.now(),
123
+ };
124
+ await sdk.config.userStore.save(storedUser);
125
+
126
+ // Later, load user
127
+ const loadedUser = await sdk.importUser(storedUser);
128
+ sdk.setCurrentUser(loadedUser);
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### ChatSDK
134
+
135
+ Main SDK class for managing chat functionality.
136
+
137
+ #### Constructor
138
+
139
+ ```typescript
140
+ new ChatSDK(config: {
141
+ userStore: UserStoreAdapter;
142
+ messageStore: MessageStoreAdapter;
143
+ groupStore: GroupStoreAdapter;
144
+ transport?: TransportAdapter;
145
+ })
146
+ ```
147
+
148
+ #### Methods
149
+
150
+ - `createUser(username: string): Promise<User>` - Create a new user with generated keys
151
+ - `importUser(userData: StoredUser): Promise<User>` - Import an existing user
152
+ - `setCurrentUser(user: User): void` - Set the active user
153
+ - `getCurrentUser(): User | null` - Get the current user
154
+ - `startSession(userA: User, userB: User): Promise<ChatSession>` - Start a 1:1 chat
155
+ - `createGroup(name: string, members: User[]): Promise<GroupSession>` - Create a group
156
+ - `loadGroup(id: string): Promise<GroupSession>` - Load an existing group
157
+ - `sendMessage(session: ChatSession | GroupSession, plaintext: string): Promise<Message>` - Send a message
158
+ - `decryptMessage(message: Message, user: User): Promise<string>` - Decrypt a message
159
+ - `getMessagesForUser(userId: string): Promise<Message[]>` - Get user's messages
160
+ - `getMessagesForGroup(groupId: string): Promise<Message[]>` - Get group messages
161
+
162
+ ### Adapters
163
+
164
+ #### UserStoreAdapter
165
+
166
+ ```typescript
167
+ interface UserStoreAdapter {
168
+ create(user: User): Promise<User>;
169
+ findById(id: string): Promise<User | undefined>;
170
+ save(user: StoredUser): Promise<void>;
171
+ list(): Promise<User[]>;
172
+ }
173
+ ```
174
+
175
+ #### MessageStoreAdapter
176
+
177
+ ```typescript
178
+ interface MessageStoreAdapter {
179
+ create(message: Message): Promise<Message>;
180
+ listByUser(userId: string): Promise<Message[]>;
181
+ listByGroup(groupId: string): Promise<Message[]>;
182
+ }
183
+ ```
184
+
185
+ #### GroupStoreAdapter
186
+
187
+ ```typescript
188
+ interface GroupStoreAdapter {
189
+ create(group: Group): Promise<Group>;
190
+ findById(id: string): Promise<Group | undefined>;
191
+ list(): Promise<Group[]>;
192
+ }
193
+ ```
194
+
195
+ #### TransportAdapter
196
+
197
+ ```typescript
198
+ interface TransportAdapter {
199
+ connect(userId: string): Promise<void>;
200
+ send(message: Message): Promise<void>;
201
+ onMessage(handler: (message: Message) => void): void;
202
+ }
203
+ ```
204
+
205
+ ## Extending the SDK
206
+
207
+ ### Custom Store Adapters
208
+
209
+ Implement the adapter interfaces to use your own database:
210
+
211
+ ```typescript
212
+ import { UserStoreAdapter, User } from 'chatly-sdk';
213
+
214
+ class PostgreSQLUserStore implements UserStoreAdapter {
215
+ async create(user: User): Promise<User> {
216
+ // Save to PostgreSQL
217
+ const result = await db.query('INSERT INTO users ...');
218
+ return result.rows[0];
219
+ }
220
+
221
+ async findById(id: string): Promise<User | undefined> {
222
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [id]);
223
+ return result.rows[0];
224
+ }
225
+
226
+ // ... implement other methods
227
+ }
228
+ ```
229
+
230
+ ### Custom Transport
231
+
232
+ Implement `TransportAdapter` to use your own WebSocket server:
233
+
234
+ ```typescript
235
+ import { TransportAdapter, Message } from 'chatly-sdk';
236
+
237
+ class WebSocketTransport implements TransportAdapter {
238
+ private ws: WebSocket;
239
+
240
+ async connect(userId: string): Promise<void> {
241
+ this.ws = new WebSocket(`wss://your-server.com/ws?userId=${userId}`);
242
+ }
243
+
244
+ async send(message: Message): Promise<void> {
245
+ this.ws.send(JSON.stringify(message));
246
+ }
247
+
248
+ onMessage(handler: (message: Message) => void): void {
249
+ this.ws.on('message', (data) => {
250
+ handler(JSON.parse(data.toString()));
251
+ });
252
+ }
253
+ }
254
+ ```
255
+
256
+ ## Cryptography
257
+
258
+ The SDK uses Node.js built-in `crypto` module:
259
+
260
+ - **Key Exchange**: ECDH with P-256 (prime256v1) curve
261
+ - **Encryption**: AES-256-GCM
262
+ - **Key Derivation**: PBKDF2 with SHA-256
263
+ - **Key Storage**: Base64-encoded strings
264
+
265
+ ### Security Notes
266
+
267
+ - Identity keys are long-term keys for user authentication
268
+ - Ephemeral keys are generated per-session (future: Double Ratchet support)
269
+ - Group keys are derived deterministically from group ID
270
+ - All messages use unique IVs for encryption
271
+
272
+ ## Examples
273
+
274
+ See the `examples/` directory for complete examples:
275
+
276
+ - `oneToOne.ts` - 1:1 chat between two users
277
+ - `groupChat.ts` - Group chat with multiple members
278
+ - `saveLoadUser.ts` - Save and load user data
279
+
280
+ Run examples:
281
+
282
+ ```bash
283
+ npm run build
284
+ node dist/examples/oneToOne.js
285
+ ```
286
+
287
+ ## Building
288
+
289
+ ```bash
290
+ npm run build
291
+ ```
292
+
293
+ This generates:
294
+ - `dist/index.js` - ES module bundle
295
+ - `dist/index.d.ts` - TypeScript definitions
296
+
297
+ ## Development
298
+
299
+ ```bash
300
+ # Install dependencies
301
+ npm install
302
+
303
+ # Run tests
304
+ npm test
305
+
306
+ # Build
307
+ npm run build
308
+ ```
309
+
310
+ ## License
311
+
312
+ ISC
@@ -0,0 +1,188 @@
1
+ interface User {
2
+ id: string;
3
+ username: string;
4
+ identityKey: string;
5
+ publicKey: string;
6
+ privateKey: string;
7
+ }
8
+ interface StoredUser extends User {
9
+ createdAt: number;
10
+ }
11
+
12
+ type MessageType = "text" | "system";
13
+ interface Message {
14
+ id: string;
15
+ senderId: string;
16
+ receiverId?: string;
17
+ groupId?: string;
18
+ ciphertext: string;
19
+ iv: string;
20
+ timestamp: number;
21
+ type: MessageType;
22
+ }
23
+
24
+ interface Group {
25
+ id: string;
26
+ name: string;
27
+ members: User[];
28
+ createdAt: number;
29
+ }
30
+
31
+ interface UserStoreAdapter {
32
+ create(user: User): Promise<User>;
33
+ findById(id: string): Promise<User | undefined>;
34
+ save(user: StoredUser): Promise<void>;
35
+ list(): Promise<User[]>;
36
+ }
37
+ interface MessageStoreAdapter {
38
+ create(message: Message): Promise<Message>;
39
+ listByUser(userId: string): Promise<Message[]>;
40
+ listByGroup(groupId: string): Promise<Message[]>;
41
+ }
42
+ interface GroupStoreAdapter {
43
+ create(group: Group): Promise<Group>;
44
+ findById(id: string): Promise<Group | undefined>;
45
+ list(): Promise<Group[]>;
46
+ }
47
+
48
+ interface TransportAdapter {
49
+ connect(userId: string): Promise<void>;
50
+ send(message: Message): Promise<void>;
51
+ onMessage(handler: (message: Message) => void): void;
52
+ }
53
+
54
+ declare class ChatSession {
55
+ readonly id: string;
56
+ readonly userA: User;
57
+ readonly userB: User;
58
+ private sharedSecret;
59
+ private ephemeralKeyPair;
60
+ constructor(id: string, userA: User, userB: User);
61
+ /**
62
+ * Initialize the session by deriving the shared secret
63
+ * ECDH is commutative, so we can use either user's keys
64
+ */
65
+ initialize(): Promise<void>;
66
+ /**
67
+ * Initialize from a specific user's perspective (useful when decrypting)
68
+ */
69
+ initializeForUser(user: User): Promise<void>;
70
+ /**
71
+ * Encrypt a message for this session
72
+ */
73
+ encrypt(plaintext: string, senderId: string): Promise<Message>;
74
+ /**
75
+ * Decrypt a message in this session
76
+ */
77
+ decrypt(message: Message, user: User): Promise<string>;
78
+ }
79
+
80
+ declare class GroupSession {
81
+ readonly group: Group;
82
+ private groupKey;
83
+ constructor(group: Group);
84
+ /**
85
+ * Initialize the session by deriving the group key
86
+ */
87
+ initialize(): Promise<void>;
88
+ /**
89
+ * Encrypt a message for this group
90
+ */
91
+ encrypt(plaintext: string, senderId: string): Promise<Message>;
92
+ /**
93
+ * Decrypt a message in this group
94
+ */
95
+ decrypt(message: Message): Promise<string>;
96
+ }
97
+
98
+ declare class InMemoryUserStore implements UserStoreAdapter {
99
+ private users;
100
+ create(user: User): Promise<User>;
101
+ findById(id: string): Promise<User | undefined>;
102
+ save(user: StoredUser): Promise<void>;
103
+ list(): Promise<User[]>;
104
+ }
105
+
106
+ declare class InMemoryMessageStore implements MessageStoreAdapter {
107
+ private messages;
108
+ create(message: Message): Promise<Message>;
109
+ listByUser(userId: string): Promise<Message[]>;
110
+ listByGroup(groupId: string): Promise<Message[]>;
111
+ }
112
+
113
+ declare class InMemoryGroupStore implements GroupStoreAdapter {
114
+ private groups;
115
+ create(group: Group): Promise<Group>;
116
+ findById(id: string): Promise<Group | undefined>;
117
+ list(): Promise<Group[]>;
118
+ }
119
+
120
+ type MessageHandler = (message: Message) => void;
121
+ declare class InMemoryTransport implements TransportAdapter {
122
+ private handler?;
123
+ private connected;
124
+ connect(_userId: string): Promise<void>;
125
+ send(message: Message): Promise<void>;
126
+ onMessage(handler: MessageHandler): void;
127
+ }
128
+
129
+ interface ChatSDKConfig {
130
+ userStore: UserStoreAdapter;
131
+ messageStore: MessageStoreAdapter;
132
+ groupStore: GroupStoreAdapter;
133
+ transport?: TransportAdapter;
134
+ }
135
+ /**
136
+ * Main ChatSDK class - production-ready WhatsApp-style chat SDK
137
+ */
138
+ declare class ChatSDK {
139
+ private config;
140
+ private currentUser;
141
+ constructor(config: ChatSDKConfig);
142
+ /**
143
+ * Create a new user with generated identity keys
144
+ */
145
+ createUser(username: string): Promise<User>;
146
+ /**
147
+ * Import an existing user from stored data
148
+ */
149
+ importUser(userData: StoredUser): Promise<User>;
150
+ /**
151
+ * Set the current active user
152
+ */
153
+ setCurrentUser(user: User): void;
154
+ /**
155
+ * Get the current active user
156
+ */
157
+ getCurrentUser(): User | null;
158
+ /**
159
+ * Start a 1:1 chat session between two users
160
+ */
161
+ startSession(userA: User, userB: User): Promise<ChatSession>;
162
+ /**
163
+ * Create a new group with members
164
+ */
165
+ createGroup(name: string, members: User[]): Promise<GroupSession>;
166
+ /**
167
+ * Load an existing group by ID
168
+ */
169
+ loadGroup(id: string): Promise<GroupSession>;
170
+ /**
171
+ * Send a message in a chat session (1:1 or group)
172
+ */
173
+ sendMessage(session: ChatSession | GroupSession, plaintext: string): Promise<Message>;
174
+ /**
175
+ * Decrypt a message
176
+ */
177
+ decryptMessage(message: Message, user: User): Promise<string>;
178
+ /**
179
+ * Get messages for a user
180
+ */
181
+ getMessagesForUser(userId: string): Promise<Message[]>;
182
+ /**
183
+ * Get messages for a group
184
+ */
185
+ getMessagesForGroup(groupId: string): Promise<Message[]>;
186
+ }
187
+
188
+ export { ChatSDK, type ChatSDKConfig, ChatSession, type Group, GroupSession, type GroupStoreAdapter, InMemoryGroupStore, InMemoryMessageStore, InMemoryTransport, InMemoryUserStore, type Message, type MessageStoreAdapter, type MessageType, type StoredUser, type TransportAdapter, type User, type UserStoreAdapter };