@serfab/cadre-host 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +19 -4
  2. package/dist/bin/host.js +32 -20
  3. package/dist/bin/host.js.map +1 -1
  4. package/dist/bin/nat-output.d.ts +5 -0
  5. package/dist/bin/nat-output.js +12 -1
  6. package/dist/bin/nat-output.js.map +1 -1
  7. package/dist/bin/ui-access.d.ts +22 -0
  8. package/dist/bin/ui-access.js +44 -0
  9. package/dist/bin/ui-access.js.map +1 -0
  10. package/dist/hosted/claim-details.d.ts +25 -3
  11. package/dist/hosted/claim-details.js +31 -37
  12. package/dist/hosted/claim-details.js.map +1 -1
  13. package/dist/hosted/hosted-node-service.d.ts +7 -0
  14. package/dist/hosted/hosted-node-service.js +16 -4
  15. package/dist/hosted/hosted-node-service.js.map +1 -1
  16. package/dist/hosted/hosted-node-watcher.d.ts +16 -4
  17. package/dist/hosted/hosted-node-watcher.js +45 -9
  18. package/dist/hosted/hosted-node-watcher.js.map +1 -1
  19. package/dist/hosted/node-status.d.ts +19 -3
  20. package/dist/hosted/node-status.js +21 -5
  21. package/dist/hosted/node-status.js.map +1 -1
  22. package/dist/hosted/types.d.ts +12 -5
  23. package/dist/hosted/types.js.map +1 -1
  24. package/dist/installer/config.d.ts +23 -0
  25. package/dist/installer/config.js +46 -0
  26. package/dist/installer/config.js.map +1 -1
  27. package/dist/installer/index.d.ts +2 -0
  28. package/dist/installer/index.js +7 -16
  29. package/dist/installer/index.js.map +1 -1
  30. package/dist/installer/wizard.d.ts +12 -2
  31. package/dist/installer/wizard.js +15 -7
  32. package/dist/installer/wizard.js.map +1 -1
  33. package/dist/nat/index.d.ts +2 -1
  34. package/dist/nat/index.js +1 -0
  35. package/dist/nat/index.js.map +1 -1
  36. package/dist/nat/nat-service.d.ts +22 -5
  37. package/dist/nat/nat-service.js +75 -26
  38. package/dist/nat/nat-service.js.map +1 -1
  39. package/dist/nat/nat-store.js +11 -0
  40. package/dist/nat/nat-store.js.map +1 -1
  41. package/dist/nat/network-mode.d.ts +19 -0
  42. package/dist/nat/network-mode.js +32 -0
  43. package/dist/nat/network-mode.js.map +1 -0
  44. package/dist/nat/reachability.d.ts +5 -4
  45. package/dist/nat/reachability.js +19 -6
  46. package/dist/nat/reachability.js.map +1 -1
  47. package/dist/nat/types.d.ts +26 -6
  48. package/dist/nat/types.js.map +1 -1
  49. package/dist/orchestrator/host-process-orchestrator.d.ts +3 -3
  50. package/dist/orchestrator/host-process-orchestrator.js +6 -15
  51. package/dist/orchestrator/host-process-orchestrator.js.map +1 -1
  52. package/dist/package-version.d.ts +1 -0
  53. package/dist/package-version.js +4 -0
  54. package/dist/package-version.js.map +1 -0
  55. package/dist/server/routes/settings.d.ts +1 -0
  56. package/dist/server/routes/settings.js +15 -1
  57. package/dist/server/routes/settings.js.map +1 -1
  58. package/dist/server/routes/status.js +2 -32
  59. package/dist/server/routes/status.js.map +1 -1
  60. package/dist/ui/assets/index-BHd8CWZA.css +1 -0
  61. package/dist/ui/assets/index-Ccr2GjPT.js +23 -0
  62. package/dist/ui/assets/{index-4FWuU8ye.js.map → index-Ccr2GjPT.js.map} +1 -1
  63. package/dist/ui/index.html +2 -2
  64. package/package.json +4 -5
  65. package/src/__tests__/cli.smoke.test.ts +1 -1
  66. package/src/__tests__/orchestrator-claim-secret.test.ts +1 -2
  67. package/src/__tests__/orchestrator-env-scrub.test.ts +160 -163
  68. package/src/__tests__/orchestrator-handle-contract.ts +1 -1
  69. package/src/__tests__/orchestrator-node-identity.test.ts +0 -5
  70. package/src/__tests__/orchestrator-push.test.ts +3 -3
  71. package/src/__tests__/orchestrator.test.ts +3 -5
  72. package/src/__tests__/ui-access.test.ts +30 -0
  73. package/src/bin/host.ts +30 -20
  74. package/src/bin/nat-output.ts +12 -1
  75. package/src/bin/ui-access.ts +50 -0
  76. package/src/hosted/__tests__/fake-orchestrator.test.ts +0 -1
  77. package/src/hosted/__tests__/fake-orchestrator.ts +263 -265
  78. package/src/hosted/__tests__/hosted-node-service.test.ts +708 -609
  79. package/src/hosted/__tests__/hosted-node-supervisor.test.ts +475 -474
  80. package/src/hosted/claim-details.ts +44 -37
  81. package/src/hosted/hosted-node-service.ts +23 -4
  82. package/src/hosted/hosted-node-watcher.ts +288 -250
  83. package/src/hosted/node-status.ts +31 -5
  84. package/src/hosted/types.ts +160 -153
  85. package/src/installer/__tests__/installer.smoke.test.ts +184 -182
  86. package/src/installer/__tests__/wizard.test.ts +76 -56
  87. package/src/installer/config.ts +66 -0
  88. package/src/installer/index.ts +252 -259
  89. package/src/installer/wizard.ts +101 -87
  90. package/src/nat/__tests__/nat-service.test.ts +608 -540
  91. package/src/nat/__tests__/nat-store.test.ts +130 -127
  92. package/src/nat/__tests__/network-mode.test.ts +38 -0
  93. package/src/nat/index.ts +91 -87
  94. package/src/nat/nat-service.ts +79 -26
  95. package/src/nat/nat-store.ts +249 -237
  96. package/src/nat/network-mode.ts +36 -0
  97. package/src/nat/reachability.ts +155 -143
  98. package/src/nat/types.ts +232 -209
  99. package/src/orchestrator/host-process-orchestrator.ts +1089 -1098
  100. package/src/package-version.ts +4 -0
  101. package/src/server/__tests__/fakes.ts +41 -40
  102. package/src/server/__tests__/hosted-nodes-route.test.ts +161 -160
  103. package/src/server/__tests__/settings-route.test.ts +31 -0
  104. package/src/server/routes/settings.ts +128 -115
  105. package/src/server/routes/status.ts +72 -102
  106. package/dist/ui/assets/index-4FWuU8ye.js +0 -19
  107. package/dist/ui/assets/index-Di9Q4d01.css +0 -1
