@serve.zone/interfaces 32.35.0 → 32.36.1

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 (41) hide show
  1. package/changelog.md +63 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/data/backup.d.ts +7 -1
  4. package/dist_ts/data/deploymentlegacyimport.d.ts +92 -0
  5. package/dist_ts/data/deploymentlegacyimport.js +237 -0
  6. package/dist_ts/data/deploymentoperation.d.ts +63 -38
  7. package/dist_ts/data/deploymentoperation.js +12 -2
  8. package/dist_ts/data/deploymentpreflight.d.ts +42 -10
  9. package/dist_ts/data/deploymentpreflight.js +47 -10
  10. package/dist_ts/data/immutableimage.d.ts +26 -2
  11. package/dist_ts/data/immutableimage.js +12 -2
  12. package/dist_ts/data/index.d.ts +5 -0
  13. package/dist_ts/data/index.js +6 -1
  14. package/dist_ts/data/runtimeassignmentexec.d.ts +110 -0
  15. package/dist_ts/data/runtimeassignmentexec.js +164 -0
  16. package/dist_ts/data/runtimeassignmentlogs.d.ts +121 -0
  17. package/dist_ts/data/runtimeassignmentlogs.js +139 -0
  18. package/dist_ts/data/runtimeassignmentstats.d.ts +60 -0
  19. package/dist_ts/data/runtimeassignmentstats.js +93 -0
  20. package/dist_ts/data/runtimelocalvolumearchive.d.ts +103 -0
  21. package/dist_ts/data/runtimelocalvolumearchive.js +111 -0
  22. package/dist_ts/data/user.d.ts +6 -1
  23. package/dist_ts/data/user.js +6 -1
  24. package/dist_ts/requests/runtimesession.d.ts +81 -0
  25. package/dist_ts/requests/service.d.ts +51 -0
  26. package/package.json +1 -1
  27. package/readme.md +105 -0
  28. package/ts/00_commitinfo_data.ts +1 -1
  29. package/ts/data/backup.ts +7 -1
  30. package/ts/data/deploymentlegacyimport.ts +371 -0
  31. package/ts/data/deploymentoperation.ts +83 -39
  32. package/ts/data/deploymentpreflight.ts +86 -18
  33. package/ts/data/immutableimage.ts +42 -3
  34. package/ts/data/index.ts +5 -0
  35. package/ts/data/runtimeassignmentexec.ts +271 -0
  36. package/ts/data/runtimeassignmentlogs.ts +249 -0
  37. package/ts/data/runtimeassignmentstats.ts +149 -0
  38. package/ts/data/runtimelocalvolumearchive.ts +207 -0
  39. package/ts/data/user.ts +10 -0
  40. package/ts/requests/runtimesession.ts +102 -0
  41. package/ts/requests/service.ts +66 -0
