@serfab/cadre-host 1.13.0 → 1.14.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 (337) hide show
  1. package/README.md +332 -423
  2. package/dist/bin/host.d.ts +6 -3
  3. package/dist/bin/host.js +301 -505
  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 +49 -0
  9. package/dist/bin/nat-output.js +127 -0
  10. package/dist/bin/nat-output.js.map +1 -0
  11. package/dist/hosted/claim-details.d.ts +44 -0
  12. package/dist/hosted/claim-details.js +66 -0
  13. package/dist/hosted/claim-details.js.map +1 -0
  14. package/dist/hosted/hosted-node-service.d.ts +318 -0
  15. package/dist/hosted/hosted-node-service.js +666 -0
  16. package/dist/hosted/hosted-node-service.js.map +1 -0
  17. package/dist/hosted/hosted-node-store.d.ts +40 -0
  18. package/dist/hosted/hosted-node-store.js +126 -0
  19. package/dist/hosted/hosted-node-store.js.map +1 -0
  20. package/dist/{donation/donation-supervisor.d.ts → hosted/hosted-node-supervisor.d.ts} +70 -53
  21. package/dist/hosted/hosted-node-supervisor.js +396 -0
  22. package/dist/hosted/hosted-node-supervisor.js.map +1 -0
  23. package/dist/hosted/hosted-node-watcher.d.ts +83 -0
  24. package/dist/hosted/hosted-node-watcher.js +224 -0
  25. package/dist/hosted/hosted-node-watcher.js.map +1 -0
  26. package/dist/hosted/index.d.ts +16 -0
  27. package/dist/hosted/index.js +13 -0
  28. package/dist/hosted/index.js.map +1 -0
  29. package/dist/hosted/node-status.d.ts +35 -0
  30. package/dist/hosted/node-status.js +61 -0
  31. package/dist/hosted/node-status.js.map +1 -0
  32. package/dist/hosted/types.d.ts +134 -0
  33. package/dist/hosted/types.js +22 -0
  34. package/dist/hosted/types.js.map +1 -0
  35. package/dist/index.d.ts +12 -24
  36. package/dist/index.js +9 -19
  37. package/dist/index.js.map +1 -1
  38. package/dist/installer/config.d.ts +4 -34
  39. package/dist/installer/config.js +3 -18
  40. package/dist/installer/config.js.map +1 -1
  41. package/dist/installer/index.d.ts +7 -19
  42. package/dist/installer/index.js +14 -69
  43. package/dist/installer/index.js.map +1 -1
  44. package/dist/installer/paths.d.ts +0 -2
  45. package/dist/installer/paths.js +0 -4
  46. package/dist/installer/paths.js.map +1 -1
  47. package/dist/installer/wizard.d.ts +0 -7
  48. package/dist/installer/wizard.js +1 -8
  49. package/dist/installer/wizard.js.map +1 -1
  50. package/dist/nat/address-resolver.d.ts +18 -17
  51. package/dist/nat/address-resolver.js +79 -25
  52. package/dist/nat/address-resolver.js.map +1 -1
  53. package/dist/nat/address-watch.d.ts +37 -0
  54. package/dist/nat/address-watch.js +84 -0
  55. package/dist/nat/address-watch.js.map +1 -0
  56. package/dist/nat/ddns/duckdns.d.ts +2 -1
  57. package/dist/nat/ddns/duckdns.js +5 -1
  58. package/dist/nat/ddns/duckdns.js.map +1 -1
  59. package/dist/nat/ddns/index.d.ts +6 -0
  60. package/dist/nat/ddns/index.js.map +1 -1
  61. package/dist/nat/index.d.ts +12 -9
  62. package/dist/nat/index.js +6 -5
  63. package/dist/nat/index.js.map +1 -1
  64. package/dist/nat/nat-service.d.ts +203 -77
  65. package/dist/nat/nat-service.js +602 -237
  66. package/dist/nat/nat-service.js.map +1 -1
  67. package/dist/nat/nat-store.d.ts +12 -5
  68. package/dist/nat/nat-store.js +114 -30
  69. package/dist/nat/nat-store.js.map +1 -1
  70. package/dist/nat/port-mapper.d.ts +83 -79
  71. package/dist/nat/port-mapper.js +148 -189
  72. package/dist/nat/port-mapper.js.map +1 -1
  73. package/dist/nat/reachability.d.ts +31 -15
  74. package/dist/nat/reachability.js +97 -22
  75. package/dist/nat/reachability.js.map +1 -1
  76. package/dist/nat/types.d.ts +78 -27
  77. package/dist/nat/types.js +8 -5
  78. package/dist/nat/types.js.map +1 -1
  79. package/dist/orchestrator/host-process-orchestrator.d.ts +29 -71
  80. package/dist/orchestrator/host-process-orchestrator.js +78 -218
  81. package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
  82. package/dist/{installer/identity.d.ts → orchestrator/identity-file.d.ts} +5 -5
  83. package/dist/{installer/identity.js → orchestrator/identity-file.js} +6 -6
  84. package/dist/orchestrator/identity-file.js.map +1 -0
  85. package/dist/orchestrator/index.d.ts +2 -3
  86. package/dist/orchestrator/index.js +1 -1
  87. package/dist/orchestrator/index.js.map +1 -1
  88. package/dist/orchestrator/node-identity.d.ts +6 -6
  89. package/dist/orchestrator/node-identity.js +7 -7
  90. package/dist/orchestrator/node-identity.js.map +1 -1
  91. package/dist/orchestrator/port-allocator.d.ts +5 -6
  92. package/dist/orchestrator/port-allocator.js +9 -11
  93. package/dist/orchestrator/port-allocator.js.map +1 -1
  94. package/dist/orchestrator/state-store.d.ts +3 -5
  95. package/dist/orchestrator/state-store.js +2 -10
  96. package/dist/orchestrator/state-store.js.map +1 -1
  97. package/dist/orchestrator/types.d.ts +33 -22
  98. package/dist/orchestrator/types.js.map +1 -1
  99. package/dist/server/error-handler.d.ts +5 -25
  100. package/dist/server/error-handler.js +11 -74
  101. package/dist/server/error-handler.js.map +1 -1
  102. package/dist/server/events/types.d.ts +7 -11
  103. package/dist/server/events/types.js +2 -1
  104. package/dist/server/events/types.js.map +1 -1
  105. package/dist/server/index.d.ts +11 -37
  106. package/dist/server/index.js +30 -48
  107. package/dist/server/index.js.map +1 -1
  108. package/dist/server/routes/hosted-nodes.d.ts +28 -0
  109. package/dist/server/routes/hosted-nodes.js +68 -0
  110. package/dist/server/routes/hosted-nodes.js.map +1 -0
  111. package/dist/server/routes/nat.d.ts +4 -1
  112. package/dist/server/routes/nat.js +13 -2
  113. package/dist/server/routes/nat.js.map +1 -1
  114. package/dist/server/routes/nodes.d.ts +6 -20
  115. package/dist/server/routes/nodes.js +7 -86
  116. package/dist/server/routes/nodes.js.map +1 -1
  117. package/dist/server/routes/settings.d.ts +2 -7
  118. package/dist/server/routes/settings.js +3 -7
  119. package/dist/server/routes/settings.js.map +1 -1
  120. package/dist/server/routes/status.d.ts +2 -25
  121. package/dist/server/routes/status.js +1 -11
  122. package/dist/server/routes/status.js.map +1 -1
  123. package/dist/server/server.js +8 -8
  124. package/dist/server/settings-store.d.ts +2 -2
  125. package/dist/server/settings-store.js +2 -2
  126. package/dist/server/static.js +1 -2
  127. package/dist/server/static.js.map +1 -1
  128. package/dist/ui/assets/index-4FWuU8ye.js +19 -0
  129. package/dist/ui/assets/index-4FWuU8ye.js.map +1 -0
  130. package/dist/ui/assets/index-Di9Q4d01.css +1 -0
  131. package/dist/ui/index.html +2 -2
  132. package/dist/update/index.d.ts +2 -2
  133. package/dist/update/index.js +2 -2
  134. package/dist/update/store.d.ts +1 -1
  135. package/dist/update/store.js +1 -1
  136. package/dist/update/types.d.ts +1 -1
  137. package/dist/update/types.js +1 -1
  138. package/package.json +5 -6
  139. package/src/__tests__/cli-nat.smoke.test.ts +219 -212
  140. package/src/__tests__/cli.smoke.test.ts +1 -1
  141. package/src/__tests__/global-setup.ts +2 -3
  142. package/src/__tests__/orchestrator-claim-secret.test.ts +112 -0
  143. package/src/__tests__/orchestrator-handle-contract.ts +134 -134
  144. package/src/__tests__/orchestrator-node-identity.test.ts +182 -203
  145. package/src/__tests__/orchestrator-ports.test.ts +124 -143
  146. package/src/__tests__/orchestrator-push.test.ts +138 -146
  147. package/src/__tests__/orchestrator.test.ts +1019 -1063
  148. package/src/bin/host.ts +368 -553
  149. package/src/bin/join-output.ts +120 -0
  150. package/src/bin/nat-output.ts +177 -0
  151. package/src/{donation → hosted}/__tests__/fake-orchestrator.test.ts +173 -173
  152. package/src/{donation → hosted}/__tests__/fake-orchestrator.ts +265 -243
  153. package/src/hosted/__tests__/hosted-node-service.test.ts +609 -0
  154. package/src/hosted/__tests__/hosted-node-supervisor.test.ts +474 -0
  155. package/src/hosted/__tests__/test-invitation.ts +33 -0
  156. package/src/hosted/claim-details.ts +96 -0
  157. package/src/hosted/hosted-node-service.ts +795 -0
  158. package/src/hosted/hosted-node-store.ts +144 -0
  159. package/src/hosted/hosted-node-supervisor.ts +439 -0
  160. package/src/hosted/hosted-node-watcher.ts +250 -0
  161. package/src/hosted/index.ts +53 -0
  162. package/src/hosted/node-status.ts +71 -0
  163. package/src/hosted/types.ts +153 -0
  164. package/src/index.ts +72 -91
  165. package/src/installer/__tests__/config.test.ts +15 -40
  166. package/src/installer/__tests__/installer.smoke.test.ts +5 -86
  167. package/src/installer/__tests__/wizard.test.ts +56 -64
  168. package/src/installer/config.ts +135 -175
  169. package/src/installer/index.ts +15 -83
  170. package/src/installer/paths.ts +104 -109
  171. package/src/installer/wizard.ts +87 -103
  172. package/src/nat/__tests__/address-resolver.test.ts +107 -82
  173. package/src/nat/__tests__/nat-service.test.ts +433 -408
  174. package/src/nat/__tests__/nat-store.test.ts +127 -93
  175. package/src/nat/__tests__/port-mapper.test.ts +30 -191
  176. package/src/nat/__tests__/reachability.test.ts +96 -13
  177. package/src/nat/address-resolver.ts +94 -52
  178. package/src/nat/address-watch.ts +92 -0
  179. package/src/nat/ddns/duckdns.ts +5 -1
  180. package/src/nat/ddns/index.ts +6 -0
  181. package/src/nat/index.ts +87 -71
  182. package/src/nat/nat-service.ts +889 -501
  183. package/src/nat/nat-store.ts +237 -154
  184. package/src/nat/port-mapper.ts +241 -329
  185. package/src/nat/reachability.ts +134 -25
  186. package/src/nat/types.ts +209 -159
  187. package/src/{installer/__tests__/identity.test.ts → orchestrator/__tests__/identity-file.test.ts} +34 -34
  188. package/src/orchestrator/__tests__/node-identity.test.ts +62 -62
  189. package/src/orchestrator/host-process-orchestrator.ts +81 -243
  190. package/src/{installer/identity.ts → orchestrator/identity-file.ts} +56 -56
  191. package/src/orchestrator/index.ts +9 -10
  192. package/src/orchestrator/node-identity.ts +41 -41
  193. package/src/orchestrator/port-allocator.ts +71 -73
  194. package/src/orchestrator/state-store.ts +76 -90
  195. package/src/orchestrator/types.ts +34 -23
  196. package/src/server/__tests__/bus.test.ts +38 -38
  197. package/src/server/__tests__/error-handler.test.ts +81 -118
  198. package/src/server/__tests__/fakes.ts +40 -44
  199. package/src/server/__tests__/hosted-nodes-route.test.ts +160 -0
  200. package/src/server/__tests__/nodes-route.test.ts +114 -283
  201. package/src/server/__tests__/publishers.test.ts +193 -180
  202. package/src/server/__tests__/server.smoke.test.ts +134 -223
  203. package/src/server/__tests__/settings-route.test.ts +179 -195
  204. package/src/server/__tests__/sse-route.test.ts +119 -119
  205. package/src/server/__tests__/status-route.test.ts +82 -121
  206. package/src/server/error-handler.ts +112 -178
  207. package/src/server/events/types.ts +20 -24
  208. package/src/server/index.ts +223 -267
  209. package/src/server/routes/hosted-nodes.ts +83 -0
  210. package/src/server/routes/nat.ts +75 -63
  211. package/src/server/routes/nodes.ts +96 -188
  212. package/src/server/routes/settings.ts +115 -123
  213. package/src/server/routes/status.ts +102 -134
  214. package/src/server/server.ts +8 -8
  215. package/src/server/settings-store.ts +2 -2
  216. package/src/server/static.ts +1 -2
  217. package/src/update/index.ts +2 -2
  218. package/src/update/store.ts +1 -1
  219. package/src/update/types.ts +1 -1
  220. package/dist/auth/duration.d.ts +0 -1
  221. package/dist/auth/duration.js +0 -32
  222. package/dist/auth/duration.js.map +0 -1
  223. package/dist/auth/index.d.ts +0 -11
  224. package/dist/auth/index.js +0 -9
  225. package/dist/auth/index.js.map +0 -1
  226. package/dist/auth/self-label.d.ts +0 -36
  227. package/dist/auth/self-label.js +0 -57
  228. package/dist/auth/self-label.js.map +0 -1
  229. package/dist/auth/trust-circle-store.d.ts +0 -26
  230. package/dist/auth/trust-circle-store.js +0 -129
  231. package/dist/auth/trust-circle-store.js.map +0 -1
  232. package/dist/auth/trust-circle.d.ts +0 -156
  233. package/dist/auth/trust-circle.js +0 -307
  234. package/dist/auth/trust-circle.js.map +0 -1
  235. package/dist/auth/types.d.ts +0 -84
  236. package/dist/auth/types.js +0 -24
  237. package/dist/auth/types.js.map +0 -1
  238. package/dist/donation/donation-service.d.ts +0 -389
  239. package/dist/donation/donation-service.js +0 -828
  240. package/dist/donation/donation-service.js.map +0 -1
  241. package/dist/donation/donation-store.d.ts +0 -51
  242. package/dist/donation/donation-store.js +0 -152
  243. package/dist/donation/donation-store.js.map +0 -1
  244. package/dist/donation/donation-supervisor.js +0 -364
  245. package/dist/donation/donation-supervisor.js.map +0 -1
  246. package/dist/donation/grant-service.d.ts +0 -80
  247. package/dist/donation/grant-service.js +0 -210
  248. package/dist/donation/grant-service.js.map +0 -1
  249. package/dist/donation/grant-store.d.ts +0 -38
  250. package/dist/donation/grant-store.js +0 -135
  251. package/dist/donation/grant-store.js.map +0 -1
  252. package/dist/donation/index.d.ts +0 -20
  253. package/dist/donation/index.js +0 -17
  254. package/dist/donation/index.js.map +0 -1
  255. package/dist/donation/types.d.ts +0 -224
  256. package/dist/donation/types.js +0 -40
  257. package/dist/donation/types.js.map +0 -1
  258. package/dist/installer/identity.js.map +0 -1
  259. package/dist/owner/index.d.ts +0 -11
  260. package/dist/owner/index.js +0 -11
  261. package/dist/owner/index.js.map +0 -1
  262. package/dist/owner/owner-node-client.d.ts +0 -110
  263. package/dist/owner/owner-node-client.js +0 -184
  264. package/dist/owner/owner-node-client.js.map +0 -1
  265. package/dist/server/routes/bootstrap-node-validation.d.ts +0 -115
  266. package/dist/server/routes/bootstrap-node-validation.js +0 -197
  267. package/dist/server/routes/bootstrap-node-validation.js.map +0 -1
  268. package/dist/server/routes/grants-admin.d.ts +0 -29
  269. package/dist/server/routes/grants-admin.js +0 -58
  270. package/dist/server/routes/grants-admin.js.map +0 -1
  271. package/dist/server/routes/grants.d.ts +0 -28
  272. package/dist/server/routes/grants.js +0 -165
  273. package/dist/server/routes/grants.js.map +0 -1
  274. package/dist/server/routes/provision-request-validation.d.ts +0 -45
  275. package/dist/server/routes/provision-request-validation.js +0 -97
  276. package/dist/server/routes/provision-request-validation.js.map +0 -1
  277. package/dist/server/routes/strands.d.ts +0 -20
  278. package/dist/server/routes/strands.js +0 -38
  279. package/dist/server/routes/strands.js.map +0 -1
  280. package/dist/server/routes/trust-circle.d.ts +0 -17
  281. package/dist/server/routes/trust-circle.js +0 -35
  282. package/dist/server/routes/trust-circle.js.map +0 -1
  283. package/dist/strands/index.d.ts +0 -7
  284. package/dist/strands/index.js +0 -6
  285. package/dist/strands/index.js.map +0 -1
  286. package/dist/strands/strand-service.d.ts +0 -50
  287. package/dist/strands/strand-service.js +0 -103
  288. package/dist/strands/strand-service.js.map +0 -1
  289. package/dist/strands/types.d.ts +0 -93
  290. package/dist/strands/types.js +0 -23
  291. package/dist/strands/types.js.map +0 -1
  292. package/dist/ui/assets/index-C6nELNfC.js +0 -23
  293. package/dist/ui/assets/index-C6nELNfC.js.map +0 -1
  294. package/dist/ui/assets/index-TtIYSeZO.css +0 -1
  295. package/src/__tests__/cli-invite.smoke.test.ts +0 -192
  296. package/src/__tests__/host-owner.smoke.test.ts +0 -123
  297. package/src/__tests__/orchestrator-owner.test.ts +0 -234
  298. package/src/__tests__/orchestrator-pin-keys.test.ts +0 -152
  299. package/src/auth/__tests__/duration.test.ts +0 -28
  300. package/src/auth/__tests__/self-label.test.ts +0 -85
  301. package/src/auth/__tests__/trust-circle-integration.test.ts +0 -152
  302. package/src/auth/__tests__/trust-circle-store.test.ts +0 -115
  303. package/src/auth/__tests__/trust-circle.test.ts +0 -445
  304. package/src/auth/duration.ts +0 -33
  305. package/src/auth/index.ts +0 -21
  306. package/src/auth/self-label.ts +0 -74
  307. package/src/auth/trust-circle-store.ts +0 -155
  308. package/src/auth/trust-circle.ts +0 -395
  309. package/src/auth/types.ts +0 -99
  310. package/src/donation/__tests__/donation-service.test.ts +0 -1193
  311. package/src/donation/__tests__/donation-supervisor.test.ts +0 -548
  312. package/src/donation/__tests__/grant-service.test.ts +0 -213
  313. package/src/donation/__tests__/grant-store.test.ts +0 -113
  314. package/src/donation/donation-service.ts +0 -1033
  315. package/src/donation/donation-store.ts +0 -176
  316. package/src/donation/donation-supervisor.ts +0 -411
  317. package/src/donation/grant-service.ts +0 -252
  318. package/src/donation/grant-store.ts +0 -152
  319. package/src/donation/index.ts +0 -65
  320. package/src/donation/types.ts +0 -260
  321. package/src/owner/__tests__/owner-node-client.test.ts +0 -194
  322. package/src/owner/index.ts +0 -12
  323. package/src/owner/owner-node-client.ts +0 -259
  324. package/src/server/__tests__/bootstrap-node-validation.test.ts +0 -138
  325. package/src/server/__tests__/grants-admin-route.test.ts +0 -171
  326. package/src/server/__tests__/grants-route.test.ts +0 -394
  327. package/src/server/__tests__/strands-route.test.ts +0 -234
  328. package/src/server/routes/bootstrap-node-validation.ts +0 -206
  329. package/src/server/routes/grants-admin.ts +0 -74
  330. package/src/server/routes/grants.ts +0 -193
  331. package/src/server/routes/provision-request-validation.ts +0 -105
  332. package/src/server/routes/strands.ts +0 -54
  333. package/src/server/routes/trust-circle.ts +0 -49
  334. package/src/strands/__tests__/strand-service.test.ts +0 -165
  335. package/src/strands/index.ts +0 -14
  336. package/src/strands/strand-service.ts +0 -135
  337. package/src/strands/types.ts +0 -102
