@serve.zone/interfaces 32.15.0 → 32.17.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.
@@ -0,0 +1,141 @@
1
+ import type { IClusterRelayAcceptedRegistration } from './cluster.js';
2
+ import type { IFleetCutoverAcceptedEvidenceReference } from './fleetcutover.js';
3
+ import type { TSha256Digest } from './immutableimage.js';
4
+ import type { IRuntimeAuthorityReference } from './runtimeassignment.js';
5
+ import type { ISecretVersionReference } from './secret.js';
6
+ /**
7
+ * The relay authority Cloudly mints for one cluster. Public metadata only: never a bearer or hash.
8
+ *
9
+ * It authenticates the cluster's whole control-plane session, exactly what the shared cluster
10
+ * authority authenticated before it, so the relay changes one credential and keeps every call it
11
+ * makes. What it adds is a generation Cloudly fences each socket against and an authority it can
12
+ * rotate without touching the shared one; its scope is not narrower. One cluster has one relay and
13
+ * therefore one such record, and rotation is its whole lifecycle: it never expires by itself.
14
+ */
15
+ export interface IClusterRelayCredential {
16
+ id: string;
17
+ clusterId: string;
18
+ /** Monotonic state: one accepted mint or rotation advances it by exactly one. */
19
+ generation: number;
20
+ /** The machine identity this credential authenticates as; Cloudly's own, never caller-chosen. */
21
+ userId: string;
22
+ /** The immutable encrypted version the bearer was delivered as. */
23
+ delivery: ISecretVersionReference;
24
+ actorId: string;
25
+ acceptedAt: number;
26
+ digest: TSha256Digest;
27
+ }
28
+ interface IClusterRelayCredentialMutationBase {
29
+ clusterId: string;
30
+ /** Retain the complete request and this ID across retries; a changed body with it fails closed. */
31
+ mutationId: string;
32
+ }
33
+ /**
34
+ * Creation is absent-only; rotation requires the exact current generation, never a minimum.
35
+ *
36
+ * There is no revocation: a rotation already ends the previous generation at commit, so it covers a
37
+ * compromised credential without a state in which the cluster has no relay authority at all.
38
+ */
39
+ export type TClusterRelayCredentialMutation = IClusterRelayCredentialMutationBase & ({
40
+ operation: 'ensure';
41
+ expectedGeneration: null;
42
+ } | {
43
+ operation: 'rotate';
44
+ expectedGeneration: number;
45
+ });
46
+ export interface IClusterRelayCredentialMutationResult {
47
+ credential: IClusterRelayCredential;
48
+ /** The shared pre-relay authority this cluster still carries; null when no record names one. */
49
+ legacy: IClusterLegacyRelayCredential | null;
50
+ /** A replay returns the original result without renewing delivery, generation or authority. */
51
+ replayed: boolean;
52
+ }
53
+ /**
54
+ * Cloudly's record of the shared cluster authority that predates the relay credential, so it can
55
+ * be named and retired. It is the machine identity every cluster-side component authenticated as;
56
+ * like the relay credential it carries no bearer and no hash.
57
+ */
58
+ export interface IClusterLegacyRelayCredential {
59
+ id: string;
60
+ clusterId: string;
61
+ userId: string;
62
+ generation: number;
63
+ state: 'active' | 'revoked';
64
+ /**
65
+ * The accepted invalidation that revoked it; null exactly while it is still accepted.
66
+ *
67
+ * The invalidation names this record at the generation before the revocation and this record
68
+ * names the invalidation at the generation after it, so neither digest covers the other's.
69
+ */
70
+ invalidation: IFleetCutoverAcceptedEvidenceReference | null;
71
+ recordedAt: number;
72
+ digest: TSha256Digest;
73
+ }
74
+ /**
75
+ * Caller intent only. Both references are compare-and-swap inputs: Cloudly dereferences them
76
+ * against its own current records and checks the relay's live socket itself.
77
+ */
78
+ export interface IClusterLegacyRelayCredentialRetirementRequest {
79
+ /** Retain the complete request and this ID across retries; a changed body with it fails closed. */
80
+ mutationId: string;
81
+ clusterId: string;
82
+ expectedLegacyGeneration: number;
83
+ /** The exact replacement relay credential generation the caller observed as current. */
84
+ replacement: IRuntimeAuthorityReference;
85
+ /** The exact accepted registration the caller observed as current. */
86
+ acceptedRegistration: IFleetCutoverAcceptedEvidenceReference;
87
+ }
88
+ /** The accepted receipt that one shared cluster authority stopped being accepted; written once. */
89
+ export interface IClusterLegacyRelayCredentialInvalidation {
90
+ id: string;
91
+ generation: 1;
92
+ clusterId: string;
93
+ /** The exact legacy generation this invalidated, read before the record advanced. */
94
+ credential: IRuntimeAuthorityReference;
95
+ /** The relay credential generation that was current when this was accepted. */
96
+ replacement: IRuntimeAuthorityReference;
97
+ /** The registration that was current, made under `replacement`, and live when this was accepted. */
98
+ acceptedRegistration: IFleetCutoverAcceptedEvidenceReference;
99
+ actorId: string;
100
+ acceptedAt: number;
101
+ digest: TSha256Digest;
102
+ }
103
+ export interface IClusterLegacyRelayCredentialRetirementResult {
104
+ credential: IClusterLegacyRelayCredential;
105
+ invalidation: IClusterLegacyRelayCredentialInvalidation;
106
+ /** A replay returns the recorded invalidation; nothing is revoked twice. */
107
+ replayed: boolean;
108
+ }
109
+ export declare const clusterRelayCredentialContract: Readonly<{
110
+ readonly credentialDigestDomain: "serve.zone/cluster-relay-credential";
111
+ readonly legacyCredentialDigestDomain: "serve.zone/cluster-relay-legacy-credential";
112
+ readonly legacyInvalidationDigestDomain: "serve.zone/cluster-relay-legacy-credential-invalidation";
113
+ }>;
114
+ /** Every reason Cloudly answers instead of mutating or retiring a cluster relay authority. */
115
+ export declare const clusterRelayCredentialRefusals: readonly ["relay-credential-absent", "legacy-credential-absent", "legacy-credential-already-retired", "legacy-retirement-replacement-not-current", "legacy-retirement-registration-not-current", "legacy-retirement-relay-not-live", "conflict"];
116
+ export type TClusterRelayCredentialRefusal = typeof clusterRelayCredentialRefusals[number];
117
+ /** Structural copy only; whether the record is the cluster's current one is Cloudly's to decide. */
118
+ export declare const snapshotClusterRelayCredential: (valueArg: unknown) => IClusterRelayCredential;
119
+ export declare const computeClusterRelayCredentialDigest: (valueArg: unknown) => Promise<TSha256Digest>;
120
+ export declare const validateClusterRelayCredential: (valueArg: unknown) => Promise<string[]>;
121
+ export declare const snapshotClusterRelayCredentialMutation: (valueArg: unknown) => TClusterRelayCredentialMutation;
122
+ export declare const snapshotClusterLegacyRelayCredential: (valueArg: unknown) => IClusterLegacyRelayCredential;
123
+ export declare const computeClusterLegacyRelayCredentialDigest: (valueArg: unknown) => Promise<TSha256Digest>;
124
+ export declare const validateClusterLegacyRelayCredential: (valueArg: unknown) => Promise<string[]>;
125
+ export declare const snapshotClusterLegacyRelayCredentialRetirementRequest: (valueArg: unknown) => IClusterLegacyRelayCredentialRetirementRequest;
126
+ export declare const snapshotClusterLegacyRelayCredentialInvalidation: (valueArg: unknown) => IClusterLegacyRelayCredentialInvalidation;
127
+ export declare const computeClusterLegacyRelayCredentialInvalidationDigest: (valueArg: unknown) => Promise<TSha256Digest>;
128
+ export declare const validateClusterLegacyRelayCredentialInvalidation: (valueArg: unknown) => Promise<string[]>;
129
+ /**
130
+ * Joins an invalidation to the shared authority it revoked and to the registration that justified
131
+ * it, which must have been made under the very replacement the invalidation names.
132
+ *
133
+ * Content join only. Cloudly must still dereference each record as its cluster's current one and
134
+ * require a live verified socket for the registration while holding its compare-and-swap.
135
+ */
136
+ export declare const bindClusterLegacyRelayCredentialInvalidation: (invalidationArg: unknown, legacyCredentialArg: unknown, acceptedRegistrationArg: unknown) => Promise<{
137
+ invalidation: IClusterLegacyRelayCredentialInvalidation;
138
+ legacyCredential: IClusterLegacyRelayCredential;
139
+ acceptedRegistration: IClusterRelayAcceptedRegistration;
140
+ }>;
141
+ export {};
@@ -0,0 +1,192 @@
1
+ import { snapshotClusterRelayAcceptedRegistration, validateClusterRelayAcceptedRegistration, } from './cluster.js';
2
+ import { captureNetwork, networkCounter, networkDigest, networkEqual, networkHash, networkId, networkRecord, networkReference, networkReferenceOf, requireNetwork, } from '../private/networkcontract.js';
3
+ export const clusterRelayCredentialContract = Object.freeze({
4
+ credentialDigestDomain: 'serve.zone/cluster-relay-credential',
5
+ legacyCredentialDigestDomain: 'serve.zone/cluster-relay-legacy-credential',
6
+ legacyInvalidationDigestDomain: 'serve.zone/cluster-relay-legacy-credential-invalidation',
7
+ });
8
+ /** Every reason Cloudly answers instead of mutating or retiring a cluster relay authority. */
9
+ export const clusterRelayCredentialRefusals = Object.freeze([
10
+ /** A rotation named a cluster that has no relay credential yet. */
11
+ 'relay-credential-absent',
12
+ /** A retirement named a cluster whose shared authority no record names. */
13
+ 'legacy-credential-absent',
14
+ /** The shared authority is already revoked; its invalidation is written once. */
15
+ 'legacy-credential-already-retired',
16
+ /** The named replacement is not the cluster's relay credential at its current generation. */
17
+ 'legacy-retirement-replacement-not-current',
18
+ /** The named registration is not current, or it was made under another credential generation. */
19
+ 'legacy-retirement-registration-not-current',
20
+ /** No verified socket carries the named registration at the instant of acceptance. */
21
+ 'legacy-retirement-relay-not-live',
22
+ /** An expected generation was not the exact current one, or a mutation ID met a changed body. */
23
+ 'conflict',
24
+ ]);
25
+ const fail = () => { throw new Error('Cluster relay credential contract rejected.'); };
26
+ const safe = (operationArg) => {
27
+ try {
28
+ return operationArg();
29
+ }
30
+ catch {
31
+ return fail();
32
+ }
33
+ };
34
+ const secretVersionReference = (valueArg) => {
35
+ const value = networkRecord(valueArg, ['secretId', 'secretVersionId']);
36
+ return {
37
+ secretId: networkId(value.secretId),
38
+ secretVersionId: networkId(value.secretVersionId),
39
+ };
40
+ };
41
+ /** Structural copy only; whether the record is the cluster's current one is Cloudly's to decide. */
42
+ export const snapshotClusterRelayCredential = (valueArg) => safe(() => {
43
+ const value = networkRecord(captureNetwork(valueArg), [
44
+ 'id', 'clusterId', 'generation', 'userId', 'delivery', 'actorId', 'acceptedAt', 'digest',
45
+ ]);
46
+ return {
47
+ id: networkId(value.id),
48
+ clusterId: networkId(value.clusterId),
49
+ generation: networkCounter(value.generation),
50
+ userId: networkId(value.userId),
51
+ delivery: secretVersionReference(value.delivery),
52
+ actorId: networkId(value.actorId),
53
+ acceptedAt: networkCounter(value.acceptedAt),
54
+ digest: networkDigest(value.digest),
55
+ };
56
+ });
57
+ export const computeClusterRelayCredentialDigest = async (valueArg) => networkHash(clusterRelayCredentialContract.credentialDigestDomain, snapshotClusterRelayCredential(valueArg));
58
+ export const validateClusterRelayCredential = async (valueArg) => {
59
+ try {
60
+ const value = snapshotClusterRelayCredential(valueArg);
61
+ return value.digest === await computeClusterRelayCredentialDigest(value)
62
+ ? [] : ['cluster relay credential digest does not match'];
63
+ }
64
+ catch {
65
+ return ['cluster relay credential must use its exact canonical schema'];
66
+ }
67
+ };
68
+ export const snapshotClusterRelayCredentialMutation = (valueArg) => safe(() => {
69
+ const value = networkRecord(captureNetwork(valueArg), [
70
+ 'clusterId', 'mutationId', 'operation', 'expectedGeneration',
71
+ ]);
72
+ const base = { clusterId: networkId(value.clusterId), mutationId: networkId(value.mutationId) };
73
+ const operation = value.operation;
74
+ requireNetwork(operation === 'ensure' || operation === 'rotate');
75
+ if (operation === 'ensure') {
76
+ requireNetwork(value.expectedGeneration === null);
77
+ return { ...base, operation, expectedGeneration: null };
78
+ }
79
+ return { ...base, operation, expectedGeneration: networkCounter(value.expectedGeneration) };
80
+ });
81
+ export const snapshotClusterLegacyRelayCredential = (valueArg) => safe(() => {
82
+ const value = networkRecord(captureNetwork(valueArg), [
83
+ 'id', 'clusterId', 'userId', 'generation', 'state', 'invalidation', 'recordedAt', 'digest',
84
+ ]);
85
+ const state = value.state;
86
+ requireNetwork(state === 'active' || state === 'revoked');
87
+ const generation = networkCounter(value.generation);
88
+ const invalidation = value.invalidation === null ? null : networkReference(value.invalidation);
89
+ // Revoked exactly when an invalidation names it, and only as the successor of an active record.
90
+ requireNetwork(state === 'active'
91
+ ? invalidation === null
92
+ : invalidation !== null && invalidation.generation === 1 && generation > 1);
93
+ return {
94
+ id: networkId(value.id),
95
+ clusterId: networkId(value.clusterId),
96
+ userId: networkId(value.userId),
97
+ generation,
98
+ state,
99
+ invalidation,
100
+ recordedAt: networkCounter(value.recordedAt),
101
+ digest: networkDigest(value.digest),
102
+ };
103
+ });
104
+ export const computeClusterLegacyRelayCredentialDigest = async (valueArg) => networkHash(clusterRelayCredentialContract.legacyCredentialDigestDomain, snapshotClusterLegacyRelayCredential(valueArg));
105
+ export const validateClusterLegacyRelayCredential = async (valueArg) => {
106
+ try {
107
+ const value = snapshotClusterLegacyRelayCredential(valueArg);
108
+ return value.digest === await computeClusterLegacyRelayCredentialDigest(value)
109
+ ? [] : ['cluster legacy relay credential digest does not match'];
110
+ }
111
+ catch {
112
+ return ['cluster legacy relay credential must use its exact canonical schema'];
113
+ }
114
+ };
115
+ export const snapshotClusterLegacyRelayCredentialRetirementRequest = (valueArg) => safe(() => {
116
+ const value = networkRecord(captureNetwork(valueArg), [
117
+ 'mutationId', 'clusterId', 'expectedLegacyGeneration', 'replacement', 'acceptedRegistration',
118
+ ]);
119
+ return {
120
+ mutationId: networkId(value.mutationId),
121
+ clusterId: networkId(value.clusterId),
122
+ expectedLegacyGeneration: networkCounter(value.expectedLegacyGeneration),
123
+ replacement: networkReference(value.replacement),
124
+ acceptedRegistration: networkReference(value.acceptedRegistration),
125
+ };
126
+ });
127
+ export const snapshotClusterLegacyRelayCredentialInvalidation = (valueArg) => safe(() => {
128
+ const value = networkRecord(captureNetwork(valueArg), [
129
+ 'id', 'generation', 'clusterId', 'credential', 'replacement', 'acceptedRegistration',
130
+ 'actorId', 'acceptedAt', 'digest',
131
+ ]);
132
+ requireNetwork(value.generation === 1);
133
+ return {
134
+ id: networkId(value.id),
135
+ generation: 1,
136
+ clusterId: networkId(value.clusterId),
137
+ credential: networkReference(value.credential),
138
+ replacement: networkReference(value.replacement),
139
+ acceptedRegistration: networkReference(value.acceptedRegistration),
140
+ actorId: networkId(value.actorId),
141
+ acceptedAt: networkCounter(value.acceptedAt),
142
+ digest: networkDigest(value.digest),
143
+ };
144
+ });
145
+ export const computeClusterLegacyRelayCredentialInvalidationDigest = async (valueArg) => networkHash(clusterRelayCredentialContract.legacyInvalidationDigestDomain, snapshotClusterLegacyRelayCredentialInvalidation(valueArg));
146
+ export const validateClusterLegacyRelayCredentialInvalidation = async (valueArg) => {
147
+ try {
148
+ const value = snapshotClusterLegacyRelayCredentialInvalidation(valueArg);
149
+ return value.digest === await computeClusterLegacyRelayCredentialInvalidationDigest(value)
150
+ ? [] : ['cluster legacy relay credential invalidation digest does not match'];
151
+ }
152
+ catch {
153
+ return ['cluster legacy relay credential invalidation must use its exact canonical schema'];
154
+ }
155
+ };
156
+ /**
157
+ * The active record is named exactly. The revoked one is that record's successor, so it is named
158
+ * by identity and the generation before it, and it names the invalidation back.
159
+ */
160
+ const invalidationNamesLegacyCredential = (invalidationArg, legacyCredentialArg) => legacyCredentialArg.state === 'active'
161
+ ? networkEqual(invalidationArg.credential, networkReferenceOf(legacyCredentialArg))
162
+ : invalidationArg.credential.id === legacyCredentialArg.id
163
+ && invalidationArg.credential.generation === legacyCredentialArg.generation - 1
164
+ && networkEqual(legacyCredentialArg.invalidation, networkReferenceOf(invalidationArg));
165
+ /**
166
+ * Joins an invalidation to the shared authority it revoked and to the registration that justified
167
+ * it, which must have been made under the very replacement the invalidation names.
168
+ *
169
+ * Content join only. Cloudly must still dereference each record as its cluster's current one and
170
+ * require a live verified socket for the registration while holding its compare-and-swap.
171
+ */
172
+ export const bindClusterLegacyRelayCredentialInvalidation = async (invalidationArg, legacyCredentialArg, acceptedRegistrationArg) => {
173
+ try {
174
+ const invalidation = snapshotClusterLegacyRelayCredentialInvalidation(invalidationArg);
175
+ const legacyCredential = snapshotClusterLegacyRelayCredential(legacyCredentialArg);
176
+ const acceptedRegistration = snapshotClusterRelayAcceptedRegistration(acceptedRegistrationArg);
177
+ if ((await validateClusterLegacyRelayCredentialInvalidation(invalidation)).length
178
+ || (await validateClusterLegacyRelayCredential(legacyCredential)).length
179
+ || (await validateClusterRelayAcceptedRegistration(acceptedRegistration)).length)
180
+ fail();
181
+ requireNetwork(legacyCredential.clusterId === invalidation.clusterId
182
+ && acceptedRegistration.clusterId === invalidation.clusterId
183
+ && invalidationNamesLegacyCredential(invalidation, legacyCredential)
184
+ && networkEqual(invalidation.acceptedRegistration, networkReferenceOf(acceptedRegistration))
185
+ && networkEqual(invalidation.replacement, acceptedRegistration.credential));
186
+ return { invalidation, legacyCredential, acceptedRegistration };
187
+ }
188
+ catch {
189
+ return fail();
190
+ }
191
+ };
192
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3RlcnJlbGF5Y3JlZGVudGlhbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvY2x1c3RlcnJlbGF5Y3JlZGVudGlhbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQ0wsd0NBQXdDLEVBQ3hDLHdDQUF3QyxHQUN6QyxNQUFNLGNBQWMsQ0FBQztBQUt0QixPQUFPLEVBQ0wsY0FBYyxFQUNkLGNBQWMsRUFDZCxhQUFhLEVBQ2IsWUFBWSxFQUNaLFdBQVcsRUFDWCxTQUFTLEVBQ1QsYUFBYSxFQUNiLGdCQUFnQixFQUNoQixrQkFBa0IsRUFDbEIsY0FBYyxHQUNmLE1BQU0sK0JBQStCLENBQUM7QUE4R3ZDLE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsc0JBQXNCLEVBQUUscUNBQXFDO0lBQzdELDRCQUE0QixFQUFFLDRDQUE0QztJQUMxRSw4QkFBOEIsRUFBRSx5REFBeUQ7Q0FDakYsQ0FBQyxDQUFDO0FBRVosOEZBQThGO0FBQzlGLE1BQU0sQ0FBQyxNQUFNLDhCQUE4QixHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDMUQsbUVBQW1FO0lBQ25FLHlCQUF5QjtJQUN6QiwyRUFBMkU7SUFDM0UsMEJBQTBCO0lBQzFCLGlGQUFpRjtJQUNqRixtQ0FBbUM7SUFDbkMsNkZBQTZGO0lBQzdGLDJDQUEyQztJQUMzQyxpR0FBaUc7SUFDakcsNENBQTRDO0lBQzVDLHNGQUFzRjtJQUN0RixrQ0FBa0M7SUFDbEMsaUdBQWlHO0lBQ2pHLFVBQVU7Q0FDRixDQUFDLENBQUM7QUFJWixNQUFNLElBQUksR0FBRyxHQUFVLEVBQUUsR0FBRyxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDOUYsTUFBTSxJQUFJLEdBQUcsQ0FBSSxZQUFxQixFQUFLLEVBQUU7SUFDM0MsSUFBSSxDQUFDO1FBQUMsT0FBTyxZQUFZ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
@@ -35,6 +35,33 @@ export interface IFleetCutoverMountTargetIdentityInput {
35
35
  /** Docker Engine Mount.Destination exact UTF-8 bytes; never normalize. */
36
36
  target: string;
37
37
  }
