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
package/errors.js ADDED
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Sentinel SDK — Typed Error Classes
3
+ *
4
+ * Machine-readable error codes for programmatic error handling.
5
+ * All SDK errors extend SentinelError with a .code property.
6
+ *
7
+ * Usage:
8
+ * import { SentinelError, ErrorCodes } from './errors.js';
9
+ * try { await connect(opts); }
10
+ * catch (e) {
11
+ * if (e.code === ErrorCodes.V2RAY_ALL_FAILED) trySwitchNode();
12
+ * if (e instanceof ValidationError) showFormError(e.message);
13
+ * }
14
+ */
15
+
16
+ export class SentinelError extends Error {
17
+ /**
18
+ * @param {string} code - Machine-readable error code (e.g. 'NODE_NO_UDVPN')
19
+ * @param {string} message - Human-readable description
20
+ * @param {object} details - Structured context for programmatic handling
21
+ */
22
+ constructor(code, message, details = {}) {
23
+ super(message);
24
+ this.name = 'SentinelError';
25
+ this.code = code;
26
+ this.details = details;
27
+ }
28
+
29
+ toJSON() {
30
+ return { name: this.name, code: this.code, message: this.message, details: this.details };
31
+ }
32
+ }
33
+
34
+ /** Input validation failures (bad mnemonic, invalid address, etc.) */
35
+ export class ValidationError extends SentinelError {
36
+ constructor(code, message, details) {
37
+ super(code, message, details);
38
+ this.name = 'ValidationError';
39
+ }
40
+ }
41
+
42
+ /** Node-level failures (offline, no udvpn, clock drift, etc.) */
43
+ export class NodeError extends SentinelError {
44
+ constructor(code, message, details) {
45
+ super(code, message, details);
46
+ this.name = 'NodeError';
47
+ }
48
+ }
49
+
50
+ /** Chain/transaction failures (broadcast failed, extract failed, etc.) */
51
+ export class ChainError extends SentinelError {
52
+ constructor(code, message, details) {
53
+ super(code, message, details);
54
+ this.name = 'ChainError';
55
+ }
56
+ }
57
+
58
+ /** Tunnel setup failures (V2Ray all failed, WG no connectivity, etc.) */
59
+ export class TunnelError extends SentinelError {
60
+ constructor(code, message, details) {
61
+ super(code, message, details);
62
+ this.name = 'TunnelError';
63
+ }
64
+ }
65
+
66
+ /** Security failures (TLS cert changed, etc.) */
67
+ export class SecurityError extends SentinelError {
68
+ constructor(code, message, details) {
69
+ super(code, message, details);
70
+ this.name = 'SecurityError';
71
+ }
72
+ }
73
+
74
+ /** Error code constants — use these for switch/if checks instead of string parsing */
75
+ export const ErrorCodes = {
76
+ // Validation
77
+ INVALID_OPTIONS: 'INVALID_OPTIONS',
78
+ INVALID_MNEMONIC: 'INVALID_MNEMONIC',
79
+ INVALID_NODE_ADDRESS: 'INVALID_NODE_ADDRESS',
80
+ INVALID_GIGABYTES: 'INVALID_GIGABYTES',
81
+ INVALID_URL: 'INVALID_URL',
82
+ INVALID_PLAN_ID: 'INVALID_PLAN_ID',
83
+
84
+ // Node
85
+ NODE_OFFLINE: 'NODE_OFFLINE',
86
+ NODE_NO_UDVPN: 'NODE_NO_UDVPN',
87
+ NODE_NOT_FOUND: 'NODE_NOT_FOUND',
88
+ NODE_CLOCK_DRIFT: 'NODE_CLOCK_DRIFT',
89
+ NODE_INACTIVE: 'NODE_INACTIVE',
90
+
91
+ // Chain
92
+ INSUFFICIENT_BALANCE: 'INSUFFICIENT_BALANCE',
93
+ BROADCAST_FAILED: 'BROADCAST_FAILED',
94
+ TX_FAILED: 'TX_FAILED',
95
+ LCD_ERROR: 'LCD_ERROR',
96
+ UNKNOWN_MSG_TYPE: 'UNKNOWN_MSG_TYPE',
97
+ ALL_ENDPOINTS_FAILED: 'ALL_ENDPOINTS_FAILED',
98
+
99
+ // Session
100
+ SESSION_EXISTS: 'SESSION_EXISTS',
101
+ SESSION_EXTRACT_FAILED: 'SESSION_EXTRACT_FAILED',
102
+ SESSION_POISONED: 'SESSION_POISONED',
103
+
104
+ // Tunnel
105
+ V2RAY_NOT_FOUND: 'V2RAY_NOT_FOUND',
106
+ V2RAY_ALL_FAILED: 'V2RAY_ALL_FAILED',
107
+ WG_NOT_AVAILABLE: 'WG_NOT_AVAILABLE',
108
+ WG_NO_CONNECTIVITY: 'WG_NO_CONNECTIVITY',
109
+ TUNNEL_SETUP_FAILED: 'TUNNEL_SETUP_FAILED',
110
+
111
+ // Security
112
+ TLS_CERT_CHANGED: 'TLS_CERT_CHANGED',
113
+
114
+ // Node (additional)
115
+ INVALID_ASSIGNED_IP: 'INVALID_ASSIGNED_IP',
116
+ NODE_DATABASE_CORRUPT: 'NODE_DATABASE_CORRUPT',
117
+
118
+ // Connection
119
+ ABORTED: 'ABORTED',
120
+ ALL_NODES_FAILED: 'ALL_NODES_FAILED',
121
+ ALREADY_CONNECTED: 'ALREADY_CONNECTED',
122
+ PARTIAL_CONNECTION_FAILED: 'PARTIAL_CONNECTION_FAILED',
123
+
124
+ // Chain timing
125
+ CHAIN_LAG: 'CHAIN_LAG',
126
+ };
127
+
128
+ // ─── Error Severity Classification ───────────────────────────────────────────
129
+
130
+ /** Severity levels for each error code. Apps use this for retry/UX logic. */
131
+ export const ERROR_SEVERITY = {
132
+ // Fatal — don't retry, user action needed
133
+ [ErrorCodes.INVALID_MNEMONIC]: 'fatal',
134
+ [ErrorCodes.INSUFFICIENT_BALANCE]: 'fatal',
135
+ [ErrorCodes.INVALID_NODE_ADDRESS]: 'fatal',
136
+ [ErrorCodes.INVALID_OPTIONS]: 'fatal',
137
+ [ErrorCodes.INVALID_GIGABYTES]: 'fatal',
138
+ [ErrorCodes.INVALID_URL]: 'fatal',
139
+ [ErrorCodes.INVALID_PLAN_ID]: 'fatal',
140
+ [ErrorCodes.UNKNOWN_MSG_TYPE]: 'fatal',
141
+ [ErrorCodes.SESSION_POISONED]: 'fatal',
142
+ [ErrorCodes.WG_NOT_AVAILABLE]: 'fatal',
143
+ [ErrorCodes.NODE_DATABASE_CORRUPT]: 'retryable',
144
+
145
+ // Retryable — node-level
146
+ [ErrorCodes.NODE_NOT_FOUND]: 'retryable',
147
+
148
+ // Retryable — try again, possibly different node
149
+ [ErrorCodes.NODE_OFFLINE]: 'retryable',
150
+ [ErrorCodes.NODE_NO_UDVPN]: 'retryable',
151
+ [ErrorCodes.NODE_CLOCK_DRIFT]: 'retryable',
152
+ [ErrorCodes.NODE_INACTIVE]: 'retryable',
153
+ [ErrorCodes.V2RAY_ALL_FAILED]: 'retryable',
154
+ [ErrorCodes.BROADCAST_FAILED]: 'retryable',
155
+ [ErrorCodes.TX_FAILED]: 'retryable',
156
+ [ErrorCodes.LCD_ERROR]: 'retryable',
157
+ [ErrorCodes.ALL_ENDPOINTS_FAILED]: 'retryable',
158
+ [ErrorCodes.ALL_NODES_FAILED]: 'retryable',
159
+ [ErrorCodes.WG_NO_CONNECTIVITY]: 'retryable',
160
+ [ErrorCodes.TUNNEL_SETUP_FAILED]: 'retryable',
161
+ [ErrorCodes.CHAIN_LAG]: 'retryable',
162
+
163
+ // Recoverable — can resume with recoverSession()
164
+ [ErrorCodes.SESSION_EXTRACT_FAILED]: 'recoverable',
165
+ [ErrorCodes.PARTIAL_CONNECTION_FAILED]: 'recoverable',
166
+ [ErrorCodes.SESSION_EXISTS]: 'recoverable',
167
+
168
+ // Infrastructure — check system state
169
+ [ErrorCodes.TLS_CERT_CHANGED]: 'infrastructure',
170
+ [ErrorCodes.V2RAY_NOT_FOUND]: 'infrastructure',
171
+ };
172
+
173
+ /** Check if an error should be retried. */
174
+ export function isRetryable(error) {
175
+ const code = error?.code || error;
176
+ return ERROR_SEVERITY[code] === 'retryable';
177
+ }
178
+
179
+ /** Map SDK error to user-friendly message. */
180
+ export function userMessage(error) {
181
+ const code = error?.code || error;
182
+ const map = {
183
+ [ErrorCodes.INSUFFICIENT_BALANCE]: 'Not enough P2P tokens. Fund your wallet to continue.',
184
+ [ErrorCodes.NODE_OFFLINE]: 'This node is offline. Try a different server.',
185
+ [ErrorCodes.NODE_NO_UDVPN]: 'This node does not accept P2P tokens.',
186
+ [ErrorCodes.NODE_CLOCK_DRIFT]: 'Node clock is out of sync. Try a different server.',
187
+ [ErrorCodes.NODE_INACTIVE]: 'Node went inactive. Try a different server.',
188
+ [ErrorCodes.V2RAY_ALL_FAILED]: 'Could not establish tunnel. Node may be overloaded.',
189
+ [ErrorCodes.V2RAY_NOT_FOUND]: 'V2Ray binary not found. Check your installation.',
190
+ [ErrorCodes.WG_NOT_AVAILABLE]: 'WireGuard is not available. Install it or use V2Ray nodes.',
191
+ [ErrorCodes.WG_NO_CONNECTIVITY]: 'VPN tunnel has no internet connectivity.',
192
+ [ErrorCodes.TUNNEL_SETUP_FAILED]: 'Tunnel setup failed. Try again or pick another server.',
193
+ [ErrorCodes.TLS_CERT_CHANGED]: 'Node certificate changed unexpectedly. This could indicate a security issue.',
194
+ [ErrorCodes.BROADCAST_FAILED]: 'Transaction failed. Check your balance and try again.',
195
+ [ErrorCodes.TX_FAILED]: 'Chain transaction rejected. Check balance and gas.',
196
+ [ErrorCodes.ALREADY_CONNECTED]: 'Already connected. Disconnect first.',
197
+ [ErrorCodes.ALL_NODES_FAILED]: 'All servers failed. Check your network connection.',
198
+ [ErrorCodes.ALL_ENDPOINTS_FAILED]: 'All chain endpoints are unreachable. Try again later.',
199
+ [ErrorCodes.INVALID_MNEMONIC]: 'Invalid wallet phrase. Must be 12 or 24 words.',
200
+ [ErrorCodes.INVALID_NODE_ADDRESS]: 'Invalid node address.',
201
+ [ErrorCodes.INVALID_OPTIONS]: 'Invalid connection options provided.',
202
+ [ErrorCodes.INVALID_GIGABYTES]: 'Invalid bandwidth amount. Must be a positive number.',
203
+ [ErrorCodes.INVALID_URL]: 'Invalid URL format.',
204
+ [ErrorCodes.INVALID_PLAN_ID]: 'Invalid plan ID.',
205
+ [ErrorCodes.UNKNOWN_MSG_TYPE]: 'Unknown message type. Check SDK version compatibility.',
206
+ [ErrorCodes.SESSION_POISONED]: 'Session is poisoned (previously failed). Start a new session.',
207
+ [ErrorCodes.NODE_NOT_FOUND]: 'Node not found on chain. It may be inactive.',
208
+ [ErrorCodes.LCD_ERROR]: 'Chain query failed. Try again later.',
209
+ [ErrorCodes.SESSION_EXISTS]: 'An active session already exists. Use recoverSession() to resume.',
210
+ [ErrorCodes.SESSION_EXTRACT_FAILED]: 'Session creation succeeded but ID extraction failed. Use recoverSession().',
211
+ [ErrorCodes.PARTIAL_CONNECTION_FAILED]: 'Payment succeeded but connection failed. Use recoverSession() to retry.',
212
+ [ErrorCodes.ABORTED]: 'Connection was cancelled.',
213
+ [ErrorCodes.CHAIN_LAG]: 'Session not yet confirmed on node. Wait a moment and try again.',
214
+ [ErrorCodes.NODE_DATABASE_CORRUPT]: 'Node has a corrupted database. Try a different server.',
215
+ [ErrorCodes.INVALID_ASSIGNED_IP]: 'Node returned an invalid IP address during handshake. Try a different server.',
216
+ };
217
+ return map[code] || error?.message || 'An unexpected error occurred.';
218
+ }
@@ -0,0 +1,64 @@
1
+ # Sentinel dVPN SDK Examples
2
+
3
+ Runnable examples showing how to use the SDK. Each file is self-contained.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 18+
8
+ - `npm install sentinel-dvpn-sdk`
9
+ - For wallet/connection examples: set `MNEMONIC` environment variable (12 or 24-word BIP39 phrase)
10
+ - For WireGuard nodes: run as admin/root. Without admin, only V2Ray nodes are available (~70% of the network).
11
+
12
+ ## Examples
13
+
14
+ | File | Description | Needs Wallet? |
15
+ |------|-------------|:---:|
16
+ | `connect-direct.mjs` | Complete VPN flow: wallet, balance, find nodes, connect, verify IP, disconnect | Yes |
17
+ | `query-nodes.mjs` | Browse nodes, filter by country/protocol/price, display table | No |
18
+ | `wallet-basics.mjs` | Generate a new wallet or import existing, check balance | Optional |
19
+ | `connect-plan.mjs` | Connect via a subscription plan (operator-managed node bundles) | Yes |
20
+ | `error-handling.mjs` | Typed error codes, severity levels, retry logic patterns | Yes |
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ # Browse the network (no wallet needed)
26
+ node query-nodes.mjs
27
+ node query-nodes.mjs --country germany --protocol wireguard
28
+
29
+ # Generate a new wallet
30
+ node wallet-basics.mjs
31
+
32
+ # Check balance of an existing wallet
33
+ MNEMONIC="your twelve word phrase here ..." node wallet-basics.mjs
34
+
35
+ # Connect to VPN (simplest)
36
+ MNEMONIC="your twelve word phrase here ..." node connect-direct.mjs
37
+
38
+ # Connect to a specific country
39
+ MNEMONIC="your twelve word phrase here ..." node connect-direct.mjs --country finland
40
+
41
+ # Connect via a plan
42
+ MNEMONIC="your twelve word phrase here ..." node connect-plan.mjs --plan-id 42
43
+
44
+ # Explore error handling
45
+ MNEMONIC="your twelve word phrase here ..." node error-handling.mjs
46
+ ```
47
+
48
+ ## Token Info
49
+
50
+ - Display name: **P2P**
51
+ - Chain denom: `udvpn` (micro denomination)
52
+ - 1 P2P = 1,000,000 udvpn
53
+ - Typical session cost: ~0.04-0.15 P2P per GB
54
+ - Minimum recommended balance: 1 P2P
55
+
56
+ ## Key Concepts
57
+
58
+ **connectAuto vs connectDirect**: `connectAuto` scans nodes, filters, retries on failure. `connectDirect` connects to one specific node address. Start with `connectAuto`.
59
+
60
+ **WireGuard vs V2Ray**: WireGuard is faster but requires admin. V2Ray works without admin and supports more transport types. The SDK picks the best available.
61
+
62
+ **Plans vs Direct**: Direct connections pay per GB/hour to individual nodes. Plans are operator bundles with fixed pricing and optional fee grants (operator pays gas for subscribers).
63
+
64
+ **Error severity**: `fatal` = user must fix, `retryable` = try another node, `recoverable` = call `recoverSession()`, `infrastructure` = check system setup.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * connect-direct.mjs — Connect to Sentinel dVPN in ~60 lines
3
+ *
4
+ * The complete flow: wallet -> balance check -> find nodes -> connect -> verify -> disconnect.
5
+ * Uses connectAuto() which handles node selection, retries, and fallback automatically.
6
+ *
7
+ * Prerequisites:
8
+ * - Node.js 18+
9
+ * - npm install sentinel-dvpn-sdk
10
+ * - Set MNEMONIC env var (12 or 24-word BIP39 phrase)
11
+ * - Run as admin/root for WireGuard support (otherwise V2Ray nodes only)
12
+ *
13
+ * Usage:
14
+ * MNEMONIC="word1 word2 ... word12" node connect-direct.mjs
15
+ * MNEMONIC="word1 word2 ..." node connect-direct.mjs --country germany
16
+ */
17
+
18
+ import {
19
+ createWallet,
20
+ getBalance,
21
+ createClient,
22
+ formatP2P,
23
+ connectAuto,
24
+ disconnect,
25
+ verifyConnection,
26
+ registerCleanupHandlers,
27
+ queryOnlineNodes,
28
+ filterNodes,
29
+ DEFAULT_RPC,
30
+ DEFAULT_LCD,
31
+ } from 'sentinel-dvpn-sdk';
32
+
33
+ const MNEMONIC = process.env.MNEMONIC;
34
+ if (!MNEMONIC) {
35
+ console.error('Set MNEMONIC environment variable (12 or 24 BIP39 words)');
36
+ process.exit(1);
37
+ }
38
+
39
+ // Parse optional --country flag
40
+ const countryArg = process.argv.includes('--country')
41
+ ? process.argv[process.argv.indexOf('--country') + 1]
42
+ : null;
43
+
44
+ async function main() {
45
+ // 1. Register cleanup handlers (ensures VPN tunnel is torn down on exit)
46
+ registerCleanupHandlers();
47
+
48
+ // 2. Create wallet from mnemonic
49
+ const { account } = await createWallet(MNEMONIC);
50
+ console.log(`Wallet: ${account.address}`);
51
+
52
+ // 3. Check balance
53
+ const client = await createClient(MNEMONIC);
54
+ const balance = await getBalance(client, account.address);
55
+ console.log(`Balance: ${formatP2P(balance.udvpn)}`);
56
+
57
+ if (balance.udvpn < 1_000_000) {
58
+ console.error('Insufficient balance. Need at least 1 P2P (~1,000,000 udvpn).');
59
+ process.exit(1);
60
+ }
61
+
62
+ // 4. Preview available nodes (optional — connectAuto does this internally)
63
+ const nodes = await queryOnlineNodes({ maxNodes: 20 });
64
+ const filtered = countryArg ? filterNodes(nodes, { country: countryArg }) : nodes;
65
+ console.log(`Found ${filtered.length} nodes${countryArg ? ` in ${countryArg}` : ''}`);
66
+ for (const n of filtered.slice(0, 5)) {
67
+ console.log(` ${n.address.slice(0, 20)}... | ${n.country || '?'} | ${n.serviceType} | score: ${n.qualityScore}`);
68
+ }
69
+
70
+ // 5. Connect — handles node selection, payment, handshake, and tunnel setup
71
+ console.log('\nConnecting...');
72
+ const result = await connectAuto({
73
+ mnemonic: MNEMONIC,
74
+ countries: countryArg ? [countryArg] : undefined,
75
+ maxAttempts: 3,
76
+ gigabytes: 1,
77
+ log: (msg) => console.log(msg),
78
+ onProgress: (step, detail) => console.log(` [${step}] ${detail}`),
79
+ });
80
+
81
+ console.log(`\nConnected to ${result.nodeAddress}`);
82
+ console.log(` Session ID: ${result.sessionId}`);
83
+ console.log(` Protocol: ${result.protocol}`);
84
+
85
+ // 6. Verify VPN is working (check external IP)
86
+ const check = await verifyConnection();
87
+ if (check.working) {
88
+ console.log(` VPN IP: ${check.vpnIp}`);
89
+ } else {
90
+ console.warn(' IP check failed — tunnel may still be working');
91
+ }
92
+
93
+ // 7. Keep connected for 30 seconds, then disconnect
94
+ console.log('\nVPN active. Press Ctrl+C to disconnect (auto-disconnect in 30s)...');
95
+ await new Promise((r) => setTimeout(r, 30_000));
96
+
97
+ // 8. Disconnect cleanly (ends session on-chain, tears down tunnel)
98
+ await disconnect();
99
+ console.log('Disconnected.');
100
+ }
101
+
102
+ main().catch((err) => {
103
+ console.error(`Error: ${err.message}`);
104
+ if (err.code) console.error(` Code: ${err.code}`);
105
+ process.exit(1);
106
+ });
@@ -0,0 +1,125 @@
1
+ /**
2
+ * connect-plan.mjs — Connect to Sentinel dVPN via a subscription plan
3
+ *
4
+ * Plans are created by operators who bundle nodes at a fixed rate.
5
+ * Subscribers get access to all nodes in the plan. Operators can pay gas
6
+ * on behalf of subscribers via fee grants (making it free for end users).
7
+ *
8
+ * Prerequisites:
9
+ * - Node.js 18+
10
+ * - npm install sentinel-dvpn-sdk
11
+ * - Set MNEMONIC env var
12
+ * - Run as admin/root for WireGuard support
13
+ *
14
+ * Usage:
15
+ * MNEMONIC="word1 word2 ..." node connect-plan.mjs # Auto-discover plans
16
+ * MNEMONIC="word1 word2 ..." node connect-plan.mjs --plan-id 42 # Specific plan
17
+ */
18
+
19
+ import {
20
+ createWallet,
21
+ createClient,
22
+ getBalance,
23
+ formatP2P,
24
+ discoverPlans,
25
+ queryPlanNodes,
26
+ connectViaPlan,
27
+ disconnect,
28
+ verifyConnection,
29
+ registerCleanupHandlers,
30
+ formatPriceP2P,
31
+ DEFAULT_LCD,
32
+ } from 'sentinel-dvpn-sdk';
33
+
34
+ const MNEMONIC = process.env.MNEMONIC;
35
+ if (!MNEMONIC) {
36
+ console.error('Set MNEMONIC environment variable (12 or 24 BIP39 words)');
37
+ process.exit(1);
38
+ }
39
+
40
+ const planIdArg = process.argv.includes('--plan-id')
41
+ ? process.argv[process.argv.indexOf('--plan-id') + 1]
42
+ : null;
43
+
44
+ async function main() {
45
+ registerCleanupHandlers();
46
+
47
+ // 1. Wallet and balance
48
+ const { account } = await createWallet(MNEMONIC);
49
+ const client = await createClient(MNEMONIC);
50
+ const balance = await getBalance(client, account.address);
51
+ console.log(`Wallet: ${account.address} | Balance: ${formatP2P(balance.udvpn)}\n`);
52
+
53
+ let planId;
54
+ let nodeAddress;
55
+
56
+ if (planIdArg) {
57
+ // 2a. Use the specified plan
58
+ planId = parseInt(planIdArg, 10);
59
+ console.log(`Using plan ${planId}`);
60
+ } else {
61
+ // 2b. Discover available plans
62
+ console.log('Discovering plans (probing IDs 1-100, may take 10-20s)...');
63
+ const plans = await discoverPlans(DEFAULT_LCD, { maxId: 100 });
64
+
65
+ if (plans.length === 0) {
66
+ console.error('No plans found. Try a specific plan ID with --plan-id.');
67
+ process.exit(1);
68
+ }
69
+
70
+ // Show available plans
71
+ console.log(`\nFound ${plans.length} plans:\n`);
72
+ console.log(' ID'.padEnd(8), 'Subscribers'.padEnd(14), 'Nodes'.padEnd(8), 'Price');
73
+ console.log(' ' + '-'.repeat(50));
74
+ for (const p of plans) {
75
+ const price = p.price ? `${formatPriceP2P(p.price.amount || '0')} P2P` : 'N/A';
76
+ console.log(
77
+ ` ${String(p.id).padEnd(8)}${String(p.subscribers).padEnd(14)}${String(p.nodeCount).padEnd(8)}${price}`,
78
+ );
79
+ }
80
+
81
+ // Pick the plan with the most nodes
82
+ const best = plans.sort((a, b) => b.nodeCount - a.nodeCount)[0];
83
+ planId = best.id;
84
+ console.log(`\nSelected plan ${planId} (${best.nodeCount} nodes, ${best.subscribers} subscribers)`);
85
+ }
86
+
87
+ // 3. Get nodes in the plan
88
+ const { items: planNodes } = await queryPlanNodes(planId);
89
+ if (planNodes.length === 0) {
90
+ console.error(`Plan ${planId} has no nodes. Try a different plan.`);
91
+ process.exit(1);
92
+ }
93
+ nodeAddress = planNodes[0].address;
94
+ console.log(`Plan has ${planNodes.length} nodes. Connecting to ${nodeAddress}...`);
95
+
96
+ // 4. Connect via the plan
97
+ const result = await connectViaPlan({
98
+ mnemonic: MNEMONIC,
99
+ planId,
100
+ nodeAddress,
101
+ log: (msg) => console.log(msg),
102
+ onProgress: (step, detail) => console.log(` [${step}] ${detail}`),
103
+ });
104
+
105
+ console.log(`\nConnected via plan ${planId}`);
106
+ console.log(` Session: ${result.sessionId}`);
107
+ console.log(` Protocol: ${result.protocol}`);
108
+
109
+ // 5. Verify
110
+ const check = await verifyConnection();
111
+ if (check.working) console.log(` VPN IP: ${check.vpnIp}`);
112
+
113
+ // 6. Stay connected
114
+ console.log('\nVPN active. Press Ctrl+C to disconnect (auto-disconnect in 30s)...');
115
+ await new Promise((r) => setTimeout(r, 30_000));
116
+
117
+ await disconnect();
118
+ console.log('Disconnected.');
119
+ }
120
+
121
+ main().catch((err) => {
122
+ console.error(`Error: ${err.message}`);
123
+ if (err.code) console.error(` Code: ${err.code}`);
124
+ process.exit(1);
125
+ });
@@ -0,0 +1,109 @@
1
+ /**
2
+ * error-handling.mjs — Typed error handling with the Sentinel SDK
3
+ *
4
+ * The SDK uses typed errors with machine-readable codes, severity levels,
5
+ * and user-friendly messages. This example shows how to handle errors
6
+ * programmatically instead of just catching strings.
7
+ *
8
+ * Usage:
9
+ * MNEMONIC="word1 word2 ..." node error-handling.mjs
10
+ */
11
+
12
+ import {
13
+ SentinelError,
14
+ ValidationError,
15
+ NodeError,
16
+ ChainError,
17
+ TunnelError,
18
+ ErrorCodes,
19
+ ERROR_SEVERITY,
20
+ isRetryable,
21
+ userMessage,
22
+ connectAuto,
23
+ registerCleanupHandlers,
24
+ disconnect,
25
+ } from 'sentinel-dvpn-sdk';
26
+
27
+ const MNEMONIC = process.env.MNEMONIC;
28
+ if (!MNEMONIC) {
29
+ console.error('Set MNEMONIC environment variable');
30
+ process.exit(1);
31
+ }
32
+
33
+ // --- Show the error taxonomy ---
34
+ console.log('Sentinel SDK Error Codes:\n');
35
+ console.log(' Code'.padEnd(32), 'Severity'.padEnd(16), 'Retryable?');
36
+ console.log(' ' + '-'.repeat(60));
37
+ for (const [name, code] of Object.entries(ErrorCodes)) {
38
+ const severity = ERROR_SEVERITY[code] || 'unknown';
39
+ const retry = isRetryable(code) ? 'yes' : 'no';
40
+ console.log(` ${code.padEnd(32)}${severity.padEnd(16)}${retry}`);
41
+ }
42
+
43
+ // --- Demonstrate real error handling ---
44
+ console.log('\n\nAttempting connection with full error handling...\n');
45
+
46
+ async function main() {
47
+ registerCleanupHandlers();
48
+
49
+ try {
50
+ const result = await connectAuto({
51
+ mnemonic: MNEMONIC,
52
+ maxAttempts: 2,
53
+ log: (msg) => console.log(msg),
54
+ });
55
+ console.log(`Connected to ${result.nodeAddress}`);
56
+ await disconnect();
57
+ } catch (err) {
58
+ // 1. Check if it is a typed SDK error
59
+ if (!(err instanceof SentinelError)) {
60
+ console.error('Unexpected error (not from SDK):', err.message);
61
+ return;
62
+ }
63
+
64
+ // 2. Use the error code for programmatic handling
65
+ console.log(`SDK Error: ${err.code}`);
66
+ console.log(` Class: ${err.constructor.name}`);
67
+ console.log(` Message: ${err.message}`);
68
+ console.log(` Severity: ${ERROR_SEVERITY[err.code] || 'unknown'}`);
69
+ console.log(` User msg: ${userMessage(err)}`);
70
+
71
+ // 3. Severity-based logic
72
+ const severity = ERROR_SEVERITY[err.code];
73
+
74
+ if (severity === 'fatal') {
75
+ // User must fix something (bad mnemonic, no balance, etc.)
76
+ console.log('\n Action: Show error to user, do not retry.');
77
+ } else if (severity === 'retryable') {
78
+ // Transient failure — try again with a different node
79
+ console.log('\n Action: Retry with a different node or wait and try again.');
80
+ } else if (severity === 'recoverable') {
81
+ // Partial success — session exists but tunnel failed
82
+ console.log('\n Action: Call recoverSession() to resume without re-paying.');
83
+ } else if (severity === 'infrastructure') {
84
+ // System-level issue (missing binary, cert mismatch)
85
+ console.log('\n Action: Check system dependencies.');
86
+ }
87
+
88
+ // 4. Type-specific handling
89
+ if (err instanceof ValidationError) {
90
+ console.log('\n Input validation failed. Check your parameters.');
91
+ } else if (err instanceof NodeError) {
92
+ console.log(`\n Node-level issue. Details: ${JSON.stringify(err.details)}`);
93
+ } else if (err instanceof ChainError) {
94
+ console.log('\n Chain/transaction issue. Check balance and gas.');
95
+ } else if (err instanceof TunnelError) {
96
+ console.log('\n Tunnel setup failed. Try another node or check WireGuard/V2Ray.');
97
+ }
98
+
99
+ // 5. Structured details are always available
100
+ if (Object.keys(err.details).length > 0) {
101
+ console.log(`\n Details: ${JSON.stringify(err.details, null, 2)}`);
102
+ }
103
+ }
104
+ }
105
+
106
+ main().catch((err) => {
107
+ console.error('Unhandled:', err);
108
+ process.exit(1);
109
+ });