@@ -0,0 +1,207 @@
1
+ import type * as plugins from '../plugins.js';
2
+ import type { TSha256Digest } from './immutableimage.js';
3
+ import type { IRuntimeAuthorityReference } from './runtimeassignment.js';
4
+ import type { IRuntimeLocalStorageClaim } from './runtimelocalstorageclaim.js';
5
+ import {
6
+ getRuntimeLocalStorageClaimReference, snapshotRuntimeLocalStorageClaim, validateRuntimeLocalStorageClaim,
7
+ } from './runtimelocalstorageclaim.js';
8
+ import type { IRuntimeSessionBinding } from './runtimesession.js';
9
+ import { snapshotRuntimeSessionBinding } from './runtimesession.js';
10
+ import {
11
+ captureNetwork, networkDigest, networkEqual, networkRecord, networkReference, requireNetwork,
12
+ } from '../private/networkcontract.js';
13
+
14
+ /**
15
+ * The one archive format a volume travels in: a POSIX.1-2001 pax archive of the volume's tree.
16
+ *
17
+ * The first entry is the volume's root, `./`, and every other name is relative below it. An entry is
18
+ * a directory, a regular file, a symbolic link (stored, never followed), a hard link to an earlier
19
+ * entry of the same archive, or a FIFO. Ownership is numeric: `uid` and `gid`, never user or group
20
+ * names, so the tree means the same on every host. Mode bits, including setuid, setgid and sticky,
21
+ * and the modification time travel exactly. A socket carries no bytes and belongs to the process
22
+ * that created it, so it is omitted; a device node, extended attributes and ACLs are not part of
23
+ * the format, and a volume that holds a device node is refused. The archive ends with the format's
24
+ * two zero blocks, so its length is a whole number of 512-byte blocks.
25
+ */
26
+ export type TRuntimeLocalVolumeArchiveFormat = 'pax';
27
+
28
+ /**
29
+ * A local controller reads one volume as an archive. The node refuses while a running container
30
+ * holds the volume: it reads a volume no container holds, or one whose holding container is
31
+ * stopped, which a node never restarts, and no container is granted the volume while it reads.
32
+ */
33
+ export interface IReadRuntimeLocalVolumeArchiveRequest {
34
+ session: IRuntimeSessionBinding;
35
+ /** The reference of the node's current generation of a bound `directory` claim. */
36
+ claim: IRuntimeAuthorityReference;
37
+ }
38
+
39
+ /**
40
+ * The archive arrives on the stream the TypedRequest answers beside this body. `holder` states
41
+ * whether the volume was read unheld or under a stopped holder. The stream carries no integrity:
42
+ * the node learns the archive's length and digest only while it writes it.
43
+ */
44
+ export interface IReadRuntimeLocalVolumeArchiveResponse {
45
+ claim: IRuntimeAuthorityReference;
46
+ format: TRuntimeLocalVolumeArchiveFormat;
47
+ holder: 'none' | 'stopped';
48
+ }
49
+
50
+ /**
51
+ * A local controller writes one archive as the bytes of an `import` claim that waits in
52
+ * `import-required`: the online form of an operator's offline import. The node receives the stream
53
+ * beside this body, whose integrity must be exactly `byteLength` and `digest`, unpacks it into a
54
+ * private staging tree, and publishes that tree as the volume only after the whole archive
55
+ * verified. The tree keeps the ownership the archive carries, as every imported tree does.
56
+ */
57
+ export interface IWriteRuntimeLocalVolumeArchiveRequest {
58
+ session: IRuntimeSessionBinding;
59
+ claim: IRuntimeAuthorityReference;
60
+ format: TRuntimeLocalVolumeArchiveFormat;
61
+ byteLength: number;
62
+ digest: TSha256Digest;
63
+ }
64
+
65
+ /**
66
+ * `receiving` permits sending and confirms nothing more. The stream's acceptance receipt is what
67
+ * confirms that the node published the volume as `ready`; a rejected or aborted stream leaves the
68
+ * claim in `import-required` with nothing published.
69
+ */
70
+ export interface IWriteRuntimeLocalVolumeArchiveResponse {
71
+ claim: IRuntimeAuthorityReference;
72
+ status: 'receiving';
73
+ }
74
+
75
+ /**
76
+ * Volume archives are a local controller's alone (`controller.kind: 'onebox'`), on the connection
77
+ * that holds its session, and a node moves one archive per volume at a time: a read or write of a
78
+ * volume that is already moving one is refused.
79
+ */
80
+ export const runtimeLocalVolumeArchiveContract = Object.freeze({
81
+ error: 'Runtime local volume archive contract rejected.',
82
+ controllerKind: 'onebox',
83
+ format: 'pax',
84
+ entryKinds: ['directory', 'file', 'symlink', 'hardlink', 'fifo'],
85
+ omittedEntryKinds: ['socket'],
86
+ ownership: 'numeric-uid-gid',
87
+ blockBytes: 512,
88
+ /** The shortest archive: the root directory's header and the two closing zero blocks. */
89
+ minimumArchiveBytes: 1536,
90
+ concurrency: 'one-archive-per-volume',
91
+ } as const);
92
+
93
+ const fail = (): never => { throw new Error(runtimeLocalVolumeArchiveContract.error); };
94
+ const safe = <T>(operationArg: () => T): T => { try { return operationArg(); } catch { return fail(); } };
95
+
96
+ const archiveBytesOf = (valueArg: unknown): number => {
97
+ requireNetwork(typeof valueArg === 'number' && Number.isSafeInteger(valueArg)
98
+ && valueArg >= runtimeLocalVolumeArchiveContract.minimumArchiveBytes
99
+ && valueArg % runtimeLocalVolumeArchiveContract.blockBytes === 0);
100
+ return valueArg;
101
+ };
102
+
103
+ export const snapshotReadRuntimeLocalVolumeArchiveRequest = (
104
+ valueArg: unknown,
105
+ ): IReadRuntimeLocalVolumeArchiveRequest => safe(() => {
106
+ const value = networkRecord(captureNetwork(valueArg), ['session', 'claim']);
107
+ return { session: snapshotRuntimeSessionBinding(value.session), claim: networkReference(value.claim) };
108
+ });
109
+
110
+ export const snapshotReadRuntimeLocalVolumeArchiveResponse = (
111
+ valueArg: unknown,
112
+ ): IReadRuntimeLocalVolumeArchiveResponse => safe(() => {
113
+ const value = networkRecord(captureNetwork(valueArg), ['claim', 'format', 'holder']);
114
+ requireNetwork(value.format === 'pax' && (value.holder === 'none' || value.holder === 'stopped'));
115
+ return { claim: networkReference(value.claim), format: 'pax', holder: value.holder };
116
+ });
117
+
118
+ export const snapshotWriteRuntimeLocalVolumeArchiveRequest = (
119
+ valueArg: unknown,
120
+ ): IWriteRuntimeLocalVolumeArchiveRequest => safe(() => {
121
+ const value = networkRecord(captureNetwork(valueArg), ['session', 'claim', 'format', 'byteLength', 'digest']);
122
+ requireNetwork(value.format === 'pax');
123
+ return {
124
+ session: snapshotRuntimeSessionBinding(value.session), claim: networkReference(value.claim), format: 'pax',
125
+ byteLength: archiveBytesOf(value.byteLength), digest: networkDigest(value.digest),
126
+ };
127
+ });
128
+
129
+ export const snapshotWriteRuntimeLocalVolumeArchiveResponse = (
130
+ valueArg: unknown,
131
+ ): IWriteRuntimeLocalVolumeArchiveResponse => safe(() => {
132
+ const value = networkRecord(captureNetwork(valueArg), ['claim', 'status']);
133
+ requireNetwork(value.status === 'receiving');
134
+ return { claim: networkReference(value.claim), status: 'receiving' };
135
+ });
136
+
137
+ /** The exact integrity the write's stream must carry: the request's length and digest. */
138
+ export const getRuntimeLocalVolumeArchiveIntegrity = (
139
+ requestArg: unknown,
140
+ ): plugins.typedrequestInterfaces.IVirtualStreamIntegrity => {
141
+ const request = snapshotWriteRuntimeLocalVolumeArchiveRequest(requestArg);
142
+ return { algorithm: 'sha256', byteLength: request.byteLength, digest: request.digest };
143
+ };
144
+
145
+ /**
146
+ * The claim a request names must be the node's current generation of a bound `directory` claim of
147
+ * the session's controller, cluster, node and runtime namespace, and the session must be a local
148
+ * controller's current one.
149
+ */
150
+ const bindClaim = async (
151
+ requestArg: { session: IRuntimeSessionBinding; claim: IRuntimeAuthorityReference },
152
+ claimArg: unknown, trustedSessionArg: unknown,
153
+ ): Promise<IRuntimeLocalStorageClaim> => {
154
+ const trustedSession = safe(() => snapshotRuntimeSessionBinding(trustedSessionArg));
155
+ const claim = safe(() => snapshotRuntimeLocalStorageClaim(claimArg));
156
+ if ((await validateRuntimeLocalStorageClaim(claim)).length) fail();
157
+ safe(() => requireNetwork(networkEqual(requestArg.session, trustedSession)
158
+ && trustedSession.controller.kind === runtimeLocalVolumeArchiveContract.controllerKind
159
+ && networkEqual(requestArg.claim, getRuntimeLocalStorageClaimReference(claim))
160
+ && claim.lifecycle === 'bound' && claim.source.kind === 'directory'
161
+ && networkEqual(claim.controller, trustedSession.controller)
162
+ && claim.clusterId === trustedSession.clusterId
163
+ && claim.nodeId === trustedSession.nodeId
164
+ && claim.runtimeNamespace === trustedSession.runtimeNamespace));
165
+ return claim;
166
+ };
167
+
168
+ /**
169
+ * Node-side content binding, not authentication. `claimArg` is the node's current generation of the
170
+ * named claim and `trustedSessionArg` its CURRENT session, on whose own connection the request must
171
+ * have arrived. Whether the volume is `ready` and who holds it are the node's ledger checks.
172
+ */
173
+ export const bindReadRuntimeLocalVolumeArchiveRequest = async (
174
+ valueArg: unknown, claimArg: unknown, trustedSessionArg: unknown,
175
+ ): Promise<{ request: IReadRuntimeLocalVolumeArchiveRequest; claim: IRuntimeLocalStorageClaim }> => {
176
+ const request = snapshotReadRuntimeLocalVolumeArchiveRequest(valueArg);
177
+ return { request, claim: await bindClaim(request, claimArg, trustedSessionArg) };
178
+ };
179
+
180
+ /**
181
+ * As the read binding, and the claim must be an `import` claim. That it still waits in
182
+ * `import-required` is the node's ledger check.
183
+ */
184
+ export const bindWriteRuntimeLocalVolumeArchiveRequest = async (
185
+ valueArg: unknown, claimArg: unknown, trustedSessionArg: unknown,
186
+ ): Promise<{ request: IWriteRuntimeLocalVolumeArchiveRequest; claim: IRuntimeLocalStorageClaim }> => {
187
+ const request = snapshotWriteRuntimeLocalVolumeArchiveRequest(valueArg);
188
+ const claim = await bindClaim(request, claimArg, trustedSessionArg);
189
+ if (claim.initialization !== 'import') fail();
190
+ return { request, claim };
191
+ };
192
+
193
+ export const bindReadRuntimeLocalVolumeArchiveResponse = (
194
+ valueArg: unknown, requestArg: unknown,
195
+ ): IReadRuntimeLocalVolumeArchiveResponse => safe(() => {
196
+ const response = snapshotReadRuntimeLocalVolumeArchiveResponse(valueArg);
197
+ requireNetwork(networkEqual(response.claim, snapshotReadRuntimeLocalVolumeArchiveRequest(requestArg).claim));
198
+ return response;
199
+ });
200
+
201
+ export const bindWriteRuntimeLocalVolumeArchiveResponse = (
202
+ valueArg: unknown, requestArg: unknown,
203
+ ): IWriteRuntimeLocalVolumeArchiveResponse => safe(() => {
204
+ const response = snapshotWriteRuntimeLocalVolumeArchiveResponse(valueArg);
205
+ requireNetwork(networkEqual(response.claim, snapshotWriteRuntimeLocalVolumeArchiveRequest(requestArg).claim));
206
+ return response;
207
+ });
package/ts/data/user.ts CHANGED
@@ -13,6 +13,11 @@ export type TServiceDeploymentCapability =
13
13
  | 'deployment:adopt-existing'
