@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
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Identity persistence for cadre-host.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* We persist the protobuf form (not raw bytes) because that's the format
|
|
10
|
-
* libp2p's `privateKeyFromProtobuf` consumes directly and it preserves the
|
|
11
|
-
* key type tag — future migrations to a different key algorithm don't
|
|
12
|
-
* require a schema change here. Raw bytes carry no type tag AND no way to tell
|
|
13
|
-
* a damaged file from a valid one: `privateKeyFromRaw` accepts any 64 bytes as
|
|
14
|
-
* an Ed25519 key without checking that the public half matches the private, so
|
|
15
|
-
* a truncated file would load as a *different* working identity.
|
|
16
|
-
*
|
|
17
|
-
* This is the repo-wide identity file format, not a cadre-host-local choice:
|
|
18
|
-
* `cadre enroll create` writes exactly these bytes, and `cadre-cli`'s
|
|
19
|
-
* `identity.keyFile` accepts nothing else (`config/loader.ts` → `loadIdentityKey`).
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
-
import { dirname } from 'node:path';
|
|
24
|
-
import {
|
|
25
|
-
generateKeyPair,
|
|
26
|
-
privateKeyFromProtobuf,
|
|
27
|
-
privateKeyToProtobuf,
|
|
28
|
-
} from '@libp2p/crypto/keys';
|
|
29
|
-
import { peerIdFromPrivateKey } from '@libp2p/peer-id';
|
|
30
|
-
import type { PrivateKey } from '@libp2p/interface';
|
|
31
|
-
|
|
32
|
-
export interface IdentityRecord {
|
|
33
|
-
privateKey: PrivateKey;
|
|
34
|
-
/** Stringified libp2p peer ID (`12D3Koo…`). */
|
|
35
|
-
peerId: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Generate a fresh Ed25519 identity and persist it. Returns the in-memory record. */
|
|
39
|
-
export async function generateIdentity(filePath: string): Promise<IdentityRecord> {
|
|
40
|
-
const privateKey = await generateKeyPair('Ed25519');
|
|
41
|
-
const bytes = privateKeyToProtobuf(privateKey);
|
|
42
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
43
|
-
writeFileSync(filePath, bytes);
|
|
44
|
-
// 0600 — owner read/write only. No-op on Windows.
|
|
45
|
-
if (process.platform !== 'win32') {
|
|
46
|
-
chmodSync(filePath, 0o600);
|
|
47
|
-
}
|
|
48
|
-
return { privateKey, peerId: peerIdFromPrivateKey(privateKey).toString() };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Load an existing identity file. Throws if it doesn't decode. */
|
|
52
|
-
export function loadIdentity(filePath: string): IdentityRecord {
|
|
53
|
-
const bytes = readFileSync(filePath);
|
|
54
|
-
const privateKey = privateKeyFromProtobuf(new Uint8Array(bytes));
|
|
55
|
-
return { privateKey, peerId: peerIdFromPrivateKey(privateKey).toString() };
|
|
56
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Identity persistence for the nodes cadre-host runs.
|
|
3
|
+
*
|
|
4
|
+
* Each managed node's libp2p peer identity is an Ed25519 keypair generated on
|
|
5
|
+
* its first spawn (`node-identity.ts`). The protobuf-encoded
|
|
6
|
+
* private key is written with mode 0600 on POSIX and loaded with
|
|
7
|
+
* `privateKeyFromProtobuf`.
|
|
8
|
+
*
|
|
9
|
+
* We persist the protobuf form (not raw bytes) because that's the format
|
|
10
|
+
* libp2p's `privateKeyFromProtobuf` consumes directly and it preserves the
|
|
11
|
+
* key type tag — future migrations to a different key algorithm don't
|
|
12
|
+
* require a schema change here. Raw bytes carry no type tag AND no way to tell
|
|
13
|
+
* a damaged file from a valid one: `privateKeyFromRaw` accepts any 64 bytes as
|
|
14
|
+
* an Ed25519 key without checking that the public half matches the private, so
|
|
15
|
+
* a truncated file would load as a *different* working identity.
|
|
16
|
+
*
|
|
17
|
+
* This is the repo-wide identity file format, not a cadre-host-local choice:
|
|
18
|
+
* `cadre enroll create` writes exactly these bytes, and `cadre-cli`'s
|
|
19
|
+
* `identity.keyFile` accepts nothing else (`config/loader.ts` → `loadIdentityKey`).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { dirname } from 'node:path';
|
|
24
|
+
import {
|
|
25
|
+
generateKeyPair,
|
|
26
|
+
privateKeyFromProtobuf,
|
|
27
|
+
privateKeyToProtobuf,
|
|
28
|
+
} from '@libp2p/crypto/keys';
|
|
29
|
+
import { peerIdFromPrivateKey } from '@libp2p/peer-id';
|
|
30
|
+
import type { PrivateKey } from '@libp2p/interface';
|
|
31
|
+
|
|
32
|
+
export interface IdentityRecord {
|
|
33
|
+
privateKey: PrivateKey;
|
|
34
|
+
/** Stringified libp2p peer ID (`12D3Koo…`). */
|
|
35
|
+
peerId: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Generate a fresh Ed25519 identity and persist it. Returns the in-memory record. */
|
|
39
|
+
export async function generateIdentity(filePath: string): Promise<IdentityRecord> {
|
|
40
|
+
const privateKey = await generateKeyPair('Ed25519');
|
|
41
|
+
const bytes = privateKeyToProtobuf(privateKey);
|
|
42
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
43
|
+
writeFileSync(filePath, bytes);
|
|
44
|
+
// 0600 — owner read/write only. No-op on Windows.
|
|
45
|
+
if (process.platform !== 'win32') {
|
|
46
|
+
chmodSync(filePath, 0o600);
|
|
47
|
+
}
|
|
48
|
+
return { privateKey, peerId: peerIdFromPrivateKey(privateKey).toString() };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Load an existing identity file. Throws if it doesn't decode. */
|
|
52
|
+
export function loadIdentity(filePath: string): IdentityRecord {
|
|
53
|
+
const bytes = readFileSync(filePath);
|
|
54
|
+
const privateKey = privateKeyFromProtobuf(new Uint8Array(bytes));
|
|
55
|
+
return { privateKey, peerId: peerIdFromPrivateKey(privateKey).toString() };
|
|
56
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
export { HostProcessOrchestrator
|
|
2
|
-
export type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
ManagedNodeInfo,
|
|
6
|
-
NodeStateListener,
|
|
7
|
-
NodePorts,
|
|
8
|
-
|
|
9
|
-
} from './
|
|
10
|
-
export type { PersistedHandle } from './state-store.js';
|
|
1
|
+
export { HostProcessOrchestrator } from './host-process-orchestrator.js';
|
|
2
|
+
export type {
|
|
3
|
+
HostProcessConfig,
|
|
4
|
+
HostedSpawnRequest,
|
|
5
|
+
ManagedNodeInfo,
|
|
6
|
+
NodeStateListener,
|
|
7
|
+
NodePorts,
|
|
8
|
+
} from './types.js';
|
|
9
|
+
export type { PersistedHandle } from './state-store.js';
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-node identity keys for orchestrator-managed children.
|
|
3
|
-
*
|
|
4
|
-
* A
|
|
5
|
-
* and
|
|
6
|
-
* identity, so a node that generated a fresh libp2p keypair per process
|
|
7
|
-
* become a stranger to the cadre that
|
|
8
|
-
* needs a stable on-disk home for its node-local stores — `cadre-cli start`
|
|
9
|
-
* opens `FileBootstrapPeerStore` / `FileTrustedOwnerStore` /
|
|
10
|
-
* `FileStrandNetworkStateStore` in the node's state directory, which defaults to
|
|
11
|
-
* the directory holding the config file the orchestrator writes into the workdir.
|
|
12
|
-
*
|
|
13
|
-
* So every managed node gets its own `identity.key` inside its workdir, written
|
|
14
|
-
* once and reused thereafter. The workdir is removed
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { existsSync } from 'node:fs';
|
|
19
|
-
import { join } from 'node:path';
|
|
20
|
-
|
|
21
|
-
import { generateIdentity, loadIdentity, type IdentityRecord } from '
|
|
22
|
-
|
|
23
|
-
const IDENTITY_FILE = 'identity.key';
|
|
24
|
-
|
|
25
|
-
/** Path to a managed node's own protobuf identity key inside its workdir. */
|
|
26
|
-
export function nodeIdentityPath(workdir: string): string {
|
|
27
|
-
return join(workdir, IDENTITY_FILE);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Ensure `<workdir>/identity.key` exists, generating an Ed25519 protobuf key on
|
|
32
|
-
* first call and REUSING it on every later call. Reuse is the load-bearing
|
|
33
|
-
* property: a re-spawn of the same container must land on the same peer id.
|
|
34
|
-
*/
|
|
35
|
-
export async function ensureNodeIdentity(workdir: string): Promise<{ path: string; peerId: string }> {
|
|
36
|
-
const path = nodeIdentityPath(workdir);
|
|
37
|
-
const record: IdentityRecord = existsSync(path)
|
|
38
|
-
? loadIdentity(path)
|
|
39
|
-
: await generateIdentity(path);
|
|
40
|
-
return { path, peerId: record.peerId };
|
|
41
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Per-node identity keys for orchestrator-managed children.
|
|
3
|
+
*
|
|
4
|
+
* A hosted node joins the cadre whose owner claimed it: the claim named its peer
|
|
5
|
+
* id, and the owner's phone dials the addresses keyed to that peer id. Both bind
|
|
6
|
+
* to the identity, so a node that generated a fresh libp2p keypair per process
|
|
7
|
+
* would become a stranger to the cadre that claimed it on every restart. It also
|
|
8
|
+
* needs a stable on-disk home for its node-local stores — `cadre-cli start`
|
|
9
|
+
* opens `FileBootstrapPeerStore` / `FileTrustedOwnerStore` /
|
|
10
|
+
* `FileStrandNetworkStateStore` in the node's state directory, which defaults to
|
|
11
|
+
* the directory holding the config file the orchestrator writes into the workdir.
|
|
12
|
+
*
|
|
13
|
+
* So every managed node gets its own `identity.key` inside its workdir, written
|
|
14
|
+
* once and reused thereafter. The workdir is removed with the node
|
|
15
|
+
* (`removeContainer`), so nothing the node persisted outlives it.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
|
|
21
|
+
import { generateIdentity, loadIdentity, type IdentityRecord } from './identity-file.js';
|
|
22
|
+
|
|
23
|
+
const IDENTITY_FILE = 'identity.key';
|
|
24
|
+
|
|
25
|
+
/** Path to a managed node's own protobuf identity key inside its workdir. */
|
|
26
|
+
export function nodeIdentityPath(workdir: string): string {
|
|
27
|
+
return join(workdir, IDENTITY_FILE);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Ensure `<workdir>/identity.key` exists, generating an Ed25519 protobuf key on
|
|
32
|
+
* first call and REUSING it on every later call. Reuse is the load-bearing
|
|
33
|
+
* property: a re-spawn of the same container must land on the same peer id.
|
|
34
|
+
*/
|
|
35
|
+
export async function ensureNodeIdentity(workdir: string): Promise<{ path: string; peerId: string }> {
|
|
36
|
+
const path = nodeIdentityPath(workdir);
|
|
37
|
+
const record: IdentityRecord = existsSync(path)
|
|
38
|
+
? loadIdentity(path)
|
|
39
|
+
: await generateIdentity(path);
|
|
40
|
+
return { path, peerId: record.peerId };
|
|
41
|
+
}
|
|
@@ -1,73 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
allocatePortSet,
|
|
3
|
-
releasePortSet,
|
|
4
|
-
reservePortSet,
|
|
5
|
-
type PortAllocator,
|
|
6
|
-
} from '@serfab/cadre-provider';
|
|
7
|
-
|
|
8
|
-
import type { NodePorts } from './types.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Allocation order for a node's port set.
|
|
12
|
-
* {@link
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
overrides
|
|
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
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return reused;
|
|
73
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
allocatePortSet,
|
|
3
|
+
releasePortSet,
|
|
4
|
+
reservePortSet,
|
|
5
|
+
type PortAllocator,
|
|
6
|
+
} from '@serfab/cadre-provider';
|
|
7
|
+
|
|
8
|
+
import type { NodePorts } from './types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Allocation order for a node's port set. A node already running keeps its
|
|
12
|
+
* ports whatever this order says ({@link reusedNodePorts}); the order decides
|
|
13
|
+
* only where a fresh allocation lands.
|
|
14
|
+
*/
|
|
15
|
+
const NODE_PORT_KEYS = ['health', 'metrics', 'p2p', 'ws'] as const satisfies readonly (keyof NodePorts)[];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Allocate a full NodePorts set atomically, in {@link NODE_PORT_KEYS} order.
|
|
19
|
+
* `overrides` supply a port instead of allocating one (a re-spawn's previous
|
|
20
|
+
* ports, see {@link reusedNodePorts}). Overrides are reserved before anything is allocated
|
|
21
|
+
* and are trusted, not checked — see `allocatePortSet`.
|
|
22
|
+
*/
|
|
23
|
+
export function allocateNodePorts(
|
|
24
|
+
allocator: PortAllocator,
|
|
25
|
+
overrides?: Partial<NodePorts>,
|
|
26
|
+
): NodePorts {
|
|
27
|
+
return allocatePortSet(allocator, NODE_PORT_KEYS, overrides);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Hold a node's whole port set against `allocator` — the inverse of
|
|
32
|
+
* {@link releaseNodePorts}. A key the set lacks is skipped (see `markUsed`).
|
|
33
|
+
*/
|
|
34
|
+
export function reserveNodePorts(allocator: PortAllocator, ports: NodePorts): void {
|
|
35
|
+
reservePortSet(allocator, NODE_PORT_KEYS, ports);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Give a node's whole port set back to `allocator`. A key the set lacks releases
|
|
40
|
+
* nothing: deleting a value the used-set never held is a no-op.
|
|
41
|
+
*/
|
|
42
|
+
export function releaseNodePorts(allocator: PortAllocator, ports: NodePorts): void {
|
|
43
|
+
releasePortSet(allocator, NODE_PORT_KEYS, ports);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The ports a re-spawn comes back on: the set the dropped handle held, as
|
|
48
|
+
* {@link allocateNodePorts} overrides. `{}` when nothing was dropped (a first
|
|
49
|
+
* spawn, or a handle lost along with `state.json`), so every port is allocated
|
|
50
|
+
* fresh.
|
|
51
|
+
*
|
|
52
|
+
* Why a re-spawn must not simply take the lowest free ports: an owner with no
|
|
53
|
+
* address of its own (a phone) reaches a hosted node only by dialing the address
|
|
54
|
+
* it scanned, so a node that comes back on a different port is one that owner
|
|
55
|
+
* can no longer find.
|
|
56
|
+
*
|
|
57
|
+
* A key the dropped handle lacks — `ws` on a handle persisted by an older build —
|
|
58
|
+
* is left out, and so allocated fresh. When more than one handle was dropped (only
|
|
59
|
+
* possible from a `state.json` that already held duplicates), the last one stored
|
|
60
|
+
* wins and the others' ports stay released.
|
|
61
|
+
*/
|
|
62
|
+
export function reusedNodePorts(dropped: readonly { ports: NodePorts }[]): Partial<NodePorts> {
|
|
63
|
+
const reused: Partial<NodePorts> = {};
|
|
64
|
+
const previous = dropped[dropped.length - 1]?.ports;
|
|
65
|
+
if (!previous) return reused;
|
|
66
|
+
for (const key of NODE_PORT_KEYS) {
|
|
67
|
+
const port = previous[key];
|
|
68
|
+
if (Number.isInteger(port)) reused[key] = port;
|
|
69
|
+
}
|
|
70
|
+
return reused;
|
|
71
|
+
}
|
|
@@ -1,90 +1,76 @@
|
|
|
1
|
-
import { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import debug from 'debug';
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
const log = debug('cadre:host:state-store');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Persisted record for a single managed child. The orchestrator mirrors its
|
|
11
|
-
* in-memory map to disk so that cadre-host can re-attach to surviving
|
|
12
|
-
* children after a restart.
|
|
13
|
-
*/
|
|
14
|
-
export interface PersistedHandle {
|
|
15
|
-
containerId: string; // provider-assigned ID, passed in createContainer
|
|
16
|
-
dockerId: string; // opaque token returned to callers (encoded "pid:token")
|
|
17
|
-
pid: number;
|
|
18
|
-
startupToken: string;
|
|
19
|
-
workdir: string;
|
|
20
|
-
ports: NodePorts;
|
|
21
|
-
spawnedAt: string; // ISO timestamp
|
|
22
|
-
partyId: string;
|
|
23
|
-
profile: 'storage' | 'transaction';
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface PersistedState {
|
|
29
|
-
version: 1;
|
|
30
|
-
handles: PersistedHandle[];
|
|
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
|
-
...(state.ownerConfig ? { ownerConfig: state.ownerConfig } : {}),
|
|
78
|
-
},
|
|
79
|
-
null,
|
|
80
|
-
2,
|
|
81
|
-
);
|
|
82
|
-
const tmp = `${this.path}.tmp`;
|
|
83
|
-
writeFileSync(tmp, payload, { encoding: 'utf8' });
|
|
84
|
-
renameSync(tmp, this.path);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
filePath(): string {
|
|
88
|
-
return this.path;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
|
|
5
|
+
import type { NodePorts } from './types.js';
|
|
6
|
+
|
|
7
|
+
const log = debug('cadre:host:state-store');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Persisted record for a single managed child. The orchestrator mirrors its
|
|
11
|
+
* in-memory map to disk so that cadre-host can re-attach to surviving
|
|
12
|
+
* children after a restart.
|
|
13
|
+
*/
|
|
14
|
+
export interface PersistedHandle {
|
|
15
|
+
containerId: string; // provider-assigned ID, passed in createContainer
|
|
16
|
+
dockerId: string; // opaque token returned to callers (encoded "pid:token")
|
|
17
|
+
pid: number;
|
|
18
|
+
startupToken: string;
|
|
19
|
+
workdir: string;
|
|
20
|
+
ports: NodePorts;
|
|
21
|
+
spawnedAt: string; // ISO timestamp
|
|
22
|
+
partyId: string;
|
|
23
|
+
profile: 'storage' | 'transaction';
|
|
24
|
+
/** The public addresses the child was started announcing. Absent in a file written before they existed. */
|
|
25
|
+
announcedAddrs?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PersistedState {
|
|
29
|
+
version: 1;
|
|
30
|
+
handles: PersistedHandle[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const STATE_VERSION = 1;
|
|
34
|
+
|
|
35
|
+
/** JSON state file at `<rootDir>/state.json`, written atomically. */
|
|
36
|
+
export class StateStore {
|
|
37
|
+
private readonly path: string;
|
|
38
|
+
|
|
39
|
+
constructor(rootDir: string) {
|
|
40
|
+
mkdirSync(rootDir, { recursive: true });
|
|
41
|
+
this.path = join(rootDir, 'state.json');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
load(): PersistedState {
|
|
45
|
+
if (!existsSync(this.path)) {
|
|
46
|
+
return { version: STATE_VERSION, handles: [] };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const raw = readFileSync(this.path, 'utf8');
|
|
50
|
+
const parsed = JSON.parse(raw) as PersistedState;
|
|
51
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.handles)) {
|
|
52
|
+
log('state file at %s has unexpected shape; treating as empty', this.path);
|
|
53
|
+
return { version: STATE_VERSION, handles: [] };
|
|
54
|
+
}
|
|
55
|
+
return { version: STATE_VERSION, handles: parsed.handles };
|
|
56
|
+
} catch (err) {
|
|
57
|
+
// A corrupt state file is operationally significant — surviving children
|
|
58
|
+
// may now be leaked to the OS without an in-memory handle. Make the
|
|
59
|
+
// failure visible rather than silently returning empty.
|
|
60
|
+
log('failed to load state file at %s: %o', this.path, err);
|
|
61
|
+
return { version: STATE_VERSION, handles: [] };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
save(state: PersistedState): void {
|
|
66
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
67
|
+
const payload = JSON.stringify({ version: STATE_VERSION, handles: state.handles }, null, 2);
|
|
68
|
+
const tmp = `${this.path}.tmp`;
|
|
69
|
+
writeFileSync(tmp, payload, { encoding: 'utf8' });
|
|
70
|
+
renameSync(tmp, this.path);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
filePath(): string {
|
|
74
|
+
return this.path;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ChildProcess } from 'node:child_process';
|
|
6
6
|
import type { PushCredentials } from '@serfab/cadre-core';
|
|
7
|
+
import type { OrchestratorCreateRequest } from '@serfab/cadre-provider';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What `HostProcessOrchestrator.createContainer` takes: the shared create request plus how
|
|
11
|
+
* the node gets into its cadre — the claim secret of a node started waiting to be claimed
|
|
12
|
+
* (`cadre-cli start` with `CADRE_CLAIM_SECRET`), or the cadre invitation it redeems
|
|
13
|
+
* (`CADRE_INVITATION`). At most one of the two. Each is a credential and reaches the child
|
|
14
|
+
* through its environment only: never as an argument (an argument shows in the process
|
|
15
|
+
* list), never in `state.json`, never in a log line.
|
|
16
|
+
*/
|
|
17
|
+
export type HostedSpawnRequest = OrchestratorCreateRequest & {
|
|
18
|
+
/** The node's one-time claim secret, base64url. Absent, the child starts with no claim. */
|
|
19
|
+
claimSecret?: string;
|
|
20
|
+
/** An encoded cadre invitation the child redeems right after it starts. */
|
|
21
|
+
invitation?: string;
|
|
22
|
+
};
|
|
7
23
|
|
|
8
24
|
/** User-facing configuration for `HostProcessOrchestrator`. */
|
|
9
25
|
export interface HostProcessConfig {
|
|
@@ -33,6 +49,15 @@ export interface HostProcessConfig {
|
|
|
33
49
|
* resolver itself is responsible for rejecting a partial credential set.
|
|
34
50
|
*/
|
|
35
51
|
pushResolver?: PushCredentialsResolver;
|
|
52
|
+
/**
|
|
53
|
+
* The public multiaddrs a node about to spawn should announce beside its listen
|
|
54
|
+
* addresses (`CADRE_APPEND_ANNOUNCE_ADDRS`), given its container id and the ports just
|
|
55
|
+
* allocated to it. Synchronous on purpose: both spawn paths allocate ports inside a
|
|
56
|
+
* window that must contain no `await` (see `restoreDroppedHandles`), and the addresses
|
|
57
|
+
* depend on those ports. A throw is logged and the node starts announcing nothing extra.
|
|
58
|
+
* Absent, no node announces anything beyond what libp2p reports.
|
|
59
|
+
*/
|
|
60
|
+
announceAddrs?: (containerId: string, ports: NodePorts) => string[];
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
/**
|
|
@@ -43,7 +68,7 @@ export interface HostProcessConfig {
|
|
|
43
68
|
export type PushCredentialsResolver = () => Promise<PushCredentials | undefined>;
|
|
44
69
|
|
|
45
70
|
/**
|
|
46
|
-
* Per-child allocated ports.
|
|
71
|
+
* Per-child allocated ports.
|
|
47
72
|
*
|
|
48
73
|
* A handle read back from a `state.json` written by an older build lacks every key
|
|
49
74
|
* added since, whatever this type says — the node-set helpers in `port-allocator.ts`
|
|
@@ -54,8 +79,6 @@ export interface NodePorts {
|
|
|
54
79
|
metrics: number;
|
|
55
80
|
/** libp2p TCP listener. */
|
|
56
81
|
p2p: number;
|
|
57
|
-
/** Loopback admin-channel port. Bound only by the owner node. */
|
|
58
|
-
admin: number;
|
|
59
82
|
/**
|
|
60
83
|
* libp2p WebSocket listener. The one a phone dials: a phone's node has no TCP
|
|
61
84
|
* transport, so the `p2p` port is unreachable to it.
|
|
@@ -74,8 +97,8 @@ export interface Handle {
|
|
|
74
97
|
spawnedAt: string;
|
|
75
98
|
partyId: string;
|
|
76
99
|
profile: 'storage' | 'transaction';
|
|
77
|
-
/**
|
|
78
|
-
|
|
100
|
+
/** The public addresses the child was started announcing ({@link HostProcessConfig.announceAddrs}). */
|
|
101
|
+
announcedAddrs: string[];
|
|
79
102
|
/** Live ChildProcess reference; absent after re-attach via init(). */
|
|
80
103
|
child?: ChildProcess;
|
|
81
104
|
/** Marked false when init() finds the PID dead or token mismatched. */
|
|
@@ -95,24 +118,12 @@ export interface ManagedNodeInfo {
|
|
|
95
118
|
spawnedAt: string;
|
|
96
119
|
workdir: string;
|
|
97
120
|
ports: NodePorts;
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* the handles so an orchestrator restart can re-spawn the owner node from
|
|
105
|
-
* `/api/nodes/:id/{start,restart}` without the caller re-supplying them.
|
|
106
|
-
*/
|
|
107
|
-
export interface OwnerSpawnConfig {
|
|
108
|
-
/** Absolute path to the host's libp2p protobuf identity (`identity.key`). */
|
|
109
|
-
identityPath: string;
|
|
110
|
-
/** Control-network partyId (the host's installId). */
|
|
111
|
-
partyId: string;
|
|
112
|
-
/** External libp2p port — mapped to the child's listen address. */
|
|
113
|
-
libp2pPort: number;
|
|
114
|
-
/** Node profile. Defaults to `storage` (the household node holds strands). */
|
|
115
|
-
profile?: 'storage' | 'transaction';
|
|
121
|
+
/**
|
|
122
|
+
* The public addresses the node was started announcing. It learns them only at start,
|
|
123
|
+
* so `NatService` compares this with the node's current public addresses and asks for
|
|
124
|
+
* a restart when they differ.
|
|
125
|
+
*/
|
|
126
|
+
announcedAddrs: string[];
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
/**
|