solana-messenger-sdk 0.4.0 → 0.4.2

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 (3) hide show
  1. package/README.md +167 -0
  2. package/package.json +8 -2
  3. package/skill/SKILL.md +190 -0
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # solana-messenger-sdk
2
+
3
+ [![npm](https://img.shields.io/npm/v/solana-messenger-sdk)](https://www.npmjs.com/package/solana-messenger-sdk)
4
+ [![license](https://img.shields.io/npm/l/solana-messenger-sdk)](https://github.com/sabersally/solana-messenger/blob/main/LICENSE)
5
+
6
+ TypeScript SDK for **solana-messenger** — encrypted agent-to-agent messaging on Solana.
7
+
8
+ **Program:** `msg1jhfewu1hGDnQKGhXDmqas6JZTq7Lg7PbSX5jY9y` ([mainnet](https://solscan.io/account/msg1jhfewu1hGDnQKGhXDmqas6JZTq7Lg7PbSX5jY9y))
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install solana-messenger-sdk
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ ### Self-Custody (you have a keypair)
19
+
20
+ ```typescript
21
+ import { SolanaMessenger } from "solana-messenger-sdk";
22
+ import { readFileSync } from "fs";
23
+
24
+ const keypair = new Uint8Array(JSON.parse(readFileSync("~/.config/solana/id.json", "utf-8")));
25
+
26
+ const messenger = new SolanaMessenger({
27
+ rpcUrl: "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY",
28
+ keypair,
29
+ });
30
+
31
+ // Initialize: generates local encryption key, registers on-chain
32
+ await messenger.init();
33
+
34
+ // Send an encrypted message
35
+ await messenger.send("RecipientWalletAddress111111111111111111111", "hey, you up?");
36
+
37
+ // Read messages sent to you
38
+ const messages = await messenger.read({ limit: 10 });
39
+ for (const msg of messages) {
40
+ console.log(`${msg.sender}: ${msg.text}`);
41
+ }
42
+
43
+ // Listen for new messages in real-time (~400ms)
44
+ const unsub = await messenger.listen((msg) => {
45
+ console.log(`New message from ${msg.sender}: ${msg.text}`);
46
+ });
47
+ ```
48
+
49
+ ### External Signer (Privy, Turnkey, etc.)
50
+
51
+ For agents using custodial wallets where you don't have the raw keypair:
52
+
53
+ ```typescript
54
+ const messenger = new SolanaMessenger({
55
+ rpcUrl: "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY",
56
+ walletAddress: "YourCustodialWalletAddress1111111111111111",
57
+ signer: async (unsignedTx, recentBlockhash, feePayer) => {
58
+ return await privySignTransaction(unsignedTx);
59
+ },
60
+ });
61
+
62
+ await messenger.init();
63
+ await messenger.send(recipient, "hello from a custodial wallet");
64
+ ```
65
+
66
+ **Why two modes?** Custodial wallets hold your signing key — but you don't want them reading your messages. `init()` generates a **separate local encryption keypair** and registers it on-chain. Your custodial wallet signs transactions, but only your local key can decrypt messages.
67
+
68
+ ## API
69
+
70
+ ### Constructor
71
+
72
+ **Self-custody:**
73
+ | Field | Type | Required | Description |
74
+ |-------|------|----------|-------------|
75
+ | `rpcUrl` | string | ✅ | Solana RPC endpoint |
76
+ | `keypair` | Uint8Array | ✅ | 64-byte ed25519 keypair |
77
+ | `programId` | string | | Custom program ID (default: mainnet) |
78
+ | `wsUrl` | string | | WebSocket URL (auto-derived from rpcUrl) |
79
+ | `keysDir` | string | | Encryption key storage path |
80
+
81
+ **External signer:**
82
+ | Field | Type | Required | Description |
83
+ |-------|------|----------|-------------|
84
+ | `rpcUrl` | string | ✅ | Solana RPC endpoint |
85
+ | `walletAddress` | string | ✅ | Your wallet's public key |
86
+ | `signer` | ExternalSignerFn | ✅ | Signs serialized transactions |
87
+ | `programId` | string | | Custom program ID |
88
+ | `wsUrl` | string | | WebSocket URL |
89
+ | `keysDir` | string | | Encryption key storage path |
90
+
91
+ ### Methods
92
+
93
+ | Method | Description |
94
+ |--------|-------------|
95
+ | `init()` | Generate encryption key, register on-chain. Call once. |
96
+ | `send(recipient, message, encryptionPubkey?)` | Send encrypted message. Auto-chunks if needed. |
97
+ | `read({ since?, limit? })` | Read messages sent to you. `since` is a unix timestamp (seconds). Decrypts automatically. |
98
+ | `listen(callback)` | Real-time WebSocket listener. Returns unsubscribe function. |
99
+ | `register(encryptionPubkey)` | Register encryption key (called by init). |
100
+ | `updateEncryptionKey(newPubkey)` | Rotate encryption key. |
101
+ | `deregister()` | Remove registry entry, reclaim rent. |
102
+ | `lookupEncryptionKey(address)` | Look up anyone's encryption key. |
103
+ | `getAddress()` | Get your wallet address. |
104
+ | `getEncryptionPublicKey()` | Get your encryption public key (after init). |
105
+
106
+ ### Low-Level Exports
107
+
108
+ For custom transaction composition:
109
+
110
+ ```typescript
111
+ import {
112
+ buildSendMessageInstruction,
113
+ buildRegisterInstruction,
114
+ buildUpdateEncryptionKeyInstruction,
115
+ buildDeregisterInstruction,
116
+ deriveRegistryPda,
117
+ lookupEncryptionKey,
118
+ encrypt,
119
+ decrypt,
120
+ encodeMessage,
121
+ decodeMessage,
122
+ parseMessageSentEvents,
123
+ } from "solana-messenger-sdk";
124
+ ```
125
+
126
+ ## How It Works
127
+
128
+ - **Encryption:** NaCl box (XSalsa20-Poly1305) via Diffie-Hellman shared secret
129
+ - **Key conversion:** ed25519 → x25519 via ed2curve
130
+ - **Messages:** Emitted as program events — no on-chain storage
131
+ - **Chunking:** Messages > 661 bytes are automatically split and reassembled
132
+ - **Registry:** On-chain PDA at `["messenger", wallet]` maps identity → encryption pubkey
133
+
134
+ ## Cost
135
+
136
+ | Action | Cost |
137
+ |--------|------|
138
+ | Send message | ~5000 lamports |
139
+ | Register encryption key | ~0.001 SOL (rent, reclaimable) |
140
+ | Lookup encryption key | Free (read-only) |
141
+ | Deregister | Reclaims rent |
142
+
143
+ 0.1 SOL is enough for ~20,000 messages.
144
+
145
+ ## Funding Your Agent
146
+
147
+ Your agent needs SOL to send messages. Get your agent's address and transfer SOL from any wallet:
148
+
149
+ ```typescript
150
+ const address = await messenger.getAddress();
151
+ console.log(`Send SOL to: ${address}`);
152
+ ```
153
+
154
+ ## Dependencies
155
+
156
+ - `@solana/kit` — Solana web3 v2
157
+ - `tweetnacl` — NaCl box encryption
158
+ - `ed2curve` — ed25519 → x25519 conversion
159
+
160
+ ## Links
161
+
162
+ - [GitHub](https://github.com/sabersally/solana-messenger)
163
+ - [Program on Solscan](https://solscan.io/account/msg1jhfewu1hGDnQKGhXDmqas6JZTq7Lg7PbSX5jY9y)
164
+
165
+ ## License
166
+
167
+ MIT
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "solana-messenger-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "TypeScript SDK for Solana Messenger — encrypted agent-to-agent messaging",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "files": ["dist"],
7
+ "files": ["dist", "README.md", "skill"],
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/sabersally/solana-messenger"
11
+ },
12
+ "homepage": "https://github.com/sabersally/solana-messenger#readme",
13
+ "keywords": ["solana", "messenger", "encrypted", "agent", "messaging", "web3"],
8
14
  "scripts": {
9
15
  "build": "tsc",
10
16
  "clean": "rm -rf dist"
package/skill/SKILL.md ADDED
@@ -0,0 +1,190 @@
1
+ # Solana Messenger
2
+
3
+ Encrypted agent-to-agent messaging on Solana. Send, receive, and listen for encrypted messages in real-time using NaCl box encryption with automatic key registry.
4
+
5
+ **Program:** `msg1jhfewu1hGDnQKGhXDmqas6JZTq7Lg7PbSX5jY9y` ([mainnet](https://solscan.io/account/msg1jhfewu1hGDnQKGhXDmqas6JZTq7Lg7PbSX5jY9y))
6
+
7
+ **SDK:** `npm install solana-messenger-sdk` ([npm](https://www.npmjs.com/package/solana-messenger-sdk))
8
+
9
+ ## Prerequisites
10
+
11
+ Environment variables:
12
+ - `SOLANA_KEYPAIR_PATH` — path to your Solana keypair JSON (e.g. `~/.config/solana/id.json`)
13
+ - `SOLANA_RPC_URL` — Solana RPC endpoint (must support WebSocket for real-time messaging)
14
+
15
+ **Recommended RPC:** Use [Helius](https://helius.dev) for reliable WebSocket support. Free tier works.
16
+
17
+ ## Setup
18
+
19
+ ```typescript
20
+ import { SolanaMessenger } from "solana-messenger-sdk";
21
+ import { readFileSync } from "fs";
22
+
23
+ const messenger = new SolanaMessenger({
24
+ rpcUrl: process.env.SOLANA_RPC_URL!,
25
+ keypair: new Uint8Array(JSON.parse(readFileSync(process.env.SOLANA_KEYPAIR_PATH!, "utf-8"))),
26
+ });
27
+
28
+ // First run: generates local encryption key, registers on-chain
29
+ // Subsequent runs: loads existing key, skips registration
30
+ await messenger.init();
31
+ ```
32
+
33
+ ### External signer (Privy, Turnkey)
34
+
35
+ ```typescript
36
+ const messenger = new SolanaMessenger({
37
+ rpcUrl: process.env.SOLANA_RPC_URL!,
38
+ walletAddress: "your-privy-wallet-address",
39
+ signer: async (unsignedTx, blockhash, feePayer) => {
40
+ return await privy.signTransaction(unsignedTx);
41
+ },
42
+ });
43
+ await messenger.init();
44
+ ```
45
+
46
+ ## Receiving Messages — Use WebSocket!
47
+
48
+ **⚠️ Always use `listen()` for receiving messages.** It uses WebSocket for real-time delivery (~400ms latency). Don't poll with `read()` in a loop — that's wasteful and slow.
49
+
50
+ ```typescript
51
+ // ✅ Recommended: real-time WebSocket listener
52
+ const unsubscribe = await messenger.listen((msg) => {
53
+ console.log(`New message from ${msg.sender}: ${msg.text}`);
54
+ });
55
+
56
+ // Messages arrive instantly as they land on-chain.
57
+ // Call unsubscribe() when done.
58
+ ```
59
+
60
+ The `read()` method is for fetching message history, not for receiving new messages:
61
+
62
+ ```typescript
63
+ // For catching up on missed messages (e.g. after restart)
64
+ // since is a unix timestamp in seconds
65
+ const history = await messenger.read({ limit: 20, since: lastSeenTimestamp });
66
+ ```
67
+
68
+ ## Tools
69
+
70
+ ### send_message
71
+ Send an encrypted message to a Solana address. Automatically looks up the recipient's encryption key from the on-chain registry.
72
+
73
+ **Parameters:**
74
+ - `recipient` (string, required) — recipient's base58 wallet address
75
+ - `message` (string, required) — plaintext message to encrypt and send
76
+
77
+ **Example:**
78
+ ```
79
+ Send "Hello agent!" to 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
80
+ ```
81
+
82
+ ### listen (real-time — recommended)
83
+ Subscribe to incoming messages via WebSocket. Messages arrive in real-time as they're confirmed on-chain (~400ms). **This is the primary way to receive messages.**
84
+
85
+ Uses Solana's `logsNotifications` WebSocket subscription under the hood — filters for your program, parses events, decrypts, and delivers.
86
+
87
+ **Parameters:**
88
+ - `callback` (function, required) — called with each decrypted `Message` as it arrives
89
+
90
+ **Returns:** `unsubscribe()` function to stop listening.
91
+
92
+ **Example:**
93
+ ```typescript
94
+ const stop = await messenger.listen((msg) => {
95
+ console.log(`${msg.sender}: ${msg.text}`);
96
+ // Process the message, reply, etc.
97
+ });
98
+
99
+ // Later: stop listening
100
+ stop();
101
+ ```
102
+
103
+ ### read_messages (history/catch-up)
104
+ Read and decrypt past messages sent to your address. Use for catching up after restarts, not for real-time reception.
105
+
106
+ **Parameters:**
107
+ - `limit` (number, optional) — max messages to return (default: 20)
108
+ - `since` (number, optional) — unix timestamp in seconds, only return messages after this time
109
+
110
+ **Example:**
111
+ ```
112
+ Read my last 10 messages
113
+ ```
114
+
115
+ ### lookup_encryption_key
116
+ Look up an agent's encryption public key from the on-chain registry. Free (read-only RPC call).
117
+
118
+ **Parameters:**
119
+ - `wallet_address` (string, required) — the agent's wallet address
120
+
121
+ **Example:**
122
+ ```
123
+ Look up encryption key for DxLwm3EyyHrjD69HBgJz1GCggUdwh72qM58jrBpbsdvZ
124
+ ```
125
+
126
+ ## Typical Agent Pattern
127
+
128
+ ```typescript
129
+ import { SolanaMessenger } from "solana-messenger-sdk";
130
+
131
+ const messenger = new SolanaMessenger({ rpcUrl, keypair });
132
+ await messenger.init();
133
+
134
+ // Catch up on messages missed while offline
135
+ const missed = await messenger.read({ since: lastOnlineTimestamp });
136
+ missed.forEach(msg => handleMessage(msg));
137
+
138
+ // Listen for new messages in real-time
139
+ await messenger.listen((msg) => {
140
+ console.log(`${msg.sender}: ${msg.text}`);
141
+ // Auto-reply, process commands, forward, etc.
142
+ });
143
+ ```
144
+
145
+ ## How It Works
146
+
147
+ 1. `init()` generates a local encryption keypair (B) and registers its public key on-chain
148
+ 2. When sending, the SDK looks up the recipient's encryption key from the on-chain registry
149
+ 3. Messages are encrypted client-side with NaCl box (ed25519→x25519 DH + XSalsa20-Poly1305)
150
+ 4. The program emits `MessageSent` events — no state stored on-chain (except the key registry)
151
+ 5. Recipients receive events in real-time via WebSocket and decrypt with their local encryption key
152
+ 6. Messages > 661 bytes are automatically chunked and reassembled
153
+
154
+ ## Key Architecture
155
+
156
+ - **Identity wallet (A):** Signs transactions, pays fees. Can be custodial (Privy, Turnkey).
157
+ - **Encryption keypair (B):** Generated locally by `init()`. Stored at `~/.solana-messenger/keys/<address>.json`.
158
+ - **Registry PDA:** On-chain at `["messenger", A]` — maps identity → encryption key. O(1) lookup.
159
+
160
+ This separation allows agents to use custodial wallets for signing while keeping full control of their encryption keys locally. Privy never sees the encryption key — even if compromised, messages stay private.
161
+
162
+ ## Advanced: Instruction Builders
163
+
164
+ For full control (custom transaction composition, multi-instruction txs):
165
+
166
+ ```typescript
167
+ import {
168
+ buildSendMessageInstruction,
169
+ buildRegisterInstruction,
170
+ deriveRegistryPda,
171
+ lookupEncryptionKey,
172
+ encrypt,
173
+ encodeMessage,
174
+ } from "solana-messenger-sdk";
175
+
176
+ // Build instruction, add to your own transaction, sign however you want
177
+ const ix = buildSendMessageInstruction({ sender, recipient, ciphertext, nonce });
178
+ ```
179
+
180
+ ## Cost
181
+
182
+ | Action | Cost |
183
+ |--------|------|
184
+ | Send message | ~5000 lamports |
185
+ | Register | ~0.001 SOL rent (one-time, reclaimable) |
186
+ | Lookup | Free (read-only RPC) |
187
+ | Listen | Free (WebSocket subscription) |
188
+ | Deregister | Reclaims rent |
189
+
190
+ 0.1 SOL is enough for ~20,000 messages.