38
+ export interface IFleetCutoverDockerMountInput {
39
+ /** Raw Docker mount type. Unsupported values produce an explicit unknown descriptor. */
40
+ type: string;
41
+ /** Exact source bytes from the Docker object being described; never substitute another field. */
42
+ source: string;
43
+ /** Exact container target bytes from the Docker object being described. */
44
+ target: string;
45
+ readOnly: boolean;
46
+ }
47
+ /**
48
+ * Value-free correlation between one local physical mount and its service-spec mount identity.
49
+ * The two identities stay distinct: a Docker volume's local Source is a host path while its
50
+ * service Source and local Name are the logical volume name.
51
+ */
52
+ export interface IFleetCutoverPhysicalMountCorrelation {
53
+ physicalMountIdentityDigest: TSha256Digest;
54
+ serviceMountIdentityDigest: TSha256Digest;
55
+ correlationIdentityDigest: TSha256Digest;
56
+ }
57
+ export interface IFleetCutoverPhysicalMountEvidence {
58
+ mount: IFleetCutoverMountDescriptor;
59
+ correlation: IFleetCutoverPhysicalMountCorrelation;
60
+ }
61
+ export interface IFleetCutoverDockerConstraintSetIdentity {
62
+ constraintCount: number;
63
+ constraintSetDigest: TSha256Digest;
64
+ }
38
65
  export type TFleetCutoverDockerTaskState = 'new' | 'allocated' | 'pending' | 'assigned' | 'accepted' | 'preparing' | 'ready' | 'starting' | 'running' | 'complete' | 'shutdown' | 'failed' | 'rejected' | 'remove' | 'orphaned';
