blue-js-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.
Files changed (215) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/LICENSE +21 -0
  3. package/README.md +75 -0
  4. package/ai-path/ADMIN-ELEVATION.md +116 -0
  5. package/ai-path/AI-MANIFESTO.md +185 -0
  6. package/ai-path/BREAKING.md +74 -0
  7. package/ai-path/CHECKLIST.md +619 -0
  8. package/ai-path/CONNECTION-STEPS.md +724 -0
  9. package/ai-path/DECISION-TREE.md +378 -0
  10. package/ai-path/DEPENDENCIES.md +459 -0
  11. package/ai-path/E2E-FLOW.md +1555 -0
  12. package/ai-path/FAILURES.md +403 -0
  13. package/ai-path/GUIDE.md +1217 -0
  14. package/ai-path/README.md +558 -0
  15. package/ai-path/SPLIT-TUNNEL.md +266 -0
  16. package/ai-path/cli.js +535 -0
  17. package/ai-path/connect.js +884 -0
  18. package/ai-path/discover.js +178 -0
  19. package/ai-path/environment.js +266 -0
  20. package/ai-path/errors.js +86 -0
  21. package/ai-path/examples/autonomous-agent.mjs +220 -0
  22. package/ai-path/examples/multi-region.mjs +174 -0
  23. package/ai-path/examples/one-shot.mjs +31 -0
  24. package/ai-path/index.js +60 -0
  25. package/ai-path/pricing.js +136 -0
  26. package/ai-path/recommend.js +413 -0
  27. package/ai-path/run-admin.vbs +25 -0
  28. package/ai-path/setup.js +291 -0
  29. package/ai-path/wallet.js +137 -0
  30. package/app-helpers.js +363 -0
  31. package/app-settings.js +95 -0
  32. package/app-types.js +267 -0
  33. package/audit.js +847 -0
  34. package/batch.js +293 -0
  35. package/bin/setup.js +376 -0
  36. package/chain/authz.js +109 -0
  37. package/chain/broadcast.js +472 -0
  38. package/chain/client.js +160 -0
  39. package/chain/fee-grants.js +305 -0
  40. package/chain/index.js +891 -0
  41. package/chain/lcd.js +313 -0
  42. package/chain/queries.js +547 -0
  43. package/chain/rpc.js +408 -0
  44. package/chain/wallet.js +141 -0
  45. package/cli/config.js +143 -0
  46. package/cli/index.js +463 -0
  47. package/cli/output.js +182 -0
  48. package/cli.js +491 -0
  49. package/client/index.js +251 -0
  50. package/client.js +271 -0
  51. package/config/index.js +255 -0
  52. package/connection/connect.js +849 -0
  53. package/connection/disconnect.js +180 -0
  54. package/connection/discovery.js +321 -0
  55. package/connection/index.js +76 -0
  56. package/connection/proxy.js +148 -0
  57. package/connection/resilience.js +428 -0
  58. package/connection/security.js +232 -0
  59. package/connection/state.js +369 -0
  60. package/connection/tunnel.js +691 -0
  61. package/consumer.js +132 -0
  62. package/cosmjs-setup.js +1884 -0
  63. package/defaults.js +366 -0
  64. package/disk-cache.js +107 -0
  65. package/dist/client.d.ts +108 -0
  66. package/dist/client.d.ts.map +1 -0
  67. package/dist/client.js +400 -0
  68. package/dist/client.js.map +1 -0
  69. package/dist/index.d.ts +8 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +8 -0
  72. package/dist/index.js.map +1 -0
  73. package/errors/index.js +112 -0
  74. package/errors.js +218 -0
  75. package/examples/README.md +64 -0
  76. package/examples/connect-direct.mjs +106 -0
  77. package/examples/connect-plan.mjs +125 -0
  78. package/examples/error-handling.mjs +109 -0
  79. package/examples/query-nodes.mjs +94 -0
  80. package/examples/wallet-basics.mjs +61 -0
  81. package/generated/amino/amino.ts +9 -0
  82. package/generated/cosmos/base/v1beta1/coin.ts +365 -0
  83. package/generated/cosmos_proto/cosmos.ts +323 -0
  84. package/generated/gogoproto/gogo.ts +9 -0
  85. package/generated/google/protobuf/descriptor.ts +7601 -0
  86. package/generated/google/protobuf/duration.ts +208 -0
  87. package/generated/google/protobuf/timestamp.ts +238 -0
  88. package/generated/sentinel/lease/v1/events.ts +924 -0
  89. package/generated/sentinel/lease/v1/lease.ts +292 -0
  90. package/generated/sentinel/lease/v1/msg.ts +949 -0
  91. package/generated/sentinel/lease/v1/params.ts +164 -0
  92. package/generated/sentinel/node/v3/events.ts +881 -0
  93. package/generated/sentinel/node/v3/msg.ts +1002 -0
  94. package/generated/sentinel/node/v3/node.ts +263 -0
  95. package/generated/sentinel/node/v3/params.ts +183 -0
  96. package/generated/sentinel/plan/v3/events.ts +675 -0
  97. package/generated/sentinel/plan/v3/msg.ts +1191 -0
  98. package/generated/sentinel/plan/v3/plan.ts +283 -0
  99. package/generated/sentinel/provider/v2/events.ts +171 -0
  100. package/generated/sentinel/provider/v2/msg.ts +480 -0
  101. package/generated/sentinel/provider/v2/params.ts +131 -0
  102. package/generated/sentinel/provider/v2/provider.ts +246 -0
  103. package/generated/sentinel/session/v3/events.ts +480 -0
  104. package/generated/sentinel/session/v3/msg.ts +616 -0
  105. package/generated/sentinel/session/v3/params.ts +260 -0
  106. package/generated/sentinel/session/v3/proof.ts +180 -0
  107. package/generated/sentinel/session/v3/session.ts +384 -0
  108. package/generated/sentinel/subscription/v3/events.ts +1181 -0
  109. package/generated/sentinel/subscription/v3/msg.ts +1305 -0
  110. package/generated/sentinel/subscription/v3/params.ts +167 -0
  111. package/generated/sentinel/subscription/v3/subscription.ts +315 -0
  112. package/generated/sentinel/types/v1/bandwidth.ts +124 -0
  113. package/generated/sentinel/types/v1/price.ts +149 -0
  114. package/generated/sentinel/types/v1/renewal.ts +87 -0
  115. package/generated/sentinel/types/v1/status.ts +54 -0
  116. package/generated/typeRegistry.ts +27 -0
  117. package/index.js +486 -0
  118. package/node-connect.js +3015 -0
  119. package/operator.js +134 -0
  120. package/package.json +113 -0
  121. package/plan-operations.js +199 -0
  122. package/preflight.js +352 -0
  123. package/pricing/index.js +262 -0
  124. package/proto/amino/amino.proto +84 -0
  125. package/proto/cosmos/base/v1beta1/coin.proto +61 -0
  126. package/proto/cosmos_proto/cosmos.proto +112 -0
  127. package/proto/gogoproto/gogo.proto +145 -0
  128. package/proto/google/api/annotations.proto +31 -0
  129. package/proto/google/api/http.proto +370 -0
  130. package/proto/google/protobuf/any.proto +106 -0
  131. package/proto/google/protobuf/duration.proto +115 -0
  132. package/proto/google/protobuf/timestamp.proto +145 -0
  133. package/proto/sentinel/lease/v1/events.proto +52 -0
  134. package/proto/sentinel/lease/v1/genesis.proto +15 -0
  135. package/proto/sentinel/lease/v1/lease.proto +25 -0
  136. package/proto/sentinel/lease/v1/msg.proto +62 -0
  137. package/proto/sentinel/lease/v1/params.proto +17 -0
  138. package/proto/sentinel/node/v3/events.proto +50 -0
  139. package/proto/sentinel/node/v3/genesis.proto +15 -0
  140. package/proto/sentinel/node/v3/msg.proto +63 -0
  141. package/proto/sentinel/node/v3/node.proto +27 -0
  142. package/proto/sentinel/node/v3/params.proto +21 -0
  143. package/proto/sentinel/node/v3/querier.proto +63 -0
  144. package/proto/sentinel/plan/v3/events.proto +41 -0
  145. package/proto/sentinel/plan/v3/genesis.proto +21 -0
  146. package/proto/sentinel/plan/v3/msg.proto +83 -0
  147. package/proto/sentinel/plan/v3/plan.proto +32 -0
  148. package/proto/sentinel/plan/v3/querier.proto +53 -0
  149. package/proto/sentinel/provider/v2/events.proto +16 -0
  150. package/proto/sentinel/provider/v2/genesis.proto +15 -0
  151. package/proto/sentinel/provider/v2/msg.proto +35 -0
  152. package/proto/sentinel/provider/v2/params.proto +17 -0
  153. package/proto/sentinel/provider/v2/provider.proto +24 -0
  154. package/proto/sentinel/provider/v3/genesis.proto +15 -0
  155. package/proto/sentinel/provider/v3/params.proto +13 -0
  156. package/proto/sentinel/session/v3/events.proto +30 -0
  157. package/proto/sentinel/session/v3/genesis.proto +15 -0
  158. package/proto/sentinel/session/v3/msg.proto +50 -0
  159. package/proto/sentinel/session/v3/params.proto +25 -0
  160. package/proto/sentinel/session/v3/proof.proto +25 -0
  161. package/proto/sentinel/session/v3/querier.proto +100 -0
  162. package/proto/sentinel/session/v3/session.proto +50 -0
  163. package/proto/sentinel/subscription/v2/allocation.proto +21 -0
  164. package/proto/sentinel/subscription/v2/payout.proto +22 -0
  165. package/proto/sentinel/subscription/v3/events.proto +65 -0
  166. package/proto/sentinel/subscription/v3/genesis.proto +17 -0
  167. package/proto/sentinel/subscription/v3/msg.proto +83 -0
  168. package/proto/sentinel/subscription/v3/params.proto +21 -0
  169. package/proto/sentinel/subscription/v3/subscription.proto +33 -0
  170. package/proto/sentinel/types/v1/bandwidth.proto +19 -0
  171. package/proto/sentinel/types/v1/price.proto +21 -0
  172. package/proto/sentinel/types/v1/renewal.proto +21 -0
  173. package/proto/sentinel/types/v1/status.proto +16 -0
  174. package/protocol/encoding.js +341 -0
  175. package/protocol/events.js +361 -0
  176. package/protocol/handshake.js +297 -0
  177. package/protocol/index.js +15 -0
  178. package/protocol/messages.js +346 -0
  179. package/protocol/plans.js +199 -0
  180. package/protocol/v2ray.js +268 -0
  181. package/protocol/v3.js +723 -0
  182. package/protocol/wireguard.js +125 -0
  183. package/security/index.js +132 -0
  184. package/session-manager.js +329 -0
  185. package/session-tracker.js +80 -0
  186. package/setup.js +376 -0
  187. package/speedtest/index.js +528 -0
  188. package/speedtest.js +567 -0
  189. package/src/client.ts +502 -0
  190. package/src/index.ts +20 -0
  191. package/state/index.js +347 -0
  192. package/state.js +516 -0
  193. package/test-all-chain-ops.js +493 -0
  194. package/test-all-logic.js +199 -0
  195. package/test-all-msg-types.js +292 -0
  196. package/test-every-connection.js +208 -0
  197. package/test-feegrant-connect.js +98 -0
  198. package/test-logic.js +148 -0
  199. package/test-mainnet.js +176 -0
  200. package/test-plan-lifecycle.js +335 -0
  201. package/tls-trust.js +132 -0
  202. package/tsconfig.build.json +20 -0
  203. package/tsconfig.json +34 -0
  204. package/types/chain.d.ts +746 -0
  205. package/types/connection.d.ts +425 -0
  206. package/types/errors.d.ts +174 -0
  207. package/types/index.d.ts +1380 -0
  208. package/types/nodes.d.ts +187 -0
  209. package/types/pricing.d.ts +156 -0
  210. package/types/protocol.d.ts +332 -0
  211. package/types/session.d.ts +236 -0
  212. package/types/settings.d.ts +192 -0
  213. package/v3protocol.js +1053 -0
  214. package/wallet/index.js +153 -0
  215. package/wireguard.js +307 -0
