@serve.zone/interfaces 19.8.0 → 20.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.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @deprecated Compatibility-only persisted shape. Service-owned Secrets and
3
+ * explicit same-organization SecretSets replace this model.
4
+ */
1
5
  export interface ISecretBundle {
2
6
  id: string;
3
7
  data: {
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @deprecated Compatibility-only persisted shape. Migrate to ISecretMetadata
3
+ * and immutable SecretVersion records; do not create new SecretGroups.
4
+ */
1
5
  export interface ISecretGroup {
2
6
  /**
3
7
  * the insatnce id. This should be a random id, except for default
@@ -13,6 +13,10 @@ import type {
13
13
  IServicePublicPortMapping,
14
14
  IServiceTargetPort,
15
15
  } from './serviceports.js';
16
+ import type {
17
+ IServiceSecretConfiguration,
18
+ IServiceSecretDeploymentPlan,
19
+ } from './secret.js';
16
20
 
17
21
  export interface IServiceVolume {
18
22
  /** Stable Docker volume name. If omitted, Coreflow derives one from service id and mount path. */
@@ -258,14 +262,22 @@ export interface IService {
258
262
  reconciliationStatus?: IServiceReconciliationStatus;
259
263
  environment: { [key: string]: string };
260
264
  /**
261
- * the main secret bundle id, exclusive to the service
265
+ * Legacy main SecretBundle ID. New services use secretConfiguration and
266
+ * the server-managed secretDeployment manifest pointer.
267
+ *
268
+ * @deprecated Compatibility-only until the versioned SecretBundle migration completes.
262
269
  */
263
- secretBundleId: string;
270
+ secretBundleId?: string;
264
271
  /**
265
- * those secret bundle ids do not belong to the service itself
266
- * and thus live past the service lifecycle
272
+ * Legacy additional SecretBundle IDs.
273
+ *
274
+ * @deprecated Use explicit same-organization SecretSet attachments.
267
275
  */
268
276
  additionalSecretBundleIds?: string[];
277
+ /** Value-free, caller-managed SecretSet attachment configuration. */
278
+ secretConfiguration?: IServiceSecretConfiguration;
279
+ /** Server-managed immutable manifest authority pointer. */
280
+ secretDeployment?: IServiceSecretDeploymentPlan;
269
281
  /** Exact OCI/Docker argument vector appended after the image entrypoint. */
270
282
  containerArgs?: string[];
271
283
  /** Secret keys mounted as files and excluded from Env and secret.json. */
@@ -338,10 +350,18 @@ export interface IService {
338
350
  };
339
351
  }
340
352
 
341
- /** Caller-writable service data. Immutable policy and rollout fields are server-managed. */
353
+ /**
354
+ * Caller-writable service data. Immutable rollout state and SecretSet
355
+ * attachments are changed only through their dedicated fenced APIs.
356
+ */
342
357
  export type TServiceWritableData = Omit<
343
358
  IService['data'],
344
- 'organizationId' | 'immutableImageRequired' | 'imageDeployment' | 'imageRolloutStatus'
359
+ | 'organizationId'
360
+ | 'immutableImageRequired'
361
+ | 'imageDeployment'
362
+ | 'imageRolloutStatus'
363
+ | 'secretConfiguration'
364
+ | 'secretDeployment'
345
365
  >;
346
366
 
347
367
  /** Caller-writable update data. Null explicitly removes an existing argument vector. */
@@ -25,6 +25,7 @@ import * as networkRequests from './network.js';
25
25
  import * as nodeRequests from './node.js';
26
26
  import * as platformRequests from './platform.js';
27
27
  import * as routingRequests from './routing.js';
28
+ import * as secretRequests from './secret.js';
28
29
  import * as secretBundleRequests from './secretbundle.js';
29
30
  import * as secretGroupRequests from './secretgroup.js';
30
31
  import * as serverRequests from './server.js';
@@ -61,6 +62,7 @@ export {
61
62
  nodeRequests as node,
62
63
  platformRequests as platform,
63
64
  routingRequests as routing,
65
+ secretRequests as secret,
64
66
  secretBundleRequests as secretbundle,
65
67
  secretGroupRequests as secretgroup,
66
68
  serverRequests as server,
@@ -0,0 +1,256 @@
1
+ import type * as plugins from '../plugins.js';
2
+ import type {
3
+ IResolvedSecretManifestEntry,
4
+ ISecretMetadata,
5
+ ISecretSetAttachment,
6
+ ISecretSetConsumerRolloutStatus,
7
+ ISecretSetMetadata,
8
+ ISecretVersionMetadata,
9
+ ISecretVersionRetentionReference,
10
+ TSecretDelivery,
11
+ TSecretEnvironment,
12
+ } from '../data/secret.js';
13
+ import type { IIdentityCredential } from '../data/user.js';
14
+
15
+ export type TSecretMutationTarget =
16
+ | { kind: 'service'; serviceId: string }
17
+ | { kind: 'secret-set'; secretSetId: string };
18
+
19
+ export type TSecretValueInput =
20
+ | { mode: 'provided'; value: string }
21
+ /** Server-generated runtime-only value; plaintext is never returned. */
22
+ | { mode: 'generated'; bytes: 32 | 48 | 64 };
23
+
24
+ export interface IReq_ListSecrets extends plugins.typedrequestInterfaces.implementsTR<
25
+ plugins.typedrequestInterfaces.ITypedRequest,
26
+ IReq_ListSecrets
27
+ > {
28
+ method: 'listSecrets';
29
+ request: {
30
+ identity: IIdentityCredential;
31
+ target: TSecretMutationTarget;
32
+ environment?: TSecretEnvironment;
33
+ };
34
+ response: {
35
+ secrets: ISecretMetadata[];
36
+ /** Observable CAS fence for the selected service or SecretSet. */
37
+ targetSecretsRevision: number;
38
+ };
39
+ }
40
+
41
+ export interface IReq_GetSecretMetadata extends plugins.typedrequestInterfaces.implementsTR<
42
+ plugins.typedrequestInterfaces.ITypedRequest,
43
+ IReq_GetSecretMetadata
44
+ > {
45
+ method: 'getSecretMetadata';
46
+ request: {
47
+ identity: IIdentityCredential;
48
+ secretId: string;
49
+ };
50
+ response: {
51
+ secret: ISecretMetadata;
52
+ versions: ISecretVersionMetadata[];
53
+ };
54
+ }
55
+
56
+ export interface IReq_CreateSecret extends plugins.typedrequestInterfaces.implementsTR<
57
+ plugins.typedrequestInterfaces.ITypedRequest,
58
+ IReq_CreateSecret
59
+ > {
60
+ method: 'createSecret';
61
+ request: {
62
+ identity: IIdentityCredential;
63
+ target: TSecretMutationTarget;
64
+ key: string;
65
+ environment: TSecretEnvironment;
66
+ name: string;
67
+ description?: string;
68
+ tags?: Array<{ key: string; value: string }>;
69
+ delivery: TSecretDelivery;
70
+ valueInput: TSecretValueInput;
71
+ expectedTargetSecretsRevision: number;
72
+ };
73
+ response: {
74
+ secret: ISecretMetadata;
75
+ version: ISecretVersionMetadata;
76
+ /** Post-create CAS revision for the selected target. */
77
+ targetSecretsRevision: number;
78
+ };
79
+ }
80
+
81
+ export interface IReq_RotateSecret extends plugins.typedrequestInterfaces.implementsTR<
82
+ plugins.typedrequestInterfaces.ITypedRequest,
83
+ IReq_RotateSecret
84
+ > {
85
+ method: 'rotateSecret';
86
+ request: {
87
+ identity: IIdentityCredential;
88
+ secretId: string;
89
+ valueInput: TSecretValueInput;
90
+ expectedSecretRevision: number;
91
+ expectedActiveVersionId?: string;
92
+ };
93
+ response: {
94
+ secret: ISecretMetadata;
95
+ version: ISecretVersionMetadata;
96
+ };
97
+ }
98
+
99
+ export interface IReq_ChangeSecretLifecycle extends plugins.typedrequestInterfaces.implementsTR<
100
+ plugins.typedrequestInterfaces.ITypedRequest,
101
+ IReq_ChangeSecretLifecycle
102
+ > {
103
+ method: 'changeSecretLifecycle';
104
+ request: {
105
+ identity: IIdentityCredential;
106
+ secretId: string;
107
+ action: 'retire' | 'revoke' | 'delete' | 'request-purge';
108
+ expectedSecretRevision: number;
109
+ };
110
+ response: {
111
+ secret: ISecretMetadata;
112
+ };
113
+ }
114
+
115
+ export interface IReq_GetSecretVersionPurgePreflight
116
+ extends plugins.typedrequestInterfaces.implementsTR<
117
+ plugins.typedrequestInterfaces.ITypedRequest,
118
+ IReq_GetSecretVersionPurgePreflight
119
+ > {
120
+ method: 'getSecretVersionPurgePreflight';
121
+ request: {
122
+ identity: IIdentityCredential;
123
+ secretId: string;
124
+ secretVersionId: string;
125
+ };
126
+ response: {
127
+ purgeAllowed: boolean;
128
+ references: ISecretVersionRetentionReference[];
129
+ };
130
+ }
131
+
132
+ export interface IReq_ListSecretSets extends plugins.typedrequestInterfaces.implementsTR<
133
+ plugins.typedrequestInterfaces.ITypedRequest,
134
+ IReq_ListSecretSets
135
+ > {
136
+ method: 'listSecretSets';
137
+ request: {
138
+ identity: IIdentityCredential;
139
+ organizationId: string;
140
+ };
141
+ response: {
142
+ secretSets: ISecretSetMetadata[];
143
+ };
144
+ }
145
+
146
+ export interface IReq_CreateSecretSet extends plugins.typedrequestInterfaces.implementsTR<
147
+ plugins.typedrequestInterfaces.ITypedRequest,
148
+ IReq_CreateSecretSet
149
+ > {
150
+ method: 'createSecretSet';
151
+ request: {
152
+ identity: IIdentityCredential;
153
+ organizationId: string;
154
+ name: string;
155
+ description?: string;
156
+ };
157
+ response: {
158
+ secretSet: ISecretSetMetadata;
159
+ };
160
+ }
161
+
162
+ export interface IReq_UpdateSecretSet extends plugins.typedrequestInterfaces.implementsTR<
163
+ plugins.typedrequestInterfaces.ITypedRequest,
164
+ IReq_UpdateSecretSet
165
+ > {
166
+ method: 'updateSecretSet';
167
+ request: {
168
+ identity: IIdentityCredential;
169
+ secretSetId: string;
170
+ name: string;
171
+ description?: string;
172
+ expectedRevision: number;
173
+ };
174
+ response: {
175
+ secretSet: ISecretSetMetadata;
176
+ };
177
+ }
178
+
179
+ export interface IReq_ChangeSecretSetLifecycle
180
+ extends plugins.typedrequestInterfaces.implementsTR<
181
+ plugins.typedrequestInterfaces.ITypedRequest,
182
+ IReq_ChangeSecretSetLifecycle
183
+ > {
184
+ method: 'changeSecretSetLifecycle';
185
+ request: {
186
+ identity: IIdentityCredential;
187
+ secretSetId: string;
188
+ action: 'retire' | 'delete';
189
+ expectedRevision: number;
190
+ };
191
+ response: {
192
+ secretSet: ISecretSetMetadata;
193
+ };
194
+ }
195
+
196
+ export interface IReq_GetSecretSetConsumerRollout
197
+ extends plugins.typedrequestInterfaces.implementsTR<
198
+ plugins.typedrequestInterfaces.ITypedRequest,
199
+ IReq_GetSecretSetConsumerRollout
200
+ > {
201
+ method: 'getSecretSetConsumerRollout';
202
+ request: {
203
+ identity: IIdentityCredential;
204
+ secretSetId: string;
205
+ rolloutId: string;
206
+ cursor?: string;
207
+ limit?: number;
208
+ };
209
+ response: {
210
+ consumers: ISecretSetConsumerRolloutStatus[];
211
+ nextCursor?: string;
212
+ };
213
+ }
214
+
215
+ export interface IReq_SetServiceSecretSetAttachments
216
+ extends plugins.typedrequestInterfaces.implementsTR<
217
+ plugins.typedrequestInterfaces.ITypedRequest,
218
+ IReq_SetServiceSecretSetAttachments
219
+ > {
220
+ method: 'setServiceSecretSetAttachments';
221
+ request: {
222
+ identity: IIdentityCredential;
223
+ serviceId: string;
224
+ attachments: ISecretSetAttachment[];
225
+ expectedSecretConfigurationRevision: number;
226
+ };
227
+ response: {
228
+ attachments: ISecretSetAttachment[];
229
+ /** Post-update attachment CAS revision. */
230
+ secretConfigurationRevision: number;
231
+ };
232
+ }
233
+
234
+ export interface ISecretResolutionPreviewCollision {
235
+ environment: TSecretEnvironment;
236
+ key: string;
237
+ sourceSecretIds: string[];
238
+ reason: 'duplicate-secret-set-key' | 'duplicate-service-key' | 'invalid-alias';
239
+ }
240
+
241
+ export interface IReq_PreviewServiceSecretResolution
242
+ extends plugins.typedrequestInterfaces.implementsTR<
243
+ plugins.typedrequestInterfaces.ITypedRequest,
244
+ IReq_PreviewServiceSecretResolution
245
+ > {
246
+ method: 'previewServiceSecretResolution';
247
+ request: {
248
+ identity: IIdentityCredential;
249
+ serviceId: string;
250
+ attachments?: ISecretSetAttachment[];
251
+ };
252
+ response: {
253
+ entries: IResolvedSecretManifestEntry[];
254
+ collisions: ISecretResolutionPreviewCollision[];
255
+ };
256
+ }
package/ts/runtime.ts ADDED
@@ -0,0 +1,80 @@
1
+ import type * as plugins from './plugins.js';
2
+ import type {
3
+ IIdentityCredential,
4
+ IResolvedSecretManifest,
5
+ TSha256Digest,
6
+ } from './data/index.js';
7
+ import {
8
+ validateResolvedSecretManifest,
9
+ verifyResolvedSecretManifestDigest,
10
+ } from './data/secret.js';
11
+
12
+ /**
13
+ * Runtime-only material. This module is exported only under the Node condition
14
+ * and is intentionally unreachable from the universal/browser-facing barrel.
15
+ */
16
+ export interface IResolvedSecretMaterialValue {
17
+ secretVersionId: string;
18
+ value: string;
19
+ }
20
+
21
+ export interface IResolvedSecretMaterial {
22
+ schemaVersion: 1;
23
+ /** Full value-free manifest so the runtime can verify its canonical digest. */
24
+ manifest: IResolvedSecretManifest;
25
+ /** Plaintext values keyed only by the exact version IDs pinned by the manifest. */
26
+ values: IResolvedSecretMaterialValue[];
27
+ }
28
+
29
+ /**
30
+ * Verifies the immutable manifest and requires exactly one material value for
31
+ * every pinned version. Callers separately compare the verified manifest
32
+ * reference to the request and their authenticated cluster scope.
33
+ */
34
+ export const verifyResolvedSecretMaterial = async (
35
+ materialArg: unknown,
36
+ ): Promise<boolean> => {
37
+ if (!materialArg || typeof materialArg !== 'object' || Array.isArray(materialArg)) return false;
38
+ const material = materialArg as Partial<IResolvedSecretMaterial>;
39
+ if (!material.manifest || !Array.isArray(material.values)) return false;
40
+ if (material.schemaVersion !== 1) return false;
41
+ if (validateResolvedSecretManifest(material.manifest).length > 0) return false;
42
+ if (!await verifyResolvedSecretManifestDigest(material.manifest)) return false;
43
+ if (!material.values.every((valueArg) => (
44
+ Boolean(valueArg)
45
+ && typeof valueArg === 'object'
46
+ && typeof valueArg.secretVersionId === 'string'
47
+ && typeof valueArg.value === 'string'
48
+ ))) return false;
49
+ const expectedVersionIds = material.manifest.entries
50
+ .map((entryArg) => entryArg.secretVersionId)
51
+ .sort();
52
+ const actualVersionIds = material.values
53
+ .map((valueArg) => valueArg.secretVersionId)
54
+ .sort();
55
+ return new Set(actualVersionIds).size === actualVersionIds.length
56
+ && JSON.stringify(actualVersionIds) === JSON.stringify(expectedVersionIds);
57
+ };
58
+
59
+ /**
60
+ * Cluster and organization are derived from the verified JWT. A caller cannot
61
+ * select either scope through request fields.
62
+ */
63
+ export interface IReq_GetResolvedSecretMaterial
64
+ extends plugins.typedrequestInterfaces.implementsTR<
65
+ plugins.typedrequestInterfaces.ITypedRequest,
66
+ IReq_GetResolvedSecretMaterial
67
+ > {
68
+ method: 'getResolvedSecretMaterial';
69
+ request: {
70
+ identity: IIdentityCredential;
71
+ serviceId: string;
72
+ secretRolloutId: string;
73
+ secretRolloutGeneration: number;
74
+ manifestId: string;
75
+ manifestDigest: TSha256Digest;
76
+ };
77
+ response: {
78
+ material: IResolvedSecretMaterial;
79
+ };
80
+ }