39
66
  export type TFleetCutoverDockerDesiredState = TFleetCutoverDockerTaskState;
40
67
  export type TFleetCutoverDockerServiceMode = {
@@ -83,6 +110,11 @@ export interface IFleetCutoverPhysicalContainer {
83
110
  imageId: TSha256Digest;
84
111
  serviceIdLabel: string | null;
85
112
  mounts: IFleetCutoverMountDescriptor[];
113
+ /**
114
+ * Optional only for compatibility with observations predating correlation evidence. When
115
+ * present it is a complete one-to-one mapping of mounts. Absence establishes no mount join.
116
+ */
117
+ mountCorrelations?: IFleetCutoverPhysicalMountCorrelation[];
86
118
  }
87
119
  export interface IFleetCutoverContainerSetIdentity {
88
120
  containerCount: number;
@@ -554,6 +586,9 @@ export declare const fleetCutoverContract: Readonly<{
554
586
  readonly maximumNodes: 1024;
555
587
  readonly maximumContainersPerNode: 512;
556
588
  readonly maximumMountsPerOwner: 32;
589
+ readonly maximumConstraintsPerService: 256;
590
+ readonly maximumDockerTextBytes: 4096;
591
+ readonly maximumDockerServiceSpecCanonicalBytes: 1048576;
557
592
  readonly maximumPreparedVolumeHandoffMutationEntries: 32;
558
593
  /** Every admitted task has one node placement and at most 32 mounts: 4096 * 32. */
559
594
  readonly maximumPreparedVolumeHandoffs: number;
@@ -564,6 +599,9 @@ export declare const fleetCutoverContract: Readonly<{
564
599
  readonly mountSourceIdentityDigestDomain: "serve.zone/fleet-cutover-mount-source-identity";
565
600
  readonly mountTargetIdentityDigestDomain: "serve.zone/fleet-cutover-mount-target-identity";
566
601
  readonly mountIdentityDigestDomain: "serve.zone/fleet-cutover-mount-identity";
602
+ readonly mountCorrelationIdentityDigestDomain: "serve.zone/fleet-cutover-mount-correlation-identity";
603
+ readonly dockerServiceSpecDigestDomain: "serve.zone/fleet-cutover-docker-service-spec";
604
+ readonly dockerConstraintSetDigestDomain: "serve.zone/fleet-cutover-docker-constraint-set";
567
605
  readonly containerSetIdentityDigestDomain: "serve.zone/fleet-cutover-container-set-identity";
568
606
  readonly localNodeSnapshotDigestDomain: "serve.zone/fleet-cutover-local-node-snapshot";
569
607
  readonly managerSnapshotDigestDomain: "serve.zone/fleet-cutover-manager-snapshot";
@@ -588,9 +626,36 @@ export declare const fleetCutoverContract: Readonly<{
588
626
  pallet: null;
589
627
  }>;
590
628
  }>;
629
+ /** Docker labels with established historical producer semantics. */
630
+ export declare const fleetCutoverDockerLabelNames: Readonly<{
631
+ readonly serviceId: "serve.zone.serviceId";
632
+ }>;
633
+ /**
634
+ * Hash the complete open-schema Docker Service.Spec object. Unknown response properties are
635
+ * intentionally included: this helper returns only the digest and never a retained projection.
636
+ */
637
+ export declare const computeFleetCutoverDockerServiceSpecDigest: (valueArg: unknown) => Promise<TSha256Digest>;
638
+ /** Sort exact constraint strings while preserving duplicate entries, then return value-free identity. */
639
+ export declare const computeFleetCutoverDockerConstraintSetIdentity: (valueArg: unknown) => Promise<IFleetCutoverDockerConstraintSetIdentity>;
640
+ export declare const snapshotFleetCutoverDockerDesiredTaskCount: (valueArg: unknown) => number;
641
+ export declare const validateFleetCutoverDockerDesiredTaskCount: (valueArg: unknown) => string[];
642
+ /** Apply only the documented exact cutover mount classifications; no path normalization occurs. */
643
+ export declare const classifyFleetCutoverDockerMount: (valueArg: IFleetCutoverDockerMountInput) => Pick<IFleetCutoverMountDescriptor, "type" | "classification">;
644
+ export declare const createFleetCutoverMountDescriptor: (valueArg: IFleetCutoverDockerMountInput) => Promise<IFleetCutoverMountDescriptor>;
645
+ /** Read only consumed Service.Spec Mount fields; future Docker response properties are ignored. */
646
+ export declare const createFleetCutoverDockerServiceMountDescriptor: (valueArg: unknown) => Promise<IFleetCutoverMountDescriptor>;
647
+ /** Read only consumed local MountPoint fields and retain the exact physical Source identity. */
648
+ export declare const createFleetCutoverDockerLocalMountDescriptor: (valueArg: unknown) => Promise<IFleetCutoverMountDescriptor>;
591
649
  export declare const computeFleetCutoverMountSourceIdentityDigest: (valueArg: IFleetCutoverMountSourceIdentityInput) => Promise<TSha256Digest>;
592
650
  export declare const computeFleetCutoverMountTargetIdentityDigest: (valueArg: IFleetCutoverMountTargetIdentityInput) => Promise<TSha256Digest>;
593
651
  export declare const computeFleetCutoverMountIdentityDigest: (valueArg: Omit<IFleetCutoverMountDescriptor, "mountIdentityDigest">) => Promise<TSha256Digest>;
652
+ export declare const computeFleetCutoverMountCorrelationIdentityDigest: (valueArg: Omit<IFleetCutoverPhysicalMountCorrelation, "correlationIdentityDigest">) => Promise<TSha256Digest>;
653
+ /**
654
+ * Produce both the exact local physical descriptor and its logical service-mount correlation.
655
+ * For a volume only MountPoint.Name supplies the logical source. Unknown types and a missing or
656
+ * empty volume Name are refused because they cannot establish a truthful service join.
657
+ */
658
+ export declare const createFleetCutoverDockerPhysicalMountEvidence: (valueArg: unknown) => Promise<IFleetCutoverPhysicalMountEvidence>;
594
659
  export declare const computeFleetCutoverContainerSetIdentityDigest: (containerIdsArg: string[]) => Promise<TSha256Digest>;
595
660
  export declare const snapshotFleetCutoverLocalNodeSnapshot: (valueArg: unknown) => IFleetCutoverLocalNodeSnapshot;
596
661
  export declare const computeFleetCutoverLocalNodeSnapshotDigest: (valueArg: unknown) => Promise<TSha256Digest>;