@serve.zone/interfaces 32.14.0 → 32.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,8 @@ import type { IRuntimeAssignment, IRuntimeAuthorityReference, IRuntimeController
3
3
  import { snapshotRuntimeAssignment, validateRuntimeAssignment } from './runtimeassignment.js';
4
4
  import { normalizeServiceAbsolutePath } from './service.js';
5
5
  import type { IResolvedFilesystemStorageBinding } from '../platform/storage.js';
6
+ import type { IAppStoreFilesystemStorageRequest } from '../appstore/types.js';
7
+ import { createStorageRequestSha256, normalizeStorageRequest } from '../platform/storage.js';
6
8
  import { canonicalizeStrictJson, createCanonicalJsonSha256Hex } from '../private/canonicaljson.js';
7
9
  import { isClusterNodeId } from '../private/nodeidentifier.js';
8
10
 
@@ -16,6 +18,35 @@ export interface IRuntimeStorageClaimBinding extends Pick<IResolvedFilesystemSto
16
18
  capabilities: { durability: 'persistent'; topology: 'singleNode' };
17
19
  }
18
20
 
21
+ /**
22
+ * Cloudly's requested filesystem binding identity before the storage provider reports readiness.
23
+ * Provider-produced observation, resource and capability fields cannot be stated in this shape.
24
+ */
25
+ export interface IRuntimeStorageClaimRequestedFilesystemBinding {
26
+ serviceId: string;
27
+ request: IAppStoreFilesystemStorageRequest & {
28
+ accessMode: 'ReadWriteOnce';
29
+ reclaimPolicy: 'retain';
30
+ };
31
+ requestDigest: string;
32
+ }
33
+
34
+ /** Cloudly-owned claim identity and desired request before a provider-produced ready binding exists. */
35
+ export interface IRuntimeStorageClaimIntent {
36
+ id: string;
37
+ generation: number;
38
+ previous: IRuntimeAuthorityReference | null;
39
+ controller: IRuntimeController & { kind: 'cloudly' };
40
+ organizationId: string;
41
+ clusterId: string;
42
+ nodeId: string;
43
+ runtimeNamespace: string;
44
+ serviceId: string;
45
+ requestedBinding: IRuntimeStorageClaimRequestedFilesystemBinding;
46
+ reclaimPolicy: 'retain';
47
+ digest: TSha256Digest;
48
+ }
49
+
19
50
  /** Cloudly authority for one retained, node-local volume; no host path or attachment attempt. */