14
14
  | 'deployment:status'
15
15
  | 'deployment:create-greenfield'
16
+ /**
17
+ * Reserve a greenfield successor whose image Cloudly imports from an attested legacy service,
18
+ * and have Cloudly import it. Granted on an organization-service-slot grant only.
19
+ */
20
+ | 'deployment:import-legacy'
16
21
  | 'deployment:promote-route'
17
22
  | 'deployment:rollback-image'
18
23
  | 'deployment:rollback-route'
@@ -53,6 +58,7 @@ const serviceDeploymentCapabilities = new Set<TServiceDeploymentCapability>([
53
58
  'deployment:adopt-existing',
54
59
  'deployment:status',
55
60
  'deployment:create-greenfield',
61
+ 'deployment:import-legacy',
56
62
  'deployment:promote-route',
57
63
  'deployment:rollback-image',
58
64
  'deployment:rollback-route',
@@ -106,6 +112,10 @@ export const validateServiceDeploymentGrant = (
106
112
  && capabilities.includes('deployment:adopt-existing')) {
107
113
  errors.push('deployment:adopt-existing requires an existing-service grant');
108
114
  }
115
+ if (capabilities && grant.scope !== 'organization-service-slot'
116
+ && capabilities.includes('deployment:import-legacy')) {
117
+ errors.push('deployment:import-legacy requires an organization-service-slot grant');
118
+ }
109
119
  return errors;
110
120
  };
111
121
 
@@ -9,6 +9,10 @@ import type * as assignmentSecret from '../runtime.assignmentsecret.js';
9
9
  import type * as fleet from '../data/fleetcutover.js';
10
10
  import type { IRuntimeStorageClaim } from '../data/runtimestorageclaim.js';
11
11
  import type * as localStorage from '../data/runtimelocalstorageclaim.js';
12
+ import type * as logs from '../data/runtimeassignmentlogs.js';
13
+ import type * as stats from '../data/runtimeassignmentstats.js';
14
+ import type * as exec from '../data/runtimeassignmentexec.js';
15
+ import type * as volumeArchive from '../data/runtimelocalvolumearchive.js';
12
16
 
13
17
  /** Current-peer, exact-projection authorization; response contains secret keys. */
14
18
  export interface IReq_Pallet_Controller_GetRuntimeManagedVpnCredential extends plugins.typedrequestInterfaces.implementsTR<
@@ -215,6 +219,104 @@ export interface IReq_Pallet_Controller_ReportRuntimeAssignmentTerminalReceipt e
215
219
  response: terminal.IReportRuntimeAssignmentTerminalReceiptResponse;
216
220
  }
