@kici-dev/orchestrator 0.14.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__test-helpers__/peer-mutual-auth.d.ts +42 -0
- package/dist/__test-helpers__/scratch-db.d.ts +16 -0
- package/dist/agent/host-roster.d.ts +100 -37
- package/dist/agent/registry.d.ts +1 -8
- package/dist/app-on-error.d.ts +1 -1
- package/dist/app.d.ts +4 -2
- package/dist/artifacts/artifact-store.d.ts +0 -2
- package/dist/audit/access-log.d.ts +1 -1
- package/dist/cache/source-cache.d.ts +1 -1
- package/dist/cache/user-cache.d.ts +0 -28
- package/dist/cli/api-client.d.ts +27 -1
- package/dist/cli/commands/access-log.d.ts +1 -1
- package/dist/cli/commands/cold-store-impl.d.ts +7 -84
- package/dist/cli/commands/db.d.ts +1 -0
- package/dist/cli/commands/host.d.ts +3 -0
- package/dist/cli/commands/orchestrator-service/status.d.ts +2 -0
- package/dist/cli/commands/org.d.ts +12 -0
- package/dist/cli/commands/scaler.d.ts +14 -0
- package/dist/cli/commands/secret.d.ts +1 -45
- package/dist/cli/commands/service-health.d.ts +21 -13
- package/dist/cli/commands/shared/cli-action.d.ts +17 -0
- package/dist/cli/commands/shared/db.d.ts +5 -5
- package/dist/cli/commands/shared/org-id.d.ts +5 -0
- package/dist/cli/commands/shared/service-verbs.d.ts +19 -0
- package/dist/cli/commands/shared/table.d.ts +3 -0
- package/dist/cli/commands/shared/unbound-context-warning.d.ts +2 -22
- package/dist/cli/commands/source-manifest.d.ts +3 -3
- package/dist/cli/commands/source.d.ts +23 -0
- package/dist/cli/commands/trust-policy.d.ts +7 -31
- package/dist/cli/join.d.ts +2 -2
- package/dist/cli/kici-admin.d.ts +6 -1
- package/dist/cli/local-github-ingress-url.d.ts +6 -0
- package/dist/cli/root-option-routing.d.ts +46 -0
- package/dist/cli/webhook-url-notes.d.ts +15 -0
- package/dist/cli.js +10667 -8461
- package/dist/cluster/coordinator-credential.d.ts +21 -0
- package/dist/cluster/coordinator.d.ts +2 -2
- package/dist/cluster/index.d.ts +4 -4
- package/dist/cluster/join-client.d.ts +23 -21
- package/dist/cluster/join-handler.d.ts +24 -16
- package/dist/cluster/join-protocol-v2.d.ts +84 -0
- package/dist/cluster/join-token.d.ts +78 -56
- package/dist/cluster/peer-auth-coordinator.d.ts +16 -2
- package/dist/cluster/peer-client.d.ts +99 -5
- package/dist/cluster/peer-crypto.d.ts +60 -3
- package/dist/cluster/peer-discovery.d.ts +67 -0
- package/dist/cluster/peer-forget.d.ts +4 -9
- package/dist/cluster/peer-handler.d.ts +28 -6
- package/dist/cluster/peer-registry.d.ts +15 -1
- package/dist/cluster/peer-request-waiters.d.ts +26 -0
- package/dist/cluster/scaler-orphans-peer.d.ts +4 -9
- package/dist/cluster/scaler-reload-fanout.d.ts +29 -0
- package/dist/cluster/scaler-reload-peer.d.ts +16 -0
- package/dist/cluster/worker-cluster-settings.d.ts +1 -0
- package/dist/cold-store/load-access-log-range.d.ts +3 -3
- package/dist/cold-store/tables/access-log.d.ts +4 -12
- package/dist/cold-store/tables/event-log.d.ts +8 -19
- package/dist/cold-store/tables/execution-jobs.d.ts +7 -24
- package/dist/cold-store/tables/execution-runs.d.ts +6 -20
- package/dist/cold-store/tables/execution-steps.d.ts +8 -26
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -11
- package/dist/config/env-overlay.d.ts +3 -8
- package/dist/config/reload.d.ts +9 -2
- package/dist/config/schema.d.ts +16 -2
- package/dist/config/types.d.ts +6 -2
- package/dist/config.d.ts +22 -17
- package/dist/contexts/held-runs.d.ts +3 -6
- package/dist/dashboard/cursor.d.ts +12 -0
- package/dist/dashboard/handler.d.ts +5 -12
- package/dist/db/deferred-index-build.d.ts +42 -0
- package/dist/db/deferred-indexes.d.ts +3 -37
- package/dist/db/migrations/100_held_runs_hold_type_vocabulary.d.ts +3 -5
- package/dist/db/migrations/157_host_roster_identity_source.d.ts +21 -0
- package/dist/db/migrations/158_normalize_legacy_values.d.ts +4 -0
- package/dist/db/repos/org-ids-repo.d.ts +87 -0
- package/dist/db/schema-snapshot.generated.d.ts +3 -0
- package/dist/db/types.d.ts +21 -24
- package/dist/fault-injection-types.d.ts +0 -6
- package/dist/git/dispatch-git-auth.d.ts +6 -12
- package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +25 -13
- package/dist/helpers/json.d.ts +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +357 -216
- package/dist/metrics/prometheus.d.ts +0 -5
- package/dist/oidc/id-token-claims.d.ts +1 -1
- package/dist/oidc/resolve-signer.d.ts +2 -4
- package/dist/orchestrator-core.d.ts +10 -3
- package/dist/pipeline/global-eval-round.d.ts +2 -26
- package/dist/pipeline/install-secrets-resolver.d.ts +0 -3
- package/dist/pipeline/manual-schedule.d.ts +1 -7
- package/dist/provenance/dispatch-context.d.ts +5 -7
- package/dist/provenance/pending-attestations-repo.d.ts +1 -1
- package/dist/provenance/trust-root.d.ts +4 -5
- package/dist/provenance/verify-deferred-capture.d.ts +3 -3
- package/dist/provider-registry.d.ts +13 -40
- package/dist/providers/github/manifest.d.ts +58 -7
- package/dist/providers/universal-git/ssh-auth.d.ts +0 -5
- package/dist/reporting/check-run-reporter.d.ts +8 -21
- package/dist/reporting/check-run-summary.d.ts +2 -2
- package/dist/reporting/execution-tracker.d.ts +1 -1
- package/dist/reporting/log-chunk-sink.d.ts +1 -1
- package/dist/reporting/run-summary-fields.d.ts +25 -0
- package/dist/reporting/s3-log-storage.d.ts +2 -2
- package/dist/routes/admin-access-log.d.ts +2 -9
- package/dist/routes/admin-backends.d.ts +2 -9
- package/dist/routes/admin-cluster-name.d.ts +2 -8
- package/dist/routes/admin-cluster-settings.d.ts +2 -8
- package/dist/routes/admin-config.d.ts +0 -3
- package/dist/routes/admin-contexts.d.ts +3 -11
- package/dist/routes/admin-db.d.ts +2 -9
- package/dist/routes/admin-env.d.ts +33 -0
- package/dist/routes/admin-event-dlq.d.ts +2 -9
- package/dist/routes/admin-event-inspect.d.ts +2 -9
- package/dist/routes/admin-event-log.d.ts +2 -9
- package/dist/routes/admin-events.d.ts +3 -13
- package/dist/routes/admin-held-runs.d.ts +2 -8
- package/dist/routes/admin-list-limit.d.ts +3 -0
- package/dist/routes/admin-maintenance.d.ts +2 -9
- package/dist/routes/admin-orchestrator-drain.d.ts +2 -9
- package/dist/routes/admin-org-settings.d.ts +2 -8
- package/dist/routes/admin-orgs.d.ts +27 -0
- package/dist/routes/admin-peer-forget.d.ts +2 -9
- package/dist/routes/admin-queue-execution.d.ts +3 -10
- package/dist/routes/admin-registrations.d.ts +3 -14
- package/dist/routes/admin-runs.d.ts +3 -14
- package/dist/routes/admin-scaler-orphans.d.ts +2 -9
- package/dist/routes/admin-scaler-reload.d.ts +41 -0
- package/dist/routes/admin-scheduled-jobs.d.ts +2 -9
- package/dist/routes/admin-signing-keys.d.ts +2 -9
- package/dist/routes/admin-sources.d.ts +28 -32
- package/dist/routes/admin-trust-policy.d.ts +2 -8
- package/dist/routes/admin.d.ts +29 -30
- package/dist/routes/cluster-join.d.ts +13 -0
- package/dist/routes/github-webhook.d.ts +13 -11
- package/dist/scaler/bare-metal-backend.d.ts +42 -1
- package/dist/scaler/config.d.ts +12 -84
- package/dist/scaler/file-reload.d.ts +48 -0
- package/dist/scaler/manager.d.ts +47 -33
- package/dist/scaler/scaler-state-store.d.ts +5 -0
- package/dist/scaler/types.d.ts +8 -9
- package/dist/secrets/job-secret-gate.d.ts +2 -5
- package/dist/secrets/secret-resolver.d.ts +2 -4
- package/dist/security/trust-policy-gate.d.ts +0 -4
- package/dist/server.d.ts +6 -17
- package/dist/server.js +10933 -10836
- package/dist/sources/source-manager.d.ts +0 -24
- package/dist/sources/webhook-url-resolvers.d.ts +60 -0
- package/dist/standalone.js +10457 -10060
- package/dist/worker/reroute-dispatch.d.ts +1 -0
- package/dist/worker/reroute-spawn-control.d.ts +1 -3
- package/dist/worker-core.d.ts +9 -2
- package/dist/ws/agent-handler.d.ts +3 -3
- package/dist/ws/dashboard-access.d.ts +57 -0
- package/dist/ws/dashboard-backends-handler.d.ts +1 -6
- package/dist/ws/dashboard-context-handler.d.ts +10 -15
- package/dist/ws/dashboard-fleet-handler.d.ts +4 -3
- package/dist/ws/dashboard-fleet-write-handler.d.ts +6 -5
- package/dist/ws/dashboard-global-workflows-handler.d.ts +1 -6
- package/dist/ws/dashboard-registrations-handler.d.ts +1 -7
- package/dist/ws/platform-client.d.ts +94 -327
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
- package/dist/cache/legacy-prefixes.d.ts +0 -19
- package/dist/cli/commands/agent-service/logs.d.ts +0 -14
- package/dist/cli/commands/agent-service/restart.d.ts +0 -12
- package/dist/cli/commands/agent-service/start.d.ts +0 -12
- package/dist/cli/commands/agent-service/stop.d.ts +0 -12
- package/dist/cli/commands/agent-service/uninstall.d.ts +0 -13
- package/dist/cli/commands/cache.d.ts +0 -51
- package/dist/cli/commands/orchestrator-service/logs.d.ts +0 -14
- package/dist/cli/commands/orchestrator-service/restart.d.ts +0 -12
- package/dist/cli/commands/orchestrator-service/start.d.ts +0 -12
- package/dist/cli/commands/orchestrator-service/stop.d.ts +0 -12
- package/dist/cli/commands/orchestrator-service/uninstall.d.ts +0 -13
- package/dist/git/inherited-secret.d.ts +0 -29
|
@@ -7,16 +7,38 @@
|
|
|
7
7
|
* - Periodic heartbeat (30s default)
|
|
8
8
|
* - Intentional disconnect flag to prevent reconnection
|
|
9
9
|
*
|
|
10
|
-
* Authentication
|
|
11
|
-
* or
|
|
10
|
+
* Authentication is mutual-v2: after the ECDH key exchange the client proves
|
|
11
|
+
* its credential (or, on first join, its join token) with an HMAC over the
|
|
12
|
+
* handshake transcript, and accepts nothing until the server proves it holds
|
|
13
|
+
* the same credential or token. The join token never leaves this host. Every
|
|
14
|
+
* frame after acceptance uses an application key that needs the shared secret.
|
|
15
|
+
* A server that does not offer mutual-v2 gets no proof at all.
|
|
12
16
|
*/
|
|
13
|
-
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerScalerOrphansRequest, type PeerForgetRequest, type PeerForgetResponse, type PeerScalerOrphansResponse, type PeerClusterSettingsRequest, type PeerClusterSettingsResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
14
|
-
import type
|
|
17
|
+
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerScalerOrphansRequest, type PeerForgetRequest, type PeerForgetResponse, type PeerScalerOrphansResponse, type PeerClusterSettingsRequest, type PeerClusterSettingsResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries, type PeerScalerReloadRequest, type PeerScalerReloadResponse } from '@kici-dev/engine';
|
|
18
|
+
import { type PeerRegistry } from './peer-registry.js';
|
|
15
19
|
import { type PEER_FORGET_TIMEOUT, type PeerForgetRequestHandler } from './peer-forget.js';
|
|
16
20
|
import { type SCALER_ORPHANS_TIMEOUT, type ScalerOrphansRequestHandler } from './scaler-orphans-peer.js';
|
|
21
|
+
import type { PEER_REQUEST_TIMEOUT } from './peer-request-waiters.js';
|
|
22
|
+
import type { ScalerFileReload } from '../scaler/file-reload.js';
|
|
17
23
|
import type { PeerAuthCoordinator } from './peer-auth-coordinator.js';
|
|
18
24
|
/** Logged when this instance has neither a credential file nor a join token. */
|
|
19
25
|
export declare const NO_AUTH_METHOD_MESSAGE = "No peer auth method: this instance has no credential file and no join token";
|
|
26
|
+
/** Logged when a dialled server does not offer mutual authentication. */
|
|
27
|
+
export declare const PEER_MUTUAL_AUTH_UNSUPPORTED_MESSAGE = "Peer does not support mutual authentication; upgrade every orchestrator in the cluster";
|
|
28
|
+
/** Where the address this client dials came from. */
|
|
29
|
+
export declare enum PeerDialOrigin {
|
|
30
|
+
/** KICI_CLUSTER_PEERS or a worker's coordinator URLs: reconnects with backoff. */
|
|
31
|
+
Static = "static",
|
|
32
|
+
/** A Platform announcement: a hint, dropped for good when mutual authentication fails. */
|
|
33
|
+
Discovered = "discovered"
|
|
34
|
+
}
|
|
35
|
+
/** Why a server failed mutual authentication. */
|
|
36
|
+
export declare enum PeerMutualAuthFailure {
|
|
37
|
+
SchemeMissing = "scheme-missing",
|
|
38
|
+
ServerProofInvalid = "server-proof-invalid",
|
|
39
|
+
InstanceIdMissing = "instance-id-missing",
|
|
40
|
+
InstanceIdMismatch = "instance-id-mismatch"
|
|
41
|
+
}
|
|
20
42
|
type PeerConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'authenticating' | 'connected';
|
|
21
43
|
export interface PeerClientOptions {
|
|
22
44
|
/** WebSocket URL of the remote peer orchestrator. */
|
|
@@ -31,6 +53,21 @@ export interface PeerClientOptions {
|
|
|
31
53
|
instanceId: string;
|
|
32
54
|
/** Peer registry to update on heartbeats from remote peer. */
|
|
33
55
|
peerRegistry: PeerRegistry;
|
|
56
|
+
/**
|
|
57
|
+
* Where the dialled address came from. A static target reconnects with
|
|
58
|
+
* backoff after any failure; a discovered target that fails mutual
|
|
59
|
+
* authentication is dropped. Default: static.
|
|
60
|
+
*/
|
|
61
|
+
origin?: PeerDialOrigin;
|
|
62
|
+
/** The instance id a discovered target was announced with; the server must report it. */
|
|
63
|
+
expectedInstanceId?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Closes a connection that has not completed authentication within this
|
|
66
|
+
* many ms, after which the normal reconnect policy applies. Default: 15000.
|
|
67
|
+
*/
|
|
68
|
+
handshakeTimeoutMs?: number;
|
|
69
|
+
/** Called when a discovered target fails mutual authentication; the client then stays down. */
|
|
70
|
+
onMutualAuthFailed?: (failure: PeerMutualAuthFailure) => void;
|
|
34
71
|
/** Callback to get this orchestrator's local agent inventory for heartbeats. */
|
|
35
72
|
getLocalInventory: () => Omit<PeerHeartbeat, 'type'>;
|
|
36
73
|
/** Heartbeat interval in ms. Default: 30000 (30s). */
|
|
@@ -105,6 +142,12 @@ export interface PeerClientOptions {
|
|
|
105
142
|
* requests are answered with ok=false.
|
|
106
143
|
*/
|
|
107
144
|
onScalerOrphansRequest?: ScalerOrphansRequestHandler;
|
|
145
|
+
/**
|
|
146
|
+
* Reloads this node's scaler config for a peer.scaler.reload.request the
|
|
147
|
+
* connected coordinator sent (`kici-admin scaler reload`). If undefined,
|
|
148
|
+
* requests are answered `rejected`.
|
|
149
|
+
*/
|
|
150
|
+
onScalerReloadRequest?: ScalerFileReload;
|
|
108
151
|
/**
|
|
109
152
|
* Forgets a departed peer the connected coordinator forgot (`kici-admin peer
|
|
110
153
|
* forget`). If undefined, requests are answered with outcome `error`.
|
|
@@ -127,11 +170,18 @@ export declare class PeerClient {
|
|
|
127
170
|
private noAuthWarned;
|
|
128
171
|
private intentionalDisconnect;
|
|
129
172
|
private _targetInstanceId;
|
|
173
|
+
/** The key the current frames use: K_hs while authenticating, K_app once connected. */
|
|
130
174
|
private sessionKey;
|
|
175
|
+
/** The handshake in flight; cleared on acceptance or close. */
|
|
176
|
+
private handshake;
|
|
177
|
+
/** The proof this connection sent, until the server's proof is checked. */
|
|
178
|
+
private pendingProof;
|
|
179
|
+
private handshakeTimer;
|
|
131
180
|
private readonly ackWaiters;
|
|
132
181
|
private readonly cacheWaiters;
|
|
133
182
|
private readonly configReloadWaiters;
|
|
134
183
|
private readonly scalerOrphansWaiters;
|
|
184
|
+
private readonly scalerReloadWaiters;
|
|
135
185
|
private readonly peerForgetWaiters;
|
|
136
186
|
private readonly clusterSettingsWaiters;
|
|
137
187
|
/** Correlates peer.logs.collect.request with the peer's chunked subtree response. */
|
|
@@ -142,11 +192,15 @@ export declare class PeerClient {
|
|
|
142
192
|
private isJoiner;
|
|
143
193
|
/** complete() callback for the in-flight token-join, if this client is joiner. */
|
|
144
194
|
private joinComplete;
|
|
145
|
-
/** The credential string this client last built
|
|
195
|
+
/** The credential string this client last built a proof with. */
|
|
146
196
|
private lastProvedCredential;
|
|
147
197
|
private readonly instanceId;
|
|
148
198
|
private readonly role;
|
|
149
199
|
private readonly peerRegistry;
|
|
200
|
+
private readonly origin;
|
|
201
|
+
private readonly expectedInstanceId?;
|
|
202
|
+
private readonly handshakeTimeoutMs;
|
|
203
|
+
private readonly onMutualAuthFailed?;
|
|
150
204
|
private readonly getLocalInventory;
|
|
151
205
|
private readonly heartbeatIntervalMs;
|
|
152
206
|
private readonly maxReconnectDelayMs;
|
|
@@ -166,6 +220,7 @@ export declare class PeerClient {
|
|
|
166
220
|
private readonly onAuthenticated?;
|
|
167
221
|
private readonly onLogsCollectRequest?;
|
|
168
222
|
private readonly onScalerOrphansRequest?;
|
|
223
|
+
private readonly onScalerReloadRequest?;
|
|
169
224
|
private readonly onPeerForgetRequest?;
|
|
170
225
|
constructor(options: PeerClientOptions);
|
|
171
226
|
/** Current connection state. */
|
|
@@ -221,6 +276,14 @@ export declare class PeerClient {
|
|
|
221
276
|
* response arrived within `timeoutMs`.
|
|
222
277
|
*/
|
|
223
278
|
sendScalerOrphansAndWait(msg: PeerScalerOrphansRequest, timeoutMs: number): Promise<PeerScalerOrphansResponse | null | typeof SCALER_ORPHANS_TIMEOUT>;
|
|
279
|
+
/**
|
|
280
|
+
* Send a peer.scaler.reload.request to the connected peer and wait for the
|
|
281
|
+
* matching response.
|
|
282
|
+
*
|
|
283
|
+
* @returns the response; null when not connected; `'timeout'` when no
|
|
284
|
+
* response arrived within `timeoutMs`.
|
|
285
|
+
*/
|
|
286
|
+
sendScalerReloadAndWait(msg: PeerScalerReloadRequest, timeoutMs: number): Promise<PeerScalerReloadResponse | null | typeof PEER_REQUEST_TIMEOUT>;
|
|
224
287
|
/**
|
|
225
288
|
* Send a peer.clusterSettings.request to the connected coordinator and wait for
|
|
226
289
|
* the matching peer.clusterSettings.response carrying the worker-settings snapshot.
|
|
@@ -248,6 +311,29 @@ export declare class PeerClient {
|
|
|
248
311
|
getReconnectDelay(): number;
|
|
249
312
|
private doConnect;
|
|
250
313
|
private handleMessage;
|
|
314
|
+
private armHandshakeTimer;
|
|
315
|
+
private clearHandshakeTimer;
|
|
316
|
+
/** A token join in flight must be released so sibling clients stop waiting on it. */
|
|
317
|
+
private releaseJoin;
|
|
318
|
+
/** Forget the handshake of a connection that closed or is being torn down. */
|
|
319
|
+
private resetHandshake;
|
|
320
|
+
/** Close a connection whose handshake broke the protocol. */
|
|
321
|
+
private abortHandshake;
|
|
322
|
+
/** Close a connection whose server failed mutual authentication. */
|
|
323
|
+
private failMutualAuth;
|
|
324
|
+
/** The server's plaintext peer.hello: refuse it unless it offers mutual-v2. */
|
|
325
|
+
private handleHello;
|
|
326
|
+
/**
|
|
327
|
+
* The encrypted peer.auth.response. Any frame that is not one closes the
|
|
328
|
+
* socket. An acceptance is checked synchronously, before any state change,
|
|
329
|
+
* registry write or callback, so a frame pipelined behind an unproven
|
|
330
|
+
* acceptance meets a client that is not connected.
|
|
331
|
+
*/
|
|
332
|
+
private handleAuthResponse;
|
|
333
|
+
/** Returns why the acceptance fails mutual authentication, or null when it passes. */
|
|
334
|
+
private checkAcceptance;
|
|
335
|
+
/** The server proved itself: register the peer and start the connected session. */
|
|
336
|
+
private completeAcceptance;
|
|
251
337
|
/**
|
|
252
338
|
* Handle a rejected peer.auth.response. For the three credential-divergence
|
|
253
339
|
* reasons, delegate to the coordinator (which deletes the shared file only if
|
|
@@ -274,6 +360,14 @@ export declare class PeerClient {
|
|
|
274
360
|
* client never reads or writes the credential file directly.
|
|
275
361
|
*/
|
|
276
362
|
private sendAuthRequest;
|
|
363
|
+
/**
|
|
364
|
+
* Route the operator-relay pairs (`scaler orphans --target`, `peer forget`,
|
|
365
|
+
* `scaler reload`): answer a request from the coordinator this client
|
|
366
|
+
* dialled, or hand a response to the request waiting for it.
|
|
367
|
+
*
|
|
368
|
+
* @returns whether `msg` was one of them.
|
|
369
|
+
*/
|
|
370
|
+
private routeOperatorRelay;
|
|
277
371
|
private routeMessage;
|
|
278
372
|
private startHeartbeat;
|
|
279
373
|
private stopHeartbeat;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ECDH key exchange and session encryption for
|
|
2
|
+
* ECDH key exchange, mutual-v2 authentication and session encryption for
|
|
3
|
+
* peer-to-peer channels.
|
|
3
4
|
*
|
|
4
5
|
* Uses X25519 for key agreement and AES-256-GCM for message encryption.
|
|
5
|
-
*
|
|
6
|
-
* and
|
|
6
|
+
* The handshake key (K_hs) is derived via HKDF-SHA256 from the ECDH shared
|
|
7
|
+
* secret and the server nonce; it protects the authentication exchange only.
|
|
8
|
+
*
|
|
9
|
+
* mutual-v2: both sides hash the handshake transcript (TH: both ephemeral keys,
|
|
10
|
+
* the nonce and the advertised schemes). The client proves the shared key (PSK:
|
|
11
|
+
* sha256 of the peer credential, or the join token hash) with an HMAC over TH,
|
|
12
|
+
* and the server answers with its own HMAC over TH and the client proof. Every
|
|
13
|
+
* frame after acceptance uses the application key (K_app), derived from K_hs
|
|
14
|
+
* and the PSK, so a side that skipped the proof check cannot read or write them.
|
|
15
|
+
* Every field is length-prefixed, so field boundaries are unambiguous.
|
|
7
16
|
*
|
|
8
17
|
* Wire format for encrypted messages (base64 encoded):
|
|
9
18
|
* IV (12 bytes) || AuthTag (16 bytes) || Ciphertext
|
|
@@ -54,4 +63,52 @@ export declare function encryptMessage(plaintext: string, sessionKey: Buffer): s
|
|
|
54
63
|
* @throws If the session key is wrong or the ciphertext has been tampered with
|
|
55
64
|
*/
|
|
56
65
|
export declare function decryptMessage(encrypted: string, sessionKey: Buffer): string;
|
|
66
|
+
/** Length of the server nonce in `peer.hello`. */
|
|
67
|
+
export declare const HANDSHAKE_NONCE_BYTES = 32;
|
|
68
|
+
/** Length of a mutual-v2 proof (HMAC-SHA256). */
|
|
69
|
+
export declare const PEER_PROOF_BYTES = 32;
|
|
70
|
+
/** Encode each field as a 4-byte big-endian length followed by its bytes. */
|
|
71
|
+
export declare function lengthPrefixed(...fields: Array<Buffer | string>): Buffer;
|
|
72
|
+
export interface PeerTranscriptInput {
|
|
73
|
+
serverEphemeralPublicKey: Buffer;
|
|
74
|
+
serverNonce: Buffer;
|
|
75
|
+
/** The schemes exactly as the server advertised them, unknown ones included. */
|
|
76
|
+
authSchemes: readonly string[];
|
|
77
|
+
clientEphemeralPublicKey: Buffer;
|
|
78
|
+
}
|
|
79
|
+
/** TH: binds both ephemeral keys, the nonce and the advertised schemes. */
|
|
80
|
+
export declare function peerTranscriptHash(input: PeerTranscriptInput): Buffer;
|
|
81
|
+
export interface ClientProofInput {
|
|
82
|
+
psk: Buffer;
|
|
83
|
+
transcriptHash: Buffer;
|
|
84
|
+
mode: string;
|
|
85
|
+
clientInstanceId: string;
|
|
86
|
+
role: string;
|
|
87
|
+
protocolVersion: number;
|
|
88
|
+
/** The join token's routing segment in token mode; empty in credential mode. */
|
|
89
|
+
tokenRouting: string;
|
|
90
|
+
}
|
|
91
|
+
export declare function computeClientProof(input: ClientProofInput): Buffer;
|
|
92
|
+
export interface ServerProofInput {
|
|
93
|
+
psk: Buffer;
|
|
94
|
+
transcriptHash: Buffer;
|
|
95
|
+
clientProof: Buffer;
|
|
96
|
+
serverInstanceId: string;
|
|
97
|
+
grantedRole: string;
|
|
98
|
+
/** The credential the server issues in this response, or null when it issues none. */
|
|
99
|
+
sessionCredential: string | null;
|
|
100
|
+
}
|
|
101
|
+
export declare function computeServerProof(input: ServerProofInput): Buffer;
|
|
102
|
+
/** A proof as sent on the wire (64 hex characters), or null when malformed. */
|
|
103
|
+
export declare function decodeProof(hex: string): Buffer | null;
|
|
104
|
+
/** Constant-time comparison; a length mismatch is a mismatch, never a throw. */
|
|
105
|
+
export declare function proofMatches(expected: Buffer, presented: Buffer): boolean;
|
|
106
|
+
/** K_app: protects every frame after acceptance, and needs the PSK as well as K_hs. */
|
|
107
|
+
export declare function deriveAppKey(input: {
|
|
108
|
+
handshakeKey: Buffer;
|
|
109
|
+
psk: Buffer;
|
|
110
|
+
transcriptHash: Buffer;
|
|
111
|
+
clientProof: Buffer;
|
|
112
|
+
serverProof: Buffer;
|
|
113
|
+
}): Buffer;
|
|
57
114
|
//# sourceMappingURL=peer-crypto.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { OrchRole } from '@kici-dev/engine';
|
|
2
|
+
import { PeerDiscoveryMode } from '../config/schema.js';
|
|
3
|
+
/** Discovered clients plus pending candidates, far above any real coordinator count. */
|
|
4
|
+
export declare const MAX_DISCOVERED_PEER_CLIENTS = 64;
|
|
5
|
+
/** A discovered client not connected for this long is dropped; a returning peer is announced again. */
|
|
6
|
+
export declare const DISCOVERED_PEER_IDLE_PRUNE_MS: number;
|
|
7
|
+
/** What discovery needs from a peer client. */
|
|
8
|
+
export interface DiscoveryPeerClient {
|
|
9
|
+
readonly state: string;
|
|
10
|
+
connect(): void;
|
|
11
|
+
disconnect(): void;
|
|
12
|
+
}
|
|
13
|
+
/** Hooks a discovered client calls back. */
|
|
14
|
+
export interface DiscoveredClientHooks {
|
|
15
|
+
/** Mutual authentication succeeded with the announced instance id. */
|
|
16
|
+
onAuthenticated: () => void;
|
|
17
|
+
/** Mutual authentication failed; the client closed and will not reconnect. */
|
|
18
|
+
onMutualAuthFailed: () => void;
|
|
19
|
+
}
|
|
20
|
+
/** One peer the Platform announced. */
|
|
21
|
+
export interface PeerAnnouncement {
|
|
22
|
+
instanceId?: string;
|
|
23
|
+
address?: string | null;
|
|
24
|
+
orchRole?: OrchRole;
|
|
25
|
+
}
|
|
26
|
+
export interface PeerDiscoveryDeps {
|
|
27
|
+
mode: PeerDiscoveryMode;
|
|
28
|
+
selfInstanceId: string;
|
|
29
|
+
/** Every outbound peer client. Static clients sit under their URL until they authenticate. */
|
|
30
|
+
peerClients: Map<string, DiscoveryPeerClient>;
|
|
31
|
+
createClient: (address: string, expectedInstanceId: string, hooks: DiscoveredClientHooks) => DiscoveryPeerClient;
|
|
32
|
+
/** Minimum time between dials of an address whose last discovered dial failed mutual authentication. */
|
|
33
|
+
redialAfterFailureMs: number;
|
|
34
|
+
maxClients?: number;
|
|
35
|
+
idlePruneMs?: number;
|
|
36
|
+
now?: () => number;
|
|
37
|
+
}
|
|
38
|
+
export declare class PeerDiscovery {
|
|
39
|
+
private readonly deps;
|
|
40
|
+
private readonly discovered;
|
|
41
|
+
private readonly candidates;
|
|
42
|
+
/** Address -> when its last discovered dial failed mutual authentication. */
|
|
43
|
+
private readonly failedAt;
|
|
44
|
+
private readonly maxClients;
|
|
45
|
+
private readonly idlePruneMs;
|
|
46
|
+
private readonly now;
|
|
47
|
+
private timer;
|
|
48
|
+
constructor(deps: PeerDiscoveryDeps);
|
|
49
|
+
announce(peer: PeerAnnouncement): void;
|
|
50
|
+
/** A static client authenticated as this peer: discovery lets go of it. */
|
|
51
|
+
releaseToStatic(instanceId: string): void;
|
|
52
|
+
/** Drop discovered clients and candidates that stayed unconnected for the idle window. */
|
|
53
|
+
pruneIdle(): void;
|
|
54
|
+
start(): void;
|
|
55
|
+
stop(): void;
|
|
56
|
+
private size;
|
|
57
|
+
private dialDiscovered;
|
|
58
|
+
private dialCandidate;
|
|
59
|
+
/**
|
|
60
|
+
* The candidate authenticated. It runs before the candidate registers the
|
|
61
|
+
* peer, so the replaced client disconnects first.
|
|
62
|
+
*/
|
|
63
|
+
private promote;
|
|
64
|
+
private drop;
|
|
65
|
+
private dropCandidate;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=peer-discovery.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PeerForgetOutcome, type PeerForgetRequest, type PeerForgetResponse } from '@kici-dev/engine';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { PeerRegistry } from './peer-registry.js';
|
|
4
|
+
import { PeerRequestWaiters } from './peer-request-waiters.js';
|
|
4
5
|
/** What forgetting did on one coordinator. */
|
|
5
6
|
export interface PeerForgetResult {
|
|
6
7
|
/** The coordinator that answered. */
|
|
@@ -125,14 +126,8 @@ export declare function forgetAcrossCoordinators(registry: PeerRegistry, selfIns
|
|
|
125
126
|
* link to: registered coordinators whose connection is down.
|
|
126
127
|
*/
|
|
127
128
|
export declare function disconnectedCoordinatorIds(registry: PeerRegistry, selfInstanceId: string): string[];
|
|
128
|
-
/** Pending forget requests sent to siblings
|
|
129
|
-
export declare class PeerForgetWaiters {
|
|
130
|
-
|
|
131
|
-
/** Wait for the response to `messageId`, or {@link PEER_FORGET_TIMEOUT}. */
|
|
132
|
-
wait(messageId: string, timeoutMs: number): Promise<PeerForgetResponse | typeof PEER_FORGET_TIMEOUT>;
|
|
133
|
-
/** Hand a response to the request waiting for it. An unknown id is dropped. */
|
|
134
|
-
resolve(response: PeerForgetResponse): void;
|
|
135
|
-
/** End every pending wait with an `error` response naming `reason`. */
|
|
136
|
-
rejectAll(reason: string): void;
|
|
129
|
+
/** Pending forget requests sent to siblings; a close answers each `error` naming the reason. */
|
|
130
|
+
export declare class PeerForgetWaiters extends PeerRequestWaiters<PeerForgetResponse> {
|
|
131
|
+
constructor();
|
|
137
132
|
}
|
|
138
133
|
//# sourceMappingURL=peer-forget.d.ts.map
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WebSocket handler for incoming peer connections from other orchestrators.
|
|
3
3
|
*
|
|
4
|
-
* Accepts WS upgrade, performs ECDH key exchange
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Accepts WS upgrade, performs ECDH key exchange, and authenticates the peer
|
|
5
|
+
* with mutual-v2: `peer.hello` advertises the scheme, the peer proves its
|
|
6
|
+
* credential (or, on first join, its join token) with an HMAC over the
|
|
7
|
+
* handshake transcript, and this server answers with its own proof and
|
|
8
|
+
* switches to the application key. A join token never arrives here: the peer
|
|
9
|
+
* sends the token's routing segment, and the role and routing key come from
|
|
10
|
+
* the join_tokens row. A request in the scheme earlier releases used is refused
|
|
11
|
+
* with PEER_MUTUAL_AUTH_REQUIRED_REASON. Registers the peer in PeerRegistry and
|
|
12
|
+
* routes messages bidirectionally. Sends periodic heartbeats to the peer.
|
|
8
13
|
*/
|
|
9
|
-
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerScalerEvent, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerScalerOrphansRequest, type PeerForgetRequest, type PeerForgetResponse, type PeerScalerOrphansResponse, type PeerClusterSettingsRequest, type WorkerClusterSettings, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
10
|
-
import type
|
|
14
|
+
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerScalerEvent, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerScalerOrphansRequest, type PeerForgetRequest, type PeerForgetResponse, type PeerScalerOrphansResponse, type PeerClusterSettingsRequest, type WorkerClusterSettings, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries, type PeerScalerReloadRequest, type PeerScalerReloadResponse } from '@kici-dev/engine';
|
|
15
|
+
import { type PeerRegistry } from './peer-registry.js';
|
|
11
16
|
import { type PEER_FORGET_TIMEOUT, type PeerForgetRequestHandler } from './peer-forget.js';
|
|
12
17
|
import { type SCALER_ORPHANS_TIMEOUT, type ScalerOrphansRequestHandler } from './scaler-orphans-peer.js';
|
|
18
|
+
import type { PEER_REQUEST_TIMEOUT } from './peer-request-waiters.js';
|
|
19
|
+
import type { ScalerFileReload } from '../scaler/file-reload.js';
|
|
13
20
|
import type { PeerCredentialStore } from './peer-credentials.js';
|
|
14
21
|
import { type JoinTokenManager } from './join-token.js';
|
|
15
22
|
/**
|
|
@@ -113,6 +120,13 @@ export interface PeerHandlerDeps {
|
|
|
113
120
|
* requests are answered with ok=false.
|
|
114
121
|
*/
|
|
115
122
|
onScalerOrphansRequest?: ScalerOrphansRequestHandler;
|
|
123
|
+
/**
|
|
124
|
+
* Reloads this node's scaler config for a peer.scaler.reload.request
|
|
125
|
+
* (`kici-admin scaler reload`). A request from a peer that is not a
|
|
126
|
+
* coordinator is refused before it reaches the handler. If undefined,
|
|
127
|
+
* requests are answered `rejected`.
|
|
128
|
+
*/
|
|
129
|
+
onScalerReloadRequest?: ScalerFileReload;
|
|
116
130
|
/**
|
|
117
131
|
* Forgets a departed peer a sibling coordinator forgot (`kici-admin peer
|
|
118
132
|
* forget`). A request from a peer that is not a coordinator is refused. If
|
|
@@ -120,6 +134,13 @@ export interface PeerHandlerDeps {
|
|
|
120
134
|
*/
|
|
121
135
|
onPeerForgetRequest?: PeerForgetRequestHandler;
|
|
122
136
|
}
|
|
137
|
+
/** The schemes this server lists in peer.hello, in order. */
|
|
138
|
+
export declare const ADVERTISED_PEER_AUTH_SCHEMES: readonly string[];
|
|
139
|
+
/**
|
|
140
|
+
* A peer.auth.request from a release before mutual authentication: it carries
|
|
141
|
+
* a proof or a token, or names no scheme.
|
|
142
|
+
*/
|
|
143
|
+
export declare function isLegacyPeerAuthRequest(raw: unknown): boolean;
|
|
123
144
|
/**
|
|
124
145
|
* Create a handler function for incoming peer WebSocket connections.
|
|
125
146
|
*
|
|
@@ -143,6 +164,7 @@ export declare function createPeerHandler(deps: PeerHandlerDeps): {
|
|
|
143
164
|
sendAndWaitAck: (targetInstanceId: string, msg: JobReroute, timeoutMs?: number) => Promise<boolean>;
|
|
144
165
|
sendConfigReloadAndWait: (targetInstanceId: string, msg: PeerConfigReload, timeoutMs?: number) => Promise<PeerConfigReloadResponse | null>;
|
|
145
166
|
sendScalerOrphansAndWait: (targetInstanceId: string, msg: PeerScalerOrphansRequest, timeoutMs: number) => Promise<PeerScalerOrphansResponse | null | typeof SCALER_ORPHANS_TIMEOUT>;
|
|
167
|
+
sendScalerReloadAndWait: (targetInstanceId: string, msg: PeerScalerReloadRequest, timeoutMs: number) => Promise<PeerScalerReloadResponse | null | typeof PEER_REQUEST_TIMEOUT>;
|
|
146
168
|
sendPeerForgetAndWait: (targetInstanceId: string, msg: PeerForgetRequest, timeoutMs: number) => Promise<PeerForgetResponse | null | typeof PEER_FORGET_TIMEOUT>;
|
|
147
169
|
sendLogsCollectAndWait: (targetInstanceId: string, msg: PeerLogsCollectRequest, timeoutMs: number) => Promise<Buffer>;
|
|
148
170
|
getConnectionCount: () => number;
|
|
@@ -5,7 +5,17 @@
|
|
|
5
5
|
* enabling routing decisions without real-time queries. Follows the
|
|
6
6
|
* same label-matching semantics as agent/registry.ts for consistency.
|
|
7
7
|
*/
|
|
8
|
-
import type { PeerHeartbeat, PeerCapabilities, ScalerCapacitySummary } from '@kici-dev/engine';
|
|
8
|
+
import type { PeerAuthScheme, PeerHeartbeat, PeerCapabilities, ScalerCapacitySummary } from '@kici-dev/engine';
|
|
9
|
+
/** Which side of a peer link authenticated: the peer dialled us, or we dialled it. */
|
|
10
|
+
export declare enum PeerLinkDirection {
|
|
11
|
+
Inbound = "inbound",
|
|
12
|
+
Outbound = "outbound"
|
|
13
|
+
}
|
|
14
|
+
/** The scheme each direction of a peer link authenticated with; null while that direction is down. */
|
|
15
|
+
export interface PeerAuthSchemes {
|
|
16
|
+
inbound: PeerAuthScheme | null;
|
|
17
|
+
outbound: PeerAuthScheme | null;
|
|
18
|
+
}
|
|
9
19
|
export interface PeerAgentInfo {
|
|
10
20
|
agentId: string;
|
|
11
21
|
labels: string[];
|
|
@@ -52,6 +62,8 @@ export interface PeerInfo {
|
|
|
52
62
|
clusterSettingsVersion: number;
|
|
53
63
|
/** Cluster role of the peer. */
|
|
54
64
|
role: 'coordinator' | 'worker';
|
|
65
|
+
/** The authentication scheme of each link direction. */
|
|
66
|
+
authScheme: PeerAuthSchemes;
|
|
55
67
|
hostname?: string;
|
|
56
68
|
osRelease?: string;
|
|
57
69
|
totalMemoryMb?: number;
|
|
@@ -119,6 +131,8 @@ export declare class PeerRegistry {
|
|
|
119
131
|
routingKeys: string[];
|
|
120
132
|
role?: 'coordinator' | 'worker';
|
|
121
133
|
}): void;
|
|
134
|
+
/** Record the scheme one direction of the link authenticated with, or null when it closed. */
|
|
135
|
+
setAuthScheme(instanceId: string, direction: PeerLinkDirection, scheme: PeerAuthScheme | null): void;
|
|
122
136
|
/**
|
|
123
137
|
* Remove a peer entirely from the registry.
|
|
124
138
|
*/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correlates a peer request with its response by message id. Shared by every
|
|
3
|
+
* request/response pair the outgoing `PeerClient` and the incoming peer handler
|
|
4
|
+
* carry, so each pair needs only its response type and its close-time answer.
|
|
5
|
+
*/
|
|
6
|
+
/** A wait that ended without a response. */
|
|
7
|
+
export declare const PEER_REQUEST_TIMEOUT = "timeout";
|
|
8
|
+
/** Pending peer requests of one message pair, keyed by message id. */
|
|
9
|
+
export declare class PeerRequestWaiters<TResponse extends {
|
|
10
|
+
messageId: string;
|
|
11
|
+
}> {
|
|
12
|
+
private readonly closedResponse;
|
|
13
|
+
private readonly waiters;
|
|
14
|
+
/**
|
|
15
|
+
* @param closedResponse the answer each pending wait gets when the connection
|
|
16
|
+
* closes, built from its message id and the close reason.
|
|
17
|
+
*/
|
|
18
|
+
constructor(closedResponse: (messageId: string, reason: string) => TResponse);
|
|
19
|
+
/** Wait for the response to `messageId`, or {@link PEER_REQUEST_TIMEOUT}. */
|
|
20
|
+
wait(messageId: string, timeoutMs: number): Promise<TResponse | typeof PEER_REQUEST_TIMEOUT>;
|
|
21
|
+
/** Hand a response to the request waiting for it. An unknown id is dropped. */
|
|
22
|
+
resolve(response: TResponse): void;
|
|
23
|
+
/** End every pending wait with the close-time answer naming `reason`. */
|
|
24
|
+
rejectAll(reason: string): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=peer-request-waiters.d.ts.map
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import type { PeerScalerOrphansRequest, PeerScalerOrphansResponse } from '@kici-dev/engine';
|
|
9
9
|
import type { ScalerOrphansAnswer } from '../scaler/orphan-requests.js';
|
|
10
|
+
import { PeerRequestWaiters } from './peer-request-waiters.js';
|
|
10
11
|
/** Answers a forwarded request from this node's own host and tracking. */
|
|
11
12
|
export type ScalerOrphansRequestHandler = (msg: PeerScalerOrphansRequest) => Promise<ScalerOrphansAnswer>;
|
|
12
13
|
/** The answer of a peer that has no handler wired. */
|
|
@@ -27,14 +28,8 @@ export declare const SCALER_ORPHANS_TIMEOUT: "timeout";
|
|
|
27
28
|
* out its timeout for a peer that cannot answer.
|
|
28
29
|
*/
|
|
29
30
|
export declare function replyToScalerOrphansRequest(msg: PeerScalerOrphansRequest, handler: ScalerOrphansRequestHandler | undefined, send: (response: PeerScalerOrphansResponse) => void, logFields?: Record<string, unknown>): void;
|
|
30
|
-
/** Pending scaler orphan requests
|
|
31
|
-
export declare class ScalerOrphansWaiters {
|
|
32
|
-
|
|
33
|
-
/** Wait for the response to `messageId`, or {@link SCALER_ORPHANS_TIMEOUT}. */
|
|
34
|
-
wait(messageId: string, timeoutMs: number): Promise<PeerScalerOrphansResponse | typeof SCALER_ORPHANS_TIMEOUT>;
|
|
35
|
-
/** Hand a response to the request waiting for it. An unknown id is dropped. */
|
|
36
|
-
resolve(response: PeerScalerOrphansResponse): void;
|
|
37
|
-
/** End every pending wait with an `ok: false` response naming `reason`. */
|
|
38
|
-
rejectAll(reason: string): void;
|
|
31
|
+
/** Pending scaler orphan requests; a close answers each `ok: false` naming the reason. */
|
|
32
|
+
export declare class ScalerOrphansWaiters extends PeerRequestWaiters<PeerScalerOrphansResponse> {
|
|
33
|
+
constructor();
|
|
39
34
|
}
|
|
40
35
|
//# sourceMappingURL=scaler-orphans-peer.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type PeerScalerReloadRequest, type PeerScalerReloadResponse, type ScalerReloadInstanceResult } from '@kici-dev/engine';
|
|
2
|
+
import type { ScalerFileReload } from '../scaler/file-reload.js';
|
|
3
|
+
import type { PeerRegistry } from './peer-registry.js';
|
|
4
|
+
import { PEER_REQUEST_TIMEOUT } from './peer-request-waiters.js';
|
|
5
|
+
/**
|
|
6
|
+
* Sends one scaler reload request to a peer and waits for its answer. Resolves
|
|
7
|
+
* null when no connection reached the peer, and {@link PEER_REQUEST_TIMEOUT}
|
|
8
|
+
* when it did not answer within `timeoutMs`.
|
|
9
|
+
*/
|
|
10
|
+
export type SendScalerReload = (instanceId: string, msg: PeerScalerReloadRequest, timeoutMs: number) => Promise<PeerScalerReloadResponse | null | typeof PEER_REQUEST_TIMEOUT>;
|
|
11
|
+
export interface ScalerReloadAcrossClusterOptions {
|
|
12
|
+
selfInstanceId: string;
|
|
13
|
+
selfRole: 'coordinator' | 'worker';
|
|
14
|
+
registry: Pick<PeerRegistry, 'getAllPeers'>;
|
|
15
|
+
local: ScalerFileReload;
|
|
16
|
+
send: SendScalerReload;
|
|
17
|
+
/** Reload this orchestrator only. */
|
|
18
|
+
single: boolean;
|
|
19
|
+
/** How long to wait for each peer's answer. */
|
|
20
|
+
timeoutMs: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Reload this orchestrator and every peer in its registry. A connected peer
|
|
24
|
+
* (coordinator or worker) gets the request; a known peer that is not connected
|
|
25
|
+
* is reported `unreachable` without one. The local reload and the peer
|
|
26
|
+
* requests run at the same time; the first result is this orchestrator's.
|
|
27
|
+
*/
|
|
28
|
+
export declare function reloadScalersAcrossCluster(opts: ScalerReloadAcrossClusterOptions): Promise<ScalerReloadInstanceResult[]>;
|
|
29
|
+
//# sourceMappingURL=scaler-reload-fanout.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type PeerScalerReloadRequest, type PeerScalerReloadResponse } from '@kici-dev/engine';
|
|
2
|
+
import type { ScalerFileReload } from '../scaler/file-reload.js';
|
|
3
|
+
import { PeerRequestWaiters } from './peer-request-waiters.js';
|
|
4
|
+
/** The answer to a request a worker sent: a worker never directs another node's scalers. */
|
|
5
|
+
export declare const SCALER_RELOAD_COORDINATORS_ONLY_DETAIL = "scaler reload requests are accepted from coordinators only";
|
|
6
|
+
/**
|
|
7
|
+
* Answer one request through `send`. A missing handler and a handler failure
|
|
8
|
+
* are both answered `rejected`, so the requester never waits out its timeout
|
|
9
|
+
* for a peer that cannot answer.
|
|
10
|
+
*/
|
|
11
|
+
export declare function replyToScalerReloadRequest(msg: PeerScalerReloadRequest, handler: ScalerFileReload | undefined, send: (response: PeerScalerReloadResponse) => void, logFields?: Record<string, unknown>): void;
|
|
12
|
+
/** Pending scaler reload requests; a close answers each `unreachable` naming the reason. */
|
|
13
|
+
export declare class ScalerReloadWaiters extends PeerRequestWaiters<PeerScalerReloadResponse> {
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=scaler-reload-peer.d.ts.map
|
|
@@ -13,6 +13,7 @@ import type { ClusterSettingsReader } from './cluster-settings-reader.js';
|
|
|
13
13
|
export interface WorkerClusterSettingDefaults {
|
|
14
14
|
agentTokenTtlMs: number;
|
|
15
15
|
firecrackerApiSocketWaitMs: number;
|
|
16
|
+
concurrencyWaitTimeoutMs: number;
|
|
16
17
|
}
|
|
17
18
|
/** A worker snapshot and the `cluster_settings.version` its values belong to. */
|
|
18
19
|
export interface WorkerClusterSettingsSnapshot {
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* the hot tuple comparison `(created_at, id) < (cursor.createdAt, cursor.id)`.
|
|
15
15
|
* The cursor also carries a `source: 'hot' | 'cold'` discriminator so a
|
|
16
16
|
* cursor minted while the page boundary was inside cold can resume in
|
|
17
|
-
* the cold stream on the next request.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* the cold stream on the next request. A cursor without a valid `source` is
|
|
18
|
+
* invalid, like any other unparsable cursor, and the read starts from the
|
|
19
|
+
* first page.
|
|
20
20
|
*/
|
|
21
21
|
import { type Kysely, type Selectable } from 'kysely';
|
|
22
22
|
import { type ColdStore } from '@kici-dev/shared';
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `access_log` cold-store adapter (Orchestrator side).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
4
|
* Contract:
|
|
6
5
|
* - tenant column: `org_id` (NULL → synthetic `__orchestrator__`)
|
|
7
6
|
* - partition column: `created_at`
|
|
8
7
|
* - warm TTL: 30 days
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* indefinitely.
|
|
9
|
+
* Rows older than 30 days live in S3 indefinitely; there is no
|
|
10
|
+
* `expires_at`-based hard delete.
|
|
13
11
|
*
|
|
14
12
|
* Synthetic tenant: orchestrator-level access events (e.g. scheduler
|
|
15
13
|
* tick failures, admin-triggered scheduled jobs) carry `org_id IS
|
|
@@ -21,20 +19,18 @@
|
|
|
21
19
|
* audit row per ~50K-row chunk.
|
|
22
20
|
*/
|
|
23
21
|
import { type Kysely, type Selectable } from 'kysely';
|
|
24
|
-
import { type ChunkCommitMetadata, type ColdRetention, type ColdStoreTableConfig, type EligiblePartition, type TableAdapter } from '@kici-dev/shared';
|
|
22
|
+
import { type ChunkCommitMetadata, type ColdRetention, type ColdStoreTableConfig, type EligiblePartition, type TableAdapter, PgTableAdapterBase } from '@kici-dev/shared';
|
|
25
23
|
import type { AccessLogTable, Database } from '../../db/types.js';
|
|
26
24
|
export type AccessLogColdStoreRow = Selectable<AccessLogTable>;
|
|
27
25
|
export interface AccessLogAdapterOptions {
|
|
28
26
|
overrides?: Partial<ColdStoreTableConfig>;
|
|
29
27
|
}
|
|
30
|
-
export declare class AccessLogAdapter implements TableAdapter<AccessLogColdStoreRow> {
|
|
31
|
-
private readonly kdb;
|
|
28
|
+
export declare class AccessLogAdapter extends PgTableAdapterBase<Database> implements TableAdapter<AccessLogColdStoreRow> {
|
|
32
29
|
private readonly instanceId;
|
|
33
30
|
readonly db: 'orchestrator';
|
|
34
31
|
readonly table = "access_log";
|
|
35
32
|
readonly tenantColumn = "org_id";
|
|
36
33
|
readonly partitionColumn = "created_at";
|
|
37
|
-
readonly config: ColdStoreTableConfig;
|
|
38
34
|
constructor(kdb: Kysely<Database>, instanceId: string, opts?: AccessLogAdapterOptions);
|
|
39
35
|
listEligiblePartitions(args: {
|
|
40
36
|
warmCutoff: Date;
|
|
@@ -43,10 +39,6 @@ export declare class AccessLogAdapter implements TableAdapter<AccessLogColdStore
|
|
|
43
39
|
tenantId: string;
|
|
44
40
|
warmCutoff: Date;
|
|
45
41
|
}): Promise<number>;
|
|
46
|
-
withPartitionLock<T>(args: {
|
|
47
|
-
tenantId: string;
|
|
48
|
-
partitionDate: string;
|
|
49
|
-
}, fn: () => Promise<T>): Promise<T | null>;
|
|
50
42
|
selectEligible(args: {
|
|
51
43
|
tenantId: string;
|
|
52
44
|
partitionDate: string;
|