@serve.zone/appstore 1.4.2 → 2.1.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.
@@ -34,11 +34,12 @@ export const appStorePackageData: IAppStorePackageData = {
34
34
  "description": "Multi-node serve.zone control plane for clusters, workload services, domains, and deployments.",
35
35
  "category": "Dev Tools",
36
36
  "iconName": "server",
37
- "latestVersion": "1.1.1",
37
+ "latestVersion": "1.1.2",
38
38
  "versions": [
39
39
  "1.0.0",
40
40
  "1.1.0",
41
- "1.1.1"
41
+ "1.1.1",
42
+ "1.1.2"
42
43
  ],
43
44
  "maintainer": "serve.zone",
44
45
  "links": {
@@ -421,6 +422,141 @@ export const appStorePackageData: IAppStorePackageData = {
421
422
  },
422
423
  "minOneboxVersion": "2.2.0"
423
424
  },
425
+ "apps/cloudly/versions/1.1.2/config.json": {
426
+ "image": "code.foss.global/serve.zone/cloudly@sha256:de503daed88b058d285bf0d7fd3fc21f1a4b8cd93a50aa1ec32fe92a38b43390",
427
+ "targetPorts": [
428
+ {
429
+ "name": "web",
430
+ "port": 3000,
431
+ "protocol": "http",
432
+ "default": true
433
+ }
434
+ ],
435
+ "envVars": [
436
+ {
437
+ "key": "SERVEZONE_ENVIRONMENT",
438
+ "value": "production",
439
+ "description": "Cloudly runtime environment.",
440
+ "required": true
441
+ },
442
+ {
443
+ "key": "SERVEZONE_PUBLIC_ORIGIN",
444
+ "value": "https://${SERVICE_DOMAIN}",
445
+ "description": "Canonical public Cloudly origin including protocol and optional public port.",
446
+ "required": true
447
+ },
448
+ {
449
+ "key": "SERVEZONE_LISTENPORT",
450
+ "value": "3000",
451
+ "description": "Internal Cloudly HTTP port inside the container.",
452
+ "required": true
453
+ },
454
+ {
455
+ "key": "SERVEZONE_SSLMODE",
456
+ "value": "external",
457
+ "description": "Use external TLS termination through Onebox or dcrouter.",
458
+ "required": true
459
+ },
460
+ {
461
+ "key": "MONGODB_URL",
462
+ "value": "${MONGODB_URI}",
463
+ "description": "Authenticated MongoDB wire-protocol URL provisioned by Onebox Corestore.",
464
+ "required": true
465
+ },
466
+ {
467
+ "key": "MONGODB_NAME",
468
+ "value": "${MONGODB_DATABASE}",
469
+ "description": "MongoDB wire-protocol database name provisioned by Onebox Corestore.",
470
+ "required": true
471
+ },
472
+ {
473
+ "key": "MONGODB_USER",
474
+ "value": "${MONGODB_USERNAME}",
475
+ "description": "MongoDB wire-protocol username provisioned by Onebox Corestore.",
476
+ "required": true
477
+ },
478
+ {
479
+ "key": "MONGODB_PASS",
480
+ "value": "${MONGODB_PASSWORD}",
481
+ "description": "MongoDB wire-protocol password provisioned by Onebox Corestore.",
482
+ "required": true
483
+ },
484
+ {
485
+ "key": "S3_ENDPOINT",
486
+ "value": "onebox-corestore",
487
+ "description": "S3 endpoint host provisioned by Onebox Corestore.",
488
+ "required": true
489
+ },
490
+ {
491
+ "key": "S3_PORT",
492
+ "value": "9000",
493
+ "description": "S3 endpoint port provisioned by Onebox Corestore.",
494
+ "required": true
495
+ },
496
+ {
497
+ "key": "S3_USESSL",
498
+ "value": "false",
499
+ "description": "Use plain HTTP for internal Corestore S3 traffic on the Onebox network.",
500
+ "required": true
501
+ },
502
+ {
503
+ "key": "S3_BUCKET",
504
+ "value": "${S3_BUCKET}",
505
+ "description": "S3 bucket provisioned by Onebox Corestore for Cloudly's registry.",
506
+ "required": true
507
+ },
508
+ {
509
+ "key": "S3_ACCESSKEY",
510
+ "value": "${S3_ACCESS_KEY}",
511
+ "description": "S3 access key provisioned by Onebox Corestore.",
512
+ "required": true
513
+ },
514
+ {
515
+ "key": "S3_SECRETKEY",
516
+ "value": "${S3_SECRET_KEY}",
517
+ "description": "S3 secret key provisioned by Onebox Corestore.",
518
+ "required": true
519
+ }
520
+ ],
521
+ "platformRequirements": {
522
+ "mongodb": true,
523
+ "s3": true
524
+ },
525
+ "platformOidc": {
526
+ "redirectPath": "/auth/oidc/callback",
527
+ "roles": [
528
+ {
529
+ "id": "admin",
530
+ "label": "Administrator",
531
+ "description": "Full Cloudly administration for this hosted instance."
532
+ },
533
+ {
534
+ "id": "user",
535
+ "label": "User",
536
+ "description": "Standard authenticated Cloudly access for this hosted instance."
537
+ }
538
+ ],
539
+ "environmentVariables": {
540
+ "issuerUrl": "SERVEZONE_PLATFORM_OIDC_ISSUER",
541
+ "clientId": "SERVEZONE_PLATFORM_OIDC_CLIENT_ID",
542
+ "clientSecret": "SERVEZONE_PLATFORM_OIDC_CLIENT_SECRET",
543
+ "redirectUri": "SERVEZONE_PLATFORM_OIDC_REDIRECT_URI",
544
+ "audience": "SERVEZONE_PLATFORM_OIDC_AUDIENCE"
545
+ },
546
+ "clientAuthenticationMethod": "client_secret_basic"
547
+ },
548
+ "minOneboxVersion": "7.1.0",
549
+ "changelog": "Upgrade Cloudly to 17.0.0 and run the Onebox-coordinated offline migration from legacy SecretGroup and SecretBundle persistence into the encrypted v2 secret runtime.",
550
+ "breaking": true,
551
+ "requiresManualReview": true,
552
+ "migrationRequired": true,
553
+ "backupBeforeUpgrade": true,
554
+ "healthCheck": {
555
+ "path": "/status",
556
+ "port": 3000,
557
+ "expectedStatus": 200
558
+ }
559
+ },
424
560
  "apps/ghost/app.json": {
425
561
  "id": "ghost",
426
562
  "name": "Ghost",
@@ -806,24 +942,22 @@ export const appStorePackageData: IAppStorePackageData = {
806
942
  },
807
943
  {
808
944
  "key": "BAO_STATIC_SEAL_KEY",
809
- "description": "Cloudly generates 32 random bytes and stores them only in this service's secret bundle.",
945
+ "description": "Cloudly generates 32 random bytes and delivers them only to the OpenBao static-seal key file.",
810
946
  "required": true,
811
947
  "secret": true,
948
+ "delivery": {
949
+ "type": "file",
950
+ "targetPath": "/run/secrets/openbao-static-seal-key",
951
+ "uid": 100,
952
+ "gid": 1000,
953
+ "mode": 256
954
+ },
812
955
  "generate": {
813
- "type": "randomHex",
956
+ "encoding": "hex",
814
957
  "bytes": 32
815
958
  }
816
959
  }
817
960
  ],
818
- "secretFiles": [
819
- {
820
- "sourceKey": "BAO_STATIC_SEAL_KEY",
821
- "targetPath": "/run/secrets/openbao-static-seal-key",
822
- "uid": 100,
823
- "gid": 1000,
824
- "mode": 256
825
- }
826
- ],
827
961
  "volumes": [
828
962
  {
829
963
  "name": "openbao-data",
@@ -1244,7 +1378,7 @@ export const appStorePackageData: IAppStorePackageData = {
1244
1378
  },
1245
1379
  "appstore.json": {
1246
1380
  "schemaVersion": 1,
1247
- "updatedAt": "2026-07-25T00:00:00Z",
1381
+ "updatedAt": "2026-08-04T00:00:00Z",
1248
1382
  "apps": [
1249
1383
  {
1250
1384
  "id": "nginx",
@@ -1452,7 +1586,7 @@ export const appStorePackageData: IAppStorePackageData = {
1452
1586
  "description": "Multi-node serve.zone control plane for clusters, workload services, domains, and deployments.",
1453
1587
  "category": "Dev Tools",
1454
1588
  "iconName": "server",
1455
- "latestVersion": "1.1.1",
1589
+ "latestVersion": "1.1.2",
1456
1590
  "tags": [
1457
1591
  "serve.zone",
1458
1592
  "control-plane",
@@ -11,7 +11,7 @@ const storageFeatureIds = interfaces.appstore.appStoreStorageFeatureIds;
11
11
  const knownStorageFeatureIds = new Set<string>(Object.values(storageFeatureIds));
12
12
  const storageClassKeyPattern = /^[A-Za-z][A-Za-z0-9-]{0,62}$/;
13
13
  const storageRequestIdPattern = /^[a-z][a-z0-9-]{0,62}$/;
14
- const environmentKeyPattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
14
+ const environmentKeyPattern = /^[A-Z_][A-Z0-9_]{0,252}$/;
15
15
  const capacityPattern = /^([1-9][0-9]*)(KiB|MiB|GiB|TiB)$/;
16
16
  const capacityMultipliers = {
17
17
  KiB: 1024n,
@@ -26,7 +26,6 @@ const appStoreVersionConfigKeys = [
26
26
  'image',
27
27
  'port',
28
28
  'containerArgs',
29
- 'secretFiles',
30
29
  'targetPorts',
31
30
  'domains',
32
31
  'publicPortMappings',
@@ -55,6 +54,12 @@ const appStoreVersionConfigKeys = [
55
54
 
56
55
  type TPlainRecord = Record<string, unknown>;
57
56
 
57
+ interface IAppInputDestinations {
58
+ declaredEnvironmentKeys: Set<string>;
59
+ runtimeEnvironmentKeys: Set<string>;
60
+ declaredSecretPaths: Set<string>;
61
+ }
62
+
58
63
  export function parseStorageCapacityQuantity(
59
64
  quantityArg: unknown,
60
65
  labelArg = 'storage capacity',
@@ -116,6 +121,78 @@ export function normalizeAppStoreStorageConfig(
116
121
  return normalized;
117
122
  }
118
123
 
124
+ function collectAppInputDestinations(
125
+ configArg: TAppStoreVersionConfig,
126
+ labelArg: string,
127
+ ): IAppInputDestinations {
128
+ const declaredEnvironmentKeys = new Set<string>();
129
+ const runtimeEnvironmentOwners = new Map<string, string>();
130
+ const secretFileOwners = new Map<string, string>();
131
+
132
+ const addDestination = (
133
+ ownersArg: Map<string, string>,
134
+ destinationArg: string,
135
+ ownerArg: string,
136
+ kindArg: 'environment' | 'secret file',
137
+ ): void => {
138
+ const existingOwner = ownersArg.get(destinationArg);
139
+ if (existingOwner) {
140
+ throw new Error(
141
+ `Invalid ${labelArg}: ${kindArg} destination '${destinationArg}' is used by both ${existingOwner} and ${ownerArg}`,
142
+ );
143
+ }
144
+ ownersArg.set(destinationArg, ownerArg);
145
+ };
146
+
147
+ for (const declaration of configArg.envVars || []) {
148
+ declaredEnvironmentKeys.add(declaration.key);
149
+ const owner = `envVars.${declaration.key}`;
150
+ if (declaration.secret === true) {
151
+ if (declaration.delivery.type === 'launcher-environment') {
152
+ addDestination(
153
+ runtimeEnvironmentOwners,
154
+ declaration.delivery.variableName,
155
+ owner,
156
+ 'environment',
157
+ );
158
+ } else {
159
+ addDestination(
160
+ secretFileOwners,
161
+ declaration.delivery.targetPath,
162
+ owner,
163
+ 'secret file',
164
+ );
165
+ }
166
+ } else {
167
+ addDestination(runtimeEnvironmentOwners, declaration.key, owner, 'environment');
168
+ }
169
+ }
170
+
171
+ const platformOidc = configArg.platformOidc as unknown;
172
+ if (platformOidc && typeof platformOidc === 'object' && !Array.isArray(platformOidc)) {
173
+ const environmentVariables = (platformOidc as TPlainRecord).environmentVariables;
174
+ if (environmentVariables && typeof environmentVariables === 'object'
175
+ && !Array.isArray(environmentVariables)) {
176
+ for (const [field, destination] of Object.entries(environmentVariables)) {
177
+ if (typeof destination === 'string') {
178
+ addDestination(
179
+ runtimeEnvironmentOwners,
180
+ destination,
181
+ `platformOidc.${field}`,
182
+ 'environment',
183
+ );
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ return {
190
+ declaredEnvironmentKeys,
191
+ runtimeEnvironmentKeys: new Set(runtimeEnvironmentOwners.keys()),
192
+ declaredSecretPaths: new Set(secretFileOwners.keys()),
193
+ };
194
+ }
195
+
119
196
  function validateAndNormalizeStorageConfig(
120
197
  configArg: TAppStoreVersionConfig,
121
198
  labelArg: string,
@@ -125,6 +202,13 @@ function validateAndNormalizeStorageConfig(
125
202
  appStoreVersionConfigKeys,
126
203
  labelArg,
127
204
  );
205
+ const environmentErrors = interfaces.appstore.validateAppStoreEnvironmentDeclarations(
206
+ configArg.envVars,
207
+ );
208
+ if (environmentErrors.length > 0) {
209
+ throw new Error(`Invalid ${labelArg}: ${environmentErrors.join('; ')}`);
210
+ }
211
+ const appInputDestinations = collectAppInputDestinations(configArg, labelArg);
128
212
  validatePlatformRequirements(configArg.platformRequirements, labelArg);
129
213
  const explicitClassInput = configArg.storageClasses;
130
214
  const explicitRequestInput = configArg.storageRequests;
@@ -140,7 +224,7 @@ function validateAndNormalizeStorageConfig(
140
224
  const explicitRequests = validateExplicitStorageRequests(
141
225
  explicitRequestInput,
142
226
  storageClasses,
143
- configArg,
227
+ appInputDestinations,
144
228
  labelArg,
145
229
  );
146
230
  const requiredFeatureIds = validateFeatureDeclaration(configArg, explicitRequests, labelArg);
@@ -227,7 +311,7 @@ function validateAndNormalizeStorageConfig(
227
311
  accessMode: 'readWrite',
228
312
  reclaimPolicy: 'retain',
229
313
  delivery: {
230
- type: 'environment',
314
+ type: 'launcher-environment',
231
315
  keys: {
232
316
  endpoint: 'S3_ENDPOINT',
233
317
  bucket: 'S3_BUCKET',
@@ -247,10 +331,14 @@ function validateAndNormalizeStorageConfig(
247
331
  };
248
332
  }
249
333
 
250
- validateAllFilesystemMountsAndSecretTargets(storageRequests, configArg, labelArg);
334
+ validateAllFilesystemMountsAndSecretTargets(
335
+ storageRequests,
336
+ appInputDestinations.declaredSecretPaths,
337
+ labelArg,
338
+ );
251
339
 
252
340
  return {
253
- schemaVersion: 1,
341
+ schemaVersion: 2,
254
342
  storageClasses,
255
343
  storageRequests,
256
344
  requestOrigins: Object.fromEntries(requestOriginEntries),
@@ -290,7 +378,7 @@ function validateStorageClasses(
290
378
  if (preferred[requirementKey] !== undefined
291
379
  && preferred[requirementKey] !== required[requirementKey]) {
292
380
  throw new Error(
293
- `Invalid ${classLabel}: preferred.${requirementKey} conflicts with required.${requirementKey}`,
381
+ `Invalid ${classLabel}: preferred.${String(requirementKey)} conflicts with required.${String(requirementKey)}`,
294
382
  );
295
383
  }
296
384
  }
@@ -349,7 +437,7 @@ function validateClassRequirement(
349
437
  function validateExplicitStorageRequests(
350
438
  requestsArg: TAppStoreVersionConfig['storageRequests'],
351
439
  classesArg: Record<string, interfaces.appstore.IAppStoreStorageClass>,
352
- configArg: TAppStoreVersionConfig,
440
+ appInputDestinationsArg: IAppInputDestinations,
353
441
  labelArg: string,
354
442
  ): interfaces.appstore.TAppStoreStorageRequest[] {
355
443
  if (requestsArg === undefined) return [];
@@ -360,22 +448,12 @@ function validateExplicitStorageRequests(
360
448
  const requestIds = new Set<string>();
361
449
  const usedClassKeys = new Set<string>();
362
450
  const deliveryEnvironmentKeys = new Set<string>();
363
- const declaredEnvironmentKeys = new Set<string>();
364
- for (const envVar of configArg.envVars || []) {
365
- declaredEnvironmentKeys.add(envVar.key);
366
- }
367
- for (const secretFile of configArg.secretFiles || []) {
368
- declaredEnvironmentKeys.add(secretFile.sourceKey);
369
- }
370
- for (const oidcEnvironmentKey of Object.values(
371
- configArg.platformOidc?.environmentVariables || {},
372
- )) {
373
- declaredEnvironmentKeys.add(oidcEnvironmentKey);
374
- }
451
+ const {
452
+ declaredEnvironmentKeys,
453
+ runtimeEnvironmentKeys,
454
+ declaredSecretPaths,
455
+ } = appInputDestinationsArg;
375
456
  const deliverySecretPaths = new Set<string>();
376
- const declaredSecretPaths = new Set((configArg.secretFiles || []).map(
377
- (entryArg) => entryArg.targetPath,
378
- ));
379
457
 
380
458
  const requests = requestsArg.map((candidateArg, indexArg) => {
381
459
  const requestLabel = `${labelArg} storageRequests[${indexArg}]`;
@@ -462,6 +540,7 @@ function validateExplicitStorageRequests(
462
540
  candidate.delivery,
463
541
  requestLabel,
464
542
  declaredEnvironmentKeys,
543
+ runtimeEnvironmentKeys,
465
544
  deliveryEnvironmentKeys,
466
545
  declaredSecretPaths,
467
546
  deliverySecretPaths,
@@ -552,13 +631,14 @@ function validateObjectStorageDelivery(
552
631
  deliveryArg: unknown,
553
632
  requestLabelArg: string,
554
633
  declaredEnvironmentKeysArg: Set<string>,
634
+ runtimeEnvironmentKeysArg: Set<string>,
555
635
  deliveryEnvironmentKeysArg: Set<string>,
556
636
  declaredSecretPathsArg: Set<string>,
557
637
  deliverySecretPathsArg: Set<string>,
558
638
  ): interfaces.platform.TObjectStorageDelivery {
559
639
  const deliveryLabel = `${requestLabelArg}.delivery`;
560
640
  const delivery = assertPlainRecord(deliveryArg, deliveryLabel);
561
- if (delivery.type === 'environment') {
641
+ if (delivery.type === 'launcher-environment') {
562
642
  assertExactKeys(delivery, ['type', 'keys'], deliveryLabel);
563
643
  const keys = assertPlainRecord(delivery.keys, `${deliveryLabel}.keys`);
564
644
  assertExactKeys(keys, [
@@ -578,7 +658,8 @@ function validateObjectStorageDelivery(
578
658
  if (localKeys.has(targetKeyArg)) {
579
659
  throw new Error(`Invalid ${deliveryLabel}: environment key '${targetKeyArg}' is duplicated`);
580
660
  }
581
- if (declaredEnvironmentKeysArg.has(targetKeyArg)) {
661
+ if (declaredEnvironmentKeysArg.has(targetKeyArg)
662
+ || runtimeEnvironmentKeysArg.has(targetKeyArg)) {
582
663
  throw new Error(
583
664
  `Invalid ${deliveryLabel}: environment key '${targetKeyArg}' collides with an app runtime input`,
584
665
  );
@@ -590,37 +671,49 @@ function validateObjectStorageDelivery(
590
671
  deliveryEnvironmentKeysArg.add(targetKeyArg);
591
672
  }
592
673
  return {
593
- type: 'environment',
674
+ type: 'launcher-environment',
594
675
  keys: { ...keys },
595
- } as interfaces.platform.IObjectStorageEnvironmentDelivery;
676
+ } as interfaces.platform.IObjectStorageLauncherEnvironmentDelivery;
596
677
  }
597
678
 
598
- if (delivery.type === 'secretFile') {
599
- assertExactKeys(delivery, ['type', 'targetPath', 'format'], deliveryLabel);
679
+ if (delivery.type === 'file') {
680
+ assertExactKeys(delivery, ['type', 'targetPath', 'format', 'uid', 'gid', 'mode'], deliveryLabel);
600
681
  if (delivery.format !== 'servezone-object-storage-v1') {
601
682
  throw new Error(`Invalid ${deliveryLabel}: secret file format is not supported`);
602
683
  }
603
684
  if (typeof delivery.targetPath !== 'string'
604
685
  || interfaces.data.normalizeServiceAbsolutePath(delivery.targetPath) !== delivery.targetPath
605
- || !/^\/run\/secrets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(delivery.targetPath)
606
- || delivery.targetPath === '/run/secrets/secret.json') {
686
+ || !/^\/run\/secrets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(delivery.targetPath)) {
607
687
  throw new Error(`Invalid ${deliveryLabel}: targetPath must be a unique canonical file below /run/secrets`);
608
688
  }
609
689
  if (declaredSecretPathsArg.has(delivery.targetPath)) {
610
- throw new Error(`Invalid ${deliveryLabel}: targetPath collides with secretFiles`);
690
+ throw new Error(`Invalid ${deliveryLabel}: targetPath collides with an app secret delivery`);
611
691
  }
612
692
  if (deliverySecretPathsArg.has(delivery.targetPath)) {
613
693
  throw new Error(`Invalid ${deliveryLabel}: targetPath is used by another storage request`);
614
694
  }
695
+ for (const identityField of ['uid', 'gid'] as const) {
696
+ if (!Number.isSafeInteger(delivery[identityField])
697
+ || (delivery[identityField] as number) < 0
698
+ || (delivery[identityField] as number) > 2_147_483_647) {
699
+ throw new Error(`Invalid ${deliveryLabel}: ${identityField} must be a bounded numeric identity`);
700
+ }
701
+ }
702
+ if (delivery.mode !== 0o400 && delivery.mode !== 0o440) {
703
+ throw new Error(`Invalid ${deliveryLabel}: mode must be 0400 or 0440`);
704
+ }
615
705
  deliverySecretPathsArg.add(delivery.targetPath);
616
706
  return {
617
- type: 'secretFile',
707
+ type: 'file',
618
708
  targetPath: delivery.targetPath,
619
709
  format: 'servezone-object-storage-v1',
710
+ uid: delivery.uid as number,
711
+ gid: delivery.gid as number,
712
+ mode: delivery.mode as 0o400 | 0o440,
620
713
  };
621
714
  }
622
715
 
623
- throw new Error(`Invalid ${deliveryLabel}: type must be environment or secretFile`);
716
+ throw new Error(`Invalid ${deliveryLabel}: type must be launcher-environment or file`);
624
717
  }
625
718
 
626
719
  function validateFeatureDeclaration(
@@ -630,18 +723,18 @@ function validateFeatureDeclaration(
630
723
  ): TAppStoreStorageFeatureId[] {
631
724
  const requiredFeatures = new Set<TAppStoreStorageFeatureId>();
632
725
  if (explicitRequestsArg.length > 0) {
633
- requiredFeatures.add(storageFeatureIds.bindingsV1);
726
+ requiredFeatures.add(storageFeatureIds.bindingsV2);
634
727
  }
635
728
  if (explicitRequestsArg.some((requestArg) => requestArg.kind === 'filesystem')) {
636
729
  requiredFeatures.add(storageFeatureIds.filesystemV1);
637
730
  }
638
731
  if (explicitRequestsArg.some((requestArg) => requestArg.kind === 'objectStorage')) {
639
- requiredFeatures.add(storageFeatureIds.objectStorageV1);
732
+ requiredFeatures.add(storageFeatureIds.objectStorageV2);
640
733
  }
641
734
  if (explicitRequestsArg.some((requestArg) => (
642
- requestArg.kind === 'objectStorage' && requestArg.delivery.type === 'secretFile'
735
+ requestArg.kind === 'objectStorage' && requestArg.delivery.type === 'file'
643
736
  ))) {
644
- requiredFeatures.add(storageFeatureIds.objectStorageSecretFileV1);
737
+ requiredFeatures.add(storageFeatureIds.objectStorageFileV2);
645
738
  }
646
739
 
647
740
  const declaredFeatures = configArg.requiresFeatures || [];
@@ -757,7 +850,7 @@ function createLegacyVolumeRequestId(
757
850
 
758
851
  function validateAllFilesystemMountsAndSecretTargets(
759
852
  requestsArg: interfaces.appstore.TAppStoreStorageRequest[],
760
- configArg: TAppStoreVersionConfig,
853
+ declaredSecretPathsArg: Set<string>,
761
854
  labelArg: string,
762
855
  ): void {
763
856
  const mountPaths: string[] = [];
@@ -766,22 +859,15 @@ function validateAllFilesystemMountsAndSecretTargets(
766
859
  if (requestArg.kind === 'filesystem') {
767
860
  assertNoPathCollision(requestArg.mountPath, mountPaths, `${labelArg} request '${requestArg.id}'`);
768
861
  mountPaths.push(requestArg.mountPath);
769
- } else if (requestArg.delivery.type === 'secretFile') {
862
+ } else if (requestArg.delivery.type === 'file') {
770
863
  objectSecretPaths.push(requestArg.delivery.targetPath);
771
864
  }
772
865
  }
773
- for (const secretPathArg of objectSecretPaths) {
866
+ for (const secretPathArg of [...declaredSecretPathsArg, ...objectSecretPaths]) {
774
867
  if (mountPaths.some((mountPathArg) => pathsOverlap(secretPathArg, mountPathArg))) {
775
- throw new Error(`Invalid ${labelArg}: object-storage secret target '${secretPathArg}' overlaps a filesystem mount`);
868
+ throw new Error(`Invalid ${labelArg}: secret target '${secretPathArg}' overlaps a filesystem mount`);
776
869
  }
777
870
  }
778
- const secretFileErrors = interfaces.data.validateServiceSecretFiles(
779
- configArg.secretFiles,
780
- mountPaths,
781
- );
782
- if (secretFileErrors.length > 0) {
783
- throw new Error(`Invalid ${labelArg}: ${secretFileErrors.join('; ')}`);
784
- }
785
871
  }
786
872
 
787
873
  function assertNoPathCollision(pathArg: string, pathsArg: string[], labelArg: string): void {
@@ -33,7 +33,7 @@ export interface IAppStoreStorageLegacyCompatibility {
33
33
  }
34
34
 
35
35
  export interface INormalizedAppStoreStorageConfig {
36
- schemaVersion: 1;
36
+ schemaVersion: 2;
37
37
  storageClasses: Record<string, TAppStoreStorageClass>;
38
38
  storageRequests: TAppStoreStorageRequest[];
39
39
  requestOrigins: Record<string, TAppStoreStorageRequestOrigin>;