@@ -1,37 +1,80 @@
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
+ /** Where a port's external route came from. */
24
+ export type PortRouteSource = 'upnp' | 'manual';
25
+ /** How one of a node's ports is reached from outside the home network. */
26
+ export interface PortRoute {
24
27
  internalPort: number;
25
- /** IP the router reports as its WAN address. Null when not auto-mapped. */
28
+ /** Port reachable from outside; null when there is no route. */
29
+ externalPort: number | null;
30
+ source: PortRouteSource | null;
31
+ /** ISO timestamp when the router lease expires; UPnP routes only. */
32
+ leaseExpiresAt: string | null;
33
+ /** Last mapping failure, in plain language; null when the last attempt succeeded. */
34
+ error: string | null;
35
+ }
36
+ /** Per-node verdict. */
37
+ export type NodeVerdict = 'mapped' | 'manual' | 'unreachable';
38
+ /** Reachability of one hosted node, as reported in the status snapshot. */
39
+ export interface NodeReachability {
40
+ nodeId: string;
41
+ /** Whether the node's process is running; a stopped node keeps its last routes until the unmap grace elapses. */
42
+ running: boolean;
43
+ verdict: NodeVerdict;
44
+ /**
45
+ * Plain-language reason and remedy when unreachable, e.g. "Router refused
46
+ * the WebSocket port mapping. Forward port 10004 to 192.168.1.20 on your
47
+ * router, then tell cadre-host the external port."
48
+ */
49
+ reason: string | null;
50
+ tcp: PortRoute;
51
+ /** Null for a handle persisted by an older build without a WebSocket port. */
52
+ ws: PortRoute | null;
53
+ /** Multiaddrs (no `/p2p/` suffix) through which the node is reachable from outside. */
54
+ publicAddrs: string[];
55
+ }
56
+ /** The UPnP gateway as the host last saw it. */
57
+ export interface NatGatewayStatus {
58
+ found: boolean;
59
+ /** This machine's address on the router's subnet — the address a user forwards to. */
60
+ lanAddress: string | null;
61
+ /** The WAN address the router reports for itself. */
26
62
  routerExternalIp: string | null;
27
- /** ISO timestamp when the current mapping lease expires. */
28
- mappingLeaseExpiresAt: string | null;
63
+ /** Why discovery or the last gateway operation failed; null when it did not. */
64
+ lastError: string | null;
65
+ }
66
+ /** Snapshot returned to the management UI / CLI. */
67
+ export interface NatStatusSnapshot {
68
+ upnpEnabled: boolean;
69
+ gateway: NatGatewayStatus;
29
70
  externalIp: string | null;
30
71
  externalIpDetectedAt: string | null;
31
72
  cgnatDetected: boolean;
73
+ /** Roll-up across running nodes; see `reachability.ts`. */
32
74
  directReachability: DirectReachability;
33
75
  lastTestedAt: string | null;
34
76
  ddns: NatDdnsStatus;
77
+ nodes: NodeReachability[];
35
78
  }