217
221
 
222
+ /**
223
+ * Pallet pushes one batch of one attempt's captured output over its current session and sends the
224
+ * next only after this one is acknowledged (`runtimeAssignmentLogContract`). The controller owns
225
+ * log persistence; the node's capture files are disposable transport artifacts.
226
+ */
227
+ export interface IReq_Pallet_Controller_ReportRuntimeAssignmentLogs extends plugins.typedrequestInterfaces.implementsTR<
228
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Pallet_Controller_ReportRuntimeAssignmentLogs
229
+ > {
230
+ method: 'reportRuntimeAssignmentLogs';
231
+ request: logs.IReportRuntimeAssignmentLogsRequest;
232
+ response: logs.IReportRuntimeAssignmentLogsResponse;
233
+ }
234
+
235
+ /** Controller read of one resource sample of one attempt, over the node's current session. */
236
+ export interface IReq_Controller_Pallet_ReadRuntimeAssignmentStats extends plugins.typedrequestInterfaces.implementsTR<
237
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Controller_Pallet_ReadRuntimeAssignmentStats
238
+ > {
239
+ method: 'readRuntimeAssignmentStats';
240
+ request: stats.IReadRuntimeAssignmentStatsRequest;
241
+ response: stats.IReadRuntimeAssignmentStatsResponse;
242
+ }
243
+
244
+ /**
245
+ * A local controller runs one command in one running attempt's container
246
+ * (`runtimeAssignmentExecContract`). The controller sends standard input on `stdin`, or states
247
+ * `null` for none. The node answers once the process runs: `stdout` carries a `tty` session's
248
+ * whole output, and `stderr` is `null` exactly for a `tty` session. The streams outlive this
249
+ * request and end when the process's output ends or the session ends.
250
+ */
251
+ export interface IReq_Onebox_Pallet_OpenRuntimeAssignmentExec extends plugins.typedrequestInterfaces.implementsTR<
252
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Onebox_Pallet_OpenRuntimeAssignmentExec
253
+ > {
254
+ method: 'openRuntimeAssignmentExec';
255
+ request: {
256
+ exec: exec.IOpenRuntimeAssignmentExecRequest;
257
+ /** Requester sends standard input; the TypedHandler receives the opposite endpoint. */
258
+ stdin: plugins.typedrequestInterfaces.TVirtualStream<'send'> | null;
259
+ };
260
+ response: {
261
+ exec: exec.IOpenRuntimeAssignmentExecResponse;
262
+ /** Requester receives output; the TypedHandler returns sender endpoints. */
263
+ stdout: plugins.typedrequestInterfaces.TVirtualStream<'receive'>;
264
+ stderr: plugins.typedrequestInterfaces.TVirtualStream<'receive'> | null;
265
+ };
266
+ }
267
+
268
+ /** A local controller resizes the terminal of one `tty` exec session. */
269
+ export interface IReq_Onebox_Pallet_ResizeRuntimeAssignmentExec extends plugins.typedrequestInterfaces.implementsTR<
270
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Onebox_Pallet_ResizeRuntimeAssignmentExec
271
+ > {
272
+ method: 'resizeRuntimeAssignmentExec';
273
+ request: exec.IResizeRuntimeAssignmentExecRequest;
274
+ response: exec.IResizeRuntimeAssignmentExecResponse;
275
+ }
276
+
277
+ /** A local controller ends one exec session, releases its slot and learns how it ended. */
278
+ export interface IReq_Onebox_Pallet_CloseRuntimeAssignmentExec extends plugins.typedrequestInterfaces.implementsTR<
279
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Onebox_Pallet_CloseRuntimeAssignmentExec
280
+ > {
281
+ method: 'closeRuntimeAssignmentExec';
282
+ request: exec.ICloseRuntimeAssignmentExecRequest;
283
+ response: exec.ICloseRuntimeAssignmentExecResponse;
284
+ }
285
+
286
+ /**
287
+ * A local controller reads one node-local volume as a pax archive
288
+ * (`runtimeLocalVolumeArchiveContract`). The archive arrives on `archive`, which outlives this
289
+ * request; the node's completion of that stream is the end of the archive.
290
+ */
291
+ export interface IReq_Onebox_Pallet_ReadRuntimeLocalVolumeArchive extends plugins.typedrequestInterfaces.implementsTR<
292
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Onebox_Pallet_ReadRuntimeLocalVolumeArchive
293
+ > {
294
+ method: 'readRuntimeLocalVolumeArchive';
295
+ request: volumeArchive.IReadRuntimeLocalVolumeArchiveRequest;
296
+ response: {
297
+ volume: volumeArchive.IReadRuntimeLocalVolumeArchiveResponse;
298
+ /** Requester receives archive bytes; the TypedHandler returns a sender endpoint. */
299
+ archive: plugins.typedrequestInterfaces.TVirtualStream<'receive'>;
300
+ };
301
+ }
302
+
303
+ /**
304
+ * A local controller writes one pax archive as the bytes of an `import` claim waiting in
305
+ * `import-required`. The stream's integrity is `getRuntimeLocalVolumeArchiveIntegrity(volume)`;
306
+ * its acceptance receipt confirms that the node published the volume as `ready`.
307
+ */
308
+ export interface IReq_Onebox_Pallet_WriteRuntimeLocalVolumeArchive extends plugins.typedrequestInterfaces.implementsTR<
309
+ plugins.typedrequestInterfaces.ITypedRequest, IReq_Onebox_Pallet_WriteRuntimeLocalVolumeArchive
310
+ > {
311
+ method: 'writeRuntimeLocalVolumeArchive';
312
+ request: {
313
+ volume: volumeArchive.IWriteRuntimeLocalVolumeArchiveRequest;
314
+ /** Requester sends archive bytes; the TypedHandler receives the opposite endpoint. */
315
+ archive: plugins.typedrequestInterfaces.TVirtualStream<'send'>;
316
+ };
317
+ response: volumeArchive.IWriteRuntimeLocalVolumeArchiveResponse;
318
+ }
319
+
218
320
  /** Current-peer, exact current assignment authorization; response is secret material. */
