@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
package/changelog.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-09-29 - 32.36.1
4
+
5
+ ### Fixes
6
+
7
+ - `IArchivePruneResult.rewrittenPacks` is optional. Corestore 34.0.0 (containerarchive 0.10)
8
+ removes unreferenced packs whole and never rewrites one, so it no longer reports the field;
9
+ Corestore up to 33.x still does. Absent means no pack was rewritten.
10
+
11
+ ## 2026-09-29 - 32.36.0
12
+
13
+ ### Features
14
+
15
+ - A node's controller can now see and reach its workloads. Four contract families ride on the
16
+ runtime session in `requests.runtimesession`:
17
+ - `reportRuntimeAssignmentLogs`: Pallet pushes batches of an attempt's captured output. Lines
18
+ come in `partial` pieces, and `lost` entries evidence `buffer-overflow`, `line-cut`,
19
+ `capture-restart` and `not-captured` loss. Batches are acknowledged by sequence, so the
20
+ controller persists logs as `IRuntimeConfig.logs` requires.
21
+ - `readRuntimeAssignmentStats`: one CPU, memory, network and writable-layer sample of a
22
+ running attempt.
23
+ - `openRuntimeAssignmentExec`, `resizeRuntimeAssignmentExec` and `closeRuntimeAssignmentExec`:
24
+ a command in a running container, with VirtualStream stdin and output, TTY or not. At most
25
+ four sessions per node, with a 15-minute idle limit.
26
+ - `readRuntimeLocalVolumeArchive` and `writeRuntimeLocalVolumeArchive`: a node-local volume as
27
+ a pax archive. A read refuses a volume a running container holds. A write fills an `import`
28
+ claim that waits in `import-required` and publishes it only after the whole verified archive.
29
+
30
+ Exec and volume archives are refused for any controller but a local one (`onebox`). Each family
31
+ has exact-schema snapshots and request and response binders (`data.runtimeAssignmentLogContract`,
32
+ `runtimeAssignmentStatsContract`, `runtimeAssignmentExecContract`,
33
+ `runtimeLocalVolumeArchiveContract`). Until now a controller had no way to read a Pallet
34
+ workload's output or resources, run a command in it, or back up and restore its volumes, so
35
+ Onebox could not move its services off Docker.
36
+ - `TDeploymentPreflightWarningCode` gains `PLATFORM_BINDING_READINESS_DEFERRED`: the promote
37
+ preflight of a held greenfield service that was never promoted may accept an enabled Corestore
38
+ `database` or `objectstorage` binding that is not `ready` yet, because a held service receives no
39
+ credential material and its binding can only become ready once promotion releases the hold. The
40
+ warning records that acceptance; the route preflight still requires the binding `ready`.
41
+ - A legacy `deployOnPush` service without deployable source can move to a greenfield successor that
42
+ runs the exact image it ran. `data.ILegacyServiceRuntimeAttestation` is an administrator-captured
43
+ record that every required replica ran healthy on one registry root, with that root's platform
44
+ manifests (`ILegacyServiceImageSource`, `ILegacyImagePlatformManifest`);
45
+ `validateLegacyServiceRuntimeAttestation` and `validateLegacyImageImportReference` state the rules.
46
+ A greenfield reservation and its preflight may name `legacyImport` (`ILegacyImageImportReference`)
47
+ in place of `sourceRevision`, `sourceDirty` and release evidence
48
+ (`ILegacyImportServiceDeploymentReservationInput`), and the operation records the same origin.
49
+ New preflight blockers: `LEGACY_IMPORT_ATTESTATION_MISSING`, `LEGACY_IMPORT_SOURCE_MISMATCH` and
50
+ `LEGACY_IMPORT_PLATFORM_MISMATCH`. Release evidence may now be Cloudly's import
51
+ (`IImageReleaseLegacyImportEvidence`, source `cloudly-legacy-import`) beside the registry push
52
+ (`IImageReleaseRegistryPushEvidence`); `selectTrustedImageReleaseEvidence` takes an optional
53
+ `evidenceSource`, which defaults to the registry push. The capability `deployment:import-legacy`
54
+ is valid on organization-service-slot grants only. New requests:
55
+ `captureLegacyServiceRuntimeAttestation` and `getLegacyServiceRuntimeAttestations` (administrators)
56
+ and `importServiceDeploymentLegacyImage`. Until now an immutable deployment needed a source
57
+ revision and a pushed build, so a legacy service whose source was lost could not leave its
58
+ legacy runtime.
59
+ - Type widening for consumers: `IServiceDeploymentOperation.data.sourceRevision` and
60
+ `IDeploymentPreflightReport.source.revision` / `.dirty` are now `string | undefined` /
61
+ `boolean | undefined`, absent exactly when the origin is a legacy import.
62
+ `IServiceDeploymentOperation.data` is the new `IServiceDeploymentOperationData`, and
63
+ `IDeploymentPreflightRequestData` and `IImageReleaseTrustedEvidence` are now union type aliases,
64
+ so they can no longer be extended as interfaces.
65
+
3
66
  ## 2026-09-29 - 32.35.0
4
67
 
5
68
  ### Features
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '32.35.0',
6
+ version: '32.36.1',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
@@ -106,8 +106,14 @@ export interface IArchiveRetentionPolicy {
106
106
  }
107
107
  export interface IArchivePruneResult {
108
108
  removedSnapshots: number;
109
+ /** Packs holding no chunk of the committed index, removed whole. */
109
110
  removedPacks: number;
110
- rewrittenPacks: number;
111
+ /**
112
+ * Packs a prune rewrote to drop unreferenced chunks. Only Corestore up to 33.x (containerarchive
113
+ * 0.3.2) reports it; containerarchive 0.10 and Corestore 34.0.0 remove unreferenced packs whole
114
+ * and never rewrite one, so they omit it. Absent means no pack was rewritten.
115
+ */
116
+ rewrittenPacks?: number;
111
117
  freedBytes: number;
112
118
  dryRun: boolean;
113
119
  }
