@serve.zone/interfaces 11.4.0 → 12.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/changelog.md +9 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/data/deploymentpreflight.d.ts +24 -6
- package/dist_ts/data/deploymentpreflight.js +31 -5
- package/dist_ts/requests/admin.d.ts +16 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/data/deploymentpreflight.ts +57 -9
- package/ts/requests/admin.ts +21 -1
package/changelog.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-07-19 - 12.0.0
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- make greenfield deployment preflight evidence fail closed (deployment)
|
|
8
|
+
- Distinguish the client intent digest from Cloudly's server-computed configuration digest.
|
|
9
|
+
- Report dirty source, registry host, complete replica counts, explicit proposed storage resources, and unknown namespace safety.
|
|
10
|
+
- Define an admin-only, idempotent machine-user grant normalization request without returning token material.
|
|
11
|
+
|
|
3
12
|
## 2026-07-19 - 11.4.0
|
|
4
13
|
|
|
5
14
|
### Features
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/interfaces',
|
|
6
|
-
version: '
|
|
6
|
+
version: '12.0.0',
|
|
7
7
|
description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHVGQUF1RjtDQUNyRyxDQUFBIn0=
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TOciManifestMediaType, TSha256Digest } from './immutableimage.js';
|
|
2
2
|
import type { TServiceDeploymentCapability } from './user.js';
|
|
3
3
|
export type TDeploymentPreflightDecision = 'GO' | 'NO-GO';
|
|
4
|
-
export type TDeploymentPreflightBlockerCode = 'INVALID_REQUEST' | 'AUTHORIZATION_MISSING' | 'SOURCE_REVISION_INVALID' | 'VERSION_NOT_RESERVED' | 'VERSION_COLLISION' | 'NAMESPACE_NOT_RESERVED' | 'NAMESPACE_COLLISION' | 'NAMESPACE_NOT_PROVEN_BLANK' | 'CONFIGURATION_DIGEST_INVALID' | 'CURRENT_REFERENCE_MUTABLE' | 'CURRENT_RUNTIME_DIGEST_MISSING' | 'OCI_INDEX_DIGEST_REQUIRED' | 'OCI_INDEX_NOT_FOUND' | 'OCI_INDEX_MEDIA_TYPE_REQUIRED' | 'OCI_PLATFORM_MISSING' | 'OCI_PLATFORM_MANIFEST_MISSING' | 'IMMUTABLE_RELEASE_TAG_REQUIRED' | 'IMMUTABLE_RELEASE_TAG_NOT_RESERVED' | 'RELEASE_EVIDENCE_MISSING' | 'RELEASE_EVIDENCE_INVALID' | 'RELEASE_NOT_AUTHORIZED' | 'GREENFIELD_EXECUTION_UNAVAILABLE' | 'ROUTE_PROMOTION_FENCE_UNAVAILABLE' | 'POST_DEPLOYMENT_VERIFICATION_UNAVAILABLE' | 'WOULD_REPLACE_EXISTING_STATE' | 'CORESTORE_INVENTORY_UNAVAILABLE' | 'RUNTIME_ATTESTATION_MISMATCH';
|
|
4
|
+
export type TDeploymentPreflightBlockerCode = 'INVALID_REQUEST' | 'AUTHORIZATION_MISSING' | 'SOURCE_REVISION_INVALID' | 'SOURCE_WORKTREE_DIRTY' | 'VERSION_NOT_RESERVED' | 'VERSION_COLLISION' | 'NAMESPACE_NOT_RESERVED' | 'NAMESPACE_COLLISION' | 'NAMESPACE_NOT_PROVEN_BLANK' | 'CONFIGURATION_DIGEST_INVALID' | 'INTENT_DIGEST_INVALID' | 'CURRENT_REFERENCE_MUTABLE' | 'CURRENT_RUNTIME_DIGEST_MISSING' | 'OCI_INDEX_DIGEST_REQUIRED' | 'OCI_INDEX_NOT_FOUND' | 'OCI_INDEX_MEDIA_TYPE_REQUIRED' | 'OCI_PLATFORM_MISSING' | 'OCI_PLATFORM_MANIFEST_MISSING' | 'IMMUTABLE_RELEASE_TAG_REQUIRED' | 'IMMUTABLE_RELEASE_TAG_NOT_RESERVED' | 'RELEASE_EVIDENCE_MISSING' | 'RELEASE_EVIDENCE_INVALID' | 'RELEASE_NOT_AUTHORIZED' | 'GREENFIELD_EXECUTION_UNAVAILABLE' | 'ROUTE_PROMOTION_FENCE_UNAVAILABLE' | 'POST_DEPLOYMENT_VERIFICATION_UNAVAILABLE' | 'WOULD_REPLACE_EXISTING_STATE' | 'CORESTORE_INVENTORY_UNAVAILABLE' | 'RUNTIME_ATTESTATION_MISMATCH';
|
|
5
5
|
export interface IDeploymentPreflightBlocker {
|
|
6
6
|
code: TDeploymentPreflightBlockerCode;
|
|
7
7
|
message: string;
|
|
@@ -37,6 +37,10 @@ export interface IDeploymentPreflightCorestoreResource {
|
|
|
37
37
|
provider: string;
|
|
38
38
|
resourceName: string;
|
|
39
39
|
}
|
|
40
|
+
export interface IDeploymentPreflightProposedCorestoreResource {
|
|
41
|
+
capability: 'database' | 'objectstorage';
|
|
42
|
+
resourceName: string;
|
|
43
|
+
}
|
|
40
44
|
export interface IDeploymentReleaseEvidenceReference {
|
|
41
45
|
kind: 'provenance' | 'sbom' | 'signature' | 'vulnerability-policy' | 'release-authorization';
|
|
42
46
|
statementDigest: TSha256Digest;
|
|
@@ -57,16 +61,18 @@ export interface IDeploymentPreflightRequestData {
|
|
|
57
61
|
requestId: string;
|
|
58
62
|
serviceId: string;
|
|
59
63
|
sourceRevision: string;
|
|
64
|
+
sourceDirty: boolean;
|
|
60
65
|
version: string;
|
|
61
|
-
|
|
66
|
+
intentDigest: string;
|
|
62
67
|
proposedNamespace: string;
|
|
68
|
+
proposedCorestoreResources: IDeploymentPreflightProposedCorestoreResource[];
|
|
63
69
|
ociIndexDigest?: string;
|
|
64
70
|
releaseTag?: string;
|
|
65
71
|
releaseEvidence?: IDeploymentReleaseEvidence;
|
|
66
72
|
greenfield: true;
|
|
67
73
|
}
|
|
68
74
|
export interface IDeploymentPreflightReport {
|
|
69
|
-
schemaVersion:
|
|
75
|
+
schemaVersion: 2;
|
|
70
76
|
decision: TDeploymentPreflightDecision;
|
|
71
77
|
mutationPerformed: false;
|
|
72
78
|
generatedAt: number;
|
|
@@ -74,13 +80,16 @@ export interface IDeploymentPreflightReport {
|
|
|
74
80
|
serviceId: string;
|
|
75
81
|
serviceName: string;
|
|
76
82
|
workspaceIdentity: string;
|
|
83
|
+
registryHost: string;
|
|
77
84
|
registryRepository: string;
|
|
78
85
|
deployOnPush: boolean;
|
|
79
86
|
};
|
|
80
87
|
source: {
|
|
81
88
|
revision: string;
|
|
89
|
+
dirty: boolean;
|
|
82
90
|
version: string;
|
|
83
|
-
|
|
91
|
+
intentDigest: TSha256Digest;
|
|
92
|
+
configurationDigest: TSha256Digest;
|
|
84
93
|
};
|
|
85
94
|
currentImage: {
|
|
86
95
|
tag?: string;
|
|
@@ -98,14 +107,23 @@ export interface IDeploymentPreflightReport {
|
|
|
98
107
|
platforms: IOciPlatformDescriptorSummary[];
|
|
99
108
|
};
|
|
100
109
|
runtimeTasks: IDeploymentPreflightRuntimeTask[];
|
|
110
|
+
runtimeAttestation: {
|
|
111
|
+
requiredReplicaCount: number;
|
|
112
|
+
observedReplicaCount: number;
|
|
113
|
+
healthyReplicaCount: number;
|
|
114
|
+
verifiedReplicaCount: number;
|
|
115
|
+
consistentDigest: boolean;
|
|
116
|
+
};
|
|
101
117
|
platformBindings: IDeploymentPreflightPlatformBinding[];
|
|
102
118
|
corestoreResources: IDeploymentPreflightCorestoreResource[];
|
|
103
119
|
proposed: {
|
|
104
120
|
namespace: string;
|
|
121
|
+
corestoreResources: IDeploymentPreflightProposedCorestoreResource[];
|
|
105
122
|
namespaceReserved: boolean;
|
|
106
|
-
|
|
123
|
+
inventoryComplete: boolean;
|
|
124
|
+
namespaceBlank: boolean | 'unknown';
|
|
107
125
|
versionReserved: boolean;
|
|
108
|
-
wouldReplaceExistingState: boolean;
|
|
126
|
+
wouldReplaceExistingState: boolean | 'unknown';
|
|
109
127
|
};
|
|
110
128
|
authorizationChecks: IDeploymentPreflightAuthorizationCheck[];
|
|
111
129
|
compatibilityBlockers: IDeploymentPreflightBlocker[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeSha256Digest } from './immutableimage.js';
|
|
2
2
|
const fullGitRevisionRegex = /^[a-f0-9]{40}$/;
|
|
3
3
|
const namespaceRegex = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
4
|
+
const semanticVersionRegex = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
4
5
|
const requiredEvidenceKinds = new Set([
|
|
5
6
|
'provenance',
|
|
6
7
|
'sbom',
|
|
@@ -23,14 +24,39 @@ export const validateDeploymentPreflightRequest = (requestArg) => {
|
|
|
23
24
|
if (typeof requestArg.sourceRevision !== 'string' || !fullGitRevisionRegex.test(requestArg.sourceRevision)) {
|
|
24
25
|
blockers.push({ code: 'SOURCE_REVISION_INVALID', message: 'Source revision must be a full lower-case Git commit SHA.' });
|
|
25
26
|
}
|
|
26
|
-
if (typeof requestArg.
|
|
27
|
-
blockers.push({ code: '
|
|
27
|
+
if (typeof requestArg.sourceDirty !== 'boolean') {
|
|
28
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Source worktree state must be reported.' });
|
|
29
|
+
}
|
|
30
|
+
else if (requestArg.sourceDirty) {
|
|
31
|
+
blockers.push({ code: 'SOURCE_WORKTREE_DIRTY', message: 'The source worktree contains uncommitted changes.' });
|
|
32
|
+
}
|
|
33
|
+
if (typeof requestArg.version !== 'string'
|
|
34
|
+
|| requestArg.version.length > 100
|
|
35
|
+
|| !semanticVersionRegex.test(requestArg.version)) {
|
|
36
|
+
blockers.push({ code: 'VERSION_NOT_RESERVED', message: 'A canonical semantic release version is required.' });
|
|
28
37
|
}
|
|
29
38
|
if (typeof requestArg.proposedNamespace !== 'string' || !namespaceRegex.test(requestArg.proposedNamespace)) {
|
|
30
39
|
blockers.push({ code: 'NAMESPACE_NOT_RESERVED', message: 'The proposed namespace is missing or malformed.' });
|
|
31
40
|
}
|
|
32
|
-
if (typeof requestArg.
|
|
33
|
-
blockers.push({ code: '
|
|
41
|
+
if (typeof requestArg.intentDigest !== 'string' || !normalizeSha256Digest(requestArg.intentDigest)) {
|
|
42
|
+
blockers.push({ code: 'INTENT_DIGEST_INVALID', message: 'Deployment intent digest must be a canonical sha256 digest.' });
|
|
43
|
+
}
|
|
44
|
+
const proposedResources = Array.isArray(requestArg.proposedCorestoreResources)
|
|
45
|
+
? requestArg.proposedCorestoreResources
|
|
46
|
+
: [];
|
|
47
|
+
if (!Array.isArray(requestArg.proposedCorestoreResources)
|
|
48
|
+
|| proposedResources.length > 8
|
|
49
|
+
|| proposedResources.some((resourceArg) => (!isRecord(resourceArg)
|
|
50
|
+
|| (resourceArg.capability !== 'database' && resourceArg.capability !== 'objectstorage')
|
|
51
|
+
|| typeof resourceArg.resourceName !== 'string'
|
|
52
|
+
|| !namespaceRegex.test(resourceArg.resourceName)))) {
|
|
53
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Proposed Corestore resources are malformed.' });
|
|
54
|
+
}
|
|
55
|
+
const proposedResourceKeys = proposedResources
|
|
56
|
+
.filter(isRecord)
|
|
57
|
+
.map((resourceArg) => `${String(resourceArg.capability)}:${String(resourceArg.resourceName)}`);
|
|
58
|
+
if (new Set(proposedResourceKeys).size !== proposedResourceKeys.length) {
|
|
59
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Proposed Corestore resources must be unique.' });
|
|
34
60
|
}
|
|
35
61
|
if (requestArg.ociIndexDigest !== undefined
|
|
36
62
|
&& (typeof requestArg.ociIndexDigest !== 'string' || !normalizeSha256Digest(requestArg.ociIndexDigest))) {
|
|
@@ -90,4 +116,4 @@ export const validateDeploymentReleaseEvidence = (argsArg) => {
|
|
|
90
116
|
}
|
|
91
117
|
return blockers;
|
|
92
118
|
};
|
|
93
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
119
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95bWVudHByZWZsaWdodC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2RhdGEvZGVwbG95bWVudHByZWZsaWdodC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQStNNUQsTUFBTSxvQkFBb0IsR0FBRyxnQkFBZ0IsQ0FBQztBQUM5QyxNQUFNLGNBQWMsR0FBRyx3Q0FBd0MsQ0FBQztBQUNoRSxNQUFNLG9CQUFvQixHQUFHLDRNQUE0TSxDQUFDO0FBQzFPLE1BQU0scUJBQXFCLEdBQUcsSUFBSSxHQUFHLENBQThDO0lBQ2pGLFlBQVk7SUFDWixNQUFNO0lBQ04sV0FBVztJQUNYLHNCQUFzQjtJQUN0Qix1QkFBdUI7Q0FDeEIsQ0FBQyxDQUFDO0FBRUgsTUFBTSxRQUFRLEdBQUcsQ0FBQyxRQUFpQixFQUF1QyxFQUFFLENBQzFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBRWhGLE1BQU0sQ0FBQyxNQUFNLGtDQUFrQyxHQUFHLENBQ2hELFVBQW1CLEVBQ1ksRUFBRTtJQUNqQyxNQUFNLFFBQVEsR0FBa0MsRUFBRSxDQUFDO0lBQ25ELElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztRQUMxQixPQUFPLENBQUMsRUFBRSxJQUFJLEVBQUUsaUJBQWlCLEVBQUUsT0FBTyxFQUFFLGlEQUFpRCxFQUFFLENBQUMsQ0FBQztJQUNuRyxDQUFDO0lBQ0QsSUFBSSxPQUFPLFVBQVUsQ0FBQyxTQUFTLEtBQUssUUFBUSxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNsSCxRQUFRLENBQUMsSUFB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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { IIdentity } from '../data/user.js';
|
|
2
|
+
import type { IIdentity, TServiceDeploymentCapability } from '../data/user.js';
|
|
3
3
|
export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_LoginWithUsernameAndPassword> {
|
|
4
4
|
method: 'adminLoginWithUsernameAndPassword';
|
|
5
5
|
request: {
|
|
@@ -10,6 +10,21 @@ export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedre
|
|
|
10
10
|
identity: IIdentity;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
export interface IReq_Admin_ConfigureServiceDeploymentMachineUser extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_Admin_ConfigureServiceDeploymentMachineUser> {
|
|
14
|
+
method: 'configureServiceDeploymentMachineUser';
|
|
15
|
+
request: {
|
|
16
|
+
identity: IIdentity;
|
|
17
|
+
userId: string;
|
|
18
|
+
serviceId: string;
|
|
19
|
+
capabilities: TServiceDeploymentCapability[];
|
|
20
|
+
};
|
|
21
|
+
response: {
|
|
22
|
+
userId: string;
|
|
23
|
+
serviceId: string;
|
|
24
|
+
capabilities: TServiceDeploymentCapability[];
|
|
25
|
+
tokenRolesNormalized: true;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
13
28
|
export interface ICoreflowBaseServiceReconciliationResult {
|
|
14
29
|
status: 'fulfilled' | 'rejected';
|
|
15
30
|
nodeNames: string[];
|
package/package.json
CHANGED
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -11,12 +11,14 @@ export type TDeploymentPreflightBlockerCode =
|
|
|
11
11
|
| 'INVALID_REQUEST'
|
|
12
12
|
| 'AUTHORIZATION_MISSING'
|
|
13
13
|
| 'SOURCE_REVISION_INVALID'
|
|
14
|
+
| 'SOURCE_WORKTREE_DIRTY'
|
|
14
15
|
| 'VERSION_NOT_RESERVED'
|
|
15
16
|
| 'VERSION_COLLISION'
|
|
16
17
|
| 'NAMESPACE_NOT_RESERVED'
|
|
17
18
|
| 'NAMESPACE_COLLISION'
|
|
18
19
|
| 'NAMESPACE_NOT_PROVEN_BLANK'
|
|
19
20
|
| 'CONFIGURATION_DIGEST_INVALID'
|
|
21
|
+
| 'INTENT_DIGEST_INVALID'
|
|
20
22
|
| 'CURRENT_REFERENCE_MUTABLE'
|
|
21
23
|
| 'CURRENT_RUNTIME_DIGEST_MISSING'
|
|
22
24
|
| 'OCI_INDEX_DIGEST_REQUIRED'
|
|
@@ -77,6 +79,11 @@ export interface IDeploymentPreflightCorestoreResource {
|
|
|
77
79
|
resourceName: string;
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
export interface IDeploymentPreflightProposedCorestoreResource {
|
|
83
|
+
capability: 'database' | 'objectstorage';
|
|
84
|
+
resourceName: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
export interface IDeploymentReleaseEvidenceReference {
|
|
81
88
|
kind: 'provenance' | 'sbom' | 'signature' | 'vulnerability-policy' | 'release-authorization';
|
|
82
89
|
statementDigest: TSha256Digest;
|
|
@@ -99,9 +106,11 @@ export interface IDeploymentPreflightRequestData {
|
|
|
99
106
|
requestId: string;
|
|
100
107
|
serviceId: string;
|
|
101
108
|
sourceRevision: string;
|
|
109
|
+
sourceDirty: boolean;
|
|
102
110
|
version: string;
|
|
103
|
-
|
|
111
|
+
intentDigest: string;
|
|
104
112
|
proposedNamespace: string;
|
|
113
|
+
proposedCorestoreResources: IDeploymentPreflightProposedCorestoreResource[];
|
|
105
114
|
ociIndexDigest?: string;
|
|
106
115
|
releaseTag?: string;
|
|
107
116
|
releaseEvidence?: IDeploymentReleaseEvidence;
|
|
@@ -109,7 +118,7 @@ export interface IDeploymentPreflightRequestData {
|
|
|
109
118
|
}
|
|
110
119
|
|
|
111
120
|
export interface IDeploymentPreflightReport {
|
|
112
|
-
schemaVersion:
|
|
121
|
+
schemaVersion: 2;
|
|
113
122
|
decision: TDeploymentPreflightDecision;
|
|
114
123
|
mutationPerformed: false;
|
|
115
124
|
generatedAt: number;
|
|
@@ -117,13 +126,16 @@ export interface IDeploymentPreflightReport {
|
|
|
117
126
|
serviceId: string;
|
|
118
127
|
serviceName: string;
|
|
119
128
|
workspaceIdentity: string;
|
|
129
|
+
registryHost: string;
|
|
120
130
|
registryRepository: string;
|
|
121
131
|
deployOnPush: boolean;
|
|
122
132
|
};
|
|
123
133
|
source: {
|
|
124
134
|
revision: string;
|
|
135
|
+
dirty: boolean;
|
|
125
136
|
version: string;
|
|
126
|
-
|
|
137
|
+
intentDigest: TSha256Digest;
|
|
138
|
+
configurationDigest: TSha256Digest;
|
|
127
139
|
};
|
|
128
140
|
currentImage: {
|
|
129
141
|
tag?: string;
|
|
@@ -141,14 +153,23 @@ export interface IDeploymentPreflightReport {
|
|
|
141
153
|
platforms: IOciPlatformDescriptorSummary[];
|
|
142
154
|
};
|
|
143
155
|
runtimeTasks: IDeploymentPreflightRuntimeTask[];
|
|
156
|
+
runtimeAttestation: {
|
|
157
|
+
requiredReplicaCount: number;
|
|
158
|
+
observedReplicaCount: number;
|
|
159
|
+
healthyReplicaCount: number;
|
|
160
|
+
verifiedReplicaCount: number;
|
|
161
|
+
consistentDigest: boolean;
|
|
162
|
+
};
|
|
144
163
|
platformBindings: IDeploymentPreflightPlatformBinding[];
|
|
145
164
|
corestoreResources: IDeploymentPreflightCorestoreResource[];
|
|
146
165
|
proposed: {
|
|
147
166
|
namespace: string;
|
|
167
|
+
corestoreResources: IDeploymentPreflightProposedCorestoreResource[];
|
|
148
168
|
namespaceReserved: boolean;
|
|
149
|
-
|
|
169
|
+
inventoryComplete: boolean;
|
|
170
|
+
namespaceBlank: boolean | 'unknown';
|
|
150
171
|
versionReserved: boolean;
|
|
151
|
-
wouldReplaceExistingState: boolean;
|
|
172
|
+
wouldReplaceExistingState: boolean | 'unknown';
|
|
152
173
|
};
|
|
153
174
|
authorizationChecks: IDeploymentPreflightAuthorizationCheck[];
|
|
154
175
|
compatibilityBlockers: IDeploymentPreflightBlocker[];
|
|
@@ -190,6 +211,7 @@ export interface IGreenfieldDeploymentOperation {
|
|
|
190
211
|
|
|
191
212
|
const fullGitRevisionRegex = /^[a-f0-9]{40}$/;
|
|
192
213
|
const namespaceRegex = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
214
|
+
const semanticVersionRegex = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
193
215
|
const requiredEvidenceKinds = new Set<IDeploymentReleaseEvidenceReference['kind']>([
|
|
194
216
|
'provenance',
|
|
195
217
|
'sbom',
|
|
@@ -217,14 +239,40 @@ export const validateDeploymentPreflightRequest = (
|
|
|
217
239
|
if (typeof requestArg.sourceRevision !== 'string' || !fullGitRevisionRegex.test(requestArg.sourceRevision)) {
|
|
218
240
|
blockers.push({ code: 'SOURCE_REVISION_INVALID', message: 'Source revision must be a full lower-case Git commit SHA.' });
|
|
219
241
|
}
|
|
220
|
-
if (typeof requestArg.
|
|
221
|
-
blockers.push({ code: '
|
|
242
|
+
if (typeof requestArg.sourceDirty !== 'boolean') {
|
|
243
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Source worktree state must be reported.' });
|
|
244
|
+
} else if (requestArg.sourceDirty) {
|
|
245
|
+
blockers.push({ code: 'SOURCE_WORKTREE_DIRTY', message: 'The source worktree contains uncommitted changes.' });
|
|
246
|
+
}
|
|
247
|
+
if (typeof requestArg.version !== 'string'
|
|
248
|
+
|| requestArg.version.length > 100
|
|
249
|
+
|| !semanticVersionRegex.test(requestArg.version)) {
|
|
250
|
+
blockers.push({ code: 'VERSION_NOT_RESERVED', message: 'A canonical semantic release version is required.' });
|
|
222
251
|
}
|
|
223
252
|
if (typeof requestArg.proposedNamespace !== 'string' || !namespaceRegex.test(requestArg.proposedNamespace)) {
|
|
224
253
|
blockers.push({ code: 'NAMESPACE_NOT_RESERVED', message: 'The proposed namespace is missing or malformed.' });
|
|
225
254
|
}
|
|
226
|
-
if (typeof requestArg.
|
|
227
|
-
blockers.push({ code: '
|
|
255
|
+
if (typeof requestArg.intentDigest !== 'string' || !normalizeSha256Digest(requestArg.intentDigest)) {
|
|
256
|
+
blockers.push({ code: 'INTENT_DIGEST_INVALID', message: 'Deployment intent digest must be a canonical sha256 digest.' });
|
|
257
|
+
}
|
|
258
|
+
const proposedResources = Array.isArray(requestArg.proposedCorestoreResources)
|
|
259
|
+
? requestArg.proposedCorestoreResources
|
|
260
|
+
: [];
|
|
261
|
+
if (!Array.isArray(requestArg.proposedCorestoreResources)
|
|
262
|
+
|| proposedResources.length > 8
|
|
263
|
+
|| proposedResources.some((resourceArg) => (
|
|
264
|
+
!isRecord(resourceArg)
|
|
265
|
+
|| (resourceArg.capability !== 'database' && resourceArg.capability !== 'objectstorage')
|
|
266
|
+
|| typeof resourceArg.resourceName !== 'string'
|
|
267
|
+
|| !namespaceRegex.test(resourceArg.resourceName)
|
|
268
|
+
))) {
|
|
269
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Proposed Corestore resources are malformed.' });
|
|
270
|
+
}
|
|
271
|
+
const proposedResourceKeys = proposedResources
|
|
272
|
+
.filter(isRecord)
|
|
273
|
+
.map((resourceArg) => `${String(resourceArg.capability)}:${String(resourceArg.resourceName)}`);
|
|
274
|
+
if (new Set(proposedResourceKeys).size !== proposedResourceKeys.length) {
|
|
275
|
+
blockers.push({ code: 'INVALID_REQUEST', message: 'Proposed Corestore resources must be unique.' });
|
|
228
276
|
}
|
|
229
277
|
if (requestArg.ociIndexDigest !== undefined
|
|
230
278
|
&& (typeof requestArg.ociIndexDigest !== 'string' || !normalizeSha256Digest(requestArg.ociIndexDigest))) {
|
package/ts/requests/admin.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
|
-
import type { IIdentity } from '../data/user.js';
|
|
2
|
+
import type { IIdentity, TServiceDeploymentCapability } from '../data/user.js';
|
|
3
3
|
|
|
4
4
|
export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedrequestInterfaces.implementsTR<
|
|
5
5
|
plugins.typedrequestInterfaces.ITypedRequest,
|
|
@@ -15,6 +15,26 @@ export interface IReq_Admin_LoginWithUsernameAndPassword extends plugins.typedre
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export interface IReq_Admin_ConfigureServiceDeploymentMachineUser
|
|
19
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
20
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
21
|
+
IReq_Admin_ConfigureServiceDeploymentMachineUser
|
|
22
|
+
> {
|
|
23
|
+
method: 'configureServiceDeploymentMachineUser';
|
|
24
|
+
request: {
|
|
25
|
+
identity: IIdentity;
|
|
26
|
+
userId: string;
|
|
27
|
+
serviceId: string;
|
|
28
|
+
capabilities: TServiceDeploymentCapability[];
|
|
29
|
+
};
|
|
30
|
+
response: {
|
|
31
|
+
userId: string;
|
|
32
|
+
serviceId: string;
|
|
33
|
+
capabilities: TServiceDeploymentCapability[];
|
|
34
|
+
tokenRolesNormalized: true;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
18
38
|
export interface ICoreflowBaseServiceReconciliationResult {
|
|
19
39
|
status: 'fulfilled' | 'rejected';
|
|
20
40
|
nodeNames: string[];
|