219
321
  export interface IReq_Pallet_Controller_GetRuntimeRegistryCredential extends plugins.typedrequestInterfaces.implementsTR<
220
322
  plugins.typedrequestInterfaces.ITypedRequest,
@@ -17,6 +17,7 @@ import type {
17
17
  IServiceDeploymentStatus,
18
18
  } from '../data/deploymentoperation.js';
19
19
  import type { TSha256Digest } from '../data/immutableimage.js';
20
+ import type { ILegacyServiceRuntimeAttestation } from '../data/deploymentlegacyimport.js';
20
21
  import type { IProtocolOffer } from '../protocol.js';
21
22
  import type {
22
23
  IServiceRuntimeAssignmentView,
@@ -324,6 +325,71 @@ extends plugins.typedrequestInterfaces.implementsTR<
324
325
  };
325
326
  }
326
327
 
328
+ /**
329
+ * Captures durable evidence that a legacy `deployOnPush` service runs exactly the registry root its
330
+ * tag names: every required replica observed healthy on that one digest, with the root's platform
331
+ * manifests read from Cloudly's own registry. Administrators only, and only while the legacy
332
+ * runtime still runs — incomplete evidence is refused, never stored.
333
+ *
334
+ * `expectedRuntimeDigest`, when stated, is an assertion the observed digest must equal. Capturing
335
+ * again for an unchanged digest returns the attestation already stored.
336
+ */
337
+ export interface IRequest_Any_Cloudly_CaptureLegacyServiceRuntimeAttestation
338
+ extends plugins.typedrequestInterfaces.implementsTR<
339
+ plugins.typedrequestInterfaces.ITypedRequest,
340
+ IRequest_Any_Cloudly_CaptureLegacyServiceRuntimeAttestation
341
+ > {
342
+ method: 'captureLegacyServiceRuntimeAttestation';
343
+ request: {
344
+ identity: IIdentityCredential;
345
+ legacyServiceId: string;
346
+ expectedRuntimeDigest?: TSha256Digest;
347
+ };
348
+ response: {
349
+ attestation: ILegacyServiceRuntimeAttestation;
350
+ };
351
+ }
352
+
353
+ /** Every attestation Cloudly holds for one legacy service, oldest first. Administrators only. */
354
+ export interface IRequest_Any_Cloudly_GetLegacyServiceRuntimeAttestations
355
+ extends plugins.typedrequestInterfaces.implementsTR<
356
+ plugins.typedrequestInterfaces.ITypedRequest,
357
+ IRequest_Any_Cloudly_GetLegacyServiceRuntimeAttestations
358
+ > {
359
+ method: 'getLegacyServiceRuntimeAttestations';
360
+ request: {
361
+ identity: IIdentityCredential;
362
+ legacyServiceId: string;
363
+ };
364
+ response: {
365
+ attestations: ILegacyServiceRuntimeAttestation[];
366
+ };
367
+ }
368
+
369
+ /**
370
+ * Imports the attested legacy image of a reserved legacy-import operation into the operation's own
371
+ * repository and release tag. Cloudly does it from its own registry — nothing is pushed — and
372
+ * records release evidence of source `cloudly-legacy-import`; the operation then continues exactly
373
+ * like one whose image was pushed. Replaying it after the import returns the recorded release.
374
+ */
375
+ export interface IRequest_Any_Cloudly_ImportServiceDeploymentLegacyImage
376
+ extends plugins.typedrequestInterfaces.implementsTR<
377
+ plugins.typedrequestInterfaces.ITypedRequest,
378
+ IRequest_Any_Cloudly_ImportServiceDeploymentLegacyImage
379
+ > {
380
+ method: 'importServiceDeploymentLegacyImage';
381
+ request: {
382
+ identity: IIdentityCredential;
383
+ serviceId: string;
384
+ operationId: string;
385
+ expectedOperationRevision: number;
386
+ };
387
+ response: {
388
+ operation: IServiceDeploymentOperation;
389
+ release: IImageRelease;
390
+ };
391
+ }
392
+
327
393
  export interface IRequest_Any_Cloudly_CreateService
328
394
  extends plugins.typedrequestInterfaces.implementsTR<
329
395
  plugins.typedrequestInterfaces.ITypedRequest,