instar 1.3.628 → 1.3.630
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +133 -5
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +52 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/FencedLease.d.ts +23 -4
- package/dist/core/FencedLease.d.ts.map +1 -1
- package/dist/core/FencedLease.js +44 -4
- package/dist/core/FencedLease.js.map +1 -1
- package/dist/core/HttpLeaseTransport.d.ts +56 -3
- package/dist/core/HttpLeaseTransport.d.ts.map +1 -1
- package/dist/core/HttpLeaseTransport.js +191 -51
- package/dist/core/HttpLeaseTransport.js.map +1 -1
- package/dist/core/LeaseCoordinator.d.ts +84 -0
- package/dist/core/LeaseCoordinator.d.ts.map +1 -1
- package/dist/core/LeaseCoordinator.js +129 -7
- package/dist/core/LeaseCoordinator.js.map +1 -1
- package/dist/core/MachineIdentity.d.ts +11 -0
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +22 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/MeshUrlAdvertiser.d.ts +50 -0
- package/dist/core/MeshUrlAdvertiser.d.ts.map +1 -1
- package/dist/core/MeshUrlAdvertiser.js +127 -0
- package/dist/core/MeshUrlAdvertiser.js.map +1 -1
- package/dist/core/MultiMachineCoordinator.d.ts +87 -0
- package/dist/core/MultiMachineCoordinator.d.ts.map +1 -1
- package/dist/core/MultiMachineCoordinator.js +241 -6
- package/dist/core/MultiMachineCoordinator.js.map +1 -1
- package/dist/core/PeerEndpointResolver.d.ts +0 -0
- package/dist/core/PeerEndpointResolver.d.ts.map +1 -0
- package/dist/core/PeerEndpointResolver.js +0 -0
- package/dist/core/PeerEndpointResolver.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +10 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +15 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +131 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +15 -2
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/machineAuth.d.ts +24 -0
- package/dist/server/machineAuth.d.ts.map +1 -1
- package/dist/server/machineAuth.js +71 -0
- package/dist/server/machineAuth.js.map +1 -1
- package/dist/server/machineRoutes.d.ts +7 -1
- package/dist/server/machineRoutes.d.ts.map +1 -1
- package/dist/server/machineRoutes.js +28 -3
- package/dist/server/machineRoutes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +22 -22
- package/src/scaffold/templates.ts +3 -0
- package/upgrades/1.3.629.md +31 -0
- package/upgrades/1.3.630.md +38 -0
- package/upgrades/side-effects/multi-machine-lease-self-heal.md +45 -0
- package/upgrades/side-effects/multi-transport-mesh-comms.md +70 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAmH7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA2EtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAy4CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CA8eN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAohftE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -85,7 +85,8 @@ import { MultiMachineCoordinator } from '../core/MultiMachineCoordinator.js';
|
|
|
85
85
|
import { isRemotelyHandled } from '../core/SessionRouter.js';
|
|
86
86
|
import { isSlackSessionKey, reconstructSlackMessage } from '../core/SlackForwardBridge.js';
|
|
87
87
|
import { formatForwardedTopicContext } from '../core/ForwardedTopicContext.js';
|
|
88
|
-
import { resolveAdvertisedMeshUrl, advertiseSelfMeshUrl } from '../core/MeshUrlAdvertiser.js';
|
|
88
|
+
import { resolveAdvertisedMeshUrl, advertiseSelfMeshUrl, detectTailscaleIp, pickPrimaryLanIp, computeSelfMeshEndpoints, advertiseSelfMeshEndpoints } from '../core/MeshUrlAdvertiser.js';
|
|
89
|
+
import { PeerEndpointResolver } from '../core/PeerEndpointResolver.js';
|
|
89
90
|
import { relayOutbound } from '../core/TelegramRelay.js';
|
|
90
91
|
import { GitSyncManager } from '../core/GitSync.js';
|
|
91
92
|
import { RegistrySyncDebouncer } from '../core/RegistrySyncDebouncer.js';
|
|
@@ -174,6 +175,58 @@ import { toInjection } from '../types/pipeline.js';
|
|
|
174
175
|
import { UserManager } from '../users/UserManager.js';
|
|
175
176
|
import { formatUserContextForSession, hasUserContext } from '../users/UserContextBuilder.js';
|
|
176
177
|
import { SafeFsExecutor } from '../core/SafeFsExecutor.js';
|
|
178
|
+
// setup.ts uses @inquirer/prompts which requires Node 20.12+
|
|
179
|
+
// Dynamic import to avoid breaking the server on older Node versions
|
|
180
|
+
// import { installAutoStart } from './setup.js';
|
|
181
|
+
/**
|
|
182
|
+
* multi-transport-mesh-comms — this machine's own IPv4 CIDRs (for the
|
|
183
|
+
* PeerEndpointResolver LAN-subnet gate). Each non-internal IPv4 interface address
|
|
184
|
+
* with its prefix length, e.g. '192.168.87.67/24'. Pure read of os.networkInterfaces().
|
|
185
|
+
*/
|
|
186
|
+
function ownIpv4Cidrs() {
|
|
187
|
+
const out = [];
|
|
188
|
+
const ifaces = os.networkInterfaces();
|
|
189
|
+
for (const addrs of Object.values(ifaces)) {
|
|
190
|
+
for (const a of addrs ?? []) {
|
|
191
|
+
const isV4 = a.family === 'IPv4' || a.family === 4;
|
|
192
|
+
if (isV4 && !a.internal && typeof a.cidr === 'string' && a.cidr)
|
|
193
|
+
out.push(a.cidr);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* multi-transport-mesh-comms (Layer 0) — compute + advertise THIS machine's mesh
|
|
200
|
+
* endpoint set (Tailscale / LAN / Cloudflare) into its registry entry, co-located
|
|
201
|
+
* with the lastKnownUrl advertise. Best-effort + idempotent: detection failures
|
|
202
|
+
* fail silent (an absent rope is simply not advertised). No-op when the mesh
|
|
203
|
+
* transport is disabled. Called at boot + on sleep/wake tunnel restart.
|
|
204
|
+
*/
|
|
205
|
+
async function advertiseSelfMeshEndpointsNow(identityManager, machineId, cfg, cloudflareUrl) {
|
|
206
|
+
try {
|
|
207
|
+
if (!cfg.multiMachine?.enabled || cfg.multiMachine?.meshTransport?.enabled === false)
|
|
208
|
+
return;
|
|
209
|
+
const tailscaleEnabled = cfg.multiMachine?.meshTransport?.tailscaleEnabled !== false;
|
|
210
|
+
const tailscaleIp = tailscaleEnabled ? await detectTailscaleIp() : null;
|
|
211
|
+
const lanIp = pickPrimaryLanIp(os.networkInterfaces());
|
|
212
|
+
const endpoints = computeSelfMeshEndpoints({
|
|
213
|
+
cloudflareUrl,
|
|
214
|
+
lanIp,
|
|
215
|
+
tailscaleIp,
|
|
216
|
+
port: cfg.port ?? 4042,
|
|
217
|
+
tailscaleEnabled,
|
|
218
|
+
});
|
|
219
|
+
// E2E-PAIRING: EXEMPT — this commit is comment-only (@silent-fallback-ok ratchet
|
|
220
|
+
// tags); it adds NO new route. The mesh routes/fields shipped in the prior commit
|
|
221
|
+
// (1cd2618e) with their integration + 2-server tests.
|
|
222
|
+
advertiseSelfMeshEndpoints(identityManager, machineId, endpoints, (m) => console.log(pc.dim(m)));
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// @silent-fallback-ok: best-effort — endpoint advertisement must never break boot
|
|
226
|
+
// (a Tailscale-detect/registry hiccup just means fewer advertised ropes this tick;
|
|
227
|
+
// the next heartbeat retries and the mesh degrades to the remaining ropes).
|
|
228
|
+
}
|
|
229
|
+
}
|
|
177
230
|
/**
|
|
178
231
|
* Pure: should the emergency "fix command" gate intercept this message?
|
|
179
232
|
*
|
|
@@ -3895,14 +3948,47 @@ export async function startServer(options) {
|
|
|
3895
3948
|
// stays true, so the lease behaves exactly as git-only. Multi-machine
|
|
3896
3949
|
// meshes get RTT-bounded acquisition + the renewal-requires-medium rule.
|
|
3897
3950
|
let leaseSeq = Date.now();
|
|
3951
|
+
// multi-transport-mesh-comms (Layer 1) — the hedged-failover resolver,
|
|
3952
|
+
// built from the FLAT meshTransport config. Injected into the transport;
|
|
3953
|
+
// when meshTransport.enabled is false the transport falls back to the
|
|
3954
|
+
// single-`url` legacy path (byte-for-byte today).
|
|
3955
|
+
const meshCfg = config.multiMachine?.meshTransport;
|
|
3956
|
+
const meshEnabled = () => meshCfg?.enabled !== false; // default ON
|
|
3957
|
+
const meshResolver = new PeerEndpointResolver({
|
|
3958
|
+
config: {
|
|
3959
|
+
enabled: meshCfg?.enabled !== false,
|
|
3960
|
+
hedgeDelayMs: meshCfg?.hedgeDelayMs ?? 1500,
|
|
3961
|
+
priorityTailscale: meshCfg?.priorityTailscale ?? 10,
|
|
3962
|
+
priorityLan: meshCfg?.priorityLan ?? 20,
|
|
3963
|
+
priorityCloudflare: meshCfg?.priorityCloudflare ?? 30,
|
|
3964
|
+
tailscaleEnabled: meshCfg?.tailscaleEnabled !== false,
|
|
3965
|
+
lanSubnetGate: meshCfg?.lanSubnetGate !== false,
|
|
3966
|
+
unhealthyAfterFailures: meshCfg?.unhealthyAfterFailures ?? 3,
|
|
3967
|
+
endpointEvictionMs: meshCfg?.endpointEvictionMs ?? 3_600_000,
|
|
3968
|
+
maxProbeBackoffMs: meshCfg?.maxProbeBackoffMs ?? 300_000,
|
|
3969
|
+
requestTimeoutMs: Math.min(seamlessness.leaseTtlMs / 2, 30_000),
|
|
3970
|
+
},
|
|
3971
|
+
ownCidrs: () => ownIpv4Cidrs(),
|
|
3972
|
+
logger: (m) => console.log(pc.dim(m)),
|
|
3973
|
+
});
|
|
3898
3974
|
leaseTransport = new HttpLeaseTransport({
|
|
3899
3975
|
selfMachineId,
|
|
3900
3976
|
signingKeyPem: idMgr.loadSigningKey(),
|
|
3901
3977
|
peers: () => {
|
|
3902
3978
|
const reg = idMgr.loadRegistry();
|
|
3903
3979
|
return Object.entries(reg.machines ?? {})
|
|
3904
|
-
.filter(([id, e]) => id !== selfMachineId && !!e.lastKnownUrl && !e.revokedAt)
|
|
3905
|
-
.map(([id, e]) => ({
|
|
3980
|
+
.filter(([id, e]) => id !== selfMachineId && (!!e.lastKnownUrl || (e.endpoints?.length ?? 0) > 0) && !e.revokedAt)
|
|
3981
|
+
.map(([id, e]) => ({
|
|
3982
|
+
machineId: id,
|
|
3983
|
+
url: (e.lastKnownUrl ?? ''),
|
|
3984
|
+
endpoints: e.endpoints,
|
|
3985
|
+
publicKeyPem: idMgr.getSigningPublicKeyPem(id),
|
|
3986
|
+
// Rolling-deploy gate: a peer that advertises an endpoint set is
|
|
3987
|
+
// running THIS build (the field + the accept-ack receiver ship
|
|
3988
|
+
// atomically), so it is ack-capable. An older peer (no endpoints,
|
|
3989
|
+
// or no resolvable pubkey) stays on the 2xx back-compat path.
|
|
3990
|
+
meshAckCapable: (e.endpoints?.length ?? 0) > 0 && !!idMgr.getSigningPublicKeyPem(id),
|
|
3991
|
+
}));
|
|
3906
3992
|
},
|
|
3907
3993
|
nextSequence: () => ++leaseSeq,
|
|
3908
3994
|
reachabilityWindowMs: seamlessness.leaseTtlMs,
|
|
@@ -3911,6 +3997,9 @@ export async function startServer(options) {
|
|
|
3911
3997
|
// the whole TTL, but ABOVE the fleet's 5-40s receiver-stall envelope
|
|
3912
3998
|
// so a slow-but-alive peer isn't converted into "no medium".
|
|
3913
3999
|
requestTimeoutMs: Math.min(seamlessness.leaseTtlMs / 2, 30_000),
|
|
4000
|
+
resolver: meshResolver,
|
|
4001
|
+
meshTransportEnabled: meshEnabled,
|
|
4002
|
+
hedgeDelayMs: meshCfg?.hedgeDelayMs ?? 1500,
|
|
3914
4003
|
logger: (m) => console.log(pc.dim(m)),
|
|
3915
4004
|
});
|
|
3916
4005
|
const leaseCoordinator = new LeaseCoordinator({
|
|
@@ -3931,6 +4020,39 @@ export async function startServer(options) {
|
|
|
3931
4020
|
return dead;
|
|
3932
4021
|
},
|
|
3933
4022
|
onEpochAdvance: (epoch) => coordinator.emit('leaseEpochChange', epoch),
|
|
4023
|
+
// F2 (multi-machine-lease-self-heal staleHolderTakeover) — DARK by default.
|
|
4024
|
+
// Resolved LIVE each call so a config flip needs no restart; off ⇒ canAcquire
|
|
4025
|
+
// is byte-for-byte the legacy behavior.
|
|
4026
|
+
staleHolderTakeover: () => {
|
|
4027
|
+
const sht = config.multiMachine?.leaseSelfHeal?.staleHolderTakeover;
|
|
4028
|
+
if (!sht?.enabled)
|
|
4029
|
+
return null;
|
|
4030
|
+
return { enabled: true, nonRenewalMissedObservations: sht.nonRenewalMissedObservations ?? 6 };
|
|
4031
|
+
},
|
|
4032
|
+
// multi-transport-mesh-comms Layer 3 (soloCaptainHold) — DARK by default.
|
|
4033
|
+
// Resolved live; the three gates are EXISTING signals: the F4 preferred-awake
|
|
4034
|
+
// config (this machine is the named preferred), every peer presumed-gone by
|
|
4035
|
+
// the same liveness timeout presumedDeadHolders uses, and (checked inside the
|
|
4036
|
+
// coordinator) no higher epoch.
|
|
4037
|
+
soloCaptainHold: () => {
|
|
4038
|
+
const sch = config.multiMachine?.leaseSelfHeal?.soloCaptainHold;
|
|
4039
|
+
return sch?.enabled ? { enabled: true } : null;
|
|
4040
|
+
},
|
|
4041
|
+
isPreferredAwakeAgreed: () => {
|
|
4042
|
+
const pref = config.multiMachine?.leaseSelfHeal?.preferredAwakeMachineId;
|
|
4043
|
+
return !!pref && pref === selfMachineId;
|
|
4044
|
+
},
|
|
4045
|
+
allPeersPresumedGone: () => {
|
|
4046
|
+
const reg = idMgr.loadRegistry();
|
|
4047
|
+
const nowMs = Date.now();
|
|
4048
|
+
const peerIds = Object.keys(reg.machines ?? {}).filter((id) => id !== selfMachineId && !reg.machines[id].revokedAt);
|
|
4049
|
+
if (peerIds.length === 0)
|
|
4050
|
+
return false; // a solo machine never "holds against" a peer
|
|
4051
|
+
return peerIds.every((id) => {
|
|
4052
|
+
const last = Date.parse(reg.machines[id].lastSeen);
|
|
4053
|
+
return !Number.isNaN(last) && nowMs - last > seamlessness.failoverThresholdMs;
|
|
4054
|
+
});
|
|
4055
|
+
},
|
|
3934
4056
|
onSelfSuspend: (reason) => console.log(pc.yellow(` [lease] self-suspend: ${reason}`)),
|
|
3935
4057
|
onEscalate: (info) => console.log(pc.yellow(` [lease] split-brain escalation: ${info.reason} (holder ${info.holder})`)),
|
|
3936
4058
|
logger: (m) => console.log(pc.dim(m)),
|
|
@@ -11119,7 +11241,10 @@ export async function startServer(options) {
|
|
|
11119
11241
|
// now stale; peers must learn the new one or cross-machine routing
|
|
11120
11242
|
// silently breaks post-wake.
|
|
11121
11243
|
if (coordinator.enabled && coordinator.identity) {
|
|
11122
|
-
|
|
11244
|
+
const cfUrl = resolveAdvertisedMeshUrl(config.tunnel, tunnelUrl);
|
|
11245
|
+
advertiseSelfMeshUrl(coordinator.managers.identityManager, coordinator.identity.machineId, cfUrl, (m) => console.log(pc.dim(m)));
|
|
11246
|
+
// multi-transport-mesh-comms — re-advertise endpoints post-wake.
|
|
11247
|
+
await advertiseSelfMeshEndpointsNow(coordinator.managers.identityManager, coordinator.identity.machineId, config, cfUrl);
|
|
11123
11248
|
}
|
|
11124
11249
|
// Re-broadcast dashboard URL after tunnel restart (quick tunnels get new URL)
|
|
11125
11250
|
if (tunnelUrl) {
|
|
@@ -17575,7 +17700,10 @@ export async function startServer(options) {
|
|
|
17575
17700
|
// out (the session pool is inert across machines). The existing
|
|
17576
17701
|
// RegistrySyncDebouncer propagates the populated entry to peers.
|
|
17577
17702
|
if (coordinator.enabled && coordinator.identity) {
|
|
17578
|
-
|
|
17703
|
+
const cfUrl = resolveAdvertisedMeshUrl(config.tunnel, tunnelUrl);
|
|
17704
|
+
advertiseSelfMeshUrl(coordinator.managers.identityManager, coordinator.identity.machineId, cfUrl, (m) => console.log(pc.dim(m)));
|
|
17705
|
+
// multi-transport-mesh-comms (Layer 0) — advertise the full endpoint set.
|
|
17706
|
+
await advertiseSelfMeshEndpointsNow(coordinator.managers.identityManager, coordinator.identity.machineId, config, cfUrl);
|
|
17579
17707
|
}
|
|
17580
17708
|
}
|
|
17581
17709
|
catch (err) {
|