@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.
Files changed (176) hide show
  1. package/dist/__test-helpers__/peer-mutual-auth.d.ts +42 -0
  2. package/dist/__test-helpers__/scratch-db.d.ts +16 -0
  3. package/dist/agent/host-roster.d.ts +100 -37
  4. package/dist/agent/registry.d.ts +1 -8
  5. package/dist/app-on-error.d.ts +1 -1
  6. package/dist/app.d.ts +4 -2
  7. package/dist/artifacts/artifact-store.d.ts +0 -2
  8. package/dist/audit/access-log.d.ts +1 -1
  9. package/dist/cache/source-cache.d.ts +1 -1
  10. package/dist/cache/user-cache.d.ts +0 -28
  11. package/dist/cli/api-client.d.ts +27 -1
  12. package/dist/cli/commands/access-log.d.ts +1 -1
  13. package/dist/cli/commands/cold-store-impl.d.ts +7 -84
  14. package/dist/cli/commands/db.d.ts +1 -0
  15. package/dist/cli/commands/host.d.ts +3 -0
  16. package/dist/cli/commands/orchestrator-service/status.d.ts +2 -0
  17. package/dist/cli/commands/org.d.ts +12 -0
  18. package/dist/cli/commands/scaler.d.ts +14 -0
  19. package/dist/cli/commands/secret.d.ts +1 -45
  20. package/dist/cli/commands/service-health.d.ts +21 -13
  21. package/dist/cli/commands/shared/cli-action.d.ts +17 -0
  22. package/dist/cli/commands/shared/db.d.ts +5 -5
  23. package/dist/cli/commands/shared/org-id.d.ts +5 -0
  24. package/dist/cli/commands/shared/service-verbs.d.ts +19 -0
  25. package/dist/cli/commands/shared/table.d.ts +3 -0
  26. package/dist/cli/commands/shared/unbound-context-warning.d.ts +2 -22
  27. package/dist/cli/commands/source-manifest.d.ts +3 -3
  28. package/dist/cli/commands/source.d.ts +23 -0
  29. package/dist/cli/commands/trust-policy.d.ts +7 -31
  30. package/dist/cli/join.d.ts +2 -2
  31. package/dist/cli/kici-admin.d.ts +6 -1
  32. package/dist/cli/local-github-ingress-url.d.ts +6 -0
  33. package/dist/cli/root-option-routing.d.ts +46 -0
  34. package/dist/cli/webhook-url-notes.d.ts +15 -0
  35. package/dist/cli.js +10667 -8461
  36. package/dist/cluster/coordinator-credential.d.ts +21 -0
  37. package/dist/cluster/coordinator.d.ts +2 -2
  38. package/dist/cluster/index.d.ts +4 -4
  39. package/dist/cluster/join-client.d.ts +23 -21
  40. package/dist/cluster/join-handler.d.ts +24 -16
  41. package/dist/cluster/join-protocol-v2.d.ts +84 -0
  42. package/dist/cluster/join-token.d.ts +78 -56
  43. package/dist/cluster/peer-auth-coordinator.d.ts +16 -2
  44. package/dist/cluster/peer-client.d.ts +99 -5
  45. package/dist/cluster/peer-crypto.d.ts +60 -3
  46. package/dist/cluster/peer-discovery.d.ts +67 -0
  47. package/dist/cluster/peer-forget.d.ts +4 -9
  48. package/dist/cluster/peer-handler.d.ts +28 -6
  49. package/dist/cluster/peer-registry.d.ts +15 -1
  50. package/dist/cluster/peer-request-waiters.d.ts +26 -0
  51. package/dist/cluster/scaler-orphans-peer.d.ts +4 -9
  52. package/dist/cluster/scaler-reload-fanout.d.ts +29 -0
  53. package/dist/cluster/scaler-reload-peer.d.ts +16 -0
  54. package/dist/cluster/worker-cluster-settings.d.ts +1 -0
  55. package/dist/cold-store/load-access-log-range.d.ts +3 -3
  56. package/dist/cold-store/tables/access-log.d.ts +4 -12
  57. package/dist/cold-store/tables/event-log.d.ts +8 -19
  58. package/dist/cold-store/tables/execution-jobs.d.ts +7 -24
  59. package/dist/cold-store/tables/execution-runs.d.ts +6 -20
  60. package/dist/cold-store/tables/execution-steps.d.ts +8 -26
  61. package/dist/cold-store/tables/secret-audit-log.d.ts +4 -11
  62. package/dist/config/env-overlay.d.ts +3 -8
  63. package/dist/config/reload.d.ts +9 -2
  64. package/dist/config/schema.d.ts +16 -2
  65. package/dist/config/types.d.ts +6 -2
  66. package/dist/config.d.ts +22 -17
  67. package/dist/contexts/held-runs.d.ts +3 -6
  68. package/dist/dashboard/cursor.d.ts +12 -0
  69. package/dist/dashboard/handler.d.ts +5 -12
  70. package/dist/db/deferred-index-build.d.ts +42 -0
  71. package/dist/db/deferred-indexes.d.ts +3 -37
  72. package/dist/db/migrations/100_held_runs_hold_type_vocabulary.d.ts +3 -5
  73. package/dist/db/migrations/157_host_roster_identity_source.d.ts +21 -0
  74. package/dist/db/migrations/158_normalize_legacy_values.d.ts +4 -0
  75. package/dist/db/repos/org-ids-repo.d.ts +87 -0
  76. package/dist/db/schema-snapshot.generated.d.ts +3 -0
  77. package/dist/db/types.d.ts +21 -24
  78. package/dist/fault-injection-types.d.ts +0 -6
  79. package/dist/git/dispatch-git-auth.d.ts +6 -12
  80. package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +25 -13
  81. package/dist/helpers/json.d.ts +3 -0
  82. package/dist/index.d.ts +1 -1
  83. package/dist/index.js +357 -216
  84. package/dist/metrics/prometheus.d.ts +0 -5
  85. package/dist/oidc/id-token-claims.d.ts +1 -1
  86. package/dist/oidc/resolve-signer.d.ts +2 -4
  87. package/dist/orchestrator-core.d.ts +10 -3
  88. package/dist/pipeline/global-eval-round.d.ts +2 -26
  89. package/dist/pipeline/install-secrets-resolver.d.ts +0 -3
  90. package/dist/pipeline/manual-schedule.d.ts +1 -7
  91. package/dist/provenance/dispatch-context.d.ts +5 -7
  92. package/dist/provenance/pending-attestations-repo.d.ts +1 -1
  93. package/dist/provenance/trust-root.d.ts +4 -5
  94. package/dist/provenance/verify-deferred-capture.d.ts +3 -3
  95. package/dist/provider-registry.d.ts +13 -40
  96. package/dist/providers/github/manifest.d.ts +58 -7
  97. package/dist/providers/universal-git/ssh-auth.d.ts +0 -5
  98. package/dist/reporting/check-run-reporter.d.ts +8 -21
  99. package/dist/reporting/check-run-summary.d.ts +2 -2
  100. package/dist/reporting/execution-tracker.d.ts +1 -1
  101. package/dist/reporting/log-chunk-sink.d.ts +1 -1
  102. package/dist/reporting/run-summary-fields.d.ts +25 -0
  103. package/dist/reporting/s3-log-storage.d.ts +2 -2
  104. package/dist/routes/admin-access-log.d.ts +2 -9
  105. package/dist/routes/admin-backends.d.ts +2 -9
  106. package/dist/routes/admin-cluster-name.d.ts +2 -8
  107. package/dist/routes/admin-cluster-settings.d.ts +2 -8
  108. package/dist/routes/admin-config.d.ts +0 -3
  109. package/dist/routes/admin-contexts.d.ts +3 -11
  110. package/dist/routes/admin-db.d.ts +2 -9
  111. package/dist/routes/admin-env.d.ts +33 -0
  112. package/dist/routes/admin-event-dlq.d.ts +2 -9
  113. package/dist/routes/admin-event-inspect.d.ts +2 -9
  114. package/dist/routes/admin-event-log.d.ts +2 -9
  115. package/dist/routes/admin-events.d.ts +3 -13
  116. package/dist/routes/admin-held-runs.d.ts +2 -8
  117. package/dist/routes/admin-list-limit.d.ts +3 -0
  118. package/dist/routes/admin-maintenance.d.ts +2 -9
  119. package/dist/routes/admin-orchestrator-drain.d.ts +2 -9
  120. package/dist/routes/admin-org-settings.d.ts +2 -8
  121. package/dist/routes/admin-orgs.d.ts +27 -0
  122. package/dist/routes/admin-peer-forget.d.ts +2 -9
  123. package/dist/routes/admin-queue-execution.d.ts +3 -10
  124. package/dist/routes/admin-registrations.d.ts +3 -14
  125. package/dist/routes/admin-runs.d.ts +3 -14
  126. package/dist/routes/admin-scaler-orphans.d.ts +2 -9
  127. package/dist/routes/admin-scaler-reload.d.ts +41 -0
  128. package/dist/routes/admin-scheduled-jobs.d.ts +2 -9
  129. package/dist/routes/admin-signing-keys.d.ts +2 -9
  130. package/dist/routes/admin-sources.d.ts +28 -32
  131. package/dist/routes/admin-trust-policy.d.ts +2 -8
  132. package/dist/routes/admin.d.ts +29 -30
  133. package/dist/routes/cluster-join.d.ts +13 -0
  134. package/dist/routes/github-webhook.d.ts +13 -11
  135. package/dist/scaler/bare-metal-backend.d.ts +42 -1
  136. package/dist/scaler/config.d.ts +12 -84
  137. package/dist/scaler/file-reload.d.ts +48 -0
  138. package/dist/scaler/manager.d.ts +47 -33
  139. package/dist/scaler/scaler-state-store.d.ts +5 -0
  140. package/dist/scaler/types.d.ts +8 -9
  141. package/dist/secrets/job-secret-gate.d.ts +2 -5
  142. package/dist/secrets/secret-resolver.d.ts +2 -4
  143. package/dist/security/trust-policy-gate.d.ts +0 -4
  144. package/dist/server.d.ts +6 -17
  145. package/dist/server.js +10933 -10836
  146. package/dist/sources/source-manager.d.ts +0 -24
  147. package/dist/sources/webhook-url-resolvers.d.ts +60 -0
  148. package/dist/standalone.js +10457 -10060
  149. package/dist/worker/reroute-dispatch.d.ts +1 -0
  150. package/dist/worker/reroute-spawn-control.d.ts +1 -3
  151. package/dist/worker-core.d.ts +9 -2
  152. package/dist/ws/agent-handler.d.ts +3 -3
  153. package/dist/ws/dashboard-access.d.ts +57 -0
  154. package/dist/ws/dashboard-backends-handler.d.ts +1 -6
  155. package/dist/ws/dashboard-context-handler.d.ts +10 -15
  156. package/dist/ws/dashboard-fleet-handler.d.ts +4 -3
  157. package/dist/ws/dashboard-fleet-write-handler.d.ts +6 -5
  158. package/dist/ws/dashboard-global-workflows-handler.d.ts +1 -6
  159. package/dist/ws/dashboard-registrations-handler.d.ts +1 -7
  160. package/dist/ws/platform-client.d.ts +94 -327
  161. package/installer-image-digests.json +3 -3
  162. package/package.json +4 -4
  163. package/sbom.spdx.json +53 -53
  164. package/dist/cache/legacy-prefixes.d.ts +0 -19
  165. package/dist/cli/commands/agent-service/logs.d.ts +0 -14
  166. package/dist/cli/commands/agent-service/restart.d.ts +0 -12
  167. package/dist/cli/commands/agent-service/start.d.ts +0 -12
  168. package/dist/cli/commands/agent-service/stop.d.ts +0 -12
  169. package/dist/cli/commands/agent-service/uninstall.d.ts +0 -13
  170. package/dist/cli/commands/cache.d.ts +0 -51
  171. package/dist/cli/commands/orchestrator-service/logs.d.ts +0 -14
  172. package/dist/cli/commands/orchestrator-service/restart.d.ts +0 -12
  173. package/dist/cli/commands/orchestrator-service/start.d.ts +0 -12
  174. package/dist/cli/commands/orchestrator-service/stop.d.ts +0 -12
  175. package/dist/cli/commands/orchestrator-service/uninstall.d.ts +0 -13
  176. 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 uses ECDH key exchange followed by join token (first connect)
11
- * or HMAC credential proof (reconnection).
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 { PeerRegistry } from './peer-registry.js';
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 an HMAC proof with. */
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 peer-to-peer channels.
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
- * Session keys are derived via HKDF-SHA256 from the ECDH shared secret
6
- * and a caller-provided nonce (to ensure per-session uniqueness).
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, keyed by message id. */
129
- export declare class PeerForgetWaiters {
130
- private readonly waiters;
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 for encrypted channel,
5
- * validates authentication via join token (first connect) or HMAC credential
6
- * proof (reconnection), registers the peer in PeerRegistry, and routes
7
- * messages bidirectionally. Sends periodic heartbeats to the connecting peer.
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 { PeerRegistry } from './peer-registry.js';
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, keyed by message id. */
31
- export declare class ScalerOrphansWaiters {
32
- private readonly waiters;
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. Cursors without a `source` field
18
- * default to `'hot'` — backwards-compatible with any tab the user had
19
- * open across a deploy boundary.
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
- * Replaces the previous 90-day `expires_at`-based hard delete (removed
11
- * by migration 007). Rows older than 30 days now live in S3
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;