@serfab/cadre-host 1.13.0 → 1.15.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 (352) hide show
  1. package/README.md +347 -423
  2. package/dist/bin/host.d.ts +6 -3
  3. package/dist/bin/host.js +332 -524
  4. package/dist/bin/host.js.map +1 -1
  5. package/dist/bin/join-output.d.ts +56 -0
  6. package/dist/bin/join-output.js +92 -0
  7. package/dist/bin/join-output.js.map +1 -0
  8. package/dist/bin/nat-output.d.ts +54 -0
  9. package/dist/bin/nat-output.js +138 -0
  10. package/dist/bin/nat-output.js.map +1 -0
  11. package/dist/bin/ui-access.d.ts +22 -0
  12. package/dist/bin/ui-access.js +44 -0
  13. package/dist/bin/ui-access.js.map +1 -0
  14. package/dist/hosted/claim-details.d.ts +66 -0
  15. package/dist/hosted/claim-details.js +60 -0
  16. package/dist/hosted/claim-details.js.map +1 -0
  17. package/dist/hosted/hosted-node-service.d.ts +325 -0
  18. package/dist/hosted/hosted-node-service.js +678 -0
  19. package/dist/hosted/hosted-node-service.js.map +1 -0
  20. package/dist/hosted/hosted-node-store.d.ts +40 -0
  21. package/dist/hosted/hosted-node-store.js +126 -0
  22. package/dist/hosted/hosted-node-store.js.map +1 -0
  23. package/dist/{donation/donation-supervisor.d.ts → hosted/hosted-node-supervisor.d.ts} +70 -53
  24. package/dist/hosted/hosted-node-supervisor.js +396 -0
  25. package/dist/hosted/hosted-node-supervisor.js.map +1 -0
  26. package/dist/hosted/hosted-node-watcher.d.ts +95 -0
  27. package/dist/hosted/hosted-node-watcher.js +260 -0
  28. package/dist/hosted/hosted-node-watcher.js.map +1 -0
  29. package/dist/hosted/index.d.ts +16 -0
  30. package/dist/hosted/index.js +13 -0
  31. package/dist/hosted/index.js.map +1 -0
  32. package/dist/hosted/node-status.d.ts +51 -0
  33. package/dist/hosted/node-status.js +77 -0
  34. package/dist/hosted/node-status.js.map +1 -0
  35. package/dist/hosted/types.d.ts +141 -0
  36. package/dist/hosted/types.js +22 -0
  37. package/dist/hosted/types.js.map +1 -0
  38. package/dist/index.d.ts +12 -24
  39. package/dist/index.js +9 -19
  40. package/dist/index.js.map +1 -1
  41. package/dist/installer/config.d.ts +27 -34
  42. package/dist/installer/config.js +46 -15
  43. package/dist/installer/config.js.map +1 -1
  44. package/dist/installer/index.d.ts +9 -19
  45. package/dist/installer/index.js +21 -85
  46. package/dist/installer/index.js.map +1 -1
  47. package/dist/installer/paths.d.ts +0 -2
  48. package/dist/installer/paths.js +0 -4
  49. package/dist/installer/paths.js.map +1 -1
  50. package/dist/installer/wizard.d.ts +12 -9
  51. package/dist/installer/wizard.js +15 -14
  52. package/dist/installer/wizard.js.map +1 -1
  53. package/dist/nat/address-resolver.d.ts +18 -17
  54. package/dist/nat/address-resolver.js +79 -25
  55. package/dist/nat/address-resolver.js.map +1 -1
  56. package/dist/nat/address-watch.d.ts +37 -0
  57. package/dist/nat/address-watch.js +84 -0
  58. package/dist/nat/address-watch.js.map +1 -0
  59. package/dist/nat/ddns/duckdns.d.ts +2 -1
  60. package/dist/nat/ddns/duckdns.js +5 -1
  61. package/dist/nat/ddns/duckdns.js.map +1 -1
  62. package/dist/nat/ddns/index.d.ts +6 -0
  63. package/dist/nat/ddns/index.js.map +1 -1
  64. package/dist/nat/index.d.ts +13 -9
  65. package/dist/nat/index.js +7 -5
  66. package/dist/nat/index.js.map +1 -1
  67. package/dist/nat/nat-service.d.ts +220 -77
  68. package/dist/nat/nat-service.js +653 -239
  69. package/dist/nat/nat-service.js.map +1 -1
  70. package/dist/nat/nat-store.d.ts +12 -5
  71. package/dist/nat/nat-store.js +124 -29
  72. package/dist/nat/nat-store.js.map +1 -1
  73. package/dist/nat/network-mode.d.ts +19 -0
  74. package/dist/nat/network-mode.js +32 -0
  75. package/dist/nat/network-mode.js.map +1 -0
  76. package/dist/nat/port-mapper.d.ts +83 -79
  77. package/dist/nat/port-mapper.js +148 -189
  78. package/dist/nat/port-mapper.js.map +1 -1
  79. package/dist/nat/reachability.d.ts +32 -15
  80. package/dist/nat/reachability.js +110 -22
  81. package/dist/nat/reachability.js.map +1 -1
  82. package/dist/nat/types.d.ts +98 -27
  83. package/dist/nat/types.js +8 -5
  84. package/dist/nat/types.js.map +1 -1
  85. package/dist/orchestrator/host-process-orchestrator.d.ts +32 -74
  86. package/dist/orchestrator/host-process-orchestrator.js +84 -233
  87. package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
  88. package/dist/{installer/identity.d.ts → orchestrator/identity-file.d.ts} +5 -5
  89. package/dist/{installer/identity.js → orchestrator/identity-file.js} +6 -6
  90. package/dist/orchestrator/identity-file.js.map +1 -0
  91. package/dist/orchestrator/index.d.ts +2 -3
  92. package/dist/orchestrator/index.js +1 -1
  93. package/dist/orchestrator/index.js.map +1 -1
  94. package/dist/orchestrator/node-identity.d.ts +6 -6
  95. package/dist/orchestrator/node-identity.js +7 -7
  96. package/dist/orchestrator/node-identity.js.map +1 -1
  97. package/dist/orchestrator/port-allocator.d.ts +5 -6
  98. package/dist/orchestrator/port-allocator.js +9 -11
  99. package/dist/orchestrator/port-allocator.js.map +1 -1
  100. package/dist/orchestrator/state-store.d.ts +3 -5
  101. package/dist/orchestrator/state-store.js +2 -10
  102. package/dist/orchestrator/state-store.js.map +1 -1
  103. package/dist/orchestrator/types.d.ts +33 -22
  104. package/dist/orchestrator/types.js.map +1 -1
  105. package/dist/package-version.d.ts +1 -0
  106. package/dist/package-version.js +4 -0
  107. package/dist/package-version.js.map +1 -0
  108. package/dist/server/error-handler.d.ts +5 -25
  109. package/dist/server/error-handler.js +11 -74
  110. package/dist/server/error-handler.js.map +1 -1
  111. package/dist/server/events/types.d.ts +7 -11
  112. package/dist/server/events/types.js +2 -1
  113. package/dist/server/events/types.js.map +1 -1
  114. package/dist/server/index.d.ts +11 -37
  115. package/dist/server/index.js +30 -48
  116. package/dist/server/index.js.map +1 -1
  117. package/dist/server/routes/hosted-nodes.d.ts +28 -0
  118. package/dist/server/routes/hosted-nodes.js +68 -0
  119. package/dist/server/routes/hosted-nodes.js.map +1 -0
  120. package/dist/server/routes/nat.d.ts +4 -1
  121. package/dist/server/routes/nat.js +13 -2
  122. package/dist/server/routes/nat.js.map +1 -1
  123. package/dist/server/routes/nodes.d.ts +6 -20
  124. package/dist/server/routes/nodes.js +7 -86
  125. package/dist/server/routes/nodes.js.map +1 -1
  126. package/dist/server/routes/settings.d.ts +3 -7
  127. package/dist/server/routes/settings.js +18 -8
  128. package/dist/server/routes/settings.js.map +1 -1
  129. package/dist/server/routes/status.d.ts +2 -25
  130. package/dist/server/routes/status.js +3 -43
  131. package/dist/server/routes/status.js.map +1 -1
  132. package/dist/server/server.js +8 -8
  133. package/dist/server/settings-store.d.ts +2 -2
  134. package/dist/server/settings-store.js +2 -2
  135. package/dist/server/static.js +1 -2
  136. package/dist/server/static.js.map +1 -1
  137. package/dist/ui/assets/index-BHd8CWZA.css +1 -0
  138. package/dist/ui/assets/index-Ccr2GjPT.js +23 -0
  139. package/dist/ui/assets/index-Ccr2GjPT.js.map +1 -0
  140. package/dist/ui/index.html +2 -2
  141. package/dist/update/index.d.ts +2 -2
  142. package/dist/update/index.js +2 -2
  143. package/dist/update/store.d.ts +1 -1
  144. package/dist/update/store.js +1 -1
  145. package/dist/update/types.d.ts +1 -1
  146. package/dist/update/types.js +1 -1
  147. package/package.json +5 -7
  148. package/src/__tests__/cli-nat.smoke.test.ts +219 -212
  149. package/src/__tests__/cli.smoke.test.ts +2 -2
  150. package/src/__tests__/global-setup.ts +2 -3
  151. package/src/__tests__/orchestrator-claim-secret.test.ts +111 -0
  152. package/src/__tests__/orchestrator-env-scrub.test.ts +160 -163
  153. package/src/__tests__/orchestrator-handle-contract.ts +134 -134
  154. package/src/__tests__/orchestrator-node-identity.test.ts +177 -203
  155. package/src/__tests__/orchestrator-ports.test.ts +124 -143
  156. package/src/__tests__/orchestrator-push.test.ts +138 -146
  157. package/src/__tests__/orchestrator.test.ts +1017 -1063
  158. package/src/__tests__/ui-access.test.ts +30 -0
  159. package/src/bin/host.ts +397 -572
  160. package/src/bin/join-output.ts +120 -0
  161. package/src/bin/nat-output.ts +188 -0
  162. package/src/bin/ui-access.ts +50 -0
  163. package/src/{donation → hosted}/__tests__/fake-orchestrator.test.ts +172 -173
  164. package/src/{donation → hosted}/__tests__/fake-orchestrator.ts +43 -23
  165. package/src/hosted/__tests__/hosted-node-service.test.ts +708 -0
  166. package/src/hosted/__tests__/hosted-node-supervisor.test.ts +475 -0
  167. package/src/hosted/__tests__/test-invitation.ts +33 -0
  168. package/src/hosted/claim-details.ts +103 -0
  169. package/src/hosted/hosted-node-service.ts +814 -0
  170. package/src/hosted/hosted-node-store.ts +144 -0
  171. package/src/hosted/hosted-node-supervisor.ts +439 -0
  172. package/src/hosted/hosted-node-watcher.ts +288 -0
  173. package/src/hosted/index.ts +53 -0
  174. package/src/hosted/node-status.ts +97 -0
  175. package/src/hosted/types.ts +160 -0
  176. package/src/index.ts +72 -91
  177. package/src/installer/__tests__/config.test.ts +15 -40
  178. package/src/installer/__tests__/installer.smoke.test.ts +184 -263
  179. package/src/installer/__tests__/wizard.test.ts +25 -13
  180. package/src/installer/config.ts +201 -175
  181. package/src/installer/index.ts +252 -327
  182. package/src/installer/paths.ts +104 -109
  183. package/src/installer/wizard.ts +24 -26
  184. package/src/nat/__tests__/address-resolver.test.ts +107 -82
  185. package/src/nat/__tests__/nat-service.test.ts +608 -515
  186. package/src/nat/__tests__/nat-store.test.ts +49 -12
  187. package/src/nat/__tests__/network-mode.test.ts +38 -0
  188. package/src/nat/__tests__/port-mapper.test.ts +30 -191
  189. package/src/nat/__tests__/reachability.test.ts +96 -13
  190. package/src/nat/address-resolver.ts +94 -52
  191. package/src/nat/address-watch.ts +92 -0
  192. package/src/nat/ddns/duckdns.ts +5 -1
  193. package/src/nat/ddns/index.ts +6 -0
  194. package/src/nat/index.ts +33 -13
  195. package/src/nat/nat-service.ts +942 -501
  196. package/src/nat/nat-store.ts +126 -31
  197. package/src/nat/network-mode.ts +36 -0
  198. package/src/nat/port-mapper.ts +241 -329
  199. package/src/nat/reachability.ts +155 -34
  200. package/src/nat/types.ts +110 -37
  201. package/src/{installer/__tests__/identity.test.ts → orchestrator/__tests__/identity-file.test.ts} +34 -34
  202. package/src/orchestrator/__tests__/node-identity.test.ts +62 -62
  203. package/src/orchestrator/host-process-orchestrator.ts +1089 -1260
  204. package/src/{installer/identity.ts → orchestrator/identity-file.ts} +56 -56
  205. package/src/orchestrator/index.ts +9 -10
  206. package/src/orchestrator/node-identity.ts +41 -41
  207. package/src/orchestrator/port-allocator.ts +71 -73
  208. package/src/orchestrator/state-store.ts +76 -90
  209. package/src/orchestrator/types.ts +34 -23
  210. package/src/package-version.ts +4 -0
  211. package/src/server/__tests__/bus.test.ts +38 -38
  212. package/src/server/__tests__/error-handler.test.ts +81 -118
  213. package/src/server/__tests__/fakes.ts +15 -18
  214. package/src/server/__tests__/hosted-nodes-route.test.ts +161 -0
  215. package/src/server/__tests__/nodes-route.test.ts +114 -283
  216. package/src/server/__tests__/publishers.test.ts +193 -180
  217. package/src/server/__tests__/server.smoke.test.ts +134 -223
  218. package/src/server/__tests__/settings-route.test.ts +210 -195
  219. package/src/server/__tests__/sse-route.test.ts +119 -119
  220. package/src/server/__tests__/status-route.test.ts +82 -121
  221. package/src/server/error-handler.ts +112 -178
  222. package/src/server/events/types.ts +20 -24
  223. package/src/server/index.ts +223 -267
  224. package/src/server/routes/hosted-nodes.ts +83 -0
  225. package/src/server/routes/nat.ts +75 -63
  226. package/src/server/routes/nodes.ts +96 -188
  227. package/src/server/routes/settings.ts +20 -15
  228. package/src/server/routes/status.ts +5 -67
  229. package/src/server/server.ts +8 -8
  230. package/src/server/settings-store.ts +2 -2
  231. package/src/server/static.ts +1 -2
  232. package/src/update/index.ts +2 -2
  233. package/src/update/store.ts +1 -1
  234. package/src/update/types.ts +1 -1
  235. package/dist/auth/duration.d.ts +0 -1
  236. package/dist/auth/duration.js +0 -32
  237. package/dist/auth/duration.js.map +0 -1
  238. package/dist/auth/index.d.ts +0 -11
  239. package/dist/auth/index.js +0 -9
  240. package/dist/auth/index.js.map +0 -1
  241. package/dist/auth/self-label.d.ts +0 -36
  242. package/dist/auth/self-label.js +0 -57
  243. package/dist/auth/self-label.js.map +0 -1
  244. package/dist/auth/trust-circle-store.d.ts +0 -26
  245. package/dist/auth/trust-circle-store.js +0 -129
  246. package/dist/auth/trust-circle-store.js.map +0 -1
  247. package/dist/auth/trust-circle.d.ts +0 -156
  248. package/dist/auth/trust-circle.js +0 -307
  249. package/dist/auth/trust-circle.js.map +0 -1
  250. package/dist/auth/types.d.ts +0 -84
  251. package/dist/auth/types.js +0 -24
  252. package/dist/auth/types.js.map +0 -1
  253. package/dist/donation/donation-service.d.ts +0 -389
  254. package/dist/donation/donation-service.js +0 -828
  255. package/dist/donation/donation-service.js.map +0 -1
  256. package/dist/donation/donation-store.d.ts +0 -51
  257. package/dist/donation/donation-store.js +0 -152
  258. package/dist/donation/donation-store.js.map +0 -1
  259. package/dist/donation/donation-supervisor.js +0 -364
  260. package/dist/donation/donation-supervisor.js.map +0 -1
  261. package/dist/donation/grant-service.d.ts +0 -80
  262. package/dist/donation/grant-service.js +0 -210
  263. package/dist/donation/grant-service.js.map +0 -1
  264. package/dist/donation/grant-store.d.ts +0 -38
  265. package/dist/donation/grant-store.js +0 -135
  266. package/dist/donation/grant-store.js.map +0 -1
  267. package/dist/donation/index.d.ts +0 -20
  268. package/dist/donation/index.js +0 -17
  269. package/dist/donation/index.js.map +0 -1
  270. package/dist/donation/types.d.ts +0 -224
  271. package/dist/donation/types.js +0 -40
  272. package/dist/donation/types.js.map +0 -1
  273. package/dist/installer/identity.js.map +0 -1
  274. package/dist/owner/index.d.ts +0 -11
  275. package/dist/owner/index.js +0 -11
  276. package/dist/owner/index.js.map +0 -1
  277. package/dist/owner/owner-node-client.d.ts +0 -110
  278. package/dist/owner/owner-node-client.js +0 -184
  279. package/dist/owner/owner-node-client.js.map +0 -1
  280. package/dist/server/routes/bootstrap-node-validation.d.ts +0 -115
  281. package/dist/server/routes/bootstrap-node-validation.js +0 -197
  282. package/dist/server/routes/bootstrap-node-validation.js.map +0 -1
  283. package/dist/server/routes/grants-admin.d.ts +0 -29
  284. package/dist/server/routes/grants-admin.js +0 -58
  285. package/dist/server/routes/grants-admin.js.map +0 -1
  286. package/dist/server/routes/grants.d.ts +0 -28
  287. package/dist/server/routes/grants.js +0 -165
  288. package/dist/server/routes/grants.js.map +0 -1
  289. package/dist/server/routes/provision-request-validation.d.ts +0 -45
  290. package/dist/server/routes/provision-request-validation.js +0 -97
  291. package/dist/server/routes/provision-request-validation.js.map +0 -1
  292. package/dist/server/routes/strands.d.ts +0 -20
  293. package/dist/server/routes/strands.js +0 -38
  294. package/dist/server/routes/strands.js.map +0 -1
  295. package/dist/server/routes/trust-circle.d.ts +0 -17
  296. package/dist/server/routes/trust-circle.js +0 -35
  297. package/dist/server/routes/trust-circle.js.map +0 -1
  298. package/dist/strands/index.d.ts +0 -7
  299. package/dist/strands/index.js +0 -6
  300. package/dist/strands/index.js.map +0 -1
  301. package/dist/strands/strand-service.d.ts +0 -50
  302. package/dist/strands/strand-service.js +0 -103
  303. package/dist/strands/strand-service.js.map +0 -1
  304. package/dist/strands/types.d.ts +0 -93
  305. package/dist/strands/types.js +0 -23
  306. package/dist/strands/types.js.map +0 -1
  307. package/dist/ui/assets/index-C6nELNfC.js +0 -23
  308. package/dist/ui/assets/index-C6nELNfC.js.map +0 -1
  309. package/dist/ui/assets/index-TtIYSeZO.css +0 -1
  310. package/src/__tests__/cli-invite.smoke.test.ts +0 -192
  311. package/src/__tests__/host-owner.smoke.test.ts +0 -123
  312. package/src/__tests__/orchestrator-owner.test.ts +0 -234
  313. package/src/__tests__/orchestrator-pin-keys.test.ts +0 -152
  314. package/src/auth/__tests__/duration.test.ts +0 -28
  315. package/src/auth/__tests__/self-label.test.ts +0 -85
  316. package/src/auth/__tests__/trust-circle-integration.test.ts +0 -152
  317. package/src/auth/__tests__/trust-circle-store.test.ts +0 -115
  318. package/src/auth/__tests__/trust-circle.test.ts +0 -445
  319. package/src/auth/duration.ts +0 -33
  320. package/src/auth/index.ts +0 -21
  321. package/src/auth/self-label.ts +0 -74
  322. package/src/auth/trust-circle-store.ts +0 -155
  323. package/src/auth/trust-circle.ts +0 -395
  324. package/src/auth/types.ts +0 -99
  325. package/src/donation/__tests__/donation-service.test.ts +0 -1193
  326. package/src/donation/__tests__/donation-supervisor.test.ts +0 -548
  327. package/src/donation/__tests__/grant-service.test.ts +0 -213
  328. package/src/donation/__tests__/grant-store.test.ts +0 -113
  329. package/src/donation/donation-service.ts +0 -1033
  330. package/src/donation/donation-store.ts +0 -176
  331. package/src/donation/donation-supervisor.ts +0 -411
  332. package/src/donation/grant-service.ts +0 -252
  333. package/src/donation/grant-store.ts +0 -152
  334. package/src/donation/index.ts +0 -65
  335. package/src/donation/types.ts +0 -260
  336. package/src/owner/__tests__/owner-node-client.test.ts +0 -194
  337. package/src/owner/index.ts +0 -12
  338. package/src/owner/owner-node-client.ts +0 -259
  339. package/src/server/__tests__/bootstrap-node-validation.test.ts +0 -138
  340. package/src/server/__tests__/grants-admin-route.test.ts +0 -171
  341. package/src/server/__tests__/grants-route.test.ts +0 -394
  342. package/src/server/__tests__/strands-route.test.ts +0 -234
  343. package/src/server/routes/bootstrap-node-validation.ts +0 -206
  344. package/src/server/routes/grants-admin.ts +0 -74
  345. package/src/server/routes/grants.ts +0 -193
  346. package/src/server/routes/provision-request-validation.ts +0 -105
  347. package/src/server/routes/strands.ts +0 -54
  348. package/src/server/routes/trust-circle.ts +0 -49
  349. package/src/strands/__tests__/strand-service.test.ts +0 -165
  350. package/src/strands/index.ts +0 -14
  351. package/src/strands/strand-service.ts +0 -135
  352. package/src/strands/types.ts +0 -102