20
51
  export interface IRuntimeStorageClaim {
21
52
  id: string;
@@ -33,6 +64,7 @@ export interface IRuntimeStorageClaim {
33
64
  }
34
65
 
35
66
  export const runtimeStorageClaimContract = Object.freeze({
67
+ intentDigestDomain: 'serve.zone/runtime-storage-claim-intent',
36
68
  digestDomain: 'serve.zone/runtime-storage-claim',
37
69
  accessMode: 'ReadWriteOnce',
38
70
  reclaimPolicy: 'retain',
@@ -71,6 +103,10 @@ const digest = (valueArg: unknown): TSha256Digest => {
71
103
  requireValue(typeof valueArg === 'string' && /^sha256:[a-f0-9]{64}$/.test(valueArg));
72
104
  return valueArg as TSha256Digest;
73
105
  };
106
+ const requestDigest = (valueArg: unknown): string => {
107
+ requireValue(typeof valueArg === 'string' && /^[a-f0-9]{64}$/.test(valueArg));
108
+ return valueArg as string;
109
+ };
74
110
  const reference = (valueArg: unknown): IRuntimeAuthorityReference => {
75
111
  const value = record(valueArg, ['id', 'generation', 'digest']);
76
112
  return { id: assignmentId(value.id), generation: generation(value.generation), digest: digest(value.digest) };
@@ -86,26 +122,72 @@ const mountPath = (valueArg: unknown): string => {
86
122
  && !valueArg.startsWith('/run/secrets/') && !'/run/secrets'.startsWith(`${valueArg}/`));
87
123
  return valueArg as string;
88
124
  };
125
+ const requestedBinding = (valueArg: unknown): IRuntimeStorageClaimRequestedFilesystemBinding => {
126
+ const value = record(valueArg, ['serviceId', 'request', 'requestDigest']);
127
+ const request = normalizeStorageRequest(value.request);
128
+ requireValue(request.kind === 'filesystem' && request.accessMode === 'ReadWriteOnce'
129
+ && request.reclaimPolicy === 'retain' && mountPath(request.mountPath) === request.mountPath);
130
+ return { serviceId: assignmentId(value.serviceId),
131
+ request: request as IRuntimeStorageClaimRequestedFilesystemBinding['request'],
132
+ requestDigest: requestDigest(value.requestDigest) };
133
+ };
89
134
  const binding = (valueArg: unknown): IRuntimeStorageClaimBinding => {
90
135
  const value = record(valueArg, ['id', 'serviceId', 'requestId', 'requestDigest', 'kind',
91
136
  'generation', 'observedGeneration', 'status', 'policy', 'resourceRef', 'mountPath',
92
137
  'accessMode', 'capabilities']);
93
138
  requireValue(value.kind === 'filesystem' && value.status === 'ready'
94
- && value.accessMode === 'ReadWriteOnce'
95
- && typeof value.requestDigest === 'string' && /^[a-f0-9]{64}$/.test(value.requestDigest));
139
+ && value.accessMode === 'ReadWriteOnce');
96
140
  const policy = record(value.policy, ['classId', 'revision']);
97
141
  const capabilities = record(value.capabilities, ['durability', 'topology']);
98
142
  requireValue(capabilities.durability === 'persistent' && capabilities.topology === 'singleNode');
99
143
  const bindingGeneration = generation(value.generation);
100
144
  requireValue(generation(value.observedGeneration) === bindingGeneration);
101
145
  return { id: id(value.id), serviceId: assignmentId(value.serviceId), requestId: id(value.requestId),
102
- requestDigest: value.requestDigest as string, kind: 'filesystem', generation: bindingGeneration,
146
+ requestDigest: requestDigest(value.requestDigest), kind: 'filesystem', generation: bindingGeneration,
103
147
  observedGeneration: bindingGeneration, status: 'ready',
104
148
  policy: { classId: id(policy.classId), revision: id(policy.revision) },
105
149
  resourceRef: id(value.resourceRef), mountPath: mountPath(value.mountPath),
106
150
  accessMode: 'ReadWriteOnce', capabilities: { durability: 'persistent', topology: 'singleNode' } };
107
151
  };
108
152
 
153
+ /** Detached exact-schema snapshot. This shape deliberately cannot state provider readiness. */
154
+ export const snapshotRuntimeStorageClaimIntent = (valueArg: unknown): IRuntimeStorageClaimIntent => {
155
+ const value = record(valueArg, ['id', 'generation', 'previous', 'controller', 'organizationId',
156
+ 'clusterId', 'nodeId', 'runtimeNamespace', 'serviceId', 'requestedBinding', 'reclaimPolicy', 'digest']);
157
+ const claimId = assignmentId(value.id), claimGeneration = generation(value.generation);
158
+ const previous = value.previous === null ? null : reference(value.previous);
159
+ requireValue(claimGeneration === 1 ? previous === null
160
+ : previous?.id === claimId && previous.generation === claimGeneration - 1);
161
+ requireValue(isClusterNodeId(value.nodeId) && value.reclaimPolicy === 'retain');
162
+ const claimBinding = requestedBinding(value.requestedBinding), serviceId = assignmentId(value.serviceId);
163
+ requireValue(claimBinding.serviceId === serviceId);
164
+ return { id: claimId, generation: claimGeneration, previous, controller: controller(value.controller),
165
+ organizationId: assignmentId(value.organizationId), clusterId: id(value.clusterId),
166
+ nodeId: value.nodeId as string, runtimeNamespace: assignmentId(value.runtimeNamespace), serviceId,
167
+ requestedBinding: claimBinding, reclaimPolicy: 'retain', digest: digest(value.digest) };
168
+ };
169
+
170
+ export const computeRuntimeStorageClaimIntentDigest = async (valueArg: unknown): Promise<TSha256Digest> => {
171
+ const { digest: _digest, ...payload } = snapshotRuntimeStorageClaimIntent(valueArg);
172
+ return `sha256:${await createCanonicalJsonSha256Hex(canonicalizeStrictJson({
173
+ domain: runtimeStorageClaimContract.intentDigestDomain, payload,
174
+ }, fail), fail)}`;
175
+ };
176
+
177
+ export const validateRuntimeStorageClaimIntent = async (valueArg: unknown): Promise<string[]> => {
178
+ try {
179
+ const intent = snapshotRuntimeStorageClaimIntent(valueArg);
180
+ if (intent.requestedBinding.requestDigest
181
+ !== await createStorageRequestSha256(intent.requestedBinding.request)) {
182
+ return ['runtime storage claim intent request digest does not match'];
183
+ }
184
+ return intent.digest === await computeRuntimeStorageClaimIntentDigest(intent)
185
+ ? [] : ['runtime storage claim intent digest does not match'];
186
+ } catch {
187
+ return ['runtime storage claim intent must use its exact canonical schema'];
188
+ }
189
+ };
190
+
109
191
  /** Detached exact-schema snapshot. Digest and authoritative provider readiness are separate checks. */
110
192
  export const snapshotRuntimeStorageClaim = (valueArg: unknown): IRuntimeStorageClaim => {
111
193
  const value = record(valueArg, ['id', 'generation', 'previous', 'controller', 'organizationId',
@@ -140,6 +222,43 @@ export const validateRuntimeStorageClaim = async (valueArg: unknown): Promise<st
140
222
  }
141
223
  };
142
224
 
225
+ /**
226
+ * Joins Cloudly-owned identity and request facts to a provider-produced ready binding.
227
+ * This content check does not authenticate either producer or grant attachment authority.
228
+ */
229
+ export const bindRuntimeStorageClaimToIntent = async (
230
+ claimArg: unknown,
231
+ intentArg: unknown,
232
+ ): Promise<{ claim: IRuntimeStorageClaim; intent: IRuntimeStorageClaimIntent }> => {
233
+ const claim = snapshotRuntimeStorageClaim(claimArg);
234
+ const intent = snapshotRuntimeStorageClaimIntent(intentArg);
235
+ if ((await validateRuntimeStorageClaim(claim)).length
236
+ || (await validateRuntimeStorageClaimIntent(intent)).length) fail();
237
+ const claimPrevious = claim.previous, intentPrevious = intent.previous;
238
+ requireValue(claim.id === intent.id
239
+ && claim.generation === intent.generation
240
+ && (claimPrevious === null
241
+ ? intentPrevious === null
242
+ : intentPrevious !== null
243
+ && claimPrevious.id === intentPrevious.id
244
+ && claimPrevious.generation === intentPrevious.generation
245
+ && claimPrevious.digest === intentPrevious.digest)
246
+ && claim.controller.id === intent.controller.id
247
+ && claim.controller.epoch === intent.controller.epoch
248
+ && claim.organizationId === intent.organizationId
249
+ && claim.clusterId === intent.clusterId
250
+ && claim.nodeId === intent.nodeId
251
+ && claim.runtimeNamespace === intent.runtimeNamespace
252
+ && claim.serviceId === intent.serviceId
253
+ && claim.binding.serviceId === intent.requestedBinding.serviceId
254
+ && claim.binding.requestId === intent.requestedBinding.request.id
255
+ && claim.binding.requestDigest === intent.requestedBinding.requestDigest
256
+ && claim.binding.mountPath === intent.requestedBinding.request.mountPath
257
+ && claim.binding.accessMode === intent.requestedBinding.request.accessMode
258
+ && claim.reclaimPolicy === intent.reclaimPolicy);
259
+ return { claim, intent };
260
+ };
261
+
143
262
  /** Content binding only; controller authentication, provider checks and attachment fencing remain owner-owned. */
144
263
  export const bindRuntimeStorageClaimToAssignment = async (
145
264
  claimArg: unknown, assignmentArg: unknown, expectedClusterIdArg: unknown,
@@ -5,6 +5,10 @@ import type { IServiceRuntimeSpec } from '../data/serviceruntime.js';
5
5
  import type {
6
6
  IFleetCutoverAcceptedEvidenceReference,
7
7
  IFleetCutoverCorestoreAdoptionReceipt,
8
+ IFleetCutoverPreparedVolumeHandoffAggregate,
9
+ IFleetCutoverPreparedVolumeHandoffIntent,
10
+ IFleetCutoverPreparedVolumeHandoffMutation,
11
+ IFleetCutoverStorageAdoptionBundle,
8
12
  IFleetCutoverIngressDesignation,
9
13
  IFleetCutoverIngressPreparationRequest,
10
14
  IFleetCutoverIngressAuthority,
@@ -15,6 +19,7 @@ import type {
15
19
  TFleetCutoverRuntimeSpecRelease,
16
20
  TFleetCutoverVolumeFence,
17
21
  } from '../data/fleetcutover.js';
22
+ import type { IRuntimeStorageClaim } from '../data/runtimestorageclaim.js';
18
23
 
19
24
  export interface IReq_Any_Cloudly_GetFleetCutoverScope
20
25
  extends plugins.typedrequestInterfaces.implementsTR<
@@ -44,6 +49,28 @@ extends plugins.typedrequestInterfaces.implementsTR<
44
49
  response: { scope: IFleetCutoverScope };
45
50
  }
46
51
 
52
+ /**
53
+ * Admin CAS input selects authenticated legacy mounts and desired filesystem requests. Cloudly
54
+ * derives every owner identity and evidence reference, then rewrites one complete aggregate
55
+ * revision without truncating untouched entries.
56
+ */
57
+ export interface IReq_Admin_Cloudly_PrepareFleetCutoverVolumeHandoffs
58
+ extends plugins.typedrequestInterfaces.implementsTR<
59
+ plugins.typedrequestInterfaces.ITypedRequest,
60
+ IReq_Admin_Cloudly_PrepareFleetCutoverVolumeHandoffs
61
+ > {
62
+ method: 'prepareFleetCutoverVolumeHandoffs';
63
+ request: {
64
+ identity: IIdentityCredential;
65
+ mutation: IFleetCutoverPreparedVolumeHandoffMutation;
66
+ };
67
+ response: {
68
+ aggregate: IFleetCutoverPreparedVolumeHandoffAggregate;
69
+ handoffs: IFleetCutoverPreparedVolumeHandoffIntent[];
70
+ replayed: boolean;
71
+ };
72
+ }
73
+
47
74
  /**
48
75
  * Forward-only legal phase CAS. All receipt references are dereferenced by Cloudly in the same
49
76
  * transaction; caller-supplied records or booleans never satisfy a cutover condition.
@@ -104,7 +131,10 @@ extends plugins.typedrequestInterfaces.implementsTR<
104
131
  response: { authority: IFleetCutoverIngressAuthority | null };
105
132
  }
106
133
 
107
- /** Root-local only. Holding an intent creates negative storage authority and no runtime session. */
134
+ /**
135
+ * @deprecated Published 32.x declaration retained for wire compatibility. Production owners do
136
+ * not register it; remove at the next protocol major.
137
+ */
108
138
  export interface IReq_PalletLocal_Pallet_HoldFleetCutoverVolumeFence
109
139
  extends plugins.typedrequestInterfaces.implementsTR<
110
140
  plugins.typedrequestInterfaces.ITypedRequest,
@@ -115,6 +145,17 @@ extends plugins.typedrequestInterfaces.implementsTR<
115
145
  response: { fence: Extract<TFleetCutoverVolumeFence, { state: 'held' }> };
116
146
  }
117
147
 
148
+ /** Spark calls only this fixed root-private Pallet socket operation for a prepared handoff. */
149
+ export interface IReq_SparkLocal_Pallet_HoldPreparedFleetCutoverVolumeFence
150
+ extends plugins.typedrequestInterfaces.implementsTR<
151
+ plugins.typedrequestInterfaces.ITypedRequest,
152
+ IReq_SparkLocal_Pallet_HoldPreparedFleetCutoverVolumeFence
153
+ > {
154
+ method: 'holdPreparedFleetCutoverVolumeFence';
155
+ request: { fenceId: string; handoffIntent: IFleetCutoverPreparedVolumeHandoffIntent };
156
+ response: { fence: Extract<TFleetCutoverVolumeFence, { state: 'held' }> };
157
+ }
158
+
118
159
  export interface IReq_PalletLocal_Pallet_GetFleetCutoverVolumeFence
119
160
  extends plugins.typedrequestInterfaces.implementsTR<
120
161
  plugins.typedrequestInterfaces.ITypedRequest,
@@ -126,8 +167,8 @@ extends plugins.typedrequestInterfaces.implementsTR<
126
167
  }
127
168
 
128
169
  /**
129
- * Pallet consumes the local fence only around a current accepted Corestore adoption result. The
130
- * receipt carries the real ready storage-claim reference; the earlier handoff intent does not.
170
+ * @deprecated Ownership-inverted published 32.x declaration. Production owners do not register
171
+ * or adapt it; remove at the next protocol major.
131
172
  */
132
173
  export interface IReq_PalletLocal_Pallet_ConsumeFleetCutoverVolumeFence
133
174
  extends plugins.typedrequestInterfaces.implementsTR<
@@ -143,8 +184,22 @@ extends plugins.typedrequestInterfaces.implementsTR<
143
184
  }
144
185
 
145
186
  /**
146
- * Corestore owns the one-shot initial lineage/RWO CAS. The local caller supplies the held fence
147
- * and server-owned handoff intent; Corestore returns the claim only after its provider is ready.
187
+ * Root-private trigger names only the durable hold. Pallet owns the per-volume lane, obtains and
188
+ * later revalidates a current controller grant, invokes Corestore, then consumes atomically.
189
+ */
190
+ export interface IReq_PalletLocal_Pallet_AdoptFleetCutoverVolumeFence
191
+ extends plugins.typedrequestInterfaces.implementsTR<
192
+ plugins.typedrequestInterfaces.ITypedRequest,
193
+ IReq_PalletLocal_Pallet_AdoptFleetCutoverVolumeFence
194
+ > {
195
+ method: 'adoptFleetCutoverVolumeFence';
196
+ request: { fence: IFleetCutoverAcceptedEvidenceReference };
197
+ response: { bundle: IFleetCutoverStorageAdoptionBundle };
198
+ }
199
+
200
+ /**
201
+ * @deprecated Published 32.x declaration retained but unsupported and unregistered. Its handoff
202
+ * cannot carry the complete desired claim intent; remove at the next protocol major.
148
203
  */
149
204
  export interface IReq_Pallet_Corestore_AdoptFleetCutoverVolume
150
205
  extends plugins.typedrequestInterfaces.implementsTR<
@@ -158,3 +213,23 @@ extends plugins.typedrequestInterfaces.implementsTR<
158
213
  };