36
79
  /** DDNS sub-section of the status snapshot. */
37
80
  export interface NatDdnsStatus {
@@ -48,18 +91,26 @@ export interface NatDdnsStatus {
48
91
  /** Most recent error message, null when last attempt succeeded. */
49
92
  lastError: string | null;
50
93
  }
94
+ /** The external ports a user forwarded on their router for one node, per port kind. */
95
+ export interface ManualForward {
96
+ tcp?: number;
97
+ ws?: number;
98
+ }
99
+ /** Patch applied by `PUT /nat/nodes/:nodeId/forward`: `null` clears that port. */
100
+ export interface ManualForwardPatch {
101
+ tcp?: number | null;
102
+ ws?: number | null;
103
+ }
51
104
  /**
52
105
  * On-disk shape of `nat.json`. NEVER contains DDNS credentials — those live
53
106
  * in the SecretsStore.
54
107
  */
55
108
  export interface NatSettingsFile {
56
109
  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. */
110
+ /** Whether to ask the router for port mappings at all. */
62
111
  upnpEnabled: boolean;
112
+ /** Per node id: the external port the user forwarded on their router, per port kind. */
113
+ forwards: Record<string, ManualForward>;
63
114
  ddns: NatDdnsSettings;
64
115
  }
65
116
  /** DDNS sub-section of `nat.json`. */
@@ -79,8 +130,8 @@ export interface NatDdnsSettings {
79
130
  }
80
131
  /** Typed error codes for NatError. */
81
132
  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';
133
+ /** A manual forward named a node id the host does not run. */
134
+ | 'unknown_node';
84
135
  /** Typed error carrying a stable `code` for HTTP mapping in the local-ui. */
85
136
  export declare class NatError extends Error {
86
137
  readonly code: NatErrorCode;
@@ -102,11 +153,10 @@ export interface DdnsProviderInfo {
102
153
  configFields: ReadonlyArray<DdnsConfigField>;
103
154
  }
104
155
  /**
105
- * Typed HTTP handlers exposed to `cadre-host-local-ui` for Fastify wiring.
156
+ * Typed HTTP handlers exposed to the local UI server for Fastify wiring.
106
157
  *
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.
158
+ * These take typed objects and either return typed results or throw a
159
+ * `NatError` whose `.code` the error handler maps to an HTTP status code.
110
160
  */
111
161
  export interface NatHandlers {
112
162
  getStatus(): Promise<NatStatusSnapshot>;
@@ -118,5 +168,6 @@ export interface NatHandlers {
118
168
  config: Record<string, string>;
119
169
  externallyManaged?: boolean;
120
170
  }): Promise<NatStatusSnapshot>;
121
- putSettings(body: Partial<Omit<NatSettingsFile, 'version'>>): Promise<NatStatusSnapshot>;
171
+ putSettings(body: Partial<Omit<NatSettingsFile, 'version' | 'forwards'>>): Promise<NatStatusSnapshot>;
172
+ putForward(nodeId: string, patch: ManualForwardPatch): Promise<NatStatusSnapshot>;
122
173
  }
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;AAiJH,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,7 +161,7 @@ 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
  *
@@ -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;