@@ -1,127 +1,130 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
- import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
-
6
- import { NatStore } from '../nat-store.js';
7
- import { NatError } from '../types.js';
8
-
9
- let tmpRoot: string;
10
-
11
- beforeEach(() => {
12
- tmpRoot = mkdtempSync(join(tmpdir(), 'cadre-host-nat-store-'));
13
- });
14
-
15
- afterEach(() => {
16
- try { rmSync(tmpRoot, { recursive: true, force: true }); } catch { /* ignore */ }
17
- });
18
-
19
- function rawFile(): Record<string, unknown> {
20
- return JSON.parse(readFileSync(join(tmpRoot, 'nat.json'), 'utf8')) as Record<string, unknown>;
21
- }
22
-
23
- describe('NatStore', () => {
24
- it('returns defaults when file is missing', () => {
25
- const store = new NatStore(tmpRoot);
26
- const s = store.load();
27
- expect(s.upnpEnabled).toBe(true);
28
- expect(s.forwards).toEqual({});
29
- expect(s.ddns.providerId).toBeNull();
30
- expect(s.ddns.externallyManaged).toBe(false);
31
- expect(s.ddns.intervalMs).toBe(5 * 60 * 1000);
32
- });
33
-
34
- it('persists and reloads settings atomically', () => {
35
- const store = new NatStore(tmpRoot);
36
- store.update({ upnpEnabled: false });
37
-
38
- expect(existsSync(join(tmpRoot, 'nat.json'))).toBe(true);
39
- expect(existsSync(join(tmpRoot, 'nat.json.tmp'))).toBe(false);
40
-
41
- const reread = new NatStore(tmpRoot);
42
- expect(reread.load().upnpEnabled).toBe(false);
43
- expect(rawFile().version).toBe(1);
44
- });
45
-
46
- it('loads a file written by an older build and drops its single-port fields on save', () => {
47
- writeFileSync(join(tmpRoot, 'nat.json'), JSON.stringify({
48
- version: 1,
49
- externalPort: 4001,
50
- internalPort: 4001,
51
- upnpEnabled: false,
52
- ddns: { providerId: null, hostname: null, externallyManaged: false, intervalMs: 300_000 },
53
- }), 'utf8');
54
- const store = new NatStore(tmpRoot);
55
- const s = store.load();
56
- expect(s.upnpEnabled).toBe(false);
57
- expect(s.forwards).toEqual({});
58
- expect('externalPort' in s).toBe(false);
59
-
60
- store.update({ upnpEnabled: true });
61
- const raw = rawFile();
62
- expect(raw.externalPort).toBeUndefined();
63
- expect(raw.internalPort).toBeUndefined();
64
- expect(raw.forwards).toEqual({});
65
- });
66
-
67
- it('merges ddns subtree on update', () => {
68
- const store = new NatStore(tmpRoot);
69
- store.update({
70
- ddns: {
71
- providerId: 'duckdns',
72
- hostname: 'foo.duckdns.org',
73
- externallyManaged: false,
74
- intervalMs: 60_000,
75
- },
76
- });
77
-
78
- store.update({ ddns: { externallyManaged: true } as never });
79
-
80
- const s = store.load();
81
- expect(s.ddns.providerId).toBe('duckdns');
82
- expect(s.ddns.hostname).toBe('foo.duckdns.org');
83
- expect(s.ddns.externallyManaged).toBe(true);
84
- expect(s.ddns.intervalMs).toBe(60_000);
85
- });
86
-
87
- it('setForward sets, clears and removes per-port entries', () => {
88
- const store = new NatStore(tmpRoot);
89
- expect(store.setForward('a', { tcp: 40000, ws: 40001 }).forwards).toEqual({ a: { tcp: 40000, ws: 40001 } });
90
- expect(store.setForward('a', { tcp: null }).forwards).toEqual({ a: { ws: 40001 } });
91
- expect(store.setForward('a', { ws: 40002 }).forwards).toEqual({ a: { ws: 40002 } });
92
- expect(store.setForward('a', { ws: null }).forwards).toEqual({});
93
- expect(new NatStore(tmpRoot).load().forwards).toEqual({});
94
- });
95
-
96
- it('deleteForward drops one node and leaves the rest', () => {
97
- const store = new NatStore(tmpRoot);
98
- store.setForward('a', { tcp: 40000 });
99
- store.setForward('b', { ws: 40001 });
100
- expect(store.deleteForward('a').forwards).toEqual({ b: { ws: 40001 } });
101
- expect(store.deleteForward('missing').forwards).toEqual({ b: { ws: 40001 } });
102
- });
103
-
104
- it('throws on malformed JSON rather than silently wiping', () => {
105
- writeFileSync(join(tmpRoot, 'nat.json'), 'not json', 'utf8');
106
- const store = new NatStore(tmpRoot);
107
- expect(() => store.load()).toThrow(NatError);
108
- });
109
-
110
- it('rejects an out-of-range or non-integer forwarded port', () => {
111
- const store = new NatStore(tmpRoot);
112
- expect(() => store.setForward('a', { tcp: 0 })).toThrow(NatError);
113
- expect(() => store.setForward('a', { ws: 70000 })).toThrow(NatError);
114
- expect(() => store.setForward('a', { tcp: 1.5 })).toThrow(NatError);
115
- });
116
-
117
- it('rejects too-small interval', () => {
118
- const store = new NatStore(tmpRoot);
119
- expect(() => store.update({ ddns: { intervalMs: 500 } as never })).toThrow(NatError);
120
- });
121
-
122
- it('rejects empty string providerId/hostname', () => {
123
- const store = new NatStore(tmpRoot);
124
- expect(() => store.update({ ddns: { providerId: '' } as never })).toThrow(NatError);
125
- expect(() => store.update({ ddns: { hostname: '' } as never })).toThrow(NatError);
126
- });
127
- });
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ import { NatStore } from '../nat-store.js';
7
+ import { NatError } from '../types.js';
8
+
9
+ let tmpRoot: string;
10
+
11
+ beforeEach(() => {
12
+ tmpRoot = mkdtempSync(join(tmpdir(), 'cadre-host-nat-store-'));
13
+ });
14
+
15
+ afterEach(() => {
16
+ try { rmSync(tmpRoot, { recursive: true, force: true }); } catch { /* ignore */ }
17
+ });
18
+
19
+ function rawFile(): Record<string, unknown> {
20
+ return JSON.parse(readFileSync(join(tmpRoot, 'nat.json'), 'utf8')) as Record<string, unknown>;
21
+ }
22
+
23
+ describe('NatStore', () => {
24
+ it('returns defaults when file is missing', () => {
25
+ const store = new NatStore(tmpRoot);
26
+ const s = store.load();
27
+ expect(s.network).toBe('auto');
28
+ expect(s.upnpEnabled).toBe(true);
29
+ expect(s.forwards).toEqual({});
30
+ expect(s.ddns.providerId).toBeNull();
31
+ expect(s.ddns.externallyManaged).toBe(false);
32
+ expect(s.ddns.intervalMs).toBe(5 * 60 * 1000);
33
+ });
34
+
35
+ it('persists and reloads settings atomically', () => {
36
+ const store = new NatStore(tmpRoot);
37
+ store.update({ upnpEnabled: false });
38
+
39
+ expect(existsSync(join(tmpRoot, 'nat.json'))).toBe(true);
40
+ expect(existsSync(join(tmpRoot, 'nat.json.tmp'))).toBe(false);
41
+
42
+ const reread = new NatStore(tmpRoot);
43
+ expect(reread.load().upnpEnabled).toBe(false);
44
+ expect(rawFile().version).toBe(1);
45
+ });
46
+
47
+ it('loads a file written by an older build and drops its single-port fields on save', () => {
48
+ writeFileSync(join(tmpRoot, 'nat.json'), JSON.stringify({
49
+ version: 1,
50
+ externalPort: 4001,
51
+ internalPort: 4001,
52
+ upnpEnabled: false,
53
+ ddns: { providerId: null, hostname: null, externallyManaged: false, intervalMs: 300_000 },
54
+ }), 'utf8');
55
+ const store = new NatStore(tmpRoot);
56
+ const s = store.load();
57
+ expect(s.upnpEnabled).toBe(false);
58
+ expect(s.forwards).toEqual({});
59
+ expect('externalPort' in s).toBe(false);
60
+ // Written before network modes existed: reads as `auto`.
61
+ expect(s.network).toBe('auto');
62
+
63
+ store.update({ upnpEnabled: true });
64
+ const raw = rawFile();
65
+ expect(raw.externalPort).toBeUndefined();
66
+ expect(raw.internalPort).toBeUndefined();
67
+ expect(raw.forwards).toEqual({});
68
+ });
69
+
70
+ it('merges ddns subtree on update', () => {
71
+ const store = new NatStore(tmpRoot);
72
+ store.update({
73
+ ddns: {
74
+ providerId: 'duckdns',
75
+ hostname: 'foo.duckdns.org',
76
+ externallyManaged: false,
77
+ intervalMs: 60_000,
78
+ },
79
+ });
80
+
81
+ store.update({ ddns: { externallyManaged: true } as never });
82
+
83
+ const s = store.load();
84
+ expect(s.ddns.providerId).toBe('duckdns');
85
+ expect(s.ddns.hostname).toBe('foo.duckdns.org');
86
+ expect(s.ddns.externallyManaged).toBe(true);
87
+ expect(s.ddns.intervalMs).toBe(60_000);
88
+ });
89
+
90
+ it('setForward sets, clears and removes per-port entries', () => {
91
+ const store = new NatStore(tmpRoot);
92
+ expect(store.setForward('a', { tcp: 40000, ws: 40001 }).forwards).toEqual({ a: { tcp: 40000, ws: 40001 } });
93
+ expect(store.setForward('a', { tcp: null }).forwards).toEqual({ a: { ws: 40001 } });
94
+ expect(store.setForward('a', { ws: 40002 }).forwards).toEqual({ a: { ws: 40002 } });
95
+ expect(store.setForward('a', { ws: null }).forwards).toEqual({});
96
+ expect(new NatStore(tmpRoot).load().forwards).toEqual({});
97
+ });
98
+
99
+ it('deleteForward drops one node and leaves the rest', () => {
100
+ const store = new NatStore(tmpRoot);
101
+ store.setForward('a', { tcp: 40000 });
102
+ store.setForward('b', { ws: 40001 });
103
+ expect(store.deleteForward('a').forwards).toEqual({ b: { ws: 40001 } });
104
+ expect(store.deleteForward('missing').forwards).toEqual({ b: { ws: 40001 } });
105
+ });
106
+
107
+ it('throws on malformed JSON rather than silently wiping', () => {
108
+ writeFileSync(join(tmpRoot, 'nat.json'), 'not json', 'utf8');
109
+ const store = new NatStore(tmpRoot);
110
+ expect(() => store.load()).toThrow(NatError);
111
+ });
112
+
113
+ it('rejects an out-of-range or non-integer forwarded port', () => {
114
+ const store = new NatStore(tmpRoot);
115
+ expect(() => store.setForward('a', { tcp: 0 })).toThrow(NatError);
116
+ expect(() => store.setForward('a', { ws: 70000 })).toThrow(NatError);
117
+ expect(() => store.setForward('a', { tcp: 1.5 })).toThrow(NatError);
118
+ });
119
+
120
+ it('rejects too-small interval', () => {
121
+ const store = new NatStore(tmpRoot);
122
+ expect(() => store.update({ ddns: { intervalMs: 500 } as never })).toThrow(NatError);
123
+ });
124
+
125
+ it('rejects empty string providerId/hostname', () => {
126
+ const store = new NatStore(tmpRoot);
127
+ expect(() => store.update({ ddns: { providerId: '' } as never })).toThrow(NatError);
128
+ expect(() => store.update({ ddns: { hostname: '' } as never })).toThrow(NatError);
129
+ });
130
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { NetworkInterfaceInfo } from 'node:os';
3
+
4
+ import { effectiveNetworkMode, publicInterfaceAddress } from '../network-mode.js';
5
+
6
+ /** One IPv4 interface entry, as `os.networkInterfaces()` reports it. */
7
+ function v4(address: string, internal = false): NetworkInterfaceInfo {
8
+ return { address, netmask: '255.255.255.0', family: 'IPv4', mac: '00:00:00:00:00:00', internal, cidr: `${address}/24` };
9
+ }
10
+
11
+ describe('publicInterfaceAddress', () => {
12
+ it('finds a VPS\'s public address among private, Docker and loopback interfaces', () => {
13
+ expect(publicInterfaceAddress({
14
+ lo: [v4('127.0.0.1', true)],
15
+ docker0: [v4('172.17.0.1')],
16
+ eth1: [v4('10.10.0.5')],
17
+ eth0: [v4('107.170.255.209')],
18
+ })).toBe('107.170.255.209');
19
+ });
20
+
21
+ it('is null behind a router: private, carrier-grade and link-local addresses only', () => {
22
+ expect(publicInterfaceAddress({
23
+ eno1: [v4('192.168.2.27')],
24
+ tailscale0: [v4('100.101.102.103')],
25
+ bk0: [v4('10.9.9.1')],
26
+ wlan0: [v4('169.254.10.10')],
27
+ })).toBeNull();
28
+ });
29
+ });
30
+
31
+ describe('effectiveNetworkMode', () => {
32
+ it('auto follows the interfaces; an explicit setting wins', () => {
33
+ expect(effectiveNetworkMode('auto', '203.0.113.7')).toBe('public');
34
+ expect(effectiveNetworkMode('auto', null)).toBe('lan');
35
+ expect(effectiveNetworkMode('lan', '203.0.113.7')).toBe('lan');
36
+ expect(effectiveNetworkMode('public', null)).toBe('public');
37
+ });
38
+ });
package/src/nat/index.ts CHANGED
@@ -1,87 +1,91 @@
1
- /**
2
- * NAT / DDNS substrate for cadre-host.
3
- *
4
- * See ./nat-service.ts for the top-level orchestrator and `docs/cadre-host.md`
5
- * for integration notes (the long-running process owner is `cadre-host start`).
6
- */
7
-
8
- export {
9
- NatService,
10
- createNatHandlers,
11
- NAT_UNMAP_GRACE_MS,
12
- NAT_RECONCILE_INTERVAL_MS,
13
- NAT_IP_REDETECT_INTERVAL_MS,
14
- } from './nat-service.js';
15
- export type { NatServiceOptions, NatNodeSource, NatChangeListener } from './nat-service.js';
16
- export { NAT_ADDRESS_RESTART_MIN_INTERVAL_MS } from './address-watch.js';
17
- export type { NodeAddressesStaleListener } from './address-watch.js';
18
- export { NatStore } from './nat-store.js';
19
- export {
20
- NatError,
21
- } from './types.js';
22
- export type {
23
- NatStatusSnapshot,
24
- NatGatewayStatus,
25
- NatDdnsStatus,
26
- NatSettingsFile,
27
- NatDdnsSettings,
28
- NatHandlers,
29
- NatErrorCode,
30
- DirectReachability,
31
- NodeReachability,
32
- NodeVerdict,
33
- PortKind,
34
- PortRoute,
35
- PortRouteSource,
36
- ManualForward,
37
- ManualForwardPatch,
38
- DdnsProviderInfo,
39
- DdnsConfigField,
40
- } from './types.js';
41
- export {
42
- ExternalIpDetector,
43
- isPlausibleIp,
44
- } from './external-ip.js';
45
- export type {
46
- ExternalIpResult,
47
- RouterIpProbe,
48
- ExternalIpDetectorOptions,
49
- } from './external-ip.js';
50
- export {
51
- UpnpPortMapper,
52
- pickLanAddress,
53
- DEFAULT_LEASE_TTL_MS,
54
- DEFAULT_REFRESH_MS,
55
- GATEWAY_DISCOVERY_TIMEOUT_MS,
56
- } from './port-mapper.js';
57
- export type {
58
- PortMapper,
59
- GatewayInfo,
60
- PortMapRequest,
61
- PortMapResult,
62
- } from './port-mapper.js';
63
- export { evaluateNodeReachability, evaluateHostReachability } from './reachability.js';
64
- export type { NodeVerdictInput, NodeVerdictResult } from './reachability.js';
65
- export { buildPublicAddresses, isPublicIpv4 } from './address-resolver.js';
66
- export type { PublicAddressInput } from './address-resolver.js';
67
- export {
68
- BUILTIN_PROVIDERS,
69
- getProvider,
70
- listProviders,
71
- duckDnsProvider,
72
- DdnsUpdater,
73
- } from './ddns/index.js';
74
- export type {
75
- DdnsProvider,
76
- DdnsProviderDeps,
77
- DdnsUpdaterOptions,
78
- DdnsUpdateOutcome,
79
- } from './ddns/index.js';
80
- export {
81
- KeytarSecretsStore,
82
- FileSecretsStore,
83
- createSecretsStore,
84
- ddnsAccount,
85
- KEYTAR_SERVICE,
86
- } from './secrets/index.js';
87
- export type { SecretsStore, KeytarLike } from './secrets/index.js';
1
+ /**
2
+ * NAT / DDNS substrate for cadre-host.
3
+ *
4
+ * See ./nat-service.ts for the top-level orchestrator and `docs/cadre-host.md`
5
+ * for integration notes (the long-running process owner is `cadre-host start`).
6
+ */
7
+
8
+ export {
9
+ NatService,
10
+ createNatHandlers,
11
+ NAT_UNMAP_GRACE_MS,
12
+ NAT_RECONCILE_INTERVAL_MS,
13
+ NAT_IP_REDETECT_INTERVAL_MS,
14
+ } from './nat-service.js';
15
+ export type { NatServiceOptions, NatNodeSource, NatChangeListener } from './nat-service.js';
16
+ export { NAT_ADDRESS_RESTART_MIN_INTERVAL_MS } from './address-watch.js';
17
+ export type { NodeAddressesStaleListener } from './address-watch.js';
18
+ export { NatStore } from './nat-store.js';
19
+ export {
20
+ NatError,
21
+ } from './types.js';
22
+ export type {
23
+ NatStatusSnapshot,
24
+ NatGatewayStatus,
25
+ NatDdnsStatus,
26
+ NatNetworkStatus,
27
+ NetworkMode,
28
+ NetworkSetting,
29
+ NatSettingsFile,
30
+ NatDdnsSettings,
31
+ NatHandlers,
32
+ NatErrorCode,
33
+ DirectReachability,
34
+ NodeReachability,
35
+ NodeVerdict,
36
+ PortKind,
37
+ PortRoute,
38
+ PortRouteSource,
39
+ ManualForward,
40
+ ManualForwardPatch,
41
+ DdnsProviderInfo,
42
+ DdnsConfigField,
43
+ } from './types.js';
44
+ export {
45
+ ExternalIpDetector,
46
+ isPlausibleIp,
47
+ } from './external-ip.js';
48
+ export type {
49
+ ExternalIpResult,
50
+ RouterIpProbe,
51
+ ExternalIpDetectorOptions,
52
+ } from './external-ip.js';
53
+ export {
54
+ UpnpPortMapper,
55
+ pickLanAddress,
56
+ DEFAULT_LEASE_TTL_MS,
57
+ DEFAULT_REFRESH_MS,
58
+ GATEWAY_DISCOVERY_TIMEOUT_MS,
59
+ } from './port-mapper.js';
60
+ export type {
61
+ PortMapper,
62
+ GatewayInfo,
63
+ PortMapRequest,
64
+ PortMapResult,
65
+ } from './port-mapper.js';
66
+ export { evaluateNodeReachability, evaluateHostReachability } from './reachability.js';
67
+ export type { NodeVerdictInput, NodeVerdictResult } from './reachability.js';
68
+ export { buildPublicAddresses, isPublicIpv4 } from './address-resolver.js';
69
+ export { publicInterfaceAddress, effectiveNetworkMode } from './network-mode.js';
70
+ export type { PublicAddressInput } from './address-resolver.js';
71
+ export {
72
+ BUILTIN_PROVIDERS,
73
+ getProvider,
74
+ listProviders,
75
+ duckDnsProvider,
76
+ DdnsUpdater,
77
+ } from './ddns/index.js';
78
+ export type {
79
+ DdnsProvider,
80
+ DdnsProviderDeps,
81
+ DdnsUpdaterOptions,
82
+ DdnsUpdateOutcome,
83
+ } from './ddns/index.js';
84
+ export {
85
+ KeytarSecretsStore,
86
+ FileSecretsStore,
87
+ createSecretsStore,
88
+ ddnsAccount,
89
+ KEYTAR_SERVICE,
90
+ } from './secrets/index.js';
91
+ export type { SecretsStore, KeytarLike } from './secrets/index.js';