@serve.zone/interfaces 31.9.0 → 32.0.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.
- package/.smartconfig.json +1 -0
- package/changelog.md +467 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/appstore/types.d.ts +4 -6
- package/dist_ts/appstore/types.js +5 -5
- package/dist_ts/data/backup.d.ts +3 -26
- package/dist_ts/data/cluster.d.ts +36 -11
- package/dist_ts/data/cluster.js +33 -22
- package/dist_ts/data/clusteringress.d.ts +8 -0
- package/dist_ts/data/clusteringress.js +13 -8
- package/dist_ts/data/clusternode.d.ts +121 -74
- package/dist_ts/data/clusternode.js +109 -132
- package/dist_ts/data/coremail.d.ts +3 -7
- package/dist_ts/data/coremail.js +2 -2
- package/dist_ts/data/coremail.runtime.d.ts +0 -1
- package/dist_ts/data/coremail.runtime.js +5 -22
- package/dist_ts/data/deploymentadoption.d.ts +0 -2
- package/dist_ts/data/deploymentadoption.js +40 -6
- package/dist_ts/data/deploymentoperation.js +16 -3
- package/dist_ts/data/deploymentpreflight.d.ts +0 -2
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/immutableimage.d.ts +38 -21
- package/dist_ts/data/immutableimage.js +45 -5
- package/dist_ts/data/index.d.ts +1 -1
- package/dist_ts/data/isolatedrestore.d.ts +139 -21
- package/dist_ts/data/isolatedrestore.golden.d.ts +0 -1
- package/dist_ts/data/isolatedrestore.golden.js +6 -9
- package/dist_ts/data/isolatedrestore.js +176 -40
- package/dist_ts/data/mail.d.ts +1 -1
- package/dist_ts/data/networkaddressplan.d.ts +8 -8
- package/dist_ts/data/networkaddressplan.js +4 -4
- package/dist_ts/data/networkauthority.d.ts +20 -22
- package/dist_ts/data/networkauthority.js +26 -28
- package/dist_ts/data/networkcredential.d.ts +10 -10
- package/dist_ts/data/networkcredential.js +16 -16
- package/dist_ts/data/networkdelivery.d.ts +18 -18
- package/dist_ts/data/networkdelivery.js +16 -16
- package/dist_ts/data/networklease.d.ts +18 -7
- package/dist_ts/data/networklease.js +29 -7
- package/dist_ts/data/networknode.d.ts +2 -2
- package/dist_ts/data/networkprojection.d.ts +59 -60
- package/dist_ts/data/networkprojection.js +53 -57
- package/dist_ts/data/networkprotection.d.ts +27 -28
- package/dist_ts/data/networkprotection.js +36 -36
- package/dist_ts/data/networkrouter.d.ts +11 -11
- package/dist_ts/data/networkrouter.js +1 -1
- package/dist_ts/data/networksigning.d.ts +16 -18
- package/dist_ts/data/networksigning.js +20 -21
- package/dist_ts/data/nodecredential.d.ts +4 -6
- package/dist_ts/data/nodeenrollment.d.ts +35 -40
- package/dist_ts/data/nodeenrollment.js +57 -72
- package/dist_ts/data/privatenetwork.d.ts +21 -24
- package/dist_ts/data/privatenetwork.js +39 -54
- package/dist_ts/data/runtimeassignment.d.ts +50 -34
- package/dist_ts/data/runtimeassignment.js +79 -45
- package/dist_ts/data/runtimeconfig.d.ts +21 -22
- package/dist_ts/data/runtimeconfig.js +24 -24
- package/dist_ts/data/runtimesession.d.ts +100 -62
- package/dist_ts/data/runtimesession.js +98 -79
- package/dist_ts/data/runtimeterminalreceipt.d.ts +28 -29
- package/dist_ts/data/runtimeterminalreceipt.js +42 -42
- package/dist_ts/data/secret.d.ts +18 -25
- package/dist_ts/data/secret.js +12 -52
- package/dist_ts/data/servicecredential.d.ts +14 -16
- package/dist_ts/data/servicecredential.js +17 -28
- package/dist_ts/data/serviceruntime.d.ts +7 -7
- package/dist_ts/data/serviceruntime.js +4 -4
- package/dist_ts/data/sparknodereport.d.ts +27 -14
- package/dist_ts/data/sparknodereport.js +35 -28
- package/dist_ts/data/webpush.d.ts +0 -1
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/platform/objectstorageretention.d.ts +21 -24
- package/dist_ts/platform/objectstorageretention.js +6 -18
- package/dist_ts/platform/storage.d.ts +1 -3
- package/dist_ts/platform/storagemigration.d.ts +0 -7
- package/dist_ts/platform/storagemigration.golden.d.ts +0 -1
- package/dist_ts/platform/storagemigration.golden.js +8 -12
- package/dist_ts/platform/storagemigration.js +6 -51
- package/dist_ts/platform/types.d.ts +2 -2
- package/dist_ts/private/canonicaljson.d.ts +9 -1
- package/dist_ts/private/canonicaljson.js +14 -2
- package/dist_ts/private/dataproperties.d.ts +62 -0
- package/dist_ts/private/dataproperties.js +117 -0
- package/dist_ts/private/networkcontract.d.ts +10 -10
- package/dist_ts/private/networkcontract.js +1 -1
- package/dist_ts/private/releaseversion.d.ts +37 -0
- package/dist_ts/private/releaseversion.js +48 -0
- package/dist_ts/private/runtimepolicy.d.ts +2 -2
- package/dist_ts/private/runtimepolicy.js +1 -1
- package/dist_ts/private/tokenlist.d.ts +9 -0
- package/dist_ts/private/tokenlist.js +23 -0
- package/dist_ts/protocol.d.ts +137 -0
- package/dist_ts/protocol.js +255 -0
- package/dist_ts/requests/admin.d.ts +8 -8
- package/dist_ts/requests/backup.d.ts +102 -112
- package/dist_ts/requests/cluster.d.ts +10 -10
- package/dist_ts/requests/clusteringress.d.ts +9 -0
- package/dist_ts/requests/clusteringress.js +1 -1
- package/dist_ts/requests/config.d.ts +3 -3
- package/dist_ts/requests/corestore.d.ts +0 -3
- package/dist_ts/requests/corestore.js +5 -9
- package/dist_ts/requests/network.d.ts +30 -30
- package/dist_ts/requests/node.d.ts +15 -28
- package/dist_ts/requests/pallet.d.ts +13 -13
- package/dist_ts/requests/runtimesession.d.ts +53 -41
- package/dist_ts/requests/secret.d.ts +4 -5
- package/dist_ts/requests/secret.js +1 -5
- package/dist_ts/runtime.corestore.d.ts +56 -74
- package/dist_ts/runtime.corestore.databaseexportpackage.d.ts +30 -36
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.d.ts +11 -12
- package/dist_ts/runtime.corestore.databaseexportpackage.golden.js +54 -71
- package/dist_ts/runtime.corestore.databaseexportpackage.js +9 -25
- package/dist_ts/runtime.corestore.js +35 -135
- package/dist_ts/runtime.corestore.relay.js +2 -7
- package/dist_ts/runtime.d.ts +38 -48
- package/dist_ts/runtime.js +20 -77
- package/dist_ts/runtime.networkprojection.d.ts +4 -4
- package/dist_ts/runtime.networkprojection.js +18 -18
- package/dist_ts/runtime.workloadinit.d.ts +23 -24
- package/dist_ts/runtime.workloadinit.js +6 -13
- package/package.json +2 -2
- package/readme.md +421 -270
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/appstore/types.ts +4 -6
- package/ts/data/backup.ts +3 -38
- package/ts/data/cluster.ts +51 -34
- package/ts/data/clusteringress.ts +12 -12
- package/ts/data/clusternode.ts +204 -232
- package/ts/data/coremail.runtime.ts +4 -21
- package/ts/data/coremail.ts +3 -7
- package/ts/data/deploymentadoption.ts +46 -4
- package/ts/data/deploymentoperation.ts +16 -1
- package/ts/data/deploymentpreflight.ts +0 -2
- package/ts/data/immutableimage.ts +71 -24
- package/ts/data/index.ts +1 -1
- package/ts/data/isolatedrestore.golden.ts +5 -9
- package/ts/data/isolatedrestore.ts +339 -54
- package/ts/data/mail.ts +1 -1
- package/ts/data/networkaddressplan.ts +13 -13
- package/ts/data/networkauthority.ts +42 -46
- package/ts/data/networkcredential.ts +24 -24
- package/ts/data/networkdelivery.ts +27 -27
- package/ts/data/networklease.ts +40 -13
- package/ts/data/networknode.ts +2 -2
- package/ts/data/networkprojection.ts +119 -124
- package/ts/data/networkprotection.ts +54 -55
- package/ts/data/networkrouter.ts +14 -14
- package/ts/data/networksigning.ts +29 -32
- package/ts/data/nodecredential.ts +4 -6
- package/ts/data/nodeenrollment.ts +96 -114
- package/ts/data/privatenetwork.ts +58 -74
- package/ts/data/runtimeassignment.ts +143 -94
- package/ts/data/runtimeconfig.ts +45 -46
- package/ts/data/runtimesession.ts +185 -138
- package/ts/data/runtimeterminalreceipt.ts +72 -73
- package/ts/data/secret.ts +28 -76
- package/ts/data/servicecredential.ts +33 -43
- package/ts/data/serviceruntime.ts +11 -11
- package/ts/data/sparknodereport.ts +48 -36
- package/ts/data/webpush.ts +0 -1
- package/ts/index.ts +2 -0
- package/ts/platform/objectstorageretention.ts +36 -55
- package/ts/platform/storage.ts +1 -3
- package/ts/platform/storagemigration.golden.ts +7 -11
- package/ts/platform/storagemigration.ts +5 -55
- package/ts/platform/types.ts +2 -2
- package/ts/private/canonicaljson.ts +13 -1
- package/ts/private/dataproperties.ts +139 -0
- package/ts/private/networkcontract.ts +9 -9
- package/ts/private/releaseversion.ts +70 -0
- package/ts/private/runtimepolicy.ts +2 -2
- package/ts/private/tokenlist.ts +30 -0
- package/ts/protocol.ts +364 -0
- package/ts/requests/admin.ts +11 -11
- package/ts/requests/backup.ts +112 -117
- package/ts/requests/cluster.ts +13 -13
- package/ts/requests/clusteringress.ts +9 -0
- package/ts/requests/config.ts +3 -3
- package/ts/requests/corestore.ts +4 -16
- package/ts/requests/network.ts +40 -40
- package/ts/requests/node.ts +21 -38
- package/ts/requests/pallet.ts +17 -17
- package/ts/requests/runtimesession.ts +63 -51
- package/ts/requests/secret.ts +5 -10
- package/ts/runtime.corestore.databaseexportpackage.golden.ts +77 -95
- package/ts/runtime.corestore.databaseexportpackage.ts +51 -73
- package/ts/runtime.corestore.relay.ts +1 -10
- package/ts/runtime.corestore.ts +118 -268
- package/ts/runtime.networkprojection.ts +30 -30
- package/ts/runtime.ts +83 -158
- package/ts/runtime.workloadinit.ts +36 -48
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.d.ts +0 -902
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.d.ts +0 -35
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.golden.js +0 -315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.js +0 -3315
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.d.ts +0 -291
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.d.ts +0 -30
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.js +0 -408
- package/dist_ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.js +0 -1126
- package/ts/runtime.cloudlylegacydeploymentsettlement.golden.ts +0 -421
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.golden.ts +0 -510
- package/ts/runtime.cloudlylegacydeploymentsettlement.offlineexport.ts +0 -2470
- package/ts/runtime.cloudlylegacydeploymentsettlement.ts +0 -6630
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { TSha256Digest, TImmutableContainerPlatform } from './immutableimage.js';
|
|
2
2
|
import { buildDigestPinnedImageReference, isSha256Digest } from './immutableimage.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
canonicalizeStrictJson, compareWireTokens, createCanonicalJsonSha256Hex,
|
|
5
|
+
} from '../private/canonicaljson.js';
|
|
6
|
+
import { isCanonicalTokenList } from '../private/tokenlist.js';
|
|
4
7
|
|
|
5
|
-
export interface
|
|
8
|
+
export interface IRuntimeController {
|
|
6
9
|
kind: 'cloudly' | 'onebox';
|
|
7
10
|
id: string;
|
|
8
11
|
/** Persisted authority incarnation; changing it requires explicit re-admission. */
|
|
@@ -10,33 +13,32 @@ export interface IRuntimeControllerV1 {
|
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
/** Content-bound reference, not a credential, mount path or permission grant. */
|
|
13
|
-
export interface
|
|
16
|
+
export interface IRuntimeAuthorityReference {
|
|
14
17
|
id: string;
|
|
15
18
|
generation: number;
|
|
16
19
|
digest: TSha256Digest;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
export interface
|
|
22
|
+
export interface IRuntimeAssignmentReference {
|
|
20
23
|
assignmentId: string;
|
|
21
24
|
generation: number;
|
|
22
25
|
digest: TSha256Digest;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
/** Derived from authenticated transport and locally configured ownership. */
|
|
26
|
-
export interface
|
|
27
|
-
controller:
|
|
29
|
+
export interface IRuntimeAssignmentScope {
|
|
30
|
+
controller: IRuntimeController;
|
|
28
31
|
organizationId: string;
|
|
29
32
|
nodeId: string;
|
|
30
33
|
runtimeNamespace: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
export type
|
|
36
|
+
export type TRuntimeAssignmentDisposition =
|
|
34
37
|
| 'run'
|
|
35
38
|
| 'stop-preserve'
|
|
36
39
|
| 'remove-runtime-preserve-storage';
|
|
37
40
|
|
|
38
|
-
export interface
|
|
39
|
-
schemaVersion: 1;
|
|
41
|
+
export interface IRuntimeAssignment extends IRuntimeAssignmentScope {
|
|
40
42
|
assignmentId: string;
|
|
41
43
|
serviceId: string;
|
|
42
44
|
/** Stable logical replica; independent of container IDs and execution attempts. */
|
|
@@ -48,8 +50,8 @@ export interface IRuntimeAssignmentV1 extends IRuntimeAssignmentScopeV1 {
|
|
|
48
50
|
siteId: string | null;
|
|
49
51
|
generation: number;
|
|
50
52
|
/** Exact CAS predecessor; null only at generation 1. */
|
|
51
|
-
previous:
|
|
52
|
-
disposition:
|
|
53
|
+
previous: IRuntimeAssignmentReference | null;
|
|
54
|
+
disposition: TRuntimeAssignmentDisposition;
|
|
53
55
|
/** No automatic relocation or expiry-based claim of a stopped writer. */
|
|
54
56
|
offlinePolicy: 'continue-node-bound';
|
|
55
57
|
workload: {
|
|
@@ -58,24 +60,67 @@ export interface IRuntimeAssignmentV1 extends IRuntimeAssignmentScopeV1 {
|
|
|
58
60
|
imageDigest: TSha256Digest;
|
|
59
61
|
platform: TImmutableContainerPlatform;
|
|
60
62
|
/** Immutable complete invocation/configuration owned by the controller. */
|
|
61
|
-
config:
|
|
62
|
-
network:
|
|
63
|
-
secrets:
|
|
63
|
+
config: IRuntimeAuthorityReference;
|
|
64
|
+
network: IRuntimeAuthorityReference | null;
|
|
65
|
+
secrets: IRuntimeAuthorityReference | null;
|
|
64
66
|
/** Sorted unique reference IDs. Empty means no persistent storage claims. */
|
|
65
|
-
storage:
|
|
67
|
+
storage: IRuntimeAuthorityReference[];
|
|
66
68
|
};
|
|
67
69
|
digest: TSha256Digest;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* What a node must be able to resolve before it may run a workload, named by the workload keys
|
|
74
|
+
* that carry it. One vocabulary, so a node, a controller and a producer all mean the same thing.
|
|
75
|
+
*/
|
|
76
|
+
export const runtimeWorkloadAuthorities = ['network', 'secrets', 'storage'] as const;
|
|
77
|
+
|
|
78
|
+
export type TRuntimeWorkloadAuthority = (typeof runtimeWorkloadAuthorities)[number];
|
|
79
|
+
|
|
80
|
+
/** What each authority looks at on the workload; a record over the union, so none can be missed. */
|
|
81
|
+
const workloadAuthorityDemands: Readonly<Record<
|
|
82
|
+
TRuntimeWorkloadAuthority,
|
|
83
|
+
(workloadArg: IRuntimeAssignment['workload']) => boolean
|
|
84
|
+
>> = Object.freeze({
|
|
85
|
+
network: (workloadArg) => workloadArg.network !== null,
|
|
86
|
+
secrets: (workloadArg) => workloadArg.secrets !== null,
|
|
87
|
+
storage: (workloadArg) => workloadArg.storage.length > 0,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The authorities this workload cannot run without.
|
|
92
|
+
*
|
|
93
|
+
* Filtered out of the vocabulary and ordered by the canonical comparison, so the result is sorted
|
|
94
|
+
* and duplicate-free whatever order the vocabulary is declared in, and compares byte-equal against
|
|
95
|
+
* the list a node resolved. A controller calls this with a workload it read from an assignment on
|
|
96
|
+
* the wire, so one the demands cannot read is refused by this family's error rather than by a
|
|
97
|
+
* `TypeError` out of a member read.
|
|
98
|
+
*/
|
|
99
|
+
export const requiredWorkloadAuthorities = (
|
|
100
|
+
workloadArg: IRuntimeAssignment['workload'],
|
|
101
|
+
): TRuntimeWorkloadAuthority[] => {
|
|
102
|
+
if (!isExactRecord(workloadArg, workloadKeys) || !Array.isArray(workloadArg.storage)) {
|
|
103
|
+
fail(workloadSchemaMessage);
|
|
104
|
+
}
|
|
105
|
+
return runtimeWorkloadAuthorities
|
|
106
|
+
.filter((authorityArg) => workloadAuthorityDemands[authorityArg](workloadArg))
|
|
107
|
+
.sort(compareWireTokens);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** True when a value is exactly a canonical authority list drawn from the authority vocabulary. */
|
|
111
|
+
export const isResolvedWorkloadAuthorities = (
|
|
112
|
+
valueArg: unknown,
|
|
113
|
+
): valueArg is TRuntimeWorkloadAuthority[] =>
|
|
114
|
+
isCanonicalTokenList(valueArg, runtimeWorkloadAuthorities);
|
|
115
|
+
|
|
116
|
+
export type TRuntimeAssignmentDecision =
|
|
117
|
+
| { status: 'accept' | 'replay'; assignment: IRuntimeAssignment }
|
|
72
118
|
| { status: 'reject'; reason: 'invalid' | 'scope-mismatch' | 'conflict' | 'stale'
|
|
73
119
|
| 'predecessor-mismatch' | 'attempt-mismatch' | 'disposition-regression' };
|
|
74
120
|
|
|
75
121
|
/** Value-free execution evidence. Receipt/acceptance alone never means ready. */
|
|
76
|
-
export interface
|
|
77
|
-
|
|
78
|
-
assignment: IRuntimeAssignmentReferenceV1;
|
|
122
|
+
export interface IRuntimeAssignmentObservation {
|
|
123
|
+
assignment: IRuntimeAssignmentReference;
|
|
79
124
|
/** Authenticated reporter session; its authority is verified by the receiver. */
|
|
80
125
|
reporterSessionId: string;
|
|
81
126
|
/** Durable per-assignment counter; never resets when a reporter reconnects. */
|
|
@@ -91,9 +136,9 @@ export interface IRuntimeAssignmentObservationV1 {
|
|
|
91
136
|
digest: TSha256Digest;
|
|
92
137
|
}
|
|
93
138
|
|
|
94
|
-
export const
|
|
95
|
-
assignmentDigestDomain: 'serve.zone/runtime-assignment
|
|
96
|
-
observationDigestDomain: 'serve.zone/runtime-assignment-observation
|
|
139
|
+
export const runtimeAssignmentContract = Object.freeze({
|
|
140
|
+
assignmentDigestDomain: 'serve.zone/runtime-assignment',
|
|
141
|
+
observationDigestDomain: 'serve.zone/runtime-assignment-observation',
|
|
97
142
|
maximumStorageReferences: 64,
|
|
98
143
|
omission: 'no-effect',
|
|
99
144
|
removal: 'retain-assignment-tombstone-and-storage',
|
|
@@ -123,32 +168,37 @@ const isExactRecord = (
|
|
|
123
168
|
});
|
|
124
169
|
};
|
|
125
170
|
|
|
126
|
-
const isController = (valueArg: unknown): valueArg is
|
|
171
|
+
const isController = (valueArg: unknown): valueArg is IRuntimeController =>
|
|
127
172
|
isExactRecord(valueArg, ['kind', 'id', 'epoch'])
|
|
128
173
|
&& (valueArg.kind === 'cloudly' || valueArg.kind === 'onebox')
|
|
129
174
|
&& isIdentifier(valueArg.id) && isIdentifier(valueArg.epoch);
|
|
130
|
-
const isAuthorityReference = (valueArg: unknown): valueArg is
|
|
175
|
+
const isAuthorityReference = (valueArg: unknown): valueArg is IRuntimeAuthorityReference =>
|
|
131
176
|
isExactRecord(valueArg, ['id', 'generation', 'digest'])
|
|
132
177
|
&& isIdentifier(valueArg.id) && isPositiveInteger(valueArg.generation) && isDigest(valueArg.digest);
|
|
133
|
-
const isAssignmentReference = (valueArg: unknown): valueArg is
|
|
178
|
+
const isAssignmentReference = (valueArg: unknown): valueArg is IRuntimeAssignmentReference =>
|
|
134
179
|
isExactRecord(valueArg, ['assignmentId', 'generation', 'digest'])
|
|
135
180
|
&& isIdentifier(valueArg.assignmentId) && isPositiveInteger(valueArg.generation)
|
|
136
181
|
&& isDigest(valueArg.digest);
|
|
137
182
|
|
|
183
|
+
/** The workload's exact members, read by the authority vocabulary and by the snapshot alike. */
|
|
184
|
+
const workloadKeys = ['registryHost', 'repository', 'imageDigest', 'platform',
|
|
185
|
+
'config', 'network', 'secrets', 'storage'] as const;
|
|
186
|
+
const workloadSchemaMessage = 'runtime assignment workload must use exact immutable references';
|
|
187
|
+
|
|
138
188
|
const assignmentKeys = [
|
|
139
|
-
'
|
|
189
|
+
'controller', 'organizationId', 'nodeId', 'runtimeNamespace',
|
|
140
190
|
'assignmentId', 'serviceId', 'replicaId', 'attemptId', 'workerGroupId', 'siteId',
|
|
141
191
|
'generation', 'previous', 'disposition', 'offlinePolicy', 'workload', 'digest',
|
|
142
192
|
] as const;
|
|
143
193
|
|
|
144
|
-
const assertAssignment = (valueArg: unknown):
|
|
194
|
+
const assertAssignment = (valueArg: unknown): IRuntimeAssignment => {
|
|
145
195
|
if (!isExactRecord(valueArg, assignmentKeys)
|
|
146
|
-
||
|
|
196
|
+
|| !isController(valueArg.controller)
|
|
147
197
|
|| !isPositiveInteger(valueArg.generation) || !isDigest(valueArg.digest)
|
|
148
198
|
|| valueArg.offlinePolicy !== 'continue-node-bound'
|
|
149
199
|
|| !['run', 'stop-preserve', 'remove-runtime-preserve-storage'].includes(
|
|
150
200
|
valueArg.disposition as string,
|
|
151
|
-
)) fail('runtime assignment must use its exact
|
|
201
|
+
)) fail('runtime assignment must use its exact schema');
|
|
152
202
|
for (const key of ['organizationId', 'nodeId', 'runtimeNamespace', 'assignmentId',
|
|
153
203
|
'serviceId', 'replicaId', 'attemptId']) {
|
|
154
204
|
if (!isIdentifier(valueArg[key])) fail(`runtime assignment ${key} must be a canonical identifier`);
|
|
@@ -163,8 +213,7 @@ const assertAssignment = (valueArg: unknown): IRuntimeAssignmentV1 => {
|
|
|
163
213
|
fail('runtime assignment must bind its exact preceding generation');
|
|
164
214
|
}
|
|
165
215
|
const workload = valueArg.workload;
|
|
166
|
-
if (!isExactRecord(workload,
|
|
167
|
-
'config', 'network', 'secrets', 'storage'])
|
|
216
|
+
if (!isExactRecord(workload, workloadKeys)
|
|
168
217
|
|| typeof workload.registryHost !== 'string' || workload.registryHost.length > 253
|
|
169
218
|
|| typeof workload.repository !== 'string' || workload.repository.length > 255
|
|
170
219
|
|| !isDigest(workload.imageDigest)
|
|
@@ -173,8 +222,8 @@ const assertAssignment = (valueArg: unknown): IRuntimeAssignmentV1 => {
|
|
|
173
222
|
|| (workload.network !== null && !isAuthorityReference(workload.network))
|
|
174
223
|
|| (workload.secrets !== null && !isAuthorityReference(workload.secrets))
|
|
175
224
|
|| !Array.isArray(workload.storage)
|
|
176
|
-
|| workload.storage.length >
|
|
177
|
-
fail(
|
|
225
|
+
|| workload.storage.length > runtimeAssignmentContract.maximumStorageReferences) {
|
|
226
|
+
fail(workloadSchemaMessage);
|
|
178
227
|
}
|
|
179
228
|
buildDigestPinnedImageReference(workload.registryHost as string,
|
|
180
229
|
workload.repository as string, workload.imageDigest as TSha256Digest);
|
|
@@ -188,7 +237,7 @@ const assertAssignment = (valueArg: unknown): IRuntimeAssignmentV1 => {
|
|
|
188
237
|
previousId = descriptor.value.id;
|
|
189
238
|
}
|
|
190
239
|
canonicalizeStrictJson(valueArg, fail);
|
|
191
|
-
return valueArg as unknown as
|
|
240
|
+
return valueArg as unknown as IRuntimeAssignment;
|
|
192
241
|
};
|
|
193
242
|
|
|
194
243
|
const digestPayload = async (domainArg: string, valueArg: { digest: TSha256Digest }): Promise<TSha256Digest> => {
|
|
@@ -199,41 +248,41 @@ const digestPayload = async (domainArg: string, valueArg: { digest: TSha256Diges
|
|
|
199
248
|
};
|
|
200
249
|
|
|
201
250
|
/** Digest helpers validate structure and exclude only the object's own digest. */
|
|
202
|
-
export const
|
|
203
|
-
assignmentArg:
|
|
251
|
+
export const computeRuntimeAssignmentDigest = async (
|
|
252
|
+
assignmentArg: IRuntimeAssignment,
|
|
204
253
|
): Promise<TSha256Digest> => digestPayload(
|
|
205
|
-
|
|
254
|
+
runtimeAssignmentContract.assignmentDigestDomain, assertAssignment(assignmentArg),
|
|
206
255
|
);
|
|
207
256
|
|
|
208
|
-
export const
|
|
257
|
+
export const validateRuntimeAssignment = async (valueArg: unknown): Promise<string[]> => {
|
|
209
258
|
try {
|
|
210
|
-
const assignment =
|
|
211
|
-
return assignment.digest === await
|
|
259
|
+
const assignment = snapshotRuntimeAssignment(valueArg);
|
|
260
|
+
return assignment.digest === await computeRuntimeAssignmentDigest(assignment)
|
|
212
261
|
? [] : ['runtime assignment digest does not match'];
|
|
213
262
|
} catch {
|
|
214
|
-
return ['runtime assignment must use its exact canonical
|
|
263
|
+
return ['runtime assignment must use its exact canonical schema'];
|
|
215
264
|
}
|
|
216
265
|
};
|
|
217
266
|
|
|
218
267
|
/**
|
|
219
268
|
* Synchronous exact-schema validation and detached canonical copy for persistence
|
|
220
269
|
* assertions. This does not verify the digest, authenticate scope, or admit an
|
|
221
|
-
* attempt. Use
|
|
270
|
+
* attempt. Use validateRuntimeAssignment/evaluateRuntimeAssignment before
|
|
222
271
|
* acceptance or effects.
|
|
223
272
|
*/
|
|
224
|
-
export const
|
|
225
|
-
JSON.parse(canonicalizeStrictJson(assertAssignment(valueArg), fail)) as
|
|
273
|
+
export const snapshotRuntimeAssignment = (valueArg: unknown): IRuntimeAssignment =>
|
|
274
|
+
JSON.parse(canonicalizeStrictJson(assertAssignment(valueArg), fail)) as IRuntimeAssignment;
|
|
226
275
|
|
|
227
|
-
export const
|
|
228
|
-
assignmentArg:
|
|
229
|
-
):
|
|
276
|
+
export const getRuntimeAssignmentReference = (
|
|
277
|
+
assignmentArg: IRuntimeAssignment,
|
|
278
|
+
): IRuntimeAssignmentReference => ({
|
|
230
279
|
assignmentId: assignmentArg.assignmentId,
|
|
231
280
|
generation: assignmentArg.generation,
|
|
232
281
|
digest: assignmentArg.digest,
|
|
233
282
|
});
|
|
234
283
|
|
|
235
284
|
const canonical = (valueArg: unknown): string => canonicalizeStrictJson(valueArg, fail);
|
|
236
|
-
const immutableAttempt = (assignmentArg:
|
|
285
|
+
const immutableAttempt = (assignmentArg: IRuntimeAssignment): string => {
|
|
237
286
|
const { generation, previous, disposition, digest, ...attempt } = assignmentArg;
|
|
238
287
|
return canonical(attempt);
|
|
239
288
|
};
|
|
@@ -245,27 +294,27 @@ const immutableAttempt = (assignmentArg: IRuntimeAssignmentV1): string => {
|
|
|
245
294
|
* admission for every older writer. An omitted assignment causes no transition.
|
|
246
295
|
* Persist only the returned detached snapshot, never the mutable input object.
|
|
247
296
|
*/
|
|
248
|
-
export const
|
|
249
|
-
incomingArg:
|
|
250
|
-
acceptedArg:
|
|
251
|
-
trustedScopeArg:
|
|
252
|
-
): Promise<
|
|
297
|
+
export const evaluateRuntimeAssignment = async (
|
|
298
|
+
incomingArg: IRuntimeAssignment,
|
|
299
|
+
acceptedArg: IRuntimeAssignment | null,
|
|
300
|
+
trustedScopeArg: IRuntimeAssignmentScope,
|
|
301
|
+
): Promise<TRuntimeAssignmentDecision> => {
|
|
253
302
|
try {
|
|
254
|
-
incomingArg =
|
|
255
|
-
acceptedArg = acceptedArg === null ? null :
|
|
303
|
+
incomingArg = snapshotRuntimeAssignment(incomingArg);
|
|
304
|
+
acceptedArg = acceptedArg === null ? null : snapshotRuntimeAssignment(acceptedArg);
|
|
256
305
|
if (!isExactRecord(trustedScopeArg, ['controller', 'organizationId', 'nodeId', 'runtimeNamespace'])
|
|
257
306
|
|| !isController(trustedScopeArg.controller)
|
|
258
307
|
|| !isIdentifier(trustedScopeArg.organizationId) || !isIdentifier(trustedScopeArg.nodeId)
|
|
259
308
|
|| !isIdentifier(trustedScopeArg.runtimeNamespace)) return { status: 'reject', reason: 'invalid' };
|
|
260
|
-
trustedScopeArg = JSON.parse(canonical(trustedScopeArg)) as
|
|
309
|
+
trustedScopeArg = JSON.parse(canonical(trustedScopeArg)) as IRuntimeAssignmentScope;
|
|
261
310
|
} catch {
|
|
262
311
|
return { status: 'reject', reason: 'invalid' };
|
|
263
312
|
}
|
|
264
|
-
if ((await
|
|
265
|
-
|| (acceptedArg !== null && (await
|
|
313
|
+
if ((await validateRuntimeAssignment(incomingArg)).length
|
|
314
|
+
|| (acceptedArg !== null && (await validateRuntimeAssignment(acceptedArg)).length)) {
|
|
266
315
|
return { status: 'reject', reason: 'invalid' };
|
|
267
316
|
}
|
|
268
|
-
const matchesScope = (assignmentArg:
|
|
317
|
+
const matchesScope = (assignmentArg: IRuntimeAssignment): boolean =>
|
|
269
318
|
assignmentArg.controller.kind === trustedScopeArg.controller.kind
|
|
270
319
|
&& assignmentArg.controller.id === trustedScopeArg.controller.id
|
|
271
320
|
&& assignmentArg.controller.epoch === trustedScopeArg.controller.epoch
|
|
@@ -289,13 +338,13 @@ export const evaluateRuntimeAssignmentV1 = async (
|
|
|
289
338
|
return incomingArg.digest === acceptedArg.digest
|
|
290
339
|
? { status: 'replay', assignment: acceptedArg } : { status: 'reject', reason: 'conflict' };
|
|
291
340
|
}
|
|
292
|
-
if (canonical(incomingArg.previous) !== canonical(
|
|
341
|
+
if (canonical(incomingArg.previous) !== canonical(getRuntimeAssignmentReference(acceptedArg))) {
|
|
293
342
|
return { status: 'reject', reason: 'predecessor-mismatch' };
|
|
294
343
|
}
|
|
295
344
|
if (immutableAttempt(incomingArg) !== immutableAttempt(acceptedArg)) {
|
|
296
345
|
return { status: 'reject', reason: 'attempt-mismatch' };
|
|
297
346
|
}
|
|
298
|
-
const order: Record<
|
|
347
|
+
const order: Record<TRuntimeAssignmentDisposition, number> = {
|
|
299
348
|
run: 0, 'stop-preserve': 1, 'remove-runtime-preserve-storage': 2,
|
|
300
349
|
};
|
|
301
350
|
if (order[incomingArg.disposition] !== order[acceptedArg.disposition] + 1) {
|
|
@@ -304,15 +353,15 @@ export const evaluateRuntimeAssignmentV1 = async (
|
|
|
304
353
|
return { status: 'accept', assignment: incomingArg };
|
|
305
354
|
};
|
|
306
355
|
|
|
307
|
-
const assertObservation = (valueArg: unknown):
|
|
308
|
-
if (!isExactRecord(valueArg, ['
|
|
356
|
+
const assertObservation = (valueArg: unknown): IRuntimeAssignmentObservation => {
|
|
357
|
+
if (!isExactRecord(valueArg, ['assignment', 'reporterSessionId',
|
|
309
358
|
'sequence', 'observedAt', 'phase', 'ready', 'runtime', 'digest'])
|
|
310
|
-
||
|
|
359
|
+
|| !isAssignmentReference(valueArg.assignment)
|
|
311
360
|
|| !isIdentifier(valueArg.reporterSessionId) || !isPositiveInteger(valueArg.sequence)
|
|
312
361
|
|| !isPositiveInteger(valueArg.observedAt) || !isDigest(valueArg.digest)
|
|
313
362
|
|| typeof valueArg.ready !== 'boolean'
|
|
314
363
|
|| !['pending', 'running', 'stopped', 'removed', 'failed', 'unknown'].includes(valueArg.phase as string)) {
|
|
315
|
-
fail('runtime observation must use its exact
|
|
364
|
+
fail('runtime observation must use its exact schema');
|
|
316
365
|
}
|
|
317
366
|
if (valueArg.runtime !== null && (!isExactRecord(valueArg.runtime, [
|
|
318
367
|
'sandboxId', 'containerId', 'imageDigest',
|
|
@@ -324,13 +373,13 @@ const assertObservation = (valueArg: unknown): IRuntimeAssignmentObservationV1 =
|
|
|
324
373
|
fail('runtime observation readiness and identity contradict its phase');
|
|
325
374
|
}
|
|
326
375
|
canonical(valueArg);
|
|
327
|
-
return valueArg as unknown as
|
|
376
|
+
return valueArg as unknown as IRuntimeAssignmentObservation;
|
|
328
377
|
};
|
|
329
378
|
|
|
330
|
-
export const
|
|
331
|
-
observationArg:
|
|
379
|
+
export const computeRuntimeAssignmentObservationDigest = async (
|
|
380
|
+
observationArg: IRuntimeAssignmentObservation,
|
|
332
381
|
): Promise<TSha256Digest> => digestPayload(
|
|
333
|
-
|
|
382
|
+
runtimeAssignmentContract.observationDigestDomain, assertObservation(observationArg),
|
|
334
383
|
);
|
|
335
384
|
|
|
336
385
|
/**
|
|
@@ -338,24 +387,24 @@ export const computeRuntimeAssignmentObservationV1Digest = async (
|
|
|
338
387
|
* assignment and reporter authority still require the observation validator and
|
|
339
388
|
* evaluator; a structurally valid observation is never a writer fence.
|
|
340
389
|
*/
|
|
341
|
-
export const
|
|
390
|
+
export const snapshotRuntimeAssignmentObservation = (
|
|
342
391
|
valueArg: unknown,
|
|
343
|
-
):
|
|
344
|
-
JSON.parse(canonical(assertObservation(valueArg))) as
|
|
392
|
+
): IRuntimeAssignmentObservation =>
|
|
393
|
+
JSON.parse(canonical(assertObservation(valueArg))) as IRuntimeAssignmentObservation;
|
|
345
394
|
|
|
346
395
|
/** Checks exact assignment evidence; transport must separately bind reporter/session. */
|
|
347
|
-
export const
|
|
396
|
+
export const validateRuntimeAssignmentObservation = async (
|
|
348
397
|
observationArg: unknown,
|
|
349
|
-
assignmentArg:
|
|
398
|
+
assignmentArg: IRuntimeAssignment,
|
|
350
399
|
): Promise<string[]> => {
|
|
351
400
|
try {
|
|
352
|
-
const observation =
|
|
353
|
-
assignmentArg =
|
|
354
|
-
if ((await
|
|
355
|
-
if (canonical(observation.assignment) !== canonical(
|
|
401
|
+
const observation = snapshotRuntimeAssignmentObservation(observationArg);
|
|
402
|
+
assignmentArg = snapshotRuntimeAssignment(assignmentArg);
|
|
403
|
+
if ((await validateRuntimeAssignment(assignmentArg)).length) return ['invalid assignment'];
|
|
404
|
+
if (canonical(observation.assignment) !== canonical(getRuntimeAssignmentReference(assignmentArg))) {
|
|
356
405
|
return ['runtime observation does not match the exact assignment'];
|
|
357
406
|
}
|
|
358
|
-
if (observation.digest !== await
|
|
407
|
+
if (observation.digest !== await computeRuntimeAssignmentObservationDigest(observation)) {
|
|
359
408
|
return ['runtime observation digest does not match'];
|
|
360
409
|
}
|
|
361
410
|
if (observation.ready && (assignmentArg.disposition !== 'run'
|
|
@@ -364,12 +413,12 @@ export const validateRuntimeAssignmentObservationV1 = async (
|
|
|
364
413
|
}
|
|
365
414
|
return [];
|
|
366
415
|
} catch {
|
|
367
|
-
return ['runtime observation must use its exact canonical
|
|
416
|
+
return ['runtime observation must use its exact canonical schema'];
|
|
368
417
|
}
|
|
369
418
|
};
|
|
370
419
|
|
|
371
|
-
export type
|
|
372
|
-
| { status: 'accept' | 'replay'; observation:
|
|
420
|
+
export type TRuntimeObservationDecision =
|
|
421
|
+
| { status: 'accept' | 'replay'; observation: IRuntimeAssignmentObservation }
|
|
373
422
|
| { status: 'reject'; reason: 'invalid' | 'session-mismatch' | 'stale' | 'conflict' | 'sequence-gap' };
|
|
374
423
|
|
|
375
424
|
/**
|
|
@@ -380,23 +429,23 @@ export type TRuntimeObservationDecisionV1 =
|
|
|
380
429
|
* receivers must process each revision's observations in order.
|
|
381
430
|
* These observations are never sufficient authority to relocate a storage writer.
|
|
382
431
|
*/
|
|
383
|
-
export const
|
|
384
|
-
incomingArg:
|
|
385
|
-
acceptedArg:
|
|
386
|
-
assignmentArg:
|
|
432
|
+
export const evaluateRuntimeAssignmentObservation = async (
|
|
433
|
+
incomingArg: IRuntimeAssignmentObservation,
|
|
434
|
+
acceptedArg: IRuntimeAssignmentObservation | null,
|
|
435
|
+
assignmentArg: IRuntimeAssignment,
|
|
387
436
|
trustedReporterSessionIdArg: string,
|
|
388
|
-
): Promise<
|
|
437
|
+
): Promise<TRuntimeObservationDecision> => {
|
|
389
438
|
try {
|
|
390
|
-
incomingArg =
|
|
439
|
+
incomingArg = snapshotRuntimeAssignmentObservation(incomingArg);
|
|
391
440
|
acceptedArg = acceptedArg === null ? null
|
|
392
|
-
:
|
|
393
|
-
assignmentArg =
|
|
441
|
+
: snapshotRuntimeAssignmentObservation(acceptedArg);
|
|
442
|
+
assignmentArg = snapshotRuntimeAssignment(assignmentArg);
|
|
394
443
|
if (!isIdentifier(trustedReporterSessionIdArg)
|
|
395
|
-
|| (await
|
|
444
|
+
|| (await validateRuntimeAssignmentObservation(incomingArg, assignmentArg)).length
|
|
396
445
|
|| (acceptedArg !== null && (
|
|
397
|
-
acceptedArg.digest !== await
|
|
446
|
+
acceptedArg.digest !== await computeRuntimeAssignmentObservationDigest(acceptedArg)
|
|
398
447
|
|| acceptedArg.assignment.assignmentId !== assignmentArg.assignmentId
|
|
399
|
-
|| (canonical(acceptedArg.assignment) !== canonical(
|
|
448
|
+
|| (canonical(acceptedArg.assignment) !== canonical(getRuntimeAssignmentReference(assignmentArg))
|
|
400
449
|
&& canonical(acceptedArg.assignment) !== canonical(assignmentArg.previous))
|
|
401
450
|
))) return { status: 'reject', reason: 'invalid' };
|
|
402
451
|
if (incomingArg.reporterSessionId !== trustedReporterSessionIdArg) {
|