@@ -0,0 +1,92 @@
1
+ import type { TImmutableContainerPlatform, TOciManifestMediaType, TSha256Digest } from './immutableimage.js';
2
+ /**
3
+ * Legacy image import: a greenfield deployment whose image is the one a legacy `deployOnPush`
4
+ * service already runs, instead of one built from a source revision.
5
+ *
6
+ * A legacy service without deployable source still has an image in Cloudly's registry and, until
7
+ * its runtime is frozen, healthy replicas running it. An administrator captures that fact once as
8
+ * a {@link ILegacyServiceRuntimeAttestation} while the replicas still run. A greenfield successor
9
+ * reservation names the attestation in `legacyImport` instead of a `sourceRevision`, and Cloudly
10
+ * then imports the attested image into the successor's own repository and release tag itself:
11
+ * the bytes never leave the registry and the release records that it was imported, not built.
12
+ *
13
+ * Deployment roots are indexes. An attested root that already is an index is imported as it is. A
14
+ * root that is one platform manifest is imported under an index whose only descriptor is that
15
+ * manifest, in the manifest's own family — a Docker manifest list around a Docker manifest, an OCI
16
+ * index around an OCI manifest — so the platform manifest stays byte-identical and its digest is
17
+ * still the one the legacy runtime ran. The attestation states everything that descriptor needs.
18
+ */
19
+ /** The media types a legacy image's per-platform manifest can carry. */
20
+ export type TLegacyImagePlatformManifestMediaType = 'application/vnd.oci.image.manifest.v1+json' | 'application/vnd.docker.distribution.manifest.v2+json';
21
+ /** The index media type of the family a platform manifest belongs to. */
22
+ export type TLegacyImageImportIndexMediaType = 'application/vnd.oci.image.index.v1+json' | 'application/vnd.docker.distribution.manifest.list.v2+json';
23
+ /** One platform manifest of a legacy image, as Cloudly read it from its own registry. */
24
+ export interface ILegacyImagePlatformManifest {
25
+ platform: TImmutableContainerPlatform;
26
+ manifestDigest: TSha256Digest;
27
+ manifestMediaType: TLegacyImagePlatformManifestMediaType;
28
+ /** Exact byte length of the platform manifest, which an index descriptor states. */
29
+ manifestSize: number;
30
+ imageConfigDigest: TSha256Digest;
31
+ }
32
+ /** The registry image a legacy service ran, under the mutable tag it was deployed from. */
33
+ export interface ILegacyServiceImageSource {
34
+ registryHost: string;
35
+ repository: string;
36
+ /** The mutable tag the legacy service was deployed from, `latest` included. */
37
+ tag: string;
38
+ /** Digest of the root the tag named: a single platform manifest or an index. */
39
+ rootDigest: TSha256Digest;
40
+ rootMediaType: TOciManifestMediaType;
41
+ /**
42
+ * The runnable Linux platforms the root carries, sorted by platform. A single-manifest root
43
+ * carries exactly one, whose manifest is the root itself.
44
+ */
45
+ platforms: ILegacyImagePlatformManifest[];
46
+ }
47
+ /** One healthy replica of the legacy service, observed running the attested digest. */
48
+ export interface ILegacyServiceRuntimeTaskAttestation {
49
+ deploymentId: string;
50
+ nodeName: string;
51
+ observedDigest: TSha256Digest;
52
+ }
53
+ /**
54
+ * Durable evidence, captured by an administrator while the legacy runtime still ran, that every
55
+ * required replica of a legacy service was healthy and ran exactly the registry root it names.
56
+ *
57
+ * Cloudly writes one and never changes it. A capture only succeeds on complete evidence, so a
58
+ * stored attestation is by construction one whose runtime digest is its source root digest.
59
+ */
60
+ export interface ILegacyServiceRuntimeAttestation {
61
+ id: string;
62
+ data: {
63
+ legacyServiceId: string;
64
+ organizationId: string;
65
+ source: ILegacyServiceImageSource;
66
+ runtime: {
67
+ requiredReplicaCount: number;
68
+ healthyReplicaCount: number;
69
+ runtimeDigest: TSha256Digest;
70
+ /** Sorted by deploymentId, one per required replica. */
71
+ tasks: ILegacyServiceRuntimeTaskAttestation[];
72
+ };
73
+ capturedByUserId: string;
74
+ capturedAt: number;
75
+ };
76
+ }
77
+ /**
78
+ * What a legacy-import reservation names in place of a source revision: the legacy service, the
79
+ * attestation Cloudly holds for it, and the root digest the deployer asserts that attestation
80
+ * carries.
81
+ */
82
+ export interface ILegacyImageImportReference {
83
+ legacyServiceId: string;
84
+ attestationId: string;
85
+ sourceRootDigest: TSha256Digest;
86
+ }
87
+ /** True when a media type names a single platform manifest rather than an index. */
88
+ export declare const isLegacyImagePlatformManifestMediaType: (valueArg: unknown) => valueArg is TLegacyImagePlatformManifestMediaType;
89
+ /** True when a media type names an index of either family. */
90
+ export declare const isLegacyImageImportIndexMediaType: (valueArg: unknown) => valueArg is TLegacyImageImportIndexMediaType;
91
+ export declare const validateLegacyImageImportReference: (referenceArg: unknown) => string[];
92
+ export declare const validateLegacyServiceRuntimeAttestation: (attestationArg: unknown) => string[];
@@ -0,0 +1,237 @@
1
+ import { normalizeSha256Digest } from './immutableimage.js';
2
+ import { hasExactKeys, isPlainRecord } from '../private/dataproperties.js';
3
+ const identifierRegex = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
4
+ /** An OCI tag, the mutable `latest` included: the source tag is whatever the legacy service ran. */
5
+ const ociTagRegex = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/;
6
+ const registryHostRegex = /^[a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?(?::[0-9]{1,5})?$/;
7
+ const repositoryRegex = /^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*$/;
8
+ const maximumManifestSize = 4 * 1024 * 1024;
9
+ const maximumReplicaCount = 64;
10
+ const legacyImagePlatformManifestMediaTypes = [
11
+ 'application/vnd.oci.image.manifest.v1+json',
12
+ 'application/vnd.docker.distribution.manifest.v2+json',
13
+ ];
14
+ const legacyImageImportIndexMediaTypes = [
15
+ 'application/vnd.oci.image.index.v1+json',
16
+ 'application/vnd.docker.distribution.manifest.list.v2+json',
17
+ ];
18
+ const legacyImagePlatforms = ['linux/amd64', 'linux/arm64'];
19
+ const isIdentifier = (valueArg) => (typeof valueArg === 'string' && identifierRegex.test(valueArg));
20
+ const isCanonicalDigest = (valueArg) => (typeof valueArg === 'string' && normalizeSha256Digest(valueArg) === valueArg);
21
+ const isPositiveSafeInteger = (valueArg, maximumArg) => (typeof valueArg === 'number'
22
+ && Number.isSafeInteger(valueArg)
23
+ && valueArg >= 1
24
+ && valueArg <= maximumArg);
25
+ /** True when a media type names a single platform manifest rather than an index. */
26
+ export const isLegacyImagePlatformManifestMediaType = (valueArg) => (legacyImagePlatformManifestMediaTypes.some((mediaTypeArg) => mediaTypeArg === valueArg));
27
+ /** True when a media type names an index of either family. */
28
+ export const isLegacyImageImportIndexMediaType = (valueArg) => (legacyImageImportIndexMediaTypes.some((mediaTypeArg) => mediaTypeArg === valueArg));
29
+ export const validateLegacyImageImportReference = (referenceArg) => {
30
+ if (!isPlainRecord(referenceArg))
31
+ return ['legacyImport must be an object'];
32
+ const errors = [];
33
+ if (!hasExactKeys(referenceArg, ['legacyServiceId', 'attestationId', 'sourceRootDigest'])) {
34
+ errors.push('legacyImport must use its exact schema');
35
+ }
36
+ if (!isIdentifier(referenceArg.legacyServiceId)) {
37
+ errors.push('legacyImport.legacyServiceId must be a bounded canonical identifier');
38
+ }
39
+ if (!isIdentifier(referenceArg.attestationId)) {
40
+ errors.push('legacyImport.attestationId must be a bounded canonical identifier');
41
+ }
42
+ if (!isCanonicalDigest(referenceArg.sourceRootDigest)) {
43
+ errors.push('legacyImport.sourceRootDigest must be a canonical sha256 digest');
44
+ }
45
+ return errors;
46
+ };
47
+ const validatePlatformManifest = (manifestArg, indexArg) => {
48
+ const label = `source.platforms[${indexArg}]`;
49
+ if (!isPlainRecord(manifestArg))
50
+ return [`${label} must be an object`];
51
+ const errors = [];
52
+ if (!hasExactKeys(manifestArg, [
53
+ 'platform',
54
+ 'manifestDigest',
55
+ 'manifestMediaType',
56
+ 'manifestSize',
57
+ 'imageConfigDigest',
58
+ ])) {
59
+ errors.push(`${label} must use its exact schema`);
60
+ }
61
+ if (!legacyImagePlatforms.some((platformArg) => platformArg === manifestArg.platform)) {
62
+ errors.push(`${label}.platform must be linux/amd64 or linux/arm64`);
63
+ }
64
+ if (!isCanonicalDigest(manifestArg.manifestDigest)) {
65
+ errors.push(`${label}.manifestDigest must be a canonical sha256 digest`);
66
+ }
67
+ if (!isLegacyImagePlatformManifestMediaType(manifestArg.manifestMediaType)) {
68
+ errors.push(`${label}.manifestMediaType must name an OCI or Docker image manifest`);
69
+ }
70
+ if (!isPositiveSafeInteger(manifestArg.manifestSize, maximumManifestSize)) {
71
+ errors.push(`${label}.manifestSize must be a positive bounded byte length`);
72
+ }
73
+ if (!isCanonicalDigest(manifestArg.imageConfigDigest)) {
74
+ errors.push(`${label}.imageConfigDigest must be a canonical sha256 digest`);
75
+ }
76
+ return errors;
77
+ };
78
+ const validateImageSource = (sourceArg) => {
79
+ if (!isPlainRecord(sourceArg))
80
+ return ['source must be an object'];
81
+ const errors = [];
82
+ if (!hasExactKeys(sourceArg, [
83
+ 'registryHost',
84
+ 'repository',
85
+ 'tag',
86
+ 'rootDigest',
87
+ 'rootMediaType',
88
+ 'platforms',
89
+ ])) {
90
+ errors.push('source must use its exact schema');
91
+ }
92
+ if (typeof sourceArg.registryHost !== 'string' || !registryHostRegex.test(sourceArg.registryHost)) {
93
+ errors.push('source.registryHost must be a canonical registry host');
94
+ }
95
+ if (typeof sourceArg.repository !== 'string'
96
+ || sourceArg.repository.length > 255
97
+ || !repositoryRegex.test(sourceArg.repository)) {
98
+ errors.push('source.repository must be a canonical OCI repository name');
99
+ }
100
+ if (typeof sourceArg.tag !== 'string' || !ociTagRegex.test(sourceArg.tag)) {
101
+ errors.push('source.tag must be an OCI tag');
102
+ }
103
+ if (!isCanonicalDigest(sourceArg.rootDigest)) {
104
+ errors.push('source.rootDigest must be a canonical sha256 digest');
105
+ }
106
+ const rootIsManifest = isLegacyImagePlatformManifestMediaType(sourceArg.rootMediaType);
107
+ const rootIsIndex = isLegacyImageImportIndexMediaType(sourceArg.rootMediaType);
108
+ if (!rootIsManifest && !rootIsIndex) {
109
+ errors.push('source.rootMediaType must name an OCI or Docker manifest or index');
110
+ }
111
+ if (!Array.isArray(sourceArg.platforms)
112
+ || sourceArg.platforms.length < 1
113
+ || sourceArg.platforms.length > legacyImagePlatforms.length) {
114
+ errors.push('source.platforms must carry one or two runnable Linux platforms');
115
+ return errors;
116
+ }
117
+ const platforms = sourceArg.platforms;
118
+ platforms.forEach((manifestArg, indexArg) => {
119
+ errors.push(...validatePlatformManifest(manifestArg, indexArg));
120
+ });
121
+ const platformNames = platforms.map((manifestArg) => (isPlainRecord(manifestArg) ? manifestArg.platform : undefined));
122
+ const sortedPlatformNames = legacyImagePlatforms.filter((platformArg) => (platformNames.includes(platformArg)));
123
+ if (JSON.stringify(platformNames) !== JSON.stringify(sortedPlatformNames)) {
124
+ errors.push('source.platforms must be unique and sorted by platform');
125
+ }
126
+ if (rootIsManifest) {
127
+ const onlyPlatform = platforms[0];
128
+ if (platforms.length !== 1
129
+ || !isPlainRecord(onlyPlatform)
130
+ || onlyPlatform.manifestDigest !== sourceArg.rootDigest
131
+ || onlyPlatform.manifestMediaType !== sourceArg.rootMediaType) {
132
+ errors.push('a single-manifest source root must be its own one platform manifest');
133
+ }
134
+ }
135
+ if (rootIsIndex && platforms.some((manifestArg) => (isPlainRecord(manifestArg) && manifestArg.manifestDigest === sourceArg.rootDigest))) {
136
+ errors.push('an index source root cannot also be one of its platform manifests');
137
+ }
138
+ return errors;
139
+ };
140
+ const validateRuntime = (runtimeArg, rootDigestArg) => {
141
+ if (!isPlainRecord(runtimeArg))
142
+ return ['runtime must be an object'];
143
+ const errors = [];
144
+ if (!hasExactKeys(runtimeArg, [
145
+ 'requiredReplicaCount',
146
+ 'healthyReplicaCount',
147
+ 'runtimeDigest',
148
+ 'tasks',
149
+ ])) {
150
+ errors.push('runtime must use its exact schema');
151
+ }
152
+ if (!isPositiveSafeInteger(runtimeArg.requiredReplicaCount, maximumReplicaCount)) {
153
+ errors.push('runtime.requiredReplicaCount must be a positive bounded replica count');
154
+ }
155
+ if (runtimeArg.healthyReplicaCount !== runtimeArg.requiredReplicaCount) {
156
+ errors.push('runtime.healthyReplicaCount must equal the required replica count');
157
+ }
158
+ if (!isCanonicalDigest(runtimeArg.runtimeDigest)) {
159
+ errors.push('runtime.runtimeDigest must be a canonical sha256 digest');
160
+ }
161
+ else if (runtimeArg.runtimeDigest !== rootDigestArg) {
162
+ errors.push('runtime.runtimeDigest must be the attested source root digest');
163
+ }
164
+ if (!Array.isArray(runtimeArg.tasks) || runtimeArg.tasks.length !== runtimeArg.requiredReplicaCount) {
165
+ errors.push('runtime.tasks must hold exactly one task per required replica');
166
+ return errors;
167
+ }
168
+ const tasks = runtimeArg.tasks;
169
+ tasks.forEach((taskArg, indexArg) => {
170
+ const label = `runtime.tasks[${indexArg}]`;
171
+ if (!isPlainRecord(taskArg)) {
172
+ errors.push(`${label} must be an object`);
173
+ return;
174
+ }
175
+ if (!hasExactKeys(taskArg, ['deploymentId', 'nodeName', 'observedDigest'])) {
176
+ errors.push(`${label} must use its exact schema`);
177
+ }
178
+ if (!isIdentifier(taskArg.deploymentId)) {
179
+ errors.push(`${label}.deploymentId must be a bounded canonical identifier`);
180
+ }
181
+ if (typeof taskArg.nodeName !== 'string' || !taskArg.nodeName || taskArg.nodeName.length > 253) {
182
+ errors.push(`${label}.nodeName must be a bounded non-empty name`);
183
+ }
184
+ if (taskArg.observedDigest !== runtimeArg.runtimeDigest) {
185
+ errors.push(`${label}.observedDigest must be the attested runtime digest`);
186
+ }
187
+ });
188
+ const deploymentIds = tasks.map((taskArg) => (isPlainRecord(taskArg) ? taskArg.deploymentId : undefined));
189
+ const sortedDeploymentIds = deploymentIds
190
+ .filter((idArg) => typeof idArg === 'string')
191
+ .sort();
192
+ if (new Set(deploymentIds).size !== deploymentIds.length
193
+ || JSON.stringify(deploymentIds) !== JSON.stringify(sortedDeploymentIds)) {
194
+ errors.push('runtime.tasks must be unique and sorted by deploymentId');
195
+ }
196
+ return errors;
197
+ };
198
+ export const validateLegacyServiceRuntimeAttestation = (attestationArg) => {
199
+ if (!isPlainRecord(attestationArg))
200
+ return ['attestation must be an object'];
201
+ const errors = [];
202
+ if (!hasExactKeys(attestationArg, ['id', 'data'])) {
203
+ errors.push('attestation must use its exact schema');
204
+ }
205
+ if (!isIdentifier(attestationArg.id)) {
206
+ errors.push('id must be a bounded canonical identifier');
207
+ }
208
+ const data = attestationArg.data;
209
+ if (!isPlainRecord(data))
210
+ return [...errors, 'data must be an object'];
211
+ if (!hasExactKeys(data, [
212
+ 'legacyServiceId',
213
+ 'organizationId',
214
+ 'source',
215
+ 'runtime',
216
+ 'capturedByUserId',
217
+ 'capturedAt',
218
+ ])) {
219
+ errors.push('data must use its exact schema');
220
+ }
221
+ if (!isIdentifier(data.legacyServiceId)) {
222
+ errors.push('legacyServiceId must be a bounded canonical identifier');
223
+ }
224
+ if (!isIdentifier(data.organizationId)) {
225
+ errors.push('organizationId must be a bounded canonical identifier');
226
+ }
227
+ if (!isIdentifier(data.capturedByUserId)) {
228
+ errors.push('capturedByUserId must be a bounded canonical identifier');
229
+ }
230
+ if (!isPositiveSafeInteger(data.capturedAt, Number.MAX_SAFE_INTEGER)) {
231
+ errors.push('capturedAt must be a positive timestamp');
232
+ }
233
+ errors.push(...validateImageSource(data.source));
234
+ errors.push(...validateRuntime(data.runtime, isPlainRecord(data.source) ? data.source.rootDigest : undefined));
235
+ return errors;
236
+ };
237
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95bWVudGxlZ2FjeWltcG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvZGVwbG95bWVudGxlZ2FjeWltcG9ydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFLQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBbUczRSxNQUFNLGVBQWUsR0FBRyxxQ0FBcUMsQ0FBQztBQUM5RCxvR0FBb0c7QUFDcEcsTUFBTSxXQUFXLEdBQUcscUNBQXFDLENBQUM7QUFDMUQsTUFBTSxpQkFBaUIsR0FBRywwREFBMEQsQ0FBQztBQUNyRixNQUFNLGVBQWUsR0FBRyxxRkFBcUYsQ0FBQztBQUM5RyxNQUFNLG1CQUFtQixHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQzVDLE1BQU0sbUJBQW1CLEdBQUcsRUFBRSxDQUFDO0FBRS9CLE1BQU0scUNBQXFDLEdBQXFEO0lBQzlGLDRDQUE0QztJQUM1QyxzREFBc0Q7Q0FDdkQsQ0FBQztBQUNGLE1BQU0sZ0NBQWdDLEdBQWdEO0lBQ3BGLHlDQUF5QztJQUN6QywyREFBMkQ7Q0FDNUQsQ0FBQztBQUNGLE1BQU0sb0JBQW9CLEdBQTJDLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0FBRXBHLE1BQU0sWUFBWSxHQUFHLENBQUMsUUFBaUIsRUFBc0IsRUFBRSxDQUFDLENBQzlELE9BQU8sUUFBUSxLQUFLLFFBQVEsSUFBSSxlQUFlLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUMvRCxDQUFDO0FBRUYsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLFFBQWlCLEVBQTZCLEVBQUUsQ0FBQyxDQUMxRSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUkscUJBQXFCLENBQUMsUUFBUSxDQUFDLEtBQUssUUFBUSxDQUM3RSxDQUFDO0FBRUYsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLFFBQWlCLEVBQUUsVUFBa0IsRUFBc0IsRUFBRSxDQUFDLENBQzNGLE9BQU8sUUFBUSxLQUFLLFFBQVE7T0FDekIsTUFBTSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUM7T0FDOUIsUUFBUSxJQUFJLENBQUM7T0FDYixRQUFRLElBQUksVUFBVSxDQUMxQixDQUFDO0FBRUYsb0ZBQW9GO0FBQ3BGLE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLENBQ3BELFFBQWlCLEVBQ2tDLEVBQUUsQ0FBQyxDQUN0RCxxQ0FBcUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDLFlBQVksS0FBSyxRQUFRLENBQUMsQ0FDeEYsQ0FBQztBQUVGLDhEQUE4RDtBQUM5RCxNQUFNLENBQUMsTUFBTSxpQ0FBaUMsR0FBRyxDQUMvQyxRQUFpQixFQUM2QixFQUFFLENBQUMsQ0FDakQsZ0NBQWdDLENBQUMsSUFBSSxDQUFDLENBQUMsWUFBWSxFQUFFLEVBQUUsQ0FBQyxZQUFZLEtBQUssUUFBUSxDQUFDLENBQ25GLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxDQUFDLFlBQXFCLEVBQVksRUFBRTtJQUNwRixJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVksQ0FBQztRQUFFLE9BQU8sQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO0lBQzVFLE1BQU0sTUFBTSxHQUFhLEVBQUUsQ0FBQztJQUM1QixJQUFJLENBQUMsWUFBWSxDQUFDLFlBQVksRUFBRSxDQUFDLGlCQUFpQixFQUFFLGVBQWUsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMxRixNQUFNLENBQUMsSUFBSSxDQUFDLHdDQUF3QyxDQUFDLENBQUM7SUFDeEQsQ0FBQztJQUNELElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7UUFDaEQsTUFBTSxDQUFDLElBQUksQ0FBQyxxRUFBcUUsQ0FBQyxDQUFDO0lBQ3JGLENBQUM7SUFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO1FBQzlDLE1BQU0sQ0FBQyxJQUFJLENBQUMsbUVBQW1FLENBQUMsQ0FBQztJQUNuRixDQUFDO0lBQ0QsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7UUFDdEQsTUFBTSxDQUFDLElBQUksQ0FBQyxpRUFBaUUsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDLENBQUM7QUFFRixNQUFNLHdCQUF3QixHQUFHLENBQy9CLFdBQW9CLEVBQ3BCLFFBQWdCLEVBQ04sRUFBRTtJQUNaLE1BQU0sS0FBSyxHQUFHLG9CQUFvQixRQUFRLEdBQUcsQ0FBQztJQUM5QyxJQUFJLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQztRQUFFLE9BQU8sQ0FBQyxHQUFHLEtBQUssb0JBQW9CLENBQUMsQ0FBQztJQUN2RSxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7SUFDNUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUU7UUFDN0IsVUFBVTtRQUNWLGdCQUFnQjtRQUNoQixtQkFBbUI7UUFDbkIsY0FBYztRQUNkLG1CQUFtQjtLQUNwQixDQUFDLEVBQUUsQ0FBQztRQUNILE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLDRCQUE0QixDQUFDLENBQUM7SUFDcEQsQ0FBQztJQUNELElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLFdBQVcsS0FBSyxXQUFXLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUN0RixNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsS0FBSyw4Q0FBOEMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFDRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7UUFDbkQsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssbURBQW1ELENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBQ0QsSUFBSSxDQUFDLHNDQUFzQyxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7UUFDM0UsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssOERBQThELENBQUMsQ0FBQztJQUN0RixDQUFDO0lBQ0QsSUFBSSxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLHNEQUFzRCxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUNELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDO1FBQ3RELE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLHNEQUFzRCxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQztBQUVGLE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxTQUFrQixFQUFZLEVBQUU7SUFDM0QsSUFBSSxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUM7UUFBRSxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQztJQUNuRSxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7SUFDNUIsSUFBSSxDQUFDLFlBQVksQ0FBQyxTQUFTLEVBQUU7UUFDM0IsY0FBYztRQUNkLFlBQVk7UUFDWixLQUFLO1FBQ0wsWUFBWTtRQUNaLGVBQWU7UUFDZixXQUFXO0tBQ1osQ0FBQyxFQUFFLENBQUM7UUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLGtDQUFrQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUNELElBQUksT0FBTyxTQUFTLENBQUMsWUFBWSxLQUFLLFFBQVEsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztRQUNsRyxNQUFNLENBQUMsSUFBSSxDQUFDLHVEQUF1RCxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUNELElBQUksT0FBTyxTQUFTLENBQUMsVUFBVSxLQUFLLFFBQVE7V0FDdkMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsR0FBRztXQUNqQyxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUM7UUFDakQsTUFBTSxDQUFDLElBQUksQ0FBQywyREFBMkQsQ0FBQyxDQUFDO0lBQzNFLENBQUM7SUFDRCxJQUFJLE9BQU8sU0FBUyxDQUFDLEdBQUcsS0FBSyxRQUFRLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0JBQStCLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBQ0QsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1FBQzdDLE1BQU0sQ0FBQyxJQUFJLENBQUMscURBQXFELENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBQ0QsTUFBTSxjQUFjLEdBQUcsc0NBQXNDLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3ZGLE1BQU0sV0FBVyxHQUFHLGlDQUFpQyxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUMvRSxJQUFJLENBQUMsY0FBYyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDcEMsTUFBTSxDQUFDLElBQUksQ0FBQyxtRUFBbUUsQ0FBQyxDQUFDO0lBQ25GLENBQUM7SUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDO1dBQ2xDLFNBQVMsQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUM7V0FDOUIsU0FBUyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsb0JBQW9CLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDOUQsTUFBTSxDQUFDLElBQUksQ0FBQyxpRUFBaUUsQ0FBQyxDQUFDO1FBQy9FLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxNQUFNLFNBQVMsR0FBRyxTQUFTLENBQUMsU0FBc0IsQ0FBQztJQUNuRCxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUMsV0FBVyxFQUFFLFFBQVEsRUFBRSxFQUFFO1FBQzFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyx3QkFBd0IsQ0FBQyxXQUFXLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUNsRSxDQUFDLENBQUMsQ0FBQztJQUNILE1BQU0sYUFBYSxHQUFHLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLENBQ25ELGFBQWEsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUM5RCxDQUFDLENBQUM7SUFDSCxNQUFNLG1CQUFtQixHQUFHLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsQ0FDdkUsYUFBYSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FDcEMsQ0FBQyxDQUFDO0lBQ0gsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxLQUFLLElBQUksQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1FBQzFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsd0RBQXdELENBQUMsQ0FBQztJQUN4RSxDQUFDO0lBQ0QsSUFBSSxjQUFjLEVBQUUsQ0FBQztRQUNuQixNQUFNLFlBQVksR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUM7ZUFDckIsQ0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDO2VBQzVCLFlBQVksQ0FBQyxjQUFjLEtBQUssU0FBUyxDQUFDLFVBQVU7ZUFDcEQsWUFBWSxDQUFDLGlCQUFpQixLQUFLLFNBQVMsQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNoRSxNQUFNLENBQUMsSUFBSSxDQUFDLHFFQUFxRSxDQUFDLENBQUM7UUFDckYsQ0FBQztJQUNILENBQUM7SUFDRCxJQUFJLFdBQVcsSUFBSSxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FBQyxDQUNqRCxhQUFhLENBQUMsV0FBVyxDQUFDLElBQUksV0FBVyxDQUFDLGNBQWMsS0FBSyxTQUFTLENBQUMsVUFBVSxDQUNsRixDQUFDLEVBQUUsQ0FBQztRQUNILE1BQU0sQ0FBQyxJQUFJLENBQUMsbUVBQW1FLENBQUMsQ0FBQztJQUNuRixDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxlQUFlLEdBQUcsQ0FBQyxVQUFtQixFQUFFLGFBQXNCLEVBQVksRUFBRTtJQUNoRixJQUFJLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQztRQUFFLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sTUFBTSxHQUFhLEVBQUUsQ0FBQztJQUM1QixJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRTtRQUM1QixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLGVBQWU7UUFDZixPQUFPO0tBQ1IsQ0FBQyxFQUFFLENBQUM7UUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLG1DQUFtQyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUNELElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxVQUFVLENBQUMsb0JBQW9CLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1FBQ2pGLE1BQU0sQ0FBQyxJQUFJLENBQUMsdUVBQXVFLENBQUMsQ0FBQztJQUN2RixDQUFDO0lBQ0QsSUFBSSxVQUFVLENBQUMsbUJBQW1CLEtBQUssVUFBVSxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDdkUsTUFBTSxDQUFDLElBQUksQ0FBQyxtRUFBbUUsQ0FBQyxDQUFDO0lBQ25GLENBQUM7SUFDRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7UUFDakQsTUFBTSxDQUFDLElBQUksQ0FBQyx5REFBeUQsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7U0FBTSxJQUFJLFVBQVUsQ0FBQyxhQUFhLEtBQUssYUFBYSxFQUFFLENBQUM7UUFDdEQsTUFBTSxDQUFDLElBQUksQ0FBQywrREFBK0QsQ0FBQyxDQUFDO0lBQy9FLENBQUM7SUFDRCxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssVUFBVSxDQUFDLG9CQUFvQixFQUFFLENBQUM7UUFDcEcsTUFBTSxDQUFDLElBQUksQ0FBQywrREFBK0QsQ0FBQyxDQUFDO1FBQzdFLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFDRCxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsS0FBa0IsQ0FBQztJQUM1QyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxFQUFFO1FBQ2xDLE1BQU0sS0FBSyxHQUFHLGlCQUFpQixRQUFRLEdBQUcsQ0FBQztRQUMzQyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDNUIsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssb0JBQW9CLENBQUMsQ0FBQztZQUMxQyxPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLENBQUMsY0FBYyxFQUFFLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUMzRSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsS0FBSyw0QkFBNEIsQ0FBQyxDQUFDO1FBQ3BELENBQUM7UUFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO1lBQ3hDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLHNEQUFzRCxDQUFDLENBQUM7UUFDOUUsQ0FBQztRQUNELElBQUksT0FBTyxPQUFPLENBQUMsUUFBUSxLQUFLLFFBQVEsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUM7WUFDL0YsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLEtBQUssNENBQTRDLENBQUMsQ0FBQztRQUNwRSxDQUFDO1FBQ0QsSUFBSSxPQUFPLENBQUMsY0FBYyxLQUFLLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUN4RCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsS0FBSyxxREFBcUQsQ0FBQyxDQUFDO1FBQzdFLENBQUM7SUFDSCxDQUFDLENBQUMsQ0FBQztJQUNILE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQzNDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUMxRCxDQUFDLENBQUM7SUFDSCxNQUFNLG1CQUFtQixHQUFHLGFBQWE7U0FDdEMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFtQixFQUFFLENBQUMsT0FBTyxLQUFLLEtBQUssUUFBUSxDQUFDO1NBQzdELElBQUksRUFBRSxDQUFDO0lBQ1YsSUFBSSxJQUFJLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQyxJQUFJLEtBQUssYUFBYSxDQUFDLE1BQU07V0FDbkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsS0FBSyxJQUFJLENBQUMsU0FBUyxDQUFDLG1CQUFtQixDQUFDLEVBQUUsQ0FBQztRQUMzRSxNQUFNLENBQUMsSUFBSSxDQUFDLHlEQUF5RCxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLHVDQUF1QyxHQUFHLENBQUMsY0FBdUIsRUFBWSxFQUFFO0lBQzNGLElBQUksQ0FBQyxhQUFhLENBQUMsY0FBYyxDQUFDO1FBQUUsT0FBTyxDQUFDLCtCQUErQixDQUFDLENBQUM7SUFDN0UsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO0lBQzVCLElBQUksQ0FBQyxZQUFZLENBQUMsY0FBYyxFQUFFLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNsRCxNQUFNLENBQUMsSUFBSSxDQUFDLHVDQUF1QyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQUNELElBQUksQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDckMsTUFBTSxDQUFDLElBQUksQ0FBQywyQ0FBMkMsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFDRCxNQUFNLElBQUksR0FBRyxjQUFjLENBQUMsSUFBSSxDQUFDO0lBQ2pDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDO1FBQUUsT0FBTyxDQUFDLEdBQUcsTUFBTSxFQUFFLHdCQUF3QixDQUFDLENBQUM7SUFDdkUsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUU7UUFDdEIsaUJBQWlCO1FBQ2pCLGdCQUFnQjtRQUNoQixRQUFRO1FBQ1IsU0FBUztRQUNULGtCQUFrQjtRQUNsQixZQUFZO0tBQ2IsQ0FBQyxFQUFFLENBQUM7UUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLGdDQUFnQyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQUNELElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7UUFDeEMsTUFBTSxDQUFDLElBQUksQ0FBQyx3REFBd0QsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7SUFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO1FBQ3ZDLE1BQU0sQ0FBQyxJQUFJLENBQUMsdURBQXVELENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBQ0QsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sQ0FBQyxJQUFJLENBQUMseURBQXlELENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBQ0QsSUFBSSxDQUFDLHFCQUFxQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztRQUNyRSxNQUFNLENBQUMsSUFBSSxDQUFDLHlDQUF5QyxDQUFDLENBQUM7SUFDekQsQ0FBQztJQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUNqRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsZUFBZSxDQUM1QixJQUFJLENBQUMsT0FBTyxFQUNaLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQ2hFLENBQUMsQ0FBQztJQUNILE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMsQ0FBQyJ9
@@ -1,6 +1,7 @@
1
1
  import type { IDeploymentPreflightProposedConfiguration, IDeploymentPreflightRuntimeTask } from './deploymentpreflight.js';
2
2
  import type { IGatewayRouteDnsResult } from './gateway.js';
3
3
  import type { IImageRolloutStatus, IImmutableImageDeploymentPlan, TSha256Digest } from './immutableimage.js';
4
+ import type { ILegacyImageImportReference } from './deploymentlegacyimport.js';
4
5
  import type { IRegistryTarget } from './registry.js';
5
6
  import type { TServiceTargetPortRef } from './serviceports.js';
6
7
  export type TDeploymentOperationMode = 'existing-service' | 'greenfield';
@@ -9,24 +10,42 @@ interface IDeploymentReservationInputBase {
9
10
  organizationId: string;
10
11
  serviceId: string;
11
12
  idempotencyKey: string;
12
- sourceRevision: string;
13
13
  version: string;
14
14
  intentDigest: TSha256Digest;
15
15
  proposedConfiguration: IDeploymentPreflightProposedConfiguration;
16
16
  releaseTag: string;
17
17
  routes: IDeploymentRouteRequest[];
18
18
  }
19
- export interface IExistingServiceDeploymentReservationInput extends IDeploymentReservationInputBase {
19
+ /** A deployment whose image its deployer builds and pushes from one source revision. */
20
+ interface ISourceDeploymentOrigin {
21
+ sourceRevision: string;
22
+ legacyImport?: never;
23
+ }
24
+ /**
25
+ * A greenfield deployment whose image Cloudly imports from an attested legacy service instead:
26
+ * there is no source revision, so none is stated.
27
+ */
28
+ interface ILegacyImportDeploymentOrigin {
29
+ legacyImport: ILegacyImageImportReference;
30
+ sourceRevision?: never;
31
+ }
32
+ export interface IExistingServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ISourceDeploymentOrigin {
20
33
  mode: 'existing-service';
21
34
  expectedRolloutGeneration: number;
22
35
  expectedCurrentRolloutId?: string;
23
36
  }
24
- export interface IGreenfieldServiceDeploymentReservationInput extends IDeploymentReservationInputBase {
37
+ export interface IGreenfieldServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ISourceDeploymentOrigin {
25
38
  mode: 'greenfield';
26
39
  expectedRolloutGeneration?: never;
27
40
  expectedCurrentRolloutId?: never;
28
41
  }
29
- export type IDeploymentReservationInput = IExistingServiceDeploymentReservationInput | IGreenfieldServiceDeploymentReservationInput;
42
+ /** A greenfield successor slot whose image is a legacy service's attested image. */
43
+ export interface ILegacyImportServiceDeploymentReservationInput extends IDeploymentReservationInputBase, ILegacyImportDeploymentOrigin {
44
+ mode: 'greenfield';
45
+ expectedRolloutGeneration?: never;
46
+ expectedCurrentRolloutId?: never;
47
+ }
48
+ export type IDeploymentReservationInput = IExistingServiceDeploymentReservationInput | IGreenfieldServiceDeploymentReservationInput | ILegacyImportServiceDeploymentReservationInput;
30
49
  export type TDeploymentOperationState = 'reserving' | 'reserved' | 'provisioning' | 'awaiting-image' | 'image-recorded' | 'promoting-image' | 'rolling-out' | 'ready-for-route' | 'promoting-route' | 'verifying-route' | 'succeeded' | 'rolling-back' | 'rolled-back' | 'failed' | 'cleaning' | 'cleaned';
31
50
  export interface IDeploymentRouteRequest {
32
51
  hostname: string;
@@ -107,40 +126,46 @@ export interface IDeploymentOperationFailure {
107
126
  export interface IServiceDeploymentOperation {
108
127
  id: string;
109
128
  revision: number;
110
- data: {
111
- mode: TDeploymentOperationMode;
112
- organizationId: string;
113
- serviceId: string;
114
- actorUserId: string;
115
- idempotencyKey: string;
116
- requestDigest: TSha256Digest;
117
- sourceRevision: string;
118
- version: string;
119
- intentDigest: TSha256Digest;
120
- configurationDigest: TSha256Digest;
121
- proposedConfiguration: IDeploymentPreflightProposedConfiguration;
122
- namespace: string;
123
- registryTarget: IRegistryTarget;
124
- releaseTag: string;
125
- routes: IDeploymentRouteRequest[];
126
- claimKeys: string[];
127
- state: TDeploymentOperationState;
128
- releaseId?: string;
129
- registryRootDigest?: TSha256Digest;
130
- rolloutId?: string;
131
- rolloutGeneration?: number;
132
- routeGeneration?: number;
133
- routeVerification?: IDeploymentRouteVerificationEvidence[];
134
- /**
135
- * Most recent failure record. Set when the operation fails and retained
136
- * as audit history through 'cleaning' and 'cleaned'; cleared only by a
137
- * forward transition such as a successful retry. A populated failure does
138
- * therefore NOT imply state === 'failed'.
139
- */
140
- failure?: IDeploymentOperationFailure;
141
- createdAt: number;
142
- updatedAt: number;
143
- };
129
+ data: IServiceDeploymentOperationData;
130
+ }
131
+ /**
132
+ * An operation's durable state. It states where its image comes from exactly as its reservation
133
+ * did: a `sourceRevision` for an image its deployer builds, or a `legacyImport` (greenfield only)
134
+ * for a legacy service's attested image Cloudly imports.
135
+ */
136
+ export type IServiceDeploymentOperationData = IServiceDeploymentOperationStateData & (ISourceDeploymentOrigin | ILegacyImportDeploymentOrigin);
137
+ interface IServiceDeploymentOperationStateData {
138
+ mode: TDeploymentOperationMode;
139
+ organizationId: string;
140
+ serviceId: string;
141
+ actorUserId: string;
142
+ idempotencyKey: string;
143
+ requestDigest: TSha256Digest;
144
+ version: string;
145
+ intentDigest: TSha256Digest;
146
+ configurationDigest: TSha256Digest;
147
+ proposedConfiguration: IDeploymentPreflightProposedConfiguration;
148
+ namespace: string;
149
+ registryTarget: IRegistryTarget;
150
+ releaseTag: string;
151
+ routes: IDeploymentRouteRequest[];
152
+ claimKeys: string[];
153
+ state: TDeploymentOperationState;
154
+ releaseId?: string;
155
+ registryRootDigest?: TSha256Digest;
156
+ rolloutId?: string;
157
+ rolloutGeneration?: number;
158
+ routeGeneration?: number;
159
+ routeVerification?: IDeploymentRouteVerificationEvidence[];
160
+ /**
161
+ * Most recent failure record. Set when the operation fails and retained
162
+ * as audit history through 'cleaning' and 'cleaned'; cleared only by a
163
+ * forward transition such as a successful retry. A populated failure does
164
+ * therefore NOT imply state === 'failed'.
165
+ */
166
+ failure?: IDeploymentOperationFailure;
167
+ createdAt: number;
168
+ updatedAt: number;
144
169
  }
145
170
  export interface IServiceDeploymentStatus {
146
171
  operation: IServiceDeploymentOperation;