159
214
  response: { adoption: IFleetCutoverCorestoreAdoptionReceipt };
160
215
  }
216
+
217
+ /**
218
+ * Private isolated one-shot invocation. Corestore performs physical-lineage proof and initial
219
+ * ready-claim CAS while its native attachment fence is held, with no callback into Pallet.
220
+ */
221
+ export interface IReq_Pallet_Corestore_AdoptPreparedFleetCutoverVolume
222
+ extends plugins.typedrequestInterfaces.implementsTR<
223
+ plugins.typedrequestInterfaces.ITypedRequest,
224
+ IReq_Pallet_Corestore_AdoptPreparedFleetCutoverVolume
225
+ > {
226
+ method: 'adoptPreparedFleetCutoverVolume';
227
+ request: {
228
+ handoffIntent: IFleetCutoverPreparedVolumeHandoffIntent;
229
+ palletFence: Extract<TFleetCutoverVolumeFence, { state: 'held' }>;
230
+ };
231
+ response: {
232
+ adoption: IFleetCutoverCorestoreAdoptionReceipt;
233
+ claim: IRuntimeStorageClaim;
234
+ };
235
+ }
@@ -5,6 +5,8 @@ import type * as network from '../data/networkdelivery.js';
5
5
  import type * as protection from '../data/networkprotection.js';
