@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
|
@@ -47,6 +47,18 @@ function scrubbedParentEnv() {
|
|
|
47
47
|
}
|
|
48
48
|
return env;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* The env carrying how the child gets into its cadre, and nowhere else (see
|
|
52
|
+
* `HostedSpawnRequest`): `CADRE_CLAIM_SECRET` makes `cadre-cli start` wait to be
|
|
53
|
+
* claimed, `CADRE_INVITATION` makes it redeem the invitation once it is up.
|
|
54
|
+
*/
|
|
55
|
+
function joinEnv(request) {
|
|
56
|
+
if (request.claimSecret)
|
|
57
|
+
return { CADRE_CLAIM_SECRET: request.claimSecret };
|
|
58
|
+
if (request.invitation)
|
|
59
|
+
return { CADRE_INVITATION: request.invitation };
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
50
62
|
/**
|
|
51
63
|
* The libp2p listen addresses a managed child binds — its `CADRE_LISTEN_ADDRS`, one
|
|
52
64
|
* entry per element: TCP on the `p2p` port and WebSocket on the `ws` port, both on
|
|
@@ -65,22 +77,17 @@ export function childListenAddrs(ports) {
|
|
|
65
77
|
/**
|
|
66
78
|
* Every TCP port a managed child binds, on the address it binds it to: health and
|
|
67
79
|
* metrics (`cadre-cli`'s `HealthServer`) and the libp2p TCP and WebSocket listeners
|
|
68
|
-
* ({@link childListenAddrs}) on every interface
|
|
69
|
-
*
|
|
70
|
-
* set lacks (a handle persisted by an older build) is skipped.
|
|
80
|
+
* ({@link childListenAddrs}), all on every interface. A key the set lacks (a handle
|
|
81
|
+
* persisted by an older build) is skipped.
|
|
71
82
|
*/
|
|
72
|
-
function childPortBindings(ports
|
|
83
|
+
function childPortBindings(ports) {
|
|
73
84
|
const bindings = [];
|
|
74
85
|
for (const port of [ports.health, ports.metrics, ports.p2p, ports.ws]) {
|
|
75
86
|
if (port !== undefined)
|
|
76
87
|
bindings.push({ port, host: '0.0.0.0' });
|
|
77
88
|
}
|
|
78
|
-
if (owner && ports.admin !== undefined)
|
|
79
|
-
bindings.push({ port: ports.admin, host: '127.0.0.1' });
|
|
80
89
|
return bindings;
|
|
81
90
|
}
|
|
82
|
-
/** Fixed friendly id for the admin's owner node. */
|
|
83
|
-
export const OWNER_CONTAINER_ID = 'owner';
|
|
84
91
|
/**
|
|
85
92
|
* Runs cadre nodes as plain Node child processes on the host.
|
|
86
93
|
*
|
|
@@ -127,6 +134,7 @@ export class HostProcessOrchestrator {
|
|
|
127
134
|
this.defaultMemoryLimit = cfg.defaultResources?.memoryLimit;
|
|
128
135
|
this.cliEntrypoint = cfg.spawn?.entrypoint ?? resolveCadreCliBin();
|
|
129
136
|
this.pushResolver = cfg.pushResolver;
|
|
137
|
+
this.announceAddrs = cfg.announceAddrs;
|
|
130
138
|
log('HostProcessOrchestrator rootDir=%s cli=%s', this.rootDir, this.cliEntrypoint);
|
|
131
139
|
}
|
|
132
140
|
/**
|
|
@@ -136,7 +144,6 @@ export class HostProcessOrchestrator {
|
|
|
136
144
|
*/
|
|
137
145
|
async init() {
|
|
138
146
|
const state = this.stateStore.load();
|
|
139
|
-
this.ownerConfig = state.ownerConfig;
|
|
140
147
|
for (const persisted of state.handles) {
|
|
141
148
|
const handle = {
|
|
142
149
|
containerId: persisted.containerId,
|
|
@@ -148,7 +155,7 @@ export class HostProcessOrchestrator {
|
|
|
148
155
|
spawnedAt: persisted.spawnedAt,
|
|
149
156
|
partyId: persisted.partyId,
|
|
150
157
|
profile: persisted.profile,
|
|
151
|
-
|
|
158
|
+
announcedAddrs: persisted.announcedAddrs ?? [],
|
|
152
159
|
alive: false,
|
|
153
160
|
};
|
|
154
161
|
handle.alive = isHandleLive(handle);
|
|
@@ -194,25 +201,32 @@ export class HostProcessOrchestrator {
|
|
|
194
201
|
}
|
|
195
202
|
/**
|
|
196
203
|
* Register a state-change listener. Invoked when a handle's `alive`
|
|
197
|
-
* transitions (child exit, manual stop)
|
|
204
|
+
* transitions (create, child exit, manual stop) and once more, as `stopped`,
|
|
205
|
+
* when a handle is removed. Returns an unsubscribe fn.
|
|
198
206
|
*/
|
|
199
207
|
onStateChange(listener) {
|
|
200
208
|
this.stateListeners.add(listener);
|
|
201
209
|
return () => { this.stateListeners.delete(listener); };
|
|
202
210
|
}
|
|
203
211
|
/**
|
|
204
|
-
* Spawn a
|
|
205
|
-
*
|
|
206
|
-
*
|
|
212
|
+
* Spawn a managed node — a hosted node for some cadre, started waiting to be
|
|
213
|
+
* claimed when the request carries a `claimSecret`, or redeeming the cadre
|
|
214
|
+
* invitation it carries as `invitation`. The node is given its own
|
|
215
|
+
* protobuf identity key inside its workdir (`ensureNodeIdentity`, reused across
|
|
207
216
|
* re-spawns of the same containerId), which is what makes its peer id stable
|
|
208
217
|
* across restarts AND what makes its node-local stores durable: `cadre-cli
|
|
209
218
|
* start` opens the file-backed bootstrap-peer, trusted-owner and strand
|
|
210
219
|
* network-state stores in the node's state directory, which defaults to the
|
|
211
220
|
* directory holding the `cadre.json` written here — the same workdir.
|
|
212
|
-
*
|
|
221
|
+
* Removing the node (`removeContainer`) deletes the workdir, so the key and
|
|
213
222
|
* every store go with it.
|
|
214
223
|
*/
|
|
215
224
|
async createContainer(request) {
|
|
225
|
+
// Before anything is allocated: `cadre-cli start` refuses the pair anyway, and a child
|
|
226
|
+
// that exits at once would only surface as a crash loop.
|
|
227
|
+
if (request.claimSecret && request.invitation) {
|
|
228
|
+
throw new Error(`container ${request.containerId}: a spawn carries a claim secret or an invitation, not both`);
|
|
229
|
+
}
|
|
216
230
|
const workdir = this.workdirFor(request.containerId);
|
|
217
231
|
// Sampled BEFORE `ensureNodeIdentity`, which brings the directory into
|
|
218
232
|
// existence as a side effect of writing the key. Only a spawn that found
|
|
@@ -233,25 +247,14 @@ export class HostProcessOrchestrator {
|
|
|
233
247
|
// node's worth of ports from a bounded range on every failed provision attempt.
|
|
234
248
|
const identity = await ensureNodeIdentity(workdir);
|
|
235
249
|
log('container %s identity peerId=%s', request.containerId, identity.peerId);
|
|
236
|
-
// A node started with pinned owner keys belongs to a FOREIGN cadre (the
|
|
237
|
-
// node-donation flow: it trusts the requester's owner key, not the host's).
|
|
238
|
-
// The host's FCM/APNs credentials are minted for the host owner's own app
|
|
239
|
-
// and are meaningless to a foreign cadre — so donated nodes get NO push
|
|
240
|
-
// block. (Per-grantee push creds would be a future ticket; none in v1.)
|
|
241
|
-
const foreignParty = (request.pinnedOwnerKeys?.length ?? 0) > 0;
|
|
242
250
|
// Only storage-profile nodes participate in strands and thus fan out push
|
|
243
251
|
// wakes — a transaction-only node need not carry credentials. Resolved
|
|
244
252
|
// BEFORE the drop below, which keeps the drop → launch window synchronous
|
|
245
253
|
// (see restoreDroppedHandles).
|
|
246
|
-
const push = request.profile === 'storage'
|
|
247
|
-
|
|
248
|
-
// `cadre-cli start` unions it into its cold-start pinnedKeyTrustPolicy so the
|
|
249
|
-
// node will accept the foreign-authority-signed seed presented via POST /seed.
|
|
250
|
-
const extraEnv = request.pinnedOwnerKeys?.length
|
|
251
|
-
? { CADRE_OWNER_KEYS: request.pinnedOwnerKeys.join(',') }
|
|
252
|
-
: undefined;
|
|
254
|
+
const push = request.profile === 'storage' ? await this.resolvePush() : undefined;
|
|
255
|
+
const extraEnv = joinEnv(request);
|
|
253
256
|
// The last `await` on this path: checked as close to the drop as it can be.
|
|
254
|
-
await this.refuseRespawnOverLiveChild(request.containerId
|
|
257
|
+
await this.refuseRespawnOverLiveChild(request.containerId);
|
|
255
258
|
// Nothing from here to the launch is `await`ed — which is what keeps
|
|
256
259
|
// `restoreDroppedHandles`'s documented precondition true even though the
|
|
257
260
|
// `try` now opens further up. Do not add an `await` past this line.
|
|
@@ -266,7 +269,6 @@ export class HostProcessOrchestrator {
|
|
|
266
269
|
partyId: request.partyId,
|
|
267
270
|
profile: request.profile,
|
|
268
271
|
ports,
|
|
269
|
-
owner: false,
|
|
270
272
|
buildConfig: () => this.buildChildConfig(request, workdir, push),
|
|
271
273
|
extraArgs: ['--identity-file', identity.path],
|
|
272
274
|
...(extraEnv ? { extraEnv } : {}),
|
|
@@ -294,7 +296,7 @@ export class HostProcessOrchestrator {
|
|
|
294
296
|
}
|
|
295
297
|
/**
|
|
296
298
|
* Remove the working directory a spawn created for `containerId` when no
|
|
297
|
-
* handle owns it — the cleanup of last resort for a
|
|
299
|
+
* handle owns it — the cleanup of last resort for a hosted-node record that
|
|
298
300
|
* never got a `dockerId` (the host died between `ensureNodeIdentity` and the
|
|
299
301
|
* handle write, so `createContainer`'s own unwind never ran). Returns whether
|
|
300
302
|
* anything was removed.
|
|
@@ -303,17 +305,12 @@ export class HostProcessOrchestrator {
|
|
|
303
305
|
* to await.
|
|
304
306
|
*/
|
|
305
307
|
reclaimWorkdir(containerId) {
|
|
306
|
-
// The admin's own node is never reclaimed by this route: its workdir is a
|
|
307
|
-
// single fixed path reused by every restart and it holds the host's own
|
|
308
|
-
// control-DB storage.
|
|
309
|
-
if (containerId === OWNER_CONTAINER_ID)
|
|
310
|
-
return false;
|
|
311
308
|
// A live handle owns that directory, and `removeContainer` — which stops
|
|
312
309
|
// the child first — is the only thing allowed to delete it.
|
|
313
310
|
if (this.resolveDockerId(containerId))
|
|
314
311
|
return false;
|
|
315
312
|
const workdir = this.workdirFor(containerId);
|
|
316
|
-
// Defence, not a live case:
|
|
313
|
+
// Defence, not a live case: hosted-node ids are `hn_<base64url>` and so can
|
|
317
314
|
// hold neither a path separator nor a dot. A caller that ever passes
|
|
318
315
|
// something else must not be able to walk out of `rootDir`.
|
|
319
316
|
if (!workdir.startsWith(this.rootDir + sep)) {
|
|
@@ -376,120 +373,27 @@ export class HostProcessOrchestrator {
|
|
|
376
373
|
}
|
|
377
374
|
}
|
|
378
375
|
/**
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* orchestrator restart) can re-spawn it without re-supplying them.
|
|
384
|
-
*
|
|
385
|
-
* The owner node is spawned as `cadre-cli start --owner
|
|
386
|
-
* --admin-port <p> --identity-file <identityPath>` so it carries the
|
|
387
|
-
* host's libp2p identity, founds/joins the control network, and exposes the
|
|
388
|
-
* loopback admin channel the manager delegates to.
|
|
376
|
+
* The public addresses the node about to spawn announces. A hook that throws costs
|
|
377
|
+
* the node its public addresses, never its start: it still binds and is reachable
|
|
378
|
+
* on its LAN addresses, and `NatService` restarts it once the addresses it should
|
|
379
|
+
* announce differ from these.
|
|
389
380
|
*/
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
throw new Error('ensureOwnerNode: no owner config supplied and none persisted');
|
|
394
|
-
}
|
|
395
|
-
this.ownerConfig = config;
|
|
396
|
-
const existing = this.findOwnerHandle();
|
|
397
|
-
if (existing && isHandleLive(existing)) {
|
|
398
|
-
this.persist();
|
|
399
|
-
return toNodeInfo(existing);
|
|
400
|
-
}
|
|
401
|
-
const profile = config.profile ?? 'storage';
|
|
402
|
-
// Re-resolve push credentials from the secret store on every (re-)spawn so a
|
|
403
|
-
// restart picks up rotated keys and nothing raw is replayed from state.json.
|
|
404
|
-
// Resolved BEFORE the drop below so the drop → launch window stays
|
|
405
|
-
// synchronous (see restoreDroppedHandles).
|
|
406
|
-
const push = await this.resolvePush();
|
|
407
|
-
// The last `await` before the drop — same guard as in createContainer.
|
|
408
|
-
await this.refuseRespawnOverLiveChild(OWNER_CONTAINER_ID, { p2p: config.libp2pPort }, true);
|
|
409
|
-
// NOTE: the short-circuit above matches via `findOwnerHandle` (owner flag OR
|
|
410
|
-
// containerId), this drop only by containerId. Equivalent today — every
|
|
411
|
-
// owner handle is launched with OWNER_CONTAINER_ID. If an owner spawn ever
|
|
412
|
-
// takes a different containerId, the two would disagree and the handle the
|
|
413
|
-
// short-circuit found would survive here, leaking it and its ports.
|
|
414
|
-
const dropped = this.dropStaleHandle(OWNER_CONTAINER_ID);
|
|
415
|
-
let ports;
|
|
381
|
+
announceAddrsFor(containerId, ports) {
|
|
382
|
+
if (!this.announceAddrs)
|
|
383
|
+
return [];
|
|
416
384
|
try {
|
|
417
|
-
|
|
418
|
-
// mapping (external → internal) lands on it — hence the p2p override, which
|
|
419
|
-
// wins over the dropped handle's. Every other port comes back as it was, for
|
|
420
|
-
// the same reason as in createContainer.
|
|
421
|
-
ports = allocateNodePorts(this.portAllocator, { ...reusedNodePorts(dropped), p2p: config.libp2pPort });
|
|
422
|
-
const result = this.launchChild({
|
|
423
|
-
containerId: OWNER_CONTAINER_ID,
|
|
424
|
-
partyId: config.partyId,
|
|
425
|
-
profile,
|
|
426
|
-
ports,
|
|
427
|
-
owner: true,
|
|
428
|
-
buildConfig: (workdir) => this.buildOwnerChildConfig(config, profile, workdir, push),
|
|
429
|
-
extraArgs: ['--owner', '--admin-port', String(ports.admin), '--identity-file', config.identityPath],
|
|
430
|
-
});
|
|
431
|
-
const handle = this.handles.get(result.dockerId);
|
|
432
|
-
return toNodeInfo(handle);
|
|
385
|
+
return this.announceAddrs(containerId, ports);
|
|
433
386
|
}
|
|
434
387
|
catch (err) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
this.releasePorts(ports);
|
|
438
|
-
this.restoreDroppedHandles(dropped);
|
|
439
|
-
// Deliberately NOT `discardWorkdir` — the asymmetry with createContainer
|
|
440
|
-
// is the point. `<rootDir>/owner` is a single fixed path reused by every
|
|
441
|
-
// restart and holds the host's own control-DB storage, so it neither
|
|
442
|
-
// grows without bound nor is safe to delete on a failed spawn.
|
|
443
|
-
throw err;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
/** Endpoint + bearer for the owner node's admin channel, if spawned. */
|
|
447
|
-
getOwnerAdminEndpoint() {
|
|
448
|
-
const handle = this.findOwnerHandle();
|
|
449
|
-
if (!handle)
|
|
450
|
-
return undefined;
|
|
451
|
-
return {
|
|
452
|
-
baseUrl: `http://127.0.0.1:${handle.ports.admin}`,
|
|
453
|
-
token: handle.startupToken,
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
/** Whether spawn parameters for the owner node are known (persisted). */
|
|
457
|
-
hasOwnerConfig() {
|
|
458
|
-
return this.ownerConfig !== undefined;
|
|
459
|
-
}
|
|
460
|
-
/** Whether the given friendly/docker id refers to the owner node. */
|
|
461
|
-
isOwnerNode(idOrDockerId) {
|
|
462
|
-
if (idOrDockerId === OWNER_CONTAINER_ID)
|
|
463
|
-
return true;
|
|
464
|
-
const direct = this.handles.get(idOrDockerId);
|
|
465
|
-
if (direct?.owner)
|
|
466
|
-
return true;
|
|
467
|
-
for (const h of this.handles.values()) {
|
|
468
|
-
if (h.owner && h.containerId === idOrDockerId)
|
|
469
|
-
return true;
|
|
470
|
-
}
|
|
471
|
-
return false;
|
|
472
|
-
}
|
|
473
|
-
/** Stop the owner node (if running). Used on graceful shutdown. */
|
|
474
|
-
async stopOwnerNode() {
|
|
475
|
-
const handle = this.findOwnerHandle();
|
|
476
|
-
if (handle && isPidAlive(handle.pid)) {
|
|
477
|
-
await this.stopContainer(handle.dockerId);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
/** Stop the owner node (if running), then re-spawn it from saved config. */
|
|
481
|
-
async restartOwnerNode() {
|
|
482
|
-
const handle = this.findOwnerHandle();
|
|
483
|
-
if (handle && isPidAlive(handle.pid)) {
|
|
484
|
-
await this.stopContainer(handle.dockerId);
|
|
388
|
+
log('announce hook failed for %s; it starts with no public addresses: %s', containerId, err.message);
|
|
389
|
+
return [];
|
|
485
390
|
}
|
|
486
|
-
return this.ensureOwnerNode();
|
|
487
391
|
}
|
|
488
392
|
/**
|
|
489
|
-
* Core spawn mechanics
|
|
393
|
+
* Core spawn mechanics behind `createContainer`.
|
|
490
394
|
*
|
|
491
395
|
* The caller has already allocated `ports` and **owns their lifetime**: this
|
|
492
|
-
* method never releases them, so
|
|
396
|
+
* method never releases them, so the caller must release on any throw out
|
|
493
397
|
* of here (it has to unwind its handle drop on the same path anyway — see
|
|
494
398
|
* `restoreDroppedHandles`).
|
|
495
399
|
*
|
|
@@ -525,6 +429,7 @@ export class HostProcessOrchestrator {
|
|
|
525
429
|
const memoryLimit = opts.memoryLimit ?? this.defaultMemoryLimit;
|
|
526
430
|
const heapBytes = parseMemoryLimit(memoryLimit);
|
|
527
431
|
const heapMB = heapBytes !== undefined ? Math.max(64, Math.floor(heapBytes / (1024 * 1024))) : undefined;
|
|
432
|
+
const announcedAddrs = this.announceAddrsFor(containerId, ports);
|
|
528
433
|
const env = {
|
|
529
434
|
// Scrubbed, not raw process.env: every CADRE_* config-override key is
|
|
530
435
|
// stripped so a var set on the manager can't silently reconfigure every
|
|
@@ -537,19 +442,16 @@ export class HostProcessOrchestrator {
|
|
|
537
442
|
CADRE_HEALTH_PORT: String(ports.health),
|
|
538
443
|
CADRE_METRICS_PORT: String(ports.metrics),
|
|
539
444
|
CADRE_LISTEN_ADDRS: childListenAddrs(ports).join(','),
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
//
|
|
546
|
-
//
|
|
547
|
-
//
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
// WebSocket port, and every strand node are reached through observed addresses only,
|
|
551
|
-
// since no `CADRE_RELAY_ADDRS` is set here and a child therefore holds no relay
|
|
552
|
-
// reservation.
|
|
445
|
+
// The append form, not `CADRE_ANNOUNCE_ADDRS`: that one replaces the advertised
|
|
446
|
+
// set and would drop the LAN addresses a phone at home dials. Every entry must be
|
|
447
|
+
// a valid multiaddr: cadre-core refuses to start on one that is not.
|
|
448
|
+
// The TCP and WebSocket ports are the CHILD'S CONTROL NODE's alone: each strand
|
|
449
|
+
// node the child runs binds the same two entries with OS-assigned ports instead,
|
|
450
|
+
// since one port cannot be held twice, and inherits neither announce field
|
|
451
|
+
// (`cadre-core/src/strand-network-config.ts`). Those ports are not mapped, and
|
|
452
|
+
// strand nodes are reached through observed addresses only, since no
|
|
453
|
+
// `CADRE_RELAY_ADDRS` is set here and a child therefore holds no relay reservation.
|
|
454
|
+
...(announcedAddrs.length > 0 ? { CADRE_APPEND_ANNOUNCE_ADDRS: announcedAddrs.join(',') } : {}),
|
|
553
455
|
CADRE_SEED_TOKEN: seedToken,
|
|
554
456
|
// Pin each child's node-local state (trusted-owner anchor, retained
|
|
555
457
|
// cold-start dial targets) to its OWN workdir. This is the same value the
|
|
@@ -612,7 +514,7 @@ export class HostProcessOrchestrator {
|
|
|
612
514
|
spawnedAt: new Date().toISOString(),
|
|
613
515
|
partyId: opts.partyId,
|
|
614
516
|
profile: opts.profile,
|
|
615
|
-
|
|
517
|
+
announcedAddrs,
|
|
616
518
|
child,
|
|
617
519
|
alive: true,
|
|
618
520
|
};
|
|
@@ -652,7 +554,7 @@ export class HostProcessOrchestrator {
|
|
|
652
554
|
}
|
|
653
555
|
/**
|
|
654
556
|
* Refuse to re-spawn `containerId` while a previous child of it may still be
|
|
655
|
-
* running.
|
|
557
|
+
* running. `createContainer` calls this as its last `await` before the handle
|
|
656
558
|
* drop, so a refusal happens while nothing has been released, no token rotated
|
|
657
559
|
* and no child launched — the caller sees an ordinary failed spawn.
|
|
658
560
|
*
|
|
@@ -670,9 +572,6 @@ export class HostProcessOrchestrator {
|
|
|
670
572
|
* is already bound, refuse. The pid is never killed on that evidence — it may
|
|
671
573
|
* not be ours.
|
|
672
574
|
*
|
|
673
|
-
* `overrides` are the ports the caller will override on top of the reused ones
|
|
674
|
-
* (the owner node's configured p2p port); `owner` adds the admin channel.
|
|
675
|
-
*
|
|
676
575
|
* NOTE: the probe cannot see a re-attached child still loading its modules —
|
|
677
576
|
* `cadre-cli start` writes its token and only then binds, so a child in that
|
|
678
577
|
* window shows neither, and a re-spawn launched then races it for the ports.
|
|
@@ -687,14 +586,14 @@ export class HostProcessOrchestrator {
|
|
|
687
586
|
* child is ever seen dying on its own port right after a host restart, probe by
|
|
688
587
|
* connecting instead of binding.
|
|
689
588
|
*/
|
|
690
|
-
async refuseRespawnOverLiveChild(containerId
|
|
589
|
+
async refuseRespawnOverLiveChild(containerId) {
|
|
691
590
|
const previous = this.handlesFor(containerId);
|
|
692
591
|
if (previous.some(isHandleLive)) {
|
|
693
592
|
throw new Error(`container ${containerId} is still running`);
|
|
694
593
|
}
|
|
695
594
|
if (!previous.some((h) => isPidAlive(h.pid)))
|
|
696
595
|
return;
|
|
697
|
-
const bindings = childPortBindings(
|
|
596
|
+
const bindings = childPortBindings(reusedNodePorts(previous));
|
|
698
597
|
try {
|
|
699
598
|
await Promise.all(bindings.map(assertPortFree));
|
|
700
599
|
}
|
|
@@ -709,9 +608,9 @@ export class HostProcessOrchestrator {
|
|
|
709
608
|
/**
|
|
710
609
|
* Drop any handle left over from a previous spawn of the same `containerId`
|
|
711
610
|
* and release its ports. Handles are keyed by the per-spawn `dockerId`, so
|
|
712
|
-
* without this a re-spawn (
|
|
611
|
+
* without this a re-spawn (a hosted node's respawn) would strand the prior
|
|
713
612
|
* handle in the map forever, leaking its ports from a bounded range each
|
|
714
|
-
* time.
|
|
613
|
+
* time.
|
|
715
614
|
*
|
|
716
615
|
* DO NOT delete the workdir: the identity key and node-local stores
|
|
717
616
|
* (trusted-owner anchor, retained cold-start dial targets) live there and are
|
|
@@ -721,16 +620,16 @@ export class HostProcessOrchestrator {
|
|
|
721
620
|
* Releasing the ports hands them straight back to the allocator, and the
|
|
722
621
|
* re-spawn then takes those same ports as overrides (`reusedNodePorts`), so a
|
|
723
622
|
* re-spawn while the *previous* child is still listening would bind-clash.
|
|
724
|
-
*
|
|
623
|
+
* The caller must therefore never drop a live child, and does so only after
|
|
725
624
|
* {@link refuseRespawnOverLiveChild} has passed. A caller that needs to replace
|
|
726
625
|
* a live child must stop it first.
|
|
727
626
|
*
|
|
728
627
|
* **The drop is reversible.** It happens before the launch can fail, so the
|
|
729
|
-
* dropped handles are returned and
|
|
628
|
+
* dropped handles are returned and the caller restores them via
|
|
730
629
|
* {@link restoreDroppedHandles} when the launch throws — otherwise the caller
|
|
731
630
|
* would be left holding a dockerId this orchestrator no longer knows, and the
|
|
732
631
|
* node's workdir could never be reclaimed. On a *successful* spawn the drop
|
|
733
|
-
* stands: `
|
|
632
|
+
* stands: `HostedNodeService.abandonRespawn` depends on the old handle being
|
|
734
633
|
* gone by then.
|
|
735
634
|
*/
|
|
736
635
|
dropStaleHandle(containerId) {
|
|
@@ -749,10 +648,10 @@ export class HostProcessOrchestrator {
|
|
|
749
648
|
*
|
|
750
649
|
* **Precondition: the drop → launch window is synchronous.** Restoring is
|
|
751
650
|
* sound only because nothing can have taken those ports or run cleanup
|
|
752
|
-
* against the handle in between — `launchChild` contains no `await` and
|
|
753
|
-
*
|
|
651
|
+
* against the handle in between — `launchChild` contains no `await` and the
|
|
652
|
+
* caller resolves push credentials *before* its drop. `launchChild`'s half
|
|
754
653
|
* is enforced: its declared return type is not a Promise, so adding an
|
|
755
|
-
* `await` there is a compile error. The
|
|
654
|
+
* `await` there is a compile error. The caller's half is not — an `await`
|
|
756
655
|
* slipped between a drop and its launch silently breaks this.
|
|
757
656
|
*
|
|
758
657
|
* No `persist()` here: `state.json` is rewritten only by `launchChild` and
|
|
@@ -778,14 +677,6 @@ export class HostProcessOrchestrator {
|
|
|
778
677
|
workdirFor(containerId) {
|
|
779
678
|
return join(this.rootDir, containerId);
|
|
780
679
|
}
|
|
781
|
-
/** Locate the owner node's handle, if one has been spawned. */
|
|
782
|
-
findOwnerHandle() {
|
|
783
|
-
for (const h of this.handles.values()) {
|
|
784
|
-
if (h.owner || h.containerId === OWNER_CONTAINER_ID)
|
|
785
|
-
return h;
|
|
786
|
-
}
|
|
787
|
-
return undefined;
|
|
788
|
-
}
|
|
789
680
|
/**
|
|
790
681
|
* Inverse of {@link releasePorts} — hold a handle's whole port set against
|
|
791
682
|
* the allocator. Used both to rehydrate on `init` and to undo a drop whose
|
|
@@ -861,6 +752,11 @@ export class HostProcessOrchestrator {
|
|
|
861
752
|
this.discardWorkdir(handle.workdir, { maxRetries: 20, retryDelay: 200 });
|
|
862
753
|
this.handles.delete(dockerId);
|
|
863
754
|
this.persist();
|
|
755
|
+
// Removal is a state change too: `NatService` releases the node's port
|
|
756
|
+
// mappings on it at once rather than at its next timer pass. A stopped
|
|
757
|
+
// event for a node no longer in `listNodes()` is how listeners tell a
|
|
758
|
+
// removal from a stop.
|
|
759
|
+
this.emitStateChange(handle);
|
|
864
760
|
}
|
|
865
761
|
/**
|
|
866
762
|
* Whether the child `dockerId` names is running — see {@link isHandleLive}. An
|
|
@@ -939,46 +835,10 @@ export class HostProcessOrchestrator {
|
|
|
939
835
|
cfg.push = push;
|
|
940
836
|
return cfg;
|
|
941
837
|
}
|
|
942
|
-
/**
|
|
943
|
-
* Child config for the **host's own personal cadre** owner node — the opt-in
|
|
944
|
-
* "founder" persona spawned only when `ownCadre.enabled` (see
|
|
945
|
-
* docs/cadre-host.md § Two roles: donor and founder). It founds/joins the
|
|
946
|
-
* control network for the host's own `partyId` (no bootstrap peers — it is the
|
|
947
|
-
* founding node of the host's *own* cadre) and carries the host identity via
|
|
948
|
-
* `--identity-file` (passed as a spawn arg).
|
|
949
|
-
*
|
|
950
|
-
* This is NOT the node cadre-host donates to a requester. Donated nodes are
|
|
951
|
-
* generic (`createContainer` / `buildChildConfig`): they join the
|
|
952
|
-
* **requester's** cadre via bootstrap peers and pin the *requester's* owner
|
|
953
|
-
* key — they never run a host genesis.
|
|
954
|
-
*/
|
|
955
|
-
buildOwnerChildConfig(cfg, profile, workdir, push) {
|
|
956
|
-
const config = {
|
|
957
|
-
controlNetwork: {
|
|
958
|
-
partyId: cfg.partyId,
|
|
959
|
-
bootstrapNodes: [],
|
|
960
|
-
},
|
|
961
|
-
profile,
|
|
962
|
-
strandFilter: 'all',
|
|
963
|
-
};
|
|
964
|
-
if (profile === 'storage') {
|
|
965
|
-
config.storage = { type: 'file', path: join(workdir, 'storage') };
|
|
966
|
-
}
|
|
967
|
-
// The host-own-cadre owner/storage node participates in strands, so it owns
|
|
968
|
-
// the push-wake fan-out when credentials are configured. (Donated nodes
|
|
969
|
-
// belong to foreign cadres and get NO host push block — see the donor path.)
|
|
970
|
-
// Written into cadre.json (same host trust boundary as the workdir's
|
|
971
|
-
// control-DB); keys are never logged — only the redacted presence line in
|
|
972
|
-
// resolvePush.
|
|
973
|
-
if (push)
|
|
974
|
-
config.push = push;
|
|
975
|
-
return config;
|
|
976
|
-
}
|
|
977
838
|
persist() {
|
|
978
839
|
const state = {
|
|
979
840
|
version: 1,
|
|
980
841
|
handles: [...this.handles.values()].map((h) => this.toPersisted(h)),
|
|
981
|
-
...(this.ownerConfig ? { ownerConfig: this.ownerConfig } : {}),
|
|
982
842
|
};
|
|
983
843
|
this.stateStore.save(state);
|
|
984
844
|
}
|
|
@@ -1006,7 +866,7 @@ export class HostProcessOrchestrator {
|
|
|
1006
866
|
spawnedAt: h.spawnedAt,
|
|
1007
867
|
partyId: h.partyId,
|
|
1008
868
|
profile: h.profile,
|
|
1009
|
-
|
|
869
|
+
announcedAddrs: h.announcedAddrs,
|
|
1010
870
|
};
|
|
1011
871
|
}
|
|
1012
872
|
}
|
|
@@ -1020,7 +880,7 @@ function toNodeInfo(h) {
|
|
|
1020
880
|
spawnedAt: h.spawnedAt,
|
|
1021
881
|
workdir: h.workdir,
|
|
1022
882
|
ports: { ...h.ports },
|
|
1023
|
-
...
|
|
883
|
+
announcedAddrs: [...h.announcedAddrs],
|
|
1024
884
|
};
|
|
1025
885
|
}
|
|
1026
886
|
/**
|
|
@@ -1040,8 +900,8 @@ function resolveCadreCliBin() {
|
|
|
1040
900
|
}
|
|
1041
901
|
/**
|
|
1042
902
|
* Whether the process a handle names is still the child it was launched as — the
|
|
1043
|
-
* one liveness rule behind `init`, `isRunning
|
|
1044
|
-
*
|
|
903
|
+
* one liveness rule behind `init`, `isRunning` and the pre-spawn guard, so they
|
|
904
|
+
* cannot drift apart.
|
|
1045
905
|
*
|
|
1046
906
|
* A handle this process spawned carries its `ChildProcess`, and that answer is
|
|
1047
907
|
* exact from the moment of spawn: no exit seen, and the pid still answering. (The
|