@@ -1,37 +1,98 @@
1
1
  /**
2
2
  * NAT/DDNS types for cadre-host.
3
3
  *
4
- * cadre-host runs on home/SMB machines that are typically behind NAT. To be
5
- * dialable from the open internet the host must:
6
- * 1. Punch a port forward on the upstream router (UPnP/NAT-PMP).
4
+ * cadre-host runs on home/SMB machines that are typically behind NAT. For the
5
+ * nodes it runs to be dialable from the open internet the host must:
6
+ * 1. Map every hosted node's two libp2p ports (TCP, and the WebSocket port a
7
+ * phone dials) through the upstream router, or let the user forward them
8
+ * by hand.
7
9
  * 2. Detect its external IP (and whether it's behind CGNAT).
8
10
  * 3. Optionally publish a stable hostname via dynamic DNS.
9
11
  *
10
- * State for these three concerns lives in `<rootDir>/nat.json` (user settings)
11
- * and is exposed as a single `NatStatusSnapshot` to the management UI.
12
+ * State for these concerns lives in `<rootDir>/nat.json` (user settings) and is
13
+ * exposed as a single `NatStatusSnapshot` to the management UI, with one
14
+ * `NodeReachability` entry per hosted node.
12
15
  *
13
16
  * Credentials for DDNS providers are NOT stored in `nat.json`. They go through
14
17
  * the SecretsStore (keytar, with a 0600 file fallback).
15
18
  */
