@serfab/cadre-host 1.12.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.
- package/README.md +332 -423
- package/dist/bin/host.d.ts +6 -3
- package/dist/bin/host.js +301 -505
- package/dist/bin/host.js.map +1 -1
- package/dist/bin/join-output.d.ts +56 -0
- package/dist/bin/join-output.js +92 -0
- package/dist/bin/join-output.js.map +1 -0
- package/dist/bin/nat-output.d.ts +49 -0
- package/dist/bin/nat-output.js +127 -0
- package/dist/bin/nat-output.js.map +1 -0
- package/dist/hosted/claim-details.d.ts +44 -0
- package/dist/hosted/claim-details.js +66 -0
- package/dist/hosted/claim-details.js.map +1 -0
- package/dist/hosted/hosted-node-service.d.ts +318 -0
- package/dist/hosted/hosted-node-service.js +666 -0
- package/dist/hosted/hosted-node-service.js.map +1 -0
- package/dist/hosted/hosted-node-store.d.ts +40 -0
- package/dist/hosted/hosted-node-store.js +126 -0
- package/dist/hosted/hosted-node-store.js.map +1 -0
- package/dist/{donation/donation-supervisor.d.ts → hosted/hosted-node-supervisor.d.ts} +70 -53
- package/dist/hosted/hosted-node-supervisor.js +396 -0
- package/dist/hosted/hosted-node-supervisor.js.map +1 -0
- package/dist/hosted/hosted-node-watcher.d.ts +83 -0
- package/dist/hosted/hosted-node-watcher.js +224 -0
- package/dist/hosted/hosted-node-watcher.js.map +1 -0
- package/dist/hosted/index.d.ts +16 -0
- package/dist/hosted/index.js +13 -0
- package/dist/hosted/index.js.map +1 -0
- package/dist/hosted/node-status.d.ts +35 -0
- package/dist/hosted/node-status.js +61 -0
- package/dist/hosted/node-status.js.map +1 -0
- package/dist/hosted/types.d.ts +134 -0
- package/dist/hosted/types.js +22 -0
- package/dist/hosted/types.js.map +1 -0
- package/dist/index.d.ts +12 -24
- package/dist/index.js +9 -19
- package/dist/index.js.map +1 -1
- package/dist/installer/config.d.ts +4 -34
- package/dist/installer/config.js +3 -18
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +7 -19
- package/dist/installer/index.js +14 -69
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/paths.d.ts +0 -2
- package/dist/installer/paths.js +0 -4
- package/dist/installer/paths.js.map +1 -1
- package/dist/installer/wizard.d.ts +0 -7
- package/dist/installer/wizard.js +1 -8
- package/dist/installer/wizard.js.map +1 -1
- package/dist/nat/address-resolver.d.ts +18 -17
- package/dist/nat/address-resolver.js +79 -25
- package/dist/nat/address-resolver.js.map +1 -1
- package/dist/nat/address-watch.d.ts +37 -0
- package/dist/nat/address-watch.js +84 -0
- package/dist/nat/address-watch.js.map +1 -0
- package/dist/nat/ddns/duckdns.d.ts +2 -1
- package/dist/nat/ddns/duckdns.js +5 -1
- package/dist/nat/ddns/duckdns.js.map +1 -1
- package/dist/nat/ddns/index.d.ts +6 -0
- package/dist/nat/ddns/index.js.map +1 -1
- package/dist/nat/index.d.ts +12 -9
- package/dist/nat/index.js +6 -5
- package/dist/nat/index.js.map +1 -1
- package/dist/nat/nat-service.d.ts +203 -77
- package/dist/nat/nat-service.js +602 -237
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/nat-store.d.ts +12 -5
- package/dist/nat/nat-store.js +114 -30
- package/dist/nat/nat-store.js.map +1 -1
- package/dist/nat/port-mapper.d.ts +83 -79
- package/dist/nat/port-mapper.js +148 -189
- package/dist/nat/port-mapper.js.map +1 -1
- package/dist/nat/reachability.d.ts +31 -15
- package/dist/nat/reachability.js +97 -22
- package/dist/nat/reachability.js.map +1 -1
- package/dist/nat/types.d.ts +78 -27
- package/dist/nat/types.js +8 -5
- package/dist/nat/types.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +29 -71
- package/dist/orchestrator/host-process-orchestrator.js +78 -218
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/{installer/identity.d.ts → orchestrator/identity-file.d.ts} +5 -5
- package/dist/{installer/identity.js → orchestrator/identity-file.js} +6 -6
- package/dist/orchestrator/identity-file.js.map +1 -0
- package/dist/orchestrator/index.d.ts +2 -3
- package/dist/orchestrator/index.js +1 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/node-identity.d.ts +6 -6
- package/dist/orchestrator/node-identity.js +7 -7
- package/dist/orchestrator/node-identity.js.map +1 -1
- package/dist/orchestrator/port-allocator.d.ts +5 -6
- package/dist/orchestrator/port-allocator.js +9 -11
- package/dist/orchestrator/port-allocator.js.map +1 -1
- package/dist/orchestrator/state-store.d.ts +3 -5
- package/dist/orchestrator/state-store.js +2 -10
- package/dist/orchestrator/state-store.js.map +1 -1
- package/dist/orchestrator/types.d.ts +33 -22
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/server/error-handler.d.ts +5 -25
- package/dist/server/error-handler.js +11 -74
- package/dist/server/error-handler.js.map +1 -1
- package/dist/server/events/types.d.ts +7 -11
- package/dist/server/events/types.js +2 -1
- package/dist/server/events/types.js.map +1 -1
- package/dist/server/index.d.ts +11 -37
- package/dist/server/index.js +30 -48
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/hosted-nodes.d.ts +28 -0
- package/dist/server/routes/hosted-nodes.js +68 -0
- package/dist/server/routes/hosted-nodes.js.map +1 -0
- package/dist/server/routes/nat.d.ts +4 -1
- package/dist/server/routes/nat.js +13 -2
- package/dist/server/routes/nat.js.map +1 -1
- package/dist/server/routes/nodes.d.ts +6 -20
- package/dist/server/routes/nodes.js +7 -86
- package/dist/server/routes/nodes.js.map +1 -1
- package/dist/server/routes/settings.d.ts +2 -7
- package/dist/server/routes/settings.js +3 -7
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/status.d.ts +2 -25
- package/dist/server/routes/status.js +1 -11
- package/dist/server/routes/status.js.map +1 -1
- package/dist/server/server.js +8 -8
- package/dist/server/settings-store.d.ts +2 -2
- package/dist/server/settings-store.js +2 -2
- package/dist/server/static.js +1 -2
- package/dist/server/static.js.map +1 -1
- package/dist/ui/assets/index-4FWuU8ye.js +19 -0
- package/dist/ui/assets/index-4FWuU8ye.js.map +1 -0
- package/dist/ui/assets/index-Di9Q4d01.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/update/index.d.ts +2 -2
- package/dist/update/index.js +2 -2
- package/dist/update/store.d.ts +1 -1
- package/dist/update/store.js +1 -1
- package/dist/update/types.d.ts +1 -1
- package/dist/update/types.js +1 -1
- package/package.json +5 -6
- package/src/__tests__/cli-nat.smoke.test.ts +219 -212
- package/src/__tests__/cli.smoke.test.ts +1 -1
- package/src/__tests__/global-setup.ts +2 -3
- package/src/__tests__/orchestrator-claim-secret.test.ts +112 -0
- package/src/__tests__/orchestrator-handle-contract.ts +134 -134
- package/src/__tests__/orchestrator-node-identity.test.ts +182 -203
- package/src/__tests__/orchestrator-ports.test.ts +124 -143
- package/src/__tests__/orchestrator-push.test.ts +138 -146
- package/src/__tests__/orchestrator.test.ts +1019 -1063
- package/src/bin/host.ts +368 -553
- package/src/bin/join-output.ts +120 -0
- package/src/bin/nat-output.ts +177 -0
- package/src/{donation → hosted}/__tests__/fake-orchestrator.test.ts +173 -173
- package/src/{donation → hosted}/__tests__/fake-orchestrator.ts +265 -243
- package/src/hosted/__tests__/hosted-node-service.test.ts +609 -0
- package/src/hosted/__tests__/hosted-node-supervisor.test.ts +474 -0
- package/src/hosted/__tests__/test-invitation.ts +33 -0
- package/src/hosted/claim-details.ts +96 -0
- package/src/hosted/hosted-node-service.ts +795 -0
- package/src/hosted/hosted-node-store.ts +144 -0
- package/src/hosted/hosted-node-supervisor.ts +439 -0
- package/src/hosted/hosted-node-watcher.ts +250 -0
- package/src/hosted/index.ts +53 -0
- package/src/hosted/node-status.ts +71 -0
- package/src/hosted/types.ts +153 -0
- package/src/index.ts +72 -91
- package/src/installer/__tests__/config.test.ts +15 -40
- package/src/installer/__tests__/installer.smoke.test.ts +5 -86
- package/src/installer/__tests__/wizard.test.ts +56 -64
- package/src/installer/config.ts +135 -175
- package/src/installer/index.ts +15 -83
- package/src/installer/paths.ts +104 -109
- package/src/installer/wizard.ts +87 -103
- package/src/nat/__tests__/address-resolver.test.ts +107 -82
- package/src/nat/__tests__/nat-service.test.ts +433 -408
- package/src/nat/__tests__/nat-store.test.ts +127 -93
- package/src/nat/__tests__/port-mapper.test.ts +30 -191
- package/src/nat/__tests__/reachability.test.ts +96 -13
- package/src/nat/address-resolver.ts +94 -52
- package/src/nat/address-watch.ts +92 -0
- package/src/nat/ddns/duckdns.ts +5 -1
- package/src/nat/ddns/index.ts +6 -0
- package/src/nat/index.ts +87 -71
- package/src/nat/nat-service.ts +889 -501
- package/src/nat/nat-store.ts +237 -154
- package/src/nat/port-mapper.ts +241 -329
- package/src/nat/reachability.ts +134 -25
- package/src/nat/types.ts +209 -159
- package/src/{installer/__tests__/identity.test.ts → orchestrator/__tests__/identity-file.test.ts} +34 -34
- package/src/orchestrator/__tests__/node-identity.test.ts +62 -62
- package/src/orchestrator/host-process-orchestrator.ts +81 -243
- package/src/{installer/identity.ts → orchestrator/identity-file.ts} +56 -56
- package/src/orchestrator/index.ts +9 -10
- package/src/orchestrator/node-identity.ts +41 -41
- package/src/orchestrator/port-allocator.ts +71 -73
- package/src/orchestrator/state-store.ts +76 -90
- package/src/orchestrator/types.ts +34 -23
- package/src/server/__tests__/bus.test.ts +38 -38
- package/src/server/__tests__/error-handler.test.ts +81 -118
- package/src/server/__tests__/fakes.ts +40 -44
- package/src/server/__tests__/hosted-nodes-route.test.ts +160 -0
- package/src/server/__tests__/nodes-route.test.ts +114 -283
- package/src/server/__tests__/publishers.test.ts +193 -180
- package/src/server/__tests__/server.smoke.test.ts +134 -223
- package/src/server/__tests__/settings-route.test.ts +179 -195
- package/src/server/__tests__/sse-route.test.ts +119 -119
- package/src/server/__tests__/status-route.test.ts +82 -121
- package/src/server/error-handler.ts +112 -178
- package/src/server/events/types.ts +20 -24
- package/src/server/index.ts +223 -267
- package/src/server/routes/hosted-nodes.ts +83 -0
- package/src/server/routes/nat.ts +75 -63
- package/src/server/routes/nodes.ts +96 -188
- package/src/server/routes/settings.ts +115 -123
- package/src/server/routes/status.ts +102 -134
- package/src/server/server.ts +8 -8
- package/src/server/settings-store.ts +2 -2
- package/src/server/static.ts +1 -2
- package/src/update/index.ts +2 -2
- package/src/update/store.ts +1 -1
- package/src/update/types.ts +1 -1
- package/dist/auth/duration.d.ts +0 -1
- package/dist/auth/duration.js +0 -32
- package/dist/auth/duration.js.map +0 -1
- package/dist/auth/index.d.ts +0 -11
- package/dist/auth/index.js +0 -9
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/self-label.d.ts +0 -36
- package/dist/auth/self-label.js +0 -57
- package/dist/auth/self-label.js.map +0 -1
- package/dist/auth/trust-circle-store.d.ts +0 -26
- package/dist/auth/trust-circle-store.js +0 -129
- package/dist/auth/trust-circle-store.js.map +0 -1
- package/dist/auth/trust-circle.d.ts +0 -156
- package/dist/auth/trust-circle.js +0 -307
- package/dist/auth/trust-circle.js.map +0 -1
- package/dist/auth/types.d.ts +0 -84
- package/dist/auth/types.js +0 -24
- package/dist/auth/types.js.map +0 -1
- package/dist/donation/donation-service.d.ts +0 -389
- package/dist/donation/donation-service.js +0 -828
- package/dist/donation/donation-service.js.map +0 -1
- package/dist/donation/donation-store.d.ts +0 -51
- package/dist/donation/donation-store.js +0 -152
- package/dist/donation/donation-store.js.map +0 -1
- package/dist/donation/donation-supervisor.js +0 -364
- package/dist/donation/donation-supervisor.js.map +0 -1
- package/dist/donation/grant-service.d.ts +0 -80
- package/dist/donation/grant-service.js +0 -210
- package/dist/donation/grant-service.js.map +0 -1
- package/dist/donation/grant-store.d.ts +0 -38
- package/dist/donation/grant-store.js +0 -135
- package/dist/donation/grant-store.js.map +0 -1
- package/dist/donation/index.d.ts +0 -20
- package/dist/donation/index.js +0 -17
- package/dist/donation/index.js.map +0 -1
- package/dist/donation/types.d.ts +0 -224
- package/dist/donation/types.js +0 -40
- package/dist/donation/types.js.map +0 -1
- package/dist/installer/identity.js.map +0 -1
- package/dist/owner/index.d.ts +0 -11
- package/dist/owner/index.js +0 -11
- package/dist/owner/index.js.map +0 -1
- package/dist/owner/owner-node-client.d.ts +0 -110
- package/dist/owner/owner-node-client.js +0 -184
- package/dist/owner/owner-node-client.js.map +0 -1
- package/dist/server/routes/bootstrap-node-validation.d.ts +0 -115
- package/dist/server/routes/bootstrap-node-validation.js +0 -197
- package/dist/server/routes/bootstrap-node-validation.js.map +0 -1
- package/dist/server/routes/grants-admin.d.ts +0 -29
- package/dist/server/routes/grants-admin.js +0 -58
- package/dist/server/routes/grants-admin.js.map +0 -1
- package/dist/server/routes/grants.d.ts +0 -28
- package/dist/server/routes/grants.js +0 -165
- package/dist/server/routes/grants.js.map +0 -1
- package/dist/server/routes/provision-request-validation.d.ts +0 -45
- package/dist/server/routes/provision-request-validation.js +0 -97
- package/dist/server/routes/provision-request-validation.js.map +0 -1
- package/dist/server/routes/strands.d.ts +0 -20
- package/dist/server/routes/strands.js +0 -38
- package/dist/server/routes/strands.js.map +0 -1
- package/dist/server/routes/trust-circle.d.ts +0 -17
- package/dist/server/routes/trust-circle.js +0 -35
- package/dist/server/routes/trust-circle.js.map +0 -1
- package/dist/strands/index.d.ts +0 -7
- package/dist/strands/index.js +0 -6
- package/dist/strands/index.js.map +0 -1
- package/dist/strands/strand-service.d.ts +0 -50
- package/dist/strands/strand-service.js +0 -103
- package/dist/strands/strand-service.js.map +0 -1
- package/dist/strands/types.d.ts +0 -93
- package/dist/strands/types.js +0 -23
- package/dist/strands/types.js.map +0 -1
- package/dist/ui/assets/index-C6nELNfC.js +0 -23
- package/dist/ui/assets/index-C6nELNfC.js.map +0 -1
- package/dist/ui/assets/index-TtIYSeZO.css +0 -1
- package/src/__tests__/cli-invite.smoke.test.ts +0 -192
- package/src/__tests__/host-owner.smoke.test.ts +0 -123
- package/src/__tests__/orchestrator-owner.test.ts +0 -234
- package/src/__tests__/orchestrator-pin-keys.test.ts +0 -152
- package/src/auth/__tests__/duration.test.ts +0 -28
- package/src/auth/__tests__/self-label.test.ts +0 -85
- package/src/auth/__tests__/trust-circle-integration.test.ts +0 -152
- package/src/auth/__tests__/trust-circle-store.test.ts +0 -115
- package/src/auth/__tests__/trust-circle.test.ts +0 -445
- package/src/auth/duration.ts +0 -33
- package/src/auth/index.ts +0 -21
- package/src/auth/self-label.ts +0 -74
- package/src/auth/trust-circle-store.ts +0 -155
- package/src/auth/trust-circle.ts +0 -395
- package/src/auth/types.ts +0 -99
- package/src/donation/__tests__/donation-service.test.ts +0 -1193
- package/src/donation/__tests__/donation-supervisor.test.ts +0 -548
- package/src/donation/__tests__/grant-service.test.ts +0 -213
- package/src/donation/__tests__/grant-store.test.ts +0 -113
- package/src/donation/donation-service.ts +0 -1033
- package/src/donation/donation-store.ts +0 -176
- package/src/donation/donation-supervisor.ts +0 -411
- package/src/donation/grant-service.ts +0 -252
- package/src/donation/grant-store.ts +0 -152
- package/src/donation/index.ts +0 -65
- package/src/donation/types.ts +0 -260
- package/src/owner/__tests__/owner-node-client.test.ts +0 -194
- package/src/owner/index.ts +0 -12
- package/src/owner/owner-node-client.ts +0 -259
- package/src/server/__tests__/bootstrap-node-validation.test.ts +0 -138
- package/src/server/__tests__/grants-admin-route.test.ts +0 -171
- package/src/server/__tests__/grants-route.test.ts +0 -394
- package/src/server/__tests__/strands-route.test.ts +0 -234
- package/src/server/routes/bootstrap-node-validation.ts +0 -206
- package/src/server/routes/grants-admin.ts +0 -74
- package/src/server/routes/grants.ts +0 -193
- package/src/server/routes/provision-request-validation.ts +0 -105
- package/src/server/routes/strands.ts +0 -54
- package/src/server/routes/trust-circle.ts +0 -49
- package/src/strands/__tests__/strand-service.test.ts +0 -165
- package/src/strands/index.ts +0 -14
- package/src/strands/strand-service.ts +0 -135
- package/src/strands/types.ts +0 -102
package/src/nat/port-mapper.ts
CHANGED
|
@@ -1,329 +1,241 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
private
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** Install the mapping and schedule periodic refresh. */
|
|
245
|
-
async start(): Promise<void> {
|
|
246
|
-
if (this.started) return;
|
|
247
|
-
this.started = true;
|
|
248
|
-
await this.installMapping();
|
|
249
|
-
this.scheduleRefresh();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/** Release the mapping and clear timers. Idempotent. */
|
|
253
|
-
async stop(): Promise<void> {
|
|
254
|
-
if (!this.started) return;
|
|
255
|
-
this.started = false;
|
|
256
|
-
if (this.refreshHandle) {
|
|
257
|
-
this.clearTimer(this.refreshHandle);
|
|
258
|
-
this.refreshHandle = null;
|
|
259
|
-
}
|
|
260
|
-
try {
|
|
261
|
-
await this.mapper.unmap(this.externalPort, this.protocol);
|
|
262
|
-
} catch (err) {
|
|
263
|
-
log('unmap during stop failed: %s', (err as Error).message);
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
await this.mapper.stop();
|
|
267
|
-
} catch (err) {
|
|
268
|
-
log('mapper.stop() failed: %s', (err as Error).message);
|
|
269
|
-
}
|
|
270
|
-
this.state = {
|
|
271
|
-
...this.state,
|
|
272
|
-
mode: 'disabled',
|
|
273
|
-
routerExternalIp: null,
|
|
274
|
-
leaseExpiresAt: null,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/** Force an immediate verify+refresh cycle (used by testReachability). */
|
|
279
|
-
async refresh(): Promise<PortMapperState> {
|
|
280
|
-
if (!this.started) return this.getState();
|
|
281
|
-
const ok = await this.mapper.verify(this.externalPort, this.protocol).catch(() => false);
|
|
282
|
-
if (!ok) {
|
|
283
|
-
log('verify() returned false; re-mapping');
|
|
284
|
-
await this.installMapping();
|
|
285
|
-
return this.getState();
|
|
286
|
-
}
|
|
287
|
-
// Refresh: re-issue the mapping to extend the lease.
|
|
288
|
-
await this.installMapping();
|
|
289
|
-
return this.getState();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
private scheduleRefresh(): void {
|
|
293
|
-
if (this.refreshHandle) this.clearTimer(this.refreshHandle);
|
|
294
|
-
this.refreshHandle = this.setTimer(() => {
|
|
295
|
-
void this.refresh().catch((err) => log('refresh timer error: %s', (err as Error).message));
|
|
296
|
-
// Re-arm after the call to avoid drift.
|
|
297
|
-
this.scheduleRefresh();
|
|
298
|
-
}, this.refreshMs);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private async installMapping(): Promise<void> {
|
|
302
|
-
try {
|
|
303
|
-
const r = await this.mapper.map({
|
|
304
|
-
externalPort: this.externalPort,
|
|
305
|
-
internalPort: this.internalPort,
|
|
306
|
-
protocol: this.protocol,
|
|
307
|
-
ttlMs: this.ttlMs,
|
|
308
|
-
});
|
|
309
|
-
this.state = {
|
|
310
|
-
mode: r.mode,
|
|
311
|
-
externalPort: r.externalPort,
|
|
312
|
-
internalPort: r.internalPort,
|
|
313
|
-
routerExternalIp: r.externalIp,
|
|
314
|
-
leaseExpiresAt: r.leaseExpiresAt,
|
|
315
|
-
lastError: null,
|
|
316
|
-
};
|
|
317
|
-
log('mapped port %d (mode=%s, externalIp=%s)', r.externalPort, r.mode, r.externalIp ?? '?');
|
|
318
|
-
} catch (err) {
|
|
319
|
-
const message = (err as Error).message;
|
|
320
|
-
this.state = {
|
|
321
|
-
...this.state,
|
|
322
|
-
mode: 'failed',
|
|
323
|
-
leaseExpiresAt: null,
|
|
324
|
-
lastError: message,
|
|
325
|
-
};
|
|
326
|
-
log('mapping failed: %s', message);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
1
|
+
import { BlockList } from 'node:net';
|
|
2
|
+
import { networkInterfaces, type NetworkInterfaceInfo } from 'node:os';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
import { upnpNat, type Gateway, type UPnPNAT } from '@achingbrain/nat-port-mapper';
|
|
5
|
+
|
|
6
|
+
import { NatError } from './types.js';
|
|
7
|
+
|
|
8
|
+
const log = debug('cadre:host:nat-port-mapper');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default lease TTL: 1 hour. `@achingbrain/nat-port-mapper` takes the TTL in
|
|
12
|
+
* milliseconds and floors the lease it asks the router for at 3600 s, so a
|
|
13
|
+
* shorter value here would still lease an hour.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_LEASE_TTL_MS = 60 * 60 * 1000;
|
|
16
|
+
|
|
17
|
+
/** Default renewal cadence: half the TTL, so two renewals per lease. */
|
|
18
|
+
export const DEFAULT_REFRESH_MS = 30 * 60 * 1000;
|
|
19
|
+
|
|
20
|
+
/** How long to listen for a UPnP gateway's SSDP answer before giving up. */
|
|
21
|
+
export const GATEWAY_DISCOVERY_TIMEOUT_MS = 10_000;
|
|
22
|
+
|
|
23
|
+
/** Bound on one SOAP call to the router (map, unmap, external IP). */
|
|
24
|
+
export const GATEWAY_OPERATION_TIMEOUT_MS = 10_000;
|
|
25
|
+
|
|
26
|
+
/** What `discover()` learned about the router. */
|
|
27
|
+
export interface GatewayInfo {
|
|
28
|
+
/** This machine's address on the router's subnet — the address mappings point at. */
|
|
29
|
+
lanAddress: string;
|
|
30
|
+
/** The router's own LAN address. */
|
|
31
|
+
routerHost: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PortMapRequest {
|
|
35
|
+
internalPort: number;
|
|
36
|
+
protocol: 'tcp' | 'udp';
|
|
37
|
+
/** Lease TTL in milliseconds. */
|
|
38
|
+
ttlMs: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Result of a successful map(). */
|
|
42
|
+
export interface PortMapResult {
|
|
43
|
+
/** The external port the router granted — may differ from the internal port. */
|
|
44
|
+
externalPort: number;
|
|
45
|
+
leaseExpiresAt: Date;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Wrapper around the UPnP client. An interface so tests inject a fake instead
|
|
50
|
+
* of a router.
|
|
51
|
+
*/
|
|
52
|
+
export interface PortMapper {
|
|
53
|
+
/**
|
|
54
|
+
* Find the router. Resolves null when no gateway answered within the
|
|
55
|
+
* discovery timeout; throws `NatError('router_unreachable')` when one did but
|
|
56
|
+
* cannot be used (no local interface shares its subnet).
|
|
57
|
+
*/
|
|
58
|
+
discover(): Promise<GatewayInfo | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Ask the router to map `internalPort` to the same external port. The router
|
|
61
|
+
* may grant another; the returned `externalPort` is the one that counts.
|
|
62
|
+
* Throws `NatError('mapping_failed')` on refusal.
|
|
63
|
+
*/
|
|
64
|
+
map(req: PortMapRequest): Promise<PortMapResult>;
|
|
65
|
+
/** Remove a mapping installed by this process. Never throws. */
|
|
66
|
+
unmap(internalPort: number, protocol: 'tcp' | 'udp'): Promise<void>;
|
|
67
|
+
/** The WAN address the router reports, or null when it cannot be asked. */
|
|
68
|
+
externalIp(): Promise<string | null>;
|
|
69
|
+
/** Release client resources. Does not unmap. Idempotent. */
|
|
70
|
+
stop(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Signal that aborts after `ms`; the timer is cleared by `done()` so it never holds the process open. */
|
|
74
|
+
function deadline(ms: number): { signal: AbortSignal; done(): void } {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const timer = setTimeout(() => controller.abort(new Error(`timed out after ${ms} ms`)), ms);
|
|
77
|
+
timer.unref();
|
|
78
|
+
return { signal: controller.signal, done: () => clearTimeout(timer) };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pick the IPv4 address of the local interface whose subnet contains the router.
|
|
83
|
+
*
|
|
84
|
+
* Not `mapAll`: that maps on every local address and leaves stray mappings for
|
|
85
|
+
* VPN and container interfaces. The address returned is the one a user is told
|
|
86
|
+
* to forward to, so it must be the one on the router's own network.
|
|
87
|
+
*/
|
|
88
|
+
export function pickLanAddress(
|
|
89
|
+
routerHost: string,
|
|
90
|
+
interfaces: Record<string, NetworkInterfaceInfo[] | undefined> = networkInterfaces(),
|
|
91
|
+
): string | null {
|
|
92
|
+
for (const entries of Object.values(interfaces)) {
|
|
93
|
+
for (const entry of entries ?? []) {
|
|
94
|
+
if (entry.family !== 'IPv4' || entry.internal || !entry.cidr) continue;
|
|
95
|
+
const prefix = Number(entry.cidr.split('/')[1]);
|
|
96
|
+
if (!Number.isInteger(prefix)) continue;
|
|
97
|
+
const subnet = new BlockList();
|
|
98
|
+
try {
|
|
99
|
+
subnet.addSubnet(entry.address, prefix, 'ipv4');
|
|
100
|
+
if (subnet.check(routerHost, 'ipv4')) return entry.address;
|
|
101
|
+
} catch (err) {
|
|
102
|
+
log('skipping interface entry %s (%s): %s', entry.address, entry.cidr, (err as Error).message);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* UPnP port mapper over `@achingbrain/nat-port-mapper` v4: `upnpNat()` hands
|
|
111
|
+
* out a client that discovers gateways; mapping, unmapping and the external IP
|
|
112
|
+
* live on the discovered `Gateway`.
|
|
113
|
+
*
|
|
114
|
+
* The library's auto-refresh stays off: cadre-host renews leases itself so it
|
|
115
|
+
* can report lease expiry and per-mapping failures.
|
|
116
|
+
*/
|
|
117
|
+
export class UpnpPortMapper implements PortMapper {
|
|
118
|
+
private readonly client: UPnPNAT;
|
|
119
|
+
private gateway: Gateway | null = null;
|
|
120
|
+
private lanAddress: string | null = null;
|
|
121
|
+
|
|
122
|
+
constructor(opts: { description?: string } = {}) {
|
|
123
|
+
this.client = upnpNat({
|
|
124
|
+
description: opts.description ?? 'sereus-cadre-host',
|
|
125
|
+
autoRefresh: false,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async discover(): Promise<GatewayInfo | null> {
|
|
130
|
+
const gateway = await this.findIpv4Gateway();
|
|
131
|
+
if (!gateway) return null;
|
|
132
|
+
const lanAddress = pickLanAddress(gateway.host);
|
|
133
|
+
if (!lanAddress) {
|
|
134
|
+
throw new NatError(
|
|
135
|
+
'router_unreachable',
|
|
136
|
+
`found a UPnP router at ${gateway.host} but no local IPv4 interface shares its subnet`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
this.gateway = gateway;
|
|
140
|
+
this.lanAddress = lanAddress;
|
|
141
|
+
log('discovered gateway %s (%s), mapping from %s', gateway.id, gateway.host, lanAddress);
|
|
142
|
+
return { lanAddress, routerHost: gateway.host };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async map(req: PortMapRequest): Promise<PortMapResult> {
|
|
146
|
+
const { gateway, lanAddress } = this.requireGateway();
|
|
147
|
+
const op = deadline(GATEWAY_OPERATION_TIMEOUT_MS);
|
|
148
|
+
try {
|
|
149
|
+
const mapping = await gateway.map(req.internalPort, lanAddress, {
|
|
150
|
+
externalPort: req.internalPort,
|
|
151
|
+
protocol: req.protocol,
|
|
152
|
+
ttl: req.ttlMs,
|
|
153
|
+
autoRefresh: false,
|
|
154
|
+
signal: op.signal,
|
|
155
|
+
});
|
|
156
|
+
return { externalPort: mapping.externalPort, leaseExpiresAt: new Date(Date.now() + req.ttlMs) };
|
|
157
|
+
} catch (err) {
|
|
158
|
+
throw new NatError(
|
|
159
|
+
'mapping_failed',
|
|
160
|
+
`router refused the mapping for port ${req.internalPort}: ${(err as Error).message}`,
|
|
161
|
+
);
|
|
162
|
+
} finally {
|
|
163
|
+
op.done();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// NOTE: the library tracks the mappings this process made and `unmap` only sends a
|
|
168
|
+
// delete for those, so after a host restart a port mapped by the previous process is
|
|
169
|
+
// left to expire with its lease rather than deleted. It also names the INTERNAL port
|
|
170
|
+
// as the external one in that delete, so a mapping the router granted on another
|
|
171
|
+
// external port is not deleted either; it, too, expires with its lease (1 h). And it
|
|
172
|
+
// appends one tracked entry per successful `map` of a port, so after N renewals an
|
|
173
|
+
// unmap sends N deletes (the first is the one that counts); they all run under the
|
|
174
|
+
// one 10 s bound, which caps the cost of a process that has renewed for days.
|
|
175
|
+
async unmap(internalPort: number, _protocol: 'tcp' | 'udp'): Promise<void> {
|
|
176
|
+
if (!this.gateway) return;
|
|
177
|
+
const op = deadline(GATEWAY_OPERATION_TIMEOUT_MS);
|
|
178
|
+
try {
|
|
179
|
+
await this.gateway.unmap(internalPort, { signal: op.signal });
|
|
180
|
+
} catch (err) {
|
|
181
|
+
log('unmap(%d) failed: %s', internalPort, (err as Error).message);
|
|
182
|
+
} finally {
|
|
183
|
+
op.done();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async externalIp(): Promise<string | null> {
|
|
188
|
+
if (!this.gateway) return null;
|
|
189
|
+
const op = deadline(GATEWAY_OPERATION_TIMEOUT_MS);
|
|
190
|
+
try {
|
|
191
|
+
return await this.gateway.externalIp({ signal: op.signal });
|
|
192
|
+
} catch (err) {
|
|
193
|
+
log('externalIp() failed: %s', (err as Error).message);
|
|
194
|
+
return null;
|
|
195
|
+
} finally {
|
|
196
|
+
op.done();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Forget the gateway. Deliberately not `gateway.stop()`: that deletes every
|
|
202
|
+
* mapping this process made, and hosted children keep running across a host
|
|
203
|
+
* restart, so their mappings must outlive this process.
|
|
204
|
+
*/
|
|
205
|
+
async stop(): Promise<void> {
|
|
206
|
+
this.gateway = null;
|
|
207
|
+
this.lanAddress = null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private requireGateway(): { gateway: Gateway; lanAddress: string } {
|
|
211
|
+
if (!this.gateway || !this.lanAddress) {
|
|
212
|
+
throw new NatError('router_unreachable', 'no UPnP gateway discovered');
|
|
213
|
+
}
|
|
214
|
+
return { gateway: this.gateway, lanAddress: this.lanAddress };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* First IPv4 gateway to answer the SSDP search, or null when none did within
|
|
219
|
+
* the timeout.
|
|
220
|
+
*
|
|
221
|
+
* NOTE: the library searches for `InternetGatewayDevice:2` only (its
|
|
222
|
+
* `upnp/discovery.js`), and a router that implements only version 1 does not
|
|
223
|
+
* answer a version-2 search, so such a router reads as "not found". There is
|
|
224
|
+
* no library option for it; if it shows up on real hosts, the fix is a fork or
|
|
225
|
+
* another library.
|
|
226
|
+
*/
|
|
227
|
+
private async findIpv4Gateway(): Promise<Gateway | null> {
|
|
228
|
+
const search = deadline(GATEWAY_DISCOVERY_TIMEOUT_MS);
|
|
229
|
+
try {
|
|
230
|
+
for await (const gateway of this.client.findGateways({ signal: search.signal })) {
|
|
231
|
+
if (gateway.family === 'IPv4') return gateway;
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (search.signal.aborted) return null;
|
|
236
|
+
throw new NatError('router_unreachable', `UPnP discovery failed: ${(err as Error).message}`);
|
|
237
|
+
} finally {
|
|
238
|
+
search.done();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|