@serve.zone/interfaces 32.67.0 → 32.69.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 (38) hide show
  1. package/changelog.md +20 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/data/clusternode.d.ts +15 -2
  4. package/dist_ts/data/clusternode.js +1 -1
  5. package/dist_ts/data/deploymentoperation.d.ts +17 -3
  6. package/dist_ts/data/deploymentoperation.js +4 -1
  7. package/dist_ts/data/deploymentreleaseredeploy.d.ts +88 -0
  8. package/dist_ts/data/deploymentreleaseredeploy.js +120 -0
  9. package/dist_ts/data/immutableimage.d.ts +17 -3
  10. package/dist_ts/data/immutableimage.js +18 -1
  11. package/dist_ts/data/index.d.ts +3 -0
  12. package/dist_ts/data/index.js +4 -1
  13. package/dist_ts/data/nodeaction.d.ts +439 -0
  14. package/dist_ts/data/nodeaction.js +434 -0
  15. package/dist_ts/data/sparkfleettarget.d.ts +80 -0
  16. package/dist_ts/data/sparkfleettarget.js +71 -0
  17. package/dist_ts/data/sparknodereport.d.ts +19 -0
  18. package/dist_ts/data/sparknodereport.js +21 -4
  19. package/dist_ts/data/user.d.ts +7 -1
  20. package/dist_ts/data/user.js +6 -1
  21. package/dist_ts/requests/deployment.d.ts +5 -0
  22. package/dist_ts/requests/node.d.ts +100 -0
  23. package/dist_ts/requests/service.d.ts +42 -0
  24. package/package.json +2 -2
  25. package/readme.md +56 -0
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/data/clusternode.ts +17 -2
  28. package/ts/data/deploymentoperation.ts +21 -2
  29. package/ts/data/deploymentreleaseredeploy.ts +182 -0
  30. package/ts/data/immutableimage.ts +36 -3
  31. package/ts/data/index.ts +3 -0
  32. package/ts/data/nodeaction.ts +755 -0
  33. package/ts/data/sparkfleettarget.ts +138 -0
  34. package/ts/data/sparknodereport.ts +42 -3
  35. package/ts/data/user.ts +12 -1
  36. package/ts/requests/deployment.ts +5 -0
  37. package/ts/requests/node.ts +118 -0
  38. package/ts/requests/service.ts +55 -0