16
- /** Port-forwarding mode currently in effect. */
17
- export type PortForwardMode = 'auto-upnp' | 'auto-natpmp' | 'manual' | 'failed' | 'disabled';
18
- /** Reachability verdict — best-effort heuristic until AutoNAT lands. */
19
+ /** Reachability roll-up across the host's running nodes — see `reachability.ts`. */
19
20
  export type DirectReachability = 'reachable' | 'unreachable' | 'unknown' | 'cgnat';
20
- /** Snapshot returned to the management UI / CLI. */
21
- export interface NatStatusSnapshot {
22
- portMode: PortForwardMode;
23
- externalPort: number;
21
+ /** The two ports of a hosted node that are mapped: libp2p TCP and libp2p WebSocket. */
22
+ export type PortKind = 'tcp' | 'ws';
23
+ /**
24
+ * How this machine meets the internet. `lan`: behind a router (home or office), so node ports need
25
+ * UPnP or a forward on the router. `public`: an interface holds a public IPv4 (a VPS, a server with a
26
+ * routed address), so node ports are reachable at their own numbers unless a firewall blocks them.
27
+ */
28
+ export type NetworkMode = 'lan' | 'public';
29
+ /** The `network` setting: `auto` picks the mode from this machine's interfaces. */
30
+ export type NetworkSetting = 'auto' | NetworkMode;
31
+ /** The network section of the status snapshot. */
32
+ export interface NatNetworkStatus {
33
+ setting: NetworkSetting;
34
+ /** The mode in effect: the setting, or what `auto` found. */
35
+ mode: NetworkMode;
36
+ /** The public IPv4 an interface holds, when one does; what `auto` decides on. */
37
+ publicInterfaceIp: string | null;
38
+ }
39
+ /** Where a port's external route came from. `direct`: public mode, the port as-is. */
40
+ export type PortRouteSource = 'upnp' | 'manual' | 'direct';
41
+ /** How one of a node's ports is reached from outside the home network. */
42
+ export interface PortRoute {
24
43
  internalPort: number;
25
- /** IP the router reports as its WAN address. Null when not auto-mapped. */
44
+ /** Port reachable from outside; null when there is no route. */
45
+ externalPort: number | null;
46
+ source: PortRouteSource | null;
47
+ /** ISO timestamp when the router lease expires; UPnP routes only. */
48
+ leaseExpiresAt: string | null;
49
+ /** Last mapping failure, in plain language; null when the last attempt succeeded. */
50
+ error: string | null;
51
+ }
52
+ /** Per-node verdict. `direct`: reachable at this machine's public address, firewall permitting. */
53
+ export type NodeVerdict = 'mapped' | 'manual' | 'direct' | 'unreachable';
54
+ /** Reachability of one hosted node, as reported in the status snapshot. */
55
+ export interface NodeReachability {
56
+ nodeId: string;
57
+ /** Whether the node's process is running; a stopped node keeps its last routes until the unmap grace elapses. */
58
+ running: boolean;
59
+ verdict: NodeVerdict;
60
+ /**
61
+ * Plain-language reason and remedy when unreachable, e.g. "Router refused
62
+ * the WebSocket port mapping. Forward port 10004 to 192.168.1.20 on your
63
+ * router, then tell cadre-host the external port." For `direct`, the
64
+ * firewall reminder: which ports to allow.
65
+ */
66
+ reason: string | null;
67
+ tcp: PortRoute;
68
+ /** Null for a handle persisted by an older build without a WebSocket port. */
69
+ ws: PortRoute | null;
70
+ /** Multiaddrs (no `/p2p/` suffix) through which the node is reachable from outside. */
71
+ publicAddrs: string[];
72
+ }
73
+ /** The UPnP gateway as the host last saw it. */
74
+ export interface NatGatewayStatus {
75
+ found: boolean;
76
+ /** This machine's address on the router's subnet — the address a user forwards to. */
77
+ lanAddress: string | null;
78
+ /** The WAN address the router reports for itself. */
26
79
  routerExternalIp: string | null;
27
- /** ISO timestamp when the current mapping lease expires. */
28
- mappingLeaseExpiresAt: string | null;
80
+ /** Why discovery or the last gateway operation failed; null when it did not. */
81
+ lastError: string | null;
82
+ }
83
+ /** Snapshot returned to the management UI / CLI. */
84
+ export interface NatStatusSnapshot {
85
+ network: NatNetworkStatus;
86
+ upnpEnabled: boolean;
87
+ gateway: NatGatewayStatus;
29
88
  externalIp: string | null;
30
89
  externalIpDetectedAt: string | null;
31
90
  cgnatDetected: boolean;
91
+ /** Roll-up across running nodes; see `reachability.ts`. */
32
92
  directReachability: DirectReachability;
33
93
  lastTestedAt: string | null;
34
94
  ddns: NatDdnsStatus;
95
+ nodes: NodeReachability[];
35
96
  }
36
97
  /** DDNS sub-section of the status snapshot. */
37
98
  export interface NatDdnsStatus {
@@ -48,18 +109,28 @@ export interface NatDdnsStatus {
48
109
  /** Most recent error message, null when last attempt succeeded. */
49
110
  lastError: string | null;
50
111
  }
112
+ /** The external ports a user forwarded on their router for one node, per port kind. */
113
+ export interface ManualForward {
114
+ tcp?: number;
115
+ ws?: number;
116
+ }
117
+ /** Patch applied by `PUT /nat/nodes/:nodeId/forward`: `null` clears that port. */
118
+ export interface ManualForwardPatch {
119
+ tcp?: number | null;
120
+ ws?: number | null;
121
+ }
51
122
  /**
52
123
  * On-disk shape of `nat.json`. NEVER contains DDNS credentials — those live
53
124
  * in the SecretsStore.
54
125
  */
55
126
  export interface NatSettingsFile {
56
127
  version: 1;
57
- /** External port to map (and announce). Default 4001. */
58
- externalPort: number;
59
- /** Internal libp2p port. Default 4001. */
60
- internalPort: number;
61
- /** Whether to attempt UPnP/NAT-PMP at all. */
128
+ /** LAN or public-IP mode; `auto` (default) decides from the interfaces. Absent in older files. */
129
+ network: NetworkSetting;
130
+ /** Whether to ask the router for port mappings at all. Ignored in public mode. */
62
131
  upnpEnabled: boolean;
132
+ /** Per node id: the external port the user forwarded on their router, per port kind. */
133
+ forwards: Record<string, ManualForward>;
63
134
  ddns: NatDdnsSettings;
64
135
  }
65
136
  /** DDNS sub-section of `nat.json`. */
@@ -79,8 +150,8 @@ export interface NatDdnsSettings {
79
150
  }
80
151
  /** Typed error codes for NatError. */
81
152
  export type NatErrorCode = 'mapping_failed' | 'router_unreachable' | 'ip_detection_failed' | 'ddns_provider_unknown' | 'ddns_credentials_missing' | 'ddns_update_failed' | 'secrets_unavailable' | 'storage_error' | 'invalid_config'
82
- /** The owner node's admin channel is unreachable / not ready. */
83
- | 'node_unavailable';
153
+ /** A manual forward named a node id the host does not run. */
154
+ | 'unknown_node';
84
155
  /** Typed error carrying a stable `code` for HTTP mapping in the local-ui. */
85
156
  export declare class NatError extends Error {
86
157
  readonly code: NatErrorCode;
@@ -102,11 +173,10 @@ export interface DdnsProviderInfo {
102
173
  configFields: ReadonlyArray<DdnsConfigField>;
103
174
  }
104
175
  /**
105
- * Typed HTTP handlers exposed to `cadre-host-local-ui` for Fastify wiring.
176
+ * Typed HTTP handlers exposed to the local UI server for Fastify wiring.
106
177
  *
107
- * Like TrustCircleHandlers, these take typed objects and either return typed
108
- * results or throw a `NatError` whose `.code` the UI ticket maps to an HTTP
109
- * status code.
178
+ * These take typed objects and either return typed results or throw a
179
+ * `NatError` whose `.code` the error handler maps to an HTTP status code.
110
180
  */
111
181
  export interface NatHandlers {
112
182
  getStatus(): Promise<NatStatusSnapshot>;
@@ -118,5 +188,6 @@ export interface NatHandlers {
118
188
  config: Record<string, string>;
119
189
  externallyManaged?: boolean;
120
190
  }): Promise<NatStatusSnapshot>;
121
- putSettings(body: Partial<Omit<NatSettingsFile, 'version'>>): Promise<NatStatusSnapshot>;
191
+ putSettings(body: Partial<Omit<NatSettingsFile, 'version' | 'forwards'>>): Promise<NatStatusSnapshot>;
192
+ putForward(nodeId: string, patch: ManualForwardPatch): Promise<NatStatusSnapshot>;
122
193
  }
package/dist/nat/types.js CHANGED
@@ -1,14 +1,17 @@
1
1
  /**
2
2
  * NAT/DDNS types for cadre-host.
3
3
  *
4
- * cadre-host runs on home/SMB machines that are typically behind NAT. To be
5
- * dialable from the open internet the host must:
6
- * 1. Punch a port forward on the upstream router (UPnP/NAT-PMP).
4
+ * cadre-host runs on home/SMB machines that are typically behind NAT. For the
5
+ * nodes it runs to be dialable from the open internet the host must:
6
+ * 1. Map every hosted node's two libp2p ports (TCP, and the WebSocket port a
7
+ * phone dials) through the upstream router, or let the user forward them
8
+ * by hand.
7
9
  * 2. Detect its external IP (and whether it's behind CGNAT).
8
10
  * 3. Optionally publish a stable hostname via dynamic DNS.
9
11
  *
10
- * State for these three concerns lives in `<rootDir>/nat.json` (user settings)
11
- * and is exposed as a single `NatStatusSnapshot` to the management UI.
12
+ * State for these concerns lives in `<rootDir>/nat.json` (user settings) and is
13
+ * exposed as a single `NatStatusSnapshot` to the management UI, with one
14
+ * `NodeReachability` entry per hosted node.
12
15
  *
13
16
  * Credentials for DDNS providers are NOT stored in `nat.json`. They go through
14
17
  * the SecretsStore (keytar, with a 0600 file fallback).
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/nat/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkGH,6EAA6E;AAC7E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGjC,YAAY,IAAkB,EAAE,OAAe;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/nat/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAwKH,6EAA6E;AAC7E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGjC,YAAY,IAAkB,EAAE,OAAe;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -1,5 +1,5 @@
1
- import { type Orchestrator, type OrchestratorCreateRequest, type OrchestratorCreateResult, type OrchestratorStats } from '@serfab/cadre-provider';
2
- import { type OwnerSpawnConfig, type HostProcessConfig, type ManagedNodeInfo, type NodePorts, type NodeStateListener } from './types.js';
1
+ import { type Orchestrator, type OrchestratorCreateResult, type OrchestratorStats } from '@serfab/cadre-provider';
2
+ import { type HostProcessConfig, type HostedSpawnRequest, type ManagedNodeInfo, type NodePorts, type NodeStateListener } from './types.js';
3
3
  /**
4
4
  * The libp2p listen addresses a managed child binds — its `CADRE_LISTEN_ADDRS`, one
5
5
  * entry per element: TCP on the `p2p` port and WebSocket on the `ws` port, both on
@@ -13,15 +13,6 @@ import { type OwnerSpawnConfig, type HostProcessConfig, type ManagedNodeInfo, ty
13
13
  * (`resolveTransportOptions` in `cadre-core/src/relay-addrs.ts`).
14
14
  */
15
15
  export declare function childListenAddrs(ports: Pick<NodePorts, 'p2p' | 'ws'>): string[];
16
- /** Fixed friendly id for the admin's owner node. */
17
- export declare const OWNER_CONTAINER_ID = "owner";
18
- /** Endpoint + bearer for the owner node's loopback admin channel (6.6). */
19
- export interface OwnerAdminEndpoint {
20
- /** e.g. `http://127.0.0.1:<adminPort>`. */
21
- baseUrl: string;
22
- /** The child's `CADRE_STARTUP_TOKEN`, used as the admin bearer secret. */
23
- token: string;
24
- }
25
16
  /**
26
17
  * Runs cadre nodes as plain Node child processes on the host.
27
18
  *
@@ -65,10 +56,10 @@ export declare class HostProcessOrchestrator implements Orchestrator {
65
56
  private readonly cliEntrypoint;
66
57
  /** Resolves push credentials fresh on every spawn (see HostProcessConfig). */
67
58
  private readonly pushResolver?;
59
+ /** The public addresses a node announces, asked at every spawn (see HostProcessConfig). */
60
+ private readonly announceAddrs?;
68
61
  /** State-change listeners — invoked when a handle's alive state changes. */
69
62
  private readonly stateListeners;
70
- /** Persisted spawn config for the owner node (re-spawn on demand). */
71
- private ownerConfig?;
72
63
  constructor(cfg: HostProcessConfig);
73
64
  /**
74
65
  * Read persisted state and re-attach to surviving children. Not part of
@@ -93,25 +84,27 @@ export declare class HostProcessOrchestrator implements Orchestrator {
93
84
  resolveDockerId(idOrDockerId: string): string | undefined;
94
85
  /**
95
86
  * Register a state-change listener. Invoked when a handle's `alive`
96
- * transitions (child exit, manual stop). Returns an unsubscribe fn.
87
+ * transitions (create, child exit, manual stop) and once more, as `stopped`,
88
+ * when a handle is removed. Returns an unsubscribe fn.
97
89
  */
98
90
  onStateChange(listener: NodeStateListener): () => void;
99
91
  /**
100
- * Spawn a generic managed node — the donated-node path (the requester's cadre,
101
- * pinned to the *requester's* owner key). The node is given its own protobuf
102
- * identity key inside its workdir (`ensureNodeIdentity`, reused across
92
+ * Spawn a managed node — a hosted node for some cadre, started waiting to be
93
+ * claimed when the request carries a `claimSecret`, or redeeming the cadre
94
+ * invitation it carries as `invitation`. The node is given its own
95
+ * protobuf identity key inside its workdir (`ensureNodeIdentity`, reused across
103
96
  * re-spawns of the same containerId), which is what makes its peer id stable
104
97
  * across restarts AND what makes its node-local stores durable: `cadre-cli
105
98
  * start` opens the file-backed bootstrap-peer, trusted-owner and strand
106
99
  * network-state stores in the node's state directory, which defaults to the
107
100
  * directory holding the `cadre.json` written here — the same workdir.
108
- * Terminating the loan (`removeContainer`) deletes the workdir, so the key and
101
+ * Removing the node (`removeContainer`) deletes the workdir, so the key and
109
102
  * every store go with it.
110
103
  */
111
- createContainer(request: OrchestratorCreateRequest): Promise<OrchestratorCreateResult>;
104
+ createContainer(request: HostedSpawnRequest): Promise<OrchestratorCreateResult>;
112
105
  /**
113
106
  * Remove the working directory a spawn created for `containerId` when no
114
- * handle owns it — the cleanup of last resort for a donation record that
107
+ * handle owns it — the cleanup of last resort for a hosted-node record that
115
108
  * never got a `dockerId` (the host died between `ensureNodeIdentity` and the
116
109
  * handle write, so `createContainer`'s own unwind never ran). Returns whether
117
110
  * anything was removed.
@@ -144,33 +137,17 @@ export declare class HostProcessOrchestrator implements Orchestrator {
144
137
  */
145
138
  private resolvePush;
146
139
  /**
147
- * Ensure the admin's owner node is running, spawning it if absent or
148
- * dead. Idempotent: a second call with the node already alive returns the
149
- * existing handle without launching a second child. The spawn parameters
150
- * are persisted so a later `/api/nodes/:id/{start,restart}` (or an
151
- * orchestrator restart) can re-spawn it without re-supplying them.
152
- *
153
- * The owner node is spawned as `cadre-cli start --owner
154
- * --admin-port <p> --identity-file <identityPath>` so it carries the
155
- * host's libp2p identity, founds/joins the control network, and exposes the
156
- * loopback admin channel the manager delegates to.
140
+ * The public addresses the node about to spawn announces. A hook that throws costs
141
+ * the node its public addresses, never its start: it still binds and is reachable
142
+ * on its LAN addresses, and `NatService` restarts it once the addresses it should
143
+ * announce differ from these.
157
144
  */
158
- ensureOwnerNode(cfg?: OwnerSpawnConfig): Promise<ManagedNodeInfo>;
159
- /** Endpoint + bearer for the owner node's admin channel, if spawned. */
160
- getOwnerAdminEndpoint(): OwnerAdminEndpoint | undefined;
161
- /** Whether spawn parameters for the owner node are known (persisted). */
162
- hasOwnerConfig(): boolean;
163
- /** Whether the given friendly/docker id refers to the owner node. */
164
- isOwnerNode(idOrDockerId: string): boolean;
165
- /** Stop the owner node (if running). Used on graceful shutdown. */
166
- stopOwnerNode(): Promise<void>;
167
- /** Stop the owner node (if running), then re-spawn it from saved config. */
168
- restartOwnerNode(): Promise<ManagedNodeInfo>;
145
+ private announceAddrsFor;
169
146
  /**
170
- * Core spawn mechanics shared by `createContainer` and `ensureOwnerNode`.
147
+ * Core spawn mechanics behind `createContainer`.
171
148
  *
172
149
  * The caller has already allocated `ports` and **owns their lifetime**: this
173
- * method never releases them, so every caller must release on any throw out
150
+ * method never releases them, so the caller must release on any throw out
174
151
  * of here (it has to unwind its handle drop on the same path anyway — see
175
152
  * `restoreDroppedHandles`).
176
153
  *
@@ -184,15 +161,15 @@ export declare class HostProcessOrchestrator implements Orchestrator {
184
161
  private launchChild;
185
162
  /**
186
163
  * Refuse to re-spawn `containerId` while a previous child of it may still be
187
- * running. Both spawn paths call this as their last `await` before the handle
164
+ * running. `createContainer` calls this as its last `await` before the handle
188
165
  * drop, so a refusal happens while nothing has been released, no token rotated
189
166
  * and no child launched — the caller sees an ordinary failed spawn.
190
167
  *
191
168
  * Why it exists: the drop hands the old handle's ports back and the launch takes
192
169
  * those same ports (`reusedNodePorts`), so a child started over a live one dies
193
- * on a port clash — but only after `launchChild` has returned success with a
194
- * fresh seed token. The caller then records a dead child, and the live one
195
- * refuses the new token.
170
+ * on a port clash — but only after `launchChild` has returned success. The
171
+ * caller then records a dead child, and the live one keeps running with no
172
+ * handle naming it.
196
173
  *
197
174
  * - A handle this process spawned is checked exactly ({@link isHandleLive}); a
198
175
  * live one refuses outright.
@@ -202,9 +179,6 @@ export declare class HostProcessOrchestrator implements Orchestrator {
202
179
  * is already bound, refuse. The pid is never killed on that evidence — it may
203
180
  * not be ours.
204
181
  *
205
- * `overrides` are the ports the caller will override on top of the reused ones
206
- * (the owner node's configured p2p port); `owner` adds the admin channel.
207
- *
208
182
  * NOTE: the probe cannot see a re-attached child still loading its modules —
209
183
  * `cadre-cli start` writes its token and only then binds, so a child in that
210
184
  * window shows neither, and a re-spawn launched then races it for the ports.
@@ -225,9 +199,9 @@ export declare class HostProcessOrchestrator implements Orchestrator {
225
199
  /**
226
200
  * Drop any handle left over from a previous spawn of the same `containerId`
227
201
  * and release its ports. Handles are keyed by the per-spawn `dockerId`, so
228
- * without this a re-spawn (donated-node respawn) would strand the prior
202
+ * without this a re-spawn (a hosted node's respawn) would strand the prior
229
203
  * handle in the map forever, leaking its ports from a bounded range each
230
- * time. Mirrors the same cleanup in `ensureOwnerNode`.
204
+ * time.
231
205
  *
232
206
  * DO NOT delete the workdir: the identity key and node-local stores
233
207
  * (trusted-owner anchor, retained cold-start dial targets) live there and are
@@ -237,16 +211,16 @@ export declare class HostProcessOrchestrator implements Orchestrator {
237
211
  * Releasing the ports hands them straight back to the allocator, and the
238
212
  * re-spawn then takes those same ports as overrides (`reusedNodePorts`), so a
239
213
  * re-spawn while the *previous* child is still listening would bind-clash.
240
- * Callers must therefore never drop a live child, and both do so only after
214
+ * The caller must therefore never drop a live child, and does so only after
241
215
  * {@link refuseRespawnOverLiveChild} has passed. A caller that needs to replace
242
216
  * a live child must stop it first.
243
217
  *
244
218
  * **The drop is reversible.** It happens before the launch can fail, so the
245
- * dropped handles are returned and every caller restores them via
219
+ * dropped handles are returned and the caller restores them via
246
220
  * {@link restoreDroppedHandles} when the launch throws — otherwise the caller
247
221
  * would be left holding a dockerId this orchestrator no longer knows, and the
248
222
  * node's workdir could never be reclaimed. On a *successful* spawn the drop
249
- * stands: `DonationService.abandonRespawn` depends on the old handle being
223
+ * stands: `HostedNodeService.abandonRespawn` depends on the old handle being
250
224
  * gone by then.
251
225
  */
252
226
  private dropStaleHandle;
@@ -257,10 +231,10 @@ export declare class HostProcessOrchestrator implements Orchestrator {
257
231
  *
258
232
  * **Precondition: the drop → launch window is synchronous.** Restoring is
259
233
  * sound only because nothing can have taken those ports or run cleanup
260
- * against the handle in between — `launchChild` contains no `await` and both
261
- * callers resolve push credentials *before* their drop. `launchChild`'s half
234
+ * against the handle in between — `launchChild` contains no `await` and the
235
+ * caller resolves push credentials *before* its drop. `launchChild`'s half
262
236
  * is enforced: its declared return type is not a Promise, so adding an
263
- * `await` there is a compile error. The callers' half is not — an `await`
237
+ * `await` there is a compile error. The caller's half is not — an `await`
264
238
  * slipped between a drop and its launch silently breaks this.
265
239
  *
266
240
  * No `persist()` here: `state.json` is rewritten only by `launchChild` and
@@ -278,8 +252,6 @@ export declare class HostProcessOrchestrator implements Orchestrator {
278
252
  private restoreDroppedHandles;
279
253
  /** A managed node's own directory under `rootDir` — config, log, storage, identity. */
280
254
  private workdirFor;
281
- /** Locate the owner node's handle, if one has been spawned. */
282
- private findOwnerHandle;
283
255
  /**
284
256
  * Inverse of {@link releasePorts} — hold a handle's whole port set against
285
257
  * the allocator. Used both to rehydrate on `init` and to undo a drop whose
@@ -299,20 +271,6 @@ export declare class HostProcessOrchestrator implements Orchestrator {
299
271
  getLogs(dockerId: string, tail?: number): Promise<string>;
300
272
  private requireHandle;
301
273
  private buildChildConfig;
302
- /**
303
- * Child config for the **host's own personal cadre** owner node — the opt-in
304
- * "founder" persona spawned only when `ownCadre.enabled` (see
305
- * docs/cadre-host.md § Two roles: donor and founder). It founds/joins the
306
- * control network for the host's own `partyId` (no bootstrap peers — it is the
307
- * founding node of the host's *own* cadre) and carries the host identity via
308
- * `--identity-file` (passed as a spawn arg).
309
- *
310
- * This is NOT the node cadre-host donates to a requester. Donated nodes are
311
- * generic (`createContainer` / `buildChildConfig`): they join the
312
- * **requester's** cadre via bootstrap peers and pin the *requester's* owner
313
- * key — they never run a host genesis.
314
- */
315
- private buildOwnerChildConfig;
316
274
  private persist;
317
275
  private emitStateChange;
318
276
  private toPersisted;