@@ -0,0 +1,724 @@
1
+ # Connection Steps — Complete Technical Walkthrough
2
+
3
+ > **Purpose:** Show every step of a successful Sentinel dVPN connection with real output, so an AI agent building on this SDK can verify each phase against its own results and pinpoint exactly where a failure occurs.
4
+
5
+ ## NON-NEGOTIABLE RULES — Read Before Writing ANY Code
6
+
7
+ These rules exist because every AI that has built on this SDK violated at least one of them. They are not suggestions. They are requirements.
8
+
9
+ 1. **NEVER use native `fetch()` for V2Ray traffic.** Node.js `fetch()` silently ignores SOCKS5 proxy. Your IP check will show your real IP and you will think the VPN is working when it is not. Use `axios` with `SocksProxyAgent` for ALL traffic that must go through V2Ray.
10
+
11
+ 2. **V2Ray is a SOCKS5 proxy, not a system tunnel.** Only traffic you explicitly route through `socks5://127.0.0.1:{port}` goes through the VPN. Everything else — your chain queries, your npm installs, your other HTTP calls — goes direct. This is by design. It is the split tunnel.
12
+
13
+ 3. **WireGuard `splitIPs` takes IP addresses, not domain names.** `splitIPs: ['google.com']` does nothing. Domains resolve to many IPs via DNS/CDN. You must provide CIDR ranges like `splitIPs: ['10.0.0.0/8']`. For per-app routing, use V2Ray SOCKS5 instead.
14
+
15
+ 4. **Use `axios` with `adapter: 'http'` for all SOCKS5 traffic.** Node.js 20+ defaults to the undici fetch adapter which silently fails with self-signed certificates and SOCKS5 proxies. Always set `adapter: 'http'` explicitly.
16
+
17
+ 5. **After V2Ray connect, verify with `verify()` — not with `fetch()`.** The SDK's `verify()` function routes through SOCKS5 automatically. A raw `fetch()` call bypasses the proxy entirely.
18
+
19
+ ---
20
+
21
+ ## Overview — The 9 Phases
22
+
23
+ ```
24
+ Phase 1: Environment Check → Can I run?
25
+ Phase 2: Wallet Setup → Do I have keys?
26
+ Phase 3: Balance Check → Can I pay?
27
+ Phase 4: Node Discovery → Who can I connect to?
28
+ Phase 5: Cost Estimation → How much will it cost?
29
+ Phase 6: Session Creation → Pay the blockchain
30
+ Phase 7: Handshake → Exchange keys with the node
31
+ Phase 8: Tunnel Installation → Start encrypted tunnel
32
+ Phase 9: Verification → Confirm traffic flows
33
+ ```
34
+
35
+ Each phase below includes:
36
+ - **What happens** (technical)
37
+ - **Successful output** (real example)
38
+ - **Failure signatures** (what goes wrong and why)
39
+ - **Diagnostic check** (how to verify this phase independently)
40
+
41
+ ---
42
+
43
+ ## Phase 1: Environment Check
44
+
45
+ ### What Happens
46
+ The SDK checks that all required binaries and permissions are available before any network calls.
47
+
48
+ ### Code
49
+ ```javascript
50
+ import { setup, getEnvironment } from 'sentinel-ai-connect';
51
+
52
+ // Quick synchronous check (no network calls)
53
+ const env = getEnvironment();
54
+ console.log(env);
55
+
56
+ // Full async check (includes chain reachability)
57
+ const check = await setup();
58
+ console.log(check);
59
+ ```
60
+
61
+ ### Successful Output
62
+ ```json
63
+ {
64
+ "os": "win32",
65
+ "arch": "x64",
66
+ "nodeVersion": "22.4.0",
67
+ "admin": true,
68
+ "v2ray": {
69
+ "available": true,
70
+ "path": "~/.sentinel-sdk/bin/v2ray.exe",
71
+ "version": "5.2.1"
72
+ },
73
+ "wireguard": {
74
+ "available": true,
75
+ "path": "C:\\Program Files\\WireGuard\\wireguard.exe"
76
+ },
77
+ "capabilities": ["v2ray", "wireguard"],
78
+ "recommendations": []
79
+ }
80
+ ```
81
+
82
+ ### Failure Signatures
83
+
84
+ | Symptom | Cause | Fix |
85
+ |---------|-------|-----|
86
+ | `v2ray.available: false` | V2Ray binary not downloaded | Run `npx sentinel-setup` or call `setup()` |
87
+ | `v2ray.version: "5.44.1"` | Wrong version — observatory bug breaks VMess | Delete and re-download v5.2.1 exactly |
88
+ | `wireguard.available: false` | WireGuard not installed | Install from wireguard.com |
89
+ | `admin: false` | Not elevated | WireGuard requires admin. Without it, only V2Ray works (~70% of network) |
90
+ | `capabilities: ["v2ray"]` | Admin false → WireGuard unavailable | Elevate with `run-admin.vbs` (Windows) or `sudo` (macOS/Linux) |
91
+
92
+ ### Diagnostic Check
93
+ ```bash
94
+ # V2Ray version (MUST be 5.2.1)
95
+ ~/.sentinel-sdk/bin/v2ray version
96
+
97
+ # WireGuard available
98
+ wireguard.exe /version
99
+
100
+ # Admin check (Windows)
101
+ net session >nul 2>&1 && echo ADMIN || echo NOT ADMIN
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Phase 2: Wallet Setup
107
+
108
+ ### What Happens
109
+ Creates or imports a BIP39 mnemonic, derives a Cosmos HD wallet (path `m/44'/118'/0'/0/0`), produces a `sent1...` address.
110
+
111
+ ### Code
112
+ ```javascript
113
+ import { createWallet, importWallet } from 'sentinel-ai-connect';
114
+
115
+ // Create new wallet
116
+ const wallet = await createWallet();
117
+ console.log(`Address: ${wallet.address}`);
118
+ console.log(`Mnemonic: ${wallet.mnemonic}`);
119
+ // ⚠ SAVE THE MNEMONIC. It cannot be recovered.
120
+
121
+ // OR import existing wallet
122
+ const imported = await importWallet('your twelve word mnemonic phrase goes right here in this string');
123
+ console.log(`Address: ${imported.address}`);
124
+ ```
125
+
126
+ ### Successful Output
127
+ ```
128
+ Address: sent1abc...xyz
129
+ Mnemonic: word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12
130
+ ```
131
+
132
+ ### Failure Signatures
133
+
134
+ | Symptom | Cause | Fix |
135
+ |---------|-------|-----|
136
+ | `INVALID_MNEMONIC` | Not 12 or 24 words, or not valid BIP39 English words | Check for typos, extra spaces, wrong word count |
137
+ | `Cannot read properties of undefined (reading 'slice')` | Empty string or null passed as mnemonic | Ensure `.env` file exists and `MNEMONIC` is set |
138
+ | Address starts with `cosmos1` instead of `sent1` | Wrong bech32 prefix (using vanilla CosmJS without Sentinel prefix) | Use SDK's `generateWallet()`, not raw CosmJS |
139
+
140
+ ### Diagnostic Check
141
+ ```javascript
142
+ // Verify address format
143
+ const addr = imported.address;
144
+ console.assert(addr.startsWith('sent1'), 'Address must start with sent1');
145
+ console.assert(addr.length === 44, 'Address must be 44 characters');
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Phase 3: Balance Check
151
+
152
+ ### What Happens
153
+ Queries the Sentinel blockchain (LCD REST API) for the wallet's `udvpn` balance. Tries 4 LCD endpoints with automatic failover.
154
+
155
+ ### Code
156
+ ```javascript
157
+ import { getBalance } from 'sentinel-ai-connect';
158
+
159
+ const bal = await getBalance('your twelve word mnemonic phrase...');
160
+ console.log(bal);
161
+ ```
162
+
163
+ ### Successful Output
164
+ ```json
165
+ {
166
+ "address": "sent1abc...xyz",
167
+ "udvpn": 47690000,
168
+ "p2p": "47.69",
169
+ "funded": true
170
+ }
171
+ ```
172
+
173
+ ### Failure Signatures
174
+
175
+ | Symptom | Cause | Fix |
176
+ |---------|-------|-----|
177
+ | `udvpn: 0, funded: false` | Wallet has no tokens | Send P2P tokens to the `sent1...` address |
178
+ | `ECONNREFUSED` or timeout on all 4 LCD endpoints | Network down or all LCD endpoints unreachable | Check internet connection. Verify manually: `curl https://lcd.sentinel.co/cosmos/bank/v1beta1/balances/sent1...` |
179
+ | `funded: false` but `udvpn > 0` | Balance below 1,000,000 udvpn (1.0 P2P) threshold | Fund wallet with at least 1.0 P2P to cover gas + cheapest node |
180
+
181
+ ### Diagnostic Check
182
+ ```bash
183
+ # Manual balance check (replace address)
184
+ curl -s "https://lcd.sentinel.co/cosmos/bank/v1beta1/balances/sent1abc...xyz" | jq '.balances[] | select(.denom=="udvpn")'
185
+ ```
186
+
187
+ ### Cost Reference
188
+
189
+ > Prices are set by independent node operators and vary. Use `estimateCost()` for live pricing.
190
+
191
+ | Action | Approximate Cost |
192
+ |--------|------|
193
+ | Gas per TX | ~0.04 P2P |
194
+ | 1 GB (cheapest nodes) | ~0.68 P2P (varies) |
195
+ | 1 GB (median node) | ~40 P2P (varies) |
196
+ | `funded: true` threshold | 1.0 P2P |
197
+ | Comfortable testing budget | 50 P2P |
198
+
199
+ ---
200
+
201
+ ## Phase 4: Node Discovery
202
+
203
+ ### What Happens
204
+ Queries the Sentinel LCD for all active nodes (`status=1`), fetches up to 5,000 nodes in a single request. Can optionally probe individual nodes for country, peers, and health.
205
+
206
+ ### Code
207
+ ```javascript
208
+ import { discoverNodes, getNetworkStats } from 'sentinel-ai-connect';
209
+
210
+ // Quick mode — chain data only, no probing (fast, < 3 seconds)
211
+ const nodes = await discoverNodes({ quick: true });
212
+ console.log(`Found ${nodes.length} active nodes`);
213
+ console.log('First node:', nodes[0]);
214
+
215
+ // Network overview
216
+ const stats = await getNetworkStats();
217
+ console.log(stats);
218
+ ```
219
+
220
+ ### Successful Output
221
+ ```
222
+ Found 1030 active nodes
223
+ First node: {
224
+ address: "sentnode1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5h38un",
225
+ country: "DE",
226
+ protocol: "wireguard",
227
+ pricePerGb: "4.22",
228
+ pricePerHour: null,
229
+ score: 85,
230
+ peers: 12,
231
+ remoteUrl: "https://45.76.32.100:8585"
232
+ }
233
+
234
+ {
235
+ totalNodes: 1030,
236
+ byCountry: { DE: 42, US: 38, SG: 35, ... },
237
+ byProtocol: { wireguard: 618, v2ray: 412 },
238
+ transportReliability: { "tcp": "100%", "grpc/none": "87%", "websocket": "75%", ... }
239
+ }
240
+ ```
241
+
242
+ ### Failure Signatures
243
+
244
+ | Symptom | Cause | Fix |
245
+ |---------|-------|-----|
246
+ | `Found 0 active nodes` | LCD endpoint returned empty, or using v2 path | Verify path is `/sentinel/node/v3/nodes?status=1` (NOT v2, NOT `STATUS_ACTIVE`) |
247
+ | Only ~400 nodes when 1000+ expected | Pagination truncated | Set `pagination.limit=5000` — SDK does this automatically |
248
+ | Country/protocol null on all nodes | Used `quick: true` mode (chain data only, no probing) | Use full mode (omit `quick`) for enriched data — takes 30-60s |
249
+ | `ECONNREFUSED` | LCD endpoints all down | Retry. Check manually: `curl https://lcd.sentinel.co/sentinel/node/v3/nodes?status=1&pagination.limit=5` |
250
+
251
+ ### Diagnostic Check
252
+ ```bash
253
+ # Count active nodes
254
+ curl -s "https://lcd.sentinel.co/sentinel/node/v3/nodes?status=1&pagination.limit=1&pagination.count_total=true" | jq '.pagination.total'
255
+
256
+ # Check single node
257
+ curl -s "https://lcd.sentinel.co/sentinel/node/v3/nodes/sentnode1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5h38un"
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Phase 5: Cost Estimation
263
+
264
+ ### What Happens
265
+ Calculates how much a connection will cost before paying. Uses the node's on-chain price or network median if no specific node.
266
+
267
+ ### Code
268
+ ```javascript
269
+ import { estimateCost, recommend } from 'sentinel-ai-connect';
270
+
271
+ // Estimate for 1 GB
272
+ const cost = await estimateCost({ gigabytes: 1 });
273
+ console.log(cost);
274
+
275
+ // Or get a full recommendation
276
+ const rec = await recommend({
277
+ country: 'Germany',
278
+ priority: 'reliability',
279
+ gigabytes: 1,
280
+ });
281
+ console.log(rec);
282
+ ```
283
+
284
+ ### Successful Output — estimateCost
285
+ ```json
286
+ {
287
+ "perGb": { "udvpn": 4220000, "p2p": "4.22" },
288
+ "total": { "udvpn": 4220000, "p2p": "4.22" },
289
+ "gas": { "udvpn": 40000, "p2p": "0.04" },
290
+ "grandTotal": { "udvpn": 4260000, "p2p": "4.26" },
291
+ "mode": "gigabyte"
292
+ }
293
+ ```
294
+
295
+ ### Successful Output — recommend
296
+ ```json
297
+ {
298
+ "action": "connect",
299
+ "confidence": 0.9,
300
+ "primary": {
301
+ "address": "sentnode1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5h38un",
302
+ "country": "DE",
303
+ "protocol": "wireguard",
304
+ "score": 85,
305
+ "pricePerGb": "4.22",
306
+ "peers": 12,
307
+ "reason": "Exact country match, WireGuard available, low peer count"
308
+ },
309
+ "alternatives": [ /* 5 more nodes */ ],
310
+ "estimatedCost": { "udvpn": 4260000, "p2p": "4.26" },
311
+ "warnings": [],
312
+ "reasoning": [
313
+ "Checked capabilities: WireGuard available, admin elevated",
314
+ "Queried 1030 active nodes",
315
+ "Filtered to 42 nodes in Germany",
316
+ "Selected top node by reliability score"
317
+ ],
318
+ "capabilities": { "wireguard": true, "v2ray": true, "admin": true }
319
+ }
320
+ ```
321
+
322
+ ### Failure Signatures
323
+
324
+ | Symptom | Cause | Fix |
325
+ |---------|-------|-----|
326
+ | `action: "cannot-connect"` | No nodes match filter (e.g. country with zero nodes) | Broaden filter, remove `strictCountry`, or use different country |
327
+ | `action: "connect-fallback"` | Exact country unavailable, using nearby country | Check `warnings` array for details |
328
+ | `pricePerGb` extremely high (>100 P2P) | Looking at `baseValue` instead of `quoteValue` | SDK handles this. If building manually: use `gigabyte_prices[0].amount` not `baseValue` |
329
+
330
+ ---
331
+
332
+ ## Phase 6: Session Creation (Payment)
333
+
334
+ ### What Happens
335
+ Broadcasts a `MsgStartSession` transaction to the Sentinel blockchain. The node is notified on-chain that a session exists. Costs real P2P tokens.
336
+
337
+ **This is the point of no return — tokens are spent regardless of whether the connection succeeds.**
338
+
339
+ ### Code (via connect — this happens automatically)
340
+ ```javascript
341
+ // connect() handles this internally. The onProgress callback shows it:
342
+ const vpn = await connect({
343
+ mnemonic: process.env.MNEMONIC,
344
+ onProgress: (step, detail) => console.log(`[${step}] ${detail}`),
345
+ });
346
+ ```
347
+
348
+ ### Successful Progress Output
349
+ ```
350
+ [wallet] Wallet ready: sent12e03...
351
+ [wallet] RPC connected: https://rpc.sentinel.co
352
+ [wallet] LCD connected: https://lcd.sentinel.co
353
+ [wallet] Balance: 47.69 P2P
354
+ [session] Found 1030 active nodes
355
+ [session] Selected node sentnode1qyp... (DE, WireGuard, 4.22 P2P/GB)
356
+ [session] Broadcasting MsgStartSession...
357
+ [session] TX broadcast: hash 7A3F...B21C
358
+ [session] Waiting for block confirmation...
359
+ [session] Session created: ID 485721
360
+ ```
361
+
362
+ > **Progress tag reference:** The SDK emits these step names: `wallet`, `session`, `node-check`, `validate`, `handshake`, `tunnel`, `verify`, `proxy`, `recover`, `cache`, `dry-run`, `log`. Match against these when programmatically parsing progress output.
363
+
364
+ ### Failure Signatures
365
+
366
+ | Symptom | Cause | Fix |
367
+ |---------|-------|-----|
368
+ | `INSUFFICIENT_BALANCE` | Not enough udvpn for session + gas | Fund wallet with more P2P tokens |
369
+ | `account sequence mismatch` | Previous TX still pending | SDK auto-retries up to 5 times with local sequence counter |
370
+ | `node does not exist` | Node went offline between query and TX | SDK retries once after 10s wait (chain lag). If persistent, try another node |
371
+ | `session already exists` | Active session with this node exists from a previous run | SDK detects and reuses the existing session |
372
+ | `BROADCAST_FAILED` after 5 retries | RPC endpoint congested | Wait 60s, then retry. Check RPC health: `curl https://rpc.sentinel.co/health` |
373
+ | `code 105: inactive` | Node just went inactive on-chain | SDK auto-retries with a different node |
374
+ | Progress stops at `[subscribe] Broadcasting...` | TX stuck in mempool | Wait up to 60s. Sentinel blocks are ~6s. If >60s, RPC may be congested |
375
+
376
+ ### Diagnostic Check
377
+ ```bash
378
+ # Check your sessions on-chain
379
+ curl -s "https://lcd.sentinel.co/sentinel/session/v3/accounts/sent1abc...xyz/sessions" | jq '.sessions | length'
380
+
381
+ # Check specific session
382
+ curl -s "https://lcd.sentinel.co/sentinel/session/v3/sessions/485721"
383
+ ```
384
+
385
+ ### Critical Timing
386
+ - After TX broadcast, the SDK waits for block confirmation (~6s)
387
+ - If session query returns "does not exist," the SDK waits 10s and retries (chain propagation lag)
388
+ - A new session may show `inactive_pending` status briefly — the SDK polls until `active`
389
+
390
+ ---
391
+
392
+ ## Phase 7: Handshake
393
+
394
+ ### What Happens
395
+ The SDK sends an authenticated POST request to the node's HTTPS API. The body contains the session ID, a public key (X25519 for WireGuard or UUID for V2Ray), and a cryptographic signature proving ownership of the wallet that paid for the session.
396
+
397
+ **Signature construction:** `secp256k1_sign(SHA256(BigEndian_uint64(sessionId) + raw_peer_data_json_bytes))`
398
+
399
+ ### Successful Progress Output — WireGuard
400
+ ```
401
+ [handshake] Handshaking with sentnode1qyp... (WireGuard)
402
+ [handshake] Generated X25519 key pair
403
+ [handshake] POST https://45.76.32.100:8585
404
+ [handshake] WireGuard config received
405
+ [log] Server public key: aB3x...kLm=
406
+ [log] Endpoint: 45.76.32.100:51820
407
+ [log] Tunnel IP: 10.8.0.2/32
408
+ ```
409
+
410
+ ### Successful Progress Output — V2Ray
411
+ ```
412
+ [handshake] Handshaking with sentnode1abc... (V2Ray)
413
+ [handshake] Generated UUID: 7f3a...e2b1
414
+ [handshake] POST https://198.51.100.42:8585
415
+ [handshake] V2Ray config received
416
+ [log] Protocol: VLess, Transport: grpc/none
417
+ [log] Metadata entries: 3 outbounds available
418
+ [log] Ports: [8686, 8787, 7874]
419
+ ```
420
+
421
+ ### Failure Signatures
422
+
423
+ | Symptom | Cause | Fix |
424
+ |---------|-------|-----|
425
+ | `HANDSHAKE_FAILED` with timeout | Node API is unreachable (HTTPS port down) | Try a different node. SDK timeout is 90s |
426
+ | `409 Conflict` | Session exists but node has stale state | SDK retries at 15s, 20s, then 25s. Usually resolves |
427
+ | `session does not exist` (code 5) | Chain propagation lag — node hasn't seen the TX yet | SDK waits 10s and retries once |
428
+ | `ECONNRESET` or `EPROTO` | Node TLS certificate issue or network instability | SDK uses TOFU TLS (first cert is trusted, changes are rejected) |
429
+ | `already exists` | Re-handshaking for an already-used session | SDK creates a fresh session and retries |
430
+ | `certificate has changed` | Possible MITM attack OR node rotated its cert | SDK rejects. Skip this node (security) |
431
+ | Handshake succeeds but V2Ray tunnel hangs | VLess `flow` field set to `xtls-rprx-vision` instead of `''` | Must be empty string — V2Ray 5.x silently rejects Xray-only flows |
432
+
433
+ ### Diagnostic Check
434
+ ```bash
435
+ # Check if node is reachable (replace URL from node's remote_addrs)
436
+ curl -k -s -o /dev/null -w "%{http_code}" https://45.76.32.100:8585/status
437
+
438
+ # Expected: 200 (node alive) or 401 (alive but needs auth)
439
+ # If timeout or connection refused: node is down
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Phase 8: Tunnel Installation
445
+
446
+ ### What Happens
447
+
448
+ **WireGuard path:**
449
+ 1. Writes `wgsent0.conf` with: private key, server public key, endpoint, allowed IPs, DNS
450
+ 2. Calls `wireguard.exe /installtunnelservice <path>/wgsent0.conf`
451
+ 3. Windows creates a `WireGuardTunnel$wgsent0` service
452
+ 4. Waits for the adapter to appear and traffic to flow
453
+ 5. Config file must remain on disk — the service reads it at startup
454
+
455
+ **V2Ray path:**
456
+ 1. Builds client config JSON with inbound (SOCKS5 proxy) and outbounds (one per transport)
457
+ 2. Spawns `v2ray.exe run -config <path>/config.json`
458
+ 3. Tests each outbound sequentially (TCP probe → SOCKS5 connectivity → traffic test)
459
+ 4. First working outbound wins — remaining are skipped
460
+ 5. SOCKS5 port: `10800 + random(1000)` — randomized to avoid TIME_WAIT collisions
461
+
462
+ ### Successful Progress Output — WireGuard
463
+ ```
464
+ [tunnel] Writing WireGuard config: wgsent0.conf
465
+ [tunnel] Config: MTU=1280, DNS=10.8.0.1, PersistentKeepalive=15
466
+ [tunnel] Installing tunnel service...
467
+ [tunnel] Service WireGuardTunnel$wgsent0 started
468
+ [tunnel] Adapter active: wgsent0
469
+ [tunnel] Waiting 3s for tunnel stabilization...
470
+ [tunnel] Tunnel ready
471
+ ```
472
+
473
+ ### Successful Progress Output — V2Ray
474
+ ```
475
+ [tunnel] Building V2Ray config with 3 outbounds
476
+ [tunnel] SOCKS5 inbound on 127.0.0.1:11342
477
+ [tunnel] Starting V2Ray process (PID 14208)
478
+ [tunnel] Testing outbound 1/3: grpc/none on port 8686...
479
+ [tunnel] TCP probe: port 8686 open (142ms)
480
+ [tunnel] SOCKS5 test: connected (1.2s)
481
+ [tunnel] Outbound 1 works — using grpc/none
482
+ ```
483
+
484
+ ### Failure Signatures
485
+
486
+ | Symptom | Cause | Fix |
487
+ |---------|-------|-----|
488
+ | `WG_NOT_AVAILABLE` or `Access denied` | Not running as admin | Elevate: `run-admin.vbs` (Windows) or `sudo` |
489
+ | `Service failed to start` | Orphaned WireGuard tunnel from previous crash | Run `wireguard.exe /uninstalltunnelservice wgsent0` manually |
490
+ | `MTU errors` or `TLS handshake failure through tunnel` | MTU set to 1420 instead of 1280 | SDK uses 1280. If building manually: ALWAYS use 1280 |
491
+ | All 3 V2Ray outbounds fail TCP probe | Node's V2Ray ports are firewalled | Try a different node |
492
+ | V2Ray starts but SOCKS5 returns `connection refused` | Port collision — another process on the same port | SDK randomizes port. If persistent: kill orphaned V2Ray processes |
493
+ | `WG_NO_CONNECTIVITY` | WireGuard tunnel installed but no traffic flows | Node may be overloaded or routing broken. Disconnect and try another node |
494
+ | Internet dies completely for ~78 seconds | Full-tunnel WireGuard installed before verification | SDK uses verify-before-capture: tests with split IPs first, then switches to full tunnel |
495
+ | V2Ray tunnel works for 10-18s then hangs | VLess with wrong `flow` field, OR VMess with clock drift >120s | Check node's clock. Ensure `flow: ''` for VLess |
496
+
497
+ ### Diagnostic Check — WireGuard
498
+ ```bash
499
+ # Check if WireGuard service exists
500
+ sc query WireGuardTunnel$wgsent0
501
+
502
+ # Check if adapter exists
503
+ netsh interface show interface name="wgsent0"
504
+
505
+ # Check if traffic flows
506
+ ping -n 1 10.8.0.1
507
+ ```
508
+
509
+ ### Diagnostic Check — V2Ray
510
+ ```bash
511
+ # Check if V2Ray process is running
512
+ tasklist | grep v2ray
513
+
514
+ # Test SOCKS5 proxy (replace port)
515
+ curl --socks5-hostname 127.0.0.1:11342 https://api.ipify.org
516
+
517
+ # IMPORTANT: Use axios with adapter:'http', NOT native fetch
518
+ # Native fetch silently ignores SOCKS5 proxy configuration
519
+ ```
520
+
521
+ ---
522
+
523
+ ## Phase 9: Verification
524
+
525
+ ### What Happens
526
+ The SDK confirms the tunnel is actually working by checking the public IP through the tunnel. This verifies end-to-end connectivity.
527
+
528
+ ### Code
529
+ ```javascript
530
+ import { verify, status } from 'sentinel-ai-connect';
531
+
532
+ const v = await verify();
533
+ console.log(v);
534
+
535
+ const s = status();
536
+ console.log(s);
537
+ ```
538
+
539
+ ### Successful Output
540
+ ```json
541
+ // verify()
542
+ {
543
+ "connected": true,
544
+ "ip": "45.76.32.100",
545
+ "verified": true
546
+ }
547
+
548
+ // status()
549
+ {
550
+ "connected": true,
551
+ "sessionId": "485721",
552
+ "protocol": "wireguard",
553
+ "nodeAddress": "sentnode1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5h38un",
554
+ "socksPort": null,
555
+ "uptimeMs": 12345,
556
+ "uptimeFormatted": "12s",
557
+ "ip": "45.76.32.100"
558
+ }
559
+ ```
560
+
561
+ ### Failure Signatures
562
+
563
+ | Symptom | Cause | Fix |
564
+ |---------|-------|-----|
565
+ | `verified: false`, `ip: null` | Tunnel is up but no traffic flows | Disconnect and try a different node |
566
+ | `ip` matches your real IP (not the node's) | Tunnel not actually routing traffic (split tunnel or DNS leak) | Check WireGuard AllowedIPs includes `0.0.0.0/0` |
567
+ | `connected: false` | Tunnel collapsed between installation and verification | Reconnect |
568
+ | Timeout on IP check | `api.ipify.org` blocked through this node | Non-critical — tunnel may still work. Try `curl https://ifconfig.me` manually |
569
+
570
+ ### Diagnostic Check
571
+ ```bash
572
+ # WireGuard: IP should match the node's IP
573
+ curl https://api.ipify.org
574
+
575
+ # V2Ray: IP should match the node's IP (must use SOCKS5)
576
+ curl --socks5-hostname 127.0.0.1:11342 https://api.ipify.org
577
+
578
+ # DNS leak check
579
+ nslookup whoami.akamai.net
580
+ # Should resolve through tunnel DNS, not your ISP's DNS
581
+ ```
582
+
583
+ ---
584
+
585
+ ## Complete Successful Connection — Full Log
586
+
587
+ This is the real output of a successful `connect()` call from start to finish:
588
+
589
+ ```
590
+ [wallet] Deriving wallet from mnemonic...
591
+ [wallet] Wallet ready: sent1abc...xyz
592
+ [wallet] Connecting to RPC: https://rpc.sentinel.co
593
+ [wallet] RPC connected (chain: sentinelhub-2, height: 18,234,567)
594
+ [wallet] LCD available: https://lcd.sentinel.co
595
+ [wallet] Balance: 47.69 P2P (47,690,000 udvpn)
596
+ [session] Querying active nodes...
597
+ [session] Found 1,030 active nodes
598
+ [session] Filtering: WireGuard preferred, country: any
599
+ [session] Selected: sentnode1qyp... (DE, WireGuard, 4.22 P2P/GB, 12 peers)
600
+ [session] Broadcasting MsgStartSession (1 GB, ~4.26 P2P total)...
601
+ [session] TX hash: 7A3F8B2C...
602
+ [session] Confirmed in block 18,234,573 (6.2s)
603
+ [session] Session ID: 485721
604
+ [handshake] POST https://45.76.32.100:8585/handshake
605
+ [handshake] WireGuard keys exchanged successfully
606
+ [tunnel] Installing WireGuard tunnel (MTU=1280, DNS=10.8.0.1)
607
+ [tunnel] Service WireGuardTunnel$wgsent0 started
608
+ [tunnel] Tunnel active — verifying connectivity...
609
+ [verify] IP changed: 45.76.32.100 (was: 203.0.113.50)
610
+ [verify] Tunnel verified — traffic flowing through node
611
+
612
+ Result: {
613
+ sessionId: "485721",
614
+ protocol: "wireguard",
615
+ nodeAddress: "sentnode1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5h38un",
616
+ socksPort: null,
617
+ ip: "45.76.32.100"
618
+ }
619
+ ```
620
+
621
+ **Total time:** ~20-45 seconds (depends on node responsiveness and chain block time)
622
+
623
+ ---
624
+
625
+ ## Failure Diagnosis Flowchart
626
+
627
+ If `connect()` throws an error, match the error against this progression to identify the exact failing phase:
628
+
629
+ ```
630
+ Error contains "mnemonic" or "wallet"?
631
+ → Phase 2 failure. Check mnemonic format.
632
+
633
+ Error contains "balance" or "insufficient"?
634
+ → Phase 3 failure. Fund wallet.
635
+
636
+ Error contains "0 nodes" or "no nodes"?
637
+ → Phase 4 failure. Check LCD endpoint connectivity.
638
+
639
+ Error contains "broadcast" or "sequence"?
640
+ → Phase 6 failure. Chain TX issue. Wait and retry.
641
+
642
+ Error contains "handshake" or "409" or "does not exist"?
643
+ → Phase 7 failure. Node communication issue. Try different node.
644
+
645
+ Error contains "V2RAY_NOT_FOUND" or "WG_NOT_AVAILABLE"?
646
+ → Phase 8 failure. Missing binary or admin rights.
647
+
648
+ Error contains "no connectivity" or "tunnel" or "timeout"?
649
+ → Phase 8-9 failure. Tunnel installed but broken. Try different node.
650
+
651
+ Error contains "ABORTED"?
652
+ → Timeout exceeded (default 120s). Increase timeout or check network.
653
+
654
+ None of the above?
655
+ → Check err.code and err.details for SDK error code.
656
+ → Report with full error output for diagnosis.
657
+ ```
658
+
659
+ ---
660
+
661
+ ## The One-Shot Test
662
+
663
+ Run this to verify everything works end-to-end in your environment:
664
+
665
+ ```javascript
666
+ import 'dotenv/config';
667
+ import { setup, getBalance, connect, verify, disconnect } from 'sentinel-ai-connect';
668
+
669
+ async function test() {
670
+ // Phase 1: Environment
671
+ console.log('--- Phase 1: Environment ---');
672
+ const env = await setup();
673
+ if (!env.ready) {
674
+ console.error('Environment not ready:', env.issues);
675
+ return;
676
+ }
677
+ console.log('Environment OK:', env.environment.capabilities);
678
+
679
+ // Phase 2-3: Wallet + Balance
680
+ console.log('\n--- Phase 2-3: Wallet + Balance ---');
681
+ const bal = await getBalance(process.env.MNEMONIC);
682
+ console.log(`Balance: ${bal.p2p} P2P, funded: ${bal.funded}`);
683
+ if (!bal.funded) {
684
+ console.error(`Fund wallet: ${bal.address}`);
685
+ return;
686
+ }
687
+
688
+ // Phase 4-9: Connect (handles discovery, payment, handshake, tunnel, verify)
689
+ console.log('\n--- Phase 4-9: Connect ---');
690
+ try {
691
+ const vpn = await connect({
692
+ mnemonic: process.env.MNEMONIC,
693
+ onProgress: (step, detail) => console.log(` [${step}] ${detail}`),
694
+ });
695
+
696
+ console.log('\n--- Result ---');
697
+ console.log(`Protocol: ${vpn.protocol}`);
698
+ console.log(`Node: ${vpn.nodeAddress}`);
699
+ console.log(`Session: ${vpn.sessionId}`);
700
+ console.log(`IP: ${vpn.ip}`);
701
+
702
+ // Extra verification
703
+ const v = await verify();
704
+ console.log(`Verified: ${v.verified}`);
705
+
706
+ // Disconnect
707
+ console.log('\n--- Disconnect ---');
708
+ await disconnect();
709
+ console.log('Disconnected successfully');
710
+
711
+ } catch (err) {
712
+ console.error(`FAILED: ${err.message}`);
713
+ console.error(`Code: ${err.code}`);
714
+ console.error(`Details: ${JSON.stringify(err.details)}`);
715
+ // Match against the Failure Diagnosis Flowchart above
716
+ }
717
+ }
718
+
719
+ test();
720
+ ```
721
+
722
+ **Expected:** All phases complete, IP changes, `verified: true`, clean disconnect.
723
+
724
+ **If it fails:** The `onProgress` output shows exactly which phase succeeded before the failure. Match the last successful phase and the error against the tables above.