@serfab/cadre-host 1.14.0 → 1.16.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 +19 -4
- package/dist/bin/host.js +32 -20
- package/dist/bin/host.js.map +1 -1
- package/dist/bin/nat-output.d.ts +5 -0
- package/dist/bin/nat-output.js +12 -1
- package/dist/bin/nat-output.js.map +1 -1
- package/dist/bin/ui-access.d.ts +22 -0
- package/dist/bin/ui-access.js +44 -0
- package/dist/bin/ui-access.js.map +1 -0
- package/dist/hosted/claim-details.d.ts +25 -3
- package/dist/hosted/claim-details.js +31 -37
- package/dist/hosted/claim-details.js.map +1 -1
- package/dist/hosted/hosted-node-service.d.ts +7 -0
- package/dist/hosted/hosted-node-service.js +16 -4
- package/dist/hosted/hosted-node-service.js.map +1 -1
- package/dist/hosted/hosted-node-watcher.d.ts +16 -4
- package/dist/hosted/hosted-node-watcher.js +45 -9
- package/dist/hosted/hosted-node-watcher.js.map +1 -1
- package/dist/hosted/node-status.d.ts +19 -3
- package/dist/hosted/node-status.js +21 -5
- package/dist/hosted/node-status.js.map +1 -1
- package/dist/hosted/types.d.ts +12 -5
- package/dist/hosted/types.js.map +1 -1
- package/dist/installer/config.d.ts +23 -0
- package/dist/installer/config.js +46 -0
- package/dist/installer/config.js.map +1 -1
- package/dist/installer/index.d.ts +2 -0
- package/dist/installer/index.js +7 -16
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/wizard.d.ts +12 -2
- package/dist/installer/wizard.js +15 -7
- package/dist/installer/wizard.js.map +1 -1
- package/dist/nat/index.d.ts +2 -1
- package/dist/nat/index.js +1 -0
- package/dist/nat/index.js.map +1 -1
- package/dist/nat/nat-service.d.ts +22 -5
- package/dist/nat/nat-service.js +75 -26
- package/dist/nat/nat-service.js.map +1 -1
- package/dist/nat/nat-store.js +11 -0
- package/dist/nat/nat-store.js.map +1 -1
- package/dist/nat/network-mode.d.ts +19 -0
- package/dist/nat/network-mode.js +32 -0
- package/dist/nat/network-mode.js.map +1 -0
- package/dist/nat/reachability.d.ts +5 -4
- package/dist/nat/reachability.js +19 -6
- package/dist/nat/reachability.js.map +1 -1
- package/dist/nat/types.d.ts +26 -6
- package/dist/nat/types.js.map +1 -1
- package/dist/orchestrator/host-process-orchestrator.d.ts +3 -3
- package/dist/orchestrator/host-process-orchestrator.js +6 -15
- package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
- package/dist/package-version.d.ts +1 -0
- package/dist/package-version.js +4 -0
- package/dist/package-version.js.map +1 -0
- package/dist/server/routes/settings.d.ts +1 -0
- package/dist/server/routes/settings.js +15 -1
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/status.js +2 -32
- package/dist/server/routes/status.js.map +1 -1
- package/dist/ui/assets/index-BHd8CWZA.css +1 -0
- package/dist/ui/assets/index-Ccr2GjPT.js +23 -0
- package/dist/ui/assets/{index-4FWuU8ye.js.map → index-Ccr2GjPT.js.map} +1 -1
- package/dist/ui/index.html +2 -2
- package/package.json +4 -5
- package/src/__tests__/cli.smoke.test.ts +1 -1
- package/src/__tests__/orchestrator-claim-secret.test.ts +1 -2
- package/src/__tests__/orchestrator-env-scrub.test.ts +160 -163
- package/src/__tests__/orchestrator-handle-contract.ts +1 -1
- package/src/__tests__/orchestrator-node-identity.test.ts +0 -5
- package/src/__tests__/orchestrator-push.test.ts +3 -3
- package/src/__tests__/orchestrator.test.ts +3 -5
- package/src/__tests__/ui-access.test.ts +30 -0
- package/src/bin/host.ts +30 -20
- package/src/bin/nat-output.ts +12 -1
- package/src/bin/ui-access.ts +50 -0
- package/src/hosted/__tests__/fake-orchestrator.test.ts +0 -1
- package/src/hosted/__tests__/fake-orchestrator.ts +263 -265
- package/src/hosted/__tests__/hosted-node-service.test.ts +708 -609
- package/src/hosted/__tests__/hosted-node-supervisor.test.ts +475 -474
- package/src/hosted/claim-details.ts +44 -37
- package/src/hosted/hosted-node-service.ts +23 -4
- package/src/hosted/hosted-node-watcher.ts +288 -250
- package/src/hosted/node-status.ts +31 -5
- package/src/hosted/types.ts +160 -153
- package/src/installer/__tests__/installer.smoke.test.ts +184 -182
- package/src/installer/__tests__/wizard.test.ts +76 -56
- package/src/installer/config.ts +66 -0
- package/src/installer/index.ts +252 -259
- package/src/installer/wizard.ts +101 -87
- package/src/nat/__tests__/nat-service.test.ts +608 -540
- package/src/nat/__tests__/nat-store.test.ts +130 -127
- package/src/nat/__tests__/network-mode.test.ts +38 -0
- package/src/nat/index.ts +91 -87
- package/src/nat/nat-service.ts +79 -26
- package/src/nat/nat-store.ts +249 -237
- package/src/nat/network-mode.ts +36 -0
- package/src/nat/reachability.ts +155 -143
- package/src/nat/types.ts +232 -209
- package/src/orchestrator/host-process-orchestrator.ts +1089 -1098
- package/src/package-version.ts +4 -0
- package/src/server/__tests__/fakes.ts +41 -40
- package/src/server/__tests__/hosted-nodes-route.test.ts +161 -160
- package/src/server/__tests__/settings-route.test.ts +31 -0
- package/src/server/routes/settings.ts +128 -115
- package/src/server/routes/status.ts +72 -102
- package/dist/ui/assets/index-4FWuU8ye.js +0 -19
- package/dist/ui/assets/index-Di9Q4d01.css +0 -1
package/src/nat/types.ts
CHANGED
|
@@ -1,209 +1,232 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NAT/DDNS types for cadre-host.
|
|
3
|
-
*
|
|
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.
|
|
9
|
-
* 2. Detect its external IP (and whether it's behind CGNAT).
|
|
10
|
-
* 3. Optionally publish a stable hostname via dynamic DNS.
|
|
11
|
-
*
|
|
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.
|
|
15
|
-
*
|
|
16
|
-
* Credentials for DDNS providers are NOT stored in `nat.json`. They go through
|
|
17
|
-
* the SecretsStore (keytar, with a 0600 file fallback).
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/** Reachability roll-up across the host's running nodes — see `reachability.ts`. */
|
|
21
|
-
export type DirectReachability = 'reachable' | 'unreachable' | 'unknown' | 'cgnat';
|
|
22
|
-
|
|
23
|
-
/** The two ports of a hosted node that are mapped: libp2p TCP and libp2p WebSocket. */
|
|
24
|
-
export type PortKind = 'tcp' | 'ws';
|
|
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
|
-
export interface
|
|
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
|
-
|
|
1
|
+
/**
|
|
2
|
+
* NAT/DDNS types for cadre-host.
|
|
3
|
+
*
|
|
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.
|
|
9
|
+
* 2. Detect its external IP (and whether it's behind CGNAT).
|
|
10
|
+
* 3. Optionally publish a stable hostname via dynamic DNS.
|
|
11
|
+
*
|
|
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.
|
|
15
|
+
*
|
|
16
|
+
* Credentials for DDNS providers are NOT stored in `nat.json`. They go through
|
|
17
|
+
* the SecretsStore (keytar, with a 0600 file fallback).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Reachability roll-up across the host's running nodes — see `reachability.ts`. */
|
|
21
|
+
export type DirectReachability = 'reachable' | 'unreachable' | 'unknown' | 'cgnat';
|
|
22
|
+
|
|
23
|
+
/** The two ports of a hosted node that are mapped: libp2p TCP and libp2p WebSocket. */
|
|
24
|
+
export type PortKind = 'tcp' | 'ws';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* How this machine meets the internet. `lan`: behind a router (home or office), so node ports need
|
|
28
|
+
* UPnP or a forward on the router. `public`: an interface holds a public IPv4 (a VPS, a server with a
|
|
29
|
+
* routed address), so node ports are reachable at their own numbers unless a firewall blocks them.
|
|
30
|
+
*/
|
|
31
|
+
export type NetworkMode = 'lan' | 'public';
|
|
32
|
+
|
|
33
|
+
/** The `network` setting: `auto` picks the mode from this machine's interfaces. */
|
|
34
|
+
export type NetworkSetting = 'auto' | NetworkMode;
|
|
35
|
+
|
|
36
|
+
/** The network section of the status snapshot. */
|
|
37
|
+
export interface NatNetworkStatus {
|
|
38
|
+
setting: NetworkSetting;
|
|
39
|
+
/** The mode in effect: the setting, or what `auto` found. */
|
|
40
|
+
mode: NetworkMode;
|
|
41
|
+
/** The public IPv4 an interface holds, when one does; what `auto` decides on. */
|
|
42
|
+
publicInterfaceIp: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Where a port's external route came from. `direct`: public mode, the port as-is. */
|
|
46
|
+
export type PortRouteSource = 'upnp' | 'manual' | 'direct';
|
|
47
|
+
|
|
48
|
+
/** How one of a node's ports is reached from outside the home network. */
|
|
49
|
+
export interface PortRoute {
|
|
50
|
+
internalPort: number;
|
|
51
|
+
/** Port reachable from outside; null when there is no route. */
|
|
52
|
+
externalPort: number | null;
|
|
53
|
+
source: PortRouteSource | null;
|
|
54
|
+
/** ISO timestamp when the router lease expires; UPnP routes only. */
|
|
55
|
+
leaseExpiresAt: string | null;
|
|
56
|
+
/** Last mapping failure, in plain language; null when the last attempt succeeded. */
|
|
57
|
+
error: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Per-node verdict. `direct`: reachable at this machine's public address, firewall permitting. */
|
|
61
|
+
export type NodeVerdict = 'mapped' | 'manual' | 'direct' | 'unreachable';
|
|
62
|
+
|
|
63
|
+
/** Reachability of one hosted node, as reported in the status snapshot. */
|
|
64
|
+
export interface NodeReachability {
|
|
65
|
+
nodeId: string;
|
|
66
|
+
/** Whether the node's process is running; a stopped node keeps its last routes until the unmap grace elapses. */
|
|
67
|
+
running: boolean;
|
|
68
|
+
verdict: NodeVerdict;
|
|
69
|
+
/**
|
|
70
|
+
* Plain-language reason and remedy when unreachable, e.g. "Router refused
|
|
71
|
+
* the WebSocket port mapping. Forward port 10004 to 192.168.1.20 on your
|
|
72
|
+
* router, then tell cadre-host the external port." For `direct`, the
|
|
73
|
+
* firewall reminder: which ports to allow.
|
|
74
|
+
*/
|
|
75
|
+
reason: string | null;
|
|
76
|
+
tcp: PortRoute;
|
|
77
|
+
/** Null for a handle persisted by an older build without a WebSocket port. */
|
|
78
|
+
ws: PortRoute | null;
|
|
79
|
+
/** Multiaddrs (no `/p2p/` suffix) through which the node is reachable from outside. */
|
|
80
|
+
publicAddrs: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The UPnP gateway as the host last saw it. */
|
|
84
|
+
export interface NatGatewayStatus {
|
|
85
|
+
found: boolean;
|
|
86
|
+
/** This machine's address on the router's subnet — the address a user forwards to. */
|
|
87
|
+
lanAddress: string | null;
|
|
88
|
+
/** The WAN address the router reports for itself. */
|
|
89
|
+
routerExternalIp: string | null;
|
|
90
|
+
/** Why discovery or the last gateway operation failed; null when it did not. */
|
|
91
|
+
lastError: string | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Snapshot returned to the management UI / CLI. */
|
|
95
|
+
export interface NatStatusSnapshot {
|
|
96
|
+
network: NatNetworkStatus;
|
|
97
|
+
upnpEnabled: boolean;
|
|
98
|
+
gateway: NatGatewayStatus;
|
|
99
|
+
|
|
100
|
+
// External IP (from the public probe, falling back to the router's report).
|
|
101
|
+
externalIp: string | null;
|
|
102
|
+
externalIpDetectedAt: string | null;
|
|
103
|
+
cgnatDetected: boolean;
|
|
104
|
+
|
|
105
|
+
/** Roll-up across running nodes; see `reachability.ts`. */
|
|
106
|
+
directReachability: DirectReachability;
|
|
107
|
+
lastTestedAt: string | null;
|
|
108
|
+
|
|
109
|
+
ddns: NatDdnsStatus;
|
|
110
|
+
nodes: NodeReachability[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** DDNS sub-section of the status snapshot. */
|
|
114
|
+
export interface NatDdnsStatus {
|
|
115
|
+
/** Provider ID (e.g. "duckdns"), null when no DDNS is configured. */
|
|
116
|
+
providerId: string | null;
|
|
117
|
+
/** Configured hostname (e.g. "foo.duckdns.org"), null when unconfigured. */
|
|
118
|
+
hostname: string | null;
|
|
119
|
+
/** True when the user opted out of cadre-host updating the record. */
|
|
120
|
+
externallyManaged: boolean;
|
|
121
|
+
/** Last attempt timestamp (ISO), null until first attempt. */
|
|
122
|
+
lastUpdateAt: string | null;
|
|
123
|
+
/** Whether the most recent attempt succeeded. */
|
|
124
|
+
lastUpdateOk: boolean | null;
|
|
125
|
+
/** Most recent error message, null when last attempt succeeded. */
|
|
126
|
+
lastError: string | null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The external ports a user forwarded on their router for one node, per port kind. */
|
|
130
|
+
export interface ManualForward {
|
|
131
|
+
tcp?: number;
|
|
132
|
+
ws?: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Patch applied by `PUT /nat/nodes/:nodeId/forward`: `null` clears that port. */
|
|
136
|
+
export interface ManualForwardPatch {
|
|
137
|
+
tcp?: number | null;
|
|
138
|
+
ws?: number | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* On-disk shape of `nat.json`. NEVER contains DDNS credentials — those live
|
|
143
|
+
* in the SecretsStore.
|
|
144
|
+
*/
|
|
145
|
+
export interface NatSettingsFile {
|
|
146
|
+
version: 1;
|
|
147
|
+
/** LAN or public-IP mode; `auto` (default) decides from the interfaces. Absent in older files. */
|
|
148
|
+
network: NetworkSetting;
|
|
149
|
+
/** Whether to ask the router for port mappings at all. Ignored in public mode. */
|
|
150
|
+
upnpEnabled: boolean;
|
|
151
|
+
/** Per node id: the external port the user forwarded on their router, per port kind. */
|
|
152
|
+
forwards: Record<string, ManualForward>;
|
|
153
|
+
ddns: NatDdnsSettings;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** DDNS sub-section of `nat.json`. */
|
|
157
|
+
export interface NatDdnsSettings {
|
|
158
|
+
/** Provider ID (e.g. "duckdns"). Null = unconfigured. */
|
|
159
|
+
providerId: string | null;
|
|
160
|
+
/** Hostname to publish. Null = unconfigured. */
|
|
161
|
+
hostname: string | null;
|
|
162
|
+
/**
|
|
163
|
+
* True means the user maintains the DNS record themselves (e.g. via the
|
|
164
|
+
* provider's own auto-update agent on the router). cadre-host shows the
|
|
165
|
+
* hostname but never tries to update it.
|
|
166
|
+
*/
|
|
167
|
+
externallyManaged: boolean;
|
|
168
|
+
/** DDNS update interval in milliseconds. Default 5 min. */
|
|
169
|
+
intervalMs: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Typed error codes for NatError. */
|
|
173
|
+
export type NatErrorCode =
|
|
174
|
+
| 'mapping_failed'
|
|
175
|
+
| 'router_unreachable'
|
|
176
|
+
| 'ip_detection_failed'
|
|
177
|
+
| 'ddns_provider_unknown'
|
|
178
|
+
| 'ddns_credentials_missing'
|
|
179
|
+
| 'ddns_update_failed'
|
|
180
|
+
| 'secrets_unavailable'
|
|
181
|
+
| 'storage_error'
|
|
182
|
+
| 'invalid_config'
|
|
183
|
+
/** A manual forward named a node id the host does not run. */
|
|
184
|
+
| 'unknown_node';
|
|
185
|
+
|
|
186
|
+
/** Typed error carrying a stable `code` for HTTP mapping in the local-ui. */
|
|
187
|
+
export class NatError extends Error {
|
|
188
|
+
readonly code: NatErrorCode;
|
|
189
|
+
|
|
190
|
+
constructor(code: NatErrorCode, message: string) {
|
|
191
|
+
super(message);
|
|
192
|
+
this.name = 'NatError';
|
|
193
|
+
this.code = code;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Description of a configurable field on a DDNS provider. */
|
|
198
|
+
export interface DdnsConfigField {
|
|
199
|
+
/** Field key (e.g. "token"). */
|
|
200
|
+
key: string;
|
|
201
|
+
/** Human-readable label (e.g. "DuckDNS token"). */
|
|
202
|
+
label: string;
|
|
203
|
+
/** True if the value is a credential that must be stored in the SecretsStore. */
|
|
204
|
+
secret: boolean;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Summary of a DDNS provider for UI / CLI listing. */
|
|
208
|
+
export interface DdnsProviderInfo {
|
|
209
|
+
id: string;
|
|
210
|
+
displayName: string;
|
|
211
|
+
configFields: ReadonlyArray<DdnsConfigField>;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Typed HTTP handlers exposed to the local UI server for Fastify wiring.
|
|
216
|
+
*
|
|
217
|
+
* These take typed objects and either return typed results or throw a
|
|
218
|
+
* `NatError` whose `.code` the error handler maps to an HTTP status code.
|
|
219
|
+
*/
|
|
220
|
+
export interface NatHandlers {
|
|
221
|
+
getStatus(): Promise<NatStatusSnapshot>;
|
|
222
|
+
testReachability(): Promise<NatStatusSnapshot>;
|
|
223
|
+
listDdnsProviders(): Promise<DdnsProviderInfo[]>;
|
|
224
|
+
putDdns(body: {
|
|
225
|
+
providerId: string;
|
|
226
|
+
hostname: string;
|
|
227
|
+
config: Record<string, string>;
|
|
228
|
+
externallyManaged?: boolean;
|
|
229
|
+
}): Promise<NatStatusSnapshot>;
|
|
230
|
+
putSettings(body: Partial<Omit<NatSettingsFile, 'version' | 'forwards'>>): Promise<NatStatusSnapshot>;
|
|
231
|
+
putForward(nodeId: string, patch: ManualForwardPatch): Promise<NatStatusSnapshot>;
|
|
232
|
+
}
|