@@ -0,0 +1,439 @@
1
+ import { type IProtocolOffer } from '../protocol.js';
2
+ import type { TNodeActionName, TNodeActionStatus } from './clusternode.js';
3
+ /**
4
+ * Every node action, in canonical token order, so a list of them (a Spark's `actionExecutors`)
5
+ * is compared byte for byte.
6
+ */
7
+ export declare const nodeActionNames: readonly ["reboot", "serveZoneServiceUpdate", "systemUpgrade"];
8
+ export declare const isNodeActionName: (valueArg: unknown) => valueArg is TNodeActionName;
9
+ /**
10
+ * Whether each action state is still open or ended.
11
+ *
12
+ * The four values the Swarm-era carrier shipped keep their meaning: `pending` is the requested
13
+ * state of every action, `running` is a Swarm-era action its daemon started, and `succeeded` and
14
+ * `failed` end one. The node runtime's lifecycle (`INodeActionLifecycle`) adds the rest:
15
+ *
16
+ * - `claimed`: the node's Spark took the action (`claim`), stating the boot it took it in.
17
+ * - `executing`, `draining`, `rebooting`: the phase Spark reports, only ever forward
18
+ * (`sparkNodeActionPhaseOrder`).
19
+ * - `verifying`: Cloudly saw the evidence the action changed the node (`nodeActionVerification`)
20
+ * and waits for the node's runtime to come back.
21
+ * - `expired`: never claimed in its claim window, or a Swarm-era record no carrier can deliver
22
+ * (`TNodeActionExpiryReason`).
23
+ * - `cancelled`: an operator withdrew it while it was still `pending`.
24
+ * - `unverified`: its deadline passed before Cloudly could verify it. An operator judges the node;
25
+ * Cloudly never retries it.
26
+ */
27
+ export declare const nodeActionStatusKinds: Readonly<{
28
+ readonly pending: 'open';
29
+ readonly running: 'open';
30
+ readonly claimed: 'open';
31
+ readonly executing: 'open';
32
+ readonly draining: 'open';
33
+ readonly rebooting: 'open';
34
+ readonly verifying: 'open';
35
+ readonly succeeded: 'terminal';
36
+ readonly failed: 'terminal';
37
+ readonly expired: 'terminal';
38
+ readonly cancelled: 'terminal';
39
+ readonly unverified: 'terminal';
40
+ }>;
41
+ /** Every open state, the ones the one-open-action-per-node rule counts. */
42
+ export declare const nodeActionOpenStatuses: readonly TNodeActionStatus[];
43
+ export declare const isNodeActionOpen: (statusArg: TNodeActionStatus) => boolean;
44
+ /**
45
+ * The phases Spark reports while it executes a claimed action, in the only order it may report
46
+ * them. A phase may be skipped, never repeated or reported after a later one.
47
+ */
48
+ export declare const sparkNodeActionPhaseOrder: readonly ["claimed", "executing", "draining", "rebooting"];
49
+ /** A phase Spark reports with a `phase` transition. */
50
+ export type TSparkNodeActionPhase = Exclude<typeof sparkNodeActionPhaseOrder[number], 'claimed'>;
51
+ /**
52
+ * Time budgets of the node runtime's action lifecycle, in milliseconds.
53
+ *
54
+ * A requested action waits `claimWindowMs` for its node's Spark to claim it, then expires. A claimed
55
+ * action must be verified within its completion budget (`nodeActionCompletionBudgetMs`), measured
56
+ * from the claim, or it ends `unverified`. Each budget is the sum of the steps the action takes. One
57
+ * budget is read from Spark's code; every other one is chosen here, and each member says which.
58
+ */
59
+ export declare const nodeActionContract: Readonly<{
60
+ /** Chosen: how long a requested action waits for its node's Spark to claim it (10 min). */
61
+ readonly claimWindowMs: 600000;
62
+ /**
63
+ * Spark code: `sparkNodeStopTimeoutSeconds`, the node unit's `TimeoutStopSec=` (522 s), in which
64
+ * Pallet stops every workload it runs.
65
+ */
66
+ readonly drainMs: 522000;
67
+ /** Chosen: a host's reboot until Spark reports from the new boot (10 min). */
68
+ readonly bootMs: 600000;
69
+ /**
70
+ * Chosen: how long Pallet takes to start and register again after the node runtime starts
71
+ * (712 s, from the 2026-11-05 survey; Spark states no start budget of its own).
72
+ */
73
+ readonly startMs: 712000;
74
+ /** Chosen: downloading and checking the target's node bundle before anything stops (10 min). */
75
+ readonly downloadMs: 600000;
76
+ /** Chosen: the host's package upgrade (30 min). */
77
+ readonly hostUpgradeMs: 1800000;
78
+ /** Entries an action's timeline keeps; each state is entered at most once. */
79
+ readonly maximumTimelineEntries: 16;
80
+ /** Characters of operator text one timeline entry or failure carries. */
81
+ readonly maximumDetailLength: 512;
82
+ /** Services one impact names; a node holding more is answered `impact-unreadable`. */
83
+ readonly maximumImpactServices: 1024;
84
+ /** Characters of one service name an impact carries. */
85
+ readonly maximumServiceNameLength: 256;
86
+ }>;
87
+ /**
88
+ * How long a claimed action has until Cloudly must have verified it, from the claim.
89
+ *
90
+ * A reboot drains, boots and starts the node. An update downloads the target, then restarts the
91
+ * node runtime on it: a drain and a start, no boot. A host upgrade runs the package upgrade and,
92
+ * with `reboot`, the reboot after it.
93
+ */
94
+ export declare const nodeActionCompletionBudgetMs: (actionNameArg: TNodeActionName, rebootArg: boolean) => number;
95
+ /** Whether an action reboots the host, which is how Cloudly verifies it (`nodeActionVerification`). */
96
+ export declare const nodeActionReboots: (actionNameArg: TNodeActionName, rebootArg: boolean) => boolean;
97
+ /**
98
+ * What Cloudly accepts as evidence that a claimed action changed the node, by what the action does.
99
+ *
100
+ * - `new-boot`: an action that reboots is verified by a heartbeat, accepted after the claim, whose
101
+ * `boot.id` differs from the boot the claim stated.
102
+ * - `target-version`: an update is verified by a heartbeat, accepted after the claim, whose bundle
103
+ * version is the action's target version.
104
+ * - `upgrade-complete`: a host upgrade without a reboot is verified by Spark's `complete` and a
105
+ * heartbeat accepted after it; a heartbeat that states `boot.rebootRequired` fails the action
106
+ * `reboot-required` instead, because applying the upgrade needs a reboot the operator did not
107
+ * request. Cloudly never escalates to one.
108
+ *
109
+ * An action that restarts the node runtime (every action but a host upgrade without a reboot) is
110
+ * then only `succeeded` once the node's Pallet registered a runtime session after the claim and
111
+ * Cloudly accepted an observation of every replica slot on the node after that registration.
112
+ */
113
+ export type TNodeActionVerification = 'new-boot' | 'target-version' | 'upgrade-complete';
114
+ export declare const nodeActionVerification: (actionNameArg: TNodeActionName, rebootArg: boolean) => TNodeActionVerification;
115
+ /** Whether the action restarts the node runtime and is verified by its return (see `nodeActionVerification`). */
116
+ export declare const nodeActionRestartsRuntime: (actionNameArg: TNodeActionName, rebootArg: boolean) => boolean;
117
+ /**
118
+ * Why an action ended `failed`, each with the remedy an operator is told.
119
+ *
120
+ * The first group is stated by Spark (`TSparkNodeActionFailureCode`), the second by Cloudly.
121
+ */
122
+ export declare const nodeActionFailureRemedies: Readonly<{
123
+ readonly 'download-failed': string;
124
+ readonly 'installer-mismatch': string;
125
+ readonly 'install-failed': string;
126
+ readonly 'drain-failed': string;
127
+ readonly 'reboot-failed': 'The host did not accept the reboot. Read the node\'s Spark journal.';
128
+ readonly 'host-upgrade-failed': string;
129
+ readonly 'executor-failed': 'Spark could not run the action. Read the node\'s Spark journal.';
130
+ readonly 'reboot-required': string;
131
+ readonly 'target-below-node': string;
132
+ }>;
133
+ export type TNodeActionFailureCode = keyof typeof nodeActionFailureRemedies;
134
+ /** The failure codes Spark may state with a `fail` transition. */
135
+ export declare const sparkNodeActionFailureCodes: readonly ["download-failed", "drain-failed", "executor-failed", "host-upgrade-failed", "install-failed", "installer-mismatch", "reboot-failed"];
136
+ export type TSparkNodeActionFailureCode = typeof sparkNodeActionFailureCodes[number];
137
+ /**
138
+ * Why an action ended `expired`.
139
+ *
140
+ * - `claim-window-passed`: no Spark claimed it within `nodeActionContract.claimWindowMs`.
141
+ * - `carrier-retired`: a Swarm-era record on a node that holds no Swarm-era session, which no
142
+ * carrier delivers: it states none of the frozen parameters and target the node runtime's
143
+ * lifecycle needs. Cloudly keeps the record.
144
+ */
145
+ export type TNodeActionExpiryReason = 'claim-window-passed' | 'carrier-retired';
146
+ export interface INodeActionExpiry {
147
+ reason: TNodeActionExpiryReason;
148
+ at: number;
149
+ }
150
+ /**
151
+ * The Spark release an update moves a node to: the fleet Spark target as it stood when the
152
+ * operator requested the action, frozen into the action.
153
+ */
154
+ export interface INodeActionTarget {
155
+ /** `MAJOR.MINOR.PATCH`, without `v`. */
156
+ version: string;
157
+ /** The SHA-256 of the release's `install.sh`, as `SHA256SUMS.txt` states it (lowercase hex). */
158
+ installerSha256: string;
159
+ /** The fleet target's revision this copy was taken from. */
160
+ targetRevision: number;
161
+ }
162
+ /**
163
+ * What an operator confirmed when requesting a node runtime action, frozen into the action.
164
+ *
165
+ * The consent is the typed hostname and the digest of the impact preview the operator read:
166
+ * Cloudly refuses the request when either no longer matches what it reads now.
167
+ */
168
+ export interface INodeActionConfirmation {
169
+ /** The hostname the operator typed; must equal the node's reported hostname. */
170
+ confirmedHostname: string;
171
+ /** `impactDigest` of the preview the operator confirmed (`computeNodeActionImpactDigest`). */
172
+ impactDigest: string;
173
+ /**
174
+ * Consent to restarting the Corestore the preview names on this node. Required `true` when the
175
+ * impact names one (`corestore`), and never assumed: a Corestore restart is never a default.
176
+ */
177
+ corestoreRestart: boolean;
178
+ /** `systemUpgrade` only: reboot after the upgrade, as part of this one action. */
179
+ reboot: boolean;
180
+ /**
181
+ * `serveZoneServiceUpdate` only, and required there: the revision of the fleet Spark target the
182
+ * preview showed (`INodeActionPreview.target.targetRevision`). Cloudly refuses the update
183
+ * (`target-changed`) when the target it would freeze into the action has another revision, so the
184
+ * consent names the exact release the node is moved to. Absent for every other action.
185
+ */
186
+ targetRevision?: number;
187
+ }
188
+ /** One service the action interrupts, as the impact preview names it. */
189
+ export interface INodeActionImpactService {
190
+ serviceId: string;
191
+ name: string;
192
+ /** Replica slots of this service on the node. */
193
+ slotsOnNode: number;
194
+ /** Replica slots of this service on other nodes; `0` means the service is down while the node is. */
195
+ slotsElsewhere: number;
196
+ /** The service is the cluster's designated ingress. */
197
+ clusterIngress: boolean;
198
+ }
199
+ /**
200
+ * What a node action interrupts, read from Cloudly's database when it is previewed and again when
201
+ * it is requested.
202
+ *
203
+ * - `clusterNodeCount` 1: the node is its cluster, so the action is a full cluster outage.
204
+ * - `relayHost`: the node hosts its cluster's relay, so every inter-node connection of the cluster
205
+ * is interrupted while it is down. The relay never moves for an action.
206
+ * - `corestore`: the node runs a Corestore (`IClusterNode.data.corestore`), which restarts with it.
207
+ * - `services`: every service with a replica slot on the node, sorted by `serviceId`.
208
+ */
209
+ export interface INodeActionImpact {
210
+ nodeId: string;
211
+ clusterId: string;
212
+ /** The hostname the node reports, which a confirmation types; `null` while it reports none. */
213
+ hostname: string | null;
214
+ clusterNodeCount: number;
215
+ relayHost: boolean;
216
+ corestore: boolean;
217
+ services: INodeActionImpactService[];
218
+ }
219
+ /** The SHA-256 (lowercase hex) of one impact's canonical JSON, which a confirmation names. */
220
+ export declare const computeNodeActionImpactDigest: (impactArg: INodeActionImpact) => Promise<string>;
221
+ /** When Spark claimed an action, and in which boot of the host. */
222
+ export interface INodeActionClaim {
223
+ claimedAt: number;
224
+ /** The host's `boot.id` the claim stated. */
225
+ bootId: string;
226
+ /** The Spark bundle version the node reported when it claimed. */
227
+ bundleVersion: string | null;
228
+ }
229
+ /** Why an action failed, and the remedy the operator was told (`nodeActionFailureRemedies`). */
230
+ export interface INodeActionFailure {
231
+ code: TNodeActionFailureCode;
232
+ remedy: string;
233
+ /** Spark's or Cloudly's own text, bounded by `nodeActionContract.maximumDetailLength`. */
234
+ detail: string | null;
235
+ }
236
+ /** Who moved an action into a state. */
237
+ export type TNodeActionActor = 'operator' | 'spark' | 'cloudly';
238
+ /** One state an action entered, in order. */
239
+ export interface INodeActionTransition {
240
+ status: TNodeActionStatus;
241
+ at: number;
242
+ actor: TNodeActionActor;
243
+ detail: string | null;
244
+ }
245
+ /**
246
+ * The node runtime's lifecycle of one action: what was confirmed and frozen at the request, and
247
+ * every state since. Absent on a Swarm-era record, which no carrier but the Swarm-era daemon's
248
+ * delivers.
249
+ *
250
+ * `revision` advances with every state change. Each change is one transaction that matches the
251
+ * action on its expected state and revision, so two writers never both move one action.
252
+ */
253
+ export interface INodeActionLifecycle {
254
+ clusterId: string;
255
+ revision: number;
256
+ confirmation: INodeActionConfirmation;
257
+ impact: INodeActionImpact;
258
+ /** `serveZoneServiceUpdate` only. */
259
+ target: INodeActionTarget | null;
260
+ /** The claim window's end while `pending`; the completion deadline once claimed. */
261
+ deadlineAt: number;
262
+ claim: INodeActionClaim | null;
263
+ failure: INodeActionFailure | null;
264
+ timeline: INodeActionTransition[];
265
+ }
266
+ /**
267
+ * Every reason Cloudly answers instead of admitting, previewing or cancelling a node action. Each
268
+ * travels by name as a `TypedResponseError`, and a refused request changes nothing.
269
+ */
270
+ export declare const nodeActionRefusals: readonly ["node-missing", "node-action-undeliverable", "node-action-unsupported", "system-upgrade-unqualified", "node-action-open", "cluster-action-open", "spark-target-unset", "spark-target-below-node", "spark-target-held", "confirmation-invalid", "confirmation-hostname-mismatch", "impact-changed", "target-changed", "corestore-consent-required", "impact-unreadable", "node-action-missing", "node-action-not-cancellable", "node-action-revision-stale"];
271
+ export type TNodeActionRefusalCode = typeof nodeActionRefusals[number];
272
+ /** What requesting one action on one node would do, and whether Cloudly would admit it now. */
273
+ export interface INodeActionPreview {
274
+ nodeId: string;
275
+ actionName: TNodeActionName;
276
+ impact: INodeActionImpact;
277
+ /** What a confirmation names (`INodeActionConfirmation.impactDigest`). */
278
+ impactDigest: string;
279
+ /** The Spark bundle version the node reports; `null` while it reports none. */
280
+ nodeBundleVersion: string | null;
281
+ /** Whether the node's Spark states that the host needs a reboot; `null` while it states nothing. */
282
+ rebootRequired: boolean | null;
283
+ /** `serveZoneServiceUpdate` only: the fleet target the update moves the node to. */
284
+ target: INodeActionTarget | null;
285
+ /** Why a request would be refused now, in `nodeActionRefusals` order; empty when it would be admitted. */
286
+ refusals: TNodeActionRefusalCode[];
287
+ }
288
+ /**
289
+ * Validates one confirmation for one action and names every reason it is refused.
290
+ *
291
+ * Shape only: an update must state `targetRevision`, every other action must not. Whether it
292
+ * matches the node, the impact and the fleet target Cloudly reads now is Cloudly's decision
293
+ * (`confirmation-hostname-mismatch`, `impact-changed`, `target-changed`, `corestore-consent-required`).
294
+ */
295
+ export declare const validateNodeActionConfirmation: (valueArg: unknown, actionNameArg: TNodeActionName, pathArg?: string) => string[];
296
+ /** HTTPS POST body asking for the action Cloudly holds for this node. Never log, persist, or return nodeToken. */
297
+ export interface ISparkNodeActionOfferRequest {
298
+ nodeId: string;
299
+ nodeToken: string;
300
+ /** Read and negotiated before the node is authenticated; see `sparkNodeActionContracts`. */
301
+ protocol: IProtocolOffer;
302
+ }
303
+ /**
304
+ * One action as Spark executes it: the node runtime's view of an open action.
305
+ *
306
+ * `status` is the action's state when Cloudly answered; `revision` is what Spark's next transition
307
+ * names. `claim` states the boot Spark claimed it in, so a Spark that restarted mid-action resumes
308
+ * by replaying the transition it had sent rather than claiming again.
309
+ */
310
+ export interface ISparkNodeActionAssignment {
311
+ id: string;
312
+ actionName: TNodeActionName;
313
+ status: Extract<TNodeActionStatus, 'pending' | 'claimed' | 'executing' | 'draining' | 'rebooting' | 'verifying'>;
314
+ revision: number;
315
+ deadlineAt: number;
316
+ /** `systemUpgrade` only: reboot after the upgrade. */
317
+ reboot: boolean;
318
+ /** `serveZoneServiceUpdate` only: install exactly this release, after checking its installer's SHA-256. */
319
+ target: INodeActionTarget | null;
320
+ claim: {
321
+ bootId: string;
322
+ } | null;
323
+ }
324
+ /** What Cloudly answers the offer route: the node's one open action, or `null`. */
325
+ export interface ISparkNodeActionOffer {
326
+ nodeId: string;
327
+ action: ISparkNodeActionAssignment | null;
328
+ /** The controller's offer, so the node judges the answer it acts on. */
329
+ protocol: IProtocolOffer;
330
+ }
331
+ /**
332
+ * One step Spark states about the action it executes.
333
+ *
334
+ * - `claim`: take a `pending` action, in the boot `bootId` names.
335
+ * - `phase`: the action entered a later phase (`sparkNodeActionPhaseOrder`). A reboot drains and
336
+ * then reboots; an update executes (downloads and installs) and then drains (restarts the node
337
+ * runtime on the new release); a host upgrade executes and, with `reboot`, drains and reboots.
338
+ * - `complete`: a host upgrade without a reboot finished; Cloudly verifies it from the next heartbeat.
339
+ * - `fail`: the action failed with one of `sparkNodeActionFailureCodes`.
340
+ *
341
+ * Every other ending is Cloudly's: it verifies an action from what the node reports
342
+ * (`nodeActionVerification`), and a deadline that passes ends one `unverified`.
343
+ */
344
+ export type TSparkNodeActionTransition = {
345
+ kind: 'claim';
346
+ bootId: string;
347
+ } | {
348
+ kind: 'phase';
349
+ phase: TSparkNodeActionPhase;
350
+ } | {
351
+ kind: 'complete';
352
+ } | {
353
+ kind: 'fail';
354
+ code: TSparkNodeActionFailureCode;
355
+ detail: string;
356
+ };
357
+ /** HTTPS POST body stating one transition. Never log, persist, or return nodeToken. */
358
+ export interface ISparkNodeActionTransitionRequest {
359
+ nodeId: string;
360
+ nodeToken: string;
361
+ /** Read and negotiated before the node is authenticated; see `sparkNodeActionContracts`. */
362
+ protocol: IProtocolOffer;
363
+ actionId: string;
364
+ /** The revision of the assignment the transition was decided from. */
365
+ expectedRevision: number;
366
+ transition: TSparkNodeActionTransition;
367
+ }
368
+ /**
369
+ * Why Cloudly did not apply a transition.
370
+ *
371
+ * - `action-unknown`: no action of that id on this node.
372
+ * - `action-closed`: the action ended; `action` states it no more, Spark stops executing it.
373
+ * - `revision-stale`: the action moved since `expectedRevision`; `action` states where it stands.
374
+ * - `transition-invalid`: the action's state or kind does not admit this transition.
375
+ */
376
+ export type TSparkNodeActionTransitionRefusal = 'action-unknown' | 'action-closed' | 'revision-stale' | 'transition-invalid';
377
+ /**
378
+ * What Cloudly answers a transition with.
379
+ *
380
+ * `replayed` answers a transition Cloudly had already applied at exactly that revision, so a
381
+ * Spark that lost the answer and sends it again is told it took effect. `action` is the action as
382
+ * it stands after the answer, `null` once it is no longer open.
383
+ */
384
+ export type TSparkNodeActionTransitionAnswer = {
385
+ outcome: 'applied' | 'replayed';
386
+ action: ISparkNodeActionAssignment | null;
387
+ protocol: IProtocolOffer;
388
+ } | {
389
+ outcome: 'refused';
390
+ reason: TSparkNodeActionTransitionRefusal;
391
+ action: ISparkNodeActionAssignment | null;
392
+ protocol: IProtocolOffer;
393
+ };
394
+ /**
395
+ * The node runtime's action routes.
396
+ *
397
+ * Spark asks the offer route after each accepted heartbeat and states every step on the transition
398
+ * route. Both are authenticated with the node's Spark credential exactly like
399
+ * `sparkNodeHeartbeatContract` and judged in the same server order: the offer first, within
400
+ * `maxRequestBytes`, answered with `protocolRefusalStatus` and an `IProtocolRefusal` body on
401
+ * refusal, before the node is authenticated. The server's offer on these routes names, as its
402
+ * minimum, the release that introduced them, so a Spark below it is refused by name and is never
403
+ * handed an action.
404
+ */
405
+ export declare const sparkNodeActionContracts: Readonly<{
406
+ readonly offer: Readonly<{
407
+ readonly endpoint: '/spark/nodes/action';
408
+ readonly maxRequestBytes: 1024;
409
+ readonly maxResponseBytes: 4096;
410
+ }>;
411
+ readonly transition: Readonly<{
412
+ readonly endpoint: '/spark/nodes/action/transition';
413
+ readonly maxRequestBytes: 2048;
414
+ readonly maxResponseBytes: 4096;
415
+ }>;
416
+ readonly protocolRefusalStatus: 422;
417
+ }>;
418
+ /** The kernel's `boot_id`: one lowercase UUID. */
419
+ export declare const isHostBootId: (valueArg: unknown) => valueArg is string;
420
+ /** One assignment, exactly its keys; detached and frozen. */
421
+ export declare const snapshotSparkNodeActionAssignment: (valueArg: unknown) => ISparkNodeActionAssignment;
422
+ export declare const snapshotSparkNodeActionOfferRequest: (valueArg: unknown) => ISparkNodeActionOfferRequest;
423
+ export declare const snapshotSparkNodeActionOffer: (valueArg: unknown) => ISparkNodeActionOffer;
424
+ export declare const snapshotSparkNodeActionTransitionRequest: (valueArg: unknown) => ISparkNodeActionTransitionRequest;
425
+ export declare const snapshotSparkNodeActionTransitionAnswer: (valueArg: unknown) => TSparkNodeActionTransitionAnswer;
426
+ /**
427
+ * Whether an open action admits one transition from Spark, judged from its state alone.
428
+ *
429
+ * `claim` takes a `pending` action. A `phase` moves strictly forward in `sparkNodeActionPhaseOrder`
430
+ * from `claimed` or an earlier phase, and only into a phase the action has: `rebooting` only for
431
+ * an action that reboots, `draining` for every action but a host upgrade without a reboot.
432
+ * `complete` ends the execution of a host upgrade without a reboot. `fail` ends any claimed action
433
+ * that Cloudly is not yet verifying.
434
+ */
435
+ export declare const admitsSparkNodeActionTransition: (actionArg: Pick<ISparkNodeActionAssignment, 'actionName' | 'status' | 'reboot'>, transitionArg: TSparkNodeActionTransition) => boolean;
436
+ /** True when a value is a canonical list of action names: known names, strictly increasing, at least one. */
437
+ export declare const isNodeActionNameList: (valueArg: unknown) => valueArg is TNodeActionName[];
438
+ /** Sorts action names into canonical order, so a list built from a set compares byte for byte. */
439
+ export declare const sortNodeActionNames: (namesArg: Iterable<TNodeActionName>) => TNodeActionName[];