6
6
  import type * as credential from '../data/networkcredential.js';
7
7
  import type * as assignmentSecret from '../runtime.assignmentsecret.js';
8
+ import type * as fleet from '../data/fleetcutover.js';
9
+ import type { IRuntimeStorageClaim } from '../data/runtimestorageclaim.js';
8
10
 
9
11
  /** Current-peer, exact-projection authorization; response contains secret keys. */
10
12
  export interface IReq_Pallet_Controller_GetRuntimeManagedVpnCredential extends plugins.typedrequestInterfaces.implementsTR<
@@ -24,6 +26,61 @@ export interface IReq_Pallet_Controller_ReportRuntimeNetworkProtection extends p
24
26
  response: protection.IReportRuntimeNetworkProtectionResponse;
25
27
  }
26
28
 
29
+ /**
30
+ * Current authenticated Pallet session only. Cloudly reads the current pallet runtime and exact
31
+ * owner records, then grants one held prepared handoff; serialized session content is no proof.
32
+ */
33
+ export interface IReq_Pallet_Controller_AuthorizeFleetCutoverStorageAdoption
34
+ extends plugins.typedrequestInterfaces.implementsTR<
35
+ plugins.typedrequestInterfaces.ITypedRequest,
36
+ IReq_Pallet_Controller_AuthorizeFleetCutoverStorageAdoption
37
+ > {
38
+ method: 'authorizeFleetCutoverStorageAdoption';
39
+ request: {
40
+ session: data.IRuntimeSessionBinding;
41
+ handoffIntent: fleet.IFleetCutoverAcceptedEvidenceReference;
42
+ fence: fleet.IFleetCutoverAcceptedEvidenceReference;
43
+ };
44
+ response: { grant: fleet.IFleetCutoverStorageAdoptionGrant; replayed: boolean };
45
+ }
46
+
47
+ /**
48
+ * Pallet revalidates current session, pallet phase and grant after the provider call, then reports
49
+ * the exact consumed bundle. A stale grant leaves the durable local hold unchanged.
50
+ */
51
+ export interface IReq_Pallet_Controller_ReportFleetCutoverStorageAdoption
52
+ extends plugins.typedrequestInterfaces.implementsTR<
53
+ plugins.typedrequestInterfaces.ITypedRequest,
54
+ IReq_Pallet_Controller_ReportFleetCutoverStorageAdoption
55
+ > {
56
+ method: 'reportFleetCutoverStorageAdoption';
57
+ request: {
58
+ session: data.IRuntimeSessionBinding;
59
+ bundle: fleet.IFleetCutoverStorageAdoptionBundle;
60
+ };
61
+ response: {
62
+ status: 'accepted' | 'replay';
63
+ grant: fleet.IFleetCutoverAcceptedEvidenceReference;
64
+ fence: fleet.IFleetCutoverAcceptedEvidenceReference;
65
+ adoption: fleet.IFleetCutoverAcceptedEvidenceReference;
66
+ claim: fleet.IFleetCutoverAcceptedEvidenceReference;
67
+ };
68
+ }
69
+
70
+ /** Current-session read of one accepted ready claim by its exact immutable reference. */
71
+ export interface IReq_Pallet_Controller_GetRuntimeStorageClaim
72
+ extends plugins.typedrequestInterfaces.implementsTR<
73
+ plugins.typedrequestInterfaces.ITypedRequest,
74
+ IReq_Pallet_Controller_GetRuntimeStorageClaim
75
+ > {
76
+ method: 'getRuntimeStorageClaim';
77
+ request: {
78
+ session: data.IRuntimeSessionBinding;
79
+ claim: fleet.IFleetCutoverAcceptedEvidenceReference;
80
+ };
81
+ response: { claim: IRuntimeStorageClaim };
82
+ }
83
+
27
84
  /** Dedicated signing trust installed only through the current authenticated physical peer. */
28
85
  export interface IReq_Controller_Pallet_ApplyRuntimeNetworkSigningAuthority extends plugins.typedrequestInterfaces.implementsTR<
29
86
  plugins.typedrequestInterfaces.ITypedRequest, IReq_Controller_Pallet_ApplyRuntimeNetworkSigningAuthority